feat: make layout, form, order pages and wizards (#6) #13
1
.gitignore
vendored
1
.gitignore
vendored
@ -130,3 +130,4 @@ dist
|
||||
.yarn/install-state.gz
|
||||
.pnp.*
|
||||
|
||||
.idea
|
8
.prettierrc.json
Normal file
8
.prettierrc.json
Normal 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
1767
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
11
package.json
11
package.json
@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "dry-wash",
|
||||
"name": "dry-wash-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",
|
||||
primakov
commented
@types/react @types/react
|
||||
"prettier": "3.3.3"
|
||||
}
|
||||
}
|
||||
|
15
src/app.tsx
15
src/app.tsx
@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
|
17
src/components /LayoutArm/LayoutArm.tsx
Normal file
17
src/components /LayoutArm/LayoutArm.tsx
Normal 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 />}
|
||||
primakov
commented
Перевести на router Перевести на router
|
||||
{currentPage === 'masters' && <Masters />}
|
||||
</Box>
|
||||
</Flex>
|
||||
);
|
||||
|
||||
export default LayoutArm;
|
1
src/components /LayoutArm/index.ts
Normal file
1
src/components /LayoutArm/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { default } from './LayoutArm';
|
24
src/components /MasterActionsMenu/MasterActionsMenu.tsx
Normal file
24
src/components /MasterActionsMenu/MasterActionsMenu.tsx
Normal 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;
|
1
src/components /MasterActionsMenu/index.ts
Normal file
1
src/components /MasterActionsMenu/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { default } from './MasterActionsMenu';
|
28
src/components /MasterItem/MasterItem.tsx
Normal file
28
src/components /MasterItem/MasterItem.tsx
Normal 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;
|
1
src/components /MasterItem/index.ts
Normal file
1
src/components /MasterItem/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { default } from './MasterItem';
|
65
src/components /MasterModal/MasterDrawer.tsx
Normal file
65
src/components /MasterModal/MasterDrawer.tsx
Normal 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;
|
1
src/components /MasterModal/index.ts
Normal file
1
src/components /MasterModal/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { default } from './MasterDrawer';
|
50
src/components /Masters/Masters.tsx
Normal file
50
src/components /Masters/Masters.tsx
Normal 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;
|
1
src/components /Masters/index.ts
Normal file
1
src/components /Masters/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { default } from './Masters';
|
40
src/components /OrderItem/OrderItem.tsx
Normal file
40
src/components /OrderItem/OrderItem.tsx
Normal 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;
|
1
src/components /OrderItem/index.ts
Normal file
1
src/components /OrderItem/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { default } from './OrderItem';
|
38
src/components /Orders/Orders.tsx
Normal file
38
src/components /Orders/Orders.tsx
Normal 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;
|
1
src/components /Orders/index.ts
Normal file
1
src/components /Orders/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { default } from './Orders';
|
42
src/components /Sidebar/Sidebar.tsx
Normal file
42
src/components /Sidebar/Sidebar.tsx
Normal 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;
|
1
src/components /Sidebar/index.ts
Normal file
1
src/components /Sidebar/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { default } from './Sidebar';
|
@ -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
31
src/mocks /index.ts
Normal file
@ -0,0 +1,31 @@
|
||||
export const mastersData = [
|
||||
{
|
||||
name: "Иван Иванов",
|
||||
primakov
commented
Добавить 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: "В процессе",
|
||||
primakov
commented
Переделать на строку на английском Переделать на строку на английском
|
||||
phone: "+7 900 123 45 67",
|
||||
primakov
commented
Храним без + и пробелов Храним без + и пробелов
|
||||
location: "Казань, ул. Баумана, 1",
|
||||
},
|
||||
{
|
||||
carNumber: "B456CD",
|
||||
washTime: "11:00",
|
||||
orderDate: "2024-10-31",
|
||||
status: "Завершено",
|
||||
phone: "+7 900 234 56 78",
|
||||
location: "Казань, ул. Кремлёвская, 3",
|
||||
},
|
||||
];
|
@ -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
5
types.d.ts
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
declare interface NodeModule {
|
||||
hot?: {
|
||||
accept: (path: string, callback: () => void) => void;
|
||||
};
|
||||
}
|
Loading…
Reference in New Issue
Block a user
Надо бы убрать -pl