Skip to content

Commit

Permalink
add HW4
Browse files Browse the repository at this point in the history
  • Loading branch information
andriiko committed Nov 30, 2020
1 parent f13afcc commit 672675b
Show file tree
Hide file tree
Showing 21 changed files with 185 additions and 86 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"redux": "^4.0.5",
"redux-devtools-extension": "^2.13.8",
"reselect": "^4.0.0",
"uuid": "^8.3.1",
"web-vitals": "^0.2.4"
},
"scripts": {
Expand Down
13 changes: 7 additions & 6 deletions src/components/basket/basket.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from 'react';
import { connect } from 'react-redux';
import { createStructuredSelector } from 'reselect';

import styles from './basket.module.css';
import BasketRow from './basket-row';
Expand Down Expand Up @@ -38,9 +39,9 @@ function Basket({ title = 'Basket', total, orderProducts }) {
);
}

export default connect((state) => {
return {
total: totalSelector(state),
orderProducts: orderProductsSelector(state),
};
})(Basket);
const mapStateToProps = createStructuredSelector({
total: totalSelector,
orderProducts: orderProductsSelector,
});

export default connect(mapStateToProps)(Basket);
6 changes: 1 addition & 5 deletions src/components/menu/menu.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,7 @@ import styles from './menu.module.css';

class Menu extends React.Component {
static propTypes = {
menu: PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.string.isRequired,
}).isRequired
).isRequired,
menu: PropTypes.arrayOf(PropTypes.string.isRequired).isRequired,
};

state = { error: null };
Expand Down
13 changes: 5 additions & 8 deletions src/components/product/product.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import React, { useEffect } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { createStructuredSelector } from 'reselect';
import styles from './product.module.css';

import { decrement, increment } from '../../redux/actions';

import Button from '../button';
import { productAmountSelector, productSelector } from '../../redux/selectors';

