From 76688a44b92f66ef47c395ea9815355b69411f69 Mon Sep 17 00:00:00 2001 From: bebriks Date: Tue, 17 Dec 2024 14:38:18 +0500 Subject: [PATCH 1/4] added all func --- .env.example | 1 - .storybook/main.ts | 40 ------ .storybook/preview.tsx | 26 ---- README.md | 3 - package-lock.json | 106 ++++++++++---- package.json | 4 +- src/components/app-header/app-header.tsx | 7 +- src/components/app/app.tsx | 135 ++++++++++++++++-- .../burger-constructor-element.tsx | 36 ++++- .../burger-constructor/burger-constructor.tsx | 53 +++++-- .../burger-ingredient/burger-ingredient.tsx | 7 +- .../burger-ingredients/burger-ingredients.tsx | 23 ++- src/components/feed-info/feed-info.tsx | 15 +- .../ingredient-details/ingredient-details.tsx | 17 ++- .../ingredients-category.tsx | 10 +- src/components/order-card/order-card.tsx | 10 +- src/components/order-info/order-info.tsx | 117 +++++++++++++-- src/components/profile-menu/profile-menu.tsx | 10 +- src/components/protected-route/index.tsx | 29 ++++ .../protected-route/protected-route.tsx | 71 +++++++++ src/components/ui/app-header/app-header.tsx | 83 +++++++---- src/components/ui/feed-info/feed-info.tsx | 1 - .../ingredient-details/ingredient-details.tsx | 1 - src/components/ui/pages/profile/profile.tsx | 2 +- src/index.tsx | 11 +- .../constructor-page/constructor-page.tsx | 5 +- src/pages/feed/feed.tsx | 24 +++- src/pages/login/login.tsx | 15 +- src/pages/profile-orders/profile-orders.tsx | 15 +- src/pages/profile/profile.tsx | 12 +- src/pages/register/register.tsx | 11 ++ src/services/slices/constructor-slice.tsx | 44 ++++++ src/services/slices/feeds-slice.tsx | 47 ++++++ src/services/slices/ingredients-slice.tsx | 46 ++++++ src/services/slices/new-order-slice.tsx | 38 +++++ src/services/slices/user-all-orders-slice.tsx | 34 +++++ src/services/slices/user-slice.tsx | 91 ++++++++++++ src/services/store.ts | 20 ++- src/utils/burger-api.ts | 27 ++-- src/utils/cookie.ts | 1 - webpack.config.js | 6 +- 41 files changed, 1027 insertions(+), 227 deletions(-) delete mode 100644 .env.example delete mode 100644 .storybook/main.ts delete mode 100644 .storybook/preview.tsx create mode 100644 src/components/protected-route/index.tsx create mode 100644 src/components/protected-route/protected-route.tsx create mode 100644 src/services/slices/constructor-slice.tsx create mode 100644 src/services/slices/feeds-slice.tsx create mode 100644 src/services/slices/ingredients-slice.tsx create mode 100644 src/services/slices/new-order-slice.tsx create mode 100644 src/services/slices/user-all-orders-slice.tsx create mode 100644 src/services/slices/user-slice.tsx diff --git a/.env.example b/.env.example deleted file mode 100644 index a0a8a7d..0000000 --- a/.env.example +++ /dev/null @@ -1 +0,0 @@ -BURGER_API_URL=https://norma.nomoreparties.space/api \ No newline at end of file diff --git a/.storybook/main.ts b/.storybook/main.ts deleted file mode 100644 index 4702269..0000000 --- a/.storybook/main.ts +++ /dev/null @@ -1,40 +0,0 @@ -import type { StorybookConfig } from '@storybook/react-webpack5'; -import path from 'path'; - -const config: StorybookConfig = { - stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'], - addons: [ - '@storybook/addon-links', - '@storybook/addon-essentials', - '@storybook/addon-onboarding', - '@storybook/addon-interactions' - ], - webpackFinal: async (config) => { - config.resolve - ? (config.resolve.alias = { - ...config.resolve.alias, - '@pages': path.resolve(__dirname, '../src/pages'), - '@components': path.resolve(__dirname, '../src/components'), - '@ui': path.resolve(__dirname, '../src/components/ui'), - '@ui-pages': path.resolve(__dirname, '../src/components/ui/pages'), - '@utils-types': path.resolve(__dirname, '../src/utils/types'), - '@api': path.resolve(__dirname, '../src/utils/burger-api.ts'), - '@slices': path.resolve(__dirname, '../src/services/slices'), - '@selectors': path.resolve(__dirname, '../src/services/selectors') - }) - : null; - return config; - }, - framework: { - name: '@storybook/react-webpack5', - options: { - builder: { - useSWC: true - } - } - }, - docs: { - autodocs: 'tag' - } -}; -export default config; diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx deleted file mode 100644 index 2900d98..0000000 --- a/.storybook/preview.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import React from 'react'; -import type { Preview } from '@storybook/react'; -import { BrowserRouter } from 'react-router-dom'; - -const preview: Preview = { - parameters: { - actions: { argTypesRegex: '^on[A-Z].*' }, - controls: { - matchers: { - color: /(background|color)$/i, - date: /Date$/i - } - } - }, - decorators: [ - (Story) => ( - -
- -
-
- ) - ] -}; - -export default preview; diff --git a/README.md b/README.md index 9da336d..e1e959f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,3 @@ -# x курс -# Фамилия Имя Отчество - # Проектная работа 11-го спринта [Макет]() diff --git a/package-lock.json b/package-lock.json index fbccaba..74f2c11 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,7 +23,8 @@ "react-dom": "^18.2.0", "react-intersection-observer": "^9.4.3", "react-redux": "^9.1.0", - "react-router-dom": "^6.10.0", + "react-router-dom": "^6.28.0", + "redux": "^5.0.1", "redux-thunk": "^3.1.0", "typescript": "^5.3.3", "uuid": "^9.0.0", @@ -52,6 +53,7 @@ "@types/node": "^20.10.5", "@types/react": "^18.2.45", "@types/react-dom": "^18.2.18", + "@types/react-router-dom": "^5.3.3", "@types/react-test-renderer": "^18.0.7", "@types/webpack-env": "^1.18.4", "@typescript-eslint/eslint-plugin": "^6.15.0", @@ -4754,9 +4756,9 @@ } }, "node_modules/@remix-run/router": { - "version": "1.14.2", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.14.2.tgz", - "integrity": "sha512-ACXpdMM9hmKZww21yEqWwiLws/UPLhNKvimN8RrYSqPSvB3ov7sLvAcfvaxePeLvccTQKGdkDIhLYApZVDFuKg==", + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.21.0.tgz", + "integrity": "sha512-xfSkCAchbdG5PnbrKqFWwia4Bi61nH+wm8wLEqfHDyp7Y3dZzgqS2itV8i4gAq9pC2HsTpwyBC6Ds8VHZ96JlA==", "engines": { "node": ">=14.0.0" } @@ -7804,6 +7806,12 @@ "@types/node": "*" } }, + "node_modules/@types/history": { + "version": "4.7.11", + "resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.11.tgz", + "integrity": "sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==", + "dev": true + }, "node_modules/@types/html-minifier-terser": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", @@ -8021,6 +8029,27 @@ "@types/react": "*" } }, + "node_modules/@types/react-router": { + "version": "5.1.20", + "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.20.tgz", + "integrity": "sha512-jGjmu/ZqS7FjSH6owMcD5qpq19+1RS9DeVRqfl1FeBMxTDQAGwlMWOcs52NDoXaNKyG3d1cYQFMs9rCrb88o9Q==", + "dev": true, + "dependencies": { + "@types/history": "^4.7.11", + "@types/react": "*" + } + }, + "node_modules/@types/react-router-dom": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.3.3.tgz", + "integrity": "sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw==", + "dev": true, + "dependencies": { + "@types/history": "^4.7.11", + "@types/react": "*", + "@types/react-router": "*" + } + }, "node_modules/@types/react-test-renderer": { "version": "18.0.7", "resolved": "https://registry.npmjs.org/@types/react-test-renderer/-/react-test-renderer-18.0.7.tgz", @@ -21271,11 +21300,11 @@ } }, "node_modules/react-router": { - "version": "6.21.2", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.21.2.tgz", - "integrity": "sha512-jJcgiwDsnaHIeC+IN7atO0XiSRCrOsQAHHbChtJxmgqG2IaYQXSnhqGb5vk2CU/wBQA12Zt+TkbuJjIn65gzbA==", + "version": "6.28.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.28.0.tgz", + "integrity": "sha512-HrYdIFqdrnhDw0PqG/AKjAqEqM7AvxCz0DQ4h2W8k6nqmc5uRBYDag0SBxx9iYz5G8gnuNVLzUe13wl9eAsXXg==", "dependencies": { - "@remix-run/router": "1.14.2" + "@remix-run/router": "1.21.0" }, "engines": { "node": ">=14.0.0" @@ -21285,12 +21314,12 @@ } }, "node_modules/react-router-dom": { - "version": "6.21.2", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.21.2.tgz", - "integrity": "sha512-tE13UukgUOh2/sqYr6jPzZTzmzc70aGRP4pAjG2if0IP3aUT+sBtAKUJh0qMh0zylJHGLmzS+XWVaON4UklHeg==", + "version": "6.28.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.28.0.tgz", + "integrity": "sha512-kQ7Unsl5YdyOltsPGl31zOjLrDv+m2VcIEcIHqYYD3Lp0UppLjrzcfJqDJwXxFw3TH/yvapbnUvPlAj7Kx5nbg==", "dependencies": { - "@remix-run/router": "1.14.2", - "react-router": "6.21.2" + "@remix-run/router": "1.21.0", + "react-router": "6.28.0" }, "engines": { "node": ">=14.0.0" @@ -28115,9 +28144,9 @@ } }, "@remix-run/router": { - "version": "1.14.2", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.14.2.tgz", - "integrity": "sha512-ACXpdMM9hmKZww21yEqWwiLws/UPLhNKvimN8RrYSqPSvB3ov7sLvAcfvaxePeLvccTQKGdkDIhLYApZVDFuKg==" + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.21.0.tgz", + "integrity": "sha512-xfSkCAchbdG5PnbrKqFWwia4Bi61nH+wm8wLEqfHDyp7Y3dZzgqS2itV8i4gAq9pC2HsTpwyBC6Ds8VHZ96JlA==" }, "@sinclair/typebox": { "version": "0.27.8", @@ -30356,6 +30385,12 @@ "@types/node": "*" } }, + "@types/history": { + "version": "4.7.11", + "resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.11.tgz", + "integrity": "sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==", + "dev": true + }, "@types/html-minifier-terser": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", @@ -30565,6 +30600,27 @@ "@types/react": "*" } }, + "@types/react-router": { + "version": "5.1.20", + "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.20.tgz", + "integrity": "sha512-jGjmu/ZqS7FjSH6owMcD5qpq19+1RS9DeVRqfl1FeBMxTDQAGwlMWOcs52NDoXaNKyG3d1cYQFMs9rCrb88o9Q==", + "dev": true, + "requires": { + "@types/history": "^4.7.11", + "@types/react": "*" + } + }, + "@types/react-router-dom": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.3.3.tgz", + "integrity": "sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw==", + "dev": true, + "requires": { + "@types/history": "^4.7.11", + "@types/react": "*", + "@types/react-router": "*" + } + }, "@types/react-test-renderer": { "version": "18.0.7", "resolved": "https://registry.npmjs.org/@types/react-test-renderer/-/react-test-renderer-18.0.7.tgz", @@ -40368,20 +40424,20 @@ } }, "react-router": { - "version": "6.21.2", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.21.2.tgz", - "integrity": "sha512-jJcgiwDsnaHIeC+IN7atO0XiSRCrOsQAHHbChtJxmgqG2IaYQXSnhqGb5vk2CU/wBQA12Zt+TkbuJjIn65gzbA==", + "version": "6.28.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.28.0.tgz", + "integrity": "sha512-HrYdIFqdrnhDw0PqG/AKjAqEqM7AvxCz0DQ4h2W8k6nqmc5uRBYDag0SBxx9iYz5G8gnuNVLzUe13wl9eAsXXg==", "requires": { - "@remix-run/router": "1.14.2" + "@remix-run/router": "1.21.0" } }, "react-router-dom": { - "version": "6.21.2", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.21.2.tgz", - "integrity": "sha512-tE13UukgUOh2/sqYr6jPzZTzmzc70aGRP4pAjG2if0IP3aUT+sBtAKUJh0qMh0zylJHGLmzS+XWVaON4UklHeg==", + "version": "6.28.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.28.0.tgz", + "integrity": "sha512-kQ7Unsl5YdyOltsPGl31zOjLrDv+m2VcIEcIHqYYD3Lp0UppLjrzcfJqDJwXxFw3TH/yvapbnUvPlAj7Kx5nbg==", "requires": { - "@remix-run/router": "1.14.2", - "react-router": "6.21.2" + "@remix-run/router": "1.21.0", + "react-router": "6.28.0" } }, "react-shallow-renderer": { diff --git a/package.json b/package.json index eb3d33f..2e65533 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,8 @@ "react-dom": "^18.2.0", "react-intersection-observer": "^9.4.3", "react-redux": "^9.1.0", - "react-router-dom": "^6.10.0", + "react-router-dom": "^6.28.0", + "redux": "^5.0.1", "redux-thunk": "^3.1.0", "typescript": "^5.3.3", "uuid": "^9.0.0", @@ -47,6 +48,7 @@ "@types/node": "^20.10.5", "@types/react": "^18.2.45", "@types/react-dom": "^18.2.18", + "@types/react-router-dom": "^5.3.3", "@types/react-test-renderer": "^18.0.7", "@types/webpack-env": "^1.18.4", "@typescript-eslint/eslint-plugin": "^6.15.0", diff --git a/src/components/app-header/app-header.tsx b/src/components/app-header/app-header.tsx index 0400084..93b6659 100644 --- a/src/components/app-header/app-header.tsx +++ b/src/components/app-header/app-header.tsx @@ -1,4 +1,9 @@ import { FC } from 'react'; import { AppHeaderUI } from '@ui'; +import { useSelector } from '../../services/store'; +import { getName } from '../../services/slices/user-slice'; -export const AppHeader: FC = () => ; +export const AppHeader: FC = () => { + const userName = useSelector(getName); + return ; +}; diff --git a/src/components/app/app.tsx b/src/components/app/app.tsx index a63c997..a47bc6e 100644 --- a/src/components/app/app.tsx +++ b/src/components/app/app.tsx @@ -1,14 +1,133 @@ -import { ConstructorPage } from '@pages'; +import { + ConstructorPage, + Feed, + ForgotPassword, + Login, + Profile, + ProfileOrders, + Register, + ResetPassword +} from '@pages'; import '../../index.css'; import styles from './app.module.css'; -import { AppHeader } from '@components'; +import { AppHeader, Modal, OrderInfo, IngredientDetails } from '@components'; +import { Route, Routes, useLocation, useNavigate } from 'react-router-dom'; +import { useEffect } from 'react'; +import { getIngredientsList } from '../../services/slices/ingredients-slice'; +import { useDispatch } from '../../services/store'; +import { apiGetUser } from '../../services/slices/user-slice'; +import { ProtectedRoute } from '../protected-route/protected-route'; -const App = () => ( -
- - -
-); +const App = () => { + const nav = useNavigate(); + const loc = useLocation(); + const disp = useDispatch(); + const back = loc.state?.background; + + useEffect(() => { + disp(getIngredientsList()); + disp(apiGetUser()); + }, []); + + return ( +
+ + + } /> + } /> + } /> + } /> + + + + } + /> + + + + } + /> + + + + } + /> + + + + } + /> + + + + } + /> + + + + } + /> + + + + } + /> + + + {back && ( + + nav(-1)}> + + + } + /> + nav(-1)}> + + + } + /> + nav(-1)}> + + + + + } + /> + + )} +
+ ); +}; export default App; diff --git a/src/components/burger-constructor-element/burger-constructor-element.tsx b/src/components/burger-constructor-element/burger-constructor-element.tsx index a9d1952..750cf34 100644 --- a/src/components/burger-constructor-element/burger-constructor-element.tsx +++ b/src/components/burger-constructor-element/burger-constructor-element.tsx @@ -1,14 +1,44 @@ import { FC, memo } from 'react'; import { BurgerConstructorElementUI } from '@ui'; import { BurgerConstructorElementProps } from './type'; +import { + constructorSelector, + deleteItem, + updateAll +} from '../../services/slices/constructor-slice'; +import { useDispatch, useSelector } from '../../services/store'; +import { TConstructorIngredient } from '@utils-types'; export const BurgerConstructorElement: FC = memo( ({ ingredient, index, totalItems }) => { - const handleMoveDown = () => {}; + const constructorItems = useSelector(constructorSelector.selectItems); + const dispatch = useDispatch(); + console.log(updateAll); + function swapElements( + array: TConstructorIngredient[], + index1: number, + index2: number + ) { + const newArr = [...array]; + [newArr[index1], newArr[index2]] = [newArr[index2], newArr[index1]]; + return newArr; + } - const handleMoveUp = () => {}; + const handleMoveDown = () => { + dispatch( + updateAll(swapElements(constructorItems.ingredients, index, index + 1)) + ); + }; - const handleClose = () => {}; + const handleMoveUp = () => { + dispatch( + updateAll(swapElements(constructorItems.ingredients, index, index - 1)) + ); + }; + + const handleClose = () => { + dispatch(deleteItem(ingredient)); + }; return ( { - /** TODO: взять переменные constructorItems, orderRequest и orderModalData из стора */ - const constructorItems = { - bun: { - price: 0 - }, - ingredients: [] - }; - - const orderRequest = false; - - const orderModalData = null; + const dispatch = useDispatch(); + const nav = useNavigate(); + const orderRequest = useSelector(getOrderRequest); + const orderModalData = useSelector(getOrderModalData); + const isAuth = useSelector(isAuthCheckedSelector); + const constructorItems = useSelector(constructorSelector.selectItems); const onOrderClick = () => { + if (!isAuth) { + return nav('/login'); + } if (!constructorItems.bun || orderRequest) return; + + const orderData = [ + constructorItems.bun._id, + ...constructorItems.ingredients.map((ingredient) => ingredient._id) + ]; + + dispatch(newOrder(orderData)); + }; + const closeOrderModal = () => { + dispatch(resetOrder()); + dispatch(clearAll()); + nav('/'); }; - const closeOrderModal = () => {}; const price = useMemo( () => @@ -30,8 +55,6 @@ export const BurgerConstructor: FC = () => { [constructorItems] ); - return null; - return ( = memo( ({ ingredient, count }) => { const location = useLocation(); + const dispatch = useDispatch(); - const handleAdd = () => {}; + const handleAdd = () => { + dispatch(addItem({ ...ingredient, id: ingredient._id })); + }; return ( { - /** TODO: взять переменные из стора */ - const buns = []; - const mains = []; - const sauces = []; + const { ingredients, loading, error } = useSelector(getIngredientsState); + const buns = ingredients.filter((item) => item.type === 'bun'); + const mains = ingredients.filter((item) => item.type === 'main'); + const sauces = ingredients.filter((item) => item.type === 'sauce'); const [currentTab, setCurrentTab] = useState('bun'); const titleBunRef = useRef(null); @@ -47,7 +54,13 @@ export const BurgerIngredients: FC = () => { titleSaucesRef.current?.scrollIntoView({ behavior: 'smooth' }); }; - return null; + if (error) { + return 'Ошибка'; + } + + if (loading) { + return ; + } return ( orders @@ -10,13 +15,13 @@ const getOrders = (orders: TOrder[], status: string): number[] => .slice(0, 20); export const FeedInfo: FC = () => { - /** TODO: взять переменные из стора */ - const orders: TOrder[] = []; - const feed = {}; + const orders = useSelector(getOrdersFeeds); + const totalFeeds = useSelector(getTotalFeeds); + const totalToday = useSelector(getTotalTodayFeeds); const readyOrders = getOrders(orders, 'done'); - const pendingOrders = getOrders(orders, 'pending'); + const feed = { total: totalFeeds, totalToday: totalToday }; return ( { - /** TODO: взять переменную из стора */ - const ingredientData = null; + const ingredientsSelector = useSelector(getIngredientsState); + const { id } = useParams(); + console.log(id); + const ingredientData = ingredientsSelector.ingredients.find( + (el) => el._id === id + ); if (!ingredientData) { return ; diff --git a/src/components/ingredients-category/ingredients-category.tsx b/src/components/ingredients-category/ingredients-category.tsx index c3d8ff0..32be782 100644 --- a/src/components/ingredients-category/ingredients-category.tsx +++ b/src/components/ingredients-category/ingredients-category.tsx @@ -2,18 +2,14 @@ import { forwardRef, useMemo } from 'react'; import { TIngredientsCategoryProps } from './type'; import { TIngredient } from '@utils-types'; import { IngredientsCategoryUI } from '../ui/ingredients-category'; +import { useSelector } from '../../services/store'; +import { constructorSelector } from '../../services/slices/constructor-slice'; export const IngredientsCategory = forwardRef< HTMLUListElement, TIngredientsCategoryProps >(({ title, titleRef, ingredients }, ref) => { - /** TODO: взять переменную из стора */ - const burgerConstructor = { - bun: { - _id: '' - }, - ingredients: [] - }; + const burgerConstructor = useSelector(constructorSelector.selectItems); const ingredientsCounters = useMemo(() => { const { bun, ingredients } = burgerConstructor; diff --git a/src/components/order-card/order-card.tsx b/src/components/order-card/order-card.tsx index 3a42e73..b610b3e 100644 --- a/src/components/order-card/order-card.tsx +++ b/src/components/order-card/order-card.tsx @@ -1,17 +1,17 @@ import { FC, memo, useMemo } from 'react'; import { useLocation } from 'react-router-dom'; -import { OrderCardProps } from './type'; import { TIngredient } from '@utils-types'; + +import { OrderCardProps } from './type'; import { OrderCardUI } from '../ui/order-card'; +import { useSelector } from '../../services/store'; +import { getIngredients } from '../../services/slices/ingredients-slice'; const maxIngredients = 6; - export const OrderCard: FC = memo(({ order }) => { const location = useLocation(); - - /** TODO: взять переменную из стора */ - const ingredients: TIngredient[] = []; + const ingredients: TIngredient[] = useSelector(getIngredients); const orderInfo = useMemo(() => { if (!ingredients.length) return null; diff --git a/src/components/order-info/order-info.tsx b/src/components/order-info/order-info.tsx index 0558e58..c48fe60 100644 --- a/src/components/order-info/order-info.tsx +++ b/src/components/order-info/order-info.tsx @@ -1,23 +1,116 @@ -import { FC, useMemo } from 'react'; +/* import React, { FC, useEffect, useMemo, useState } from 'react'; +import { useParams } from 'react-router-dom'; + +import { getOrderByNumberApi } from '@api'; +import { TIngredient, TOrder } from '@utils-types'; + +import { Preloader } from '../ui/preloader'; +import { OrderInfoUI } from '../ui/order-info'; +import { useSelector } from '../../services/store'; +import { getIngredients } from '../../services/slices/ingredients-slice'; + +interface OrderData extends Omit { + ingredients: string[]; +} + +interface IngredientsWithCount { + [key: string]: TIngredient & { count: number }; +} +interface OrderInfoProcessed extends OrderData { + ingredientsInfo: IngredientsWithCount; + date: Date; + total: number; +} + +export const OrderInfo: FC = () => { + const [orderData, setOrderData] = useState(null); + const ingredients = useSelector(getIngredients); + const { number: orderNumber } = useParams(); + + const orderInfo = useMemo(() => { + if (!orderData || !ingredients.length) return null; + + const date = new Date(orderData.createdAt); + + const ingredientsInfo = orderData.ingredients.reduce( + (acc, item) => { + if (!acc[item]) { + const ingredient = ingredients.find((ing) => ing._id === item); + if (ingredient) { + acc[item] = { + ...ingredient, + count: 1 + }; + } + } else { + acc[item].count++; + } + + return acc; + }, + {} + ); + + const total = Object.values(ingredientsInfo).reduce( + (acc, item) => acc + item.price * item.count, + 0 + ); + + return { + ...orderData, + ingredientsInfo, + date, + total + }; + }, [orderData, ingredients]); + + useEffect(() => { + if (orderNumber) { + const fetchOrderData = async () => { + try { + const data = await getOrderByNumberApi(Number(orderNumber)); + if (data && data.orders && data.orders.length > 0) { + setOrderData(data.orders[0]); + } + } catch (e) { + console.error('Error fetching the order data', e); + } + }; + + fetchOrderData(); + } + }, [orderNumber]); + + if (!orderInfo) { + return ; + } + + return ; +}; */ +import { FC, useEffect, useMemo, useState } from 'react'; +import { useParams } from 'react-router-dom'; + +import { getOrderByNumberApi } from '@api'; +import { TIngredient, TOrder } from '@utils-types'; + import { Preloader } from '../ui/preloader'; import { OrderInfoUI } from '../ui/order-info'; -import { TIngredient } from '@utils-types'; +import { useSelector } from '../../services/store'; +import { getIngredients } from '../../services/slices/ingredients-slice'; export const OrderInfo: FC = () => { - /** TODO: взять переменные orderData и ingredients из стора */ - const orderData = { + const [orderData, setOrderData] = useState({ + _id: '', createdAt: '', ingredients: [], - _id: '', status: '', name: '', updatedAt: 'string', number: 0 - }; - - const ingredients: TIngredient[] = []; + }); - /* Готовим данные для отображения */ + const id = Number(useParams().number); + const ingredients: TIngredient[] = useSelector(getIngredients); const orderInfo = useMemo(() => { if (!orderData || !ingredients.length) return null; @@ -59,6 +152,12 @@ export const OrderInfo: FC = () => { }; }, [orderData, ingredients]); + useEffect(() => { + getOrderByNumberApi(Number(id)).then((data) => { + setOrderData(data.orders[0]); + }); + }, []); + if (!orderInfo) { return ; } diff --git a/src/components/profile-menu/profile-menu.tsx b/src/components/profile-menu/profile-menu.tsx index 1745072..73cef43 100644 --- a/src/components/profile-menu/profile-menu.tsx +++ b/src/components/profile-menu/profile-menu.tsx @@ -1,11 +1,15 @@ -import { FC } from 'react'; +import { FC, useEffect } from 'react'; import { useLocation } from 'react-router-dom'; import { ProfileMenuUI } from '@ui'; +import { useDispatch } from '../../services/store'; +import { logout } from '../../services/slices/user-slice'; export const ProfileMenu: FC = () => { const { pathname } = useLocation(); - - const handleLogout = () => {}; + const dispatch = useDispatch(); + const handleLogout = () => { + dispatch(logout()); + }; return ; }; diff --git a/src/components/protected-route/index.tsx b/src/components/protected-route/index.tsx new file mode 100644 index 0000000..6646c85 --- /dev/null +++ b/src/components/protected-route/index.tsx @@ -0,0 +1,29 @@ +import React from 'react'; +import { Navigate, useLocation } from 'react-router-dom'; + +import { useSelector } from '../../services/store'; +import { isAuthCheckedSelector } from '../../services/slices/user-slice'; + +type ProtectedRouteProps = { + onlyUnAuth?: boolean; + children: React.ReactElement; +}; + +export const ProtectedRoute = ({ + onlyUnAuth = false, + children +}: ProtectedRouteProps) => { + const isAuthChecked = useSelector(isAuthCheckedSelector); + const location = useLocation(); + + if (!onlyUnAuth && !isAuthChecked) { + return ; + } + + if (onlyUnAuth && isAuthChecked) { + const fromPage = location.state?.from || { pathname: '/' }; + + return ; + } + return children; +}; diff --git a/src/components/protected-route/protected-route.tsx b/src/components/protected-route/protected-route.tsx new file mode 100644 index 0000000..e7737f1 --- /dev/null +++ b/src/components/protected-route/protected-route.tsx @@ -0,0 +1,71 @@ +import React, { ReactElement } from 'react'; +import { Navigate, useLocation, Location, useNavigate } from 'react-router-dom'; +import { useSelector } from '../../services/store'; +import { isAuthCheckedSelector } from '../../services/slices/user-slice'; + +type ProtectedRouteProps = { + nonAuth?: boolean; + children: ReactElement; +}; + +export const ProtectedRoute: React.FC = ({ + nonAuth = false, + children +}) => { + const isAuthChecked = useSelector(isAuthCheckedSelector); + const location = useLocation(); + const navigate = useNavigate(); + + const fromPage = (location.state as { from?: Location })?.from || { + pathname: '/' + }; + const navigateTo = (to: Location) => { + navigate(to); + }; + + if (isAuthChecked === undefined) { + return null; + } + + if (!nonAuth && !isAuthChecked) { + return ; + } + + if (nonAuth && isAuthChecked) { + return ; + } + + return children; +}; + +export default ProtectedRoute; + +/* import React from 'react'; +import { Navigate, useLocation } from 'react-router-dom'; + +import { useSelector } from '../../services/store'; +import { isAuthCheckedSelector } from '../../services/slices/user-slice'; + +type ProtectedRouteProps = { + nonAuth?: boolean; + children: React.ReactElement; +}; + +export const ProtectedRoute = ({ + nonAuth = false, + children +}: ProtectedRouteProps) => { + const isAuthChecked = useSelector(isAuthCheckedSelector); + const location = useLocation(); + + if (!nonAuth && !isAuthChecked) { + return ; + } + + if (nonAuth && isAuthChecked) { + const fromPage = location.state?.from || { pathname: '/' }; + + return ; + } + return children; +}; */ diff --git a/src/components/ui/app-header/app-header.tsx b/src/components/ui/app-header/app-header.tsx index fc042a6..d5ef4dd 100644 --- a/src/components/ui/app-header/app-header.tsx +++ b/src/components/ui/app-header/app-header.tsx @@ -1,6 +1,6 @@ import React, { FC } from 'react'; -import styles from './app-header.module.css'; -import { TAppHeaderUIProps } from './type'; +import { Link, NavLink } from 'react-router-dom'; + import { BurgerIcon, ListIcon, @@ -8,28 +8,57 @@ import { ProfileIcon } from '@zlden/react-developer-burger-ui-components'; -export const AppHeaderUI: FC = ({ userName }) => ( -
- -
-); +import styles from './app-header.module.css'; +import { TAppHeaderUIProps } from './type'; + +export const AppHeaderUI: FC = ({ userName }) => { + const getClassName = (isActive: boolean) => + [styles.link, isActive ? styles.link_active : null] + .filter(Boolean) + .join(' '); + + return ( +
+ +
+ ); +}; diff --git a/src/components/ui/feed-info/feed-info.tsx b/src/components/ui/feed-info/feed-info.tsx index cf6f56a..8132dbf 100644 --- a/src/components/ui/feed-info/feed-info.tsx +++ b/src/components/ui/feed-info/feed-info.tsx @@ -7,7 +7,6 @@ import { FeedInfoUIProps, HalfColumnProps, TColumnProps } from './type'; export const FeedInfoUI: FC = memo( ({ feed, readyOrders, pendingOrders }) => { const { total, totalToday } = feed; - return (
diff --git a/src/components/ui/ingredient-details/ingredient-details.tsx b/src/components/ui/ingredient-details/ingredient-details.tsx index 4057180..f33162d 100644 --- a/src/components/ui/ingredient-details/ingredient-details.tsx +++ b/src/components/ui/ingredient-details/ingredient-details.tsx @@ -6,7 +6,6 @@ export const IngredientDetailsUI: FC = memo( ({ ingredientData }) => { const { name, image_large, calories, proteins, fat, carbohydrates } = ingredientData; - return (
= ({ />
{isFormChanged && ( -
+