From 0b2dae79ac328cbc7ab6379b7eb37d6848b47cfb Mon Sep 17 00:00:00 2001 From: ilnaz <237x237@gmail.com> Date: Sat, 7 Dec 2024 23:40:22 +0300 Subject: [PATCH] feat: add stub fetch admin (#44) --- bro.config.js | 2 +- locales/en.json | 4 + locales/ru.json | 4 + src/api/arm.ts | 34 +++++++++ src/components/MasterItem/MasterItem.tsx | 13 ++++ src/components/Masters/Masters.tsx | 63 ++++++++++++++-- src/components/Orders/Orders.tsx | 96 +++++++++++++++++++----- stubs/api/index.js | 7 +- 8 files changed, 196 insertions(+), 27 deletions(-) create mode 100644 src/api/arm.ts diff --git a/bro.config.js b/bro.config.js index f12638a..5cb04ae 100644 --- a/bro.config.js +++ b/bro.config.js @@ -24,6 +24,6 @@ module.exports = { }, }, config: { - 'dry-wash-pl.api': '/api', + 'dry-wash.api': '/api', }, }; diff --git a/locales/en.json b/locales/en.json index 5820580..35e36a8 100644 --- a/locales/en.json +++ b/locales/en.json @@ -16,6 +16,8 @@ "dry-wash.landing.social-proof-section.heading": "We are being chosen", "dry-wash.arm.master.add": "Add", "dry-wash.arm.order.title": "Orders", + "dry-wash.arm.order.table.empty": "Table empty", + "dry-wash.arm.order.error.title": "Error loading data", "dry-wash.arm.order.status.progress": "In Progress", "dry-wash.arm.order.status.complete": "Completed", "dry-wash.arm.order.status.pending": "Pending", @@ -30,6 +32,8 @@ "dry-wash.arm.order.table.header.location": "Location", "dry-wash.arm.master.title": "Masters", "dry-wash.arm.master.table.header.name": "Name", + "dry-wash.arm.master.table.empty": "Table empty", + "dry-wash.arm.master.error.title": "Error loading data", "dry-wash.arm.master.table.header.currentJob": "Current Job", "dry-wash.arm.master.table.header.phone": "Phone", "dry-wash.arm.master.table.header.actions": "Actions", diff --git a/locales/ru.json b/locales/ru.json index 5c9d91c..fc04889 100644 --- a/locales/ru.json +++ b/locales/ru.json @@ -13,7 +13,11 @@ "dry-wash.arm.order.table.header.status": "Статус", "dry-wash.arm.order.table.header.telephone": "Телефон", "dry-wash.arm.order.table.header.location": "Расположение", + "dry-wash.arm.order.table.empty": "Список пуст", + "dry-wash.arm.order.error.title": "Ошибка при загрузке данных", "dry-wash.arm.master.title": "Мастера", + "dry-wash.arm.master.table.empty": "Список пуст", + "dry-wash.arm.master.error.title": "Ошибка при загрузке данных", "dry-wash.arm.master.table.header.name": "Имя", "dry-wash.arm.master.table.header.currentJob": "Актуальная занятость", "dry-wash.arm.master.table.header.phone": "Телефон", diff --git a/src/api/arm.ts b/src/api/arm.ts new file mode 100644 index 0000000..534440d --- /dev/null +++ b/src/api/arm.ts @@ -0,0 +1,34 @@ +import { getConfigValue } from '@brojs/cli'; + +enum ArmEndpoints { + ORDERS = '/arm/orders', + MASTERS = '/arm/masters', +} + +const armService = () => { + const endpoint = getConfigValue('dry-wash.api'); + + const fetchOrders = async () => { + const response = await fetch(`${endpoint}${ArmEndpoints.ORDERS}`); + + if (!response.ok) { + throw new Error(`Failed to fetch orders: ${response.status}`); + } + + return await response.json(); + }; + + const fetchMasters = async () => { + const response = await fetch(`${endpoint}${ArmEndpoints.MASTERS}`); + + if (!response.ok) { + throw new Error(`Failed to fetch masters: ${response.status}`); + } + + return await response.json(); + }; + + return { fetchOrders, fetchMasters }; +}; + +export { armService, ArmEndpoints }; diff --git a/src/components/MasterItem/MasterItem.tsx b/src/components/MasterItem/MasterItem.tsx index 9895d29..825d621 100644 --- a/src/components/MasterItem/MasterItem.tsx +++ b/src/components/MasterItem/MasterItem.tsx @@ -4,6 +4,19 @@ 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 }) => { return ( diff --git a/src/components/Masters/Masters.tsx b/src/components/Masters/Masters.tsx index ea07603..7846fc8 100644 --- a/src/components/Masters/Masters.tsx +++ b/src/components/Masters/Masters.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useEffect, useState } from 'react'; import { Box, Heading, @@ -10,12 +10,17 @@ import { Button, useDisclosure, Flex, + useToast, + Td, + Text, + Spinner, } from '@chakra-ui/react'; import { useTranslation } from 'react-i18next'; import MasterItem from '../MasterItem'; import MasterDrawer from '../MasterDrawer'; -import data from '../../../stubs/json/arm-masters/success.json'; +import { armService } from '../../api/arm'; +import { MasterProps } from '../MasterItem/MasterItem'; const TABLE_HEADERS = [ 'name' as const, @@ -26,11 +31,41 @@ const TABLE_HEADERS = [ const Masters = () => { const { isOpen, onOpen, onClose } = useDisclosure(); - + const toast = useToast(); const { t } = useTranslation('~', { keyPrefix: 'dry-wash.arm.master', }); + const [masters, setMasters] = useState([]); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(null); + + const { fetchMasters } = armService(); + + useEffect(() => { + const loadMasters = async () => { + setLoading(true); + + try { + const data = await fetchMasters(); + setMasters(data.body); + } catch (err) { + setError(err.message); + toast({ + title: t('error.title'), + status: 'error', + duration: 5000, + isClosable: true, + position: 'bottom-right', + }); + } finally { + setLoading(false); + } + }; + + loadMasters(); + }, [toast, t]); + return ( @@ -48,9 +83,25 @@ const Masters = () => { - {data.body.map((master, index) => ( - - ))} + {loading && ( + + + + + + )} + {!loading && masters.length === 0 && !error && ( + + + {t('table.empty')} + + + )} + {!loading && + !error && + masters.map((master, index) => ( + + ))} diff --git a/src/components/Orders/Orders.tsx b/src/components/Orders/Orders.tsx index 10831e7..e2dd6bc 100644 --- a/src/components/Orders/Orders.tsx +++ b/src/components/Orders/Orders.tsx @@ -1,24 +1,68 @@ -import React from 'react'; -import { Box, Heading, Table, Thead, Tbody, Tr, Th } from '@chakra-ui/react'; +import React, { useEffect, useState } from 'react'; +import { + Box, + Heading, + Table, + Thead, + Tbody, + Tr, + Th, + Spinner, + Text, + Td, + useToast, +} from '@chakra-ui/react'; import { useTranslation } from 'react-i18next'; import OrderItem from '../OrderItem'; import { OrderProps } from '../OrderItem/OrderItem'; -import data from '../../../stubs/json/arm-orders/success.json'; +import { armService } from '../../api/arm'; + +const TABLE_HEADERS = [ + 'carNumber' as const, + 'washingTime' as const, + 'orderDate' as const, + 'status' as const, + 'telephone' as const, + 'location' as const, +]; const Orders = () => { const { t } = useTranslation('~', { keyPrefix: 'dry-wash.arm.order', }); - const TABLE_HEADERS = [ - 'carNumber' as const, - 'washingTime' as const, - 'orderDate' as const, - 'status' as const, - 'telephone' as const, - 'location' as const, - ]; + const { fetchOrders } = armService(); + + const toast = useToast(); + + const [orders, setOrders] = useState([]); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(null); + + useEffect(() => { + const loadOrders = async () => { + setLoading(true); + + try { + const data = await fetchOrders(); + setOrders(data.body); + } catch (err) { + setError(err.message); + toast({ + title: t('error.title'), + status: 'error', + duration: 5000, + isClosable: true, + position: 'bottom-right', + }); + } finally { + setLoading(false); + } + }; + + loadOrders(); + }, [toast, t]); return ( @@ -34,13 +78,29 @@ const Orders = () => { - {data.body.map((order, index) => ( - - ))} + {loading && ( + + + + + + )} + {!loading && orders.length === 0 && !error && ( + + + {t('table.empty')} + + + )} + {!loading && + !error && + orders.map((order, index) => ( + + ))} diff --git a/stubs/api/index.js b/stubs/api/index.js index 22ac104..b4df477 100644 --- a/stubs/api/index.js +++ b/stubs/api/index.js @@ -6,7 +6,10 @@ const STUBS = require('./admin').STUBS; const commonError = { success: false, message: 'Что-то пошло не так' }; -const sleep = (duration = 300) => (req, res, next) => setTimeout(next, duration); +const sleep = + (duration = 1000) => + (req, res, next) => + setTimeout(next, duration); router.use(sleep()); @@ -32,4 +35,4 @@ router.get('/arm/orders', (req, res) => { router.use('/admin', require('./admin')); -module.exports = router; \ No newline at end of file +module.exports = router;