Merge pull request 'feature/successJson' (#45) from feature/successJson into main
All checks were successful
it-academy/dry-wash-pl/pipeline/head This commit looks good
All checks were successful
it-academy/dry-wash-pl/pipeline/head This commit looks good
Reviewed-on: #45
This commit is contained in:
commit
48d076a829
@ -24,6 +24,6 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
config: {
|
config: {
|
||||||
'dry-wash-pl.api': '/api',
|
'dry-wash.api': '/api',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -16,6 +16,8 @@
|
|||||||
"dry-wash.landing.social-proof-section.heading": "We are being chosen",
|
"dry-wash.landing.social-proof-section.heading": "We are being chosen",
|
||||||
"dry-wash.arm.master.add": "Add",
|
"dry-wash.arm.master.add": "Add",
|
||||||
"dry-wash.arm.order.title": "Orders",
|
"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.progress": "In Progress",
|
||||||
"dry-wash.arm.order.status.complete": "Completed",
|
"dry-wash.arm.order.status.complete": "Completed",
|
||||||
"dry-wash.arm.order.status.pending": "Pending",
|
"dry-wash.arm.order.status.pending": "Pending",
|
||||||
@ -30,6 +32,8 @@
|
|||||||
"dry-wash.arm.order.table.header.location": "Location",
|
"dry-wash.arm.order.table.header.location": "Location",
|
||||||
"dry-wash.arm.master.title": "Masters",
|
"dry-wash.arm.master.title": "Masters",
|
||||||
"dry-wash.arm.master.table.header.name": "Name",
|
"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.currentJob": "Current Job",
|
||||||
"dry-wash.arm.master.table.header.phone": "Phone",
|
"dry-wash.arm.master.table.header.phone": "Phone",
|
||||||
"dry-wash.arm.master.table.header.actions": "Actions",
|
"dry-wash.arm.master.table.header.actions": "Actions",
|
||||||
|
@ -13,7 +13,11 @@
|
|||||||
"dry-wash.arm.order.table.header.status": "Статус",
|
"dry-wash.arm.order.table.header.status": "Статус",
|
||||||
"dry-wash.arm.order.table.header.telephone": "Телефон",
|
"dry-wash.arm.order.table.header.telephone": "Телефон",
|
||||||
"dry-wash.arm.order.table.header.location": "Расположение",
|
"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.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.name": "Имя",
|
||||||
"dry-wash.arm.master.table.header.currentJob": "Актуальная занятость",
|
"dry-wash.arm.master.table.header.currentJob": "Актуальная занятость",
|
||||||
"dry-wash.arm.master.table.header.phone": "Телефон",
|
"dry-wash.arm.master.table.header.phone": "Телефон",
|
||||||
|
34
src/api/arm.ts
Normal file
34
src/api/arm.ts
Normal file
@ -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 };
|
@ -4,6 +4,19 @@ import { Badge, Link, Stack, Td, Tr } from '@chakra-ui/react';
|
|||||||
import MasterActionsMenu from '../MasterActionsMenu';
|
import MasterActionsMenu from '../MasterActionsMenu';
|
||||||
import { getTimeSlot } from '../../lib/date-helpers';
|
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, schedule, phone }) => {
|
||||||
return (
|
return (
|
||||||
<Tr>
|
<Tr>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Heading,
|
Heading,
|
||||||
@ -10,12 +10,17 @@ import {
|
|||||||
Button,
|
Button,
|
||||||
useDisclosure,
|
useDisclosure,
|
||||||
Flex,
|
Flex,
|
||||||
|
useToast,
|
||||||
|
Td,
|
||||||
|
Text,
|
||||||
|
Spinner,
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import MasterItem from '../MasterItem';
|
import MasterItem from '../MasterItem';
|
||||||
import MasterDrawer from '../MasterDrawer';
|
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 = [
|
const TABLE_HEADERS = [
|
||||||
'name' as const,
|
'name' as const,
|
||||||
@ -26,11 +31,41 @@ const TABLE_HEADERS = [
|
|||||||
|
|
||||||
const Masters = () => {
|
const Masters = () => {
|
||||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||||
|
const toast = useToast();
|
||||||
const { t } = useTranslation('~', {
|
const { t } = useTranslation('~', {
|
||||||
keyPrefix: 'dry-wash.arm.master',
|
keyPrefix: 'dry-wash.arm.master',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const [masters, setMasters] = useState<MasterProps[]>([]);
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [error, setError] = useState<string | null>(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 (
|
return (
|
||||||
<Box p='8'>
|
<Box p='8'>
|
||||||
<Flex justifyContent='space-between' alignItems='center' mb='5'>
|
<Flex justifyContent='space-between' alignItems='center' mb='5'>
|
||||||
@ -48,7 +83,23 @@ const Masters = () => {
|
|||||||
</Tr>
|
</Tr>
|
||||||
</Thead>
|
</Thead>
|
||||||
<Tbody>
|
<Tbody>
|
||||||
{data.body.map((master, index) => (
|
{loading && (
|
||||||
|
<Tr>
|
||||||
|
<Td colSpan={TABLE_HEADERS.length} textAlign='center' py='8'>
|
||||||
|
<Spinner size='lg' />
|
||||||
|
</Td>
|
||||||
|
</Tr>
|
||||||
|
)}
|
||||||
|
{!loading && masters.length === 0 && !error && (
|
||||||
|
<Tr>
|
||||||
|
<Td colSpan={TABLE_HEADERS.length}>
|
||||||
|
<Text>{t('table.empty')}</Text>
|
||||||
|
</Td>
|
||||||
|
</Tr>
|
||||||
|
)}
|
||||||
|
{!loading &&
|
||||||
|
!error &&
|
||||||
|
masters.map((master, index) => (
|
||||||
<MasterItem key={index} {...master} />
|
<MasterItem key={index} {...master} />
|
||||||
))}
|
))}
|
||||||
</Tbody>
|
</Tbody>
|
||||||
|
@ -1,24 +1,68 @@
|
|||||||
import React from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { Box, Heading, Table, Thead, Tbody, Tr, Th } from '@chakra-ui/react';
|
import {
|
||||||
|
Box,
|
||||||
|
Heading,
|
||||||
|
Table,
|
||||||
|
Thead,
|
||||||
|
Tbody,
|
||||||
|
Tr,
|
||||||
|
Th,
|
||||||
|
Spinner,
|
||||||
|
Text,
|
||||||
|
Td,
|
||||||
|
useToast,
|
||||||
|
} from '@chakra-ui/react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import OrderItem from '../OrderItem';
|
import OrderItem from '../OrderItem';
|
||||||
import { OrderProps } from '../OrderItem/OrderItem';
|
import { OrderProps } from '../OrderItem/OrderItem';
|
||||||
import data from '../../../stubs/json/arm-orders/success.json';
|
import { armService } from '../../api/arm';
|
||||||
|
|
||||||
const Orders = () => {
|
const TABLE_HEADERS = [
|
||||||
const { t } = useTranslation('~', {
|
|
||||||
keyPrefix: 'dry-wash.arm.order',
|
|
||||||
});
|
|
||||||
|
|
||||||
const TABLE_HEADERS = [
|
|
||||||
'carNumber' as const,
|
'carNumber' as const,
|
||||||
'washingTime' as const,
|
'washingTime' as const,
|
||||||
'orderDate' as const,
|
'orderDate' as const,
|
||||||
'status' as const,
|
'status' as const,
|
||||||
'telephone' as const,
|
'telephone' as const,
|
||||||
'location' as const,
|
'location' as const,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const Orders = () => {
|
||||||
|
const { t } = useTranslation('~', {
|
||||||
|
keyPrefix: 'dry-wash.arm.order',
|
||||||
|
});
|
||||||
|
|
||||||
|
const { fetchOrders } = armService();
|
||||||
|
|
||||||
|
const toast = useToast();
|
||||||
|
|
||||||
|
const [orders, setOrders] = useState<OrderProps[]>([]);
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [error, setError] = useState<string | null>(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 (
|
return (
|
||||||
<Box p='8'>
|
<Box p='8'>
|
||||||
@ -34,7 +78,23 @@ const Orders = () => {
|
|||||||
</Tr>
|
</Tr>
|
||||||
</Thead>
|
</Thead>
|
||||||
<Tbody>
|
<Tbody>
|
||||||
{data.body.map((order, index) => (
|
{loading && (
|
||||||
|
<Tr>
|
||||||
|
<Td colSpan={TABLE_HEADERS.length} textAlign='center' py='8'>
|
||||||
|
<Spinner size='lg' />
|
||||||
|
</Td>
|
||||||
|
</Tr>
|
||||||
|
)}
|
||||||
|
{!loading && orders.length === 0 && !error && (
|
||||||
|
<Tr>
|
||||||
|
<Td colSpan={TABLE_HEADERS.length}>
|
||||||
|
<Text>{t('table.empty')}</Text>
|
||||||
|
</Td>
|
||||||
|
</Tr>
|
||||||
|
)}
|
||||||
|
{!loading &&
|
||||||
|
!error &&
|
||||||
|
orders.map((order, index) => (
|
||||||
<OrderItem
|
<OrderItem
|
||||||
key={index}
|
key={index}
|
||||||
{...order}
|
{...order}
|
||||||
|
38
stubs/api/admin.js
Normal file
38
stubs/api/admin.js
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
/* eslint-disable no-undef */
|
||||||
|
/* eslint-disable @typescript-eslint/no-require-imports */
|
||||||
|
const router = require('express').Router();
|
||||||
|
|
||||||
|
const STUBS = { masters: 'success', orders: 'success' };
|
||||||
|
|
||||||
|
router.get('/set/:name/:value', (req, res) => {
|
||||||
|
const { name, value } = req.params;
|
||||||
|
|
||||||
|
STUBS[name] = value;
|
||||||
|
|
||||||
|
res.send('ok');
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get('/', (req, res) => {
|
||||||
|
res.send(`<div>
|
||||||
|
<fieldset>
|
||||||
|
<legend>Мастера</legend>
|
||||||
|
${generateRadioInput('masters', 'success')}
|
||||||
|
${generateRadioInput('masters', 'error')}
|
||||||
|
</fieldset>
|
||||||
|
<fieldset>
|
||||||
|
<legend>Заказы</legend>
|
||||||
|
${generateRadioInput('orders', 'success')}
|
||||||
|
${generateRadioInput('orders', 'error')}
|
||||||
|
</fieldset>
|
||||||
|
</div>`);
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
|
module.exports.STUBS = STUBS;
|
||||||
|
|
||||||
|
function generateRadioInput(name, type) {
|
||||||
|
return `<label>
|
||||||
|
<input ${STUBS[name] === type ? 'checked' : ''} onclick="fetch('/api/admin/set/${name}/${type}')" type="radio" name="${name}">
|
||||||
|
${type}
|
||||||
|
</label>`;
|
||||||
|
}
|
@ -2,4 +2,37 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-require-imports */
|
/* eslint-disable @typescript-eslint/no-require-imports */
|
||||||
const router = require('express').Router();
|
const router = require('express').Router();
|
||||||
|
|
||||||
|
const STUBS = require('./admin').STUBS;
|
||||||
|
|
||||||
|
const commonError = { success: false, message: 'Что-то пошло не так' };
|
||||||
|
|
||||||
|
const sleep =
|
||||||
|
(duration = 1000) =>
|
||||||
|
(req, res, next) =>
|
||||||
|
setTimeout(next, duration);
|
||||||
|
|
||||||
|
router.use(sleep());
|
||||||
|
|
||||||
|
router.get('/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.get('/arm/orders', (req, res) => {
|
||||||
|
res
|
||||||
|
.status(/error/.test(STUBS.orders) ? 500 : 200)
|
||||||
|
.send(
|
||||||
|
/^error$/.test(STUBS.orders)
|
||||||
|
? commonError
|
||||||
|
: require(`../json/arm-orders/${STUBS.orders}.json`),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
router.use('/admin', require('./admin'));
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
|
Loading…
Reference in New Issue
Block a user