feat: make layout, form, order pages and wizards (#6) #13

Merged
237x237 merged 2 commits from feature/arm-layout into main 2024-11-03 11:44:24 +03:00
25 changed files with 2161 additions and 23 deletions
Showing only changes of commit 7275383e5f - Show all commits

1
.gitignore vendored
View File

@ -130,3 +130,4 @@ dist
.yarn/install-state.gz
.pnp.*
.idea

8
.prettierrc.json Normal file
View File

@ -0,0 +1,8 @@
{
"singleQuote": true,
"jsxSingleQuote": true,
"arrowParens": "always",
"max-len": ["error", 140, 2],
"tabWidth": 2,
"useTabs": false
}

1767
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
{
"name": "dry-wash",
"name": "dry-wash-pl",
Review

Надо бы убрать -pl

Надо бы убрать _-pl_
"version": "0.0.0",
"description": "<a id=\"readme-top\"></a>",
"main": "./src/index.tsx",
@ -15,9 +15,18 @@
"license": "ISC",
"dependencies": {
"@brojs/cli": "^1.3.0",
"@chakra-ui/icons": "^2.2.4",
"@chakra-ui/react": "^2.4.2",
"@emotion/react": "^11.4.1",
"@emotion/styled": "^11.3.0",
"express": "^4.21.1",
"framer-motion": "^6.2.8",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-router-dom": "^6.27.0"
},
"devDependencies": {
"@types/react-dom": "^18.3.1",
Review

@types/react

@types/react
"prettier": "3.3.3"
}
}

View File

@ -1,12 +1,15 @@
import React from "react";
import { BrowserRouter } from "react-router-dom";
import Routers from "./routes";
import React from 'react';
import { BrowserRouter } from 'react-router-dom';
import Routers from './routes';
import { ChakraProvider, theme as chakraTheme } from '@chakra-ui/react';
const App = () => {
return (
<BrowserRouter>
<Routers></Routers>
</BrowserRouter>
<ChakraProvider theme={chakraTheme}>
<BrowserRouter>
<Routers></Routers>
</BrowserRouter>
</ChakraProvider>
);
};

View File

@ -0,0 +1,17 @@
import { Box, Flex } from '@chakra-ui/react';
import Sidebar from '../Sidebar';
import Orders from '../Orders';
import Masters from '../Masters';
import React from 'react';
const LayoutArm = ({ currentPage, onSelectPage }) => (
<Flex h='100vh'>
<Sidebar onSelectPage={onSelectPage} />
<Box flex='1' bg='gray.50'>
{currentPage === 'orders' && <Orders />}
Review

Перевести на router

Перевести на router
{currentPage === 'masters' && <Masters />}
</Box>
</Flex>
);
export default LayoutArm;

View File

@ -0,0 +1 @@
export { default } from './LayoutArm';

View File

@ -0,0 +1,24 @@
import React from 'react';
import {
Menu,
MenuButton,
MenuList,
MenuItem,
IconButton,
} from '@chakra-ui/react';
import { EditIcon } from '@chakra-ui/icons';
const MasterActionsMenu = () => {
return (
<Menu>
<MenuButton icon={<EditIcon />} as={IconButton} variant='outline' />
<MenuList>
<MenuItem>Посмотреть профиль</MenuItem>
<MenuItem>Изменить расписание</MenuItem>
<MenuItem>Удалить мастера</MenuItem>
</MenuList>
</Menu>
);
};
export default MasterActionsMenu;

View File

@ -0,0 +1 @@
export { default } from './MasterActionsMenu';

View File

@ -0,0 +1,28 @@
import React from 'react';
import { Badge, Link, Stack, Td, Tr } from '@chakra-ui/react';
import MasterActionsMenu from '../MasterActionsMenu';
const MasterItem = ({ name, schedule, phone }) => {
return (
<Tr>
<Td>{name}</Td>
<Td>
<Stack direction='row'>
{schedule.map((time, index) => (
<Badge colorScheme={'green'} key={index}>
{time}
</Badge>
))}
</Stack>
</Td>
<Td>
<Link href='tel:'>{phone}</Link>
</Td>
<Td>
<MasterActionsMenu />
</Td>
</Tr>
);
};
export default MasterItem;

View File

@ -0,0 +1 @@
export { default } from './MasterItem';

View File

@ -0,0 +1,65 @@
import React, { useState } from 'react';
import {
Button,
FormControl,
FormLabel,
Input,
Drawer,
DrawerBody,
DrawerCloseButton,
DrawerContent,
DrawerFooter,
DrawerHeader,
DrawerOverlay,
} from '@chakra-ui/react';
const MasterDrawer = ({ isOpen, onClose }) => {
const [newMaster, setNewMaster] = useState({ name: '', phone: '' });
const handleSave = () => {
console.log(`Сохранение мастера: ${newMaster}`);
onClose();
};
return (
<Drawer isOpen={isOpen} onClose={onClose} size='md'>
<DrawerOverlay />
<DrawerContent>
<DrawerCloseButton />
<DrawerHeader>Добавить нового мастера</DrawerHeader>
<DrawerBody>
<FormControl mb='4'>
<FormLabel>ФИО</FormLabel>
<Input
value={newMaster.name}
onChange={(e) =>
setNewMaster({ ...newMaster, name: e.target.value })
}
placeholder='Введите ФИО'
/>
</FormControl>
<FormControl>
<FormLabel>Номер телефона</FormLabel>
<Input
value={newMaster.phone}
onChange={(e) =>
setNewMaster({ ...newMaster, phone: e.target.value })
}
placeholder='Введите номер телефона'
/>
</FormControl>
</DrawerBody>
<DrawerFooter>
<Button colorScheme='teal' mr={3} onClick={handleSave}>
Сохранить
</Button>
<Button variant='ghost' onClick={onClose}>
Отменить
</Button>
</DrawerFooter>
</DrawerContent>
</Drawer>
);
};
export default MasterDrawer;

View File

@ -0,0 +1 @@
export { default } from './MasterDrawer';

View File

@ -0,0 +1,50 @@
import React from 'react';
import {
Box,
Heading,
Table,
Thead,
Tbody,
Tr,
Th,
Button,
useDisclosure,
Flex,
} from '@chakra-ui/react';
import { mastersData } from '../../mocks ';
import MasterItem from '../MasterItem';
import MasterDrawer from '../MasterModal';
const TABLE_HEADERS = ['Имя', 'Актуальная занятость', 'Телефон', 'Действия'];
const Masters = () => {
const { isOpen, onOpen, onClose } = useDisclosure();
return (
<Box p='8'>
<Flex justifyContent='space-between' alignItems='center' mb='5'>
<Heading size='lg'>Мастера</Heading>
<Button colorScheme='green' onClick={onOpen}>
+ Добавить
</Button>
</Flex>
<Table variant='simple' colorScheme='blackAlpha'>
<Thead>
<Tr>
{TABLE_HEADERS.map((name) => (
<Th>{name}</Th>
))}
</Tr>
</Thead>
<Tbody>
{mastersData.map((master, index) => (
<MasterItem key={index} {...master} />
))}
</Tbody>
</Table>
<MasterDrawer isOpen={isOpen} onClose={onClose} />
</Box>
);
};
export default Masters;

View File

@ -0,0 +1 @@
export { default } from './Masters';

View File

@ -0,0 +1,40 @@
import React, { useState } from 'react';
import { Td, Tr, Link, Select } from '@chakra-ui/react';
const OrderItem = ({
carNumber,
washTime,
orderDate,
status,
phone,
location,
}) => {
const [statusSelect, setStatus] = useState(status);
return (
<Tr>
<Td>{carNumber}</Td>
<Td>{washTime}</Td>
<Td>{orderDate}</Td>
<Td>
<Select
value={statusSelect}
onChange={(e) => setStatus(e.target.value)}
placeholder='Выберите статус'
>
<option value='в ожидании'>в ожидании</option>
<option value='В процессе'>в процессе</option>
<option value='в работе'>в работе</option>
<option value='отменил'>отменил</option>
<option value='Завершено'>Завершено</option>
</Select>
</Td>
<Td>
<Link href='tel:'>{phone}</Link>
</Td>
<Td>{location}</Td>
</Tr>
);
};
export default OrderItem;

View File

@ -0,0 +1 @@
export { default } from './OrderItem';

View File

@ -0,0 +1,38 @@
import { Box, Heading, Table, Thead, Tbody, Tr, Th } from '@chakra-ui/react';
import React from 'react';
import { ordersData } from '../../mocks ';
import OrderItem from '../OrderItem';
const Orders = () => {
const TABLE_HEADERS = [
'Номер машины',
'Время мойки',
'Дата заказа',
'Статус',
'Телефон',
'Расположение',
];
return (
<Box p='8'>
<Heading size='lg' mb='5'>
Заказы
</Heading>
<Table variant='simple' colorScheme='blackAlpha'>
<Thead>
<Tr>
{TABLE_HEADERS.map((name, key) => (
<Th key={key}>{name}</Th>
))}
</Tr>
</Thead>
<Tbody>
{ordersData.map((order, index) => (
<OrderItem key={index} {...order} />
))}
</Tbody>
</Table>
</Box>
);
};
export default Orders;

View File

@ -0,0 +1 @@
export { default } from './Orders';

View File

@ -0,0 +1,42 @@
import { Box, Button, Heading, VStack } from '@chakra-ui/react';
import React from 'react';
import { Divider } from '@chakra-ui/react';
const Sidebar = ({ onSelectPage }) => (
<Box
borderRight='1px solid black'
bg='gray.50'
color='white'
w='250px'
p='5'
pt='8'
>
<Heading color='green' size='lg' mb='5'>
Сухой мастер
</Heading>
<VStack align='start' spacing='4'>
<Divider />
<Button
onClick={() => onSelectPage('orders')}
w='100%'
colorScheme='green'
variant='ghost'
>
Заказы
</Button>
<Divider />
<Button
onClick={() => onSelectPage('masters')}
w='100%'
colorScheme='green'
variant='ghost'
>
Мастера
</Button>
<Divider />
</VStack>
</Box>
);
export default Sidebar;

View File

@ -0,0 +1 @@
export { default } from './Sidebar';

View File

@ -2,21 +2,20 @@
/* eslint-disable react/display-name */
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './app';
export default () => <App/>;
let rootElement: ReactDOM.Root
export const mount = (Сomponent, element = document.getElementById('app')) => {
const rootElement = ReactDOM.createRoot(element);
rootElement.render(<Сomponent/>);
if(module.hot) {
module.hot.accept('./app', ()=> {
rootElement.render(<Сomponent/>);
})
import App from './app';
export default () => <App />;
let rootElement: ReactDOM.Root;
export const mount = (Component, element = document.getElementById('app')) => {
const rootElement = ReactDOM.createRoot(element);
rootElement.render(<Component />);
if (module.hot) {
module.hot.accept('./app', () => {
rootElement.render(<Component />);
});
}
};

31
src/mocks /index.ts Normal file
View File

@ -0,0 +1,31 @@
export const mastersData = [
{
name: "Иван Иванов",

Добавить Id

Добавить Id
schedule: ["15:00 - 16:30", "17:00 - 18:00"],
phone: "+7 900 123 45 67",
},
{
name: "Сергей Петров",
schedule: ["10:00 - 12:30", "14:00 - 15:30", "16:00 - 17:00"],
phone: "+7 900 234 56 78",
},
];
export const ordersData = [
{
carNumber: "A123BC",
washTime: "10:30",
orderDate: "2024-10-31",
status: "В процессе",

Переделать на строку на английском

Переделать на строку на английском
phone: "+7 900 123 45 67",

Храним без + и пробелов

Храним без + и пробелов
location: "Казань, ул. Баумана, 1",
},
{
carNumber: "B456CD",
washTime: "11:00",
orderDate: "2024-10-31",
status: "Завершено",
phone: "+7 900 234 56 78",
location: "Казань, ул. Кремлёвская, 3",
},
];

View File

@ -1,7 +1,10 @@
import React from "react";
import React, { useState } from 'react';
import LayoutArm from '../../components /LayoutArm';
const Page = () => {
return <h1>Arm </h1>;
const [currentPage, setCurrentPage] = useState('orders');
return <LayoutArm currentPage={currentPage} onSelectPage={setCurrentPage} />;
};
export default Page;

5
types.d.ts vendored Normal file
View File

@ -0,0 +1,5 @@
declare interface NodeModule {
hot?: {
accept: (path: string, callback: () => void) => void;
};
}