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

HT7 #56

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open

HT7 #56

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,21 @@
5. При загрузках показывать лоадеры, все грузить максимально низко, там где эти данные нужны
6. Все данные грузить только один раз (не загружать повторно данные, которые уже есть)
7. (Опционально) переписать все на **immer**

# HT6

1. Сделать reviews/menu отдельными роутами (/restaurants/:id/reviews)
2. В корзине сделать продукты линками на их ресторан

## HT7

1. Сделать редирект со **/** и с **/restaurants** на страницу ресторана
2. Проверить если мы на **/checkout**, то при нажатии на кнопку:

- отправить **POST** запрос на: '/api/order' с **JSON** формата [{id: "d75f762a-eadd-49be-8918-ed0daa8dd024", amount: 2}]
- блокировать кнопку на время запроса (можно добавить лоадер)
- при успешном ответе (при сумме заказа от 50 до 200) редиректить на новую страницу "Спасибо за заказ!" и очищать корзину
- при ошибке редиректить на странцу ошибки, показать текст ошибки

3. Реализовать переключение валюты, хранить словарь словарь в контексте (минимум 3 валюты)
4. Анимировать добавление ревью использус css modules
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"@wojtekmaj/enzyme-adapter-react-17": "^0.4.1",
"classnames": "^2.2.6",
"concurrently": "^5.3.0",
"connected-react-router": "^6.8.0",
"enzyme": "^3.11.0",
"immer": "^8.0.0",
"normalize.css": "^8.0.1",
Expand All @@ -19,6 +20,7 @@
"react-redux": "^7.2.2",
"react-router-dom": "^5.2.0",
"react-scripts": "4.0.1",
"react-transition-group": "^4.4.1",
"redux": "^4.0.5",
"redux-devtools-extension": "^2.13.8",
"redux-thunk": "^2.3.0",
Expand Down
17 changes: 17 additions & 0 deletions simple_api/api/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,21 @@ router.get('/users', (req, res, next) => {
reply(res, users);
});

const min = (m) => `you ordered for $${m}, but the min order amount is $50`;
const max = (m) => `you ordered for $${m}, but the max order amount is $200`;

router.post('/order', function (req, res, next) {
try {
const total = req.body
.map((it) => products.find((p) => p.id === it.id).price * it.amount)
.reduce((acc, next) => acc + next, 0);

if (total < 50) return reply(res, min(total), 3000, 400);
if (total > 200) return reply(res, max(total), 3000, 400);
return reply(res, 'ok', 3000);
} catch {
return reply(res, 'wrong data', 1000, 400);
}
});

module.exports = router;
29 changes: 17 additions & 12 deletions src/components/app/app.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,26 @@
import React, { PureComponent } from 'react';
import { Route, Switch } from 'react-router-dom';
import React, { useState } from 'react';
import { Route, Switch, Redirect } from 'react-router-dom';
import RestaurantsPage from '../../pages/restaurants-page';
import Header from '../header';
import Basket from '../basket';
import { UserProvider } from '../../contexts/user-context';

export default class App extends PureComponent {
render() {
return (
<div>
const App = () => {
const [name, setName] = useState('Igor');

return (
<div>
<UserProvider value={{ name, setName }}>
<Header />
<Switch>
<Route path="/" exact component={() => 'Main page'} />
<Route path="/checkout" component={Basket} />
<Route path="/restaurants" component={RestaurantsPage} />
<Route path="/" component={() => '404 - Not found'} />
<Route path="/error" component={() => <h1>Error Page</h1>} />
<Route path="/" component={() => <Redirect to="/restaurants" />} />
</Switch>
</div>
);
}
}
</UserProvider>
</div>
);
};

export default App;
6 changes: 5 additions & 1 deletion src/components/basket/basket-item/basket-item.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 { Link } from 'react-router-dom';
import cn from 'classnames';
import { increment, decrement, remove } from '../../../redux/actions';
import Button from '../../button';
Expand All @@ -9,14 +10,17 @@ 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}/menu`}>
<span>{product.name}</span>
</Link>
</div>
<div className={styles.info}>
<div className={styles.counter}>
Expand Down
20 changes: 20 additions & 0 deletions src/components/basket/basket.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
.basket-animation-enter {
opacity: 0;
transform: scale(0.9);
}

.basket-animation-enter-active {
opacity: 1;
transform: translateX(0);
transition: opacity 2000ms, transform 2000ms;
}

.basket-animation-exit {
opacity: 1;
}

.basket-animation-exit-active {
opacity: 0;
transform: scale(0.9);
transition: opacity 2000ms, transform 2000ms;
}
43 changes: 33 additions & 10 deletions src/components/basket/basket.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,22 @@ import React from 'react';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';
import { createStructuredSelector } from 'reselect';
import { CSSTransition, TransitionGroup } from 'react-transition-group';

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

import itemStyles from './basket-item/basket-item.module.css';
import BasketItem from './basket-item';
import Button from '../button';
import { orderProductsSelector, totalSelector } from '../../redux/selectors';
import { UserConsumer } from '../../contexts/user-context';

import { CurrencyConsumer } from '../../contexts/currency-context';

function Basket({ title = 'Basket', total, orderProducts }) {
// const { name } = useContext(userContext);

if (!total) {
return (
<div className={styles.basket}>
Expand All @@ -20,22 +28,37 @@ function Basket({ title = 'Basket', total, orderProducts }) {

return (
<div className={styles.basket}>
<h4 className={styles.title}>{title}</h4>
{orderProducts.map(({ product, amount, subtotal }) => (
<BasketItem
product={product}
amount={amount}
key={product.id}
subtotal={subtotal}
/>
))}
{/* <h4 className={styles.title}>{`${name}'s ${title}`}</h4> */}
<h4 className={styles.title}>
<UserConsumer>{({ name }) => `${name}'s ${title}`}</UserConsumer>
</h4>
<TransitionGroup>
{orderProducts.map(({ product, amount, subtotal, restaurantId }) => (
<CSSTransition
key={product.id}
timeout={2000}
classNames="basket-animation"
>
<BasketItem
product={product}
amount={amount}
subtotal={subtotal}
restaurantId={restaurantId}
/>
</CSSTransition>
))}
</TransitionGroup>
<hr className={styles.hr} />
<div className={itemStyles.basketItem}>
<div className={itemStyles.name}>
<p>Total</p>
</div>
<div className={itemStyles.info}>
<p>{`${total} $`}</p>
<p>
<CurrencyConsumer>
{({ currency }) => `${total} ${currency}`}
</CurrencyConsumer>
</p>
</div>
</div>
<Link to="/checkout">
Expand Down
17 changes: 11 additions & 6 deletions src/components/header/header.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
import React from 'react';
import React, { useContext } from 'react';

