From 6096cfc15c384b8800ef78cae1b8a15756638763 Mon Sep 17 00:00:00 2001 From: ilnaz <237x237@gmail.com> Date: Sun, 22 Dec 2024 12:00:12 +0300 Subject: [PATCH 1/4] feat: add fetch for multi-stub (#59) --- locales/ru.json | 3 +- src/api/arm.ts | 36 ++++++++++- .../MasterActionsMenu/MasterActionsMenu.tsx | 37 ++++++++++- src/components/MasterDrawer/MasterDrawer.tsx | 62 +++++++++++++++---- src/components/MasterItem/MasterItem.tsx | 21 +++---- src/components/OrderItem/OrderItem.tsx | 20 ++++++ src/components/Orders/Orders.tsx | 21 +++++-- stubs/api/index.js | 34 +++++++++- stubs/json/arm-masters/success.json | 20 ------ stubs/json/arm-orders/success.json | 10 ++- 10 files changed, 205 insertions(+), 59 deletions(-) diff --git a/locales/ru.json b/locales/ru.json index d3e17d6..a36cd02 100644 --- a/locales/ru.json +++ b/locales/ru.json @@ -11,6 +11,7 @@ "dry-wash.arm.order.table.header.washingTime": "Время мойки", "dry-wash.arm.order.table.header.orderDate": "Дата заказа", "dry-wash.arm.order.table.header.status": "Статус", + "dry-wash.arm.order.table.header.masters": "Мастер", "dry-wash.arm.order.table.header.telephone": "Телефон", "dry-wash.arm.order.table.header.location": "Расположение", "dry-wash.arm.order.table.empty": "Список пуст", @@ -89,4 +90,4 @@ "dry-wash.errorBoundary.description": "Мы уже работаем над исправлением проблемы", "dry-wash.errorBoundary.button.reload": "Перезагрузить страницу", "dry-wash.washTime.timeSlot": "{{start}} - {{end}}" -} \ No newline at end of file +} diff --git a/src/api/arm.ts b/src/api/arm.ts index c406c54..e4003cb 100644 --- a/src/api/arm.ts +++ b/src/api/arm.ts @@ -34,7 +34,41 @@ const armService = () => { return await response.json(); }; - return { fetchOrders, fetchMasters }; + const addMaster = async ({ + name, + phone, + }: { + name: string; + phone: string; + }) => { + const response = await fetch(`${endpoint}${ArmEndpoints.MASTERS}`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ name, phone }), + }); + + if (!response.ok) { + throw new Error(`Failed to fetch masters: ${response.status}`); + } + + return await response.json(); + }; + + const deleteMaster = async ({ id }: { id: string }) => { + const response = await fetch(`${endpoint}${ArmEndpoints.MASTERS}/${id}`, { + method: 'DELETE', + }); + + if (!response.ok) { + throw new Error(`Failed to fetch masters: ${response.status}`); + } + + return await response.json(); + }; + + return { fetchOrders, fetchMasters, addMaster, deleteMaster }; }; export { armService, ArmEndpoints }; diff --git a/src/components/MasterActionsMenu/MasterActionsMenu.tsx b/src/components/MasterActionsMenu/MasterActionsMenu.tsx index a11ab5b..27d9ea2 100644 --- a/src/components/MasterActionsMenu/MasterActionsMenu.tsx +++ b/src/components/MasterActionsMenu/MasterActionsMenu.tsx @@ -5,20 +5,53 @@ import { MenuList, MenuItem, IconButton, + useToast, } from '@chakra-ui/react'; import { EditIcon } from '@chakra-ui/icons'; import { useTranslation } from 'react-i18next'; +import { armService } from '../../api/arm'; -const MasterActionsMenu = () => { +interface MasterActionsMenu { + id: string; +} + +const MasterActionsMenu = ({ id }: MasterActionsMenu) => { const { t } = useTranslation('~', { keyPrefix: 'dry-wash.arm.master.table.actionsMenu', }); + const { deleteMaster } = armService(); + const toast = useToast(); + + const handleClickDelete = async () => { + try { + await deleteMaster({ id }); + toast({ + title: 'Мастер удалён.', + description: `Мастер с ID "${id}" успешно удалён.`, + status: 'success', + duration: 5000, + isClosable: true, + position: 'top-right', + }); + } catch (error) { + toast({ + title: 'Ошибка удаления мастера.', + description: 'Не удалось удалить мастера. Попробуйте ещё раз.', + status: 'error', + duration: 5000, + isClosable: true, + position: 'top-right', + }); + console.error(error); + } + }; + return ( } as={IconButton} variant='outline' /> - {t('delete')} + {t('delete')} ); diff --git a/src/components/MasterDrawer/MasterDrawer.tsx b/src/components/MasterDrawer/MasterDrawer.tsx index 03cc9d1..fa2b199 100644 --- a/src/components/MasterDrawer/MasterDrawer.tsx +++ b/src/components/MasterDrawer/MasterDrawer.tsx @@ -11,15 +11,47 @@ import { DrawerFooter, DrawerHeader, DrawerOverlay, + useToast, + InputGroup, + InputLeftElement, } from '@chakra-ui/react'; import { useTranslation } from 'react-i18next'; +import { armService } from '../../api/arm'; +import { PhoneIcon } from '@chakra-ui/icons'; const MasterDrawer = ({ isOpen, onClose }) => { + const { addMaster } = armService(); + const toast = useToast(); + const [newMaster, setNewMaster] = useState({ name: '', phone: '' }); - const handleSave = () => { - console.log(`Сохранение мастера: ${newMaster}`); - onClose(); + const handleSave = async () => { + if (newMaster.name.trim() === '' || newMaster.phone.trim() === '') { + return; + } + + try { + await addMaster(newMaster); + toast({ + title: 'Мастер создан.', + description: `Мастер "${newMaster.name}" успешно добавлен.`, + status: 'success', + duration: 5000, + isClosable: true, + position: 'top-right', + }); + onClose(); + } catch (error) { + toast({ + title: 'Ошибка при создании мастера.', + description: 'Не удалось добавить мастера. Попробуйте еще раз.', + status: 'error', + duration: 5000, + isClosable: true, + position: 'top-right', + }); + console.error(error); + } }; const { t } = useTranslation('~', { @@ -36,6 +68,7 @@ const MasterDrawer = ({ isOpen, onClose }) => { {t('inputName.label')} setNewMaster({ ...newMaster, name: e.target.value }) @@ -44,14 +77,21 @@ const MasterDrawer = ({ isOpen, onClose }) => { /> - {t('inputPhone.label')} - - setNewMaster({ ...newMaster, phone: e.target.value }) - } - placeholder={t('inputPhone.placeholder')} - /> + {t('inputPhone.label')} + + + + + + + setNewMaster({ ...newMaster, phone: e.target.value }) + } + placeholder={t('inputPhone.placeholder')} + /> + diff --git a/src/components/MasterItem/MasterItem.tsx b/src/components/MasterItem/MasterItem.tsx index 825d621..15f590d 100644 --- a/src/components/MasterItem/MasterItem.tsx +++ b/src/components/MasterItem/MasterItem.tsx @@ -4,37 +4,30 @@ import { Badge, Link, Stack, Td, Tr } from '@chakra-ui/react'; import MasterActionsMenu from '../MasterActionsMenu'; import { getTimeSlot } from '../../lib/date-helpers'; -export interface Schedule { - id: string; - startWashTime: string; - endWashTime: string; -} - export type MasterProps = { id: string; name: string; - schedule: Schedule[]; phone: string; }; -const MasterItem = ({ name, schedule, phone }) => { +const MasterItem = ({ name, phone, id }) => { return ( {name} - {schedule.map(({ startWashTime, endWashTime }, index) => ( - - {getTimeSlot(startWashTime, endWashTime)} - - ))} + {/*{schedule.map(({ startWashTime, endWashTime }, index) => (*/} + {/* */} + {/* {getTimeSlot(startWashTime, endWashTime)}*/} + {/* */} + {/*))}*/} {phone} - + ); diff --git a/src/components/OrderItem/OrderItem.tsx b/src/components/OrderItem/OrderItem.tsx index 92e7041..5621d15 100644 --- a/src/components/OrderItem/OrderItem.tsx +++ b/src/components/OrderItem/OrderItem.tsx @@ -3,6 +3,7 @@ import { Td, Tr, Link, Select } from '@chakra-ui/react'; import { useTranslation } from 'react-i18next'; import dayjs from 'dayjs'; +import { MasterProps } from '../MasterItem/MasterItem'; import { getTimeSlot } from '../../lib'; const statuses = [ @@ -24,6 +25,9 @@ export type OrderProps = { status?: GetArrItemType; phone?: string; location?: string; + master: MasterProps; + notes: ''; + allMasters: MasterProps[]; }; type Status = (typeof statuses)[number]; @@ -44,6 +48,8 @@ const OrderItem = ({ status, phone, location, + master, + allMasters, }: OrderProps) => { const { t } = useTranslation('~', { keyPrefix: 'dry-wash.arm.order', @@ -51,6 +57,7 @@ const OrderItem = ({ const [statusSelect, setStatus] = useState(status); const bgColor = statusColors[statusSelect]; + const [masterSelect, setMaster] = useState(master?.name); return ( @@ -71,6 +78,19 @@ const OrderItem = ({ ))} + + + {phone} diff --git a/src/components/Orders/Orders.tsx b/src/components/Orders/Orders.tsx index de4070c..9fa95f9 100644 --- a/src/components/Orders/Orders.tsx +++ b/src/components/Orders/Orders.tsx @@ -19,12 +19,14 @@ import OrderItem from '../OrderItem'; import { OrderProps } from '../OrderItem/OrderItem'; import { armService } from '../../api/arm'; import DateNavigator from '../DateNavigator'; +import { MasterProps } from '../MasterItem/MasterItem'; const TABLE_HEADERS = [ 'carNumber' as const, 'washingTime' as const, 'orderDate' as const, 'status' as const, + 'masters' as const, 'telephone' as const, 'location' as const, ]; @@ -35,21 +37,29 @@ const Orders = () => { }); const { fetchOrders } = armService(); + const { fetchMasters } = armService(); const toast = useToast(); const [orders, setOrders] = useState([]); + const [allMasters, setAllMasters] = useState([]); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [currentDate, setCurrentDate] = useState(new Date()); useEffect(() => { - const loadOrders = async () => { + const loadData = async () => { setLoading(true); + setError(null); try { - const data = await fetchOrders({ date: currentDate }); - setOrders(data.body); + const [ordersData, mastersData] = await Promise.all([ + fetchOrders({ date: currentDate }), + fetchMasters(), + ]); + + setOrders(ordersData.body); + setAllMasters(mastersData.body); } catch (err) { setError(err.message); toast({ @@ -64,8 +74,8 @@ const Orders = () => { } }; - loadOrders(); - }, [toast, t, currentDate]); + loadData(); + }, [currentDate, toast, t]); return ( @@ -112,6 +122,7 @@ const Orders = () => { !error && orders.map((order, index) => ( - (req, res, next) => - setTimeout(next, duration); + (req, res, next) => + setTimeout(next, duration); router.use(sleep()); @@ -23,6 +23,36 @@ router.get('/arm/masters', (req, res) => { ); }); +router.post('/arm/masters', (req, res) => { + res + .status(/error/.test(STUBS.masters) ? 500 : 200) + .send( + /^error$/.test(STUBS.masters) + ? commonError + : require(`../json/arm-masters/${STUBS.masters}.json`), + ); +}); + +router.patch('/arm/masters/:id', (req, res) => { + res + .status(/error/.test(STUBS.masters) ? 500 : 200) + .send( + /^error$/.test(STUBS.masters) + ? commonError + : require(`../json/arm-masters/${STUBS.masters}.json`), + ); +}); + +router.delete('/arm/masters/:id', (req, res) => { + res + .status(/error/.test(STUBS.masters) ? 500 : 200) + .send( + /^error$/.test(STUBS.masters) + ? commonError + : require(`../json/arm-masters/${STUBS.masters}.json`), + ); +}); + router.post('/arm/orders', (req, res) => { res .status(/error/.test(STUBS.orders) ? 500 : 200) diff --git a/stubs/json/arm-masters/success.json b/stubs/json/arm-masters/success.json index ce50415..e392eb0 100644 --- a/stubs/json/arm-masters/success.json +++ b/stubs/json/arm-masters/success.json @@ -4,31 +4,11 @@ { "id": "masters1", "name": "Иван Иванов", - "schedule": [ { - "id": "order1", - "startWashTime": "2024-11-24T10:30:00.000Z", - "endWashTime": "2024-11-24T16:30:00.000Z" - }, - { - "id": "order2", - "startWashTime": "2024-11-24T11:30:00.000Z", - "endWashTime": "2024-11-24T17:30:00.000Z" - }], "phone": "+7 900 123 45 67" }, { "id": "masters12", "name": "Иван Иванов", - "schedule": [ { - "id": "order1", - "startWashTime": "2024-11-24T10:30:00.000Z", - "endWashTime": "2024-11-24T16:30:00.000Z" - }, - { - "id": "order2", - "startWashTime": "2024-11-24T11:30:00.000Z", - "endWashTime": "2024-11-24T17:30:00.000Z" - }], "phone": "+7 900 123 45 67" } ] diff --git a/stubs/json/arm-orders/success.json b/stubs/json/arm-orders/success.json index 15669e8..9cf7c26 100644 --- a/stubs/json/arm-orders/success.json +++ b/stubs/json/arm-orders/success.json @@ -9,7 +9,9 @@ "orderDate": "2024-11-24T08:41:46.366Z", "status": "progress", "phone": "79001234563", - "location": "Казань, ул. Баумана, 1" + "location": "Казань, ул. Баумана, 1", + "master": [], + "notes": "" }, { "id": "order2", @@ -19,7 +21,9 @@ "orderDate": "2024-11-24T07:40:46.366Z", "status": "progress", "phone": "79001234567", - "location": "Казань, ул. Баумана, 43" + "location": "Казань, ул. Баумана, 43", + "master": [], + "notes": "" } ] -} \ No newline at end of file +} From 1dee68b65d0a93ebd96efdbe03c06c46f857a5e6 Mon Sep 17 00:00:00 2001 From: ilnaz <237x237@gmail.com> Date: Sat, 11 Jan 2025 22:43:55 +0300 Subject: [PATCH 2/4] fix: place the spinner in the center --- src/pages/arm/index.tsx | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/pages/arm/index.tsx b/src/pages/arm/index.tsx index ee1ed04..6c1a376 100644 --- a/src/pages/arm/index.tsx +++ b/src/pages/arm/index.tsx @@ -1,12 +1,11 @@ import React, { useEffect, useState } from 'react'; import { useNavigate } from 'react-router-dom'; -import { AbsoluteCenter, Spinner } from '@chakra-ui/react'; +import { AbsoluteCenter, Box, Spinner } from '@chakra-ui/react'; import LayoutArm from '../../components/LayoutArm'; import authLogin from '../../keycloak'; import { URLs } from '../../__data__/urls'; - const Page = () => { const [user, setUser] = useState(null); @@ -20,9 +19,11 @@ const Page = () => { if (!user) return ( - - - + + + + + ); return ; From cad31605d91da1e2218c2b13672b2539be15a7e1 Mon Sep 17 00:00:00 2001 From: ilnaz <237x237@gmail.com> Date: Sun, 12 Jan 2025 00:34:37 +0300 Subject: [PATCH 3/4] fix: eslint fix --- locales/en.json | 1 + .../MasterActionsMenu/MasterActionsMenu.tsx | 1 + src/components/MasterDrawer/MasterDrawer.tsx | 3 ++- src/components/MasterItem/MasterItem.tsx | 21 ++++++++++++------- stubs/api/index.js | 4 ++-- 5 files changed, 20 insertions(+), 10 deletions(-) diff --git a/locales/en.json b/locales/en.json index fbda83c..bcc10dc 100644 --- a/locales/en.json +++ b/locales/en.json @@ -62,6 +62,7 @@ "dry-wash.arm.order.table.header.washingTime": "Washing Time", "dry-wash.arm.order.table.header.orderDate": "Order Date", "dry-wash.arm.order.table.header.status": "Status", + "dry-wash.arm.order.table.header.masters": "Master", "dry-wash.arm.order.table.header.telephone": "Telephone", "dry-wash.arm.order.table.header.location": "Location", "dry-wash.arm.master.title": "Masters", diff --git a/src/components/MasterActionsMenu/MasterActionsMenu.tsx b/src/components/MasterActionsMenu/MasterActionsMenu.tsx index 27d9ea2..384c4aa 100644 --- a/src/components/MasterActionsMenu/MasterActionsMenu.tsx +++ b/src/components/MasterActionsMenu/MasterActionsMenu.tsx @@ -9,6 +9,7 @@ import { } from '@chakra-ui/react'; import { EditIcon } from '@chakra-ui/icons'; import { useTranslation } from 'react-i18next'; + import { armService } from '../../api/arm'; interface MasterActionsMenu { diff --git a/src/components/MasterDrawer/MasterDrawer.tsx b/src/components/MasterDrawer/MasterDrawer.tsx index fa2b199..1f4ac79 100644 --- a/src/components/MasterDrawer/MasterDrawer.tsx +++ b/src/components/MasterDrawer/MasterDrawer.tsx @@ -16,9 +16,10 @@ import { InputLeftElement, } from '@chakra-ui/react'; import { useTranslation } from 'react-i18next'; -import { armService } from '../../api/arm'; import { PhoneIcon } from '@chakra-ui/icons'; +import { armService } from '../../api/arm'; + const MasterDrawer = ({ isOpen, onClose }) => { const { addMaster } = armService(); const toast = useToast(); diff --git a/src/components/MasterItem/MasterItem.tsx b/src/components/MasterItem/MasterItem.tsx index 15f590d..23c683a 100644 --- a/src/components/MasterItem/MasterItem.tsx +++ b/src/components/MasterItem/MasterItem.tsx @@ -2,25 +2,32 @@ import React from 'react'; import { Badge, Link, Stack, Td, Tr } from '@chakra-ui/react'; import MasterActionsMenu from '../MasterActionsMenu'; -import { getTimeSlot } from '../../lib/date-helpers'; +import { getTimeSlot } from '../../lib'; + +export interface Schedule { + id: string; + startWashTime: string; + endWashTime: string; +} export type MasterProps = { id: string; name: string; phone: string; + schedule: Schedule[]; }; -const MasterItem = ({ name, phone, id }) => { +const MasterItem = ({ name, phone, id, schedule }) => { return ( {name} - {/*{schedule.map(({ startWashTime, endWashTime }, index) => (*/} - {/* */} - {/* {getTimeSlot(startWashTime, endWashTime)}*/} - {/* */} - {/*))}*/} + {schedule.map(({ startWashTime, endWashTime }, index) => ( + + {getTimeSlot(startWashTime, endWashTime)} + + ))} diff --git a/stubs/api/index.js b/stubs/api/index.js index ae73c8f..f360596 100644 --- a/stubs/api/index.js +++ b/stubs/api/index.js @@ -8,8 +8,8 @@ const commonError = { success: false, message: 'Что-то пошло не та const sleep = (duration = 1000) => - (req, res, next) => - setTimeout(next, duration); + (req, res, next) => + setTimeout(next, duration); router.use(sleep()); From 9b0bda3cdacd8fd025905a04b8d2e348d00dc05b Mon Sep 17 00:00:00 2001 From: ilnaz <237x237@gmail.com> Date: Sun, 12 Jan 2025 00:34:56 +0300 Subject: [PATCH 4/4] fix: add schedule for masters --- stubs/json/arm-masters/success.json | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/stubs/json/arm-masters/success.json b/stubs/json/arm-masters/success.json index e392eb0..ce50415 100644 --- a/stubs/json/arm-masters/success.json +++ b/stubs/json/arm-masters/success.json @@ -4,11 +4,31 @@ { "id": "masters1", "name": "Иван Иванов", + "schedule": [ { + "id": "order1", + "startWashTime": "2024-11-24T10:30:00.000Z", + "endWashTime": "2024-11-24T16:30:00.000Z" + }, + { + "id": "order2", + "startWashTime": "2024-11-24T11:30:00.000Z", + "endWashTime": "2024-11-24T17:30:00.000Z" + }], "phone": "+7 900 123 45 67" }, { "id": "masters12", "name": "Иван Иванов", + "schedule": [ { + "id": "order1", + "startWashTime": "2024-11-24T10:30:00.000Z", + "endWashTime": "2024-11-24T16:30:00.000Z" + }, + { + "id": "order2", + "startWashTime": "2024-11-24T11:30:00.000Z", + "endWashTime": "2024-11-24T17:30:00.000Z" + }], "phone": "+7 900 123 45 67" } ]