diff --git a/src/components/reviews/review/review.module.css b/src/components/reviews/review/review.module.css
index 94455a8..7c20d46 100644
--- a/src/components/reviews/review/review.module.css
+++ b/src/components/reviews/review/review.module.css
@@ -55,3 +55,24 @@
line-height: 20px;
}
}
+
+.review-item-enter {
+ opacity: 0;
+ transform: scale(0.9);
+}
+
+.review-item-enter-active {
+ opacity: 1;
+ transform: translateX(0);
+ transition: opacity 5000ms, transform 5000ms;
+}
+
+.review-item-exit {
+ opacity: 1;
+}
+
+.review-item-exit-active {
+ opacity: 0;
+ transform: scale(0.9);
+ transition: opacity 5000ms, transform 5000ms;
+}
diff --git a/src/components/reviews/reviews.js b/src/components/reviews/reviews.js
index 2b09fda..d826542 100644
--- a/src/components/reviews/reviews.js
+++ b/src/components/reviews/reviews.js
@@ -5,12 +5,14 @@ import Review from './review';
import Loader from '../loader';
import ReviewForm from './review-form';
import styles from './reviews.module.css';
+import reviewStyles from './review/review.module.css';
import { loadReviews, loadUsers } from '../../redux/actions';
import {
reviewsLoadedSelector,
usersLoadedSelector,
} from '../../redux/selectors';
+import { CSSTransition, TransitionGroup } from 'react-transition-group';
const Reviews = ({
reviews,
@@ -27,11 +29,26 @@ const Reviews = ({
if (!usersLoaded || !reviewsLoaded) return
;
+ console.log('ff: ', reviewStyles);
+
return (
- {reviews.map((id) => (
-
- ))}
+
+ {reviews.map((id) => (
+
+
+
+ ))}
+
);
diff --git a/src/contexts/currency-context.js b/src/contexts/currency-context.js
new file mode 100644
index 0000000..f9cc2d1
--- /dev/null
+++ b/src/contexts/currency-context.js
@@ -0,0 +1,20 @@
+import { createContext } from 'react';
+
+export const DEFAULT_CURRENCY = 'USD';
+export const CURRENCIES = {
+ USD: 1,
+ RUB: 72,
+ BYN: 2.5,
+ UAH: 27,
+};
+
+export const currencyContext = createContext(DEFAULT_CURRENCY);
+
+export const CurrencyProvider = currencyContext.Provider;
+export const CurrencyConsumer = currencyContext.Consumer;
+
+export const toCurrency = (value) => (
+
+ {({ currency }) => `${value * CURRENCIES[currency]} ${currency}`}
+
+);
diff --git a/src/contexts/user-context.js b/src/contexts/user-context.js
index 6932695..b67cda2 100644
--- a/src/contexts/user-context.js
+++ b/src/contexts/user-context.js
@@ -4,3 +4,7 @@ export const userContext = createContext('Default user');
export const UserProvider = userContext.Provider;
export const UserConsumer = userContext.Consumer;
+
+export const printUser = () => (
+
{({ name }) => name}
+);
diff --git a/src/redux/actions.js b/src/redux/actions.js
index ef07010..9d39e09 100644
--- a/src/redux/actions.js
+++ b/src/redux/actions.js
@@ -1,4 +1,4 @@
-import { replace } from 'connected-react-router';
+import { replace, push } from 'connected-react-router';
import {
DECREMENT,
INCREMENT,
@@ -12,6 +12,7 @@ import {
REQUEST,
SUCCESS,
FAILURE,
+ SEND_ORDER,
} from './constants';
import {
@@ -19,6 +20,9 @@ import {
usersLoadedSelector,
reviewsLoadingSelector,
reviewsLoadedSelector,
+ locationSelector,
+ orderSelector,
+ orderLoadedSelector,
} from './selectors';
export const increment = (id) => ({ type: INCREMENT, id });
@@ -77,3 +81,39 @@ export const loadUsers = () => async (dispatch, getState) => {
dispatch(_loadUsers());
};
+
+export const checkout = () => async (dispatch, getState) => {
+ const state = getState();
+ const location = locationSelector(state);
+
+ if (location.pathname !== '/checkout') {
+ return dispatch(push('/checkout'));
+ }
+
+ dispatch({ type: SEND_ORDER + REQUEST });
+
+ try {
+ const order = orderSelector(state);
+ const orderData = Object.entries(order).map(([id, amount]) => ({
+ id,
+ amount: amount,
+ }));
+
+ const result = await fetch('/api/order', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify(orderData),
+ });
+
+ const info = await result.json();
+
+ dispatch({ type: SEND_ORDER + SUCCESS, info, status: result.status });
+
+ const loaded = orderLoadedSelector(getState());
+
+ dispatch(push(loaded ? '/success' : '/error'));
+ } catch (error) {
+ dispatch({ type: SEND_ORDER + FAILURE, error });
+ dispatch(push('/error'));
+ }
+};
diff --git a/src/redux/constants.js b/src/redux/constants.js
index 6429975..efb66de 100644
--- a/src/redux/constants.js
+++ b/src/redux/constants.js
@@ -10,6 +10,8 @@ export const LOAD_PRODUCTS = 'LOAD_PRODUCTS';
export const LOAD_REVIEWS = 'LOAD_REVIEWS';
export const LOAD_USERS = 'LOAD_USERS';
+export const SEND_ORDER = 'SEND_ORDER';
+
export const REQUEST = '_REQUEST';
export const SUCCESS = '_SUCCESS';
export const FAILURE = '_FAILURE';
diff --git a/src/redux/reducer/order.js b/src/redux/reducer/order.js
index e20530c..3ae0e14 100644
--- a/src/redux/reducer/order.js
+++ b/src/redux/reducer/order.js
@@ -1,16 +1,56 @@
-import { DECREMENT, INCREMENT, REMOVE } from '../constants';
+import produce from 'immer';
+import {
+ DECREMENT,
+ INCREMENT,
+ REMOVE,
+ SEND_ORDER,
+ REQUEST,
+ SUCCESS,
+ FAILURE,
+} from '../constants';
-// { [productId]: amount }
-export default function (state = {}, action) {
- const { type, id } = action;
- switch (type) {
- case INCREMENT:
- return { ...state, [id]: (state[id] || 0) + 1 };
- case DECREMENT:
- return { ...state, [id]: state[id] > 0 ? (state[id] || 0) - 1 : 0 };
- case REMOVE:
- return { ...state, [id]: 0 };
- default:
- return state;
- }
-}
+const initialState = {
+ entities: {},
+ loading: false,
+ loaded: false,
+ info: null,
+ error: null,
+};
+
+export default (state = initialState, action) =>
+ produce(state, (draft) => {
+ const { type, id, info, error, status = 200 } = action;
+ const entities = draft.entities;
+
+ switch (type) {
+ case INCREMENT:
+ entities[id] = (entities[id] || 0) + 1;
+ break;
+ case DECREMENT:
+ entities[id] = entities[id] > 0 ? (entities[id] || 0) - 1 : 0;
+ break;
+ case REMOVE:
+ entities[id] = 0;
+ break;
+ case SEND_ORDER + REQUEST: {
+ draft.loading = true;
+ draft.info = null;
+ draft.error = null;
+ break;
+ }
+ case SEND_ORDER + SUCCESS: {
+ draft.loading = false;
+ draft.loaded = status === 200;
+ draft.info = info;
+ break;
+ }
+ case SEND_ORDER + FAILURE: {
+ draft.loading = false;
+ draft.loaded = false;
+ draft.error = error;
+ break;
+ }
+ default:
+ return;
+ }
+ });
diff --git a/src/redux/selectors.js b/src/redux/selectors.js
index 94b62a0..0083841 100644
--- a/src/redux/selectors.js
+++ b/src/redux/selectors.js
@@ -2,10 +2,11 @@ import { createSelector } from 'reselect';
const restaurantsSelector = (state) => state.restaurants.entities;
const productsSelector = (state) => state.products.entities;
-const orderSelector = (state) => state.order;
const reviewsSelector = (state) => state.reviews.entities;
const usersSelector = (state) => state.users.entities;
+const routerSelector = (state) => state.router;
+export const orderSelector = (state) => state.order.entities;
export const activeIdRestaurantSelector = (state) => state.restaurants.activeId;
export const restaurantsLoadingSelector = (state) => state.restaurants.loading;
export const restaurantsLoadedSelector = (state) => state.restaurants.loaded;
@@ -15,6 +16,11 @@ export const productsLoadingSelector = (state, props) =>
export const productsLoadedSelector = (state, props) =>
state.products.loaded[props.restId];
+export const orderLoadingSelector = (state) => state.order.loading;
+export const orderLoadedSelector = (state) => state.order.loaded;
+export const orderInfoSelector = (state) => state.order.info;
+export const orderErrorSelector = (state) => state.order.error;
+
export const reviewsLoadingSelector = (state, props) =>
state.reviews.loading[props.restId];
export const reviewsLoadedSelector = (state, props) =>
@@ -88,3 +94,7 @@ export const averageRatingSelector = createSelector(
);
}
);
+
+export const locationSelector = createSelector(routerSelector, (router) => {
+ return router.location;
+});