diff --git a/src/components/menu/menu.js b/src/components/menu/menu.js index ff13c50..5ecf038 100644 --- a/src/components/menu/menu.js +++ b/src/components/menu/menu.js @@ -18,7 +18,6 @@ class Menu extends React.Component { render() { const { menu } = this.props; - if (this.state.error) { return

В этом ресторане меню не доступно

; } diff --git a/src/components/product/product.js b/src/components/product/product.js index 018320d..448bd95 100644 --- a/src/components/product/product.js +++ b/src/components/product/product.js @@ -4,16 +4,26 @@ import PropTypes from 'prop-types'; import { createStructuredSelector } from 'reselect'; import styles from './product.module.css'; -import { decrement, increment } from '../../redux/actions'; +import { decrement, increment, loadProduct } from '../../redux/actions'; import Button from '../button'; import { productAmountSelector, productSelector } from '../../redux/selectors'; +import Loader from '../loader'; -const Product = ({ product, amount, increment, decrement, fetchData }) => { +const Product = ({ + id, + product, + amount, + increment, + decrement, + fetchData, + loadProduct, +}) => { useEffect(() => { fetchData && fetchData(product.id); }, []); // eslint-disable-line + if (!product) return ; return (
diff --git a/src/components/restaurant/restaurant.js b/src/components/restaurant/restaurant.js index 7f4ec3e..069d439 100644 --- a/src/components/restaurant/restaurant.js +++ b/src/components/restaurant/restaurant.js @@ -1,15 +1,23 @@ -import React from 'react'; -import { connect } from 'react-redux'; +import React, { useEffect } from 'react'; +import { connect, useDispatch, useSelector } from 'react-redux'; import PropTypes from 'prop-types'; import Menu from '../menu'; import Reviews from '../reviews'; import Banner from '../banner'; import Rate from '../rate'; import Tabs from '../tabs'; -import { averageRatingSelector } from '../../redux/selectors'; +import { averageRatingSelector, productsSelector } from '../../redux/selectors'; +import { loadProducts } from '../../redux/actions'; -const Restaurant = ({ restaurant, averageRating }) => { +const Restaurant = ({ restaurant, allProducts, loadProducts }) => { const { id, name, menu, reviews } = restaurant; + useEffect(() => { + const allProdArray = Object.keys(allProducts); + if (!allProdArray.includes(menu[0])) { + loadProducts(id); + } + }, [id, menu]); + const tabs = [ { title: 'Menu', content: }, { @@ -17,6 +25,19 @@ const Restaurant = ({ restaurant, averageRating }) => { content: , }, ]; + const allReviews = useSelector((state) => state.reviews.entities); + const filteredReviewsIdArr = Object.keys(allReviews).filter((reviewId) => + reviews.includes(reviewId) + ); + + //todo fix loop + let averageRating = 0; + for (const reviewId of filteredReviewsIdArr) { + const review = allReviews[reviewId]; + const rating = review.rating; + averageRating = averageRating + rating; + } + averageRating = averageRating / filteredReviewsIdArr.length; return (
@@ -38,6 +59,10 @@ Restaurant.propTypes = { averageRating: PropTypes.number, }; -export default connect((state, props) => ({ - averageRating: averageRatingSelector(state, props), -}))(Restaurant); +export default connect( + (state, props) => ({ + //averageRating: averageRatingSelector(state, props), + allProducts: productsSelector(state, props), + }), + { loadProducts } +)(Restaurant); diff --git a/src/components/restaurants/restaurants.js b/src/components/restaurants/restaurants.js index a55d003..b8b3320 100644 --- a/src/components/restaurants/restaurants.js +++ b/src/components/restaurants/restaurants.js @@ -9,9 +9,18 @@ import { restaurantsLoadedSelector, restaurantsLoadingSelector, } from '../../redux/selectors'; -import { loadRestaurants } from '../../redux/actions'; +import { loadRestaurants, loadUsers } from '../../redux/actions'; -const Restaurants = ({ restaurants, loading, loaded, loadRestaurants }) => { +const Restaurants = ({ + restaurants, + loading, + loaded, + loadRestaurants, + loadUsers, +}) => { + useEffect(() => { + loadUsers(); + }, []); useEffect(() => { if (!loading && !loaded) loadRestaurants(); }, [loading, loaded, loadRestaurants]); @@ -22,7 +31,6 @@ const Restaurants = ({ restaurants, loading, loaded, loadRestaurants }) => { title: restaurant.name, content: , })); - return ; }; @@ -40,5 +48,5 @@ export default connect( loading: restaurantsLoadingSelector(state), loaded: restaurantsLoadedSelector(state), }), - { loadRestaurants } + { loadRestaurants, loadUsers } )(Restaurants); diff --git a/src/components/reviews/review/review.js b/src/components/reviews/review/review.js index 4ebbfe1..801ce62 100644 --- a/src/components/reviews/review/review.js +++ b/src/components/reviews/review/review.js @@ -1,39 +1,49 @@ import React from 'react'; -import { connect } from 'react-redux'; +import { connect, useSelector } from 'react-redux'; import PropTypes from 'prop-types'; import Rate from '../../rate'; import styles from './review.module.css'; -import { reviewWitUserSelector } from '../../../redux/selectors'; +import Loader from '../../loader'; +import { usersSelector } from '../../../redux/selectors'; -const Review = ({ user, text, rating }) => ( -
-
-
-

- {user} -

-

- {text} -

-
-
- +const Review = ({ id }) => { + const allUsers = useSelector((state) => usersSelector(state)); + const allReviews = useSelector((state) => state.reviews.entities); + const review = allReviews[id]; + if (!review) { + return ; + } + const { userId, text, rating } = review; + const user = allUsers[userId].name; + + return ( +
+
+
+

+ {user} +

+

+ {text} +

+
+
+ +
-
-); + ); +}; Review.propTypes = { user: PropTypes.string, text: PropTypes.string, - rating: PropTypes.number.isRequired, + //rating: PropTypes.number.isRequired, }; Review.defaultProps = { user: 'Anonymous', }; -export default connect((state, props) => ({ - ...reviewWitUserSelector(state, props), -}))(Review); +export default connect((state, props) => ({}))(Review); diff --git a/src/components/reviews/reviews.js b/src/components/reviews/reviews.js index b224553..1ac2e91 100644 --- a/src/components/reviews/reviews.js +++ b/src/components/reviews/reviews.js @@ -1,15 +1,23 @@ import React, { useEffect } from 'react'; -import { connect } from 'react-redux'; +import { connect, useSelector } from 'react-redux'; import PropTypes from 'prop-types'; import Review from './review'; import ReviewForm from './review-form'; import styles from './reviews.module.css'; import { loadReviews } from '../../redux/actions'; +import { + reviewsIsLoadingSelector, + reviewsSelector, +} from '../../redux/selectors'; +import Loader from '../loader'; -const Reviews = ({ reviews, restaurantId, loadReviews }) => { +const Reviews = ({ reviews, restaurantId, loadReviews, allReviews }) => { useEffect(() => { - loadReviews(restaurantId); + const allReviewsArr = Object.keys(allReviews); + if (!allReviewsArr.includes(reviews[0])) { + loadReviews(restaurantId); + } }, [loadReviews, restaurantId]); return ( @@ -27,4 +35,9 @@ Reviews.propTypes = { reviews: PropTypes.arrayOf(PropTypes.string.isRequired).isRequired, }; -export default connect(null, { loadReviews })(Reviews); +export default connect( + (state, props) => ({ + allReviews: reviewsSelector(state).entities, + }), + { loadReviews } +)(Reviews); diff --git a/src/fixtures.js b/src/fixtures.js deleted file mode 100644 index 3c84f3b..0000000 --- a/src/fixtures.js +++ /dev/null @@ -1,390 +0,0 @@ -const restaurants = [ - { - id: 'a757a0e9-03c1-4a2a-b384-8ac21dbe2fb2', - name: 'Dishoom', - location: { - lat: 51.51307933813641, - lng: -0.13968944549560547, - }, - image: - 'https://scontent-waw1-1.xx.fbcdn.net/v/t1.0-9/12065623_177150569292352_3493242754411252126_n.jpg?_nc_cat=108&_nc_ht=scontent-waw1-1.xx&oh=94f9c77559a2f31edf4fd68a89b6dda7&oe=5D6D6D4A', - menu: [ - { - id: 'd75f762a-eadd-49be-8918-ed0daa8dd024', - name: 'Chicken tikka masala', - price: 12, - ingredients: ['chicken', 'rice'], - }, - { - id: 'c3cb8f92-a2ed-4716-92a1-b6ea813e9049', - name: 'Naan', - price: 3, - ingredients: ['bread'], - }, - { - id: 'bd129641-c0eb-432b-84b6-8b81d2930358', - name: 'Samosa', - price: 8, - ingredients: ['chicken', 'bread'], - }, - ], - reviews: [ - { - id: '5909796d-5030-4e36-adec-68b8f9ec2d96', - user: 'Antony', - text: 'Not bad', - rating: 5, - }, - { - id: '429dea85-11dd-4054-a31e-c60c92e17255', - user: 'Sam', - text: 'No burgers', - rating: 3, - }, - ], - }, - { - id: 'bb8afbec-2fec-491f-93e9-7f13950dd80b', - name: 'Homeslice', - location: { - lat: 51.51847684708113, - lng: -0.13999606534701844, - }, - image: - 'https://scontent-waw1-1.xx.fbcdn.net/v/t1.0-9/14492346_873920782710134_3797018371088115698_n.jpg?_nc_cat=108&_nc_ht=scontent-waw1-1.xx&oh=31de8f2906284981f858f7fdf36b0235&oe=5D6AA774', - menu: [ - { - id: '25402233-0095-49ea-9939-1e67ed89ffb9', - name: 'Margarita', - price: 9, - ingredients: ['bread', 'cheese', 'tomatoes'], - }, - { - id: '90902233-0095-49ea-9939-1e67ed89ffb9', - name: 'Chef pizza', - price: 10, - ingredients: ['bread', 'cheese', 'tomatoes', 'chicken'], - }, - ], - reviews: [ - { - id: '53b642d7-5e86-4717-a466-0640a1dee076', - user: 'Diana', - text: 'Perfect Margarita', - rating: 5, - }, - { - id: 'c27ab88e-375c-4e98-aa94-8a180150a797', - user: 'Sam', - text: 'No burgers again. But Chef Pizza is the best one', - rating: 4, - }, - { - id: 'abc0c5e1-cd57-4f0a-99d9-00e6b4533b3a', - user: 'Lolly', - text: 'Good for lunch', - rating: 5, - }, - ], - }, - { - id: '982bfbce-c5e0-41a0-9f99-d5c20ecee49d', - name: 'Fabrique', - location: { - lat: 51.513614456342495, - lng: -0.1284961359927072, - }, - image: 'http://fabrique.co.uk/wp-content/uploads/2012/11/our22.png', - menu: [ - { - id: '08c9ffa0-d003-4310-9e15-20978743296e', - name: 'Cinnamon buns', - price: 5, - ingredients: ['bread'], - }, - { - id: '64a4967c-2080-4a99-9074-4655a4569a95', - name: 'Semlor', - price: 2, - ingredients: ['bread', 'cream'], - }, - { - id: '4bc8528e-26d1-46c3-a522-8e18d10c8c84', - name: 'Saffron bun', - price: 4, - ingredients: ['bread'], - }, - ], - reviews: [ - { - id: '53b642d7-5e86-4717-a466-0640a1dee076', - user: 'Agata', - text: 'Best bakery', - rating: 5, - }, - ], - }, - { - id: 'd9241927-09e1-44f3-8986-a76346869037', - name: 'Flat Iron', - location: { - lat: 51.51084146746025, - lng: -0.12409270211070839, - }, - image: - 'https://scontent-waw1-1.xx.fbcdn.net/v/t1.0-9/602319_106412009502974_1112399097_n.jpg?_nc_cat=110&_nc_ht=scontent-waw1-1.xx&oh=c23931271965e9edd3921d559f7440ba&oe=5D6CC921', - menu: [ - { - id: '6c02c2ce-b868-4191-b4a7-8686429f4bac', - name: 'Flat Iron Steak', - price: 10, - ingredients: ['beef'], - }, - { - id: '99bb6fbb-e53b-4b7e-b9c2-23b63b77385d', - name: 'Flat Iron Burger', - price: 10, - ingredients: ['bread', 'beef'], - }, - ], - reviews: [ - { - id: '5db6247b-ab1c-49db-be1f-8dd27fd38b81', - user: 'Sam', - text: - 'Finally! This place is amazing place for breakfast, lunch, dinner and supper', - rating: 5, - }, - { - id: '381b0c31-6360-43ff-80d1-581a116159d8', - user: 'Rebeca', - text: 'Meat here is extremely delicious', - rating: 5, - }, - ], - }, -]; - -const normalizedRestaurants = [ - { - id: 'a757a0e9-03c1-4a2a-b384-8ac21dbe2fb2', - name: 'Dishoom', - location: { - lat: 51.51307933813641, - lng: -0.13968944549560547, - }, - image: - 'https://scontent-waw1-1.xx.fbcdn.net/v/t1.0-9/12065623_177150569292352_3493242754411252126_n.jpg?_nc_cat=108&_nc_ht=scontent-waw1-1.xx&oh=94f9c77559a2f31edf4fd68a89b6dda7&oe=5D6D6D4A', - menu: [ - 'd75f762a-eadd-49be-8918-ed0daa8dd024', - 'c3cb8f92-a2ed-4716-92a1-b6ea813e9049', - 'bd129641-c0eb-432b-84b6-8b81d2930358', - ], - reviews: [ - '5909796d-5030-4e36-adec-68b8f9ec2d96', - '429dea85-11dd-4054-a31e-c60c92e17255', - ], - }, - { - id: 'bb8afbec-2fec-491f-93e9-7f13950dd80b', - name: 'Homeslice', - location: { - lat: 51.51847684708113, - lng: -0.13999606534701844, - }, - image: - 'https://scontent-waw1-1.xx.fbcdn.net/v/t1.0-9/14492346_873920782710134_3797018371088115698_n.jpg?_nc_cat=108&_nc_ht=scontent-waw1-1.xx&oh=31de8f2906284981f858f7fdf36b0235&oe=5D6AA774', - menu: [ - '25402233-0095-49ea-9939-1e67ed89ffb9', - '90902233-0095-49ea-9939-1e67ed89ffb9', - ], - reviews: [ - '53b642d7-5e86-4717-a466-0640a1dee076', - 'c27ab88e-375c-4e98-aa94-8a180150a797', - 'abc0c5e1-cd57-4f0a-99d9-00e6b4533b3a', - ], - }, - { - id: '982bfbce-c5e0-41a0-9f99-d5c20ecee49d', - name: 'Fabrique', - location: { - lat: 51.513614456342495, - lng: -0.1284961359927072, - }, - image: 'http://fabrique.co.uk/wp-content/uploads/2012/11/our22.png', - menu: [ - '08c9ffa0-d003-4310-9e15-20978743296e', - '64a4967c-2080-4a99-9074-4655a4569a95', - '4bc8528e-26d1-46c3-a522-8e18d10c8c84', - ], - reviews: ['13b642d7-5e86-4717-a466-0640a1dee076'], - }, - { - id: 'd9241927-09e1-44f3-8986-a76346869037', - name: 'Flat Iron', - location: { - lat: 51.51084146746025, - lng: -0.12409270211070839, - }, - image: - 'https://scontent-waw1-1.xx.fbcdn.net/v/t1.0-9/602319_106412009502974_1112399097_n.jpg?_nc_cat=110&_nc_ht=scontent-waw1-1.xx&oh=c23931271965e9edd3921d559f7440ba&oe=5D6CC921', - menu: [ - '6c02c2ce-b868-4191-b4a7-8686429f4bac', - '99bb6fbb-e53b-4b7e-b9c2-23b63b77385d', - ], - reviews: [ - '5db6247b-ab1c-49db-be1f-8dd27fd38b81', - '381b0c31-6360-43ff-80d1-581a116159d8', - ], - }, -]; - -const normalizedProducts = [ - { - id: 'd75f762a-eadd-49be-8918-ed0daa8dd024', - name: 'Chicken tikka masala', - price: 12, - ingredients: ['chicken', 'rice'], - }, - { - id: 'c3cb8f92-a2ed-4716-92a1-b6ea813e9049', - name: 'Naan', - price: 3, - ingredients: ['bread'], - }, - { - id: 'bd129641-c0eb-432b-84b6-8b81d2930358', - name: 'Samosa', - price: 8, - ingredients: ['chicken', 'bread'], - }, - { - id: '25402233-0095-49ea-9939-1e67ed89ffb9', - name: 'Margarita', - price: 9, - ingredients: ['bread', 'cheese', 'tomatoes'], - }, - { - id: '90902233-0095-49ea-9939-1e67ed89ffb9', - name: 'Chef pizza', - price: 10, - ingredients: ['bread', 'cheese', 'tomatoes', 'chicken'], - }, - { - id: '08c9ffa0-d003-4310-9e15-20978743296e', - name: 'Cinnamon buns', - price: 5, - ingredients: ['bread'], - }, - { - id: '64a4967c-2080-4a99-9074-4655a4569a95', - name: 'Semlor', - price: 2, - ingredients: ['bread', 'cream'], - }, - { - id: '4bc8528e-26d1-46c3-a522-8e18d10c8c84', - name: 'Saffron bun', - price: 4, - ingredients: ['bread'], - }, - { - id: '6c02c2ce-b868-4191-b4a7-8686429f4bac', - name: 'Flat Iron Steak', - price: 10, - ingredients: ['beef'], - }, - { - id: '99bb6fbb-e53b-4b7e-b9c2-23b63b77385d', - name: 'Flat Iron Burger', - price: 10, - ingredients: ['bread', 'beef'], - }, -]; - -const normalizedReviews = [ - { - id: '5909796d-5030-4e36-adec-68b8f9ec2d96', - userId: 'a304959a-76c0-4b34-954a-b38dbf310360', - text: 'Not bad', - rating: 5, - }, - { - id: '429dea85-11dd-4054-a31e-c60c92e17255', - userId: 'dfb982e9-b432-4b7d-aec6-7f6ff2e6af54', - text: 'No burgers', - rating: 3, - }, - { - id: '53b642d7-5e86-4717-a466-0640a1dee076', - userId: '20bed9b5-9c7b-4771-8221-75b74ed1904a', - text: 'Perfect Margarita', - rating: 5, - }, - { - id: 'c27ab88e-375c-4e98-aa94-8a180150a797', - userId: 'dfb982e9-b432-4b7d-aec6-7f6ff2e6af54', - text: 'No burgers again. But Chef Pizza is the best one', - rating: 4, - }, - { - id: 'abc0c5e1-cd57-4f0a-99d9-00e6b4533b3a', - userId: 'c3d4abd4-c3ef-46e1-8719-eb17db1d6e99', - text: 'Good for lunch', - rating: 5, - }, - { - id: '13b642d7-5e86-4717-a466-0640a1dee076', - userId: '52a63cc0-5a6f-41f3-9774-0161ea4c9b0c', - text: 'Best bakery', - rating: 5, - }, - { - id: '5db6247b-ab1c-49db-be1f-8dd27fd38b81', - userId: 'dfb982e9-b432-4b7d-aec6-7f6ff2e6af54', - text: - 'Finally! This place is amazing place for breakfast, lunch, dinner and supper', - rating: 5, - }, - { - id: '381b0c31-6360-43ff-80d1-581a116159d8', - userId: '1547335a-ea18-4547-a73d-32bd6e9f651c', - text: 'Meat here is extremely delicious', - rating: 5, - }, -]; - -const normalizedUsers = [ - { - id: 'a304959a-76c0-4b34-954a-b38dbf310360', - name: 'Antony', - }, - { - id: '20bed9b5-9c7b-4771-8221-75b74ed1904a', - name: 'Diana', - }, - { - id: 'c3d4abd4-c3ef-46e1-8719-eb17db1d6e99', - name: 'Lolly', - }, - { - id: '52a63cc0-5a6f-41f3-9774-0161ea4c9b0c', - name: 'Agata', - }, - { - id: '1547335a-ea18-4547-a73d-32bd6e9f651c', - name: 'Rebeca', - }, - { - id: 'dfb982e9-b432-4b7d-aec6-7f6ff2e6af54', - name: 'Sam', - }, -]; - -export { - restaurants, - normalizedProducts, - normalizedRestaurants, - normalizedReviews, - normalizedUsers, -}; diff --git a/src/redux/actions.js b/src/redux/actions.js index 42b28b2..758aadd 100644 --- a/src/redux/actions.js +++ b/src/redux/actions.js @@ -8,6 +8,8 @@ import { REQUEST, SUCCESS, FAILURE, + LOAD_PRODUCT, + LOAD_USERS, } from './constants'; export const increment = (id) => ({ type: INCREMENT, payload: { id } }); @@ -29,11 +31,34 @@ export const loadReviews = (restaurantId) => async (dispatch) => { dispatch({ type: LOAD_REVIEWS + REQUEST }); try { - const data = await fetch(`/api/reviews?id=${restaurantId}`).then((res) => + const reviews = await fetch(`/api/reviews?id=${restaurantId}`).then((res) => res.json() ); - dispatch({ type: LOAD_REVIEWS + SUCCESS, data }); + + dispatch({ type: LOAD_REVIEWS + SUCCESS, reviews }); } catch (error) { dispatch({ type: LOAD_REVIEWS + FAILURE, error }); } }; +export const loadProducts = (restaurantId) => async (dispatch) => { + dispatch({ type: LOAD_PRODUCT + REQUEST }); + try { + const products = await fetch( + `/api/products?id=${restaurantId}` + ).then((res) => res.json()); + + dispatch({ type: LOAD_PRODUCT + SUCCESS, products }); + } catch (error) { + dispatch({ type: LOAD_PRODUCT + FAILURE, error }); + } +}; +export const loadUsers = () => async (dispatch) => { + dispatch({ type: LOAD_USERS + REQUEST }); + try { + const users = await fetch(`/api/users`).then((res) => res.json()); + + dispatch({ type: LOAD_USERS + SUCCESS, users }); + } catch (error) { + dispatch({ type: LOAD_USERS + FAILURE, error }); + } +}; diff --git a/src/redux/constants.js b/src/redux/constants.js index a47d519..e631056 100644 --- a/src/redux/constants.js +++ b/src/redux/constants.js @@ -5,6 +5,8 @@ export const ADD_REVIEW = 'ADD_REVIEW'; export const LOAD_RESTAURANTS = 'LOAD_RESTAURANTS'; export const LOAD_REVIEWS = 'LOAD_REVIEWS'; +export const LOAD_PRODUCT = 'LOAD_PRODUCT'; +export const LOAD_USERS = 'LOAD_USERS'; export const REQUEST = '_REQUEST'; export const SUCCESS = '_SUCCESS'; diff --git a/src/redux/reducer/products.js b/src/redux/reducer/products.js index c87287b..75ba186 100644 --- a/src/redux/reducer/products.js +++ b/src/redux/reducer/products.js @@ -1,11 +1,45 @@ -import { normalizedProducts } from '../../fixtures'; import { arrToMap } from '../utils'; +import { FAILURE, LOAD_PRODUCT, REQUEST, SUCCESS } from '../constants'; + +const initialState = { + entities: {}, + loading: false, + loaded: false, + error: null, +}; // { [productId]: product } -export default (state = arrToMap(normalizedProducts), action) => { - const { type } = action; +export default (state = initialState, action) => { + const { type, payload, products, error } = action; switch (type) { + case LOAD_PRODUCT + SUCCESS: + const newProducts = products.reduce( + (acc, product) => ({ ...acc, [product.id]: product }), + { ...state.entities } + ); + return { + ...state, + loading: false, + loaded: true, + error: null, + entities: newProducts, + }; + case LOAD_PRODUCT + REQUEST: + return { + ...state, + loading: true, + error: null, + loaded: false, + }; + case LOAD_PRODUCT + FAILURE: + return { + ...state, + loading: false, + loaded: false, + error, + }; + default: return state; } diff --git a/src/redux/reducer/reviews.js b/src/redux/reducer/reviews.js index 787f625..6c26e7e 100644 --- a/src/redux/reducer/reviews.js +++ b/src/redux/reducer/reviews.js @@ -1,11 +1,50 @@ -import { ADD_REVIEW } from '../constants'; -import { normalizedReviews } from '../../fixtures'; +import { + ADD_REVIEW, + FAILURE, + LOAD_REVIEWS, + REQUEST, + SUCCESS, +} from '../constants'; import { arrToMap } from '../utils'; -export default (state = arrToMap(normalizedReviews), action) => { - const { type, payload, reviewId, userId } = action; +const initialState = { + entities: {}, + loading: false, + loaded: false, + error: null, +}; + +export default (state = initialState, action) => { + const { type, payload, reviewId, userId, error, reviews } = action; switch (type) { + case LOAD_REVIEWS + SUCCESS: + const newReviews = reviews.reduce( + (acc, review) => ({ ...acc, [review.id]: review }), + { ...state.entities } + ); + + return { + ...state, + loading: false, + loaded: true, + error: null, + entities: newReviews, + }; + case LOAD_REVIEWS + FAILURE: + return { + ...state, + loading: false, + loaded: false, + error, + }; + case LOAD_REVIEWS + REQUEST: + return { + ...state, + loading: true, + loaded: false, + error: null, + }; case ADD_REVIEW: const { text, rating } = payload.review; return { diff --git a/src/redux/reducer/users.js b/src/redux/reducer/users.js index 063b7be..de69e93 100644 --- a/src/redux/reducer/users.js +++ b/src/redux/reducer/users.js @@ -1,12 +1,42 @@ import produce from 'immer'; -import { ADD_REVIEW } from '../constants'; -import { normalizedUsers } from '../../fixtures'; +import { + ADD_REVIEW, + FAILURE, + LOAD_USERS, + REQUEST, + SUCCESS, +} from '../constants'; import { arrToMap } from '../utils'; -export default produce((draft = arrToMap(normalizedUsers), action) => { - const { type, payload, userId } = action; +const initialState = { + entities: {}, + loading: false, + loaded: false, + error: null, +}; + +export default produce((draft = initialState, action) => { + const { type, payload, userId, users, error } = action; switch (type) { + case LOAD_USERS + SUCCESS: + draft.loaded = true; + draft.loading = false; + draft.error = null; + draft.entities = arrToMap(users); + + case LOAD_USERS + FAILURE: + draft.loaded = false; + draft.loading = false; + draft.error = error; + break; + + case LOAD_USERS + REQUEST: + draft.loaded = false; + draft.loading = true; + draft.error = null; + break; + case ADD_REVIEW: const { name } = payload.review; draft[userId] = { id: userId, name }; diff --git a/src/redux/selectors.js b/src/redux/selectors.js index fbbe656..5c57cff 100644 --- a/src/redux/selectors.js +++ b/src/redux/selectors.js @@ -3,9 +3,11 @@ import { getById } from './utils'; const restaurantsSelector = (state) => state.restaurants.entities; const orderSelector = (state) => state.order; -const productsSelector = (state) => state.products; -const reviewsSelector = (state) => state.reviews; -const usersSelector = (state) => state.users; +export const productsSelector = (state) => state.products.entities; +export const reviewsSelector = (state) => state.reviews; +export const reviewsIsLoadingSelector = (state) => state.reviews.loading; + +export const usersSelector = (state) => state.users.entities; export const restaurantsLoadingSelector = (state) => state.restaurants.loading; export const restaurantsLoadedSelector = (state) => state.restaurants.loaded;