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 +}