const Product = ({ product, amount, increment, decrement, fetchData }) => {
useEffect(() => {
Expand Down Expand Up @@ -49,16 +51,11 @@ Product.propTypes = {
increment: PropTypes.func,
};

const mapStateToProps = (state, ownProps) => ({
amount: state.order[ownProps.id] || 0,
product: state.products[ownProps.id],
const mapStateToProps = createStructuredSelector({
amount: productAmountSelector,
product: productSelector,
});

// const mapDispatchToProps = {
// increment,
// decrement,
// };

const mapDispatchToProps = (dispatch, ownProps) => ({
increment: () => dispatch(increment(ownProps.id)),
decrement: () => dispatch(decrement(ownProps.id)),
Expand Down
36 changes: 16 additions & 20 deletions src/components/restaurant/restaurant.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,20 @@
import React, { useMemo } from 'react';
import React from 'react';
import { connect } 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';

const Restaurant = ({ restaurant }) => {
const { name, menu, reviews } = restaurant;

const averageRating = useMemo(() => {
const total = reviews.reduce((acc, { rating }) => acc + rating, 0);
return Math.round(total / reviews.length);
}, [reviews]);

const Restaurant = ({ id, name, menu, reviews, averageRating }) => {
const tabs = [
{ title: 'Menu', content: <Menu menu={menu} /> },
{ title: 'Reviews', content: <Reviews reviews={reviews} /> },
{
title: 'Reviews',
content: <Reviews reviews={reviews} restaurantId={id} />,
},
];

return (
Expand All @@ -30,15 +28,13 @@ const Restaurant = ({ restaurant }) => {
};

Restaurant.propTypes = {
restaurant: PropTypes.shape({
name: PropTypes.string,
menu: PropTypes.array,
reviews: PropTypes.arrayOf(
PropTypes.shape({
rating: PropTypes.number.isRequired,
}).isRequired
).isRequired,
}).isRequired,
id: PropTypes.string,
name: PropTypes.string,
menu: PropTypes.array,
reviews: PropTypes.array,
averageRating: PropTypes.number,
};

export default Restaurant;
export default connect((state, props) => ({
averageRating: averageRatingSelector(state, props),
}))(Restaurant);
5 changes: 3 additions & 2 deletions src/components/restaurants/restaurants.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import Restaurant from '../restaurant';
import Tabs from '../tabs';
import { restaurantsListSelector } from '../../redux/selectors';

const Restaurants = ({ restaurants }) => {
const tabs = restaurants.map((restaurant) => ({
title: restaurant.name,
content: <Restaurant restaurant={restaurant} />,
content: <Restaurant {...restaurant} />,
}));

return <Tabs tabs={tabs} />;
Expand All @@ -22,5 +23,5 @@ Restaurants.propTypes = {
};

export default connect((state) => ({
restaurants: state.restaurants,
restaurants: restaurantsListSelector(state),
}))(Restaurants);
15 changes: 11 additions & 4 deletions src/components/reviews/review-form/review-form.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import React from 'react';
import PropTypes from 'prop-types';
import useForm from '../../../hooks/use-form';

import Rate from '../../rate';
import styles from './review-form.module.css';
import { connect } from 'react-redux';
import Button from '../../button';
import { addReview } from '../../../redux/actions';

const INITIAL_VALUES = { name: '', text: '', rate: 5 };
const INITIAL_VALUES = { name: '', text: '', rating: 5 };

const ReviewForm = ({ onSubmit }) => {
const { values, handlers, reset } = useForm(INITIAL_VALUES);
Expand Down Expand Up @@ -38,7 +40,7 @@ const ReviewForm = ({ onSubmit }) => {
<div className={styles.rateWrap}>
<span>Rating: </span>
<span>
<Rate {...handlers.rate} />
<Rate {...handlers.rating} />
</span>
</div>
<div className={styles.publish}>
Expand All @@ -51,6 +53,11 @@ const ReviewForm = ({ onSubmit }) => {
);
};

export default connect(null, () => ({
onSubmit: (values) => console.log(values), // TODO
ReviewForm.propTypes = {
restaurantId: PropTypes.string,
onSubmit: PropTypes.func.isRequired,
};

export default connect(null, (dispatch, props) => ({
onSubmit: (review) => dispatch(addReview(review, props.restaurantId)),
}))(ReviewForm);
20 changes: 11 additions & 9 deletions src/components/reviews/review/review.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ import PropTypes from 'prop-types';

import Rate from '../../rate';
import styles from './review.module.css';
import { connect } from 'react-redux';
import { reviewWitUserSelector } from '../../../redux/selectors';

const Review = ({ user, text, rating }) => (
const Review = ({ review: { user = 'Anonymous', text, rating } }) => (
<div className={styles.review} data-id="review">
<div className={styles.content}>
<div>
Expand All @@ -23,13 +25,13 @@ const Review = ({ user, text, rating }) => (
);

Review.propTypes = {
user: PropTypes.string,
text: PropTypes.string,
rating: PropTypes.number.isRequired,
review: PropTypes.shape({
user: PropTypes.string,
text: PropTypes.string,
rating: PropTypes.number.isRequired,
}),
};

Review.defaultProps = {
user: 'Anonymous',
};

export default Review;
export default connect((state, props) => ({
review: reviewWitUserSelector(state, props),
}))(Review);
15 changes: 6 additions & 9 deletions src/components/reviews/reviews.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,20 @@ import Review from './review';
import ReviewForm from './review-form';
import styles from './reviews.module.css';

const Reviews = ({ reviews }) => {
const Reviews = ({ reviews, restaurantId }) => {
return (
<div className={styles.reviews}>
{reviews.map((review) => (
<Review key={review.id} {...review} />
{reviews.map((id) => (
<Review key={id} id={id} />
))}
<ReviewForm />
<ReviewForm restaurantId={restaurantId} />
</div>
);
};

Reviews.propTypes = {
reviews: PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.string.isRequired,
}).isRequired
).isRequired,
restaurantId: PropTypes.string.isRequired,
reviews: PropTypes.arrayOf(PropTypes.string.isRequired).isRequired,
};

export default Reviews;
8 changes: 7 additions & 1 deletion src/redux/actions.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import { INCREMENT, DECREMENT, REMOVE } from './constants';
import { INCREMENT, DECREMENT, REMOVE, ADD_REVIEW } from './constants';

export const increment = (id) => ({ type: INCREMENT, payload: { id } });
export const decrement = (id) => ({ type: DECREMENT, payload: { id } });
export const remove = (id) => ({ type: REMOVE, payload: { id } });

export const addReview = (review, restaurantId) => ({
type: ADD_REVIEW,
payload: { review, restaurantId },
generateId: ['reviewId', 'userId'],
});
1 change: 1 addition & 0 deletions src/redux/constants.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export const INCREMENT = 'INCREMENT';
export const DECREMENT = 'DECREMENT';
export const REMOVE = 'REMOVE';
export const ADD_REVIEW = 'ADD_REVIEW';
13 changes: 13 additions & 0 deletions src/redux/middleware/generateId.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { v4 as uuid } from 'uuid';

const generateId = (store) => (next) => (action) => {
if (!action.generateId) return next(action);

const { generateId, ...rest } = action;
next({
...rest,
...generateId.reduce((acc, key) => ({ ...acc, [key]: uuid() }), {}),
});
};

export default generateId;
2 changes: 2 additions & 0 deletions src/redux/reducer/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@ import order from './order';
import restaurants from './restaurants';
import products from './products';
import reviews from './reviews';
import users from './users';

const reducer = combineReducers({
order,
restaurants,
products,
reviews,
users,
});

export default reducer;
10 changes: 3 additions & 7 deletions src/redux/reducer/products.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
import { normalizedProducts } from '../../fixtures';

const defaultProducts = normalizedProducts.reduce(
(acc, product) => ({ ...acc, [product.id]: product }),
{}
);
import { arrToMap } from '../utils';

// { [productId]: product }
const reducer = (products = defaultProducts, action) => {
const reducer = (state = arrToMap(normalizedProducts), action) => {
const { type } = action;

switch (type) {
default:
return products;
return state;
}
};

Expand Down
20 changes: 16 additions & 4 deletions src/redux/reducer/restaurants.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,23 @@
import { normalizedRestaurants as defaultRestaurants } from '../../fixtures';
import { ADD_REVIEW } from '../constants';
import { normalizedRestaurants } from '../../fixtures';
import { arrToMap } from '../utils';

const reducer = (restaurants = defaultRestaurants, action) => {
const { type } = action;
const reducer = (state = arrToMap(normalizedRestaurants), action) => {
const { type, payload, reviewId } = action;

switch (type) {
case ADD_REVIEW:
const restaurant = state[payload.restaurantId];
return {
...state,
[payload.restaurantId]: {
...restaurant,
reviews: [...restaurant.reviews, reviewId],
},
};

default:
return restaurants;
return state;
}
};

Expand Down
16 changes: 12 additions & 4 deletions src/redux/reducer/reviews.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
import { normalizedReviews as defaultReviews } from '../../fixtures';
import { ADD_REVIEW } from '../constants';
import { normalizedReviews } from '../../fixtures';
import { arrToMap } from '../utils';

const reducer = (reviews = defaultReviews, action) => {
const { type } = action;
const reducer = (state = arrToMap(normalizedReviews), action) => {
const { type, payload, reviewId, userId } = action;

switch (type) {
case ADD_REVIEW:
const { text, rating } = payload.review;
return {
...state,
[reviewId]: { id: reviewId, userId, text, rating },
};
default:
return reviews;
return state;
}
};

Expand Down
21 changes: 21 additions & 0 deletions src/redux/reducer/users.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { ADD_REVIEW } from '../constants';
import { normalizedUsers } from '../../fixtures';
import { arrToMap } from '../utils';

const reducer = (state = arrToMap(normalizedUsers), action) => {
const { type, payload, userId } = action;

switch (type) {
case ADD_REVIEW:
const { name } = payload.review;
return {
...state,
[userId]: { id: userId, name },
};

default:
return state;
}
};

export default reducer;
Loading

0 comments on commit 672675b

Please sign in to comment.