import Logo from './logo';
import styles from './header.module.css';
import { userContext } from '../../contexts/user-context';

const Header = () => (
<header className={styles.header}>
<Logo />
</header>
);
const Header = () => {
const { name, setName } = useContext(userContext);
return (
<header className={styles.header} onClick={() => setName('Ivan')}>
<Logo />
<h2>{name}</h2>
</header>
);
};

export default Header;
8 changes: 8 additions & 0 deletions src/components/header/header.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,12 @@
justify-content: center;
height: 60px;
background: var(--black);
position: relative;
}

.header h2 {
position: absolute;
color: var(--white);
right: 20px;
top: 0;
}
10 changes: 9 additions & 1 deletion src/components/product/product.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import PropTypes from 'prop-types';
import { createStructuredSelector } from 'reselect';
import styles from './product.module.css';

import { CurrencyConsumer } from '../../contexts/currency-context';

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

import Button from '../button';
Expand All @@ -18,7 +20,13 @@ const Product = ({ product, amount, increment, decrement }) => {
<div>
<h4 className={styles.title}>{product.name}</h4>
<p className={styles.description}>{product.ingredients.join(', ')}</p>
<div className={styles.price}>{product.price} $</div>
<div className={styles.price}>
<CurrencyConsumer>
{({ currency, coefficient }) =>
`${product.price * coefficient} ${currency}`
}
</CurrencyConsumer>
</div>
</div>
<div>
<div className={styles.counter}>
Expand Down
23 changes: 18 additions & 5 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 { Route, Redirect, Switch } from 'react-router-dom';
import PropTypes from 'prop-types';
import { createStructuredSelector } from 'reselect';

Expand All @@ -13,11 +14,8 @@ import { averageRatingSelector } from '../../redux/selectors';
const Restaurant = ({ restaurant, averageRating }) => {
const { id, name, menu, reviews } = restaurant;
const tabs = [
{ title: 'Menu', content: <Menu menu={menu} restaurantId={id} /> },
{
title: 'Reviews',
content: <Reviews reviews={reviews} restaurantId={id} />,
},
{ title: 'Menu', to: `/restaurants/${id}/menu` },
{ title: 'Reviews', to: `/restaurants/${id}/reviews` },
];

return (
Expand All @@ -26,6 +24,21 @@ const Restaurant = ({ restaurant, averageRating }) => {
{!!averageRating && <Rate value={averageRating} />}
</Banner>
<Tabs tabs={tabs} />
<Switch>
<Route
path="/restaurants/:restId/menu"
render={() => <Menu menu={menu} restaurantId={id} />}
/>
<Route
path="/restaurants/:restId/reviews"
render={() => <Reviews reviews={reviews} restaurantId={id} />}
/>
<Redirect
exact
from="/restaurants/:restId"
to={`/restaurants/${id}/menu`}
/>
</Switch>
</div>
);
};
Expand Down
24 changes: 8 additions & 16 deletions src/components/restaurants/restaurants.js
Original file line number Diff line number Diff line change
@@ -1,32 +1,24 @@
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 Tabs from '../tabs';
import Restaurant from '../restaurant';
import { restaurantsListSelector } from '../../redux/selectors';

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

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

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

return (
<>
<div className={styles.tabs}>
{restaurants.map(({ id, name }, index) => (
<NavLink
key={id}
className={styles.tab}
to={`/restaurants/${id}`}
activeClassName={styles.active}
>
{name}
</NavLink>
))}
</div>
<Restaurant restaurant={restaurant} />
<Tabs tabs={tabs} />
{restaurant && <Restaurant restaurant={restaurant} />}
</>
);
};
Expand Down
20 changes: 0 additions & 20 deletions src/components/restaurants/restaurants.module.css

This file was deleted.

Loading