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

[ht6] links & nested routes #76

Open
wants to merge 12 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 @@ node_modules
npm-debug.log*
yarn-debug.log*
yarn-error.log*

.idea/
4 changes: 3 additions & 1 deletion src/components/basket/basket-item/basket-item.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,21 @@ import cn from 'classnames';
import { increment, decrement, remove } from '../../../redux/actions';
import Button from '../../button';
import styles from './basket-item.module.css';
import { Link } from 'react-router-dom';

function BasketItem({
product,
amount,
subtotal,
restaurantId,
increment,
decrement,
remove,
}) {
return (
<div className={styles.basketItem}>
<div className={styles.name}>
<span>{product.name}</span>
<Link to={`/restaurants/${restaurantId}`}>{product.name}</Link>
</div>
<div className={styles.info}>
<div className={styles.counter}>
Expand Down
3 changes: 2 additions & 1 deletion src/components/basket/basket.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,13 @@ function Basket({ title = 'Basket', total, orderProducts }) {
return (
<div className={styles.basket}>
<h4 className={styles.title}>{title}</h4>
{orderProducts.map(({ product, amount, subtotal }) => (
{orderProducts.map(({ product, amount, subtotal, restaurantId }) => (
<BasketItem
product={product}
amount={amount}
key={product.id}
subtotal={subtotal}
restaurantId={restaurantId}
/>
))}
<hr className={styles.hr} />
Expand Down
23 changes: 21 additions & 2 deletions src/components/restaurant/restaurant.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 { useRouteMatch, Switch, Route, Redirect } from 'react-router-dom';
import PropTypes from 'prop-types';
import { createStructuredSelector } from 'reselect';

Expand All @@ -11,20 +12,38 @@ import Tabs from '../tabs';
import { averageRatingSelector } from '../../redux/selectors';

const Restaurant = ({ id, name, menu, reviews, averageRating }) => {
const MENU = 'menu';
const REVIEWS = 'reviews';
const match = useRouteMatch();
const tabs = [
{ title: 'Menu', content: <Menu menu={menu} restaurantId={id} /> },
{
title: 'Menu',
id: `${id}_menu`,
to: `${match.url}/${MENU}`,
},
{
title: 'Reviews',
content: <Reviews reviews={reviews} restaurantId={id} />,
id: `${id}_reviews`,
to: `${match.url}/${REVIEWS}`,
},
];

/*as an option, active tab can be stored at redux store*/
return (
<div>
<Banner heading={name}>
{!!averageRating && <Rate value={averageRating} />}
</Banner>
<Tabs tabs={tabs} />
<Switch>
<Route path={`${match.path}/${REVIEWS}`} exact>
<Reviews reviews={reviews} restaurantId={id} />
</Route>
<Route path={`${match.path}/${MENU}`} exact>
<Menu menu={menu} restaurantId={id} />
</Route>
<Redirect to={`${match.url}/${MENU}`} />
</Switch>
</div>
);
};
Expand Down
22 changes: 8 additions & 14 deletions src/components/restaurants/restaurants.js
Original file line number Diff line number Diff line change
@@ -1,31 +1,25 @@
import React from 'react';
import { connect } from 'react-redux';
import { NavLink } from 'react-router-dom';
import { createStructuredSelector } from 'reselect';
import PropTypes from 'prop-types';
import Restaurant from '../restaurant';
import { restaurantsListSelector } from '../../redux/selectors';

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

const Restaurants = ({ restaurants, match }) => {
const { restId } = match.params;
const restaurant = restaurants.find((restaurant) => restaurant.id === restId);

const tabs = restaurants.map((restaurant) => ({
id: restaurant.id,
title: restaurant.name,
to: `/restaurants/${restaurant.id}`,
}));

return (
<>
<div className={styles.tabs}>
{restaurants.map(({ id, name }) => (
<NavLink
key={id}
to={`/restaurants/${id}`}
className={styles.tab}
activeClassName={styles.active}
>
{name}
</NavLink>
))}
</div>
<Tabs tabs={tabs} />
<Restaurant {...restaurant} />
</>
);
Expand Down
32 changes: 14 additions & 18 deletions src/components/tabs/tabs.js
Original file line number Diff line number Diff line change
@@ -1,28 +1,25 @@
import React, { useState } from 'react';
import React from 'react';
import PropTypes from 'prop-types';
import cn from 'classnames';

import styles from './tabs.module.css';
import { NavLink } from 'react-router-dom';

const Tabs = ({ tabs }) => {
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(({ id, title, to }) => {
return (
<NavLink
key={id}
to={to}
className={styles.tab}
activeClassName={styles.active}
>
{title}
</NavLink>
);
})}
</div>
{content}
</>
);
};
Expand All @@ -31,7 +28,6 @@ Tabs.propTypes = {
tabs: PropTypes.arrayOf(
PropTypes.shape({
title: PropTypes.string.isRequired,
content: PropTypes.element.isRequired,
}).isRequired
).isRequired,
};
Expand Down
4 changes: 3 additions & 1 deletion src/components/tabs/tabs.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
background-color: var(--grey);
}

.tabs span {
.tabs a {
cursor: pointer;
text-decoration: none;
color: #1d1d1d;
}

.tab {
Expand Down
15 changes: 9 additions & 6 deletions src/redux/selectors.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { createSelector } from 'reselect';
import { getById } from './utils';
import { findIdInListOfObjects, getById } from './utils';

const restaurantsSelector = (state) => state.restaurants.entities;
const productsSelector = (state) => state.products.entities;
Expand All @@ -24,17 +24,24 @@ export const reviewsLoadedSelector = (state, props) =>
export const usersLoadingSelector = (state) => state.users.loading;
export const usersLoadedSelector = (state) => state.users.loaded;

export const restaurantsListSelector = createSelector(
restaurantsSelector,
Object.values
);

export const orderProductsSelector = createSelector(
productsSelector,
orderSelector,
(products, order) => {
restaurantsListSelector,
(products, order, restaurants) => {
return Object.keys(order)
.filter((productId) => order[productId] > 0)
.map((productId) => products[productId])
.map((product) => ({
product,
amount: order[product.id],
subtotal: order[product.id] * product.price,
restaurantId: findIdInListOfObjects(restaurants, 'menu', product.id),
}));
}
);
Expand All @@ -45,10 +52,6 @@ export const totalSelector = createSelector(
orderProducts.reduce((acc, { subtotal }) => acc + subtotal, 0)
);

export const restaurantsListSelector = createSelector(
restaurantsSelector,
Object.values
);
export const productAmountSelector = getById(orderSelector, 0);
export const productSelector = getById(productsSelector);
const reviewSelector = getById(reviewsSelector);
Expand Down
7 changes: 7 additions & 0 deletions src/redux/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,10 @@ export const getById = (selector, defaultValue) =>
(_, props) => props.id,
(entity, id) => entity[id] || defaultValue
);

export const findIdInListOfObjects = (list, prop, id) => {
Copy link
Owner

Choose a reason for hiding this comment

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

эта операция выполняется для каждого елемента в корзине при любом изменени корзины. Это необходимо оптимизизовать, я покажу как

const restaurant = list.find((higher) => {
return higher[prop].find((item) => item === id);
});
return restaurant && restaurant.id;
};