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/__data__/service/api.ts b/src/__data__/service/api.ts index 29a8898..962a0f4 100644 --- a/src/__data__/service/api.ts +++ b/src/__data__/service/api.ts @@ -1,20 +1,51 @@ import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'; import { getConfigValue } from '@brojs/cli'; +import dayjs from 'dayjs'; -import { Master } from '../../models/api/master'; +import { Master, OrderArm } from '../../models/api'; 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 +54,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..8fb77ec 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, @@ -9,63 +9,51 @@ 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; - onSubmit: ({ - id, - name, - phone, - }: { - id: string; - name?: string; - phone?: string; - }) => Promise; - as: 'phone' | 'name'; + fieldName: 'phone' | 'name'; id: string; } -const EditableWrapper = ({ value, onSubmit, as, id }: EditableWrapperProps) => { +const EditableWrapper = ({ value, fieldName, id }: EditableWrapperProps) => { + const [updateMaster, { isError, isSuccess, error }] = + useUpdateMasterMutation(); + const { t } = useTranslation('~', { 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; - try { - await onSubmit({ id, [as]: newValue }); + await updateMaster({ id, [fieldName]: newValue }); - setCurrentValue(newValue); - - toast({ - title: 'Успешно!', - description: 'Данные обновлены.', - status: 'success', - duration: 2000, - isClosable: true, - position: 'top-right', - }); - } catch (error) { - toast({ - title: 'Ошибка!', - description: 'Не удалось обновить данные.', - status: 'error', - duration: 2000, - isClosable: true, - position: 'top-right', - }); - console.error('Ошибка при обновлении данных:', error); - } + setCurrentValue(newValue); }; + useEffect(() => { + if (isSuccess) { + showToast(t('toast.success'), 'success'); + } + }, [isSuccess]); + + useEffect(() => { + if (isError) { + showToast(t('toast.error.title'), 'error', t('toast.error.description')); + console.error(t('toast.error.description'), error); + } + }, [isError, error]); + function EditableControls() { const { isEditing, diff --git a/src/components/MasterActionsMenu/MasterActionsMenu.tsx b/src/components/MasterActionsMenu/MasterActionsMenu.tsx index 384c4aa..de66905 100644 --- a/src/components/MasterActionsMenu/MasterActionsMenu.tsx +++ b/src/components/MasterActionsMenu/MasterActionsMenu.tsx @@ -1,16 +1,16 @@ -import React from 'react'; +import React, { useEffect } from 'react'; import { Menu, MenuButton, MenuList, MenuItem, IconButton, - useToast, } from '@chakra-ui/react'; import { EditIcon } from '@chakra-ui/icons'; import { useTranslation } from 'react-i18next'; -import { armService } from '../../api/arm'; +import { useDeleteMasterMutation } from '../../__data__/service/api'; +import useShowToast from '../../hooks/useShowToast'; interface MasterActionsMenu { id: string; @@ -21,38 +21,35 @@ const MasterActionsMenu = ({ id }: MasterActionsMenu) => { keyPrefix: 'dry-wash.arm.master.table.actionsMenu', }); - const { deleteMaster } = armService(); - const toast = useToast(); + const [deleteMaster, { isSuccess, isError, error, isLoading }] = + useDeleteMasterMutation(); + + const showToast = useShowToast(); 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', - }); + await deleteMaster({ id }); + }; + + useEffect(() => { + if (isSuccess) { + showToast(t('toast.success'), 'success'); + } + }, [isSuccess]); + + useEffect(() => { + if (isError) { + showToast(t('toast.error.title'), 'error', t('toast.error.description')); console.error(error); } - }; + }, [isError]); return ( } as={IconButton} variant='outline' /> - {t('delete')} + + {t('delete')} + ); diff --git a/src/components/MasterDrawer/MasterDrawer.tsx b/src/components/MasterDrawer/MasterDrawer.tsx index 482073d..680b1e7 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, @@ -20,9 +19,9 @@ import { import { useTranslation } from 'react-i18next'; import { PhoneIcon } from '@chakra-ui/icons'; -import { api } from '../../__data__/service/api'; -import showToast from '../../helpers/showToast'; +import { useAddMasterMutation } from '../../__data__/service/api'; import { DrawerInputs } from '../../models/arm/form'; +import useShowToast from '../../hooks/useShowToast'; interface MasterDrawerProps { isOpen: boolean; @@ -50,28 +49,19 @@ 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; } await addMaster(trimMaster); }; - const [addMaster, { error, isSuccess }] = api.useAddMasterMutation(); - const toast = useToast(); + const [addMaster, { error, isSuccess }] = useAddMasterMutation(); + 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 8df73de..8c2f781 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..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, @@ -19,7 +18,8 @@ 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'; +import useShowToast from '../../hooks/useShowToast'; const TABLE_HEADERS = [ 'name' as const, @@ -30,26 +30,17 @@ const TABLE_HEADERS = [ const Masters = () => { const { isOpen, onOpen, onClose } = useDisclosure(); - const toast = useToast(); + const showToast = useShowToast(); + const { t } = useTranslation('~', { keyPrefix: 'dry-wash.arm.master', }); - const { - data: masters, - error, - isLoading, - isSuccess, - } = api.useGetMastersQuery(); + const { data: masters, error, isLoading, isSuccess } = useGetMastersQuery(); 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/OrderItem/OrderItem.tsx b/src/components/OrderItem/OrderItem.tsx index d622187..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 { armService } from '../../api/arm'; - -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 { useUpdateOrdersMutation } from '../../__data__/service/api'; +import { OrderArm, Status, statuses } from '../../models/api'; const statusColors: Record = { pending: 'yellow.100', @@ -53,9 +26,8 @@ const OrderItem = ({ master, allMasters, id, -}: OrderProps) => { - const { updateOrders } = armService(); - +}: OrderArm) => { + const [updateOrders] = useUpdateOrdersMutation(); const { t } = useTranslation('~', { keyPrefix: 'dry-wash.arm.order', }); @@ -72,16 +44,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 OrderArm['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..309719e 100644 --- a/src/components/Orders/Orders.tsx +++ b/src/components/Orders/Orders.tsx @@ -10,16 +10,18 @@ import { Spinner, Text, Td, - useToast, } from '@chakra-ui/react'; import { useTranslation } from 'react-i18next'; 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'; +import useShowToast from '../../hooks/useShowToast'; const TABLE_HEADERS = [ 'carNumber' as const, @@ -34,47 +36,34 @@ const Orders = () => { const { t } = useTranslation('~', { keyPrefix: 'dry-wash.arm.order', }); + const showToast = useShowToast(); - 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) { + showToast(t('error.title'), 'error'); + } + }, [isError, ordersError, mastersError, t]); return ( @@ -103,25 +92,24 @@ const Orders = () => { - {loading && ( + {isLoading && ( )} - {!loading && orders.length === 0 && !error && ( + {isSuccess && orders.length === 0 && ( {t('table.empty')} )} - {!loading && - !error && + {isSuccess && orders.map((order, index) => ( 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; diff --git a/src/models/api/common.ts b/src/models/api/common.ts index 7f24440..dae27c3 100644 --- a/src/models/api/common.ts +++ b/src/models/api/common.ts @@ -10,4 +10,4 @@ type ErrorResponse = { message: ErrorMessage; }; -export type BaseResponse = 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..4a80fad 100644 --- a/src/models/api/index.ts +++ b/src/models/api/index.ts @@ -1,2 +1,3 @@ export * from './common'; -export * from './order'; \ No newline at end of file +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; +}; 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(