From 7fe35f718c1757b44e69b6f1836d44535435c8af Mon Sep 17 00:00:00 2001 From: Yulia Berezhnaya Date: Wed, 29 Mar 2023 03:00:42 +0300 Subject: [PATCH 01/13] =?UTF-8?q?=D0=A2=D0=B8=D0=BF=D0=B8=D0=B7=D0=B0?= =?UTF-8?q?=D1=86=D0=B8=D1=8F=20=D1=87=D0=B0=D1=81=D1=82=D0=B8=20=D0=BA?= =?UTF-8?q?=D0=BE=D0=BC=D0=BF=D0=BE=D0=BD=D0=B5=D0=BD=D1=82=D0=BE=D0=B2=20?= =?UTF-8?q?=D0=B8=20=D0=BA=D0=BE=D0=BD=D1=81=D1=82=D0=B0=D0=BD=D1=82,=20?= =?UTF-8?q?=D0=BD=D0=B5=20=D1=82=D1=80=D0=B5=D0=B1=D1=83=D1=8E=D1=89=D0=B8?= =?UTF-8?q?=D1=85=20redux?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 62 ++++++++++++++ package.json | 2 + src/components/App/App.js | 2 +- .../AppHeader/{AppHeader.js => AppHeader.tsx} | 12 ++- .../BurgerConstructor/BurgerConstructor.js | 1 - .../{ErrorBoundary.js => ErrorBoundary.tsx} | 20 +++-- .../{IngredientCard.js => IngredientCard.tsx} | 17 ++-- .../IngredientCategory/IngredientCategory.js | 2 +- src/components/Modal/{Modal.js => Modal.tsx} | 27 ++++--- .../{ModalOverlay.js => ModalOverlay.tsx} | 13 ++- src/{index.js => index.tsx} | 6 +- src/pages/{notFound.jsx => notFound.tsx} | 3 +- src/services/actions/ingredients.js | 2 +- src/utils/TIngredient.tsx | 16 ++++ src/utils/constants.js | 81 ------------------- src/utils/constants.tsx | 81 +++++++++++++++++++ src/utils/propTypes.js | 18 ----- tsconfig.json | 2 +- 18 files changed, 217 insertions(+), 150 deletions(-) rename src/components/AppHeader/{AppHeader.js => AppHeader.tsx} (68%) rename src/components/ErrorBoundary/{ErrorBoundary.js => ErrorBoundary.tsx} (59%) rename src/components/IngredientCard/{IngredientCard.js => IngredientCard.tsx} (80%) rename src/components/Modal/{Modal.js => Modal.tsx} (70%) rename src/components/ModalOverlay/{ModalOverlay.js => ModalOverlay.tsx} (52%) rename src/{index.js => index.tsx} (87%) rename src/pages/{notFound.jsx => notFound.tsx} (93%) create mode 100644 src/utils/TIngredient.tsx delete mode 100644 src/utils/constants.js create mode 100644 src/utils/constants.tsx delete mode 100644 src/utils/propTypes.js diff --git a/package-lock.json b/package-lock.json index da58908..58f4757 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,6 +33,8 @@ "web-vitals": "^2.1.4" }, "devDependencies": { + "@types/react-redux": "^7.1.25", + "@types/redux-devtools-extension": "^2.13.2", "gh-pages": "^4.0.0" } }, @@ -4242,6 +4244,28 @@ "@types/react": "*" } }, + "node_modules/@types/react-redux": { + "version": "7.1.25", + "resolved": "http://npm.prakticum-team.ru/@types/react-redux/-/react-redux-7.1.25.tgz", + "integrity": "sha512-bAGh4e+w5D8dajd6InASVIyCo4pZLJ66oLb80F9OBLO1gKESbZcRCJpTT6uLXX+HAB57zw1WTdwJdAsewuTweg==", + "dev": true, + "dependencies": { + "@types/hoist-non-react-statics": "^3.3.0", + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0", + "redux": "^4.0.0" + } + }, + "node_modules/@types/redux-devtools-extension": { + "version": "2.13.2", + "resolved": "http://npm.prakticum-team.ru/@types/redux-devtools-extension/-/redux-devtools-extension-2.13.2.tgz", + "integrity": "sha512-pN9V4wx+IW12Rf0FZl6nkvAsa+Uqa6C/mOeYai8cT3RDDVsOY+DoFt58ZeBrSLMXMgPgI4n6MDRrkET+DvkI1g==", + "deprecated": "This is a stub types definition for redux-devtools-extension (https://github.com/zalmoxisus/redux-devtools-extension). redux-devtools-extension provides its own type definitions, so you don't need @types/redux-devtools-extension installed!", + "dev": true, + "dependencies": { + "redux-devtools-extension": "*" + } + }, "node_modules/@types/resolve": { "version": "1.17.1", "resolved": "http://npm.prakticum-team.ru/@types%2fresolve/-/resolve-1.17.1.tgz", @@ -15881,6 +15905,16 @@ "@babel/runtime": "^7.9.2" } }, + "node_modules/redux-devtools-extension": { + "version": "2.13.9", + "resolved": "http://npm.prakticum-team.ru/redux-devtools-extension/-/redux-devtools-extension-2.13.9.tgz", + "integrity": "sha512-cNJ8Q/EtjhQaZ71c8I9+BPySIBVEKssbPpskBfsXqb8HJ002A3KRVHfeRzwRo6mGPqsm7XuHTqNSNeS1Khig0A==", + "deprecated": "Package moved to @redux-devtools/extension.", + "dev": true, + "peerDependencies": { + "redux": "^3.1.0 || ^4.0.0" + } + }, "node_modules/redux-thunk": { "version": "2.4.2", "resolved": "http://npm.prakticum-team.ru/redux-thunk/-/redux-thunk-2.4.2.tgz", @@ -21738,6 +21772,27 @@ "@types/react": "*" } }, + "@types/react-redux": { + "version": "7.1.25", + "resolved": "http://npm.prakticum-team.ru/@types/react-redux/-/react-redux-7.1.25.tgz", + "integrity": "sha512-bAGh4e+w5D8dajd6InASVIyCo4pZLJ66oLb80F9OBLO1gKESbZcRCJpTT6uLXX+HAB57zw1WTdwJdAsewuTweg==", + "dev": true, + "requires": { + "@types/hoist-non-react-statics": "^3.3.0", + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0", + "redux": "^4.0.0" + } + }, + "@types/redux-devtools-extension": { + "version": "2.13.2", + "resolved": "http://npm.prakticum-team.ru/@types/redux-devtools-extension/-/redux-devtools-extension-2.13.2.tgz", + "integrity": "sha512-pN9V4wx+IW12Rf0FZl6nkvAsa+Uqa6C/mOeYai8cT3RDDVsOY+DoFt58ZeBrSLMXMgPgI4n6MDRrkET+DvkI1g==", + "dev": true, + "requires": { + "redux-devtools-extension": "*" + } + }, "@types/resolve": { "version": "1.17.1", "resolved": "http://npm.prakticum-team.ru/@types%2fresolve/-/resolve-1.17.1.tgz", @@ -29294,6 +29349,13 @@ "@babel/runtime": "^7.9.2" } }, + "redux-devtools-extension": { + "version": "2.13.9", + "resolved": "http://npm.prakticum-team.ru/redux-devtools-extension/-/redux-devtools-extension-2.13.9.tgz", + "integrity": "sha512-cNJ8Q/EtjhQaZ71c8I9+BPySIBVEKssbPpskBfsXqb8HJ002A3KRVHfeRzwRo6mGPqsm7XuHTqNSNeS1Khig0A==", + "dev": true, + "requires": {} + }, "redux-thunk": { "version": "2.4.2", "resolved": "http://npm.prakticum-team.ru/redux-thunk/-/redux-thunk-2.4.2.tgz", diff --git a/package.json b/package.json index d2c71b0..1788526 100644 --- a/package.json +++ b/package.json @@ -55,6 +55,8 @@ ] }, "devDependencies": { + "@types/react-redux": "^7.1.25", + "@types/redux-devtools-extension": "^2.13.2", "gh-pages": "^4.0.0" } } diff --git a/src/components/App/App.js b/src/components/App/App.js index c20d459..30a92b0 100644 --- a/src/components/App/App.js +++ b/src/components/App/App.js @@ -45,7 +45,7 @@ function App() {
- } /> + } /> } /> } /> } /> diff --git a/src/components/AppHeader/AppHeader.js b/src/components/AppHeader/AppHeader.tsx similarity index 68% rename from src/components/AppHeader/AppHeader.js rename to src/components/AppHeader/AppHeader.tsx index 4dd0f22..957783e 100644 --- a/src/components/AppHeader/AppHeader.js +++ b/src/components/AppHeader/AppHeader.tsx @@ -1,14 +1,12 @@ -// import React from 'react'; import { BurgerIcon, ListIcon, Logo, ProfileIcon } from '@ya.praktikum/react-developer-burger-ui-components'; import { NavLink } from 'react-router-dom'; import AppHeaderStyles from './AppHeader.module.css'; function AppHeader() { - - const active = AppHeaderStyles.link + ' ' + AppHeaderStyles.active + ' pl-5 pt-4 pr-5 pb-4 text text_type_main-default'; - const link = AppHeaderStyles.link + ' pl-5 pt-4 pr-5 pb-4 text text_type_main-default text_color_inactive'; - const activeAccountLink = AppHeaderStyles.link + ' ' + AppHeaderStyles.active + ' ' + AppHeaderStyles.account + ' pl-5 pt-4 pr-5 pb-4 text text_type_main-default'; - const accountLink = AppHeaderStyles.link + ' ' + AppHeaderStyles.account + ' pl-5 pt-4 pr-5 pb-4 text text_type_main-default text_color_inactive'; + const active: string = AppHeaderStyles.link + ' ' + AppHeaderStyles.active + ' pl-5 pt-4 pr-5 pb-4 text text_type_main-default'; + const link: string = AppHeaderStyles.link + ' pl-5 pt-4 pr-5 pb-4 text text_type_main-default text_color_inactive'; + const activeAccountLink: string = AppHeaderStyles.link + ' ' + AppHeaderStyles.active + ' ' + AppHeaderStyles.account + ' pl-5 pt-4 pr-5 pb-4 text text_type_main-default'; + const accountLink: string = AppHeaderStyles.link + ' ' + AppHeaderStyles.account + ' pl-5 pt-4 pr-5 pb-4 text text_type_main-default text_color_inactive'; return (
@@ -41,4 +39,4 @@ function AppHeader() { ); } -export default AppHeader; \ No newline at end of file +export default AppHeader; diff --git a/src/components/BurgerConstructor/BurgerConstructor.js b/src/components/BurgerConstructor/BurgerConstructor.js index a8f1c10..a6b6c89 100644 --- a/src/components/BurgerConstructor/BurgerConstructor.js +++ b/src/components/BurgerConstructor/BurgerConstructor.js @@ -1,6 +1,5 @@ import BurgerConstructorStyles from './BurgerConstructor.module.css'; -import React, { useMemo, useRef } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import PropTypes from 'prop-types'; import { useDrop } from 'react-dnd'; diff --git a/src/components/ErrorBoundary/ErrorBoundary.js b/src/components/ErrorBoundary/ErrorBoundary.tsx similarity index 59% rename from src/components/ErrorBoundary/ErrorBoundary.js rename to src/components/ErrorBoundary/ErrorBoundary.tsx index 60e8a07..2eba67f 100644 --- a/src/components/ErrorBoundary/ErrorBoundary.js +++ b/src/components/ErrorBoundary/ErrorBoundary.tsx @@ -1,16 +1,24 @@ -import React from "react"; +import React, { ErrorInfo, ReactNode } from "react"; -class ErrorBoundary extends React.Component { - constructor(props) { +type TErrorProps = { + children?: ReactNode; +} + +type TErrorState = { + hasError: boolean; +}; + +class ErrorBoundary extends React.Component { + constructor(props: TErrorProps) { super(props); this.state = { hasError: false }; } - static getDerivedStateFromError(error) { + static getDerivedStateFromError() { return { hasError: true }; } - componentDidCatch(error, info) { + componentDidCatch(error: Error, info: ErrorInfo) { console.log("Произошла ошибка.", error, info); } @@ -30,4 +38,4 @@ class ErrorBoundary extends React.Component { } } -export default ErrorBoundary; \ No newline at end of file +export default ErrorBoundary; diff --git a/src/components/IngredientCard/IngredientCard.js b/src/components/IngredientCard/IngredientCard.tsx similarity index 80% rename from src/components/IngredientCard/IngredientCard.js rename to src/components/IngredientCard/IngredientCard.tsx index bb817fa..f78e030 100644 --- a/src/components/IngredientCard/IngredientCard.js +++ b/src/components/IngredientCard/IngredientCard.tsx @@ -1,13 +1,18 @@ import IngredientCardStyles from './IngredientCard.module.css'; -import PropTypes from 'prop-types'; -import IngredientsPropTypes from '../../utils/propTypes'; +import TIngredient from '../../utils/TIngredient'; +import { FC } from 'react'; import { useDrag } from 'react-dnd'; import { Link, useLocation } from 'react-router-dom'; import { CurrencyIcon, Counter } from '@ya.praktikum/react-developer-burger-ui-components'; -function IngredientCard({ data, count, openIngredientModal }) { +type TIngredientCardProps = { + data: TIngredient; + count?: number; + openIngredientModal: () => void; +} +const IngredientCard: FC = ({ data, count, openIngredientModal }) => { const location = useLocation(); const [{ opacity }, ref] = useDrag({ @@ -40,10 +45,4 @@ function IngredientCard({ data, count, openIngredientModal }) { ); } -IngredientCard.propTypes = { - data: IngredientsPropTypes.isRequired, - count: PropTypes.number, - openIngredientModal: PropTypes.func.isRequired -} - export default IngredientCard; diff --git a/src/components/IngredientCategory/IngredientCategory.js b/src/components/IngredientCategory/IngredientCategory.js index 1cc1c08..b849ab5 100644 --- a/src/components/IngredientCategory/IngredientCategory.js +++ b/src/components/IngredientCategory/IngredientCategory.js @@ -3,7 +3,7 @@ import IngredientCategoryStyles from './IngredientCategory.module.css'; import { useMemo, useEffect } from 'react'; import PropTypes from 'prop-types'; -import IngredientsPropTypes from '../../utils/propTypes'; +import IngredientsPropTypes from '../../utils/TIngredient'; import IngredientCard from '../IngredientCard/IngredientCard'; import { useDispatch, useSelector } from 'react-redux'; diff --git a/src/components/Modal/Modal.js b/src/components/Modal/Modal.tsx similarity index 70% rename from src/components/Modal/Modal.js rename to src/components/Modal/Modal.tsx index 2dc16b5..72e5ae9 100644 --- a/src/components/Modal/Modal.js +++ b/src/components/Modal/Modal.tsx @@ -1,16 +1,25 @@ -import React from 'react'; +import { useEffect, FC, ReactNode } from 'react'; import ReactDOM from 'react-dom'; -import PropTypes from 'prop-types'; import ModalOverlay from '../ModalOverlay/ModalOverlay'; import ModalStyles from './Modal.module.css'; import { CloseIcon } from '@ya.praktikum/react-developer-burger-ui-components'; -const modalsContainer = document.querySelector('#modals'); +const modalsContainer = document.querySelector('#modals') as HTMLElement; -function Modal({ title, onClose, children }) { +type TModalProps = { + title: string; + onClose: () => void; + children: ReactNode; +} + +interface IKeyboardEvent { + key: string; +} - React.useEffect(() => { - const handleEscKeydown = (e) => { +const Modal: FC = ({ title, onClose, children }) => { + + useEffect(() => { + const handleEscKeydown = (e: IKeyboardEvent) => { e.key === 'Escape' && onClose(); }; @@ -36,10 +45,4 @@ function Modal({ title, onClose, children }) { ); }; -Modal.propTypes = { - title: PropTypes.string, - onClose: PropTypes.func.isRequired, - children: PropTypes.node -} - export default Modal; diff --git a/src/components/ModalOverlay/ModalOverlay.js b/src/components/ModalOverlay/ModalOverlay.tsx similarity index 52% rename from src/components/ModalOverlay/ModalOverlay.js rename to src/components/ModalOverlay/ModalOverlay.tsx index 756341d..a46c1ec 100644 --- a/src/components/ModalOverlay/ModalOverlay.js +++ b/src/components/ModalOverlay/ModalOverlay.tsx @@ -1,16 +1,15 @@ -import React from "react"; -import PropTypes from 'prop-types'; +import { FC } from "react"; import ModalOverlayStyles from './ModalOverlay.module.css'; -function ModalOverlay({onClick}) { +type TModalProps = { + onClick: () => void; +} + +const ModalOverlay: FC = ({onClick}) => { return (
) } -ModalOverlay.propTypes = { - onClick: PropTypes.func.isRequired -} - export default ModalOverlay; diff --git a/src/index.js b/src/index.tsx similarity index 87% rename from src/index.js rename to src/index.tsx index a4b9364..44467bb 100644 --- a/src/index.js +++ b/src/index.tsx @@ -12,12 +12,12 @@ import { socketMiddleware } from './services/middlewares/ws'; import { wsConnect, wsDisconnect, wsConnecting, wsOpen, wsClose, wsError, wsMessage } from './services/actions/ws'; const root = ReactDOM.createRoot( - document.getElementById('root') + document.getElementById('root') as HTMLElement ); const composeEnhancers = - typeof window === 'object' && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ - ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) + typeof window === 'object' && (window as any)['__REDUX_DEVTOOLS_EXTENSION_COMPOSE__'] + ? (window as any)['__REDUX_DEVTOOLS_EXTENSION_COMPOSE__']({}) : compose; const wsActions = { diff --git a/src/pages/notFound.jsx b/src/pages/notFound.tsx similarity index 93% rename from src/pages/notFound.jsx rename to src/pages/notFound.tsx index 8516a85..171d9ff 100644 --- a/src/pages/notFound.jsx +++ b/src/pages/notFound.tsx @@ -1,4 +1,3 @@ -import React from 'react'; import NotFoundPageStyles from './notFound.module.css'; import { Link } from 'react-router-dom'; @@ -12,4 +11,4 @@ export function NotFoundPage() {

Зато у нас можно подкрепиться вкусными бургерами

); -} \ No newline at end of file +} diff --git a/src/services/actions/ingredients.js b/src/services/actions/ingredients.js index 2dcbc57..d021872 100644 --- a/src/services/actions/ingredients.js +++ b/src/services/actions/ingredients.js @@ -8,7 +8,7 @@ import { GET_ORDER_FAILED } from '../../utils/constants'; import { getAccessToken, getCookie, setCookie } from "../../utils/cookie"; - import { getUserRequest, refreshTokenRequest } from "../../utils/api"; + import { refreshTokenRequest } from "../../utils/api"; export function getIngredients() { return function(dispatch) { diff --git a/src/utils/TIngredient.tsx b/src/utils/TIngredient.tsx new file mode 100644 index 0000000..916c899 --- /dev/null +++ b/src/utils/TIngredient.tsx @@ -0,0 +1,16 @@ +type TIngredient = { + _id: string; + name: string; + type: string; + proteins: number; + fat: number; + carbohydrates: number; + calories: number; + price: number; + image: string; + image_mobile: string; + image_large: string; + __v?: number; +}; + +export default TIngredient; diff --git a/src/utils/constants.js b/src/utils/constants.js deleted file mode 100644 index 982d55e..0000000 --- a/src/utils/constants.js +++ /dev/null @@ -1,81 +0,0 @@ -const BASEURL = 'https://norma.nomoreparties.space/api'; -export const GETINGREDIENTSURL = BASEURL + '/ingredients'; -export const SAVEORDERURL = BASEURL + '/orders'; -export const ADDUSERURL = BASEURL + '/auth/register'; -export const LOGINUSERURL = BASEURL + '/auth/login'; -export const LOGOUTUSERURL = BASEURL + '/auth/logout'; -export const REFRESHTOKENURL = BASEURL + '/auth/token'; -export const USERURL = BASEURL + '/auth/user'; -export const PASSWORDFORGOTURL = BASEURL + '/password-reset'; -export const PASSWORDRESETURL = BASEURL + '/password-reset/reset'; - -export const WSURL = 'wss://norma.nomoreparties.space'; -export const GET_ALL_ORDERS_URL = WSURL + '/orders/all'; -export const GET_USER_ORDERS_URL = WSURL + '/orders'; -export const WS_STATUS = { - CONNECTING : 'CONNECTING...', - ONLINE : 'ONLINE', - OFFLINE : 'OFFLINE' -} -export const WS_MESSAGE = 'WS_MESSAGE'; - -// Константы для обработки запроса для получения всех ингридиентов -export const GET_INGREDIENTS_REQUEST = 'GET_INGREDIENTS_REQUEST'; -export const GET_INGREDIENTS_SUCCESS = 'GET_INGREDIENTS_SUCCESS'; -export const GET_INGREDIENTS_FAILED = 'GET_INGREDIENTS_FAILED'; - -// Константы для обработки запроса оформления заказа -export const GET_ORDER_REQUEST = 'GET_ORDER_REQUEST'; -export const GET_ORDER_SUCCESS = 'GET_ORDER_SUCCESS'; -export const GET_ORDER_FAILED = 'GET_ORDER_FAILED'; - -// Константы для получения/удаления данных об отдельном ингридиенте -export const SHOW_INGREDIENT = 'SHOW_INGREDIENT'; -export const HIDE_INGREDIENT = 'HIDE_INGREDIENT'; - -// Константы для добавления ингридиента в конструктор бургера -export const ADD_INGREDIENT = 'ADD_INGREDIENT'; -export const DELETE_INGREDIENT = 'DELETE_INGREDIENT'; -export const ADD_BUN = 'ADD_BUN'; - -// Константы для сортировки ингридиентов бургера -export const CHANGE_INGREDIENT_ORDER = 'CHANGE_INGREDIENT_ORDER'; - -// Константа для изменения активного таба -export const SET_ACTIVE_TAB = 'SET_ACTIVE_TAB'; - -// Константы для обработки запроса получения данных о пользователе -export const SET_REGISTER_FORM_VALUE = 'SET_REGISTER_FORM_VALUE'; -export const SET_EDIT_USER_FORM = 'SET_EDIT_USER_FORM'; - -export const ADD_USER_REQUEST = 'ADD_USER_REQUEST'; -export const ADD_USER_SUCCESS = 'ADD_USER_SUCCESS'; -export const ADD_USER_FAILED = 'ADD_USER_FAILED'; - -export const LOGIN_USER_REQUEST = 'LOGIN_USER_REQUEST'; -export const LOGIN_USER_SUCCESS = 'LOGIN_USER_SUCCESS'; -export const LOGIN_USER_FAILED = 'LOGIN_USER_FAILED'; - -export const LOGOUT_USER_SUCCESS = 'LOGOUT_USER_SUCCESS'; -export const LOGOUT_USER_FAILED = 'LOGOUT_USER_FAILED'; - -export const GET_USER_SUCCESS = 'GET_USER_SUCCESS'; -export const GET_USER_FAILED = 'GET_USER_FAILED'; - -export const EDIT_USER_SUCCESS = 'EDIT_USER_SUCCESS'; -export const EDIT_USER_FAILED = 'EDIT_USER_FAILED'; -export const RESET_EDIT_USER_FORM = 'RESET_EDIT_USER_FORM'; - -export const FORGOT_PASSWORD_SUCCESS = 'FORGOT_PASSWORD_SUCCESS'; -export const FORGOT_PASSWORD_FAILED = 'FORGOT_PASSWORD_FAILED'; - -export const RESET_PASSWORD_SUCCESS = 'RESET_PASSWORD_SUCCESS'; -export const RESET_PASSWORD_FAILED = 'RESET_PASSWORD_FAILED'; - -export const GET_USER_ORDERS = 'GET_USER_ORDERS'; - -export const ORDER_STATUSES = { - done: 'Выполнен', - cancelled: 'Отменён', - progress: 'Готовится' -} diff --git a/src/utils/constants.tsx b/src/utils/constants.tsx new file mode 100644 index 0000000..0fdfb4f --- /dev/null +++ b/src/utils/constants.tsx @@ -0,0 +1,81 @@ +const BASEURL = 'https://norma.nomoreparties.space/api'; +export const GETINGREDIENTSURL = BASEURL + '/ingredients'; +export const SAVEORDERURL = BASEURL + '/orders'; +export const ADDUSERURL = BASEURL + '/auth/register'; +export const LOGINUSERURL = BASEURL + '/auth/login'; +export const LOGOUTUSERURL = BASEURL + '/auth/logout'; +export const REFRESHTOKENURL = BASEURL + '/auth/token'; +export const USERURL = BASEURL + '/auth/user'; +export const PASSWORDFORGOTURL = BASEURL + '/password-reset'; +export const PASSWORDRESETURL = BASEURL + '/password-reset/reset'; + +export const WSURL = 'wss://norma.nomoreparties.space'; +export const GET_ALL_ORDERS_URL = WSURL + '/orders/all'; +export const GET_USER_ORDERS_URL = WSURL + '/orders'; +export const WS_STATUS = { + CONNECTING : 'CONNECTING...', + ONLINE : 'ONLINE', + OFFLINE : 'OFFLINE' +} +export const WS_MESSAGE: 'WS_MESSAGE' = 'WS_MESSAGE'; + +// Константы для обработки запроса для получения всех ингридиентов +export const GET_INGREDIENTS_REQUEST: 'GET_INGREDIENTS_REQUEST' = 'GET_INGREDIENTS_REQUEST'; +export const GET_INGREDIENTS_SUCCESS: 'GET_INGREDIENTS_SUCCESS' = 'GET_INGREDIENTS_SUCCESS'; +export const GET_INGREDIENTS_FAILED: 'GET_INGREDIENTS_FAILED' = 'GET_INGREDIENTS_FAILED'; + +// Константы для обработки запроса оформления заказа +export const GET_ORDER_REQUEST: 'GET_ORDER_REQUEST'= 'GET_ORDER_REQUEST'; +export const GET_ORDER_SUCCESS: 'GET_ORDER_SUCCESS' = 'GET_ORDER_SUCCESS'; +export const GET_ORDER_FAILED: 'GET_ORDER_FAILED' = 'GET_ORDER_FAILED'; + +// Константы для получения/удаления данных об отдельном ингридиенте +export const SHOW_INGREDIENT: 'SHOW_INGREDIENT' = 'SHOW_INGREDIENT'; +export const HIDE_INGREDIENT: 'HIDE_INGREDIENT' = 'HIDE_INGREDIENT'; + +// Константы для добавления ингридиента в конструктор бургера +export const ADD_INGREDIENT: 'ADD_INGREDIENT' = 'ADD_INGREDIENT'; +export const DELETE_INGREDIENT: 'DELETE_INGREDIENT' = 'DELETE_INGREDIENT'; +export const ADD_BUN: 'ADD_BUN' = 'ADD_BUN'; + +// Константы для сортировки ингридиентов бургера +export const CHANGE_INGREDIENT_ORDER: 'CHANGE_INGREDIENT_ORDER' = 'CHANGE_INGREDIENT_ORDER'; + +// Константа для изменения активного таба +export const SET_ACTIVE_TAB: 'SET_ACTIVE_TAB' = 'SET_ACTIVE_TAB'; + +// Константы для обработки запроса получения данных о пользователе +export const SET_REGISTER_FORM_VALUE: 'SET_REGISTER_FORM_VALUE' = 'SET_REGISTER_FORM_VALUE'; +export const SET_EDIT_USER_FORM: 'SET_EDIT_USER_FORM' = 'SET_EDIT_USER_FORM'; + +export const ADD_USER_REQUEST: 'ADD_USER_REQUEST' = 'ADD_USER_REQUEST'; +export const ADD_USER_SUCCESS: 'ADD_USER_SUCCESS' = 'ADD_USER_SUCCESS'; +export const ADD_USER_FAILED: 'ADD_USER_FAILED' = 'ADD_USER_FAILED'; + +export const LOGIN_USER_REQUEST: 'LOGIN_USER_REQUEST' = 'LOGIN_USER_REQUEST'; +export const LOGIN_USER_SUCCESS: 'LOGIN_USER_SUCCESS' = 'LOGIN_USER_SUCCESS'; +export const LOGIN_USER_FAILED: 'LOGIN_USER_FAILED' = 'LOGIN_USER_FAILED'; + +export const LOGOUT_USER_SUCCESS: 'LOGOUT_USER_SUCCESS' = 'LOGOUT_USER_SUCCESS'; +export const LOGOUT_USER_FAILED: 'LOGOUT_USER_FAILED' = 'LOGOUT_USER_FAILED'; + +export const GET_USER_SUCCESS: 'GET_USER_SUCCESS' = 'GET_USER_SUCCESS'; +export const GET_USER_FAILED: 'GET_USER_FAILED' = 'GET_USER_FAILED'; + +export const EDIT_USER_SUCCESS: 'EDIT_USER_SUCCESS' = 'EDIT_USER_SUCCESS'; +export const EDIT_USER_FAILED: 'EDIT_USER_FAILED' = 'EDIT_USER_FAILED'; +export const RESET_EDIT_USER_FORM: 'RESET_EDIT_USER_FORM' = 'RESET_EDIT_USER_FORM'; + +export const FORGOT_PASSWORD_SUCCESS: 'FORGOT_PASSWORD_SUCCESS' = 'FORGOT_PASSWORD_SUCCESS'; +export const FORGOT_PASSWORD_FAILED: 'FORGOT_PASSWORD_FAILED' = 'FORGOT_PASSWORD_FAILED'; + +export const RESET_PASSWORD_SUCCESS: 'RESET_PASSWORD_SUCCESS' = 'RESET_PASSWORD_SUCCESS'; +export const RESET_PASSWORD_FAILED: 'RESET_PASSWORD_FAILED' = 'RESET_PASSWORD_FAILED'; + +export const GET_USER_ORDERS: 'GET_USER_ORDERS' = 'GET_USER_ORDERS'; + +export const ORDER_STATUSES = { + done: 'Выполнен', + cancelled: 'Отменён', + progress: 'Готовится' +} diff --git a/src/utils/propTypes.js b/src/utils/propTypes.js deleted file mode 100644 index 0954f1f..0000000 --- a/src/utils/propTypes.js +++ /dev/null @@ -1,18 +0,0 @@ -import PropTypes from 'prop-types'; - -const ingredientsPropTypes = PropTypes.shape({ - _id: PropTypes.string.isRequired, - name: PropTypes.string.isRequired, - type: PropTypes.string.isRequired, - proteins:PropTypes.number, - fat: PropTypes.number, - carbohydrates: PropTypes.number, - calories: PropTypes.number, - price: PropTypes.number.isRequired, - image: PropTypes.string.isRequired, - image_mobile: PropTypes.string.isRequired, - image_large: PropTypes.string.isRequired, - __v: PropTypes.number -}); - -export default ingredientsPropTypes; diff --git a/tsconfig.json b/tsconfig.json index a273b0c..34bbca6 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "target": "es5", + "target": "ESNext", "lib": [ "dom", "dom.iterable", From 428265899933adc90429ef959bd875cf97af5152 Mon Sep 17 00:00:00 2001 From: Yulia Berezhnaya Date: Wed, 29 Mar 2023 18:24:24 +0300 Subject: [PATCH 02/13] =?UTF-8?q?=D0=9E=D0=B1=D1=89=D0=B8=D0=B5=20=D0=BC?= =?UTF-8?q?=D0=BE=D0=B4=D1=83=D0=BB=D0=B8=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82?= =?UTF-8?q?=D1=8B=20=D1=81=20=D0=BA=D1=83=D0=BA=D0=B0=D0=BC=D0=B8=20=D0=B8?= =?UTF-8?q?=20=D0=B4=D0=B0=D1=82=D0=B0=D0=BC=D0=B8=20=D0=BF=D0=B5=D1=80?= =?UTF-8?q?=D0=B5=D0=B2=D0=B5=D0=B4=D0=B5=D0=BD=D1=8B=20=D0=BD=D0=B0=20typ?= =?UTF-8?q?escript?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../IngredientCategory/IngredientCategory.js | 18 +- src/utils/{cookie.js => cookie.tsx} | 28 ++- src/utils/data.js | 214 ------------------ src/utils/date.js | 23 -- src/utils/date.tsx | 24 ++ src/utils/order.js | 132 ----------- 6 files changed, 54 insertions(+), 385 deletions(-) rename src/utils/{cookie.js => cookie.tsx} (52%) delete mode 100644 src/utils/data.js delete mode 100644 src/utils/date.js create mode 100644 src/utils/date.tsx delete mode 100644 src/utils/order.js diff --git a/src/components/IngredientCategory/IngredientCategory.js b/src/components/IngredientCategory/IngredientCategory.js index b849ab5..29c08af 100644 --- a/src/components/IngredientCategory/IngredientCategory.js +++ b/src/components/IngredientCategory/IngredientCategory.js @@ -3,7 +3,7 @@ import IngredientCategoryStyles from './IngredientCategory.module.css'; import { useMemo, useEffect } from 'react'; import PropTypes from 'prop-types'; -import IngredientsPropTypes from '../../utils/TIngredient'; +// import IngredientsPropTypes from '../../utils/TIngredient'; import IngredientCard from '../IngredientCard/IngredientCard'; import { useDispatch, useSelector } from 'react-redux'; @@ -51,13 +51,13 @@ function IngredientCategory({ id, title, data, openIngredientModal, link, inView ); } -IngredientCategory.propTypes = { - id: PropTypes.string.isRequired, - title: PropTypes.string.isRequired, - data: PropTypes.arrayOf(IngredientsPropTypes).isRequired, - openIngredientModal: PropTypes.func.isRequired, - link: PropTypes.func.isRequired, - inView: PropTypes.bool.isRequired -} +// IngredientCategory.propTypes = { +// id: PropTypes.string.isRequired, +// title: PropTypes.string.isRequired, +// data: PropTypes.arrayOf(IngredientsPropTypes).isRequired, +// openIngredientModal: PropTypes.func.isRequired, +// link: PropTypes.func.isRequired, +// inView: PropTypes.bool.isRequired +// } export default IngredientCategory; diff --git a/src/utils/cookie.js b/src/utils/cookie.tsx similarity index 52% rename from src/utils/cookie.js rename to src/utils/cookie.tsx index 0aff85e..55d696d 100644 --- a/src/utils/cookie.js +++ b/src/utils/cookie.tsx @@ -1,4 +1,15 @@ -export const setCookie = (name, value, props) => { +type ICookieProps = { + path: string; + expires?: Date | string; + domain?: string; + httpOnly?: boolean; + sameSite?: boolean; + secure?: boolean; + overwrite?: boolean; + maxAge?: number; +} + +export const setCookie = (name: string, value: string, props?: ICookieProps) => { props = { path: '/', ...props @@ -12,14 +23,16 @@ export const setCookie = (name, value, props) => { exp = props.expires = d; } - if (exp && exp.toUTCString) { + if (exp && exp instanceof Date) { props.expires = exp.toUTCString(); } value = encodeURIComponent(value); let updatedCookie = name + '=' + value; - for (const propName in props) { + let propName: keyof ICookieProps; + + for (propName in props) { updatedCookie += '; ' + propName; const propValue = props[propName]; if (propValue !== true) { @@ -30,9 +43,10 @@ export const setCookie = (name, value, props) => { document.cookie = updatedCookie; } -export const getCookie = (name) => { - let cookies = document.cookie.split('; ').reduce((res, el) => { - let cookie = el.split('='); +export const getCookie = (name: string) => { + let cookies = document.cookie.split('; ').reduce((res : any, el) => { + let cookie: Array = el.split('='); + res[cookie[0]] = cookie[1]; return res; }, {}); @@ -40,6 +54,6 @@ export const getCookie = (name) => { return cookies[name]; } -export const getAccessToken = (accessToken) => { +export const getAccessToken = (accessToken: string) => { return accessToken.split('Bearer ')[1]; } diff --git a/src/utils/data.js b/src/utils/data.js deleted file mode 100644 index 6ff8c71..0000000 --- a/src/utils/data.js +++ /dev/null @@ -1,214 +0,0 @@ -const INGREDIENTS = [ - { - "_id":"60666c42cc7b410027a1a9b1", - "name":"Краторная булка N-200i", - "type":"bun", - "proteins":80, - "fat":24, - "carbohydrates":53, - "calories":420, - "price":1255, - "image":"https://code.s3.yandex.net/react/code/bun-02.png", - "image_mobile":"https://code.s3.yandex.net/react/code/bun-02-mobile.png", - "image_large":"https://code.s3.yandex.net/react/code/bun-02-large.png", - "__v":0 - }, - { - "_id":"60666c42cc7b410027a1a9b5", - "name":"Говяжий метеорит (отбивная)", - "type":"main", - "proteins":800, - "fat":800, - "carbohydrates":300, - "calories":2674, - "price":3000, - "image":"https://code.s3.yandex.net/react/code/meat-04.png", - "image_mobile":"https://code.s3.yandex.net/react/code/meat-04-mobile.png", - "image_large":"https://code.s3.yandex.net/react/code/meat-04-large.png", - "__v":0 - }, - { - "_id":"60666c42cc7b410027a1a9b6", - "name":"Биокотлета из марсианской Магнолии", - "type":"main", - "proteins":420, - "fat":142, - "carbohydrates":242, - "calories":4242, - "price":424, - "image":"https://code.s3.yandex.net/react/code/meat-01.png", - "image_mobile":"https://code.s3.yandex.net/react/code/meat-01-mobile.png", - "image_large":"https://code.s3.yandex.net/react/code/meat-01-large.png", - "__v":0 - }, - { - "_id":"60666c42cc7b410027a1a9b7", - "name":"Соус Spicy-X", - "type":"sauce", - "proteins":30, - "fat":20, - "carbohydrates":40, - "calories":30, - "price":90, - "image":"https://code.s3.yandex.net/react/code/sauce-02.png", - "image_mobile":"https://code.s3.yandex.net/react/code/sauce-02-mobile.png", - "image_large":"https://code.s3.yandex.net/react/code/sauce-02-large.png", - "__v":0 - }, - { - "_id":"60666c42cc7b410027a1a9b4", - "name":"Мясо бессмертных моллюсков Protostomia", - "type":"main", - "proteins":433, - "fat":244, - "carbohydrates":33, - "calories":420, - "price":1337, - "image":"https://code.s3.yandex.net/react/code/meat-02.png", - "image_mobile":"https://code.s3.yandex.net/react/code/meat-02-mobile.png", - "image_large":"https://code.s3.yandex.net/react/code/meat-02-large.png", - "__v":0 - }, - { - "_id":"60666c42cc7b410027a1a9b9", - "name":"Соус традиционный галактический", - "type":"sauce", - "proteins":42, - "fat":24, - "carbohydrates":42, - "calories":99, - "price":15, - "image":"https://code.s3.yandex.net/react/code/sauce-03.png", - "image_mobile":"https://code.s3.yandex.net/react/code/sauce-03-mobile.png", - "image_large":"https://code.s3.yandex.net/react/code/sauce-03-large.png", - "__v":0 - }, - { - "_id":"60666c42cc7b410027a1a9b8", - "name":"Соус фирменный Space Sauce", - "type":"sauce", - "proteins":50, - "fat":22, - "carbohydrates":11, - "calories":14, - "price":80, - "image":"https://code.s3.yandex.net/react/code/sauce-04.png", - "image_mobile":"https://code.s3.yandex.net/react/code/sauce-04-mobile.png", - "image_large":"https://code.s3.yandex.net/react/code/sauce-04-large.png", - "__v":0 - }, - { - "_id":"60666c42cc7b410027a1a9bc", - "name":"Плоды Фалленианского дерева", - "type":"main", - "proteins":20, - "fat":5, - "carbohydrates":55, - "calories":77, - "price":874, - "image":"https://code.s3.yandex.net/react/code/sp_1.png", - "image_mobile":"https://code.s3.yandex.net/react/code/sp_1-mobile.png", - "image_large":"https://code.s3.yandex.net/react/code/sp_1-large.png", - "__v":0 - }, - { - "_id":"60666c42cc7b410027a1a9bb", - "name":"Хрустящие минеральные кольца", - "type":"main", - "proteins":808, - "fat":689, - "carbohydrates":609, - "calories":986, - "price":300, - "image":"https://code.s3.yandex.net/react/code/mineral_rings.png", - "image_mobile":"https://code.s3.yandex.net/react/code/mineral_rings-mobile.png", - "image_large":"https://code.s3.yandex.net/react/code/mineral_rings-large.png", - "__v":0 - }, - { - "_id":"60666c42cc7b410027a1a9ba", - "name":"Соус с шипами Антарианского плоскоходца", - "type":"sauce", - "proteins":101, - "fat":99, - "carbohydrates":100, - "calories":100, - "price":88, - "image":"https://code.s3.yandex.net/react/code/sauce-01.png", - "image_mobile":"https://code.s3.yandex.net/react/code/sauce-01-mobile.png", - "image_large":"https://code.s3.yandex.net/react/code/sauce-01-large.png", - "__v":0 - }, - { - "_id":"60666c42cc7b410027a1a9bd", - "name":"Кристаллы марсианских альфа-сахаридов", - "type":"main", - "proteins":234, - "fat":432, - "carbohydrates":111, - "calories":189, - "price":762, - "image":"https://code.s3.yandex.net/react/code/core.png", - "image_mobile":"https://code.s3.yandex.net/react/code/core-mobile.png", - "image_large":"https://code.s3.yandex.net/react/code/core-large.png", - "__v":0 - }, - { - "_id":"60666c42cc7b410027a1a9be", - "name":"Мини-салат Экзо-Плантаго", - "type":"main", - "proteins":1, - "fat":2, - "carbohydrates":3, - "calories":6, - "price":4400, - "image":"https://code.s3.yandex.net/react/code/salad.png", - "image_mobile":"https://code.s3.yandex.net/react/code/salad-mobile.png", - "image_large":"https://code.s3.yandex.net/react/code/salad-large.png", - "__v":0 - }, - { - "_id":"60666c42cc7b410027a1a9b3", - "name":"Филе Люминесцентного тетраодонтимформа", - "type":"main", - "proteins":44, - "fat":26, - "carbohydrates":85, - "calories":643, - "price":988, - "image":"https://code.s3.yandex.net/react/code/meat-03.png", - "image_mobile":"https://code.s3.yandex.net/react/code/meat-03-mobile.png", - "image_large":"https://code.s3.yandex.net/react/code/meat-03-large.png", - "__v":0 - }, - { - "_id":"60666c42cc7b410027a1a9bf", - "name":"Сыр с астероидной плесенью", - "type":"main", - "proteins":84, - "fat":48, - "carbohydrates":420, - "calories":3377, - "price":4142, - "image":"https://code.s3.yandex.net/react/code/cheese.png", - "image_mobile":"https://code.s3.yandex.net/react/code/cheese-mobile.png", - "image_large":"https://code.s3.yandex.net/react/code/cheese-large.png", - "__v":0 - }, - { - "_id":"60666c42cc7b410027a1a9b2", - "name":"Флюоресцентная булка R2-D3", - "type":"bun", - "proteins":44, - "fat":26, - "carbohydrates":85, - "calories":643, - "price":988, - "image":"https://code.s3.yandex.net/react/code/bun-01.png", - "image_mobile":"https://code.s3.yandex.net/react/code/bun-01-mobile.png", - "image_large":"https://code.s3.yandex.net/react/code/bun-01-large.png", - "__v":0 - } -]; - -export { INGREDIENTS }; diff --git a/src/utils/date.js b/src/utils/date.js deleted file mode 100644 index 427a626..0000000 --- a/src/utils/date.js +++ /dev/null @@ -1,23 +0,0 @@ -export function getFormattedDate(dateStr) { - const dateNow = new Date(); - const date = new Date(dateStr); - const dateTimezone = 'i-GMT' + date.getTimezoneOffset()/60; - const hours = date.getHours(); - const formattedHours = hours < 10 ? '0' + hours : hours; - const minutes = date.getMinutes(); - const formattedMinutes = minutes < 10 ? '0' + minutes : minutes; - const dateOffsetHours = (dateNow - date)/3600000; - - let dateFormat = ''; - - if (dateNow.toDateString() === date.toDateString()) { // Если даты совпадают - dateFormat = 'Сегодня'; - } else if (dateOffsetHours < 24) { // Если прошло не более суток - dateFormat = 'Вчера'; - } else { - dateFormat = Math.floor(dateOffsetHours/24); - dateFormat < 5 ? dateFormat += ' дня' : dateFormat += ' дней'; - } - - return dateFormat + ' ' + formattedHours + ':' + formattedMinutes + ' ' + dateTimezone; -} \ No newline at end of file diff --git a/src/utils/date.tsx b/src/utils/date.tsx new file mode 100644 index 0000000..ff2f6df --- /dev/null +++ b/src/utils/date.tsx @@ -0,0 +1,24 @@ +export function getFormattedDate(dateStr: string) { + const dateNow: Date = new Date(); + const date: Date = new Date(dateStr); + const dateTimezone: string = 'i-GMT' + date.getTimezoneOffset()/60; + const hours: number = date.getHours(); + const formattedHours: string = hours < 10 ? '0' + hours : hours.toString(); + const minutes: number = date.getMinutes(); + const formattedMinutes: string = minutes < 10 ? '0' + minutes : minutes.toString(); + const dateOffsetHours: number = (dateNow.getTime() - date.getTime())/3600000; + + let dateFormat: string = ''; + + if (dateNow.toDateString() === date.toDateString()) { // Если даты совпадают + dateFormat = 'Сегодня'; + } else if (dateOffsetHours < 24) { // Если прошло не более суток + dateFormat = 'Вчера'; + } else { + const dateNumber: number = Math.floor(dateOffsetHours/24); + dateFormat += dateNumber; + dateNumber < 5 ? dateFormat += ' дня' : dateFormat += ' дней'; + } + + return dateFormat + ' ' + formattedHours + ':' + formattedMinutes + ' ' + dateTimezone; +} \ No newline at end of file diff --git a/src/utils/order.js b/src/utils/order.js deleted file mode 100644 index 6ec4aa9..0000000 --- a/src/utils/order.js +++ /dev/null @@ -1,132 +0,0 @@ -const ORDER = { - 'bun': { - "_id": "60d3b41abdacab0026a733c6", - "name": "Краторная булка N-200i", - "type": "bun", - "proteins": 80, - "fat": 24, - "carbohydrates": 53, - "calories": 420, - "price": 1255, - "image": "https://code.s3.yandex.net/react/code/bun-02.png", - "image_mobile": "https://code.s3.yandex.net/react/code/bun-02-mobile.png", - "image_large": "https://code.s3.yandex.net/react/code/bun-02-large.png", - "__v": 0 - }, - 'others': [ - { - "_id": "60d3b41abdacab0026a733c9", - "name": "Мясо бессмертных моллюсков Protostomia", - "type": "main", - "proteins": 433, - "fat": 244, - "carbohydrates": 33, - "calories": 420, - "price": 1337, - "image": "https://code.s3.yandex.net/react/code/meat-02.png", - "image_mobile": "https://code.s3.yandex.net/react/code/meat-02-mobile.png", - "image_large": "https://code.s3.yandex.net/react/code/meat-02-large.png", - "__v": 0 - }, - { - "_id": "60d3b41abdacab0026a733cb", - "name": "Биокотлета из марсианской Магнолии", - "type": "main", - "proteins": 420, - "fat": 142, - "carbohydrates": 242, - "calories": 4242, - "price": 424, - "image": "https://code.s3.yandex.net/react/code/meat-01.png", - "image_mobile": "https://code.s3.yandex.net/react/code/meat-01-mobile.png", - "image_large": "https://code.s3.yandex.net/react/code/meat-01-large.png", - "__v": 0 - }, - { - "_id": "60d3b41abdacab0026a733cc", - "name": "Соус Spicy-X", - "type": "sauce", - "proteins": 30, - "fat": 20, - "carbohydrates": 40, - "calories": 30, - "price": 90, - "image": "https://code.s3.yandex.net/react/code/sauce-02.png", - "image_mobile": "https://code.s3.yandex.net/react/code/sauce-02-mobile.png", - "image_large": "https://code.s3.yandex.net/react/code/sauce-02-large.png", - "__v": 0 - }, - { - "_id": "60d3b41abdacab0026a733d1", - "name": "Плоды Фалленианского дерева", - "type": "main", - "proteins": 20, - "fat": 5, - "carbohydrates": 55, - "calories": 77, - "price": 874, - "image": "https://code.s3.yandex.net/react/code/sp_1.png", - "image_mobile": "https://code.s3.yandex.net/react/code/sp_1-mobile.png", - "image_large": "https://code.s3.yandex.net/react/code/sp_1-large.png", - "__v": 0 - }, - { - "_id": "60d3b41abdacab0026a733d0", - "name": "Хрустящие минеральные кольца", - "type": "main", - "proteins": 808, - "fat": 689, - "carbohydrates": 609, - "calories": 986, - "price": 300, - "image": "https://code.s3.yandex.net/react/code/mineral_rings.png", - "image_mobile": "https://code.s3.yandex.net/react/code/mineral_rings-mobile.png", - "image_large": "https://code.s3.yandex.net/react/code/mineral_rings-large.png", - "__v": 0 - }, - { - "_id": "60d3b41abdacab0026a733d3", - "name": "Мини-салат Экзо-Плантаго", - "type": "main", - "proteins": 1, - "fat": 2, - "carbohydrates": 3, - "calories": 6, - "price": 4400, - "image": "https://code.s3.yandex.net/react/code/salad.png", - "image_mobile": "https://code.s3.yandex.net/react/code/salad-mobile.png", - "image_large": "https://code.s3.yandex.net/react/code/salad-large.png", - "__v": 0 - }, - { - "_id": "60d3b41abdacab0026a733d3", - "name": "Мини-салат Экзо-Плантаго", - "type": "main", - "proteins": 1, - "fat": 2, - "carbohydrates": 3, - "calories": 6, - "price": 4400, - "image": "https://code.s3.yandex.net/react/code/salad.png", - "image_mobile": "https://code.s3.yandex.net/react/code/salad-mobile.png", - "image_large": "https://code.s3.yandex.net/react/code/salad-large.png", - "__v": 0 - }, - { - "_id": "60d3b41abdacab0026a733d4", - "name": "Сыр с астероидной плесенью", - "type": "main", - "proteins": 84, - "fat": 48, - "carbohydrates": 420, - "calories": 3377, - "price": 4142, - "image": "https://code.s3.yandex.net/react/code/cheese.png", - "image_mobile": "https://code.s3.yandex.net/react/code/cheese-mobile.png", - "image_large": "https://code.s3.yandex.net/react/code/cheese-large.png", - "__v": 0 - } - ] -} - -export { ORDER }; From 0ae4e8131982d404eb3e44f412419b36bfff81a5 Mon Sep 17 00:00:00 2001 From: Yulia Berezhnaya Date: Thu, 30 Mar 2023 15:24:45 +0300 Subject: [PATCH 03/13] =?UTF-8?q?=D0=9F=D0=B5=D1=80=D0=B2=D0=B0=D1=8F=20?= =?UTF-8?q?=D0=B2=D0=B5=D1=80=D1=81=D0=B8=D1=8F=20=D1=82=D0=B8=D0=BF=D0=B8?= =?UTF-8?q?=D0=B7=D0=B0=D1=86=D0=B8=D0=B8=20=D1=8D=D0=BA=D1=88=D0=BD=D0=BE?= =?UTF-8?q?=D0=B2=20=D0=B8=20=D0=B0=D0=BF=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/services/actions/ingredients.js | 86 --------- src/services/actions/ingredients.tsx | 135 +++++++++++++ src/services/actions/{user.js => user.tsx} | 151 +++++++-------- src/services/reducers/index.js | 12 +- src/utils/{api.js => api.tsx} | 26 +-- src/utils/constants.tsx | 6 +- src/utils/cookie.tsx | 10 +- .../{TIngredient.tsx => ingredientsTypes.tsx} | 10 +- src/utils/requestTypes.tsx | 24 +++ src/utils/userTypes.tsx | 180 ++++++++++++++++++ 10 files changed, 449 insertions(+), 191 deletions(-) delete mode 100644 src/services/actions/ingredients.js create mode 100644 src/services/actions/ingredients.tsx rename src/services/actions/{user.js => user.tsx} (52%) rename src/utils/{api.js => api.tsx} (70%) rename src/utils/{TIngredient.tsx => ingredientsTypes.tsx} (57%) create mode 100644 src/utils/requestTypes.tsx create mode 100644 src/utils/userTypes.tsx diff --git a/src/services/actions/ingredients.js b/src/services/actions/ingredients.js deleted file mode 100644 index d021872..0000000 --- a/src/services/actions/ingredients.js +++ /dev/null @@ -1,86 +0,0 @@ -import { getIngredientsRequest, sendOrderRequest } from "../../utils/api"; -import { - GET_INGREDIENTS_REQUEST, - GET_INGREDIENTS_SUCCESS, - GET_INGREDIENTS_FAILED, - GET_ORDER_REQUEST, - GET_ORDER_SUCCESS, - GET_ORDER_FAILED - } from '../../utils/constants'; - import { getAccessToken, getCookie, setCookie } from "../../utils/cookie"; - import { refreshTokenRequest } from "../../utils/api"; - -export function getIngredients() { - return function(dispatch) { - dispatch({type: GET_INGREDIENTS_REQUEST}); - - getIngredientsRequest() - .then((res) => { - if (res && res.success) { - const ingredientsById = {}; - const ingredientsByType = {}; - - - res.data.forEach((item) => { - ingredientsById[item._id] = item; - (ingredientsByType[item.type] || (ingredientsByType[item.type] = [])).push(item); - }); - - dispatch({ - type: GET_INGREDIENTS_SUCCESS, - ingredientsByType, - ingredientsById - }) - } else { - dispatch({type: GET_INGREDIENTS_FAILED}); - } - }) - .catch((err) => { - dispatch({type: GET_INGREDIENTS_FAILED}); - }) - } -} - -export function sendOrder(ids) { - return function(dispatch) { - dispatch({type: GET_ORDER_REQUEST}); - const accessToken = getCookie('accessToken'); - - sendOrderRequest(ids, accessToken) - .then((res) => { - if (res && res.success) { - dispatch({ - type: GET_ORDER_SUCCESS, - orderId: res.order.number - }) - } else if (res.message === "jwt expired") { - - refreshTokenRequest(getCookie('token')) - .then((res) => { - if (res && res.success) { - setCookie('accessToken', getAccessToken(res.accessToken)); - setCookie('token', res.refreshToken); - - sendOrderRequest(ids, getAccessToken(res.accessToken)) - .then((res) => { - if (res && res.success) { - dispatch({ - type: GET_ORDER_SUCCESS, - orderId: res.order.number - }) - } - }) - .catch((err) => { - dispatch({type: GET_ORDER_FAILED}); - }) - } - }) - } else { - dispatch({type: GET_ORDER_FAILED}); - } - }) - .catch((err) => { - dispatch({type: GET_ORDER_FAILED}); - }) - } -} diff --git a/src/services/actions/ingredients.tsx b/src/services/actions/ingredients.tsx new file mode 100644 index 0000000..38c7e8d --- /dev/null +++ b/src/services/actions/ingredients.tsx @@ -0,0 +1,135 @@ +import { getIngredientsRequest, sendOrderRequest } from "../../utils/api"; +import { + GET_INGREDIENTS_REQUEST, + GET_INGREDIENTS_SUCCESS, + GET_INGREDIENTS_FAILED, + SEND_ORDER_REQUEST, + SEND_ORDER_SUCCESS, + SEND_ORDER_FAILED + } from '../../utils/constants'; + import { TIngredient, TIngredientsByType, TIngredientsById } from "../../utils/ingredientsTypes"; + import { getAccessToken, getCookie, setCookie } from "../../utils/cookie"; + import { refreshTokenRequest } from "../../utils/api"; + +// Типизация получения списка ингридиентов +interface IGetIngredientsAction { + readonly type: typeof GET_INGREDIENTS_REQUEST; +} + +interface IGetIngredientsFailedAction { + readonly type: typeof GET_INGREDIENTS_FAILED; +} + +interface IGetCountriesSuccessAction { + readonly type: typeof GET_INGREDIENTS_SUCCESS; + readonly ingredientsByType: TIngredientsByType; + readonly ingredientsById: TIngredientsById; +} + +const getIngredientsAction = (): IGetIngredientsAction => ({ + type: GET_INGREDIENTS_REQUEST +}); +const getIngredientsSuccessAction = +(ingredientsByType: TIngredientsByType, ingredientsById: TIngredientsById): IGetCountriesSuccessAction => ({ + type: GET_INGREDIENTS_SUCCESS, + ingredientsByType, + ingredientsById +}); +const getIngredientsFailedAction = (): IGetIngredientsFailedAction => ({ + type: GET_INGREDIENTS_FAILED +}); + +// Типизация создания заказа +interface ISendOrderAction { + readonly type: typeof SEND_ORDER_REQUEST; +} + +interface ISendOrderFailedAction { + readonly type: typeof SEND_ORDER_FAILED; +} + +interface ISendOrderSuccessAction { + readonly type: typeof SEND_ORDER_SUCCESS; + readonly orderId: number; +} + +const sendOrderAction = (): ISendOrderAction => ({ + type: SEND_ORDER_REQUEST +}); + +const sendOrderSuccessAction = (orderId: number): ISendOrderSuccessAction => ({ + type: SEND_ORDER_SUCCESS, + orderId +}); + +const sendOrderFailedAction = (): ISendOrderFailedAction => ({ + type: SEND_ORDER_FAILED +}); + + + +export function getIngredients() { + return function(dispatch: any) { + dispatch(getIngredientsAction()); + + getIngredientsRequest() + .then((res) => { + if (res && res.success) { + const ingredientsById: TIngredientsById = {}; + const ingredientsByType: TIngredientsByType = {}; + + res.data.forEach((item: TIngredient) => { + ingredientsById[item._id] = item; + (ingredientsByType[item.type] || (ingredientsByType[item.type] = [])).push(item); + }); + + dispatch(getIngredientsSuccessAction(ingredientsByType, ingredientsById)) + } else { + // Пробрасывать ошибку и обрабатывать все только в catch + dispatch(getIngredientsFailedAction()); + } + }) + .catch(() => { + dispatch(getIngredientsFailedAction()); + }) + } +} + +export function sendOrder(ids: Array) { + return function(dispatch: any) { + dispatch(sendOrderAction()); + + const accessToken: string = getCookie('accessToken'); + + sendOrderRequest(ids, accessToken) + .then((res) => { + if (res && res.success) { + dispatch(sendOrderSuccessAction(res.order.number)); + } else if (res.message === "jwt expired") { + + refreshTokenRequest(getCookie('token')) + .then((res) => { + if (res && res.success) { + setCookie('accessToken', getAccessToken(res.accessToken)); + setCookie('token', res.refreshToken); + + sendOrderRequest(ids, getAccessToken(res.accessToken)) + .then((res) => { + if (res && res.success) { + dispatch(sendOrderSuccessAction(res.order.number)); + } + }) + .catch(() => { + dispatch(sendOrderFailedAction()); + }) + } + }) + } else { + dispatch(sendOrderFailedAction()); + } + }) + .catch(() => { + dispatch(sendOrderFailedAction()); + }) + } +} diff --git a/src/services/actions/user.js b/src/services/actions/user.tsx similarity index 52% rename from src/services/actions/user.js rename to src/services/actions/user.tsx index 520a54a..1c65380 100644 --- a/src/services/actions/user.js +++ b/src/services/actions/user.tsx @@ -9,52 +9,54 @@ import { passwordResetRequest } from "../../utils/api"; import { setCookie, getCookie, getAccessToken } from "../../utils/cookie"; +import { + TUser, + addUserAction, + addUserSuccessAction, + addUserFailedAction, + loginUserAction, + loginUserSuccessAction, + loginUserFailedAction, + getUserSuccessAction, + getUserFailedAction, + editUserSuccessAction, + editUserFailedAction, + forgotPasswordSuccessAction, + forgotPasswordFailedAction, + resetPasswordSuccessAction, + resetPasswordFailedAction, + logoutUserSuccessAction, + logoutUserFailedAction + } from "../../utils/userTypes"; + import { + TRequestRetryOptions, + requestRetryOnFail, + requestRetryOnSuccess , + requestRetry +} from "../../utils/requestTypes"; import { SET_REGISTER_FORM_VALUE, SET_EDIT_USER_FORM, +} from '../../utils/constants'; - ADD_USER_REQUEST, - ADD_USER_SUCCESS, - ADD_USER_FAILED, - - LOGIN_USER_REQUEST, - LOGIN_USER_SUCCESS, - LOGIN_USER_FAILED, - - GET_USER_SUCCESS, - GET_USER_FAILED, - - EDIT_USER_SUCCESS, - EDIT_USER_FAILED, - - FORGOT_PASSWORD_SUCCESS, - FORGOT_PASSWORD_FAILED, - - RESET_PASSWORD_SUCCESS, - RESET_PASSWORD_FAILED, - - LOGOUT_USER_SUCCESS, - LOGOUT_USER_FAILED, - GET_USER_ORDERS - } from '../../utils/constants'; -export const setRegisterFormValue = (field, value) => ({ +export const setRegisterFormValue = (field: string, value: string) => ({ type: SET_REGISTER_FORM_VALUE, field, value }); -export const setEditUserForm = (field, value) => ({ +export const setEditUserForm = (field: string, value: string) => ({ type: SET_EDIT_USER_FORM, field, value }); // Добавление нового пользователя -export function addUser(user) { - return function(dispatch) { - dispatch({type: ADD_USER_REQUEST}); +export function addUser(user: TUser) { + return function(dispatch: any) { + dispatch(addUserAction()); addUserRequest(user) .then((res) => { @@ -63,111 +65,102 @@ export function addUser(user) { setCookie('token', res.refreshToken); setCookie('accessToken', accessToken); - dispatch({ - type: ADD_USER_SUCCESS, - user: res.user - }) + + dispatch(addUserSuccessAction(res.user)) }}) - .catch((err) => { - dispatch({type: ADD_USER_FAILED}); + .catch(() => { + dispatch(addUserFailedAction()); }) } } // Вход уже существующего пользователя -export function loginUser(user) { - return function(dispatch) { - dispatch({type: LOGIN_USER_REQUEST}); +export function loginUser(user: TUser) { + return function(dispatch: any) { + dispatch(loginUserAction()); loginRequest(user) .then((res) => { if (res && res.success) { - const accessToken = getAccessToken(res.accessToken); + const accessToken: string = getAccessToken(res.accessToken); setCookie('token', res.refreshToken); setCookie('accessToken', accessToken); - dispatch({ - type: LOGIN_USER_SUCCESS, - user: res.user - }) + dispatch(loginUserSuccessAction(res.user)); }}) .catch((err) => { - dispatch({type: LOGIN_USER_FAILED, errorMessage: err.message}); + dispatch(loginUserFailedAction(err.message)); }) } } // Получение информации о пользователе export function getUser() { - return function(dispatch) { + return function(dispatch: any) { getRequestWithRetry( getUserRequest, - ({user}) => dispatch({type: GET_USER_SUCCESS, user}), - () => dispatch({type: GET_USER_FAILED}) + ({user}) => dispatch(getUserSuccessAction(user)), + () => dispatch(getUserFailedAction()) ); } } -export function editUser(user) { - return function(dispatch) { +// Редактирование информации о пользователе +export function editUser(user: TUser) { + return function(dispatch: any) { getRequestWithRetry( editUserRequest, - ({user}) => dispatch({type: EDIT_USER_SUCCESS, user}), - () => dispatch({type: EDIT_USER_FAILED}), - {user} + ({user}) => dispatch(editUserSuccessAction(user)), + () => dispatch(editUserFailedAction()), + { user } ); } } -export function forgotPassword(email) { - return function(dispatch) { +export function forgotPassword(email: string) { + return function(dispatch: any) { passwordForgotRequest(email) .then((res) => { if (res && res.success) { - dispatch({ - type: FORGOT_PASSWORD_SUCCESS, - user: res.user - }); + dispatch(forgotPasswordSuccessAction(res.user)); } }) - .catch((err) => { - dispatch({type: FORGOT_PASSWORD_FAILED}); + .catch(() => { + dispatch(forgotPasswordFailedAction()); }) } } -export function resetPassword(password, code) { - return function(dispatch) { +export function resetPassword(password: string, code: number) { + return function(dispatch: any) { passwordResetRequest(password, code) .then((res) => { if (res && res.success) { - dispatch({type: RESET_PASSWORD_SUCCESS}) + dispatch(resetPasswordSuccessAction()) } }) - .catch((err) => { - dispatch({type: RESET_PASSWORD_FAILED}); + .catch(() => { + dispatch(resetPasswordFailedAction()); }) } } export function logoutUser() { - return function(dispatch) { + return function(dispatch: any) { getRequestWithRetry( logoutRequest, - (res) => { - setCookie('accessToken', '', {Expires: 0}); - setCookie('token', '', {Expires: 0}); - dispatch({ - type: LOGOUT_USER_SUCCESS - }) + () => { + setCookie('accessToken', '', {expires: 0}); + setCookie('token', '', {expires: 0}); + dispatch(logoutUserSuccessAction()) }, ({message}) => { - dispatch({type: LOGOUT_USER_FAILED, errorMessage: message}); + dispatch(logoutUserFailedAction(message)); }, {token: getCookie('token')} ) @@ -181,15 +174,17 @@ export function logoutUser() { * @param {string} onSuccess - функция для обработки успешного результата * @param {string} onFail - функция для обработки ошибок */ -function getRequestWithRetry(getRequest, onSuccess, onFail, options = {}) { + +// TODO что делать с options и getRequest??? +function getRequestWithRetry(getRequest: requestRetry, onSuccess: requestRetryOnSuccess, onFail: requestRetryOnFail, options: any = {}) { options.accessToken = getCookie('accessToken'); getRequest(options) - .then((res) => { + .then((res: any) => { if (res && res.success) { onSuccess(res); }}) - .catch((err) => { + .catch((err: any) => { if (err.cause.message === "jwt expired") { return refreshTokenRequest(getCookie('token')) .then((res) => { @@ -200,7 +195,7 @@ function getRequestWithRetry(getRequest, onSuccess, onFail, options = {}) { options.accessToken = getAccessToken(res.accessToken); return getRequest(options) - .then((res) => { + .then((res: any) => { if (res && res.success) { onSuccess(res) } @@ -211,7 +206,7 @@ function getRequestWithRetry(getRequest, onSuccess, onFail, options = {}) { return err; }) - .catch((err) => { + .catch((err: any) => { onFail(err); }) } diff --git a/src/services/reducers/index.js b/src/services/reducers/index.js index 38b4237..c079b69 100644 --- a/src/services/reducers/index.js +++ b/src/services/reducers/index.js @@ -5,9 +5,9 @@ import { GET_INGREDIENTS_REQUEST, GET_INGREDIENTS_SUCCESS, GET_INGREDIENTS_FAILED, - GET_ORDER_REQUEST, - GET_ORDER_SUCCESS, - GET_ORDER_FAILED, + SEND_ORDER_REQUEST, + SEND_ORDER_SUCCESS, + SEND_ORDER_FAILED, SHOW_INGREDIENT, HIDE_INGREDIENT, ADD_INGREDIENT, @@ -61,14 +61,14 @@ export const ingredientsReducer = (state = initialMenuState, action) => { ingredientsFailed: true } } - case GET_ORDER_REQUEST: { + case SEND_ORDER_REQUEST: { return { ...state, orderRequest: true, orderFailed: false } } - case GET_ORDER_SUCCESS: { + case SEND_ORDER_SUCCESS: { return { ...state, orderRequest: false, @@ -79,7 +79,7 @@ export const ingredientsReducer = (state = initialMenuState, action) => { } } } - case GET_ORDER_FAILED: { + case SEND_ORDER_FAILED: { return { ...state, orderRequest: false, diff --git a/src/utils/api.js b/src/utils/api.tsx similarity index 70% rename from src/utils/api.js rename to src/utils/api.tsx index 862d10a..fd978bf 100644 --- a/src/utils/api.js +++ b/src/utils/api.tsx @@ -9,21 +9,25 @@ import { PASSWORDFORGOTURL, PASSWORDRESETURL } from "./constants"; +import { IEditUserOptions, IGetUserOptions, ILogoutUserOptions } from './requestTypes'; +import { TUser } from "./userTypes"; -const checkResponse = async (data) => { +// TODO: нужно ли типизировать data? Должен быть {ok: boolean, json?: () => any } ??? +const checkResponse = async (data: any) => { if (!data.ok) { throw new Error(data.message, { cause: await data.json() }); } return data.json(); } -const getIngredientsRequest = async () => { +// TODO: как вообще типизировать ответ сервера??? +const getIngredientsRequest = async (): Promise => { const res = await fetch(GETINGREDIENTSURL); return await checkResponse(res); } -const sendOrderRequest = async (ingredientIds, accessToken) => { +const sendOrderRequest = async (ingredientIds: Array, accessToken: string): Promise => { const res = await fetch(SAVEORDERURL, { method: 'POST', headers: { @@ -38,7 +42,7 @@ const sendOrderRequest = async (ingredientIds, accessToken) => { return await checkResponse(res); } -const getUserRequest = async ({accessToken}) => { +const getUserRequest = async ({accessToken}: IGetUserOptions): Promise => { const res = await fetch(USERURL, { method: 'GET', headers: { @@ -50,7 +54,7 @@ const getUserRequest = async ({accessToken}) => { return await checkResponse(res); } -const editUserRequest = async ({user, accessToken}) => { +const editUserRequest = async ({user, accessToken} : IEditUserOptions): Promise => { const res = await fetch(USERURL, { method: 'PATCH', headers: { @@ -63,7 +67,7 @@ const editUserRequest = async ({user, accessToken}) => { return checkResponse(res); } -const addUserRequest = async (user) => { +const addUserRequest = async (user: TUser): Promise => { const res = await fetch(ADDUSERURL, { method: 'POST', headers: { @@ -75,7 +79,7 @@ const addUserRequest = async (user) => { return await checkResponse(res); } -const loginRequest = async (user) => { +const loginRequest = async (user: TUser): Promise => { const res = await fetch(LOGINUSERURL, { method: 'POST', headers: { @@ -87,7 +91,7 @@ const loginRequest = async (user) => { return await checkResponse(res); } -const logoutRequest = async ({token}) => { +const logoutRequest = async ({token}: ILogoutUserOptions): Promise => { const res = await fetch(LOGOUTUSERURL, { method: 'POST', headers: { @@ -99,7 +103,7 @@ const logoutRequest = async ({token}) => { return await checkResponse(res); } -const refreshTokenRequest = async (token) => { +const refreshTokenRequest = async (token: string): Promise => { const res = await fetch(REFRESHTOKENURL, { method: 'POST', headers: { @@ -111,7 +115,7 @@ const refreshTokenRequest = async (token) => { return await checkResponse(res); } -const passwordForgotRequest = async (email) => { +const passwordForgotRequest = async (email: string): Promise => { const res = await fetch(PASSWORDFORGOTURL, { method: 'POST', headers: { @@ -123,7 +127,7 @@ const passwordForgotRequest = async (email) => { return await checkResponse(res); } -const passwordResetRequest = async (password, token) => { +const passwordResetRequest = async (password: string, token: number): Promise => { const res = await fetch(PASSWORDRESETURL, { method: 'POST', headers: { diff --git a/src/utils/constants.tsx b/src/utils/constants.tsx index 0fdfb4f..6831976 100644 --- a/src/utils/constants.tsx +++ b/src/utils/constants.tsx @@ -25,9 +25,9 @@ export const GET_INGREDIENTS_SUCCESS: 'GET_INGREDIENTS_SUCCESS' = 'GET_INGREDIEN export const GET_INGREDIENTS_FAILED: 'GET_INGREDIENTS_FAILED' = 'GET_INGREDIENTS_FAILED'; // Константы для обработки запроса оформления заказа -export const GET_ORDER_REQUEST: 'GET_ORDER_REQUEST'= 'GET_ORDER_REQUEST'; -export const GET_ORDER_SUCCESS: 'GET_ORDER_SUCCESS' = 'GET_ORDER_SUCCESS'; -export const GET_ORDER_FAILED: 'GET_ORDER_FAILED' = 'GET_ORDER_FAILED'; +export const SEND_ORDER_REQUEST: 'SEND_ORDER_REQUEST'= 'SEND_ORDER_REQUEST'; +export const SEND_ORDER_SUCCESS: 'SEND_ORDER_SUCCESS' = 'SEND_ORDER_SUCCESS'; +export const SEND_ORDER_FAILED: 'SEND_ORDER_FAILED' = 'SEND_ORDER_FAILED'; // Константы для получения/удаления данных об отдельном ингридиенте export const SHOW_INGREDIENT: 'SHOW_INGREDIENT' = 'SHOW_INGREDIENT'; diff --git a/src/utils/cookie.tsx b/src/utils/cookie.tsx index 55d696d..4515ff7 100644 --- a/src/utils/cookie.tsx +++ b/src/utils/cookie.tsx @@ -1,6 +1,6 @@ -type ICookieProps = { - path: string; - expires?: Date | string; +type TCookieProps = { + path?: string; + expires?: Date | string | number; domain?: string; httpOnly?: boolean; sameSite?: boolean; @@ -9,7 +9,7 @@ type ICookieProps = { maxAge?: number; } -export const setCookie = (name: string, value: string, props?: ICookieProps) => { +export const setCookie = (name: string, value: string, props?: TCookieProps) => { props = { path: '/', ...props @@ -30,7 +30,7 @@ export const setCookie = (name: string, value: string, props?: ICookieProps) => value = encodeURIComponent(value); let updatedCookie = name + '=' + value; - let propName: keyof ICookieProps; + let propName: keyof TCookieProps; for (propName in props) { updatedCookie += '; ' + propName; diff --git a/src/utils/TIngredient.tsx b/src/utils/ingredientsTypes.tsx similarity index 57% rename from src/utils/TIngredient.tsx rename to src/utils/ingredientsTypes.tsx index 916c899..afdeb54 100644 --- a/src/utils/TIngredient.tsx +++ b/src/utils/ingredientsTypes.tsx @@ -1,4 +1,4 @@ -type TIngredient = { +export type TIngredient = { _id: string; name: string; type: string; @@ -13,4 +13,10 @@ type TIngredient = { __v?: number; }; -export default TIngredient; +export type TIngredientsByType = { + [name: string]: Array; +} + +export type TIngredientsById = { + [name: string] : TIngredient; +} \ No newline at end of file diff --git a/src/utils/requestTypes.tsx b/src/utils/requestTypes.tsx new file mode 100644 index 0000000..cac5f57 --- /dev/null +++ b/src/utils/requestTypes.tsx @@ -0,0 +1,24 @@ +import { TUser } from "./userTypes"; + +export interface IGetUserOptions { + accessToken?: string; +} + +export interface IEditUserOptions { + accessToken?: string; + user?: TUser; +} + +export interface ILogoutUserOptions { + token?: string; +} + +export type TRequestRetryOptions = IGetUserOptions | IEditUserOptions | ILogoutUserOptions; + +export type TRequestRetryOnSuccess = { + [name: string]: string; +} + +export type requestRetry = (options: any) => Promise; +export type requestRetryOnFail = (error: any) => void; +export type requestRetryOnSuccess = (res: any) => void; diff --git a/src/utils/userTypes.tsx b/src/utils/userTypes.tsx new file mode 100644 index 0000000..df794b1 --- /dev/null +++ b/src/utils/userTypes.tsx @@ -0,0 +1,180 @@ +import { + ADD_USER_REQUEST, + ADD_USER_SUCCESS, + ADD_USER_FAILED, + + LOGIN_USER_REQUEST, + LOGIN_USER_SUCCESS, + LOGIN_USER_FAILED, + + GET_USER_SUCCESS, + GET_USER_FAILED, + + EDIT_USER_SUCCESS, + EDIT_USER_FAILED, + + FORGOT_PASSWORD_SUCCESS, + FORGOT_PASSWORD_FAILED, + + RESET_PASSWORD_SUCCESS, + RESET_PASSWORD_FAILED, + + LOGOUT_USER_SUCCESS, + LOGOUT_USER_FAILED + } from './constants'; + +export type TUser = { + name: string; + email: string; + password?: string; +}; + +// Типизация создания нового пользователя +interface IAddUserAction { + readonly type: typeof ADD_USER_REQUEST; +} + +interface IAddUserSuccessAction { + readonly type: typeof ADD_USER_SUCCESS; + user: TUser +} + +interface IAddUserFailedAction { + readonly type: typeof ADD_USER_FAILED; +} + +export const addUserAction = (): IAddUserAction => ({ + type: ADD_USER_REQUEST +}); + +export const addUserSuccessAction = (user: TUser): IAddUserSuccessAction => ({ + type: ADD_USER_SUCCESS, + user +}); + +export const addUserFailedAction = (): IAddUserFailedAction => ({ + type: ADD_USER_FAILED +}); + +// Типизация для логина пользователя +interface ILoginUserAction { + readonly type: typeof LOGIN_USER_REQUEST; +} + +interface ILoginUserSuccessAction { + readonly type: typeof LOGIN_USER_SUCCESS; + user: TUser +} + +interface ILoginUserFailedAction { + readonly type: typeof LOGIN_USER_FAILED; + readonly errorMessage: string; +} + +export const loginUserAction = (): ILoginUserAction => ({ + type: LOGIN_USER_REQUEST +}); + +export const loginUserSuccessAction = (user: TUser): ILoginUserSuccessAction => ({ + type: LOGIN_USER_SUCCESS, + user +}); + +export const loginUserFailedAction = (errorMessage: string): ILoginUserFailedAction => ({ + type: LOGIN_USER_FAILED, + errorMessage +}); + +// Типизация получения данных о пользователе +interface IGetUserSuccessAction { + readonly type: typeof GET_USER_SUCCESS; + user: TUser +} + +interface IGetUserFailedAction { + readonly type: typeof GET_USER_FAILED; +} + +export const getUserSuccessAction = (user: TUser): IGetUserSuccessAction => ({ + type: GET_USER_SUCCESS, + user +}); + +export const getUserFailedAction = (): IGetUserFailedAction => ({ + type: GET_USER_FAILED +}); + +// Типизация редактирования данных о пользователе +interface IEditUserSuccessAction { + readonly type: typeof EDIT_USER_SUCCESS; + user: TUser +} + +interface IEditUserFailedAction { + readonly type: typeof EDIT_USER_FAILED; +} + +export const editUserSuccessAction = (user: TUser): IEditUserSuccessAction => ({ + type: EDIT_USER_SUCCESS, + user +}); + +export const editUserFailedAction = (): IEditUserFailedAction => ({ + type: EDIT_USER_FAILED +}); + +// Типизация экшнов для забытого юзером пароля +interface IForgotPasswordSuccessAction { + readonly type: typeof RESET_PASSWORD_SUCCESS; + user: TUser +} + +interface IForgotPasswordFailedAction { + readonly type: typeof FORGOT_PASSWORD_FAILED; +} + +export const forgotPasswordSuccessAction = (user: TUser): IForgotPasswordSuccessAction => ({ + type: RESET_PASSWORD_SUCCESS, + user +}); + +export const forgotPasswordFailedAction = (): IForgotPasswordFailedAction => ({ + type: FORGOT_PASSWORD_FAILED +}); + + +// Типизация экшнов для сброса пароля +interface IResetPasswordSuccessAction { + readonly type: typeof FORGOT_PASSWORD_SUCCESS; +} + +interface IResetPasswordFailedAction { + readonly type: typeof RESET_PASSWORD_FAILED; +} + +export const resetPasswordSuccessAction = (): IResetPasswordSuccessAction => ({ + type: FORGOT_PASSWORD_SUCCESS +}); + +export const resetPasswordFailedAction = (): IResetPasswordFailedAction => ({ + type: RESET_PASSWORD_FAILED +}); + +// Типизация экшнов для логаута пользователя +interface ILogoutUserSuccessAction { + readonly type: typeof LOGOUT_USER_SUCCESS; +} + +interface ILogoutUserFailedAction { + readonly type: typeof LOGOUT_USER_FAILED; + readonly errorMessage: string; +} + +export const logoutUserSuccessAction = (): ILogoutUserSuccessAction => ({ + type: LOGOUT_USER_SUCCESS +}); + +export const logoutUserFailedAction = (errorMessage: string): ILogoutUserFailedAction => ({ + type: LOGOUT_USER_FAILED, + errorMessage +}); From 016aec322a6568d40df257c251c0365ebd866da8 Mon Sep 17 00:00:00 2001 From: Yulia Berezhnaya Date: Sat, 1 Apr 2023 17:43:27 +0300 Subject: [PATCH 04/13] =?UTF-8?q?=D0=A2=D0=B8=D0=BF=D0=B8=D0=B7=D0=B0?= =?UTF-8?q?=D1=86=D0=B8=D1=8F=20=D1=8D=D0=BA=D1=88=D0=BD=D0=BE=D0=B2=20?= =?UTF-8?q?=D0=B8=20=D1=80=D0=B5=D0=B4=D1=8C=D1=8E=D1=81=D0=B5=D1=80=D0=BE?= =?UTF-8?q?=D0=B2=20=D0=B4=D0=BB=D1=8F=20=D0=B2=D0=B5=D0=B1=D1=81=D0=BE?= =?UTF-8?q?=D0=BA=D0=B5=D1=82-=D1=81=D0=BE=D0=B5=D0=B4=D0=B8=D0=BD=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D1=8F,=20=D0=B4=D0=BE=D1=80=D0=B0=D0=B1=D0=BE?= =?UTF-8?q?=D1=82=D0=BA=D0=B8=20=D0=B4=D0=BB=D1=8F=20=D1=85=D1=80=D0=B0?= =?UTF-8?q?=D0=BD=D0=B8=D0=BB=D0=B8=D1=89=D0=B0=20=D0=B4=D0=B0=D0=BD=D0=BD?= =?UTF-8?q?=D1=8F=D1=85=20=D0=BF=D0=BE=D0=BB=D1=8C=D0=B7=D0=BE=D0=B2=D0=B0?= =?UTF-8?q?=D1=82=D0=B5=D0=BB=D1=8F=20=D0=B8=20=D0=B8=D0=BD=D0=B3=D1=80?= =?UTF-8?q?=D0=B8=D0=B4=D0=B8=D0=B5=D0=BD=D1=82=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Ingredient/Ingredient.js | 4 +- .../IngredientCard/IngredientCard.tsx | 2 +- .../IngredientCategory/IngredientCategory.js | 6 +- src/services/actions/ingredients.tsx | 58 +++++++++- src/services/actions/ws.js | 36 ------ src/services/actions/ws.tsx | 103 ++++++++++++++++++ src/services/reducers/{index.js => index.tsx} | 37 +++++-- src/services/reducers/{user.js => user.tsx} | 50 +++++++-- src/services/reducers/ws.js | 31 ------ src/services/reducers/ws.tsx | 32 ++++++ src/utils/constants.tsx | 6 + src/utils/orderTypes.tsx | 25 +++++ src/utils/userTypes.tsx | 47 +++++++- 13 files changed, 344 insertions(+), 93 deletions(-) delete mode 100644 src/services/actions/ws.js create mode 100644 src/services/actions/ws.tsx rename src/services/reducers/{index.js => index.tsx} (80%) rename src/services/reducers/{user.js => user.tsx} (83%) delete mode 100644 src/services/reducers/ws.js create mode 100644 src/services/reducers/ws.tsx create mode 100644 src/utils/orderTypes.tsx diff --git a/src/components/Ingredient/Ingredient.js b/src/components/Ingredient/Ingredient.js index 36556cc..69c0dd8 100644 --- a/src/components/Ingredient/Ingredient.js +++ b/src/components/Ingredient/Ingredient.js @@ -15,8 +15,8 @@ function Ingredient({id, index}) { const {ingredientsById} = useSelector(state => state.menu); - const moveItem = (prevId, newId) => { - dispatch({type: CHANGE_INGREDIENT_ORDER, prevId, newId}); + const moveItem = (prevIndex, newIndex) => { + dispatch({type: CHANGE_INGREDIENT_ORDER, prevId: prevIndex, newId: newIndex}); } const handleDeleteIngredient = (e) => { diff --git a/src/components/IngredientCard/IngredientCard.tsx b/src/components/IngredientCard/IngredientCard.tsx index f78e030..035ef40 100644 --- a/src/components/IngredientCard/IngredientCard.tsx +++ b/src/components/IngredientCard/IngredientCard.tsx @@ -1,6 +1,6 @@ import IngredientCardStyles from './IngredientCard.module.css'; -import TIngredient from '../../utils/TIngredient'; +import { TIngredient } from '../../utils/ingredientsTypes'; import { FC } from 'react'; import { useDrag } from 'react-dnd'; import { Link, useLocation } from 'react-router-dom'; diff --git a/src/components/IngredientCategory/IngredientCategory.js b/src/components/IngredientCategory/IngredientCategory.js index 29c08af..afd593b 100644 --- a/src/components/IngredientCategory/IngredientCategory.js +++ b/src/components/IngredientCategory/IngredientCategory.js @@ -2,7 +2,7 @@ import IngredientCategoryStyles from './IngredientCategory.module.css'; import { useMemo, useEffect } from 'react'; -import PropTypes from 'prop-types'; +// import PropTypes from 'prop-types'; // import IngredientsPropTypes from '../../utils/TIngredient'; import IngredientCard from '../IngredientCard/IngredientCard'; @@ -15,7 +15,7 @@ function IngredientCategory({ id, title, data, openIngredientModal, link, inView const { addedIngredients } = useSelector(store => store.menu); const ingredientsCount = useMemo(() => { - return data.reduce((res, item) => { + return data?.reduce((res, item) => { res[item._id] = {}; if (item.type === 'bun' && item._id === addedIngredients.bun?._id) { res[item._id].count = 1; @@ -39,7 +39,7 @@ function IngredientCategory({ id, title, data, openIngredientModal, link, inView {title}
- {data.map((item) => { + {data?.map((item) => { return ({ type: SEND_ORDER_FAILED }); +// Типизация других действий с ингридиентами +interface IShowIngredient { + readonly type: typeof SHOW_INGREDIENT; + id: string; +} + +interface IHideIngredient { + readonly type: typeof HIDE_INGREDIENT; +} + +interface IAddIngredient { + readonly type: typeof ADD_INGREDIENT; + id: string; +} + +interface IDeleteIngredient { + readonly type: typeof DELETE_INGREDIENT; + index: number; +} + +interface IAddBun { + readonly type: typeof ADD_BUN; + id: string; +} + +interface IChangeOrderIngredients { + readonly type: typeof CHANGE_INGREDIENT_ORDER; + prevIndex: number; + newIndex: number; +} + +interface ISetActiveTab { + readonly type: typeof SET_ACTIVE_TAB; + name: string; +} +export type TIngredientsAction = + IGetIngredientsAction + | IGetCountriesSuccessAction + | IGetIngredientsFailedAction + | ISendOrderAction + | ISendOrderSuccessAction + | ISendOrderFailedAction + | IShowIngredient + | IHideIngredient + | IAddIngredient + | IDeleteIngredient + | IAddBun + | IChangeOrderIngredients + | ISetActiveTab; export function getIngredients() { return function(dispatch: any) { diff --git a/src/services/actions/ws.js b/src/services/actions/ws.js deleted file mode 100644 index 3a2be81..0000000 --- a/src/services/actions/ws.js +++ /dev/null @@ -1,36 +0,0 @@ -import { createAction } from '@reduxjs/toolkit'; -import { refreshTokenRequest } from '../../utils/api'; -import { setCookie, getCookie, getAccessToken } from '../../utils/cookie'; -import { GET_USER_ORDERS_URL, WS_MESSAGE } from '../../utils/constants'; - - -export const wsConnect = createAction('WS_CONNECT'); -export const wsDisconnect = createAction('WS_DISCONNECT'); -export const wsConnecting = createAction('WS_CONNECTING'); -export const wsOpen = createAction('WS_OPEN'); -export const wsClose = createAction('WS_CLOSE'); -export const wsError = createAction('WS_ERROR'); - -export const wsMessage = (action) => (dispatch) => { - - dispatch({type: WS_MESSAGE, payload: action}); - - if (action.message === 'Invalid or missing token') { - dispatch(wsDisconnect()); - refreshTokenRequest(getCookie('token')) - .then((res) => { - if (res && res.success) { - setCookie('accessToken', getAccessToken(res.accessToken)); - setCookie('token', res.refreshToken); - - dispatch(wsConnect(`${GET_USER_ORDERS_URL}?token=${getCookie('accessToken')}`)); - } else { - throw new Error('Произошла ошибка ', res.message); - } - - }) - .catch((err) => { - console.log('error!', err); - }); - } -} diff --git a/src/services/actions/ws.tsx b/src/services/actions/ws.tsx new file mode 100644 index 0000000..f01f912 --- /dev/null +++ b/src/services/actions/ws.tsx @@ -0,0 +1,103 @@ +import { createAction } from '@reduxjs/toolkit'; +import { refreshTokenRequest } from '../../utils/api'; +import { setCookie, getCookie, getAccessToken } from '../../utils/cookie'; +import { + GET_USER_ORDERS_URL, + WS_MESSAGE, + WS_CONNECT, + WS_DISCONNECT, + WS_CONNECTING, + WS_OPEN, + WS_CLOSE, + WS_ERROR +} from '../../utils/constants'; +import { TOrder } from '../../utils/orderTypes'; + +function withPayloadType() { + return (t: T) => ({ payload: t }) +} +export const wsConnect = createAction(WS_CONNECT, withPayloadType()); +export const wsDisconnect = createAction(WS_DISCONNECT); +export const wsConnecting = createAction(WS_CONNECTING); +export const wsOpen = createAction(WS_OPEN); +export const wsClose = createAction(WS_CLOSE); +export const wsError = createAction(WS_ERROR); + +export interface IWSMessage { + success: boolean; + message?: string; + orders?: Array; +} + +interface IWSMessageAction { + readonly type: typeof WS_MESSAGE; + payload: IWSMessage; +} + +interface IWSErrorAction { + readonly type: typeof WS_ERROR; + payload: IWSMessage; +} + +interface IWSOpenAction { + readonly type: typeof WS_OPEN; + payload: IWSMessage; +} + +interface IWSCloseAction { + readonly type: typeof WS_CLOSE; + payload: IWSMessage; +} + +interface IWSConnectingAction { + readonly type: typeof WS_CONNECTING; + payload: IWSMessage; +} + +interface IWSConnectAction { + readonly type: typeof WS_CONNECT; + payload: IWSMessage; +} + +interface IWSDisconnectAction { + readonly type: typeof WS_DISCONNECT; + payload: IWSMessage; +} + +const wsMessageAction = (payload: IWSMessage): IWSMessageAction => ({ + type: WS_MESSAGE, + payload +}); + +export type TWSAction = + IWSMessageAction + | IWSErrorAction + | IWSOpenAction + | IWSCloseAction + | IWSConnectingAction + | IWSConnectAction + | IWSDisconnectAction; + +export const wsMessage = (action: IWSMessage) => (dispatch: any) => { + + dispatch(wsMessageAction(action)); + + if (action.message === 'Invalid or missing token') { + dispatch(wsDisconnect()); + refreshTokenRequest(getCookie('token')) + .then((res) => { + if (res && res.success) { + setCookie('accessToken', getAccessToken(res.accessToken)); + setCookie('token', res.refreshToken); + + dispatch(wsConnect(`${GET_USER_ORDERS_URL}?token=${getCookie('accessToken')}`)); + } else { + throw new Error('Произошла ошибка ', res.message); + } + + }) + .catch((err) => { + console.log('error!', err); + }); + } +} diff --git a/src/services/reducers/index.js b/src/services/reducers/index.tsx similarity index 80% rename from src/services/reducers/index.js rename to src/services/reducers/index.tsx index c079b69..dd846f9 100644 --- a/src/services/reducers/index.js +++ b/src/services/reducers/index.tsx @@ -15,11 +15,33 @@ import { ADD_BUN, CHANGE_INGREDIENT_ORDER, SET_ACTIVE_TAB - } from '../../utils/constants'; +} from '../../utils/constants'; +import { TIngredientsByType, TIngredientsById } from '../../utils/ingredientsTypes'; +import { TIngredientsAction } from '../actions/ingredients'; -const initialMenuState = { - ingredientsByType: null, - ingredientsById: null, +type TMenuState = { + ingredientsByType: TIngredientsByType , + ingredientsById: TIngredientsById, + ingredientsRequest: boolean, + ingredientsFailed: boolean, + + addedIngredients: { + bun: string| null, + others: Array + }, + currentIngredient: string | null, + + activeTab: 'bun' | 'main' | 'sauce', + + orderId: number | null, + orderRequest: boolean, + orderFailed: boolean +} + + +const initialMenuState: TMenuState = { + ingredientsByType: {}, + ingredientsById: {}, ingredientsRequest: false, ingredientsFailed: false, @@ -37,7 +59,8 @@ const initialMenuState = { } let uniqId = 0; -export const ingredientsReducer = (state = initialMenuState, action) => { + +export const ingredientsReducer = (state = initialMenuState, action: TIngredientsAction) => { switch (action.type) { case GET_INGREDIENTS_REQUEST: { return { @@ -129,10 +152,10 @@ export const ingredientsReducer = (state = initialMenuState, action) => { } case CHANGE_INGREDIENT_ORDER: { const items = state.addedIngredients.others.slice(); - const draggedItem = items.splice(action.prevId, 1)[0]; + const draggedItem = items.splice(action.prevIndex, 1)[0]; items.splice( - action.newId, + action.newIndex, 0, draggedItem ); diff --git a/src/services/reducers/user.js b/src/services/reducers/user.tsx similarity index 83% rename from src/services/reducers/user.js rename to src/services/reducers/user.tsx index 17d27df..e2564e2 100644 --- a/src/services/reducers/user.js +++ b/src/services/reducers/user.tsx @@ -27,9 +27,37 @@ import { LOGOUT_USER_SUCCESS, LOGOUT_USER_FAILED, - GET_USER_ORDERS - } from '../../utils/constants'; + // GET_USER_ORDERS +} from '../../utils/constants'; +import { TUserActions } from '../../utils/userTypes'; +type TUserState = { + name: string, + email: string, + password: string, + prevSavedName: string, + prevSavedEmail: string, + prevSavedPassword: string, + + isAuthSuccess: boolean, + isUserLoaded: boolean, + + addUserRequest: boolean, + addUserFailed: boolean, + + loginUserRequest: boolean, + loginUserFailed: boolean, + loginErrorMessage: string, + + logoutError: boolean, + + editUserFailed: boolean, + isUserEdited: boolean, + + canResetPassword: boolean, + resetPasswordSuccess: boolean, + code: number +} const initialUserState = { name: '', @@ -56,12 +84,12 @@ const initialUserState = { canResetPassword: false, resetPasswordSuccess: false, - code: '', + code: 0, - orders: [] + // orders: [] } -export const userReducer = (state = initialUserState, action) => { +export const userReducer = (state: TUserState = initialUserState, action: TUserActions) => { switch (action.type) { case SET_REGISTER_FORM_VALUE: { @@ -222,12 +250,12 @@ export const userReducer = (state = initialUserState, action) => { - case GET_USER_ORDERS: { - return { - ...state, - orders: action.action.orders - } - } + // case GET_USER_ORDERS: { + // return { + // ...state, + // orders: action.action.orders + // } + // } default: return state; diff --git a/src/services/reducers/ws.js b/src/services/reducers/ws.js deleted file mode 100644 index bce4041..0000000 --- a/src/services/reducers/ws.js +++ /dev/null @@ -1,31 +0,0 @@ -import { WS_STATUS, WS_MESSAGE } from '../../utils/constants'; -import { wsClose, wsConnecting, wsError, wsOpen } from '../actions/ws'; -import { createReducer } from '@reduxjs/toolkit'; - -const initialWSState = { - status: WS_STATUS.OFFLINE, - connectionError: '', - feed: {} -} - -export const wsReducer = createReducer(initialWSState, (builder) => { - - builder - .addCase(wsConnecting, (state) => { - state.status = WS_STATUS.CONNECTING - }) - .addCase(wsOpen, (state) => { - state.status = WS_STATUS.ONLINE; - state.connectionError = '' - }) - .addCase(wsClose, (state) => { - state.status = WS_STATUS.OFFLINE; - state.connectionError = '' - }) - .addCase(wsError, (state, action) => { - state.connectionError = action.payload - }) - .addCase(WS_MESSAGE, (state, action) => { - state.feed = action.payload; - }) -}) diff --git a/src/services/reducers/ws.tsx b/src/services/reducers/ws.tsx new file mode 100644 index 0000000..ca7583b --- /dev/null +++ b/src/services/reducers/ws.tsx @@ -0,0 +1,32 @@ +import { WS_STATUS, WS_MESSAGE } from '../../utils/constants'; +import { wsClose, wsConnecting, wsError, wsOpen, TWSAction } from '../actions/ws'; +import { createReducer } from '@reduxjs/toolkit'; +import { TWSState } from '../../utils/orderTypes'; + +const initialWSState: TWSState = { + status: WS_STATUS.OFFLINE, + connectionError: '', + feed: {} +} + +export const wsReducer = createReducer(initialWSState, (builder: any) => { + + builder + .addCase(wsConnecting, (state: TWSState) => { + state.status = WS_STATUS.CONNECTING + }) + .addCase(wsOpen, (state: TWSState) => { + state.status = WS_STATUS.ONLINE; + state.connectionError = '' + }) + .addCase(wsClose, (state: TWSState) => { + state.status = WS_STATUS.OFFLINE; + state.connectionError = '' + }) + .addCase(wsError, (state: TWSState, action: TWSAction) => { + state.connectionError = action.payload + }) + .addCase(WS_MESSAGE, (state: TWSState, action: TWSAction) => { + state.feed = action.payload; + }) +}) diff --git a/src/utils/constants.tsx b/src/utils/constants.tsx index 6831976..08661c6 100644 --- a/src/utils/constants.tsx +++ b/src/utils/constants.tsx @@ -18,6 +18,12 @@ export const WS_STATUS = { OFFLINE : 'OFFLINE' } export const WS_MESSAGE: 'WS_MESSAGE' = 'WS_MESSAGE'; +export const WS_CONNECT: 'WS_CONNECT' = 'WS_CONNECT'; +export const WS_DISCONNECT: 'WS_DISCONNECT' = 'WS_DISCONNECT'; +export const WS_CONNECTING: 'WS_CONNECTING' = 'WS_CONNECTING'; +export const WS_OPEN: 'WS_OPEN' = 'WS_OPEN'; +export const WS_CLOSE: 'WS_CLOSE' = 'WS_CLOSE'; +export const WS_ERROR: 'WS_ERROR' = 'WS_ERROR'; // Константы для обработки запроса для получения всех ингридиентов export const GET_INGREDIENTS_REQUEST: 'GET_INGREDIENTS_REQUEST' = 'GET_INGREDIENTS_REQUEST'; diff --git a/src/utils/orderTypes.tsx b/src/utils/orderTypes.tsx new file mode 100644 index 0000000..e09e8ee --- /dev/null +++ b/src/utils/orderTypes.tsx @@ -0,0 +1,25 @@ +import { TIngredient } from "./ingredientsTypes"; +import { IWSMessage } from '../services/actions/ws'; + +export type TOrder = { + _id: string; + status: string; + name: string; + createdAt: Date | string; + updatedAt: Date | string; + number: number; + ingredients: Array +} + +export type TFeedData = { + success: boolean; + total: number; + totalToday: number; + orders: Array; +} + +export type TWSState = { + status: string; + connectionError: string | IWSMessage; + feed: TFeedData | {}; +} diff --git a/src/utils/userTypes.tsx b/src/utils/userTypes.tsx index df794b1..a860b6b 100644 --- a/src/utils/userTypes.tsx +++ b/src/utils/userTypes.tsx @@ -20,7 +20,11 @@ import { RESET_PASSWORD_FAILED, LOGOUT_USER_SUCCESS, - LOGOUT_USER_FAILED + LOGOUT_USER_FAILED, + + SET_REGISTER_FORM_VALUE, + SET_EDIT_USER_FORM, + RESET_EDIT_USER_FORM } from './constants'; export type TUser = { @@ -178,3 +182,44 @@ export const logoutUserFailedAction = (errorMessage: string): ILogoutUserFailedA type: LOGOUT_USER_FAILED, errorMessage }); + +// Типизация экшнов работы с формами +interface ISetRegisterFormAction { + readonly type: typeof SET_REGISTER_FORM_VALUE; + [name: string]: string; +} + +type TSetEditFormAction = { + readonly type: typeof SET_EDIT_USER_FORM; + [name: string]: string; +} & { + isUserEdited: boolean; +} + +interface IResetEditUserFormUserAction { + readonly type: typeof RESET_EDIT_USER_FORM; + name: string; + email: string; + password: string; +} + +export type TUserActions = + IAddUserAction + | IAddUserSuccessAction + | IAddUserFailedAction + | ILoginUserAction + | ILoginUserSuccessAction + | ILoginUserFailedAction + | IGetUserSuccessAction + | IGetUserFailedAction + | IEditUserSuccessAction + | IEditUserFailedAction + | IForgotPasswordSuccessAction + | IForgotPasswordFailedAction + | IResetPasswordSuccessAction + | IResetPasswordFailedAction + | ILogoutUserSuccessAction + | ILogoutUserFailedAction + | ISetRegisterFormAction + | TSetEditFormAction + | IResetEditUserFormUserAction; From 7cc598dd06cd8f5f78558b6fe7573bb12f44645e Mon Sep 17 00:00:00 2001 From: Yulia Berezhnaya Date: Sat, 1 Apr 2023 18:25:13 +0300 Subject: [PATCH 05/13] =?UTF-8?q?=D0=92=20services=20=D0=B2=D1=8B=D0=BD?= =?UTF-8?q?=D0=B5=D1=81=D0=B5=D0=BD=D1=8B=20store=20=D0=B8=20=D1=82=D0=B8?= =?UTF-8?q?=D0=BF=D1=8B=20user,=20ingredients=20=D0=B8=20ws?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../IngredientCard/IngredientCard.tsx | 2 +- src/index.tsx | 26 +------------------ src/services/actions/ingredients.tsx | 2 +- src/services/actions/user.tsx | 4 +-- src/services/actions/ws.tsx | 2 +- src/services/reducers/index.tsx | 8 +++--- src/services/reducers/user.tsx | 4 +-- src/services/reducers/ws.tsx | 9 ++++++- src/services/store.tsx | 24 +++++++++++++++++ src/services/types/index.tsx | 3 +++ .../types/ingredients.tsx} | 0 .../types/order.tsx} | 9 ++----- .../types/request.tsx} | 2 +- .../userTypes.tsx => services/types/user.tsx} | 2 +- src/utils/api.tsx | 4 +-- 15 files changed, 53 insertions(+), 48 deletions(-) create mode 100644 src/services/store.tsx create mode 100644 src/services/types/index.tsx rename src/{utils/ingredientsTypes.tsx => services/types/ingredients.tsx} (100%) rename src/{utils/orderTypes.tsx => services/types/order.tsx} (58%) rename src/{utils/requestTypes.tsx => services/types/request.tsx} (93%) rename src/{utils/userTypes.tsx => services/types/user.tsx} (99%) diff --git a/src/components/IngredientCard/IngredientCard.tsx b/src/components/IngredientCard/IngredientCard.tsx index 035ef40..825e8cd 100644 --- a/src/components/IngredientCard/IngredientCard.tsx +++ b/src/components/IngredientCard/IngredientCard.tsx @@ -1,6 +1,6 @@ import IngredientCardStyles from './IngredientCard.module.css'; -import { TIngredient } from '../../utils/ingredientsTypes'; +import { TIngredient } from '../../services/types/ingredients'; import { FC } from 'react'; import { useDrag } from 'react-dnd'; import { Link, useLocation } from 'react-router-dom'; diff --git a/src/index.tsx b/src/index.tsx index 44467bb..4964e82 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -3,44 +3,20 @@ import ReactDOM from 'react-dom/client'; import './index.css'; import App from './components/App/App'; import reportWebVitals from './reportWebVitals'; -import { rootReducer } from './services/reducers'; -import { compose, createStore, applyMiddleware } from 'redux'; -import thunk from 'redux-thunk'; import { Provider } from 'react-redux'; import { BrowserRouter } from 'react-router-dom'; -import { socketMiddleware } from './services/middlewares/ws'; -import { wsConnect, wsDisconnect, wsConnecting, wsOpen, wsClose, wsError, wsMessage } from './services/actions/ws'; +import { store } from './services/store'; const root = ReactDOM.createRoot( document.getElementById('root') as HTMLElement ); -const composeEnhancers = - typeof window === 'object' && (window as any)['__REDUX_DEVTOOLS_EXTENSION_COMPOSE__'] - ? (window as any)['__REDUX_DEVTOOLS_EXTENSION_COMPOSE__']({}) - : compose; - -const wsActions = { - wsConnect, - wsDisconnect, - wsConnecting, - onOpen: wsOpen, - onClose: wsClose, - onError: wsError, - onMessage: wsMessage -} -const enhancer = composeEnhancers(applyMiddleware(thunk, socketMiddleware(wsActions))); - -const store = createStore(rootReducer, enhancer); - root.render( - // - // ); // If you want to start measuring performance in your app, pass a function diff --git a/src/services/actions/ingredients.tsx b/src/services/actions/ingredients.tsx index 1c4ecc4..39a4b33 100644 --- a/src/services/actions/ingredients.tsx +++ b/src/services/actions/ingredients.tsx @@ -14,7 +14,7 @@ import { CHANGE_INGREDIENT_ORDER, SET_ACTIVE_TAB } from '../../utils/constants'; - import { TIngredient, TIngredientsByType, TIngredientsById } from "../../utils/ingredientsTypes"; + import { TIngredient, TIngredientsByType, TIngredientsById } from "../types/ingredients"; import { getAccessToken, getCookie, setCookie } from "../../utils/cookie"; import { refreshTokenRequest } from "../../utils/api"; diff --git a/src/services/actions/user.tsx b/src/services/actions/user.tsx index 1c65380..e6c715d 100644 --- a/src/services/actions/user.tsx +++ b/src/services/actions/user.tsx @@ -27,13 +27,13 @@ import { resetPasswordFailedAction, logoutUserSuccessAction, logoutUserFailedAction - } from "../../utils/userTypes"; + } from "../types/user"; import { TRequestRetryOptions, requestRetryOnFail, requestRetryOnSuccess , requestRetry -} from "../../utils/requestTypes"; +} from "../types/request"; import { SET_REGISTER_FORM_VALUE, SET_EDIT_USER_FORM, diff --git a/src/services/actions/ws.tsx b/src/services/actions/ws.tsx index f01f912..5178af5 100644 --- a/src/services/actions/ws.tsx +++ b/src/services/actions/ws.tsx @@ -11,7 +11,7 @@ import { WS_CLOSE, WS_ERROR } from '../../utils/constants'; -import { TOrder } from '../../utils/orderTypes'; +import { TOrder } from '../types/order'; function withPayloadType() { return (t: T) => ({ payload: t }) diff --git a/src/services/reducers/index.tsx b/src/services/reducers/index.tsx index dd846f9..1f09dcf 100644 --- a/src/services/reducers/index.tsx +++ b/src/services/reducers/index.tsx @@ -16,17 +16,17 @@ import { CHANGE_INGREDIENT_ORDER, SET_ACTIVE_TAB } from '../../utils/constants'; -import { TIngredientsByType, TIngredientsById } from '../../utils/ingredientsTypes'; +import { TIngredientsByType, TIngredientsById, TIngredient } from '../types/ingredients'; import { TIngredientsAction } from '../actions/ingredients'; -type TMenuState = { +export type TMenuState = { ingredientsByType: TIngredientsByType , ingredientsById: TIngredientsById, ingredientsRequest: boolean, ingredientsFailed: boolean, addedIngredients: { - bun: string| null, + bun: TIngredient | null, others: Array }, currentIngredient: string | null, @@ -60,7 +60,7 @@ const initialMenuState: TMenuState = { let uniqId = 0; -export const ingredientsReducer = (state = initialMenuState, action: TIngredientsAction) => { +export const ingredientsReducer = (state: TMenuState = initialMenuState, action: TIngredientsAction) => { switch (action.type) { case GET_INGREDIENTS_REQUEST: { return { diff --git a/src/services/reducers/user.tsx b/src/services/reducers/user.tsx index e2564e2..4307510 100644 --- a/src/services/reducers/user.tsx +++ b/src/services/reducers/user.tsx @@ -29,9 +29,9 @@ import { // GET_USER_ORDERS } from '../../utils/constants'; -import { TUserActions } from '../../utils/userTypes'; +import { TUserActions } from '../types/user'; -type TUserState = { +export type TUserState = { name: string, email: string, password: string, diff --git a/src/services/reducers/ws.tsx b/src/services/reducers/ws.tsx index ca7583b..8f08981 100644 --- a/src/services/reducers/ws.tsx +++ b/src/services/reducers/ws.tsx @@ -1,7 +1,14 @@ import { WS_STATUS, WS_MESSAGE } from '../../utils/constants'; import { wsClose, wsConnecting, wsError, wsOpen, TWSAction } from '../actions/ws'; import { createReducer } from '@reduxjs/toolkit'; -import { TWSState } from '../../utils/orderTypes'; +import { IWSMessage } from '../actions/ws'; +import { TFeedData } from '../types/order'; + +export type TWSState = { + status: string; + connectionError: string | IWSMessage; + feed: TFeedData | {}; +} const initialWSState: TWSState = { status: WS_STATUS.OFFLINE, diff --git a/src/services/store.tsx b/src/services/store.tsx new file mode 100644 index 0000000..2a10bdb --- /dev/null +++ b/src/services/store.tsx @@ -0,0 +1,24 @@ +import { compose, createStore, applyMiddleware } from 'redux'; +import { socketMiddleware } from './middlewares/ws'; +import thunk from 'redux-thunk'; +import { rootReducer } from './reducers'; +import { wsConnect, wsDisconnect, wsConnecting, wsOpen, wsClose, wsError, wsMessage } from './actions/ws'; + +const wsActions = { + wsConnect, + wsDisconnect, + wsConnecting, + onOpen: wsOpen, + onClose: wsClose, + onError: wsError, + onMessage: wsMessage +} + +const composeEnhancers = + typeof window === 'object' && (window as any)['__REDUX_DEVTOOLS_EXTENSION_COMPOSE__'] + ? (window as any)['__REDUX_DEVTOOLS_EXTENSION_COMPOSE__']({}) + : compose; + +const enhancer = composeEnhancers(applyMiddleware(thunk, socketMiddleware(wsActions))); + +export const store = createStore(rootReducer, enhancer); diff --git a/src/services/types/index.tsx b/src/services/types/index.tsx new file mode 100644 index 0000000..80540a7 --- /dev/null +++ b/src/services/types/index.tsx @@ -0,0 +1,3 @@ +import { store } from '../store'; + +export type TRootState = ReturnType diff --git a/src/utils/ingredientsTypes.tsx b/src/services/types/ingredients.tsx similarity index 100% rename from src/utils/ingredientsTypes.tsx rename to src/services/types/ingredients.tsx diff --git a/src/utils/orderTypes.tsx b/src/services/types/order.tsx similarity index 58% rename from src/utils/orderTypes.tsx rename to src/services/types/order.tsx index e09e8ee..c83ab05 100644 --- a/src/utils/orderTypes.tsx +++ b/src/services/types/order.tsx @@ -1,5 +1,5 @@ -import { TIngredient } from "./ingredientsTypes"; -import { IWSMessage } from '../services/actions/ws'; +import { TIngredient } from "./ingredients"; +import { IWSMessage } from '../actions/ws'; export type TOrder = { _id: string; @@ -18,8 +18,3 @@ export type TFeedData = { orders: Array; } -export type TWSState = { - status: string; - connectionError: string | IWSMessage; - feed: TFeedData | {}; -} diff --git a/src/utils/requestTypes.tsx b/src/services/types/request.tsx similarity index 93% rename from src/utils/requestTypes.tsx rename to src/services/types/request.tsx index cac5f57..782e6ec 100644 --- a/src/utils/requestTypes.tsx +++ b/src/services/types/request.tsx @@ -1,4 +1,4 @@ -import { TUser } from "./userTypes"; +import { TUser } from "./user"; export interface IGetUserOptions { accessToken?: string; diff --git a/src/utils/userTypes.tsx b/src/services/types/user.tsx similarity index 99% rename from src/utils/userTypes.tsx rename to src/services/types/user.tsx index a860b6b..17aba4e 100644 --- a/src/utils/userTypes.tsx +++ b/src/services/types/user.tsx @@ -25,7 +25,7 @@ import { SET_REGISTER_FORM_VALUE, SET_EDIT_USER_FORM, RESET_EDIT_USER_FORM - } from './constants'; + } from '../../utils/constants'; export type TUser = { name: string; diff --git a/src/utils/api.tsx b/src/utils/api.tsx index fd978bf..3511c06 100644 --- a/src/utils/api.tsx +++ b/src/utils/api.tsx @@ -9,8 +9,8 @@ import { PASSWORDFORGOTURL, PASSWORDRESETURL } from "./constants"; -import { IEditUserOptions, IGetUserOptions, ILogoutUserOptions } from './requestTypes'; -import { TUser } from "./userTypes"; +import { IEditUserOptions, IGetUserOptions, ILogoutUserOptions } from '../services/types/request'; +import { TUser } from "../services/types/user"; // TODO: нужно ли типизировать data? Должен быть {ok: boolean, json?: () => any } ??? const checkResponse = async (data: any) => { From 0c6de223cc648a5f695989fdc78b762dc3c50263 Mon Sep 17 00:00:00 2001 From: Yulia Berezhnaya Date: Sun, 2 Apr 2023 01:00:14 +0300 Subject: [PATCH 06/13] =?UTF-8?q?=D0=A2=D0=B8=D0=BF=D0=B8=D0=B7=D0=B0?= =?UTF-8?q?=D1=86=D0=B8=D1=8F=20=D1=81=D1=82=D1=80=D0=B0=D0=BD=D0=B8=D1=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/App/App.js | 2 +- ...{forgotPassword.jsx => forgotPassword.tsx} | 8 +- src/pages/{login.jsx => login.tsx} | 8 +- src/pages/{logoutPage.jsx => logoutPage.tsx} | 5 +- src/pages/{profile.jsx => profile.tsx} | 15 +- src/pages/{register.jsx => register.tsx} | 10 +- .../{resetPassword.jsx => resetPassword.tsx} | 10 +- ...{userOrdersPage.jsx => userOrdersPage.tsx} | 12 +- src/services/actions/ingredients.tsx | 252 +++++------------- src/services/actions/user.tsx | 104 ++++---- src/services/hooks.tsx | 10 + src/services/reducers/index.tsx | 7 +- src/services/reducers/user.tsx | 4 +- src/services/reducers/ws.tsx | 12 +- src/services/types/index.tsx | 14 + src/services/types/ingredients.tsx | 127 ++++++++- src/services/types/order.tsx | 1 - src/services/types/request.tsx | 17 +- src/services/types/user.tsx | 8 +- src/services/types/ws.tsx | 66 +++++ src/utils/constants.tsx | 2 +- 21 files changed, 385 insertions(+), 309 deletions(-) rename src/pages/{forgotPassword.jsx => forgotPassword.tsx} (87%) rename src/pages/{login.jsx => login.tsx} (89%) rename src/pages/{logoutPage.jsx => logoutPage.tsx} (86%) rename src/pages/{profile.jsx => profile.tsx} (81%) rename src/pages/{register.jsx => register.tsx} (88%) rename src/pages/{resetPassword.jsx => resetPassword.tsx} (88%) rename src/pages/{userOrdersPage.jsx => userOrdersPage.tsx} (84%) create mode 100644 src/services/hooks.tsx create mode 100644 src/services/types/ws.tsx diff --git a/src/components/App/App.js b/src/components/App/App.js index 30a92b0..46a71c5 100644 --- a/src/components/App/App.js +++ b/src/components/App/App.js @@ -10,7 +10,7 @@ import ProtectedRoute from '../ProtectedRoute/ProtectedRoute'; import Orders from '../Orders/Orders'; -import { LoginPage } from '../../pages/login'; +import { LoginPage } from '../../pages/login.tsx'; import { RegisterPage } from '../../pages/register'; import { ForgotPasswordPage } from '../../pages/forgotPassword'; import { ResetPasswordPage } from '../../pages/resetPassword'; diff --git a/src/pages/forgotPassword.jsx b/src/pages/forgotPassword.tsx similarity index 87% rename from src/pages/forgotPassword.jsx rename to src/pages/forgotPassword.tsx index 7e8a6c6..fc4dd0e 100644 --- a/src/pages/forgotPassword.jsx +++ b/src/pages/forgotPassword.tsx @@ -1,5 +1,5 @@ -import React from 'react'; -import { useDispatch, useSelector } from 'react-redux'; +import { ChangeEvent, FormEvent } from 'react'; +import { useDispatch, useSelector } from '../services/hooks'; import ForgotPasswordPageStyles from './login.module.css'; import { Link, Navigate, useLocation } from 'react-router-dom'; @@ -12,13 +12,13 @@ export function ForgotPasswordPage() { const {email, canResetPassword} = useSelector(state => state.user); - const onFormSubmit = (e) => { + const onFormSubmit = (e: FormEvent) => { e.preventDefault(); dispatch(forgotPassword(email)); } - const onFormChange = (e) => { + const onFormChange = (e: ChangeEvent) => { dispatch(setRegisterFormValue(e.target.name, e.target.value)); } diff --git a/src/pages/login.jsx b/src/pages/login.tsx similarity index 89% rename from src/pages/login.jsx rename to src/pages/login.tsx index f888590..8fffe2e 100644 --- a/src/pages/login.jsx +++ b/src/pages/login.tsx @@ -1,5 +1,5 @@ -import React from 'react'; -import { useDispatch, useSelector } from 'react-redux'; +import { ChangeEvent, FormEvent } from 'react'; +import { useDispatch, useSelector } from '../services/hooks'; import LoginPageStyles from './login.module.css'; import { Link, Navigate } from 'react-router-dom'; @@ -11,13 +11,13 @@ export function LoginPage() { const {email, password, isAuthSuccess} = useSelector(state => state.user); - const onFormSubmit = (e) => { + const onFormSubmit = (e: FormEvent) => { e.preventDefault(); dispatch(loginUser({email, password})); } - const onFormChange = (e) => { + const onFormChange = (e: ChangeEvent) => { dispatch(setRegisterFormValue(e.target.name, e.target.value)); } diff --git a/src/pages/logoutPage.jsx b/src/pages/logoutPage.tsx similarity index 86% rename from src/pages/logoutPage.jsx rename to src/pages/logoutPage.tsx index c57c6b7..0b66a8d 100644 --- a/src/pages/logoutPage.jsx +++ b/src/pages/logoutPage.tsx @@ -1,4 +1,5 @@ -import { useDispatch, useSelector } from 'react-redux'; +import { FormEvent } from 'react'; +import { useDispatch, useSelector } from '../services/hooks'; import LogoutPageStyles from './login.module.css'; import { Navigate } from 'react-router-dom'; @@ -10,7 +11,7 @@ export function LogoutPage() { const {isAuthSuccess} = useSelector(state => state.user); - const onFormSubmit = (e) => { + const onFormSubmit = (e: FormEvent) => { e.preventDefault(); dispatch(logoutUser({})); diff --git a/src/pages/profile.jsx b/src/pages/profile.tsx similarity index 81% rename from src/pages/profile.jsx rename to src/pages/profile.tsx index c18c08f..442cd0f 100644 --- a/src/pages/profile.jsx +++ b/src/pages/profile.tsx @@ -1,4 +1,5 @@ -import { useDispatch, useSelector } from 'react-redux'; +import { FormEvent, ChangeEvent } from 'react'; +import { useDispatch, useSelector } from '../services/hooks'; import ProfilePageStyles from './profile.module.css'; import { NavLink } from 'react-router-dom'; @@ -10,21 +11,21 @@ export function ProfilePage() { const dispatch = useDispatch(); const {name, email, password, isUserEdited, editUserFailed} = useSelector(state => state.user); - const onFormChange = (e) => { + const onFormChange = (e: ChangeEvent) => { dispatch(setEditUserForm(e.target.name, e.target.value)); } - const onFormSubmit = (e) => { + const onFormSubmit = (e: FormEvent) => { e.preventDefault(); dispatch(editUser({name, email, password})); } - const onReset = (e) => { + const onReset = () => { dispatch({type: RESET_EDIT_USER_FORM}); } - const active = ProfilePageStyles.active + ' ' + ProfilePageStyles.link + ' pt-4 pb-4 text text_type_main-medium'; - const link = ProfilePageStyles.link + ' pt-4 pb-4 text text_type_main-medium text_color_inactive'; + const active: string = ProfilePageStyles.active + ' ' + ProfilePageStyles.link + ' pt-4 pb-4 text text_type_main-medium'; + const link: string = ProfilePageStyles.link + ' pt-4 pb-4 text text_type_main-medium text_color_inactive'; return (
@@ -44,7 +45,7 @@ export function ProfilePage() {
- + { isUserEdited &&
diff --git a/src/pages/register.jsx b/src/pages/register.tsx similarity index 88% rename from src/pages/register.jsx rename to src/pages/register.tsx index 00c0633..3d89158 100644 --- a/src/pages/register.jsx +++ b/src/pages/register.tsx @@ -1,5 +1,5 @@ -import React from 'react'; -import { useDispatch, useSelector } from 'react-redux'; +import { FormEvent, ChangeEvent } from 'react'; +import { useDispatch, useSelector } from '../services/hooks'; import RegisterPageStyles from './login.module.css'; import { Link, Navigate } from 'react-router-dom'; @@ -11,13 +11,13 @@ export function RegisterPage() { const {name, email, password, isAuthSuccess} = useSelector(state => state.user); - const addUserHandler = (e) => { + const addUserHandler = (e: FormEvent) => { e.preventDefault(); dispatch(addUser({name, email, password})); } - const onFormChange = (e) => { + const onFormChange = (e: ChangeEvent) => { dispatch(setRegisterFormValue(e.target.name, e.target.value)); } @@ -44,4 +44,4 @@ export function RegisterPage() {
); -} \ No newline at end of file +} diff --git a/src/pages/resetPassword.jsx b/src/pages/resetPassword.tsx similarity index 88% rename from src/pages/resetPassword.jsx rename to src/pages/resetPassword.tsx index 11265df..352eb42 100644 --- a/src/pages/resetPassword.jsx +++ b/src/pages/resetPassword.tsx @@ -1,5 +1,5 @@ -import React from 'react'; -import { useDispatch, useSelector } from 'react-redux'; +import { FormEvent, ChangeEvent } from 'react'; +import { useDispatch, useSelector } from '../services/hooks'; import ResetPasswordPageStyles from './login.module.css'; import { Link, Navigate } from 'react-router-dom'; @@ -11,13 +11,13 @@ export function ResetPasswordPage() { const {password, code, resetPasswordSuccess} = useSelector(state => state.user); - const onFormSubmit = (e) => { + const onFormSubmit = (e: FormEvent) => { e.preventDefault(); dispatch(resetPassword(password, code)); } - const onFormChange = (e) => { + const onFormChange = (e: ChangeEvent) => { dispatch(setRegisterFormValue(e.target.name, e.target.value)); } @@ -43,4 +43,4 @@ export function ResetPasswordPage() {
); -} \ No newline at end of file +} diff --git a/src/pages/userOrdersPage.jsx b/src/pages/userOrdersPage.tsx similarity index 84% rename from src/pages/userOrdersPage.jsx rename to src/pages/userOrdersPage.tsx index b7505a0..8f53ed4 100644 --- a/src/pages/userOrdersPage.jsx +++ b/src/pages/userOrdersPage.tsx @@ -1,8 +1,8 @@ -import React, { useEffect } from 'react'; +import { useEffect } from 'react'; +import { useDispatch, useSelector } from '../services/hooks'; import UserOrdersPageStyles from './userOrdersPage.module.css'; import { NavLink } from 'react-router-dom'; -import { useDispatch, useSelector } from 'react-redux'; import { wsConnect, wsDisconnect } from '../services/actions/ws'; import { GET_USER_ORDERS_URL } from '../utils/constants'; @@ -19,10 +19,10 @@ export function UserOrdersPage() { useEffect(() => { dispatch(wsConnect(`${GET_USER_ORDERS_URL}?token=${token}`)); - return () => dispatch(wsDisconnect()); + return () => { dispatch(wsDisconnect()); } }, [dispatch]); - const { orders } = useSelector(state => state.ws.feed); + const feed = useSelector(state => state.ws.feed); return (
@@ -41,8 +41,8 @@ export function UserOrdersPage() {

- {!orders &&
Заказов пока нет
} - {orders && orders.map((item, index) => { + {!feed &&
Заказов пока нет
} + {feed?.orders && feed.orders.map((item, index) => { return () })}
diff --git a/src/services/actions/ingredients.tsx b/src/services/actions/ingredients.tsx index 39a4b33..7d5f7e8 100644 --- a/src/services/actions/ingredients.tsx +++ b/src/services/actions/ingredients.tsx @@ -1,191 +1,75 @@ +import { AppDispatch, AppThunk } from '../types/index'; import { getIngredientsRequest, sendOrderRequest } from "../../utils/api"; +import { TIngredient, TIngredientsByType, TIngredientsById } from "../types/ingredients"; +import { getAccessToken, getCookie, setCookie } from "../../utils/cookie"; +import { refreshTokenRequest } from "../../utils/api"; import { - GET_INGREDIENTS_REQUEST, - GET_INGREDIENTS_SUCCESS, - GET_INGREDIENTS_FAILED, - SEND_ORDER_REQUEST, - SEND_ORDER_SUCCESS, - SEND_ORDER_FAILED, - SHOW_INGREDIENT, - HIDE_INGREDIENT, - ADD_INGREDIENT, - DELETE_INGREDIENT, - ADD_BUN, - CHANGE_INGREDIENT_ORDER, - SET_ACTIVE_TAB - } from '../../utils/constants'; - import { TIngredient, TIngredientsByType, TIngredientsById } from "../types/ingredients"; - import { getAccessToken, getCookie, setCookie } from "../../utils/cookie"; - import { refreshTokenRequest } from "../../utils/api"; - -// Типизация получения списка ингридиентов -interface IGetIngredientsAction { - readonly type: typeof GET_INGREDIENTS_REQUEST; -} - -interface IGetIngredientsFailedAction { - readonly type: typeof GET_INGREDIENTS_FAILED; -} - -interface IGetCountriesSuccessAction { - readonly type: typeof GET_INGREDIENTS_SUCCESS; - readonly ingredientsByType: TIngredientsByType; - readonly ingredientsById: TIngredientsById; -} - -const getIngredientsAction = (): IGetIngredientsAction => ({ - type: GET_INGREDIENTS_REQUEST -}); -const getIngredientsSuccessAction = -(ingredientsByType: TIngredientsByType, ingredientsById: TIngredientsById): IGetCountriesSuccessAction => ({ - type: GET_INGREDIENTS_SUCCESS, - ingredientsByType, - ingredientsById -}); -const getIngredientsFailedAction = (): IGetIngredientsFailedAction => ({ - type: GET_INGREDIENTS_FAILED -}); - -// Типизация создания заказа -interface ISendOrderAction { - readonly type: typeof SEND_ORDER_REQUEST; -} - -interface ISendOrderFailedAction { - readonly type: typeof SEND_ORDER_FAILED; -} - -interface ISendOrderSuccessAction { - readonly type: typeof SEND_ORDER_SUCCESS; - readonly orderId: number; -} - -const sendOrderAction = (): ISendOrderAction => ({ - type: SEND_ORDER_REQUEST -}); - -const sendOrderSuccessAction = (orderId: number): ISendOrderSuccessAction => ({ - type: SEND_ORDER_SUCCESS, - orderId -}); - -const sendOrderFailedAction = (): ISendOrderFailedAction => ({ - type: SEND_ORDER_FAILED -}); - -// Типизация других действий с ингридиентами -interface IShowIngredient { - readonly type: typeof SHOW_INGREDIENT; - id: string; -} - -interface IHideIngredient { - readonly type: typeof HIDE_INGREDIENT; -} - -interface IAddIngredient { - readonly type: typeof ADD_INGREDIENT; - id: string; -} - -interface IDeleteIngredient { - readonly type: typeof DELETE_INGREDIENT; - index: number; -} - -interface IAddBun { - readonly type: typeof ADD_BUN; - id: string; -} - -interface IChangeOrderIngredients { - readonly type: typeof CHANGE_INGREDIENT_ORDER; - prevIndex: number; - newIndex: number; -} - -interface ISetActiveTab { - readonly type: typeof SET_ACTIVE_TAB; - name: string; -} - -export type TIngredientsAction = - IGetIngredientsAction - | IGetCountriesSuccessAction - | IGetIngredientsFailedAction - | ISendOrderAction - | ISendOrderSuccessAction - | ISendOrderFailedAction - | IShowIngredient - | IHideIngredient - | IAddIngredient - | IDeleteIngredient - | IAddBun - | IChangeOrderIngredients - | ISetActiveTab; - -export function getIngredients() { - return function(dispatch: any) { - dispatch(getIngredientsAction()); - - getIngredientsRequest() - .then((res) => { - if (res && res.success) { - const ingredientsById: TIngredientsById = {}; - const ingredientsByType: TIngredientsByType = {}; - - res.data.forEach((item: TIngredient) => { - ingredientsById[item._id] = item; - (ingredientsByType[item.type] || (ingredientsByType[item.type] = [])).push(item); - }); - - dispatch(getIngredientsSuccessAction(ingredientsByType, ingredientsById)) - } else { - // Пробрасывать ошибку и обрабатывать все только в catch - dispatch(getIngredientsFailedAction()); - } - }) - .catch(() => { + getIngredientsSuccessAction, + getIngredientsFailedAction, + getIngredientsAction, + sendOrderAction, + sendOrderSuccessAction, + sendOrderFailedAction +} from '../types/ingredients'; + +export const getIngredients: AppThunk = () => (dispatch: AppDispatch) => { + dispatch(getIngredientsAction()); + + getIngredientsRequest() + .then((res) => { + if (res && res.success) { + const ingredientsById: TIngredientsById = {}; + const ingredientsByType: TIngredientsByType = {}; + + res.data.forEach((item: TIngredient) => { + ingredientsById[item._id] = item; + (ingredientsByType[item.type] || (ingredientsByType[item.type] = [])).push(item); + }); + + dispatch(getIngredientsSuccessAction(ingredientsByType, ingredientsById)) + } else { + // Пробрасывать ошибку и обрабатывать все только в catch dispatch(getIngredientsFailedAction()); - }) - } + } + }) + .catch(() => { + dispatch(getIngredientsFailedAction()); + }) } -export function sendOrder(ids: Array) { - return function(dispatch: any) { - dispatch(sendOrderAction()); - - const accessToken: string = getCookie('accessToken'); - - sendOrderRequest(ids, accessToken) - .then((res) => { - if (res && res.success) { - dispatch(sendOrderSuccessAction(res.order.number)); - } else if (res.message === "jwt expired") { - - refreshTokenRequest(getCookie('token')) - .then((res) => { - if (res && res.success) { - setCookie('accessToken', getAccessToken(res.accessToken)); - setCookie('token', res.refreshToken); - - sendOrderRequest(ids, getAccessToken(res.accessToken)) - .then((res) => { - if (res && res.success) { - dispatch(sendOrderSuccessAction(res.order.number)); - } - }) - .catch(() => { - dispatch(sendOrderFailedAction()); - }) - } - }) - } else { - dispatch(sendOrderFailedAction()); - } - }) - .catch(() => { +export const sendOrder: AppThunk = (ids: Array) => (dispatch: AppDispatch) => { + dispatch(sendOrderAction()); + + const accessToken: string = getCookie('accessToken'); + + sendOrderRequest(ids, accessToken) + .then((res) => { + if (res && res.success) { + dispatch(sendOrderSuccessAction(res.order.number)); + } else if (res.message === "jwt expired") { + + refreshTokenRequest(getCookie('token')) + .then((res) => { + if (res && res.success) { + setCookie('accessToken', getAccessToken(res.accessToken)); + setCookie('token', res.refreshToken); + + sendOrderRequest(ids, getAccessToken(res.accessToken)) + .then((res) => { + if (res && res.success) { + dispatch(sendOrderSuccessAction(res.order.number)); + } + }) + .catch(() => { + dispatch(sendOrderFailedAction()); + }) + } + }) + } else { dispatch(sendOrderFailedAction()); - }) - } + } + }) + .catch(() => { + dispatch(sendOrderFailedAction()); + }) } diff --git a/src/services/actions/user.tsx b/src/services/actions/user.tsx index e6c715d..bc72e63 100644 --- a/src/services/actions/user.tsx +++ b/src/services/actions/user.tsx @@ -1,3 +1,4 @@ +import { AppDispatch, AppThunk } from '../types/index'; import { addUserRequest, refreshTokenRequest, @@ -55,7 +56,7 @@ export const setEditUserForm = (field: string, value: string) => ({ // Добавление нового пользователя export function addUser(user: TUser) { - return function(dispatch: any) { + return function(dispatch: AppDispatch) { dispatch(addUserAction()); addUserRequest(user) @@ -75,29 +76,27 @@ export function addUser(user: TUser) { } // Вход уже существующего пользователя -export function loginUser(user: TUser) { - return function(dispatch: any) { - dispatch(loginUserAction()); +export const loginUser: AppThunk = (user: TUser) => (dispatch: AppDispatch) => { + dispatch(loginUserAction()); - loginRequest(user) - .then((res) => { - if (res && res.success) { - const accessToken: string = getAccessToken(res.accessToken); + loginRequest(user) + .then((res) => { + if (res && res.success) { + const accessToken: string = getAccessToken(res.accessToken); - setCookie('token', res.refreshToken); - setCookie('accessToken', accessToken); + setCookie('token', res.refreshToken); + setCookie('accessToken', accessToken); - dispatch(loginUserSuccessAction(res.user)); - }}) - .catch((err) => { - dispatch(loginUserFailedAction(err.message)); - }) - } + dispatch(loginUserSuccessAction(res.user)); + }}) + .catch((err) => { + dispatch(loginUserFailedAction(err.message)); + }) } // Получение информации о пользователе export function getUser() { - return function(dispatch: any) { + return function(dispatch: AppDispatch) { getRequestWithRetry( getUserRequest, ({user}) => dispatch(getUserSuccessAction(user)), @@ -108,34 +107,30 @@ export function getUser() { // Редактирование информации о пользователе export function editUser(user: TUser) { - return function(dispatch: any) { + return function(dispatch: AppDispatch) { getRequestWithRetry( editUserRequest, ({user}) => dispatch(editUserSuccessAction(user)), () => dispatch(editUserFailedAction()), - { user } + user ); } } -export function forgotPassword(email: string) { - return function(dispatch: any) { - - passwordForgotRequest(email) - .then((res) => { - if (res && res.success) { - dispatch(forgotPasswordSuccessAction(res.user)); - } - }) - .catch(() => { - dispatch(forgotPasswordFailedAction()); - }) - } - +export const forgotPassword: AppThunk = (email: string) => (dispatch: AppDispatch) => { + passwordForgotRequest(email) + .then((res) => { + if (res && res.success) { + dispatch(forgotPasswordSuccessAction(res.user)); + } + }) + .catch(() => { + dispatch(forgotPasswordFailedAction()); + }) } -export function resetPassword(password: string, code: number) { - return function(dispatch: any) { +export function resetPassword(password: string, code: string) { + return function(dispatch: AppDispatch) { passwordResetRequest(password, code) .then((res) => { @@ -150,21 +145,19 @@ export function resetPassword(password: string, code: number) { } -export function logoutUser() { - return function(dispatch: any) { - getRequestWithRetry( - logoutRequest, - () => { - setCookie('accessToken', '', {expires: 0}); - setCookie('token', '', {expires: 0}); - dispatch(logoutUserSuccessAction()) - }, - ({message}) => { - dispatch(logoutUserFailedAction(message)); - }, - {token: getCookie('token')} - ) - } +export const logoutUser: AppThunk = () => (dispatch: AppDispatch) => { + getRequestWithRetry( + logoutRequest, + () => { + setCookie('accessToken', '', {expires: 0}); + setCookie('token', '', {expires: 0}); + dispatch(logoutUserSuccessAction()) + }, + ({message}) => { + dispatch(logoutUserFailedAction(message)); + }, + getCookie('token') + ) } /** @@ -176,10 +169,9 @@ export function logoutUser() { */ // TODO что делать с options и getRequest??? -function getRequestWithRetry(getRequest: requestRetry, onSuccess: requestRetryOnSuccess, onFail: requestRetryOnFail, options: any = {}) { - options.accessToken = getCookie('accessToken'); - - getRequest(options) +function getRequestWithRetry(getRequest: requestRetry, onSuccess: requestRetryOnSuccess, onFail: requestRetryOnFail, options?: TRequestRetryOptions) { + + getRequest(getCookie('accessToken'), options) .then((res: any) => { if (res && res.success) { onSuccess(res); @@ -192,9 +184,7 @@ function getRequestWithRetry(getRequest: requestRetry, onSuccess: requestRetryOn setCookie('accessToken', getAccessToken(res.accessToken)); setCookie('token', res.refreshToken); - options.accessToken = getAccessToken(res.accessToken); - - return getRequest(options) + return getRequest(getAccessToken(res.accessToken), options) .then((res: any) => { if (res && res.success) { onSuccess(res) diff --git a/src/services/hooks.tsx b/src/services/hooks.tsx new file mode 100644 index 0000000..ba9691a --- /dev/null +++ b/src/services/hooks.tsx @@ -0,0 +1,10 @@ +import { + TypedUseSelectorHook, + useSelector as selectorHook, + useDispatch as dispatchHook +} from 'react-redux'; +import { AppDispatch, AppThunk } from './types/index'; +import { TRootState } from './types'; + +export const useSelector: TypedUseSelectorHook = selectorHook; +export const useDispatch = () => dispatchHook(); diff --git a/src/services/reducers/index.tsx b/src/services/reducers/index.tsx index 1f09dcf..ee02513 100644 --- a/src/services/reducers/index.tsx +++ b/src/services/reducers/index.tsx @@ -17,11 +17,11 @@ import { SET_ACTIVE_TAB } from '../../utils/constants'; import { TIngredientsByType, TIngredientsById, TIngredient } from '../types/ingredients'; -import { TIngredientsAction } from '../actions/ingredients'; +import { TIngredientsAction } from '../types/ingredients'; export type TMenuState = { - ingredientsByType: TIngredientsByType , - ingredientsById: TIngredientsById, + ingredientsByType: {} & TIngredientsByType , + ingredientsById: {} & TIngredientsById, ingredientsRequest: boolean, ingredientsFailed: boolean, @@ -38,7 +38,6 @@ export type TMenuState = { orderFailed: boolean } - const initialMenuState: TMenuState = { ingredientsByType: {}, ingredientsById: {}, diff --git a/src/services/reducers/user.tsx b/src/services/reducers/user.tsx index 4307510..90d8a7f 100644 --- a/src/services/reducers/user.tsx +++ b/src/services/reducers/user.tsx @@ -56,7 +56,7 @@ export type TUserState = { canResetPassword: boolean, resetPasswordSuccess: boolean, - code: number + code: string } const initialUserState = { @@ -84,7 +84,7 @@ const initialUserState = { canResetPassword: false, resetPasswordSuccess: false, - code: 0, + code: '', // orders: [] } diff --git a/src/services/reducers/ws.tsx b/src/services/reducers/ws.tsx index 8f08981..d5d803a 100644 --- a/src/services/reducers/ws.tsx +++ b/src/services/reducers/ws.tsx @@ -1,19 +1,19 @@ import { WS_STATUS, WS_MESSAGE } from '../../utils/constants'; -import { wsClose, wsConnecting, wsError, wsOpen, TWSAction } from '../actions/ws'; +import { wsClose, wsConnecting, wsError, wsOpen } from '../actions/ws'; import { createReducer } from '@reduxjs/toolkit'; -import { IWSMessage } from '../actions/ws'; +import { IWSMessage, TWSAction } from '../types/ws'; import { TFeedData } from '../types/order'; export type TWSState = { status: string; - connectionError: string | IWSMessage; - feed: TFeedData | {}; + connectionError: string | undefined; + feed: TFeedData | IWSMessage | undefined; } const initialWSState: TWSState = { status: WS_STATUS.OFFLINE, connectionError: '', - feed: {} + feed: undefined } export const wsReducer = createReducer(initialWSState, (builder: any) => { @@ -31,7 +31,7 @@ export const wsReducer = createReducer(initialWSState, (builder: any) => { state.connectionError = '' }) .addCase(wsError, (state: TWSState, action: TWSAction) => { - state.connectionError = action.payload + state.connectionError = action.payload?.message }) .addCase(WS_MESSAGE, (state: TWSState, action: TWSAction) => { state.feed = action.payload; diff --git a/src/services/types/index.tsx b/src/services/types/index.tsx index 80540a7..e7a1aa8 100644 --- a/src/services/types/index.tsx +++ b/src/services/types/index.tsx @@ -1,3 +1,17 @@ +import { ThunkAction } from 'redux-thunk'; +import { Action, ActionCreator } from 'redux'; import { store } from '../store'; +import { TIngredientsAction } from '../types/ingredients'; +import { TUserActions } from './user'; +import { TWSAction } from '../types/ws'; export type TRootState = ReturnType + +// Типизация всех экшенов приложения +type TApplicationActions = TIngredientsAction | TUserActions | TWSAction; + +export type AppDispatch = typeof store.dispatch; + +export type AppThunk = ActionCreator< + ThunkAction +>; \ No newline at end of file diff --git a/src/services/types/ingredients.tsx b/src/services/types/ingredients.tsx index afdeb54..b353473 100644 --- a/src/services/types/ingredients.tsx +++ b/src/services/types/ingredients.tsx @@ -1,3 +1,21 @@ +import { AppThunk } from './index'; + +import { + GET_INGREDIENTS_REQUEST, + GET_INGREDIENTS_FAILED, + GET_INGREDIENTS_SUCCESS, + SEND_ORDER_REQUEST, + SEND_ORDER_FAILED, + SEND_ORDER_SUCCESS, + SHOW_INGREDIENT, + HIDE_INGREDIENT, + ADD_INGREDIENT, + DELETE_INGREDIENT, + ADD_BUN, + CHANGE_INGREDIENT_ORDER, + SET_ACTIVE_TAB +} from '../../utils/constants'; + export type TIngredient = { _id: string; name: string; @@ -19,4 +37,111 @@ export type TIngredientsByType = { export type TIngredientsById = { [name: string] : TIngredient; -} \ No newline at end of file +} + + +// Типизация получения списка ингридиентов +interface IGetIngredientsAction { + readonly type: typeof GET_INGREDIENTS_REQUEST; +} + +interface IGetIngredientsFailedAction { + readonly type: typeof GET_INGREDIENTS_FAILED; +} + +interface IGetCountriesSuccessAction { + readonly type: typeof GET_INGREDIENTS_SUCCESS; + readonly ingredientsByType: TIngredientsByType; + readonly ingredientsById: TIngredientsById; +} + +export const getIngredientsAction = (): IGetIngredientsAction => ({ + type: GET_INGREDIENTS_REQUEST +}); +export const getIngredientsSuccessAction = +(ingredientsByType: TIngredientsByType, ingredientsById: TIngredientsById): IGetCountriesSuccessAction => ({ + type: GET_INGREDIENTS_SUCCESS, + ingredientsByType, + ingredientsById +}); +export const getIngredientsFailedAction = (): IGetIngredientsFailedAction => ({ + type: GET_INGREDIENTS_FAILED +}); + +// Типизация создания заказа +interface ISendOrderAction { + readonly type: typeof SEND_ORDER_REQUEST; +} + +interface ISendOrderFailedAction { + readonly type: typeof SEND_ORDER_FAILED; +} + +interface ISendOrderSuccessAction { + readonly type: typeof SEND_ORDER_SUCCESS; + readonly orderId: number; +} + +export const sendOrderAction = (): ISendOrderAction => ({ + type: SEND_ORDER_REQUEST +}); + +export const sendOrderSuccessAction = (orderId: number): ISendOrderSuccessAction => ({ + type: SEND_ORDER_SUCCESS, + orderId +}); + +export const sendOrderFailedAction = (): ISendOrderFailedAction => ({ + type: SEND_ORDER_FAILED +}); + +// Типизация других действий с ингридиентами +interface IShowIngredient { + readonly type: typeof SHOW_INGREDIENT; + id: string; +} + +interface IHideIngredient { + readonly type: typeof HIDE_INGREDIENT; +} + +interface IAddIngredient { + readonly type: typeof ADD_INGREDIENT; + id: string; +} + +interface IDeleteIngredient { + readonly type: typeof DELETE_INGREDIENT; + index: number; +} + +interface IAddBun { + readonly type: typeof ADD_BUN; + id: string; +} + +interface IChangeOrderIngredients { + readonly type: typeof CHANGE_INGREDIENT_ORDER; + prevIndex: number; + newIndex: number; +} + +interface ISetActiveTab { + readonly type: typeof SET_ACTIVE_TAB; + name: string; +} + +export type TIngredientsAction = + IGetIngredientsAction + | IGetCountriesSuccessAction + | IGetIngredientsFailedAction + | ISendOrderAction + | ISendOrderSuccessAction + | ISendOrderFailedAction + | IShowIngredient + | IHideIngredient + | IAddIngredient + | IDeleteIngredient + | IAddBun + | IChangeOrderIngredients + | ISetActiveTab; diff --git a/src/services/types/order.tsx b/src/services/types/order.tsx index c83ab05..799e588 100644 --- a/src/services/types/order.tsx +++ b/src/services/types/order.tsx @@ -1,5 +1,4 @@ import { TIngredient } from "./ingredients"; -import { IWSMessage } from '../actions/ws'; export type TOrder = { _id: string; diff --git a/src/services/types/request.tsx b/src/services/types/request.tsx index 782e6ec..af803b4 100644 --- a/src/services/types/request.tsx +++ b/src/services/types/request.tsx @@ -1,24 +1,11 @@ import { TUser } from "./user"; -export interface IGetUserOptions { - accessToken?: string; -} - -export interface IEditUserOptions { - accessToken?: string; - user?: TUser; -} - -export interface ILogoutUserOptions { - token?: string; -} - -export type TRequestRetryOptions = IGetUserOptions | IEditUserOptions | ILogoutUserOptions; +export type TRequestRetryOptions = TUser | string | undefined; export type TRequestRetryOnSuccess = { [name: string]: string; } -export type requestRetry = (options: any) => Promise; +export type requestRetry = (accessToken: string, options: TRequestRetryOptions) => Promise; export type requestRetryOnFail = (error: any) => void; export type requestRetryOnSuccess = (res: any) => void; diff --git a/src/services/types/user.tsx b/src/services/types/user.tsx index 17aba4e..88dfb33 100644 --- a/src/services/types/user.tsx +++ b/src/services/types/user.tsx @@ -33,7 +33,7 @@ export type TUser = { password?: string; }; -// Типизация создания нового пользователя +// Типизация экшнов для создания нового пользователя interface IAddUserAction { readonly type: typeof ADD_USER_REQUEST; } @@ -60,7 +60,7 @@ export const addUserFailedAction = (): IAddUserFailedAction => ({ type: ADD_USER_FAILED }); -// Типизация для логина пользователя +// Типизация экшнов для логина пользователя interface ILoginUserAction { readonly type: typeof LOGIN_USER_REQUEST; } @@ -89,7 +89,7 @@ export const loginUserFailedAction = (errorMessage: string): ILoginUserFailedAct errorMessage }); -// Типизация получения данных о пользователе +// Типизация экшнов получения данных о пользователе interface IGetUserSuccessAction { readonly type: typeof GET_USER_SUCCESS; user: TUser @@ -108,7 +108,7 @@ export const getUserFailedAction = (): IGetUserFailedAction => ({ type: GET_USER_FAILED }); -// Типизация редактирования данных о пользователе +// Типизация экшнов редактирования данных о пользователе interface IEditUserSuccessAction { readonly type: typeof EDIT_USER_SUCCESS; user: TUser diff --git a/src/services/types/ws.tsx b/src/services/types/ws.tsx new file mode 100644 index 0000000..d0be5ec --- /dev/null +++ b/src/services/types/ws.tsx @@ -0,0 +1,66 @@ +import { + WS_MESSAGE, + WS_CONNECT, + WS_DISCONNECT, + WS_CONNECTING, + WS_OPEN, + WS_CLOSE, + WS_ERROR +} from '../../utils/constants'; +import { TOrder } from './order'; + + +export interface IWSMessage { + success: boolean; + message?: string; + orders?: Array; +} + +interface IWSMessageAction { + readonly type: typeof WS_MESSAGE; + payload: IWSMessage; +} + +interface IWSErrorAction { + readonly type: typeof WS_ERROR; + payload: IWSMessage; +} + +interface IWSOpenAction { + readonly type: typeof WS_OPEN; + payload: IWSMessage; +} + +interface IWSCloseAction { + readonly type: typeof WS_CLOSE; + payload: IWSMessage; +} + +interface IWSConnectingAction { + readonly type: typeof WS_CONNECTING; + payload: IWSMessage; +} + +interface IWSConnectAction { + readonly type: typeof WS_CONNECT; + payload: IWSMessage; +} + +interface IWSDisconnectAction { + readonly type: typeof WS_DISCONNECT; + payload: IWSMessage | undefined; +} + +export const wsMessageAction = (payload: IWSMessage): IWSMessageAction => ({ + type: WS_MESSAGE, + payload +}); + +export type TWSAction = + IWSMessageAction + | IWSErrorAction + | IWSOpenAction + | IWSCloseAction + | IWSConnectingAction + | IWSConnectAction + | IWSDisconnectAction; diff --git a/src/utils/constants.tsx b/src/utils/constants.tsx index 08661c6..263ea95 100644 --- a/src/utils/constants.tsx +++ b/src/utils/constants.tsx @@ -1,5 +1,5 @@ const BASEURL = 'https://norma.nomoreparties.space/api'; -export const GETINGREDIENTSURL = BASEURL + '/ingredients'; +export const GETINGREDIENTSURL = BASEURL + '/ingredientsd'; export const SAVEORDERURL = BASEURL + '/orders'; export const ADDUSERURL = BASEURL + '/auth/register'; export const LOGINUSERURL = BASEURL + '/auth/login'; From 63980d43c0af11d645fb031fad00791a8b7358fa Mon Sep 17 00:00:00 2001 From: Yulia Berezhnaya Date: Sun, 2 Apr 2023 03:52:58 +0300 Subject: [PATCH 07/13] =?UTF-8?q?=D0=A4=D0=B8=D0=BA=D1=81=20=D0=BE=D1=88?= =?UTF-8?q?=D0=B8=D0=B1=D0=BE=D0=BA=20=D1=82=D0=B8=D0=BF=D0=B8=D0=B7=D0=B0?= =?UTF-8?q?=D1=86=D0=B8=D0=B8=20=D0=B2=D0=B5=D0=B1=D1=81=D0=BE=D0=BA=D0=B5?= =?UTF-8?q?=D1=82-=D1=81=D0=BE=D0=B5=D0=B4=D0=B8=D0=BD=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/App/App.js | 9 +- src/components/Orders/Orders.js | 7 +- src/components/TotalPrice/TotalPrice.js | 34 -------- src/components/TotalPrice/TotalPrice.tsx | 28 +++++++ src/pages/userOrdersPage.tsx | 6 +- src/services/actions/user.tsx | 20 ++--- src/services/actions/ws.tsx | 102 ++++++----------------- src/services/reducers/index.tsx | 2 +- src/services/reducers/ws.tsx | 65 ++++++++------- src/services/types/ingredients.tsx | 2 + src/services/types/request.tsx | 5 -- src/services/types/ws.tsx | 30 +++---- src/utils/api.tsx | 23 ++--- src/utils/constants.tsx | 15 ++-- 14 files changed, 156 insertions(+), 192 deletions(-) delete mode 100644 src/components/TotalPrice/TotalPrice.js create mode 100644 src/components/TotalPrice/TotalPrice.tsx diff --git a/src/components/App/App.js b/src/components/App/App.js index 46a71c5..9f9f38b 100644 --- a/src/components/App/App.js +++ b/src/components/App/App.js @@ -4,6 +4,8 @@ import React, { useEffect } from 'react'; import { Routes, Route, useLocation, useNavigate } from 'react-router-dom'; import { useSelector, useDispatch } from 'react-redux'; +import { WS_STATUS_ONLINE } from '../../utils/constants'; + import Main from '../Main/Main'; import AppHeader from '../AppHeader/AppHeader'; import ProtectedRoute from '../ProtectedRoute/ProtectedRoute'; @@ -33,8 +35,11 @@ function App() { const modalBackground = location.state?.modalBackground; const { ingredientsByType } = useSelector(state => state.menu); - const { orders } = useSelector(state => state.ws.feed); - + + const { orders } = useSelector(({ status, feed }) => ( + status === WS_STATUS_ONLINE && feed?.success ? feed : {} + )); + useEffect(() => { dispatch(getIngredients()); dispatch(getUser()); diff --git a/src/components/Orders/Orders.js b/src/components/Orders/Orders.js index 263a709..2645218 100644 --- a/src/components/Orders/Orders.js +++ b/src/components/Orders/Orders.js @@ -1,5 +1,5 @@ import OrdersStyles from './Orders.module.css'; -import { GET_ALL_ORDERS_URL } from '../../utils/constants'; +import { GET_ALL_ORDERS_URL, WS_STATUS_ONLINE } from '../../utils/constants'; import { wsConnect, wsDisconnect } from '../../services/actions/ws'; import { useEffect } from 'react'; import { useDispatch, useSelector } from 'react-redux'; @@ -8,7 +8,10 @@ import OrderSnippet from '../OrderSnippet/OrderSnippet'; function Orders() { const dispatch = useDispatch(); - const { total, totalToday, orders } = useSelector( state => state.ws.feed); + const { total, totalToday, orders } = useSelector(({ws}) => ( + ws.status === WS_STATUS_ONLINE && ws.feed.success ? ws.feed : {} + )); + const ordersByStatus = orders?.length && orders.reduce((res, item) => { if (res[item.status].length < 20) { res[item.status].push(item); diff --git a/src/components/TotalPrice/TotalPrice.js b/src/components/TotalPrice/TotalPrice.js deleted file mode 100644 index 43db974..0000000 --- a/src/components/TotalPrice/TotalPrice.js +++ /dev/null @@ -1,34 +0,0 @@ -import TotalPriceStyles from './TotalPrice.module.css'; - -import { useMemo } from 'react'; -import { useSelector } from 'react-redux'; - -import { CurrencyIcon } from '@ya.praktikum/react-developer-burger-ui-components'; - - -function TotalPrice() { - - const {addedIngredients} = useSelector(state => state.menu); - - const total = useMemo(() => { - let price = addedIngredients.bun ? addedIngredients.bun.price * 2 : 0; - - if (!addedIngredients.others.length) { - return price; - } - - return addedIngredients.others.reduce((res, item) => { - res += item.price; - - return res; - }, price); - }, [addedIngredients.bun, addedIngredients.others]); - - return ( -
- {total} - -
- ) -} -export default TotalPrice; \ No newline at end of file diff --git a/src/components/TotalPrice/TotalPrice.tsx b/src/components/TotalPrice/TotalPrice.tsx new file mode 100644 index 0000000..156e0dd --- /dev/null +++ b/src/components/TotalPrice/TotalPrice.tsx @@ -0,0 +1,28 @@ +import TotalPriceStyles from './TotalPrice.module.css'; + +import { useMemo, FC } from 'react'; +import { useSelector } from 'react-redux'; +import { TRootState } from '../../services/types/index'; + +import { CurrencyIcon } from '@ya.praktikum/react-developer-burger-ui-components'; +import { TMenuState } from '../../services/reducers'; + +const TotalPrice: FC = () => { + + const { addedIngredients: { bun, others } }: TMenuState = useSelector((state: TRootState) => state.menu); + + const total = useMemo(() => ( + others.reduce( + (res, {price}) => res += price, + bun ? bun.price * 2 : 0 + )), [bun, others]); + + return ( +
+ {total} + +
+ ) +} + +export default TotalPrice; diff --git a/src/pages/userOrdersPage.tsx b/src/pages/userOrdersPage.tsx index 8f53ed4..00696d7 100644 --- a/src/pages/userOrdersPage.tsx +++ b/src/pages/userOrdersPage.tsx @@ -4,7 +4,7 @@ import UserOrdersPageStyles from './userOrdersPage.module.css'; import { NavLink } from 'react-router-dom'; import { wsConnect, wsDisconnect } from '../services/actions/ws'; -import { GET_USER_ORDERS_URL } from '../utils/constants'; +import { GET_USER_ORDERS_URL, WS_STATUS_ONLINE } from '../utils/constants'; import OrderSnippet from '../components/OrderSnippet/OrderSnippet'; import { getCookie } from '../utils/cookie'; @@ -22,7 +22,9 @@ export function UserOrdersPage() { return () => { dispatch(wsDisconnect()); } }, [dispatch]); - const feed = useSelector(state => state.ws.feed); + const feed = useSelector(({ ws }) => ( + ws.status === WS_STATUS_ONLINE && ws.feed?.success ? ws.feed : undefined + )); return (
diff --git a/src/services/actions/user.tsx b/src/services/actions/user.tsx index bc72e63..5d43000 100644 --- a/src/services/actions/user.tsx +++ b/src/services/actions/user.tsx @@ -30,10 +30,8 @@ import { logoutUserFailedAction } from "../types/user"; import { - TRequestRetryOptions, requestRetryOnFail, requestRetryOnSuccess , - requestRetry } from "../types/request"; import { SET_REGISTER_FORM_VALUE, @@ -98,7 +96,7 @@ export const loginUser: AppThunk = (user: TUser) => (dispatch: AppDispatch) => { export function getUser() { return function(dispatch: AppDispatch) { getRequestWithRetry( - getUserRequest, + (accessToken) => getUserRequest(accessToken), ({user}) => dispatch(getUserSuccessAction(user)), () => dispatch(getUserFailedAction()) ); @@ -109,10 +107,9 @@ export function getUser() { export function editUser(user: TUser) { return function(dispatch: AppDispatch) { getRequestWithRetry( - editUserRequest, + (accessToken) => editUserRequest(accessToken, user), ({user}) => dispatch(editUserSuccessAction(user)), - () => dispatch(editUserFailedAction()), - user + () => dispatch(editUserFailedAction()) ); } } @@ -147,7 +144,7 @@ export function resetPassword(password: string, code: string) { export const logoutUser: AppThunk = () => (dispatch: AppDispatch) => { getRequestWithRetry( - logoutRequest, + (accessToken) => logoutRequest(accessToken, getCookie('token')), () => { setCookie('accessToken', '', {expires: 0}); setCookie('token', '', {expires: 0}); @@ -155,8 +152,7 @@ export const logoutUser: AppThunk = () => (dispatch: AppDispatch) => { }, ({message}) => { dispatch(logoutUserFailedAction(message)); - }, - getCookie('token') + } ) } @@ -169,9 +165,9 @@ export const logoutUser: AppThunk = () => (dispatch: AppDispatch) => { */ // TODO что делать с options и getRequest??? -function getRequestWithRetry(getRequest: requestRetry, onSuccess: requestRetryOnSuccess, onFail: requestRetryOnFail, options?: TRequestRetryOptions) { +function getRequestWithRetry(getRequest: ((accessToken: string) => Promise), onSuccess: requestRetryOnSuccess, onFail: requestRetryOnFail) { - getRequest(getCookie('accessToken'), options) + getRequest(getCookie('accessToken')) .then((res: any) => { if (res && res.success) { onSuccess(res); @@ -184,7 +180,7 @@ function getRequestWithRetry(getRequest: requestRetry, onSuccess: requestRetryOn setCookie('accessToken', getAccessToken(res.accessToken)); setCookie('token', res.refreshToken); - return getRequest(getAccessToken(res.accessToken), options) + return getRequest(getAccessToken(res.accessToken)) .then((res: any) => { if (res && res.success) { onSuccess(res) diff --git a/src/services/actions/ws.tsx b/src/services/actions/ws.tsx index 5178af5..12067bf 100644 --- a/src/services/actions/ws.tsx +++ b/src/services/actions/ws.tsx @@ -1,9 +1,9 @@ +import { AppDispatch } from '../types/index'; import { createAction } from '@reduxjs/toolkit'; import { refreshTokenRequest } from '../../utils/api'; import { setCookie, getCookie, getAccessToken } from '../../utils/cookie'; import { GET_USER_ORDERS_URL, - WS_MESSAGE, WS_CONNECT, WS_DISCONNECT, WS_CONNECTING, @@ -11,7 +11,10 @@ import { WS_CLOSE, WS_ERROR } from '../../utils/constants'; -import { TOrder } from '../types/order'; +import { + TWSMessage, + wsMessageAction +} from '../types/ws'; function withPayloadType() { return (t: T) => ({ payload: t }) @@ -23,81 +26,28 @@ export const wsOpen = createAction(WS_OPEN); export const wsClose = createAction(WS_CLOSE); export const wsError = createAction(WS_ERROR); -export interface IWSMessage { - success: boolean; - message?: string; - orders?: Array; -} - -interface IWSMessageAction { - readonly type: typeof WS_MESSAGE; - payload: IWSMessage; -} - -interface IWSErrorAction { - readonly type: typeof WS_ERROR; - payload: IWSMessage; -} - -interface IWSOpenAction { - readonly type: typeof WS_OPEN; - payload: IWSMessage; -} - -interface IWSCloseAction { - readonly type: typeof WS_CLOSE; - payload: IWSMessage; -} - -interface IWSConnectingAction { - readonly type: typeof WS_CONNECTING; - payload: IWSMessage; -} - -interface IWSConnectAction { - readonly type: typeof WS_CONNECT; - payload: IWSMessage; -} - -interface IWSDisconnectAction { - readonly type: typeof WS_DISCONNECT; - payload: IWSMessage; -} - -const wsMessageAction = (payload: IWSMessage): IWSMessageAction => ({ - type: WS_MESSAGE, - payload -}); - -export type TWSAction = - IWSMessageAction - | IWSErrorAction - | IWSOpenAction - | IWSCloseAction - | IWSConnectingAction - | IWSConnectAction - | IWSDisconnectAction; - -export const wsMessage = (action: IWSMessage) => (dispatch: any) => { - +export const wsMessage = (action: TWSMessage) => (dispatch: AppDispatch) => { dispatch(wsMessageAction(action)); - if (action.message === 'Invalid or missing token') { - dispatch(wsDisconnect()); - refreshTokenRequest(getCookie('token')) - .then((res) => { - if (res && res.success) { - setCookie('accessToken', getAccessToken(res.accessToken)); - setCookie('token', res.refreshToken); - - dispatch(wsConnect(`${GET_USER_ORDERS_URL}?token=${getCookie('accessToken')}`)); - } else { - throw new Error('Произошла ошибка ', res.message); - } - - }) - .catch((err) => { - console.log('error!', err); - }); + if (!action.success) { + if (action.message === 'Invalid or missing token') { + dispatch(wsDisconnect()); + refreshTokenRequest(getCookie('token')) + .then((res) => { + if (res && res.success) { + setCookie('accessToken', getAccessToken(res.accessToken)); + setCookie('token', res.refreshToken); + + dispatch(wsConnect(`${GET_USER_ORDERS_URL}?token=${getCookie('accessToken')}`)); + } else { + throw new Error('Произошла ошибка ', res.message); + } + + }) + .catch((err) => { + console.log('error!', err); + }); + } } + } diff --git a/src/services/reducers/index.tsx b/src/services/reducers/index.tsx index ee02513..3134a77 100644 --- a/src/services/reducers/index.tsx +++ b/src/services/reducers/index.tsx @@ -27,7 +27,7 @@ export type TMenuState = { addedIngredients: { bun: TIngredient | null, - others: Array + others: Array }, currentIngredient: string | null, diff --git a/src/services/reducers/ws.tsx b/src/services/reducers/ws.tsx index d5d803a..26b9982 100644 --- a/src/services/reducers/ws.tsx +++ b/src/services/reducers/ws.tsx @@ -1,39 +1,48 @@ -import { WS_STATUS, WS_MESSAGE } from '../../utils/constants'; +import { WS_STATUS_OFFLINE, WS_STATUS_CONNECTING, WS_STATUS_ONLINE, WS_MESSAGE } from '../../utils/constants'; import { wsClose, wsConnecting, wsError, wsOpen } from '../actions/ws'; import { createReducer } from '@reduxjs/toolkit'; -import { IWSMessage, TWSAction } from '../types/ws'; +import { TWSMessage, TWSAction, IWSErrorAction } from '../types/ws'; import { TFeedData } from '../types/order'; -export type TWSState = { - status: string; - connectionError: string | undefined; - feed: TFeedData | IWSMessage | undefined; -} +type TWSStateOffline = { + status: typeof WS_STATUS_OFFLINE; + connectionError: string; +}; +type TWSStateConnecting = { + status: typeof WS_STATUS_CONNECTING; + connectionError: string; +}; +type TWSStateOnline = { + status: typeof WS_STATUS_ONLINE; + feed: TFeedData | TWSMessage; + connectionError: string; +}; + +export type TWSState = TWSStateOffline | TWSStateConnecting | TWSStateOnline; const initialWSState: TWSState = { - status: WS_STATUS.OFFLINE, - connectionError: '', - feed: undefined + status: WS_STATUS_OFFLINE, + connectionError: '' } -export const wsReducer = createReducer(initialWSState, (builder: any) => { +export const wsReducer = createReducer(initialWSState as TWSState, (builder: any) => { builder - .addCase(wsConnecting, (state: TWSState) => { - state.status = WS_STATUS.CONNECTING - }) - .addCase(wsOpen, (state: TWSState) => { - state.status = WS_STATUS.ONLINE; - state.connectionError = '' - }) - .addCase(wsClose, (state: TWSState) => { - state.status = WS_STATUS.OFFLINE; - state.connectionError = '' - }) - .addCase(wsError, (state: TWSState, action: TWSAction) => { - state.connectionError = action.payload?.message - }) - .addCase(WS_MESSAGE, (state: TWSState, action: TWSAction) => { - state.feed = action.payload; - }) + .addCase(wsConnecting, (state: TWSState) => { + state.status = WS_STATUS_CONNECTING + }) + .addCase(wsOpen, (state: TWSState) => { + state.status = WS_STATUS_ONLINE; + state.connectionError = '' + }) + .addCase(wsClose, (state: TWSState) => { + state.status = WS_STATUS_OFFLINE; + state.connectionError = '' + }) + .addCase(wsError, (state: TWSState, action: TWSAction) => { + state.connectionError = action.payload.success ? '' : action.payload.message; + }) + .addCase(WS_MESSAGE, (state: TWSStateOnline, action: TWSAction) => { + state.feed = action.payload; + }) }) diff --git a/src/services/types/ingredients.tsx b/src/services/types/ingredients.tsx index b353473..95632cb 100644 --- a/src/services/types/ingredients.tsx +++ b/src/services/types/ingredients.tsx @@ -58,12 +58,14 @@ interface IGetCountriesSuccessAction { export const getIngredientsAction = (): IGetIngredientsAction => ({ type: GET_INGREDIENTS_REQUEST }); + export const getIngredientsSuccessAction = (ingredientsByType: TIngredientsByType, ingredientsById: TIngredientsById): IGetCountriesSuccessAction => ({ type: GET_INGREDIENTS_SUCCESS, ingredientsByType, ingredientsById }); + export const getIngredientsFailedAction = (): IGetIngredientsFailedAction => ({ type: GET_INGREDIENTS_FAILED }); diff --git a/src/services/types/request.tsx b/src/services/types/request.tsx index af803b4..bdb5774 100644 --- a/src/services/types/request.tsx +++ b/src/services/types/request.tsx @@ -1,11 +1,6 @@ -import { TUser } from "./user"; - -export type TRequestRetryOptions = TUser | string | undefined; - export type TRequestRetryOnSuccess = { [name: string]: string; } -export type requestRetry = (accessToken: string, options: TRequestRetryOptions) => Promise; export type requestRetryOnFail = (error: any) => void; export type requestRetryOnSuccess = (res: any) => void; diff --git a/src/services/types/ws.tsx b/src/services/types/ws.tsx index d0be5ec..980e1c3 100644 --- a/src/services/types/ws.tsx +++ b/src/services/types/ws.tsx @@ -9,49 +9,51 @@ import { } from '../../utils/constants'; import { TOrder } from './order'; - -export interface IWSMessage { - success: boolean; - message?: string; - orders?: Array; +export type TWSErrorMessage = { + success: false; + message: string; } +export type TWSMessage = { + success: true; + orders: Array; +} | TWSErrorMessage; interface IWSMessageAction { readonly type: typeof WS_MESSAGE; - payload: IWSMessage; + payload: TWSMessage; } -interface IWSErrorAction { +export interface IWSErrorAction { readonly type: typeof WS_ERROR; - payload: IWSMessage; + payload: TWSErrorMessage; } interface IWSOpenAction { readonly type: typeof WS_OPEN; - payload: IWSMessage; + payload: TWSMessage; } interface IWSCloseAction { readonly type: typeof WS_CLOSE; - payload: IWSMessage; + payload: TWSMessage; } interface IWSConnectingAction { readonly type: typeof WS_CONNECTING; - payload: IWSMessage; + payload: TWSMessage; } interface IWSConnectAction { readonly type: typeof WS_CONNECT; - payload: IWSMessage; + payload: TWSMessage; } interface IWSDisconnectAction { readonly type: typeof WS_DISCONNECT; - payload: IWSMessage | undefined; + payload: TWSMessage; } -export const wsMessageAction = (payload: IWSMessage): IWSMessageAction => ({ +export const wsMessageAction = (payload: TWSMessage): IWSMessageAction => ({ type: WS_MESSAGE, payload }); diff --git a/src/utils/api.tsx b/src/utils/api.tsx index 3511c06..13070a8 100644 --- a/src/utils/api.tsx +++ b/src/utils/api.tsx @@ -9,19 +9,22 @@ import { PASSWORDFORGOTURL, PASSWORDRESETURL } from "./constants"; -import { IEditUserOptions, IGetUserOptions, ILogoutUserOptions } from '../services/types/request'; import { TUser } from "../services/types/user"; +import { TIngredient } from "../services/types/ingredients"; -// TODO: нужно ли типизировать data? Должен быть {ok: boolean, json?: () => any } ??? -const checkResponse = async (data: any) => { +type TErrorResponse = { success: false; message: string; }; +type TIngredientsResponse = { success: true; data: Array; } | TErrorResponse; + +export type TResponse = TIngredientsResponse; + +const checkResponse = async (data: Response): Promise => { if (!data.ok) { - throw new Error(data.message, { cause: await data.json() }); + throw new Error(data.status.toString(), { cause: await data.json() }); } return data.json(); } -// TODO: как вообще типизировать ответ сервера??? -const getIngredientsRequest = async (): Promise => { +const getIngredientsRequest = async (): Promise => { const res = await fetch(GETINGREDIENTSURL); return await checkResponse(res); @@ -42,7 +45,7 @@ const sendOrderRequest = async (ingredientIds: Array, accessToken: strin return await checkResponse(res); } -const getUserRequest = async ({accessToken}: IGetUserOptions): Promise => { +const getUserRequest = async (accessToken: string): Promise => { const res = await fetch(USERURL, { method: 'GET', headers: { @@ -54,7 +57,7 @@ const getUserRequest = async ({accessToken}: IGetUserOptions): Promise => { return await checkResponse(res); } -const editUserRequest = async ({user, accessToken} : IEditUserOptions): Promise => { +const editUserRequest = async (accessToken: string, user: TUser): Promise => { const res = await fetch(USERURL, { method: 'PATCH', headers: { @@ -91,7 +94,7 @@ const loginRequest = async (user: TUser): Promise => { return await checkResponse(res); } -const logoutRequest = async ({token}: ILogoutUserOptions): Promise => { +const logoutRequest = async (accessToken: string, token: string): Promise => { const res = await fetch(LOGOUTUSERURL, { method: 'POST', headers: { @@ -127,7 +130,7 @@ const passwordForgotRequest = async (email: string): Promise => { return await checkResponse(res); } -const passwordResetRequest = async (password: string, token: number): Promise => { +const passwordResetRequest = async (password: string, token: string): Promise => { const res = await fetch(PASSWORDRESETURL, { method: 'POST', headers: { diff --git a/src/utils/constants.tsx b/src/utils/constants.tsx index 263ea95..c9ee3a6 100644 --- a/src/utils/constants.tsx +++ b/src/utils/constants.tsx @@ -1,5 +1,5 @@ const BASEURL = 'https://norma.nomoreparties.space/api'; -export const GETINGREDIENTSURL = BASEURL + '/ingredientsd'; +export const GETINGREDIENTSURL = BASEURL + '/ingredients'; export const SAVEORDERURL = BASEURL + '/orders'; export const ADDUSERURL = BASEURL + '/auth/register'; export const LOGINUSERURL = BASEURL + '/auth/login'; @@ -12,11 +12,14 @@ export const PASSWORDRESETURL = BASEURL + '/password-reset/reset'; export const WSURL = 'wss://norma.nomoreparties.space'; export const GET_ALL_ORDERS_URL = WSURL + '/orders/all'; export const GET_USER_ORDERS_URL = WSURL + '/orders'; -export const WS_STATUS = { - CONNECTING : 'CONNECTING...', - ONLINE : 'ONLINE', - OFFLINE : 'OFFLINE' -} +export const WS_STATUS_OFFLINE: 'OFFLINE' = 'OFFLINE'; +export const WS_STATUS_ONLINE: 'ONLINE' = 'ONLINE'; +export const WS_STATUS_CONNECTING: 'CONNECTING...' = 'CONNECTING...'; +// export const WS_STATUS = { +// CONNECTING : 'CONNECTING...', +// ONLINE : 'ONLINE', +// OFFLINE : 'OFFLINE' +// } export const WS_MESSAGE: 'WS_MESSAGE' = 'WS_MESSAGE'; export const WS_CONNECT: 'WS_CONNECT' = 'WS_CONNECT'; export const WS_DISCONNECT: 'WS_DISCONNECT' = 'WS_DISCONNECT'; From 4b044ec517254b42dc8066e9aceebd566e7b0dc9 Mon Sep 17 00:00:00 2001 From: Yulia Berezhnaya Date: Sun, 2 Apr 2023 11:45:46 +0300 Subject: [PATCH 08/13] =?UTF-8?q?=D0=A4=D0=B8=D0=BA=D1=81=20=D0=BE=D1=88?= =?UTF-8?q?=D0=B8=D0=B1=D0=BE=D0=BA=20=D1=82=D0=B8=D0=BF=D0=B8=D0=B7=D0=B0?= =?UTF-8?q?=D1=86=D0=B8=D0=B8=20=D0=B4=D0=BB=D1=8F=20=D0=B2=D0=BE=D1=81?= =?UTF-8?q?=D1=81=D1=82=D0=B0=D0=BD=D0=BE=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D1=8F=20=D0=BF=D0=B0=D1=80=D0=BE=D0=BB=D1=8F;=20=D1=82=D0=B8?= =?UTF-8?q?=D0=BF=D0=B8=D0=B7=D0=B0=D1=86=D0=B8=D1=8F=20=D0=90=D0=9F=D0=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ProtectedRoute/ProtectedRoute.js | 3 +- src/services/actions/user.tsx | 2 +- src/services/actions/ws.tsx | 2 +- src/services/reducers/user.tsx | 19 +----- src/services/types/user.tsx | 12 ++-- src/utils/api.tsx | 62 ++++++++++++++++--- 6 files changed, 63 insertions(+), 37 deletions(-) diff --git a/src/components/ProtectedRoute/ProtectedRoute.js b/src/components/ProtectedRoute/ProtectedRoute.js index 34f6518..3486792 100644 --- a/src/components/ProtectedRoute/ProtectedRoute.js +++ b/src/components/ProtectedRoute/ProtectedRoute.js @@ -14,7 +14,8 @@ function ProtectedRoute({element, isAuthPage, accessFrom}) { if (!isUserLoaded && (accessToken || refreshToken)) { return

Загрузка

-} + } + if (isAuthPage && isAuthSuccess) { return ; } diff --git a/src/services/actions/user.tsx b/src/services/actions/user.tsx index 5d43000..517d114 100644 --- a/src/services/actions/user.tsx +++ b/src/services/actions/user.tsx @@ -118,7 +118,7 @@ export const forgotPassword: AppThunk = (email: string) => (dispatch: AppDispatc passwordForgotRequest(email) .then((res) => { if (res && res.success) { - dispatch(forgotPasswordSuccessAction(res.user)); + dispatch(forgotPasswordSuccessAction()); } }) .catch(() => { diff --git a/src/services/actions/ws.tsx b/src/services/actions/ws.tsx index 12067bf..49245ce 100644 --- a/src/services/actions/ws.tsx +++ b/src/services/actions/ws.tsx @@ -40,7 +40,7 @@ export const wsMessage = (action: TWSMessage) => (dispatch: AppDispatch) => { dispatch(wsConnect(`${GET_USER_ORDERS_URL}?token=${getCookie('accessToken')}`)); } else { - throw new Error('Произошла ошибка ', res.message); + throw new Error('Произошла ошибка '); } }) diff --git a/src/services/reducers/user.tsx b/src/services/reducers/user.tsx index 90d8a7f..ebb0091 100644 --- a/src/services/reducers/user.tsx +++ b/src/services/reducers/user.tsx @@ -25,9 +25,7 @@ import { RESET_PASSWORD_FAILED, LOGOUT_USER_SUCCESS, - LOGOUT_USER_FAILED, - - // GET_USER_ORDERS + LOGOUT_USER_FAILED } from '../../utils/constants'; import { TUserActions } from '../types/user'; @@ -84,9 +82,7 @@ const initialUserState = { canResetPassword: false, resetPasswordSuccess: false, - code: '', - - // orders: [] + code: '' } export const userReducer = (state: TUserState = initialUserState, action: TUserActions) => { @@ -246,17 +242,6 @@ export const userReducer = (state: TUserState = initialUserState, action: TUserA logoutError: true } } - - - - - // case GET_USER_ORDERS: { - // return { - // ...state, - // orders: action.action.orders - // } - // } - default: return state; } diff --git a/src/services/types/user.tsx b/src/services/types/user.tsx index 88dfb33..f13500a 100644 --- a/src/services/types/user.tsx +++ b/src/services/types/user.tsx @@ -129,17 +129,15 @@ export const editUserFailedAction = (): IEditUserFailedAction => ({ // Типизация экшнов для забытого юзером пароля interface IForgotPasswordSuccessAction { - readonly type: typeof RESET_PASSWORD_SUCCESS; - user: TUser + readonly type: typeof FORGOT_PASSWORD_SUCCESS; } interface IForgotPasswordFailedAction { readonly type: typeof FORGOT_PASSWORD_FAILED; } -export const forgotPasswordSuccessAction = (user: TUser): IForgotPasswordSuccessAction => ({ - type: RESET_PASSWORD_SUCCESS, - user +export const forgotPasswordSuccessAction = (): IForgotPasswordSuccessAction => ({ + type: FORGOT_PASSWORD_SUCCESS }); export const forgotPasswordFailedAction = (): IForgotPasswordFailedAction => ({ @@ -149,7 +147,7 @@ export const forgotPasswordFailedAction = (): IForgotPasswordFailedAction => ({ // Типизация экшнов для сброса пароля interface IResetPasswordSuccessAction { - readonly type: typeof FORGOT_PASSWORD_SUCCESS; + readonly type: typeof RESET_PASSWORD_SUCCESS; } interface IResetPasswordFailedAction { @@ -157,7 +155,7 @@ interface IResetPasswordFailedAction { } export const resetPasswordSuccessAction = (): IResetPasswordSuccessAction => ({ - type: FORGOT_PASSWORD_SUCCESS + type: RESET_PASSWORD_SUCCESS }); export const resetPasswordFailedAction = (): IResetPasswordFailedAction => ({ diff --git a/src/utils/api.tsx b/src/utils/api.tsx index 13070a8..0e77ee9 100644 --- a/src/utils/api.tsx +++ b/src/utils/api.tsx @@ -11,11 +11,53 @@ import { } from "./constants"; import { TUser } from "../services/types/user"; import { TIngredient } from "../services/types/ingredients"; +import { TOrder } from "../services/types/order"; type TErrorResponse = { success: false; message: string; }; type TIngredientsResponse = { success: true; data: Array; } | TErrorResponse; +type TSendOrderResponse = { + success: true; + name: string; + order: TOrder; +} | TErrorResponse; + +type TUserResponse = { + success: true; + user: TUser +} | TErrorResponse; + +type TLoginUserResponse = { + success: true; + accessToken: string; + refreshToken: string; + user: TUser; +} | TErrorResponse; + +type TLogoutUserResponse = { + success: true; + message: string; +} | TErrorResponse; + +type TRefreshTokenResponse = { + success: true; + accessToken: string; + refreshToken: string; +} | TErrorResponse; + +type TForgotPasswordResponse = { + success: boolean; + message: string; + user: TUser; +} -export type TResponse = TIngredientsResponse; +export type TResponse = +TIngredientsResponse +& TSendOrderResponse +& TUserResponse +& TLoginUserResponse +& TLogoutUserResponse +& TRefreshTokenResponse +& TForgotPasswordResponse; const checkResponse = async (data: Response): Promise => { if (!data.ok) { @@ -30,7 +72,7 @@ const getIngredientsRequest = async (): Promise => { return await checkResponse(res); } -const sendOrderRequest = async (ingredientIds: Array, accessToken: string): Promise => { +const sendOrderRequest = async (ingredientIds: Array, accessToken: string): Promise => { const res = await fetch(SAVEORDERURL, { method: 'POST', headers: { @@ -45,7 +87,7 @@ const sendOrderRequest = async (ingredientIds: Array, accessToken: strin return await checkResponse(res); } -const getUserRequest = async (accessToken: string): Promise => { +const getUserRequest = async (accessToken: string): Promise => { const res = await fetch(USERURL, { method: 'GET', headers: { @@ -57,7 +99,7 @@ const getUserRequest = async (accessToken: string): Promise => { return await checkResponse(res); } -const editUserRequest = async (accessToken: string, user: TUser): Promise => { +const editUserRequest = async (accessToken: string, user: TUser): Promise => { const res = await fetch(USERURL, { method: 'PATCH', headers: { @@ -70,7 +112,7 @@ const editUserRequest = async (accessToken: string, user: TUser): Promise = return checkResponse(res); } -const addUserRequest = async (user: TUser): Promise => { +const addUserRequest = async (user: TUser): Promise => { const res = await fetch(ADDUSERURL, { method: 'POST', headers: { @@ -82,7 +124,7 @@ const addUserRequest = async (user: TUser): Promise => { return await checkResponse(res); } -const loginRequest = async (user: TUser): Promise => { +const loginRequest = async (user: TUser): Promise => { const res = await fetch(LOGINUSERURL, { method: 'POST', headers: { @@ -94,7 +136,7 @@ const loginRequest = async (user: TUser): Promise => { return await checkResponse(res); } -const logoutRequest = async (accessToken: string, token: string): Promise => { +const logoutRequest = async (accessToken: string, token: string): Promise => { const res = await fetch(LOGOUTUSERURL, { method: 'POST', headers: { @@ -106,7 +148,7 @@ const logoutRequest = async (accessToken: string, token: string): Promise = return await checkResponse(res); } -const refreshTokenRequest = async (token: string): Promise => { +const refreshTokenRequest = async (token: string): Promise => { const res = await fetch(REFRESHTOKENURL, { method: 'POST', headers: { @@ -118,7 +160,7 @@ const refreshTokenRequest = async (token: string): Promise => { return await checkResponse(res); } -const passwordForgotRequest = async (email: string): Promise => { +const passwordForgotRequest = async (email: string): Promise => { const res = await fetch(PASSWORDFORGOTURL, { method: 'POST', headers: { @@ -130,7 +172,7 @@ const passwordForgotRequest = async (email: string): Promise => { return await checkResponse(res); } -const passwordResetRequest = async (password: string, token: string): Promise => { +const passwordResetRequest = async (password: string, token: string): Promise => { const res = await fetch(PASSWORDRESETURL, { method: 'POST', headers: { From a073513f9328eb52560224f969181d0cb376e7c2 Mon Sep 17 00:00:00 2001 From: Yulia Berezhnaya Date: Sun, 2 Apr 2023 22:31:00 +0300 Subject: [PATCH 09/13] =?UTF-8?q?=D0=A2=D0=B8=D0=BF=D0=B8=D0=B7=D0=B0?= =?UTF-8?q?=D1=86=D0=B8=D1=8F=20=D1=87=D0=B0=D1=81=D1=82=D0=B8=20=D0=BA?= =?UTF-8?q?=D0=BE=D0=BC=D0=BF=D0=BE=D0=BD=D0=B5=D0=BD=D1=82=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/App/{App.js => App.tsx} | 12 +++---- ...erConstructor.js => BurgerConstructor.tsx} | 19 +++++----- ...erIngredients.js => BurgerIngredients.tsx} | 26 ++++++++------ .../{Ingredient.js => Ingredient.tsx} | 36 +++++++++---------- ...ientCategory.js => IngredientCategory.tsx} | 30 ++++++++-------- ...edientDetails.js => IngredientDetails.tsx} | 2 +- ...dientPreview.jsx => IngredientPreview.tsx} | 5 +-- src/components/Modal/Modal.tsx | 2 +- .../OrderAccepted/OrderAccepted.jsx | 2 +- src/components/OrderSnippet/OrderSnippet.jsx | 2 +- src/components/Orders/Orders.js | 2 +- src/components/TotalPrice/TotalPrice.tsx | 7 ++-- src/services/reducers/index.tsx | 35 +++++++++--------- src/services/types/index.tsx | 10 +++++- src/services/types/ingredients.tsx | 3 ++ src/utils/constants.tsx | 8 ++--- 16 files changed, 108 insertions(+), 93 deletions(-) rename src/components/App/{App.js => App.tsx} (93%) rename src/components/BurgerConstructor/{BurgerConstructor.js => BurgerConstructor.tsx} (84%) rename src/components/BurgerIngredients/{BurgerIngredients.js => BurgerIngredients.tsx} (77%) rename src/components/Ingredient/{Ingredient.js => Ingredient.tsx} (63%) rename src/components/IngredientCategory/{IngredientCategory.js => IngredientCategory.tsx} (67%) rename src/components/IngredientDetails/{IngredientDetails.js => IngredientDetails.tsx} (96%) rename src/components/IngredientPreview/{IngredientPreview.jsx => IngredientPreview.tsx} (83%) diff --git a/src/components/App/App.js b/src/components/App/App.tsx similarity index 93% rename from src/components/App/App.js rename to src/components/App/App.tsx index 9f9f38b..2f121dc 100644 --- a/src/components/App/App.js +++ b/src/components/App/App.tsx @@ -1,8 +1,8 @@ import AppStyles from './App.module.css'; -import React, { useEffect } from 'react'; +import { useEffect } from 'react'; import { Routes, Route, useLocation, useNavigate } from 'react-router-dom'; -import { useSelector, useDispatch } from 'react-redux'; +import { useSelector, useDispatch } from '../../services/hooks'; import { WS_STATUS_ONLINE } from '../../utils/constants'; @@ -12,7 +12,7 @@ import ProtectedRoute from '../ProtectedRoute/ProtectedRoute'; import Orders from '../Orders/Orders'; -import { LoginPage } from '../../pages/login.tsx'; +import { LoginPage } from '../../pages/login'; import { RegisterPage } from '../../pages/register'; import { ForgotPasswordPage } from '../../pages/forgotPassword'; import { ResetPasswordPage } from '../../pages/resetPassword'; @@ -28,16 +28,14 @@ import { getUser } from '../../services/actions/user'; function App() { const dispatch = useDispatch(); - const history = useNavigate(); - const location = useLocation(); const modalBackground = location.state?.modalBackground; const { ingredientsByType } = useSelector(state => state.menu); - const { orders } = useSelector(({ status, feed }) => ( - status === WS_STATUS_ONLINE && feed?.success ? feed : {} + const { orders } = useSelector(({ws}) => ( + ws.status === WS_STATUS_ONLINE && ws.feed?.success ? ws.feed : { orders: []} )); useEffect(() => { diff --git a/src/components/BurgerConstructor/BurgerConstructor.js b/src/components/BurgerConstructor/BurgerConstructor.tsx similarity index 84% rename from src/components/BurgerConstructor/BurgerConstructor.js rename to src/components/BurgerConstructor/BurgerConstructor.tsx index a6b6c89..96e5d7b 100644 --- a/src/components/BurgerConstructor/BurgerConstructor.js +++ b/src/components/BurgerConstructor/BurgerConstructor.tsx @@ -1,8 +1,9 @@ +import { FC } from 'react'; import BurgerConstructorStyles from './BurgerConstructor.module.css'; -import { useDispatch, useSelector } from 'react-redux'; +import { useDispatch, useSelector } from '../../services/hooks'; import PropTypes from 'prop-types'; -import { useDrop } from 'react-dnd'; +import { useDrop, DropTargetMonitor } from 'react-dnd'; import { ConstructorElement, Button } from '@ya.praktikum/react-developer-burger-ui-components'; import TotalPrice from '../TotalPrice/TotalPrice'; @@ -11,14 +12,15 @@ import Ingredient from '../Ingredient/Ingredient'; import { sendOrder } from '../../services/actions/ingredients'; import { ADD_BUN, ADD_INGREDIENT } from '../../utils/constants'; import { useNavigate } from 'react-router-dom'; +import { TIngredientList } from '../../services/types/ingredients'; -function BurgerConstructor({ openOrderDetailsModal }) { +const BurgerConstructor: FC<{openOrderDetailsModal: () => void}> = ({ openOrderDetailsModal }) => { const dispatch = useDispatch(); const navigate = useNavigate(); - const {addedIngredients, ingredientsById} = useSelector(state => state.menu); - const {isAuthSuccess} = useSelector(state => state.user); + const { addedIngredients, ingredientsById } = useSelector(state => state.menu); + const { isAuthSuccess } = useSelector(state => state.user); const ingredientIds = () => { let res = addedIngredients.bun ? [addedIngredients.bun._id] : []; @@ -43,13 +45,12 @@ function BurgerConstructor({ openOrderDetailsModal }) { } } - const [{isHover}, dropTarget] = useDrop({ + const [{ isHover }, dropTarget] = useDrop({ accept: 'ingredient', - item: {}, - collect: monitor => ({ + collect: (monitor: DropTargetMonitor) => ({ isHover: monitor.isOver() }), - drop(item) { + drop(item: TIngredientList) { const type = ingredientsById[item.id].type === 'bun' ? ADD_BUN : ADD_INGREDIENT; dispatch({type: type, id: item.id}) }, diff --git a/src/components/BurgerIngredients/BurgerIngredients.js b/src/components/BurgerIngredients/BurgerIngredients.tsx similarity index 77% rename from src/components/BurgerIngredients/BurgerIngredients.js rename to src/components/BurgerIngredients/BurgerIngredients.tsx index 192d380..73ebf80 100644 --- a/src/components/BurgerIngredients/BurgerIngredients.js +++ b/src/components/BurgerIngredients/BurgerIngredients.tsx @@ -1,6 +1,7 @@ +import { FC, Ref } from 'react'; import BurgerIngredientsStyles from './BurgerIngredients.module.css'; -import { useDispatch, useSelector } from 'react-redux'; +import { useDispatch, useSelector } from '../../services/hooks'; import PropTypes from 'prop-types'; import { useInView } from 'react-intersection-observer'; @@ -10,19 +11,24 @@ import IngredientCategory from '../IngredientCategory/IngredientCategory'; import { SET_ACTIVE_TAB } from '../../utils/constants'; +type TCategoriesTitles = { + bun: string; + main: string; + sauce: string; +} -function BurgerIngredients({ openIngredientModal }) { +const BurgerIngredients: FC<{ openIngredientModal: () => void }> = ({ openIngredientModal }) => { const dispatch = useDispatch(); const { ingredientsByType, activeTab } = useSelector(state => state.menu); - const categoriesTitles = { - 'bun': 'Булки', - 'main': 'Начинки', - 'sauce': 'Соусы' + const categoriesTitles: TCategoriesTitles = { + bun: 'Булки', + main: 'Начинки', + sauce: 'Соусы' } - const [bunsRef, inViewBuns] = useInView({ + const [bunRef, inViewBun] = useInView({ threshold: 0.3 }); @@ -33,13 +39,13 @@ function BurgerIngredients({ openIngredientModal }) { threshold: 0.3 }); - const categoriesOrder = [ - {name: 'bun', ref: bunsRef, inView: inViewBuns}, + const categoriesOrder: Array<{name: keyof TCategoriesTitles; ref: Ref; inView: boolean}> = [ + {name: 'bun', ref: bunRef, inView: inViewBun}, {name: 'main', ref: mainsRef, inView: inViewFilling}, {name: 'sauce', ref: saucesRef, inView: inViewSauces} ]; - const setActiveTab = (name) => { + const setActiveTab = (name: string) => { dispatch({type: SET_ACTIVE_TAB, name}) } diff --git a/src/components/Ingredient/Ingredient.js b/src/components/Ingredient/Ingredient.tsx similarity index 63% rename from src/components/Ingredient/Ingredient.js rename to src/components/Ingredient/Ingredient.tsx index 69c0dd8..b9929ea 100644 --- a/src/components/Ingredient/Ingredient.js +++ b/src/components/Ingredient/Ingredient.tsx @@ -1,26 +1,28 @@ import IngredientStyles from './Ingredient.module.css'; -import React, { useRef } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; -import PropTypes from 'prop-types'; -import { useDrag, useDrop } from 'react-dnd'; +import { useRef, FC, BaseSyntheticEvent, MouseEvent } from 'react'; +import { useDispatch, useSelector } from '../../services/hooks'; +import { useDrag, useDrop, DropTargetMonitor } from 'react-dnd'; import { DragIcon, ConstructorElement } from '@ya.praktikum/react-developer-burger-ui-components'; import { CHANGE_INGREDIENT_ORDER, DELETE_INGREDIENT } from '../../utils/constants'; +import { TIngredientList } from '../../services/types/ingredients'; - -function Ingredient({id, index}) { +const Ingredient: FC = ({id, index}) => { const dispatch = useDispatch(); - const ref = useRef(null); + const ref = useRef(null); - const {ingredientsById} = useSelector(state => state.menu); + const { ingredientsById } = useSelector(state => state.menu); - const moveItem = (prevIndex, newIndex) => { - dispatch({type: CHANGE_INGREDIENT_ORDER, prevId: prevIndex, newId: newIndex}); + const moveItem = (prevIndex: number, newIndex: number) => { + dispatch({type: CHANGE_INGREDIENT_ORDER, prevIndex, newIndex}); } - const handleDeleteIngredient = (e) => { - const targetElement = e.target.parentNode.parentNode; + // TODO + const handleDeleteIngredient = (e: MouseEvent) => { + console.log('e', e.target); + const targetElement = (e.target as HTMLLIElement).parentNode?.parentNode as HTMLElement; + if (targetElement.classList.contains('constructor-element__action')) { dispatch({type: DELETE_INGREDIENT, index: e.currentTarget.id}); } @@ -29,7 +31,7 @@ function Ingredient({id, index}) { const [, dropRef] = useDrop({ accept: 'sortIngregient', - hover(item, monitor) { + hover(item: TIngredientList, monitor: DropTargetMonitor) { if (!ref.current) { return } @@ -42,6 +44,7 @@ function Ingredient({id, index}) { const hoverBoundingRect = ref.current?.getBoundingClientRect() const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2; const clientOffset = monitor.getClientOffset(); + if (!clientOffset) return; const hoverClientY = clientOffset.y - hoverBoundingRect.top; if (dragIndex < hoverIndex && hoverClientY < hoverMiddleY) { @@ -71,7 +74,7 @@ function Ingredient({id, index}) { dragRef(dropRef(ref)); return ( -
  • +
  • ; + openIngredientModal: () => void; + link: Ref; + inView: boolean; +} -function IngredientCategory({ id, title, data, openIngredientModal, link, inView }) { +const IngredientCategory: FC = ({ id, title, data, openIngredientModal, link, inView }) => { const dispatch = useDispatch(); const { addedIngredients } = useSelector(store => store.menu); const ingredientsCount = useMemo(() => { - return data?.reduce((res, item) => { + return data?.reduce((res: {[name: string]: { count?: number}}, item) => { res[item._id] = {}; if (item.type === 'bun' && item._id === addedIngredients.bun?._id) { res[item._id].count = 1; @@ -51,13 +58,4 @@ function IngredientCategory({ id, title, data, openIngredientModal, link, inView ); } -// IngredientCategory.propTypes = { -// id: PropTypes.string.isRequired, -// title: PropTypes.string.isRequired, -// data: PropTypes.arrayOf(IngredientsPropTypes).isRequired, -// openIngredientModal: PropTypes.func.isRequired, -// link: PropTypes.func.isRequired, -// inView: PropTypes.bool.isRequired -// } - export default IngredientCategory; diff --git a/src/components/IngredientDetails/IngredientDetails.js b/src/components/IngredientDetails/IngredientDetails.tsx similarity index 96% rename from src/components/IngredientDetails/IngredientDetails.js rename to src/components/IngredientDetails/IngredientDetails.tsx index f08bf2d..95737e2 100644 --- a/src/components/IngredientDetails/IngredientDetails.js +++ b/src/components/IngredientDetails/IngredientDetails.tsx @@ -1,6 +1,6 @@ import IngredientDetailsStyles from './IngredientDetails.module.css'; -import { useDispatch, useSelector } from 'react-redux'; +import { useDispatch, useSelector } from '../../services/hooks'; import { useParams } from 'react-router-dom'; import { SHOW_INGREDIENT } from '../../utils/constants'; diff --git a/src/components/IngredientPreview/IngredientPreview.jsx b/src/components/IngredientPreview/IngredientPreview.tsx similarity index 83% rename from src/components/IngredientPreview/IngredientPreview.jsx rename to src/components/IngredientPreview/IngredientPreview.tsx index 85dd410..eca5e92 100644 --- a/src/components/IngredientPreview/IngredientPreview.jsx +++ b/src/components/IngredientPreview/IngredientPreview.tsx @@ -1,8 +1,9 @@ import IngredientPreviewStyles from './IngredientPreview.module.css'; -import { useSelector } from 'react-redux'; +import { FC } from 'react'; +import { useSelector } from '../../services/hooks'; -function IngredientPreview({ id, hidedLength }) { +const IngredientPreview: FC<{id: string; hidedLength: number}> = ({ id, hidedLength }) => { const { ingredientsById } = useSelector((state) => state.menu); return ( diff --git a/src/components/Modal/Modal.tsx b/src/components/Modal/Modal.tsx index 72e5ae9..9316f21 100644 --- a/src/components/Modal/Modal.tsx +++ b/src/components/Modal/Modal.tsx @@ -7,7 +7,7 @@ import { CloseIcon } from '@ya.praktikum/react-developer-burger-ui-components'; const modalsContainer = document.querySelector('#modals') as HTMLElement; type TModalProps = { - title: string; + title?: string; onClose: () => void; children: ReactNode; } diff --git a/src/components/OrderAccepted/OrderAccepted.jsx b/src/components/OrderAccepted/OrderAccepted.jsx index cb42103..24bc626 100644 --- a/src/components/OrderAccepted/OrderAccepted.jsx +++ b/src/components/OrderAccepted/OrderAccepted.jsx @@ -1,6 +1,6 @@ import OrderAcceptedStyles from './OrderAccepted.module.css'; -import { useSelector } from 'react-redux'; +import { useSelector } from '../../services/hooks'; import done from '../../images/done.svg'; diff --git a/src/components/OrderSnippet/OrderSnippet.jsx b/src/components/OrderSnippet/OrderSnippet.jsx index bb4b40c..18a9ac8 100644 --- a/src/components/OrderSnippet/OrderSnippet.jsx +++ b/src/components/OrderSnippet/OrderSnippet.jsx @@ -1,7 +1,7 @@ import OrderSnippetStyles from './OrderSnippet.module.css'; import { Link, useLocation } from 'react-router-dom'; -import { useSelector } from 'react-redux'; +import { useSelector } from '../../services/hooks'; import { CurrencyIcon } from '@ya.praktikum/react-developer-burger-ui-components'; import IngredientPreview from '../IngredientPreview/IngredientPreview'; diff --git a/src/components/Orders/Orders.js b/src/components/Orders/Orders.js index 2645218..97813f3 100644 --- a/src/components/Orders/Orders.js +++ b/src/components/Orders/Orders.js @@ -9,7 +9,7 @@ import OrderSnippet from '../OrderSnippet/OrderSnippet'; function Orders() { const dispatch = useDispatch(); const { total, totalToday, orders } = useSelector(({ws}) => ( - ws.status === WS_STATUS_ONLINE && ws.feed.success ? ws.feed : {} + ws.status === WS_STATUS_ONLINE && ws.feed.success ? ws.feed : {orders: []} )); const ordersByStatus = orders?.length && orders.reduce((res, item) => { diff --git a/src/components/TotalPrice/TotalPrice.tsx b/src/components/TotalPrice/TotalPrice.tsx index 156e0dd..a44bae9 100644 --- a/src/components/TotalPrice/TotalPrice.tsx +++ b/src/components/TotalPrice/TotalPrice.tsx @@ -1,19 +1,20 @@ import TotalPriceStyles from './TotalPrice.module.css'; import { useMemo, FC } from 'react'; -import { useSelector } from 'react-redux'; +import { useSelector } from '../../services/hooks'; import { TRootState } from '../../services/types/index'; import { CurrencyIcon } from '@ya.praktikum/react-developer-burger-ui-components'; import { TMenuState } from '../../services/reducers'; +import { TIngredient } from '../../services/types/ingredients'; const TotalPrice: FC = () => { const { addedIngredients: { bun, others } }: TMenuState = useSelector((state: TRootState) => state.menu); const total = useMemo(() => ( - others.reduce( - (res, {price}) => res += price, + (others as TIngredient[]).reduce( + (res: number, {price}: TIngredient) => res += price, bun ? bun.price * 2 : 0 )), [bun, others]); diff --git a/src/services/reducers/index.tsx b/src/services/reducers/index.tsx index 3134a77..d297548 100644 --- a/src/services/reducers/index.tsx +++ b/src/services/reducers/index.tsx @@ -19,26 +19,29 @@ import { import { TIngredientsByType, TIngredientsById, TIngredient } from '../types/ingredients'; import { TIngredientsAction } from '../types/ingredients'; + +type TAddedIngredients = { + bun: TIngredient | null; + others: Array | []; +} + export type TMenuState = { - ingredientsByType: {} & TIngredientsByType , - ingredientsById: {} & TIngredientsById, - ingredientsRequest: boolean, - ingredientsFailed: boolean, + ingredientsByType: TIngredientsByType; + ingredientsById: TIngredientsById; + ingredientsRequest: boolean; + ingredientsFailed: boolean; - addedIngredients: { - bun: TIngredient | null, - others: Array - }, - currentIngredient: string | null, + addedIngredients: TAddedIngredients; + currentIngredient: null | TIngredient; - activeTab: 'bun' | 'main' | 'sauce', + activeTab: string; - orderId: number | null, - orderRequest: boolean, - orderFailed: boolean + orderId: number | null; + orderRequest: boolean; + orderFailed: boolean; } -const initialMenuState: TMenuState = { +const initialMenuState = { ingredientsByType: {}, ingredientsById: {}, ingredientsRequest: false, @@ -46,7 +49,7 @@ const initialMenuState: TMenuState = { addedIngredients: { bun: null, - others: [] + others: [], }, currentIngredient: null, @@ -59,7 +62,7 @@ const initialMenuState: TMenuState = { let uniqId = 0; -export const ingredientsReducer = (state: TMenuState = initialMenuState, action: TIngredientsAction) => { +export const ingredientsReducer = (state: TMenuState = initialMenuState, action: TIngredientsAction): TMenuState => { switch (action.type) { case GET_INGREDIENTS_REQUEST: { return { diff --git a/src/services/types/index.tsx b/src/services/types/index.tsx index e7a1aa8..ffe68a3 100644 --- a/src/services/types/index.tsx +++ b/src/services/types/index.tsx @@ -4,8 +4,16 @@ import { store } from '../store'; import { TIngredientsAction } from '../types/ingredients'; import { TUserActions } from './user'; import { TWSAction } from '../types/ws'; +import { TMenuState } from '../reducers'; +import { TUserState } from '../reducers/user'; +import { TWSState } from '../reducers/ws'; -export type TRootState = ReturnType +// export type TRootState = ReturnType +export type TRootState = { + menu: TMenuState; + user: TUserState; + ws: TWSState; +}; // Типизация всех экшенов приложения type TApplicationActions = TIngredientsAction | TUserActions | TWSAction; diff --git a/src/services/types/ingredients.tsx b/src/services/types/ingredients.tsx index 95632cb..34590bf 100644 --- a/src/services/types/ingredients.tsx +++ b/src/services/types/ingredients.tsx @@ -29,6 +29,7 @@ export type TIngredient = { image_mobile: string; image_large: string; __v?: number; + key?: number; }; export type TIngredientsByType = { @@ -147,3 +148,5 @@ export type TIngredientsAction = | IAddBun | IChangeOrderIngredients | ISetActiveTab; + +export type TIngredientList = {id: string, index: number}; diff --git a/src/utils/constants.tsx b/src/utils/constants.tsx index c9ee3a6..3d87810 100644 --- a/src/utils/constants.tsx +++ b/src/utils/constants.tsx @@ -15,11 +15,7 @@ export const GET_USER_ORDERS_URL = WSURL + '/orders'; export const WS_STATUS_OFFLINE: 'OFFLINE' = 'OFFLINE'; export const WS_STATUS_ONLINE: 'ONLINE' = 'ONLINE'; export const WS_STATUS_CONNECTING: 'CONNECTING...' = 'CONNECTING...'; -// export const WS_STATUS = { -// CONNECTING : 'CONNECTING...', -// ONLINE : 'ONLINE', -// OFFLINE : 'OFFLINE' -// } + export const WS_MESSAGE: 'WS_MESSAGE' = 'WS_MESSAGE'; export const WS_CONNECT: 'WS_CONNECT' = 'WS_CONNECT'; export const WS_DISCONNECT: 'WS_DISCONNECT' = 'WS_DISCONNECT'; @@ -28,6 +24,8 @@ export const WS_OPEN: 'WS_OPEN' = 'WS_OPEN'; export const WS_CLOSE: 'WS_CLOSE' = 'WS_CLOSE'; export const WS_ERROR: 'WS_ERROR' = 'WS_ERROR'; +// export const CATEGORIES_TITLES: 'bun' | 'main' | 'sauce' = ; + // Константы для обработки запроса для получения всех ингридиентов export const GET_INGREDIENTS_REQUEST: 'GET_INGREDIENTS_REQUEST' = 'GET_INGREDIENTS_REQUEST'; export const GET_INGREDIENTS_SUCCESS: 'GET_INGREDIENTS_SUCCESS' = 'GET_INGREDIENTS_SUCCESS'; From 6526381a9485585f1c42d7f54da537f769ef3d0c Mon Sep 17 00:00:00 2001 From: Yulia Berezhnaya Date: Sun, 2 Apr 2023 23:46:25 +0300 Subject: [PATCH 10/13] =?UTF-8?q?=D0=A2=D0=B8=D0=BF=D0=B8=D0=B7=D0=B0?= =?UTF-8?q?=D1=86=D0=B8=D1=8F=20=D0=BA=D0=BE=D0=BC=D0=BF=D0=BE=D0=BD=D0=B5?= =?UTF-8?q?=D0=BD=D1=82=D0=BE=D0=B2=20=D0=B4=D0=BB=D1=8F=20=D0=BE=D1=82?= =?UTF-8?q?=D0=BE=D0=B1=D1=80=D0=B0=D0=B6=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=B7?= =?UTF-8?q?=D0=B0=D0=BA=D0=B0=D0=B7=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../IngredientPreview/IngredientPreview.tsx | 2 +- src/components/ModalOverlay/ModalOverlay.tsx | 2 +- .../{OrderAccepted.jsx => OrderAccepted.tsx} | 3 +- .../{OrderDetails.js => OrderDetails.tsx} | 39 ++++++++++++------- .../{OrderSnippet.jsx => OrderSnippet.tsx} | 11 +++++- .../Orders/{Orders.js => Orders.tsx} | 33 +++++++++------- src/services/types/order.tsx | 5 ++- src/utils/constants.tsx | 3 +- src/utils/date.tsx | 2 +- 9 files changed, 64 insertions(+), 36 deletions(-) rename src/components/OrderAccepted/{OrderAccepted.jsx => OrderAccepted.tsx} (94%) rename src/components/OrderDetails/{OrderDetails.js => OrderDetails.tsx} (77%) rename src/components/OrderSnippet/{OrderSnippet.jsx => OrderSnippet.tsx} (91%) rename src/components/Orders/{Orders.js => Orders.tsx} (70%) diff --git a/src/components/IngredientPreview/IngredientPreview.tsx b/src/components/IngredientPreview/IngredientPreview.tsx index eca5e92..ba1708d 100644 --- a/src/components/IngredientPreview/IngredientPreview.tsx +++ b/src/components/IngredientPreview/IngredientPreview.tsx @@ -3,7 +3,7 @@ import IngredientPreviewStyles from './IngredientPreview.module.css'; import { FC } from 'react'; import { useSelector } from '../../services/hooks'; -const IngredientPreview: FC<{id: string; hidedLength: number}> = ({ id, hidedLength }) => { +const IngredientPreview: FC<{id: string; hidedLength?: number, key?: number}> = ({ id, hidedLength, key }) => { const { ingredientsById } = useSelector((state) => state.menu); return ( diff --git a/src/components/ModalOverlay/ModalOverlay.tsx b/src/components/ModalOverlay/ModalOverlay.tsx index a46c1ec..2494911 100644 --- a/src/components/ModalOverlay/ModalOverlay.tsx +++ b/src/components/ModalOverlay/ModalOverlay.tsx @@ -5,7 +5,7 @@ type TModalProps = { onClick: () => void; } -const ModalOverlay: FC = ({onClick}) => { +const ModalOverlay: FC = ({ onClick }) => { return (
    diff --git a/src/components/OrderAccepted/OrderAccepted.jsx b/src/components/OrderAccepted/OrderAccepted.tsx similarity index 94% rename from src/components/OrderAccepted/OrderAccepted.jsx rename to src/components/OrderAccepted/OrderAccepted.tsx index 24bc626..7f0855e 100644 --- a/src/components/OrderAccepted/OrderAccepted.jsx +++ b/src/components/OrderAccepted/OrderAccepted.tsx @@ -1,10 +1,11 @@ +import { FC } from 'react'; import OrderAcceptedStyles from './OrderAccepted.module.css'; import { useSelector } from '../../services/hooks'; import done from '../../images/done.svg'; -function OrderAccepted() { +const OrderAccepted: FC = () => { const {orderId, orderRequest, orderFailed} = useSelector(state => state.menu); return ( diff --git a/src/components/OrderDetails/OrderDetails.js b/src/components/OrderDetails/OrderDetails.tsx similarity index 77% rename from src/components/OrderDetails/OrderDetails.js rename to src/components/OrderDetails/OrderDetails.tsx index f7cd88f..c1f0d76 100644 --- a/src/components/OrderDetails/OrderDetails.js +++ b/src/components/OrderDetails/OrderDetails.tsx @@ -1,30 +1,40 @@ import OrderDetailsStyles from './OrderDetails.module.css'; -import { useEffect } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; +import { useEffect, FC } from 'react'; +import { useDispatch, useSelector } from '../../services/hooks'; import { useLocation, useParams } from 'react-router-dom'; import IngredientPreview from '../IngredientPreview/IngredientPreview'; import { CurrencyIcon } from '@ya.praktikum/react-developer-burger-ui-components'; -import { ORDER_STATUSES, GET_ALL_ORDERS_URL, GET_USER_ORDERS_URL } from '../../utils/constants'; +import { ORDER_STATUSES, GET_ALL_ORDERS_URL, GET_USER_ORDERS_URL, WS_STATUS_ONLINE } from '../../utils/constants'; import { wsConnect, wsDisconnect } from '../../services/actions/ws'; import { getFormattedDate } from '../../utils/date'; import { getCookie } from '../../utils/cookie'; +import { TFeedData } from '../../services/types/order'; -function OrderDetails() { +type TIngredientCounts = { + total: number; + ingredients: { + [name: string]: number; + } +} + +const OrderDetails: FC = () => { const dispatch = useDispatch(); const location = useLocation(); - const { orders } = useSelector(state => state.ws.feed); + const { orders } = useSelector(({ ws }) => ( + ws.status === WS_STATUS_ONLINE && ws.feed.success ? ws.feed as TFeedData : {orders: [], total: 0, totalToday: 0} + )); const { ingredientsById } = useSelector(state => state.menu); const { orderId } = useParams(); const token = getCookie('accessToken'); let fullPageClass = location.state ? '' : 'fullPage'; - const currentOrder = orders?.find((el) => { + const currentOrder = orders.find((el) => { return el._id === orderId; }); - const ingredientCounts = currentOrder?.ingredients.reduce((res, item) => { + const ingredientCounts: TIngredientCounts | undefined = currentOrder ? currentOrder.ingredients.reduce((res: TIngredientCounts, item) => { if (item) { if (!res.ingredients[item]) { res.ingredients[item] = 0 @@ -35,7 +45,7 @@ function OrderDetails() { } return res; - }, {total: 0, ingredients: {}}); + }, {total: 0, ingredients: {}}) : undefined; useEffect(() => { if (!orders && location.pathname.includes('/feed')) { @@ -46,17 +56,18 @@ function OrderDetails() { dispatch(wsConnect(`${GET_USER_ORDERS_URL}?token=${token}`)); } - return () => { - if (!location.state) { - return dispatch(wsDisconnect()); - } - }; + return () => { dispatch(wsDisconnect()); } + // return () => { + // if (!location.state) { + // return dispatch(wsDisconnect()); + // } + // }; }, [dispatch]) return ( <> { !currentOrder &&

    Обрабатываем запрос

    } - { currentOrder && + { currentOrder && ingredientCounts &&

    #{currentOrder.number}

    {currentOrder.name}

    diff --git a/src/components/OrderSnippet/OrderSnippet.jsx b/src/components/OrderSnippet/OrderSnippet.tsx similarity index 91% rename from src/components/OrderSnippet/OrderSnippet.jsx rename to src/components/OrderSnippet/OrderSnippet.tsx index 18a9ac8..2576ee3 100644 --- a/src/components/OrderSnippet/OrderSnippet.jsx +++ b/src/components/OrderSnippet/OrderSnippet.tsx @@ -1,3 +1,4 @@ +import { FC } from 'react'; import OrderSnippetStyles from './OrderSnippet.module.css'; import { Link, useLocation } from 'react-router-dom'; @@ -7,8 +8,15 @@ import { CurrencyIcon } from '@ya.praktikum/react-developer-burger-ui-components import IngredientPreview from '../IngredientPreview/IngredientPreview'; import { ORDER_STATUSES } from '../../utils/constants'; import { getFormattedDate } from '../../utils/date'; +import { TOrder } from '../../services/types/order'; -function OrderSnippet({ order, needDetails, link }) { +type TOrderSnippet = { + order: TOrder; + needDetails?: boolean; + link: string; +} + +const OrderSnippet: FC = ({ order, needDetails, link }) => { const location = useLocation(); const { ingredientsById } = useSelector(state => state.menu); @@ -26,7 +34,6 @@ function OrderSnippet({ order, needDetails, link }) { return res; }, {hidedLength: order.ingredients.length - 6, maxLength: 6, ingredients: []}); - // console.log('OrderSnippet location', location); return (
    diff --git a/src/components/Orders/Orders.js b/src/components/Orders/Orders.tsx similarity index 70% rename from src/components/Orders/Orders.js rename to src/components/Orders/Orders.tsx index 97813f3..97bebb4 100644 --- a/src/components/Orders/Orders.js +++ b/src/components/Orders/Orders.tsx @@ -1,32 +1,39 @@ import OrdersStyles from './Orders.module.css'; import { GET_ALL_ORDERS_URL, WS_STATUS_ONLINE } from '../../utils/constants'; import { wsConnect, wsDisconnect } from '../../services/actions/ws'; -import { useEffect } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; +import { useEffect, FC } from 'react'; +import { useDispatch, useSelector } from '../../services/hooks'; import OrderSnippet from '../OrderSnippet/OrderSnippet'; +import { TFeedData, TOrder } from '../../services/types/order'; +type TOrdersByStatus = { + done: Array; + progress: Array; + created: Array; + cancelled: Array; +}; -function Orders() { +const Orders: FC = () => { const dispatch = useDispatch(); - const { total, totalToday, orders } = useSelector(({ws}) => ( - ws.status === WS_STATUS_ONLINE && ws.feed.success ? ws.feed : {orders: []} + const { total, totalToday, orders } = useSelector(({ ws }) => ( + ws.status === WS_STATUS_ONLINE && ws.feed.success ? ws.feed as TFeedData : {orders: [], total: 0, totalToday: 0} )); - const ordersByStatus = orders?.length && orders.reduce((res, item) => { + const ordersByStatus: TOrdersByStatus | undefined = orders?.length ? (orders as Array).reduce((res: TOrdersByStatus, item) => { if (res[item.status].length < 20) { res[item.status].push(item); } return res; - }, {done: [], progress: [], created: []}); + }, {done: [], progress: [], created: [], cancelled: []}) : undefined; - const columnCountDone = Math.floor(ordersByStatus?.done.length / 10); - const columnCountProgress = Math.floor(ordersByStatus?.progress.length / 10); + const columnCountDone = (ordersByStatus && ordersByStatus.done.length > 10 )? Math.floor(ordersByStatus.done.length / 10) : 1; + const columnCountProgress = (ordersByStatus && ordersByStatus.progress.length > 10 )? Math.floor(ordersByStatus.progress.length / 10) : 1; useEffect(() => { dispatch(wsConnect(GET_ALL_ORDERS_URL)); - return () => dispatch(wsDisconnect()); + return () => { dispatch(wsDisconnect()); } }, [dispatch]) return ( @@ -35,7 +42,7 @@ function Orders() {
    {orders && orders.map((item, index) => { - return + return }) }
    @@ -43,7 +50,7 @@ function Orders() {

    Готовы:

    -
      +
        {ordersByStatus && ordersByStatus.done.map((item, index) => { return (
      • {item.number}
      • @@ -53,7 +60,7 @@ function Orders() {

    В работе:

    -
      +
        {ordersByStatus && ordersByStatus.progress.map((item, index) => { return (
      • {item.number}
      • diff --git a/src/services/types/order.tsx b/src/services/types/order.tsx index 799e588..bde6c29 100644 --- a/src/services/types/order.tsx +++ b/src/services/types/order.tsx @@ -2,12 +2,13 @@ import { TIngredient } from "./ingredients"; export type TOrder = { _id: string; - status: string; + status: 'created' | 'done' | 'progress' | 'cancelled'; name: string; createdAt: Date | string; updatedAt: Date | string; number: number; - ingredients: Array + ingredients: Array + // ingredients: Array } export type TFeedData = { diff --git a/src/utils/constants.tsx b/src/utils/constants.tsx index 3d87810..14c7eed 100644 --- a/src/utils/constants.tsx +++ b/src/utils/constants.tsx @@ -84,5 +84,6 @@ export const GET_USER_ORDERS: 'GET_USER_ORDERS' = 'GET_USER_ORDERS'; export const ORDER_STATUSES = { done: 'Выполнен', cancelled: 'Отменён', - progress: 'Готовится' + progress: 'Готовится', + created: 'Создан' } diff --git a/src/utils/date.tsx b/src/utils/date.tsx index ff2f6df..f7ab0e8 100644 --- a/src/utils/date.tsx +++ b/src/utils/date.tsx @@ -1,4 +1,4 @@ -export function getFormattedDate(dateStr: string) { +export function getFormattedDate(dateStr: string | Date) { const dateNow: Date = new Date(); const date: Date = new Date(dateStr); const dateTimezone: string = 'i-GMT' + date.getTimezoneOffset()/60; From d36e8dde6ffc0ec9a226fc8065a3d356990b98f1 Mon Sep 17 00:00:00 2001 From: Yulia Berezhnaya Date: Mon, 3 Apr 2023 00:37:11 +0300 Subject: [PATCH 11/13] =?UTF-8?q?=D0=94=D0=BE=D1=80=D0=B0=D0=B1=D0=BE?= =?UTF-8?q?=D1=82=D0=BA=D0=B8=20=D1=82=D0=B8=D0=BF=D0=B8=D0=B7=D0=B0=D1=86?= =?UTF-8?q?=D0=B8=D0=B8=20=D0=BA=D0=BE=D0=BC=D0=BF=D0=BE=D0=BD=D0=B5=D0=BD?= =?UTF-8?q?=D1=82=D0=BE=D0=B2=20=D0=B4=D0=BB=D1=8F=20=D0=BE=D1=82=D0=BE?= =?UTF-8?q?=D0=B1=D1=80=D0=B0=D0=B6=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=B7=D0=B0?= =?UTF-8?q?=D0=BA=D0=B0=D0=B7=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../IngredientPreview/IngredientPreview.tsx | 2 +- src/components/OrderDetails/OrderDetails.tsx | 11 +++++------ src/components/OrderSnippet/OrderSnippet.tsx | 14 ++++++++++---- src/components/Orders/Orders.tsx | 4 ++-- 4 files changed, 18 insertions(+), 13 deletions(-) diff --git a/src/components/IngredientPreview/IngredientPreview.tsx b/src/components/IngredientPreview/IngredientPreview.tsx index ba1708d..d53390c 100644 --- a/src/components/IngredientPreview/IngredientPreview.tsx +++ b/src/components/IngredientPreview/IngredientPreview.tsx @@ -3,7 +3,7 @@ import IngredientPreviewStyles from './IngredientPreview.module.css'; import { FC } from 'react'; import { useSelector } from '../../services/hooks'; -const IngredientPreview: FC<{id: string; hidedLength?: number, key?: number}> = ({ id, hidedLength, key }) => { +const IngredientPreview: FC<{id: string; hidedLength?: number}> = ({ id, hidedLength }) => { const { ingredientsById } = useSelector((state) => state.menu); return ( diff --git a/src/components/OrderDetails/OrderDetails.tsx b/src/components/OrderDetails/OrderDetails.tsx index c1f0d76..91bdfa0 100644 --- a/src/components/OrderDetails/OrderDetails.tsx +++ b/src/components/OrderDetails/OrderDetails.tsx @@ -56,12 +56,11 @@ const OrderDetails: FC = () => { dispatch(wsConnect(`${GET_USER_ORDERS_URL}?token=${token}`)); } - return () => { dispatch(wsDisconnect()); } - // return () => { - // if (!location.state) { - // return dispatch(wsDisconnect()); - // } - // }; + return () => { + if (!location.state) { + dispatch(wsDisconnect()); + } + }; }, [dispatch]) return ( diff --git a/src/components/OrderSnippet/OrderSnippet.tsx b/src/components/OrderSnippet/OrderSnippet.tsx index 2576ee3..7e11c8a 100644 --- a/src/components/OrderSnippet/OrderSnippet.tsx +++ b/src/components/OrderSnippet/OrderSnippet.tsx @@ -16,18 +16,24 @@ type TOrderSnippet = { link: string; } +type TIngredientsPreview = { + hidedLength: number; + maxLength: 6; + ingredients: Array; +} + const OrderSnippet: FC = ({ order, needDetails, link }) => { const location = useLocation(); const { ingredientsById } = useSelector(state => state.menu); - const totalPrice = order.ingredients.reduce((res, item) => { - if (item) { - res += ingredientsById[item].price; + const totalPrice = order.ingredients.reduce((res, id) => { + if (id) { + res += ingredientsById[id].price; } return res; }, 0); - const ingredientsPreview = order.ingredients.reduceRight((res, item, index) => { + const ingredientsPreview: TIngredientsPreview = order.ingredients.reduceRight((res: TIngredientsPreview, item, index) => { if (item && index < res.maxLength) { res.ingredients.push(item); } diff --git a/src/components/Orders/Orders.tsx b/src/components/Orders/Orders.tsx index 97bebb4..a2f4c9a 100644 --- a/src/components/Orders/Orders.tsx +++ b/src/components/Orders/Orders.tsx @@ -15,8 +15,8 @@ type TOrdersByStatus = { const Orders: FC = () => { const dispatch = useDispatch(); - const { total, totalToday, orders } = useSelector(({ ws }) => ( - ws.status === WS_STATUS_ONLINE && ws.feed.success ? ws.feed as TFeedData : {orders: [], total: 0, totalToday: 0} + const { total, totalToday, orders } = useSelector((state) => ( + state.ws.status === WS_STATUS_ONLINE && state.ws.feed?.success ? state.ws.feed as TFeedData : {orders: [], total: 0, totalToday: 0} )); const ordersByStatus: TOrdersByStatus | undefined = orders?.length ? (orders as Array).reduce((res: TOrdersByStatus, item) => { From 7db32f69ee495420230ce355596a7194be993292 Mon Sep 17 00:00:00 2001 From: Yulia Berezhnaya Date: Mon, 3 Apr 2023 01:57:53 +0300 Subject: [PATCH 12/13] =?UTF-8?q?=D0=A4=D0=B8=D0=BA=D1=81=20=D0=BE=D1=88?= =?UTF-8?q?=D0=B8=D0=B1=D0=BE=D0=BA=20=D1=82=D0=B8=D0=BF=D0=B8=D0=B7=D0=B0?= =?UTF-8?q?=D1=86=D0=B8=D0=B8=20=D0=BA=D0=BE=D0=BC=D0=BF=D0=BE=D0=BD=D0=B5?= =?UTF-8?q?=D0=BD=D1=82=D0=BE=D0=B2=20=D0=B8=20=D1=85=D1=80=D0=B0=D0=BD?= =?UTF-8?q?=D0=B8=D0=BB=D0=B8=D1=89=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/App/App.tsx | 4 ++-- src/components/AppHeader/AppHeader.tsx | 3 ++- .../BurgerIngredients/BurgerIngredients.tsx | 4 ++-- .../IngredientCard/IngredientCard.tsx | 5 +++-- .../IngredientCategory/IngredientCategory.tsx | 4 ++-- .../IngredientDetails/IngredientDetails.tsx | 4 ++-- src/components/Main/{Main.js => Main.tsx} | 8 ++++---- .../{ProtectedRoute.js => ProtectedRoute.tsx} | 18 +++++++++--------- src/services/actions/ws.tsx | 4 ++-- src/services/reducers/ws.tsx | 8 ++++---- src/services/types/ws.tsx | 6 +++--- src/utils/constants.tsx | 2 -- 12 files changed, 35 insertions(+), 35 deletions(-) rename src/components/Main/{Main.js => Main.tsx} (89%) rename src/components/ProtectedRoute/{ProtectedRoute.js => ProtectedRoute.tsx} (75%) diff --git a/src/components/App/App.tsx b/src/components/App/App.tsx index 2f121dc..ae62dba 100644 --- a/src/components/App/App.tsx +++ b/src/components/App/App.tsx @@ -1,6 +1,6 @@ import AppStyles from './App.module.css'; -import { useEffect } from 'react'; +import { useEffect, FC } from 'react'; import { Routes, Route, useLocation, useNavigate } from 'react-router-dom'; import { useSelector, useDispatch } from '../../services/hooks'; @@ -26,7 +26,7 @@ import OrderDetails from '../OrderDetails/OrderDetails'; import { getIngredients } from '../../services/actions/ingredients'; import { getUser } from '../../services/actions/user'; -function App() { +const App: FC = () => { const dispatch = useDispatch(); const history = useNavigate(); const location = useLocation(); diff --git a/src/components/AppHeader/AppHeader.tsx b/src/components/AppHeader/AppHeader.tsx index 957783e..aab2680 100644 --- a/src/components/AppHeader/AppHeader.tsx +++ b/src/components/AppHeader/AppHeader.tsx @@ -1,8 +1,9 @@ +import { FC } from 'react'; import { BurgerIcon, ListIcon, Logo, ProfileIcon } from '@ya.praktikum/react-developer-burger-ui-components'; import { NavLink } from 'react-router-dom'; import AppHeaderStyles from './AppHeader.module.css'; -function AppHeader() { +const AppHeader: FC = () => { const active: string = AppHeaderStyles.link + ' ' + AppHeaderStyles.active + ' pl-5 pt-4 pr-5 pb-4 text text_type_main-default'; const link: string = AppHeaderStyles.link + ' pl-5 pt-4 pr-5 pb-4 text text_type_main-default text_color_inactive'; const activeAccountLink: string = AppHeaderStyles.link + ' ' + AppHeaderStyles.active + ' ' + AppHeaderStyles.account + ' pl-5 pt-4 pr-5 pb-4 text text_type_main-default'; diff --git a/src/components/BurgerIngredients/BurgerIngredients.tsx b/src/components/BurgerIngredients/BurgerIngredients.tsx index 73ebf80..b6db730 100644 --- a/src/components/BurgerIngredients/BurgerIngredients.tsx +++ b/src/components/BurgerIngredients/BurgerIngredients.tsx @@ -1,4 +1,4 @@ -import { FC, Ref } from 'react'; +import { FC, Ref, MouseEvent } from 'react'; import BurgerIngredientsStyles from './BurgerIngredients.module.css'; import { useDispatch, useSelector } from '../../services/hooks'; @@ -17,7 +17,7 @@ type TCategoriesTitles = { sauce: string; } -const BurgerIngredients: FC<{ openIngredientModal: () => void }> = ({ openIngredientModal }) => { +const BurgerIngredients: FC<{ openIngredientModal: (e: MouseEvent) => void }> = ({ openIngredientModal }) => { const dispatch = useDispatch(); const { ingredientsByType, activeTab } = useSelector(state => state.menu); diff --git a/src/components/IngredientCard/IngredientCard.tsx b/src/components/IngredientCard/IngredientCard.tsx index 825e8cd..ce71e05 100644 --- a/src/components/IngredientCard/IngredientCard.tsx +++ b/src/components/IngredientCard/IngredientCard.tsx @@ -1,7 +1,7 @@ import IngredientCardStyles from './IngredientCard.module.css'; import { TIngredient } from '../../services/types/ingredients'; -import { FC } from 'react'; +import { FC, MouseEvent } from 'react'; import { useDrag } from 'react-dnd'; import { Link, useLocation } from 'react-router-dom'; @@ -10,8 +10,9 @@ import { CurrencyIcon, Counter } from '@ya.praktikum/react-developer-burger-ui-c type TIngredientCardProps = { data: TIngredient; count?: number; - openIngredientModal: () => void; + openIngredientModal: (e: MouseEvent) => void; } + const IngredientCard: FC = ({ data, count, openIngredientModal }) => { const location = useLocation(); diff --git a/src/components/IngredientCategory/IngredientCategory.tsx b/src/components/IngredientCategory/IngredientCategory.tsx index 3914486..66619c4 100644 --- a/src/components/IngredientCategory/IngredientCategory.tsx +++ b/src/components/IngredientCategory/IngredientCategory.tsx @@ -1,6 +1,6 @@ import IngredientCategoryStyles from './IngredientCategory.module.css'; -import { useMemo, useEffect, FC, Ref } from 'react'; +import { useMemo, useEffect, FC, Ref, MouseEvent} from 'react'; import IngredientCard from '../IngredientCard/IngredientCard'; import { useDispatch, useSelector } from '../../services/hooks'; @@ -11,7 +11,7 @@ type TIngredientCategory = { id: string; title: string; data: Array; - openIngredientModal: () => void; + openIngredientModal: (e: MouseEvent) => void; link: Ref; inView: boolean; } diff --git a/src/components/IngredientDetails/IngredientDetails.tsx b/src/components/IngredientDetails/IngredientDetails.tsx index 95737e2..6860425 100644 --- a/src/components/IngredientDetails/IngredientDetails.tsx +++ b/src/components/IngredientDetails/IngredientDetails.tsx @@ -4,9 +4,9 @@ import { useDispatch, useSelector } from '../../services/hooks'; import { useParams } from 'react-router-dom'; import { SHOW_INGREDIENT } from '../../utils/constants'; -import { useEffect } from 'react'; +import { useEffect, FC } from 'react'; -function IngredientDetails() { +const IngredientDetails: FC = () => { const dispatch = useDispatch(); const { ingredientId } = useParams(); diff --git a/src/components/Main/Main.js b/src/components/Main/Main.tsx similarity index 89% rename from src/components/Main/Main.js rename to src/components/Main/Main.tsx index 324c5c0..da2120d 100644 --- a/src/components/Main/Main.js +++ b/src/components/Main/Main.tsx @@ -1,5 +1,5 @@ -import React, { useState } from 'react'; -import { useSelector, useDispatch } from 'react-redux'; +import { useState, FC, MouseEvent } from 'react'; +import { useSelector, useDispatch } from '../../services/hooks'; import { DndProvider } from "react-dnd"; import { HTML5Backend } from "react-dnd-html5-backend"; @@ -11,7 +11,7 @@ import OrderAccepted from '../OrderAccepted/OrderAccepted'; import {SHOW_INGREDIENT, HIDE_INGREDIENT} from '../../utils/constants'; -function Main() { +const Main: FC = () => { const dispatch = useDispatch(); const [isOrderDetailsOpened, setIsOrderDetailsOpened] = useState(false); @@ -27,7 +27,7 @@ function Main() { setIsOrderDetailsOpened(true); } - const openIngredientModal = (e) => { + const openIngredientModal = (e: MouseEvent) => { dispatch({type: SHOW_INGREDIENT, id: e.currentTarget.id}); } diff --git a/src/components/ProtectedRoute/ProtectedRoute.js b/src/components/ProtectedRoute/ProtectedRoute.tsx similarity index 75% rename from src/components/ProtectedRoute/ProtectedRoute.js rename to src/components/ProtectedRoute/ProtectedRoute.tsx index 3486792..d375fdd 100644 --- a/src/components/ProtectedRoute/ProtectedRoute.js +++ b/src/components/ProtectedRoute/ProtectedRoute.tsx @@ -1,10 +1,16 @@ +import { FC, ReactElement } from 'react'; import { Navigate, useLocation } from 'react-router-dom'; -import { useSelector } from 'react-redux'; +import { useSelector } from '../../services/hooks'; -import PropTypes from 'prop-types'; import { getCookie } from '../../utils/cookie'; -function ProtectedRoute({element, isAuthPage, accessFrom}) { +type TProtectedRoute = { + element: ReactElement; + isAuthPage?: boolean; + accessFrom?: string; +} + +const ProtectedRoute: FC = ({element, isAuthPage, accessFrom}) => { const accessToken = getCookie('accessToken'); const refreshToken = getCookie('token'); const { isAuthSuccess, isUserLoaded } = useSelector(state => state.user); @@ -31,10 +37,4 @@ function ProtectedRoute({element, isAuthPage, accessFrom}) { return element; } -ProtectedRoute.propTypes = { - element: PropTypes.node.isRequired, - isAuthPage: PropTypes.bool, - accessFrom: PropTypes.string -} - export default ProtectedRoute; \ No newline at end of file diff --git a/src/services/actions/ws.tsx b/src/services/actions/ws.tsx index 49245ce..710b31b 100644 --- a/src/services/actions/ws.tsx +++ b/src/services/actions/ws.tsx @@ -1,4 +1,4 @@ -import { AppDispatch } from '../types/index'; +import { AppDispatch, AppThunk } from '../types/index'; import { createAction } from '@reduxjs/toolkit'; import { refreshTokenRequest } from '../../utils/api'; import { setCookie, getCookie, getAccessToken } from '../../utils/cookie'; @@ -26,7 +26,7 @@ export const wsOpen = createAction(WS_OPEN); export const wsClose = createAction(WS_CLOSE); export const wsError = createAction(WS_ERROR); -export const wsMessage = (action: TWSMessage) => (dispatch: AppDispatch) => { +export const wsMessage: AppThunk = (action: TWSMessage) => (dispatch: AppDispatch) => { dispatch(wsMessageAction(action)); if (!action.success) { diff --git a/src/services/reducers/ws.tsx b/src/services/reducers/ws.tsx index 26b9982..e575c46 100644 --- a/src/services/reducers/ws.tsx +++ b/src/services/reducers/ws.tsx @@ -1,7 +1,7 @@ import { WS_STATUS_OFFLINE, WS_STATUS_CONNECTING, WS_STATUS_ONLINE, WS_MESSAGE } from '../../utils/constants'; import { wsClose, wsConnecting, wsError, wsOpen } from '../actions/ws'; import { createReducer } from '@reduxjs/toolkit'; -import { TWSMessage, TWSAction, IWSErrorAction } from '../types/ws'; +import { TWSMessage, IWSMessageAction, IWSErrorAction } from '../types/ws'; import { TFeedData } from '../types/order'; type TWSStateOffline = { @@ -10,7 +10,7 @@ type TWSStateOffline = { }; type TWSStateConnecting = { status: typeof WS_STATUS_CONNECTING; - connectionError: string; + connectionError: string | TWSMessage; }; type TWSStateOnline = { status: typeof WS_STATUS_ONLINE; @@ -39,10 +39,10 @@ export const wsReducer = createReducer(initialWSState as TWSState, (builder: any state.status = WS_STATUS_OFFLINE; state.connectionError = '' }) - .addCase(wsError, (state: TWSState, action: TWSAction) => { + .addCase(wsError, (state: TWSState, action: IWSErrorAction) => { state.connectionError = action.payload.success ? '' : action.payload.message; }) - .addCase(WS_MESSAGE, (state: TWSStateOnline, action: TWSAction) => { + .addCase(WS_MESSAGE, (state: TWSStateOnline, action: IWSMessageAction) => { state.feed = action.payload; }) }) diff --git a/src/services/types/ws.tsx b/src/services/types/ws.tsx index 980e1c3..9c98ca4 100644 --- a/src/services/types/ws.tsx +++ b/src/services/types/ws.tsx @@ -18,7 +18,7 @@ export type TWSMessage = { orders: Array; } | TWSErrorMessage; -interface IWSMessageAction { +export interface IWSMessageAction { readonly type: typeof WS_MESSAGE; payload: TWSMessage; } @@ -43,9 +43,9 @@ interface IWSConnectingAction { payload: TWSMessage; } -interface IWSConnectAction { +export interface IWSConnectAction { readonly type: typeof WS_CONNECT; - payload: TWSMessage; + payload: string; } interface IWSDisconnectAction { diff --git a/src/utils/constants.tsx b/src/utils/constants.tsx index 14c7eed..f0fb827 100644 --- a/src/utils/constants.tsx +++ b/src/utils/constants.tsx @@ -24,8 +24,6 @@ export const WS_OPEN: 'WS_OPEN' = 'WS_OPEN'; export const WS_CLOSE: 'WS_CLOSE' = 'WS_CLOSE'; export const WS_ERROR: 'WS_ERROR' = 'WS_ERROR'; -// export const CATEGORIES_TITLES: 'bun' | 'main' | 'sauce' = ; - // Константы для обработки запроса для получения всех ингридиентов export const GET_INGREDIENTS_REQUEST: 'GET_INGREDIENTS_REQUEST' = 'GET_INGREDIENTS_REQUEST'; export const GET_INGREDIENTS_SUCCESS: 'GET_INGREDIENTS_SUCCESS' = 'GET_INGREDIENTS_SUCCESS'; From 0e6d389b8ada305e41e5736fe72d022a9e8894e1 Mon Sep 17 00:00:00 2001 From: Yulia Berezhnaya Date: Mon, 3 Apr 2023 16:39:12 +0300 Subject: [PATCH 13/13] =?UTF-8?q?=D0=9F=D1=80=D0=B0=D0=B2=D0=BA=D0=B8=20?= =?UTF-8?q?=D0=BF=D0=BE=20=D1=80=D0=B5=D0=B2=D1=8C=D1=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/App/App.tsx | 7 +--- src/components/AppHeader/AppHeader.tsx | 8 ++-- .../BurgerConstructor/BurgerConstructor.tsx | 5 ++- src/components/Ingredient/Ingredient.tsx | 4 +- .../OrderAccepted/OrderAccepted.tsx | 2 +- src/components/OrderDetails/OrderDetails.tsx | 5 +-- src/components/Orders/Orders.tsx | 12 +++--- src/components/TotalPrice/TotalPrice.tsx | 3 +- src/services/actions/ingredients.tsx | 3 +- src/services/actions/user.tsx | 2 - src/services/reducers/ws.tsx | 38 ++++++++++--------- src/services/types/ingredients.tsx | 2 - src/services/types/order.tsx | 3 -- src/services/types/ws.tsx | 18 +++++---- src/utils/constants.tsx | 6 +-- 15 files changed, 57 insertions(+), 61 deletions(-) diff --git a/src/components/App/App.tsx b/src/components/App/App.tsx index ae62dba..70024f2 100644 --- a/src/components/App/App.tsx +++ b/src/components/App/App.tsx @@ -4,8 +4,6 @@ import { useEffect, FC } from 'react'; import { Routes, Route, useLocation, useNavigate } from 'react-router-dom'; import { useSelector, useDispatch } from '../../services/hooks'; -import { WS_STATUS_ONLINE } from '../../utils/constants'; - import Main from '../Main/Main'; import AppHeader from '../AppHeader/AppHeader'; import ProtectedRoute from '../ProtectedRoute/ProtectedRoute'; @@ -25,6 +23,7 @@ import IngredientDetails from '../IngredientDetails/IngredientDetails'; import OrderDetails from '../OrderDetails/OrderDetails'; import { getIngredients } from '../../services/actions/ingredients'; import { getUser } from '../../services/actions/user'; +import { TFeedData } from '../../services/types/order'; const App: FC = () => { const dispatch = useDispatch(); @@ -34,9 +33,7 @@ const App: FC = () => { const { ingredientsByType } = useSelector(state => state.menu); - const { orders } = useSelector(({ws}) => ( - ws.status === WS_STATUS_ONLINE && ws.feed?.success ? ws.feed : { orders: []} - )); + const { orders } = useSelector(({ws}) => (ws.feed as TFeedData)); useEffect(() => { dispatch(getIngredients()); diff --git a/src/components/AppHeader/AppHeader.tsx b/src/components/AppHeader/AppHeader.tsx index aab2680..9e2a7ed 100644 --- a/src/components/AppHeader/AppHeader.tsx +++ b/src/components/AppHeader/AppHeader.tsx @@ -4,10 +4,10 @@ import { NavLink } from 'react-router-dom'; import AppHeaderStyles from './AppHeader.module.css'; const AppHeader: FC = () => { - const active: string = AppHeaderStyles.link + ' ' + AppHeaderStyles.active + ' pl-5 pt-4 pr-5 pb-4 text text_type_main-default'; - const link: string = AppHeaderStyles.link + ' pl-5 pt-4 pr-5 pb-4 text text_type_main-default text_color_inactive'; - const activeAccountLink: string = AppHeaderStyles.link + ' ' + AppHeaderStyles.active + ' ' + AppHeaderStyles.account + ' pl-5 pt-4 pr-5 pb-4 text text_type_main-default'; - const accountLink: string = AppHeaderStyles.link + ' ' + AppHeaderStyles.account + ' pl-5 pt-4 pr-5 pb-4 text text_type_main-default text_color_inactive'; + const active = AppHeaderStyles.link + ' ' + AppHeaderStyles.active + ' pl-5 pt-4 pr-5 pb-4 text text_type_main-default'; + const link = AppHeaderStyles.link + ' pl-5 pt-4 pr-5 pb-4 text text_type_main-default text_color_inactive'; + const activeAccountLink = AppHeaderStyles.link + ' ' + AppHeaderStyles.active + ' ' + AppHeaderStyles.account + ' pl-5 pt-4 pr-5 pb-4 text text_type_main-default'; + const accountLink = AppHeaderStyles.link + ' ' + AppHeaderStyles.account + ' pl-5 pt-4 pr-5 pb-4 text text_type_main-default text_color_inactive'; return (
        diff --git a/src/components/BurgerConstructor/BurgerConstructor.tsx b/src/components/BurgerConstructor/BurgerConstructor.tsx index 96e5d7b..f0df5cc 100644 --- a/src/components/BurgerConstructor/BurgerConstructor.tsx +++ b/src/components/BurgerConstructor/BurgerConstructor.tsx @@ -14,8 +14,11 @@ import { ADD_BUN, ADD_INGREDIENT } from '../../utils/constants'; import { useNavigate } from 'react-router-dom'; import { TIngredientList } from '../../services/types/ingredients'; +type TBurgerConstructor = { + openOrderDetailsModal: () => void; +}; -const BurgerConstructor: FC<{openOrderDetailsModal: () => void}> = ({ openOrderDetailsModal }) => { +const BurgerConstructor: FC = ({ openOrderDetailsModal }) => { const dispatch = useDispatch(); const navigate = useNavigate(); diff --git a/src/components/Ingredient/Ingredient.tsx b/src/components/Ingredient/Ingredient.tsx index b9929ea..d8d00fa 100644 --- a/src/components/Ingredient/Ingredient.tsx +++ b/src/components/Ingredient/Ingredient.tsx @@ -1,6 +1,6 @@ import IngredientStyles from './Ingredient.module.css'; -import { useRef, FC, BaseSyntheticEvent, MouseEvent } from 'react'; +import { useRef, FC, MouseEvent } from 'react'; import { useDispatch, useSelector } from '../../services/hooks'; import { useDrag, useDrop, DropTargetMonitor } from 'react-dnd'; @@ -18,9 +18,7 @@ const Ingredient: FC = ({id, index}) => { dispatch({type: CHANGE_INGREDIENT_ORDER, prevIndex, newIndex}); } - // TODO const handleDeleteIngredient = (e: MouseEvent) => { - console.log('e', e.target); const targetElement = (e.target as HTMLLIElement).parentNode?.parentNode as HTMLElement; if (targetElement.classList.contains('constructor-element__action')) { diff --git a/src/components/OrderAccepted/OrderAccepted.tsx b/src/components/OrderAccepted/OrderAccepted.tsx index 7f0855e..c7182f9 100644 --- a/src/components/OrderAccepted/OrderAccepted.tsx +++ b/src/components/OrderAccepted/OrderAccepted.tsx @@ -25,4 +25,4 @@ const OrderAccepted: FC = () => { ) }; -export default OrderAccepted; \ No newline at end of file +export default OrderAccepted; diff --git a/src/components/OrderDetails/OrderDetails.tsx b/src/components/OrderDetails/OrderDetails.tsx index 91bdfa0..36a1742 100644 --- a/src/components/OrderDetails/OrderDetails.tsx +++ b/src/components/OrderDetails/OrderDetails.tsx @@ -22,9 +22,8 @@ type TIngredientCounts = { const OrderDetails: FC = () => { const dispatch = useDispatch(); const location = useLocation(); - const { orders } = useSelector(({ ws }) => ( - ws.status === WS_STATUS_ONLINE && ws.feed.success ? ws.feed as TFeedData : {orders: [], total: 0, totalToday: 0} - )); + const { orders } = useSelector(({ ws }) => (ws.feed as TFeedData)); + const { ingredientsById } = useSelector(state => state.menu); const { orderId } = useParams(); const token = getCookie('accessToken'); diff --git a/src/components/Orders/Orders.tsx b/src/components/Orders/Orders.tsx index a2f4c9a..6a4883f 100644 --- a/src/components/Orders/Orders.tsx +++ b/src/components/Orders/Orders.tsx @@ -15,9 +15,11 @@ type TOrdersByStatus = { const Orders: FC = () => { const dispatch = useDispatch(); - const { total, totalToday, orders } = useSelector((state) => ( - state.ws.status === WS_STATUS_ONLINE && state.ws.feed?.success ? state.ws.feed as TFeedData : {orders: [], total: 0, totalToday: 0} - )); + const feed = useSelector((state) => (state.ws.feed as TFeedData)); + + const total = feed ? feed.total : 0; + const totalToday = feed ? feed.totalToday : 0; + const orders = feed ? feed.orders : []; const ordersByStatus: TOrdersByStatus | undefined = orders?.length ? (orders as Array).reduce((res: TOrdersByStatus, item) => { if (res[item.status].length < 20) { @@ -41,8 +43,8 @@ const Orders: FC = () => {

        Лента заказов

        {orders && - orders.map((item, index) => { - return + orders.map((order: TOrder) => { + return }) }
        diff --git a/src/components/TotalPrice/TotalPrice.tsx b/src/components/TotalPrice/TotalPrice.tsx index a44bae9..965f702 100644 --- a/src/components/TotalPrice/TotalPrice.tsx +++ b/src/components/TotalPrice/TotalPrice.tsx @@ -5,12 +5,11 @@ import { useSelector } from '../../services/hooks'; import { TRootState } from '../../services/types/index'; import { CurrencyIcon } from '@ya.praktikum/react-developer-burger-ui-components'; -import { TMenuState } from '../../services/reducers'; import { TIngredient } from '../../services/types/ingredients'; const TotalPrice: FC = () => { - const { addedIngredients: { bun, others } }: TMenuState = useSelector((state: TRootState) => state.menu); + const { addedIngredients: { bun, others } } = useSelector((state: TRootState) => state.menu); const total = useMemo(() => ( (others as TIngredient[]).reduce( diff --git a/src/services/actions/ingredients.tsx b/src/services/actions/ingredients.tsx index 7d5f7e8..5f97169 100644 --- a/src/services/actions/ingredients.tsx +++ b/src/services/actions/ingredients.tsx @@ -28,8 +28,7 @@ export const getIngredients: AppThunk = () => (dispatch: AppDispatch) => { dispatch(getIngredientsSuccessAction(ingredientsByType, ingredientsById)) } else { - // Пробрасывать ошибку и обрабатывать все только в catch - dispatch(getIngredientsFailedAction()); + throw new Error('Ошибка: ' + res.message); } }) .catch(() => { diff --git a/src/services/actions/user.tsx b/src/services/actions/user.tsx index 517d114..37c0431 100644 --- a/src/services/actions/user.tsx +++ b/src/services/actions/user.tsx @@ -158,13 +158,11 @@ export const logoutUser: AppThunk = () => (dispatch: AppDispatch) => { /** * @param {function} getRequest - запрос для ретрая - * @param {object} options - параметры запроса * @param {string} options.accessToken - токен авторизации * @param {string} onSuccess - функция для обработки успешного результата * @param {string} onFail - функция для обработки ошибок */ -// TODO что делать с options и getRequest??? function getRequestWithRetry(getRequest: ((accessToken: string) => Promise), onSuccess: requestRetryOnSuccess, onFail: requestRetryOnFail) { getRequest(getCookie('accessToken')) diff --git a/src/services/reducers/ws.tsx b/src/services/reducers/ws.tsx index e575c46..34c9276 100644 --- a/src/services/reducers/ws.tsx +++ b/src/services/reducers/ws.tsx @@ -4,28 +4,19 @@ import { createReducer } from '@reduxjs/toolkit'; import { TWSMessage, IWSMessageAction, IWSErrorAction } from '../types/ws'; import { TFeedData } from '../types/order'; -type TWSStateOffline = { - status: typeof WS_STATUS_OFFLINE; - connectionError: string; -}; -type TWSStateConnecting = { - status: typeof WS_STATUS_CONNECTING; - connectionError: string | TWSMessage; -}; -type TWSStateOnline = { - status: typeof WS_STATUS_ONLINE; - feed: TFeedData | TWSMessage; +export type TWSState = { + status: typeof WS_STATUS_ONLINE | typeof WS_STATUS_CONNECTING | typeof WS_STATUS_OFFLINE; + feed?: TFeedData | TWSMessage | undefined; connectionError: string; }; -export type TWSState = TWSStateOffline | TWSStateConnecting | TWSStateOnline; - const initialWSState: TWSState = { status: WS_STATUS_OFFLINE, - connectionError: '' + connectionError: '', + feed: undefined } -export const wsReducer = createReducer(initialWSState as TWSState, (builder: any) => { +export const wsReducer = createReducer(initialWSState, (builder) => { builder .addCase(wsConnecting, (state: TWSState) => { @@ -40,9 +31,20 @@ export const wsReducer = createReducer(initialWSState as TWSState, (builder: any state.connectionError = '' }) .addCase(wsError, (state: TWSState, action: IWSErrorAction) => { - state.connectionError = action.payload.success ? '' : action.payload.message; + let error = ''; + if (typeof action.payload !== 'string' && typeof action.payload !== 'undefined') { + if (!action.payload.success) { + error = action.payload.message; + } + } + + state.connectionError = error; }) - .addCase(WS_MESSAGE, (state: TWSStateOnline, action: IWSMessageAction) => { - state.feed = action.payload; + .addCase(WS_MESSAGE, (state: TWSState, action: IWSMessageAction) => { + let feed = undefined; + if (typeof action.payload !== 'string') { + feed = action.payload; + } + state.feed = feed; }) }) diff --git a/src/services/types/ingredients.tsx b/src/services/types/ingredients.tsx index 34590bf..dd217c2 100644 --- a/src/services/types/ingredients.tsx +++ b/src/services/types/ingredients.tsx @@ -1,5 +1,3 @@ -import { AppThunk } from './index'; - import { GET_INGREDIENTS_REQUEST, GET_INGREDIENTS_FAILED, diff --git a/src/services/types/order.tsx b/src/services/types/order.tsx index bde6c29..56cdc6e 100644 --- a/src/services/types/order.tsx +++ b/src/services/types/order.tsx @@ -1,5 +1,3 @@ -import { TIngredient } from "./ingredients"; - export type TOrder = { _id: string; status: 'created' | 'done' | 'progress' | 'cancelled'; @@ -8,7 +6,6 @@ export type TOrder = { updatedAt: Date | string; number: number; ingredients: Array - // ingredients: Array } export type TFeedData = { diff --git a/src/services/types/ws.tsx b/src/services/types/ws.tsx index 9c98ca4..3b41ccd 100644 --- a/src/services/types/ws.tsx +++ b/src/services/types/ws.tsx @@ -13,44 +13,48 @@ export type TWSErrorMessage = { success: false; message: string; } + export type TWSMessage = { success: true; orders: Array; } | TWSErrorMessage; + +type TPayload = TWSMessage | TWSErrorMessage | string| undefined; + export interface IWSMessageAction { readonly type: typeof WS_MESSAGE; - payload: TWSMessage; + payload: TPayload; } export interface IWSErrorAction { readonly type: typeof WS_ERROR; - payload: TWSErrorMessage; + payload: TPayload; } interface IWSOpenAction { readonly type: typeof WS_OPEN; - payload: TWSMessage; + payload: TPayload; } interface IWSCloseAction { readonly type: typeof WS_CLOSE; - payload: TWSMessage; + payload: TPayload; } interface IWSConnectingAction { readonly type: typeof WS_CONNECTING; - payload: TWSMessage; + payload: TPayload; } export interface IWSConnectAction { readonly type: typeof WS_CONNECT; - payload: string; + payload: TPayload; } interface IWSDisconnectAction { readonly type: typeof WS_DISCONNECT; - payload: TWSMessage; + payload: TPayload; } export const wsMessageAction = (payload: TWSMessage): IWSMessageAction => ({ diff --git a/src/utils/constants.tsx b/src/utils/constants.tsx index f0fb827..cab95c3 100644 --- a/src/utils/constants.tsx +++ b/src/utils/constants.tsx @@ -12,9 +12,9 @@ export const PASSWORDRESETURL = BASEURL + '/password-reset/reset'; export const WSURL = 'wss://norma.nomoreparties.space'; export const GET_ALL_ORDERS_URL = WSURL + '/orders/all'; export const GET_USER_ORDERS_URL = WSURL + '/orders'; -export const WS_STATUS_OFFLINE: 'OFFLINE' = 'OFFLINE'; -export const WS_STATUS_ONLINE: 'ONLINE' = 'ONLINE'; -export const WS_STATUS_CONNECTING: 'CONNECTING...' = 'CONNECTING...'; +export const WS_STATUS_OFFLINE = 'OFFLINE'; +export const WS_STATUS_ONLINE = 'ONLINE'; +export const WS_STATUS_CONNECTING = 'CONNECTING...'; export const WS_MESSAGE: 'WS_MESSAGE' = 'WS_MESSAGE'; export const WS_CONNECT: 'WS_CONNECT' = 'WS_CONNECT';