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/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..384c4aa 100644
--- a/src/components/MasterActionsMenu/MasterActionsMenu.tsx
+++ b/src/components/MasterActionsMenu/MasterActionsMenu.tsx
@@ -5,20 +5,54 @@ import {
MenuList,
MenuItem,
IconButton,
+ useToast,
} from '@chakra-ui/react';
import { EditIcon } from '@chakra-ui/icons';
import { useTranslation } from 'react-i18next';
-const MasterActionsMenu = () => {
+import { armService } from '../../api/arm';
+
+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 (
);
diff --git a/src/components/MasterDrawer/MasterDrawer.tsx b/src/components/MasterDrawer/MasterDrawer.tsx
index 03cc9d1..1f4ac79 100644
--- a/src/components/MasterDrawer/MasterDrawer.tsx
+++ b/src/components/MasterDrawer/MasterDrawer.tsx
@@ -11,15 +11,48 @@ import {
DrawerFooter,
DrawerHeader,
DrawerOverlay,
+ useToast,
+ InputGroup,
+ InputLeftElement,
} from '@chakra-ui/react';
import { useTranslation } from 'react-i18next';
+import { PhoneIcon } from '@chakra-ui/icons';
+
+import { armService } from '../../api/arm';
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 +69,7 @@ const MasterDrawer = ({ isOpen, onClose }) => {
{t('inputName.label')}
setNewMaster({ ...newMaster, name: e.target.value })
@@ -44,14 +78,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..23c683a 100644
--- a/src/components/MasterItem/MasterItem.tsx
+++ b/src/components/MasterItem/MasterItem.tsx
@@ -2,7 +2,7 @@ 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;
@@ -13,11 +13,11 @@ export interface Schedule {
export type MasterProps = {
id: string;
name: string;
- schedule: Schedule[];
phone: string;
+ schedule: Schedule[];
};
-const MasterItem = ({ name, schedule, phone }) => {
+const MasterItem = ({ name, phone, id, schedule }) => {
return (
{name} |
@@ -34,7 +34,7 @@ const MasterItem = ({ name, schedule, phone }) => {
{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) => (
{
const [user, setUser] = useState(null);
@@ -20,9 +19,11 @@ const Page = () => {
if (!user)
return (
-
-
-
+
+
+
+
+
);
return ;
diff --git a/stubs/api/index.js b/stubs/api/index.js
index 6fd2901..f360596 100644
--- a/stubs/api/index.js
+++ b/stubs/api/index.js
@@ -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-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
+}