Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ht4] normalisation fixes and review posts #65

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,5 @@
npm-debug.log*
yarn-debug.log*
yarn-error.log*

.idea/
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
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
20 changes: 12 additions & 8 deletions src/components/restaurant/restaurant.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import React, { useMemo } 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';

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

const averageRating = useMemo(() => {
const total = reviews.reduce((acc, { rating }) => acc + rating, 0);
Expand All @@ -33,12 +34,15 @@ Restaurant.propTypes = {
restaurant: PropTypes.shape({
name: PropTypes.string,
menu: PropTypes.array,
reviews: PropTypes.arrayOf(
PropTypes.shape({
rating: PropTypes.number.isRequired,
}).isRequired
).isRequired,
reviews: PropTypes.arrayOf(PropTypes.string.isRequired).isRequired,
}).isRequired,
};

export default Restaurant;
const mapStateToProps = (state, ownProps) => {
const { reviews } = ownProps.restaurant;
return {
reviews: reviews.map((id) => state.reviews[id]),
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

это нужно выносить в селекторы и мемоизировать.

};
};

export default connect(mapStateToProps)(Restaurant);
32 changes: 21 additions & 11 deletions src/components/restaurants/restaurants.js
Original file line number Diff line number Diff line change
@@ -1,26 +1,36 @@
import React from 'react';
import React, { useEffect } from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import Restaurant from '../restaurant';
import Tabs from '../tabs';
import { setActive } from '../../redux/actions';

const Restaurants = ({ restaurants }) => {
const tabs = restaurants.map((restaurant) => ({
const Restaurants = ({ restaurants, setActiveRestaurant }) => {
useEffect(() => {
setActiveRestaurant({ id: Object.keys(restaurants)[0] });
}, [restaurants, setActiveRestaurant]);

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

return <Tabs tabs={tabs} />;
return <Tabs tabs={tabs} onSetActiveCallback={setActiveRestaurant} />;
};

Restaurants.propTypes = {
restaurants: PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.string.isRequired,
}).isRequired
).isRequired,
restaurants: PropTypes.object.isRequired,
};

export default connect((state) => ({
const mapStateToProps = (state) => ({
restaurants: state.restaurants,
}))(Restaurants);
});

const mapDispatchToProps = (dispatch) => {
return {
setActiveRestaurant: (entity) => dispatch(setActive(entity.id)),
};
};

export default connect(mapStateToProps, mapDispatchToProps)(Restaurants);
17 changes: 12 additions & 5 deletions src/components/reviews/review-form/review-form.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,16 @@ 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 ReviewForm = ({ onSubmit }) => {
const ReviewForm = ({ onSubmit, activeRestaurant }) => {
const { values, handlers, reset } = useForm(INITIAL_VALUES);

const handleSubmit = (ev) => {
ev.preventDefault();
onSubmit(values);
onSubmit({ ...values, activeRestaurant });
reset();
};

Expand Down Expand Up @@ -51,6 +52,12 @@ const ReviewForm = ({ onSubmit }) => {
);
};

export default connect(null, () => ({
onSubmit: (values) => console.log(values), // TODO
}))(ReviewForm);
const mapStateToProps = (state) => ({
activeRestaurant: state.activeRestaurant,
});

const mapDispatchToProps = (dispatch) => ({
onSubmit: (values) => dispatch(addReview(values)),
});

export default connect(mapStateToProps, mapDispatchToProps)(ReviewForm);
8 changes: 7 additions & 1 deletion src/components/reviews/review/review.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';

import Rate from '../../rate';
Expand Down Expand Up @@ -32,4 +33,9 @@ Review.defaultProps = {
user: 'Anonymous',
};

export default Review;
const mapStateToProps = (state, ownProps) => {
const user = state.users[ownProps.userId];
return { user };
};

export default connect(mapStateToProps)(Review);
29 changes: 18 additions & 11 deletions src/components/tabs/tabs.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,30 @@ import cn from 'classnames';

import styles from './tabs.module.css';

const Tabs = ({ tabs }) => {
const Tabs = ({ tabs, onSetActiveCallback }) => {
const [activeTab, setActiveTab] = useState(0);

const { content } = tabs[activeTab];

return (
<>
<div className={styles.tabs}>
{tabs.map(({ title }, index) => (
<span
key={title}
className={cn(styles.tab, { [styles.active]: index === activeTab })}
onClick={() => setActiveTab(index)}
>
{title}
</span>
))}
{tabs.map((entity, index) => {
const { title } = entity;
return (
<span
key={title}
className={cn(styles.tab, {
[styles.active]: index === activeTab,
})}
onClick={() => {
setActiveTab(index);
onSetActiveCallback && onSetActiveCallback(entity);
}}
>
{title}
</span>
);
})}
</div>
{content}
</>
Expand Down
18 changes: 17 additions & 1 deletion src/redux/actions.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,21 @@
import { INCREMENT, DECREMENT, REMOVE } from './constants';
import {
INCREMENT,
DECREMENT,
REMOVE,
ADD_REVIEW,
SET_ACTIVE,
} from './constants';

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

/*form*/
export const addReview = (data) => ({ type: ADD_REVIEW, payload: { ...data } });

/*restaurant*/
export const setActive = (restaurantId) => ({
type: SET_ACTIVE,
payload: restaurantId,
});
7 changes: 7 additions & 0 deletions src/redux/constants.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
/*product*/
export const INCREMENT = 'INCREMENT';
export const DECREMENT = 'DECREMENT';
export const REMOVE = 'REMOVE';

/*form*/
export const ADD_REVIEW = 'ADD_REVIEW';

/*restaurant*/
export const SET_ACTIVE = 'SET_ACTIVE';
12 changes: 12 additions & 0 deletions src/redux/middleware/uuid-generator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { v1 as uuidv4 } from 'uuid';
import { ADD_REVIEW } from '../constants';

const uuidGenerator = () => (next) => (action) => {
if (action.type === ADD_REVIEW) {
action.payload.userId = uuidv4();
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

не нужно мутировать action

action.payload.id = uuidv4();
}
next(action);
};

export default uuidGenerator;
14 changes: 14 additions & 0 deletions src/redux/reducer/activeRestaurant.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { SET_ACTIVE } from '../constants';

const reducer = (activeRestaurant = '', action) => {
const { type } = action;

switch (type) {
case SET_ACTIVE:
return action.payload;
default:
return activeRestaurant;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

теперь знание о какой ресторан активный храниться в двух местах, в этом редьюсере и в компоненте

}
};

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

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

export default reducer;
13 changes: 12 additions & 1 deletion src/redux/reducer/restaurants.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
import { normalizedRestaurants as defaultRestaurants } from '../../fixtures';
import { normalizedRestaurants } from '../../fixtures';
import { ADD_REVIEW } from '../constants';

const defaultRestaurants = normalizedRestaurants.reduce(
(acc, restaurant) => ({ ...acc, [restaurant.id]: restaurant }),
{}
);

const reducer = (restaurants = defaultRestaurants, action) => {
const { type } = action;

switch (type) {
case ADD_REVIEW:
const { id, activeRestaurant } = action.payload;
const restaurant = restaurants[activeRestaurant];
restaurant.reviews.push(id);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

мутации в редьюсере недопустимы

return { ...restaurants };
default:
return restaurants;
}
Expand Down
14 changes: 13 additions & 1 deletion src/redux/reducer/reviews.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,21 @@
import { normalizedReviews as defaultReviews } from '../../fixtures';
import { normalizedReviews } from '../../fixtures';
import { ADD_REVIEW } from '../constants';

const defaultReviews = normalizedReviews.reduce(
(acc, review) => ({ ...acc, [review.id]: review }),
{}
);

const reducer = (reviews = defaultReviews, action) => {
const { type } = action;

switch (type) {
case ADD_REVIEW:
const { id, userId, text, rate } = action.payload;
return {
...reviews,
[action.payload.id]: { id, userId, text, rating: rate },
};
default:
return reviews;
}
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 { normalizedUsers } from '../../fixtures';
import { ADD_REVIEW } from '../constants';

const defaultUsers = normalizedUsers.reduce(
(acc, user) => ({ ...acc, [user.id]: user.name }),
{}
);

const reducer = (users = defaultUsers, action) => {
const { type } = action;

switch (type) {
case ADD_REVIEW:
const { userId, name } = action.payload;
return { ...users, [userId]: name };
default:
return users;
}
};

export default reducer;
3 changes: 2 additions & 1 deletion src/redux/store.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { applyMiddleware, createStore } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension';
import logger from './middleware/logger';
import uuidGenerator from './middleware/uuid-generator';

import reducer from './reducer';

const store = createStore(
reducer,
composeWithDevTools(applyMiddleware(logger))
composeWithDevTools(applyMiddleware(logger, uuidGenerator))
);

export default store;
2 changes: 1 addition & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -11256,7 +11256,7 @@ uuid@^3.3.2, uuid@^3.4.0:
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==

uuid@^8.3.0:
uuid@^8.3.0, uuid@^8.3.1:
version "8.3.1"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.1.tgz#2ba2e6ca000da60fce5a196954ab241131e05a31"
integrity sha512-FOmRr+FmWEIG8uhZv6C2bTgEVXsHk08kE7mPlrBbEe+c3r9pjceVPgupIfNIhc4yx55H69OXANrUaSuu9eInKg==
Expand Down