diff --git a/src/components/app/app.js b/src/components/app/app.js
index 8c56569..865ee0d 100644
--- a/src/components/app/app.js
+++ b/src/components/app/app.js
@@ -1,5 +1,5 @@
import React, { useState } from 'react';
-import { Route, Switch } from 'react-router-dom';
+import { Route, Switch, Redirect } from 'react-router-dom';
import RestaurantsPage from '../../pages/restaurants-page';
import Header from '../header';
import Basket from '../basket';
@@ -13,10 +13,10 @@ const App = () => {
-
+
Error Page
} />
- '404 - Not found'} />
+
diff --git a/src/components/basket/basket.js b/src/components/basket/basket.js
index f37d68b..85af38a 100644
--- a/src/components/basket/basket.js
+++ b/src/components/basket/basket.js
@@ -1,6 +1,6 @@
-import React from 'react';
+import React, { useMemo } from 'react';
import { connect } from 'react-redux';
-import { Link } from 'react-router-dom';
+import { Link, withRouter } from 'react-router-dom';
import { createStructuredSelector } from 'reselect';
import { CSSTransition, TransitionGroup } from 'react-transition-group';
@@ -10,11 +10,49 @@ 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 {
+ orderProductsSelector,
+ totalSelector,
+ locationSelector,
+ orderLoadingSelector,
+ errorOrderSelector,
+ successOrderSelector,
+} from '../../redux/selectors';
import { UserConsumer } from '../../contexts/user-context';
+import { takeOrder } from '../../redux/actions';
+import LoadBanner from '../loadBanner';
-function Basket({ title = 'Basket', total, orderProducts }) {
+function Basket({
+ title = 'Basket',
+ total,
+ orderProducts,
+ loading,
+ location,
+ history,
+ takeOrder,
+ errorOrder,
+ successOrderMessage,
+}) {
// const { name } = useContext(userContext);
+ const canTakeOrder = useMemo(() => {
+ return location.pathname === '/checkout';
+ }, [location]);
+
+ function onBtnHandler() {
+ if (canTakeOrder) {
+ takeOrder();
+ } else {
+ history.push('/checkout');
+ }
+ }
+
+ if (successOrderMessage) {
+ return (
+
+
{successOrderMessage}
+
+ );
+ }
if (!total) {
return (
@@ -26,7 +64,7 @@ function Basket({ title = 'Basket', total, orderProducts }) {
return (
- {/*
{`${name}'s ${title}`}
*/}
+ {loading &&
}
{({ name }) => `${name}'s ${title}`}
@@ -55,11 +93,10 @@ function Basket({ title = 'Basket', total, orderProducts }) {
{`${total} $`}
-
-
-
+ {errorOrder}
+
);
}
@@ -67,6 +104,10 @@ function Basket({ title = 'Basket', total, orderProducts }) {
const mapStateToProps = createStructuredSelector({
total: totalSelector,
orderProducts: orderProductsSelector,
+ location: locationSelector,
+ loading: orderLoadingSelector,
+ errorOrder: errorOrderSelector,
+ successOrderMessage: successOrderSelector,
});
-export default connect(mapStateToProps)(Basket);
+export default connect(mapStateToProps, { takeOrder })(withRouter(Basket));
diff --git a/src/components/loadBanner/index.js b/src/components/loadBanner/index.js
new file mode 100644
index 0000000..114ac42
--- /dev/null
+++ b/src/components/loadBanner/index.js
@@ -0,0 +1 @@
+export { default } from './loadBanner';
diff --git a/src/components/loadBanner/loadBanner.js b/src/components/loadBanner/loadBanner.js
new file mode 100644
index 0000000..c14a1e4
--- /dev/null
+++ b/src/components/loadBanner/loadBanner.js
@@ -0,0 +1,13 @@
+import React from 'react';
+import Loader from '../loader';
+import styles from './loadBanner.module.css';
+
+function LoadBanner() {
+ return (
+
+
+
+ );
+}
+
+export default LoadBanner;
diff --git a/src/components/loadBanner/loadBanner.module.css b/src/components/loadBanner/loadBanner.module.css
new file mode 100644
index 0000000..a7a3120
--- /dev/null
+++ b/src/components/loadBanner/loadBanner.module.css
@@ -0,0 +1,10 @@
+.loadBanner {
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ top: 0;
+ left: 0;
+ display: flex;
+ align-items: center;
+ background: rgb(0 0 0 / 18%);
+}
diff --git a/src/pages/restaurants-page.js b/src/pages/restaurants-page.js
index 9e8e1ed..dfb748c 100644
--- a/src/pages/restaurants-page.js
+++ b/src/pages/restaurants-page.js
@@ -1,6 +1,6 @@
-import React, { useEffect } from 'react';
+import React, { useEffect, useMemo } from 'react';
import { connect } from 'react-redux';
-import { Route } from 'react-router-dom';
+import { Route, Redirect } from 'react-router-dom';
import { createStructuredSelector } from 'reselect';
import Restaurants from '../components/restaurants';
import Loader from '../components/loader';
@@ -12,20 +12,25 @@ import {
} from '../redux/selectors';
import { loadRestaurants } from '../redux/actions';
-function RestaurantsPage({ loading, loaded, loadRestaurants, match }) {
+function RestaurantsPage({
+ restaurants,
+ loading,
+ loaded,
+ loadRestaurants,
+ match,
+}) {
useEffect(() => {
if (!loading && !loaded) loadRestaurants();
}, [loading, loaded, loadRestaurants]);
+ const firstRestaurantId = useMemo(() => {
+ return restaurants.length ? restaurants[0].id : null;
+ }, [restaurants]);
+
if (loading || !loaded) return ;
- if (match.isExact) {
- return (
- <>
-
- Select restaurant
- >
- );
+ if (match.isExact && firstRestaurantId) {
+ return ;
}
return ;
diff --git a/src/redux/actions.js b/src/redux/actions.js
index 60ecd64..d5b1eed 100644
--- a/src/redux/actions.js
+++ b/src/redux/actions.js
@@ -7,12 +7,18 @@ import {
LOAD_REVIEWS,
LOAD_PRODUCTS,
LOAD_USERS,
+ ORDER_SUCCESS,
+ ORDER_ERROR,
+ ORDER_LOADING_TOGGLE,
+ CLEAN_OUT,
} from './constants';
+import products from './reducer/products';
import {
usersLoadingSelector,
usersLoadedSelector,
reviewsLoadingSelector,
reviewsLoadedSelector,
+ orderSelector,
} from './selectors';
export const increment = (id) => ({ type: INCREMENT, payload: { id } });
@@ -63,3 +69,47 @@ export const loadUsers = () => async (dispatch, getState) => {
dispatch(_loadUsers());
};
+
+export const takeOrder = () => async (dispatch, getState) => {
+ const state = orderSelector(getState());
+ const order = Object.keys(state).map((productId) => {
+ return { id: productId, amount: state[productId] };
+ });
+
+ dispatch(orderLoading(true));
+
+ await fetch('/api/order', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify(order),
+ })
+ .then((res) => {
+ return res.json();
+ })
+ .then((res) => {
+ dispatch(orderLoading(false));
+ if (res === 'ok') {
+ dispatch(orderSuccess());
+
+ return;
+ }
+
+ dispatch(orderError(res));
+ })
+ .catch(dispatch(orderError));
+};
+
+const orderSuccess = () => ({
+ type: CLEAN_OUT,
+ payload: {},
+});
+
+const orderLoading = (isLoad) => ({
+ type: ORDER_LOADING_TOGGLE,
+ payload: { loading: isLoad },
+});
+
+const orderError = (error) => ({
+ type: ORDER_ERROR,
+ payload: { error },
+});
diff --git a/src/redux/constants.js b/src/redux/constants.js
index 27ba575..1342ad4 100644
--- a/src/redux/constants.js
+++ b/src/redux/constants.js
@@ -11,3 +11,7 @@ export const LOAD_USERS = 'LOAD_USERS';
export const REQUEST = '_REQUEST';
export const SUCCESS = '_SUCCESS';
export const FAILURE = '_FAILURE';
+
+export const ORDER_ERROR = 'ORDER_ERROR';
+export const ORDER_LOADING_TOGGLE = 'ORDER_LOADING_TOGGLE';
+export const CLEAN_OUT = 'CLEAN_OUT';
diff --git a/src/redux/reducer/order.js b/src/redux/reducer/order.js
index 8837726..3d701e9 100644
--- a/src/redux/reducer/order.js
+++ b/src/redux/reducer/order.js
@@ -1,20 +1,65 @@
-import { DECREMENT, INCREMENT, REMOVE } from '../constants';
+import {
+ DECREMENT,
+ INCREMENT,
+ REMOVE,
+ ORDER_LOADING_TOGGLE,
+ ORDER_ERROR,
+ CLEAN_OUT,
+} from '../constants';
// { [productId]: amount }
-export default (state = {}, action) => {
- const { type, payload } = action;
+
+const initialState = {
+ entities: {},
+ loading: false,
+ error: null,
+ success: null,
+ // payload: 0,
+};
+
+export default (state = initialState, action) => {
+ const { type, payload, loading } = action;
switch (type) {
case INCREMENT:
- return { ...state, [payload.id]: (state[payload.id] || 0) + 1 };
+ return {
+ ...state,
+ entities: {
+ ...state.entities,
+ [payload.id]: (state.entities[payload.id] || 0) + 1,
+ },
+ };
case DECREMENT:
return {
...state,
- [payload.id]: Math.max((state[payload.id] || 0) - 1, 0),
+ entities: {
+ ...state.entities,
+ [payload.id]: Math.max((state.entities[payload.id] || 0) - 1, 0),
+ },
};
case REMOVE:
return {
...state,
- [payload.id]: 0,
+ entities: {
+ ...state.entities,
+ [payload.id]: 0,
+ },
+ };
+ case ORDER_LOADING_TOGGLE:
+ return {
+ ...state,
+ loading: payload.loading,
+ };
+ case ORDER_ERROR:
+ return {
+ ...state,
+ error: payload.error,
+ };
+ case CLEAN_OUT:
+ return {
+ ...state,
+ entities: {},
+ success: 'Заказ сформирован',
+ error: null,
};
default:
return state;
diff --git a/src/redux/selectors.js b/src/redux/selectors.js
index ca4f2a9..73533b0 100644
--- a/src/redux/selectors.js
+++ b/src/redux/selectors.js
@@ -2,10 +2,20 @@ import { createSelector } from 'reselect';
import { getById } from './utils';
const restaurantsSelector = (state) => state.restaurants.entities;
-const orderSelector = (state) => state.order;
+export const orderSelector = (state) => state.order.entities;
const productsSelector = (state) => state.products.entities;
const reviewsSelector = (state) => state.reviews.entities;
const usersSelector = (state) => state.users.entities;
+const historyRouterSelector = (state) => state.router;
+
+export const orderLoadingSelector = (state) => state.order.loading;
+export const errorOrderSelector = (state) => state.order.error;
+export const successOrderSelector = (state) => state.order.success;
+
+export const locationSelector = createSelector(
+ historyRouterSelector,
+ (history) => history.location
+);
export const restaurantsLoadingSelector = (state) => state.restaurants.loading;
export const restaurantsLoadedSelector = (state) => state.restaurants.loaded;