From a00aaff29d1b73d23dd202049ffc083b7ff5191a Mon Sep 17 00:00:00 2001 From: ilnaz <237x237@gmail.com> Date: Sun, 2 Feb 2025 11:59:27 +0300 Subject: [PATCH 1/3] fear: change requests to RTK query --- src/__data__/service/api.ts | 58 +++++++- src/__data__/service/utils.ts | 2 +- src/api/arm.ts | 139 ------------------ src/components/Editable/Editable.tsx | 34 +++-- .../MasterActionsMenu/MasterActionsMenu.tsx | 27 +++- src/components/MasterItem/MasterItem.tsx | 16 +- src/components/Masters/Masters.tsx | 9 +- src/components/OrderItem/OrderItem.tsx | 11 +- src/components/Orders/Orders.tsx | 81 +++++----- src/models/api/common.ts | 2 +- src/models/api/index.ts | 2 +- stubs/api/index.js | 2 +- 12 files changed, 145 insertions(+), 238 deletions(-) delete mode 100644 src/api/arm.ts diff --git a/src/__data__/service/api.ts b/src/__data__/service/api.ts index 29a8898..f636621 100644 --- a/src/__data__/service/api.ts +++ b/src/__data__/service/api.ts @@ -1,20 +1,52 @@ import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'; import { getConfigValue } from '@brojs/cli'; +import dayjs from 'dayjs'; import { Master } from '../../models/api/master'; +import { OrderProps } from '../../components/OrderItem/OrderItem'; import { extractBodyFromResponse } from './utils'; +export type UpdateMasterPayload = Required> & + Partial>; + +type UpdateOrderProps = Required> & + Partial> & { + master?: string; + }; + export const api = createApi({ reducerPath: 'api', baseQuery: fetchBaseQuery({ baseUrl: getConfigValue('dry-wash.api') }), - tagTypes: ['Masters'], + tagTypes: ['Masters', 'Orders'], endpoints: (builder) => ({ getMasters: builder.query({ query: () => ({ url: '/arm/masters' }), transformResponse: extractBodyFromResponse, providesTags: ['Masters'], }), + updateOrders: builder.mutation({ + query: ({ id, status, notes, master }) => ({ + url: `/order/${id}`, + method: 'PATCH', + body: { status, notes, master }, + }), + invalidatesTags: ['Orders'], + }), + getOrders: builder.query({ + query: ({ date }) => { + const startDate = dayjs(date).startOf('day').toISOString(); + const endDate = dayjs(date).endOf('day').toISOString(); + return { + url: '/arm/orders', + method: 'POST', + body: { startDate, endDate }, + }; + }, + transformResponse: extractBodyFromResponse, + providesTags: ['Orders'], + }), + addMaster: builder.mutation>({ query: (master) => ({ url: '/arm/masters', @@ -23,5 +55,29 @@ export const api = createApi({ }), invalidatesTags: ['Masters'], }), + deleteMaster: builder.mutation({ + query: ({ id }) => ({ + url: `/arm/masters/${id}`, + method: 'DELETE', + }), + invalidatesTags: ['Masters'], + }), + updateMaster: builder.mutation({ + query: ({ id, name, phone }) => ({ + url: `/arm/masters/${id}`, + method: 'PATCH', + body: { name, phone }, + }), + invalidatesTags: ['Masters'], + }), }), }); + +export const { + useGetMastersQuery, + useAddMasterMutation, + useDeleteMasterMutation, + useUpdateMasterMutation, + useGetOrdersQuery, + useUpdateOrdersMutation, +} = api; diff --git a/src/__data__/service/utils.ts b/src/__data__/service/utils.ts index 6ca45b2..0f7d325 100644 --- a/src/__data__/service/utils.ts +++ b/src/__data__/service/utils.ts @@ -12,4 +12,4 @@ export const extractErrorMessageFromResponse = ({ data }: FetchBaseQueryError) = if (typeof data === 'object' && 'message' in data && typeof data.message === 'string') { return data.message; } -}; \ No newline at end of file +}; diff --git a/src/api/arm.ts b/src/api/arm.ts deleted file mode 100644 index 5c9e63f..0000000 --- a/src/api/arm.ts +++ /dev/null @@ -1,139 +0,0 @@ -import { getConfigValue } from '@brojs/cli'; -import dayjs from 'dayjs'; - -enum ArmEndpoints { - ORDERS = '/arm/orders', - MASTERS = '/arm/masters', -} - -const armService = () => { - const endpoint = getConfigValue('dry-wash.api'); - - const fetchOrders = async ({ date }: { date: Date }) => { - const startDate = dayjs(date).startOf('day').toISOString(); - const endDate = dayjs(date).endOf('day').toISOString(); - - const response = await fetch(`${endpoint}${ArmEndpoints.ORDERS}`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ startDate, endDate }), - }); - - 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(); - }; - - 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(); - }; - - const updateOrders = async ({ - id, - status, - notes, - masterId, - }: { - id: string; - status?: string; - notes?: string; - masterId?: string; - }) => { - const body = JSON.stringify({ status, notes, masterId }); - - const response = await fetch(`${endpoint}/order/${id}`, { - method: 'PATCH', - headers: { - 'Content-Type': 'application/json', - }, - body, - }); - - if (!response.ok) { - throw new Error(`Failed to fetch update masters: ${response.status}`); - } - - return await response.json(); - }; - - const updateMaster = async ({ - id, - name, - phone, - }: { - id: string; - name?: string; - phone?: string; - }) => { - const body = JSON.stringify({ name, phone }); - - const response = await fetch(`${endpoint}${ArmEndpoints.MASTERS}/${id}`, { - method: 'PATCH', - headers: { - 'Content-Type': 'application/json', - }, - body, - }); - - if (!response.ok) { - throw new Error(`Failed to fetch update masters: ${response.status}`); - } - - return await response.json(); - }; - - return { - fetchOrders, - fetchMasters, - addMaster, - deleteMaster, - updateMaster, - updateOrders, - }; -}; - -export { armService, ArmEndpoints }; diff --git a/src/components/Editable/Editable.tsx b/src/components/Editable/Editable.tsx index 9280bdf..61b6fad 100644 --- a/src/components/Editable/Editable.tsx +++ b/src/components/Editable/Editable.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React, { useEffect, useState } from 'react'; import { Editable, EditableInput, @@ -14,22 +14,18 @@ import { import { CheckIcon, CloseIcon, EditIcon } from '@chakra-ui/icons'; import { useTranslation } from 'react-i18next'; +import { useUpdateMasterMutation } from '../../__data__/service/api'; + interface EditableWrapperProps { value: string; - onSubmit: ({ - id, - name, - phone, - }: { - id: string; - name?: string; - phone?: string; - }) => Promise; as: 'phone' | 'name'; id: string; } -const EditableWrapper = ({ value, onSubmit, as, id }: EditableWrapperProps) => { +const EditableWrapper = ({ value, as, id }: EditableWrapperProps) => { + const [updateMaster, { isError, isSuccess, error }] = + useUpdateMasterMutation(); + const { t } = useTranslation('~', { keyPrefix: 'dry-wash.arm.master.editable', }); @@ -40,11 +36,13 @@ const EditableWrapper = ({ value, onSubmit, as, id }: EditableWrapperProps) => { const handleSubmit = async (newValue: string) => { if (currentValue === newValue) return; - try { - await onSubmit({ id, [as]: newValue }); + await updateMaster({ id, [as]: newValue }); - setCurrentValue(newValue); + setCurrentValue(newValue); + }; + useEffect(() => { + if (isSuccess) { toast({ title: 'Успешно!', description: 'Данные обновлены.', @@ -53,7 +51,11 @@ const EditableWrapper = ({ value, onSubmit, as, id }: EditableWrapperProps) => { isClosable: true, position: 'top-right', }); - } catch (error) { + } + }, [isSuccess]); + + useEffect(() => { + if (isError) { toast({ title: 'Ошибка!', description: 'Не удалось обновить данные.', @@ -64,7 +66,7 @@ const EditableWrapper = ({ value, onSubmit, as, id }: EditableWrapperProps) => { }); console.error('Ошибка при обновлении данных:', error); } - }; + }, [isError, error]); function EditableControls() { const { diff --git a/src/components/MasterActionsMenu/MasterActionsMenu.tsx b/src/components/MasterActionsMenu/MasterActionsMenu.tsx index 384c4aa..503a810 100644 --- a/src/components/MasterActionsMenu/MasterActionsMenu.tsx +++ b/src/components/MasterActionsMenu/MasterActionsMenu.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useEffect } from 'react'; import { Menu, MenuButton, @@ -10,7 +10,7 @@ import { import { EditIcon } from '@chakra-ui/icons'; import { useTranslation } from 'react-i18next'; -import { armService } from '../../api/arm'; +import { useDeleteMasterMutation } from '../../__data__/service/api'; interface MasterActionsMenu { id: string; @@ -21,12 +21,17 @@ const MasterActionsMenu = ({ id }: MasterActionsMenu) => { keyPrefix: 'dry-wash.arm.master.table.actionsMenu', }); - const { deleteMaster } = armService(); + const [deleteMaster, { isSuccess, isError, error, isLoading }] = + useDeleteMasterMutation(); + const toast = useToast(); const handleClickDelete = async () => { - try { - await deleteMaster({ id }); + await deleteMaster({ id }); + }; + + useEffect(() => { + if (isSuccess) { toast({ title: 'Мастер удалён.', description: `Мастер с ID "${id}" успешно удалён.`, @@ -35,7 +40,11 @@ const MasterActionsMenu = ({ id }: MasterActionsMenu) => { isClosable: true, position: 'top-right', }); - } catch (error) { + } + }, [isSuccess]); + + useEffect(() => { + if (isError) { toast({ title: 'Ошибка удаления мастера.', description: 'Не удалось удалить мастера. Попробуйте ещё раз.', @@ -46,13 +55,15 @@ const MasterActionsMenu = ({ id }: MasterActionsMenu) => { }); console.error(error); } - }; + }, [isError]); return ( } as={IconButton} variant='outline' /> - {t('delete')} + + {t('delete')} + ); diff --git a/src/components/MasterItem/MasterItem.tsx b/src/components/MasterItem/MasterItem.tsx index 8df73de..80c5bdc 100644 --- a/src/components/MasterItem/MasterItem.tsx +++ b/src/components/MasterItem/MasterItem.tsx @@ -5,10 +5,8 @@ import { useTranslation } from 'react-i18next'; import MasterActionsMenu from '../MasterActionsMenu'; import { getTimeSlot } from '../../lib'; import EditableWrapper from '../Editable/Editable'; -import { armService } from '../../api/arm'; const MasterItem = ({ name, phone, id, schedule }) => { - const { updateMaster } = armService(); const { t } = useTranslation('~', { keyPrefix: 'dry-wash.arm.master', }); @@ -16,12 +14,7 @@ const MasterItem = ({ name, phone, id, schedule }) => { return ( - + {schedule?.length > 0 ? ( @@ -37,12 +30,7 @@ const MasterItem = ({ name, phone, id, schedule }) => { )} - + diff --git a/src/components/Masters/Masters.tsx b/src/components/Masters/Masters.tsx index a7aff65..e6a8cee 100644 --- a/src/components/Masters/Masters.tsx +++ b/src/components/Masters/Masters.tsx @@ -19,7 +19,7 @@ import { useTranslation } from 'react-i18next'; import MasterItem from '../MasterItem'; import MasterDrawer from '../MasterDrawer'; -import { api } from '../../__data__/service/api'; +import { useGetMastersQuery } from '../../__data__/service/api'; const TABLE_HEADERS = [ 'name' as const, @@ -35,12 +35,7 @@ const Masters = () => { keyPrefix: 'dry-wash.arm.master', }); - const { - data: masters, - error, - isLoading, - isSuccess, - } = api.useGetMastersQuery(); + const { data: masters, error, isLoading, isSuccess } = useGetMastersQuery(); useEffect(() => { if (error) { diff --git a/src/components/OrderItem/OrderItem.tsx b/src/components/OrderItem/OrderItem.tsx index d622187..fd33955 100644 --- a/src/components/OrderItem/OrderItem.tsx +++ b/src/components/OrderItem/OrderItem.tsx @@ -5,7 +5,7 @@ import dayjs from 'dayjs'; import { getTimeSlot } from '../../lib'; import { Master } from '../../models/api/master'; -import { armService } from '../../api/arm'; +import { useUpdateOrdersMutation } from '../../__data__/service/api'; const statuses = [ 'pending' as const, @@ -54,8 +54,7 @@ const OrderItem = ({ allMasters, id, }: OrderProps) => { - const { updateOrders } = armService(); - + const [updateOrders] = useUpdateOrdersMutation(); const { t } = useTranslation('~', { keyPrefix: 'dry-wash.arm.order', }); @@ -72,16 +71,16 @@ const OrderItem = ({ if (selectedMaster) { setMaster(masterName); - updateOrders({ id, masterId: selectedMaster.id }); + updateOrders({ id, master: selectedMaster.id }); } else { console.error('Master not found'); } }; const handeChangeStatus = (e: ChangeEvent) => { - const status = e.target.value; + const status = e.target.value as OrderProps['status']; updateOrders({ id, status }); - setStatus(e.target.value as OrderProps['status']); + setStatus(status); }; return ( diff --git a/src/components/Orders/Orders.tsx b/src/components/Orders/Orders.tsx index 8a74f15..e4a37be 100644 --- a/src/components/Orders/Orders.tsx +++ b/src/components/Orders/Orders.tsx @@ -17,9 +17,11 @@ import dayjs from 'dayjs'; import OrderItem from '../OrderItem'; import { OrderProps } from '../OrderItem/OrderItem'; -import { armService } from '../../api/arm'; import DateNavigator from '../DateNavigator'; -import { Master } from '../../models/api/master'; +import { + useGetMastersQuery, + useGetOrdersQuery, +} from '../../__data__/service/api'; const TABLE_HEADERS = [ 'carNumber' as const, @@ -34,47 +36,41 @@ const Orders = () => { const { t } = useTranslation('~', { keyPrefix: 'dry-wash.arm.order', }); - - 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()); + const { + data: orders, + isLoading: isOrdersLoading, + isSuccess: isOrdersSuccess, + isError: isOrdersError, + error: ordersError, + } = useGetOrdersQuery({ date: currentDate }); + + const { + data: masters, + isLoading: isMastersLoading, + isSuccess: isMastersSuccess, + isError: isMastersError, + error: mastersError, + } = useGetMastersQuery(); + + const isLoading = isOrdersLoading || isMastersLoading; + const isSuccess = isOrdersSuccess && isMastersSuccess; + const isError = isOrdersError || isMastersError; useEffect(() => { - const loadData = async () => { - setLoading(true); - setError(null); - - try { - const [ordersData, mastersData] = await Promise.all([ - fetchOrders({ date: currentDate }), - fetchMasters(), - ]); - - setOrders(ordersData.body); - setAllMasters(mastersData.body); - } catch (err) { - setError(err.message); - toast({ - title: t('error.title'), - status: 'error', - duration: 5000, - isClosable: true, - position: 'bottom-right', - }); - } finally { - setLoading(false); - } - }; - - loadData(); - }, [currentDate, toast, t]); + if (isError) { + toast({ + title: t('error.title'), + // description: errorMessage, + status: 'error', + duration: 5000, + isClosable: true, + position: 'bottom-right', + }); + } + }, [isError, ordersError, mastersError, toast, t]); return ( @@ -103,25 +99,24 @@ const Orders = () => { - {loading && ( + {isLoading && ( )} - {!loading && orders.length === 0 && !error && ( + {isSuccess && orders.length === 0 && ( {t('table.empty')} )} - {!loading && - !error && + {isSuccess && orders.map((order, index) => ( = SuccessResponse | ErrorResponse; \ No newline at end of file +export type BaseResponse = SuccessResponse | ErrorResponse; diff --git a/src/models/api/index.ts b/src/models/api/index.ts index 73fe494..16ed054 100644 --- a/src/models/api/index.ts +++ b/src/models/api/index.ts @@ -1,2 +1,2 @@ export * from './common'; -export * from './order'; \ No newline at end of file +export * from './order'; diff --git a/stubs/api/index.js b/stubs/api/index.js index f21ace4..8b8a7b4 100644 --- a/stubs/api/index.js +++ b/stubs/api/index.js @@ -53,7 +53,7 @@ router.delete('/arm/masters/:id', (req, res) => { ); }); -router.patch('/orders/:id', (req, res) => { +router.patch('/order/:id', (req, res) => { res .status(/error/.test(STUBS.orders) ? 500 : 200) .send( From ed8ae95436406cd37431a7b631acbd1345b65ffe Mon Sep 17 00:00:00 2001 From: ilnaz <237x237@gmail.com> Date: Sat, 8 Feb 2025 22:18:32 +0300 Subject: [PATCH 2/3] feat: add hook useShowToast.ts --- locales/ru.json | 6 ++++ src/components/Editable/Editable.tsx | 30 +++++-------------- .../MasterActionsMenu/MasterActionsMenu.tsx | 22 +++----------- src/components/MasterDrawer/MasterDrawer.tsx | 29 ++++++------------ src/components/MasterItem/MasterItem.tsx | 4 +-- src/components/Masters/Masters.tsx | 12 +++----- src/components/Orders/Orders.tsx | 15 +++------- src/helpers/showToast.ts | 21 ------------- src/hooks/useShowToast.ts | 28 +++++++++++++++++ 9 files changed, 65 insertions(+), 102 deletions(-) delete mode 100644 src/helpers/showToast.ts create mode 100644 src/hooks/useShowToast.ts diff --git a/locales/ru.json b/locales/ru.json index 6b03952..56f22d2 100644 --- a/locales/ru.json +++ b/locales/ru.json @@ -25,10 +25,16 @@ "dry-wash.arm.master.table.header.phone": "Телефон", "dry-wash.arm.master.table.header.actions": "Действия", "dry-wash.arm.master.table.actionsMenu.delete": "Удалить мастера", + "dry-wash.arm.master.table.actionsMenu.toast.success": "Мастер удалён", + "dry-wash.arm.master.table.actionsMenu.toast.error.title": "Ошибка!", + "dry-wash.arm.master.table.actionsMenu.toast.error.description": "Не удалось удалить мастера. Попробуйте ещё раз.", "dry-wash.arm.master.schedule.empty": "Свободен", "dry-wash.arm.master.editable.aria.cancel": "Отменить изменения", "dry-wash.arm.master.editable.aria.save": "Сохранить изменения", "dry-wash.arm.master.editable.aria.edit": "Редактировать", + "dry-wash.arm.master.editable.toast.success": "Успешно!", + "dry-wash.arm.master.editable.toast.error.description": "Не удалось обновить данные", + "dry-wash.arm.master.editable.toast.error.title": "Ошибка!", "dry-wash.arm.master.drawer.title": "Добавить нового мастера", "dry-wash.arm.master.drawer.inputName.label": "ФИО", "dry-wash.arm.master.drawer.inputName.placeholder": "Введите ФИО", diff --git a/src/components/Editable/Editable.tsx b/src/components/Editable/Editable.tsx index 61b6fad..8fb77ec 100644 --- a/src/components/Editable/Editable.tsx +++ b/src/components/Editable/Editable.tsx @@ -9,20 +9,20 @@ import { useEditableControls, ButtonGroup, Stack, - useToast, } from '@chakra-ui/react'; import { CheckIcon, CloseIcon, EditIcon } from '@chakra-ui/icons'; import { useTranslation } from 'react-i18next'; import { useUpdateMasterMutation } from '../../__data__/service/api'; +import useShowToast from '../../hooks/useShowToast'; interface EditableWrapperProps { value: string; - as: 'phone' | 'name'; + fieldName: 'phone' | 'name'; id: string; } -const EditableWrapper = ({ value, as, id }: EditableWrapperProps) => { +const EditableWrapper = ({ value, fieldName, id }: EditableWrapperProps) => { const [updateMaster, { isError, isSuccess, error }] = useUpdateMasterMutation(); @@ -30,41 +30,27 @@ const EditableWrapper = ({ value, as, id }: EditableWrapperProps) => { keyPrefix: 'dry-wash.arm.master.editable', }); - const toast = useToast(); + const showToast = useShowToast(); const [currentValue, setCurrentValue] = useState(value); const handleSubmit = async (newValue: string) => { if (currentValue === newValue) return; - await updateMaster({ id, [as]: newValue }); + await updateMaster({ id, [fieldName]: newValue }); setCurrentValue(newValue); }; useEffect(() => { if (isSuccess) { - toast({ - title: 'Успешно!', - description: 'Данные обновлены.', - status: 'success', - duration: 2000, - isClosable: true, - position: 'top-right', - }); + showToast(t('toast.success'), 'success'); } }, [isSuccess]); useEffect(() => { if (isError) { - toast({ - title: 'Ошибка!', - description: 'Не удалось обновить данные.', - status: 'error', - duration: 2000, - isClosable: true, - position: 'top-right', - }); - console.error('Ошибка при обновлении данных:', error); + showToast(t('toast.error.title'), 'error', t('toast.error.description')); + console.error(t('toast.error.description'), error); } }, [isError, error]); diff --git a/src/components/MasterActionsMenu/MasterActionsMenu.tsx b/src/components/MasterActionsMenu/MasterActionsMenu.tsx index 503a810..de66905 100644 --- a/src/components/MasterActionsMenu/MasterActionsMenu.tsx +++ b/src/components/MasterActionsMenu/MasterActionsMenu.tsx @@ -5,12 +5,12 @@ import { MenuList, MenuItem, IconButton, - useToast, } from '@chakra-ui/react'; import { EditIcon } from '@chakra-ui/icons'; import { useTranslation } from 'react-i18next'; import { useDeleteMasterMutation } from '../../__data__/service/api'; +import useShowToast from '../../hooks/useShowToast'; interface MasterActionsMenu { id: string; @@ -24,7 +24,7 @@ const MasterActionsMenu = ({ id }: MasterActionsMenu) => { const [deleteMaster, { isSuccess, isError, error, isLoading }] = useDeleteMasterMutation(); - const toast = useToast(); + const showToast = useShowToast(); const handleClickDelete = async () => { await deleteMaster({ id }); @@ -32,27 +32,13 @@ const MasterActionsMenu = ({ id }: MasterActionsMenu) => { useEffect(() => { if (isSuccess) { - toast({ - title: 'Мастер удалён.', - description: `Мастер с ID "${id}" успешно удалён.`, - status: 'success', - duration: 5000, - isClosable: true, - position: 'top-right', - }); + showToast(t('toast.success'), 'success'); } }, [isSuccess]); useEffect(() => { if (isError) { - toast({ - title: 'Ошибка удаления мастера.', - description: 'Не удалось удалить мастера. Попробуйте ещё раз.', - status: 'error', - duration: 5000, - isClosable: true, - position: 'top-right', - }); + showToast(t('toast.error.title'), 'error', t('toast.error.description')); console.error(error); } }, [isError]); diff --git a/src/components/MasterDrawer/MasterDrawer.tsx b/src/components/MasterDrawer/MasterDrawer.tsx index 482073d..c1c3c1d 100644 --- a/src/components/MasterDrawer/MasterDrawer.tsx +++ b/src/components/MasterDrawer/MasterDrawer.tsx @@ -12,7 +12,6 @@ import { DrawerFooter, DrawerHeader, DrawerOverlay, - useToast, InputGroup, InputLeftElement, FormErrorMessage, @@ -21,8 +20,8 @@ import { useTranslation } from 'react-i18next'; import { PhoneIcon } from '@chakra-ui/icons'; import { api } from '../../__data__/service/api'; -import showToast from '../../helpers/showToast'; import { DrawerInputs } from '../../models/arm/form'; +import useShowToast from '../../hooks/useShowToast'; interface MasterDrawerProps { isOpen: boolean; @@ -50,12 +49,7 @@ const MasterDrawer = ({ isOpen, onClose }: MasterDrawerProps) => { const isEmptyFields = trimMaster.name === '' || trimMaster.phone === ''; if (isEmptyFields) { - showToast({ - toast, - title: t('toast.error.base'), - description: t('toast.error.empty-fields'), - status: 'error', - }); + showToast(t('toast.error.base'), 'error', t('toast.error.empty-fields')); return; } @@ -63,15 +57,11 @@ const MasterDrawer = ({ isOpen, onClose }: MasterDrawerProps) => { }; const [addMaster, { error, isSuccess }] = api.useAddMasterMutation(); - const toast = useToast(); + const showToast = useShowToast(); useEffect(() => { if (isSuccess) { - showToast({ - toast, - title: t('toast.create-master'), - status: 'success', - }); + showToast(t('toast.create-master'), 'success'); reset(); onClose(); } @@ -79,12 +69,11 @@ const MasterDrawer = ({ isOpen, onClose }: MasterDrawerProps) => { useEffect(() => { if (error) { - showToast({ - toast, - title: t('toast.error.create-master'), - description: t('toast.error.create-master-details'), - status: 'error', - }); + showToast( + t('toast.error.create-master'), + 'error', + t('toast.error.create-master-details'), + ); console.error(error); } }, [error]); diff --git a/src/components/MasterItem/MasterItem.tsx b/src/components/MasterItem/MasterItem.tsx index 80c5bdc..8c2f781 100644 --- a/src/components/MasterItem/MasterItem.tsx +++ b/src/components/MasterItem/MasterItem.tsx @@ -14,7 +14,7 @@ const MasterItem = ({ name, phone, id, schedule }) => { return ( - + {schedule?.length > 0 ? ( @@ -30,7 +30,7 @@ const MasterItem = ({ name, phone, id, schedule }) => { )} - + diff --git a/src/components/Masters/Masters.tsx b/src/components/Masters/Masters.tsx index e6a8cee..48f46f4 100644 --- a/src/components/Masters/Masters.tsx +++ b/src/components/Masters/Masters.tsx @@ -10,7 +10,6 @@ import { Button, useDisclosure, Flex, - useToast, Td, Text, Spinner, @@ -20,6 +19,7 @@ import { useTranslation } from 'react-i18next'; import MasterItem from '../MasterItem'; import MasterDrawer from '../MasterDrawer'; import { useGetMastersQuery } from '../../__data__/service/api'; +import useShowToast from '../../hooks/useShowToast'; const TABLE_HEADERS = [ 'name' as const, @@ -30,7 +30,8 @@ const TABLE_HEADERS = [ const Masters = () => { const { isOpen, onOpen, onClose } = useDisclosure(); - const toast = useToast(); + const showToast = useShowToast(); + const { t } = useTranslation('~', { keyPrefix: 'dry-wash.arm.master', }); @@ -39,12 +40,7 @@ const Masters = () => { useEffect(() => { if (error) { - toast({ - title: t('error.title'), - status: 'error', - isClosable: true, - position: 'bottom-right', - }); + showToast(t('error.title'), 'error'); } }, [error]); diff --git a/src/components/Orders/Orders.tsx b/src/components/Orders/Orders.tsx index e4a37be..309719e 100644 --- a/src/components/Orders/Orders.tsx +++ b/src/components/Orders/Orders.tsx @@ -10,7 +10,6 @@ import { Spinner, Text, Td, - useToast, } from '@chakra-ui/react'; import { useTranslation } from 'react-i18next'; import dayjs from 'dayjs'; @@ -22,6 +21,7 @@ import { useGetMastersQuery, useGetOrdersQuery, } from '../../__data__/service/api'; +import useShowToast from '../../hooks/useShowToast'; const TABLE_HEADERS = [ 'carNumber' as const, @@ -36,7 +36,7 @@ const Orders = () => { const { t } = useTranslation('~', { keyPrefix: 'dry-wash.arm.order', }); - const toast = useToast(); + const showToast = useShowToast(); const [currentDate, setCurrentDate] = useState(new Date()); const { @@ -61,16 +61,9 @@ const Orders = () => { useEffect(() => { if (isError) { - toast({ - title: t('error.title'), - // description: errorMessage, - status: 'error', - duration: 5000, - isClosable: true, - position: 'bottom-right', - }); + showToast(t('error.title'), 'error'); } - }, [isError, ordersError, mastersError, toast, t]); + }, [isError, ordersError, mastersError, t]); return ( diff --git a/src/helpers/showToast.ts b/src/helpers/showToast.ts deleted file mode 100644 index eed5aca..0000000 --- a/src/helpers/showToast.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { UseToastOptions } from '@chakra-ui/react'; - -interface ShowToast { - toast: (options: UseToastOptions) => void; - title: string; - description?: string; - status: 'info' | 'warning' | 'success' | 'error'; -} - -const showToast = ({ toast, title, description, status }: ShowToast) => { - toast({ - title, - description, - status, - duration: 5000, - isClosable: true, - position: 'top-right', - }); -}; - -export default showToast; diff --git a/src/hooks/useShowToast.ts b/src/hooks/useShowToast.ts new file mode 100644 index 0000000..3914ff7 --- /dev/null +++ b/src/hooks/useShowToast.ts @@ -0,0 +1,28 @@ +import { useToast } from '@chakra-ui/react'; +import { useCallback } from 'react'; + +const useShowToast = () => { + const toast = useToast(); + + const showToast = useCallback( + ( + title: string, + status: 'info' | 'warning' | 'success' | 'error', + description?: string, + ) => { + toast({ + title, + description, + status, + duration: 5000, + isClosable: true, + position: 'top-right', + }); + }, + [toast], + ); + + return showToast; +}; + +export default useShowToast; From 658e23d4e3baaa142bb0d4ee634299759f9b028f Mon Sep 17 00:00:00 2001 From: ilnaz <237x237@gmail.com> Date: Sat, 8 Feb 2025 22:36:30 +0300 Subject: [PATCH 3/3] fix: place orders in the model --- src/__data__/service/api.ts | 11 +++--- src/components/MasterDrawer/MasterDrawer.tsx | 4 +- src/components/OrderItem/OrderItem.tsx | 33 ++-------------- src/models/api/index.ts | 1 + src/models/api/order.ts | 40 +++++++++++++++++--- 5 files changed, 45 insertions(+), 44 deletions(-) diff --git a/src/__data__/service/api.ts b/src/__data__/service/api.ts index f636621..962a0f4 100644 --- a/src/__data__/service/api.ts +++ b/src/__data__/service/api.ts @@ -2,16 +2,15 @@ import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'; import { getConfigValue } from '@brojs/cli'; import dayjs from 'dayjs'; -import { Master } from '../../models/api/master'; -import { OrderProps } from '../../components/OrderItem/OrderItem'; +import { Master, OrderArm } from '../../models/api'; import { extractBodyFromResponse } from './utils'; export type UpdateMasterPayload = Required> & Partial>; -type UpdateOrderProps = Required> & - Partial> & { +type UpdateOrderProps = Required> & + Partial> & { master?: string; }; @@ -33,7 +32,7 @@ export const api = createApi({ }), invalidatesTags: ['Orders'], }), - getOrders: builder.query({ + getOrders: builder.query({ query: ({ date }) => { const startDate = dayjs(date).startOf('day').toISOString(); const endDate = dayjs(date).endOf('day').toISOString(); @@ -43,7 +42,7 @@ export const api = createApi({ body: { startDate, endDate }, }; }, - transformResponse: extractBodyFromResponse, + transformResponse: extractBodyFromResponse, providesTags: ['Orders'], }), diff --git a/src/components/MasterDrawer/MasterDrawer.tsx b/src/components/MasterDrawer/MasterDrawer.tsx index c1c3c1d..680b1e7 100644 --- a/src/components/MasterDrawer/MasterDrawer.tsx +++ b/src/components/MasterDrawer/MasterDrawer.tsx @@ -19,7 +19,7 @@ import { import { useTranslation } from 'react-i18next'; import { PhoneIcon } from '@chakra-ui/icons'; -import { api } from '../../__data__/service/api'; +import { useAddMasterMutation } from '../../__data__/service/api'; import { DrawerInputs } from '../../models/arm/form'; import useShowToast from '../../hooks/useShowToast'; @@ -56,7 +56,7 @@ const MasterDrawer = ({ isOpen, onClose }: MasterDrawerProps) => { await addMaster(trimMaster); }; - const [addMaster, { error, isSuccess }] = api.useAddMasterMutation(); + const [addMaster, { error, isSuccess }] = useAddMasterMutation(); const showToast = useShowToast(); useEffect(() => { diff --git a/src/components/OrderItem/OrderItem.tsx b/src/components/OrderItem/OrderItem.tsx index fd33955..bba3c82 100644 --- a/src/components/OrderItem/OrderItem.tsx +++ b/src/components/OrderItem/OrderItem.tsx @@ -4,35 +4,8 @@ import { useTranslation } from 'react-i18next'; import dayjs from 'dayjs'; import { getTimeSlot } from '../../lib'; -import { Master } from '../../models/api/master'; import { useUpdateOrdersMutation } from '../../__data__/service/api'; - -const statuses = [ - 'pending' as const, - 'progress' as const, - 'working' as const, - 'canceled' as const, - 'complete' as const, -]; - -type GetArrItemType = - ArrType extends Array ? ItemType : never; - -export type OrderProps = { - carNumber?: string; - startWashTime?: string; - endWashTime?: string; - orderDate?: string; - status?: GetArrItemType; - phone?: string; - location?: string; - master: Master; - notes: ''; - allMasters: Master[]; - id: string; -}; - -type Status = (typeof statuses)[number]; +import { OrderArm, Status, statuses } from '../../models/api'; const statusColors: Record = { pending: 'yellow.100', @@ -53,7 +26,7 @@ const OrderItem = ({ master, allMasters, id, -}: OrderProps) => { +}: OrderArm) => { const [updateOrders] = useUpdateOrdersMutation(); const { t } = useTranslation('~', { keyPrefix: 'dry-wash.arm.order', @@ -78,7 +51,7 @@ const OrderItem = ({ }; const handeChangeStatus = (e: ChangeEvent) => { - const status = e.target.value as OrderProps['status']; + const status = e.target.value as OrderArm['status']; updateOrders({ id, status }); setStatus(status); }; diff --git a/src/models/api/index.ts b/src/models/api/index.ts index 16ed054..4a80fad 100644 --- a/src/models/api/index.ts +++ b/src/models/api/index.ts @@ -1,2 +1,3 @@ export * from './common'; export * from './order'; +export * from './master'; diff --git a/src/models/api/order.ts b/src/models/api/order.ts index e622348..dabe6f9 100644 --- a/src/models/api/order.ts +++ b/src/models/api/order.ts @@ -1,21 +1,49 @@ /* eslint-disable @typescript-eslint/no-namespace */ -import { Order } from "../landing"; +import { Order } from '../landing'; -import { ErrorMessage } from "./common"; +import { ErrorMessage } from './common'; +import { Master } from './master'; export namespace GetOrder { export type Response = Order.View; export type Params = { - orderId: Order.Id + orderId: Order.Id; }; export type Error = ErrorMessage; } export namespace CreateOrder { export type Response = { - id: Order.Id + id: Order.Id; }; export type Params = { - body: Order.Create + body: Order.Create; }; -}; \ No newline at end of file +} + +type GetArrItemType = + ArrType extends Array ? ItemType : never; + +export const statuses = [ + 'pending' as const, + 'progress' as const, + 'working' as const, + 'canceled' as const, + 'complete' as const, +]; + +export type Status = (typeof statuses)[number]; + +export type OrderArm = { + carNumber?: string; + startWashTime?: string; + endWashTime?: string; + orderDate?: string; + status?: GetArrItemType; + phone?: string; + location?: string; + master: Master; + notes: ''; + allMasters: Master[]; + id: string; +};