Merge branch 'master' of https://git.jkhsakha.ru/HotamovFO/Web-Fuel
This commit is contained in:
76
src/components/Forms/Driver.tsx
Normal file
76
src/components/Forms/Driver.tsx
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
import { useState } from "react";
|
||||||
|
import { IDriver, IDriverLicense } from "../../interfaces/Driver";
|
||||||
|
import { ActionIcon, Button, Flex, Table, Transition } from "@mantine/core";
|
||||||
|
import { stringToSnils } from "../../utils/format";
|
||||||
|
import { dateToDDMMYYYY } from "../../utils/date";
|
||||||
|
import { IconEdit, IconLinkPlus, IconPlus, IconTrash } from "@tabler/icons-react";
|
||||||
|
import DriverLicense from "./DriverLicense";
|
||||||
|
|
||||||
|
interface IDriverProps {
|
||||||
|
driver: IDriver,
|
||||||
|
handleEditDriver: (driver: IDriver) => void,
|
||||||
|
handleDeleteDriver: (id: number) => void,
|
||||||
|
handleEditLicense: (license: IDriverLicense) => void,
|
||||||
|
handleDeleteLicense: (id: number) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const Driver = ({ driver, handleEditDriver, handleDeleteDriver, handleEditLicense, handleDeleteLicense }: IDriverProps) => {
|
||||||
|
const [opened, setOpened] = useState(false)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Table.Tr style={{ cursor: 'pointer' }} onClick={() => setOpened(!opened)}>
|
||||||
|
<Table.Td>{driver.fullname}</Table.Td>
|
||||||
|
<Table.Td>{stringToSnils(driver.snils)}</Table.Td>
|
||||||
|
<Table.Td>{dateToDDMMYYYY(driver.birthday)}</Table.Td>
|
||||||
|
<Table.Td>{driver.iin}</Table.Td>
|
||||||
|
<Table.Td>
|
||||||
|
<ActionIcon color="blue" onClick={(e) => { e.stopPropagation(); handleEditDriver(driver); }} style={{ marginRight: "10px" }}>
|
||||||
|
<IconEdit />
|
||||||
|
</ActionIcon>
|
||||||
|
<ActionIcon color="red" onClick={(e) => { e.stopPropagation(); handleDeleteDriver(driver.id); }}>
|
||||||
|
<IconTrash />
|
||||||
|
</ActionIcon>
|
||||||
|
</Table.Td>
|
||||||
|
</Table.Tr>
|
||||||
|
|
||||||
|
<Transition
|
||||||
|
mounted={opened}
|
||||||
|
transition={{
|
||||||
|
in: { opacity: 1, transform: 'scaleY(1)' },
|
||||||
|
out: { opacity: 0, transform: 'scaleY(0)' },
|
||||||
|
common: { transformOrigin: 'top' },
|
||||||
|
transitionProperty: 'transform, opacity',
|
||||||
|
}}
|
||||||
|
duration={200}
|
||||||
|
timingFunction='ease'
|
||||||
|
keepMounted
|
||||||
|
>
|
||||||
|
{(transitionStyle) => (
|
||||||
|
<Table.Tr style={{ ...transitionStyle }}>
|
||||||
|
<Table.Td colSpan={5}>
|
||||||
|
<Flex justify='space-evenly'>
|
||||||
|
<Button leftSection={<IconPlus />} onClick={() => handleEditLicense({} as IDriverLicense)}>
|
||||||
|
Добавить водительские права
|
||||||
|
</Button>
|
||||||
|
<Button leftSection={<IconLinkPlus />}>Привязать существующие права</Button>
|
||||||
|
</Flex>
|
||||||
|
</Table.Td>
|
||||||
|
</Table.Tr>
|
||||||
|
)}
|
||||||
|
</Transition>
|
||||||
|
{opened && driver.license && Array.isArray(driver.license) && driver.license.map((license: IDriverLicense, index) => (
|
||||||
|
<Table.Tr>
|
||||||
|
<DriverLicense
|
||||||
|
key={`licId-${license.id}-${index}`}
|
||||||
|
license={license}
|
||||||
|
handleDeleteLicense={handleDeleteLicense}
|
||||||
|
handleEditLicense={handleEditLicense}
|
||||||
|
/>
|
||||||
|
</Table.Tr>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Driver
|
||||||
29
src/components/Forms/DriverLicense.tsx
Normal file
29
src/components/Forms/DriverLicense.tsx
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import { ActionIcon, Table } from "@mantine/core"
|
||||||
|
import { IDriverLicense } from "../../interfaces/Driver"
|
||||||
|
import { dateToDDMMYYYY } from "../../utils/date"
|
||||||
|
import { IconEdit, IconTrash } from "@tabler/icons-react"
|
||||||
|
|
||||||
|
const DriverLicense = ({ license, handleDeleteLicense, handleEditLicense }: {
|
||||||
|
license: IDriverLicense,
|
||||||
|
handleDeleteLicense: (id: number) => void,
|
||||||
|
handleEditLicense: (license: IDriverLicense) => void
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Table.Td>{license.series_number}</Table.Td>
|
||||||
|
<Table.Td>{dateToDDMMYYYY(license.form_date)}</Table.Td>
|
||||||
|
<Table.Td>{dateToDDMMYYYY(license.to_date)}</Table.Td>
|
||||||
|
<Table.Td>{license.categories?.map((c) => c.name_short).join(", ")}</Table.Td>
|
||||||
|
<Table.Td>
|
||||||
|
<ActionIcon color="blue" onClick={() => handleEditLicense(license)} style={{ marginRight: "10px" }}>
|
||||||
|
<IconEdit />
|
||||||
|
</ActionIcon>
|
||||||
|
<ActionIcon color="red" onClick={() => handleDeleteLicense(license.id)}>
|
||||||
|
<IconTrash />
|
||||||
|
</ActionIcon>
|
||||||
|
</Table.Td>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DriverLicense
|
||||||
@ -4,6 +4,7 @@ export interface IDriver {
|
|||||||
snils: string
|
snils: string
|
||||||
birthday: string
|
birthday: string
|
||||||
iin: string
|
iin: string
|
||||||
|
license?: IDriverLicense[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IDriverLicense {
|
export interface IDriverLicense {
|
||||||
@ -27,3 +28,18 @@ export interface IDriverLicenseCategory {
|
|||||||
name: string
|
name: string
|
||||||
name_short: string
|
name_short: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IDriverConnection {
|
||||||
|
driver_id: number
|
||||||
|
driver_license_id: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IDriverLicenseConnection {
|
||||||
|
id: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IDriverLicenseCategoryConnection {
|
||||||
|
id: number
|
||||||
|
driver_license_id: number
|
||||||
|
driver_license_category_id: number
|
||||||
|
}
|
||||||
@ -75,13 +75,17 @@ const Dictionaries = () => {
|
|||||||
<Select
|
<Select
|
||||||
data={dictionaryOptions}
|
data={dictionaryOptions}
|
||||||
value={selectedDictionary}
|
value={selectedDictionary}
|
||||||
onChange={setSelectedDictionary}
|
onChange={(value) => {
|
||||||
|
if (value) {
|
||||||
|
setSelectedDictionary(value)
|
||||||
|
}
|
||||||
|
}}
|
||||||
allowDeselect={false}
|
allowDeselect={false}
|
||||||
searchable
|
searchable
|
||||||
nothingFoundMessage="Не найдено..."
|
nothingFoundMessage="Не найдено..."
|
||||||
maxDropdownHeight={300}
|
maxDropdownHeight={300}
|
||||||
/><br/>
|
/><br />
|
||||||
<Button onClick={() => { setCurrentItem(null); setModalOpened(true); }}>Добавить</Button><br/><br/>
|
<Button onClick={() => { setCurrentItem(null); setModalOpened(true); }}>Добавить</Button><br /><br />
|
||||||
|
|
||||||
<DictionaryTable
|
<DictionaryTable
|
||||||
data={data}
|
data={data}
|
||||||
|
|||||||
@ -1,103 +1,13 @@
|
|||||||
import 'dayjs/locale/ru'
|
import 'dayjs/locale/ru'
|
||||||
import { useState, useEffect } from "react"
|
import { useState, useEffect } from "react"
|
||||||
import { Button, TextInput, Table, ActionIcon, Modal, Checkbox, Tabs, Flex, Transition, Select, MultiSelect } from "@mantine/core"
|
import { Button, TextInput, Table, Modal, Checkbox, Tabs, Flex, Select, MultiSelect } from "@mantine/core"
|
||||||
import { useForm } from "@mantine/form"
|
import { useForm } from "@mantine/form"
|
||||||
import { IconEdit, IconLinkPlus, IconPlus, IconTrash } from "@tabler/icons-react"
|
|
||||||
import { fetchDictionary, createDictionaryItem, updateDictionaryItem, deleteDictionaryItem } from "../api/api"
|
import { fetchDictionary, createDictionaryItem, updateDictionaryItem, deleteDictionaryItem } from "../api/api"
|
||||||
import MaskedDateInput from '../components/MaskedDateInput'
|
import MaskedDateInput from '../components/MaskedDateInput'
|
||||||
import { IDriver, IDriverLicense, IDriverLicenseCategory } from '../interfaces/Driver'
|
import { IDriver, IDriverLicense, IDriverLicenseCategory } from '../interfaces/Driver'
|
||||||
import { dateToDDMMYYYY, dateToYYYYMMDD } from '../utils/date'
|
import { dateToYYYYMMDD } from '../utils/date'
|
||||||
import { stringToSnils } from '../utils/format'
|
import DriverLicense from '../components/Forms/DriverLicense'
|
||||||
|
import Driver from '../components/Forms/Driver'
|
||||||
const DriverLicense = ({ license, handleDeleteLicense, handleEditLicense }: {
|
|
||||||
license: IDriverLicense,
|
|
||||||
handleDeleteLicense: (id: number) => void,
|
|
||||||
handleEditLicense: (license: IDriverLicense) => void
|
|
||||||
}) => {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Table.Td>{license.series_number}</Table.Td>
|
|
||||||
<Table.Td>{dateToDDMMYYYY(license.form_date)}</Table.Td>
|
|
||||||
<Table.Td>{dateToDDMMYYYY(license.to_date)}</Table.Td>
|
|
||||||
<Table.Td>{license.categories?.map((c) => c.name_short).join(", ")}</Table.Td>
|
|
||||||
<Table.Td>
|
|
||||||
<ActionIcon color="blue" onClick={() => handleEditLicense(license)} style={{ marginRight: "10px" }}>
|
|
||||||
<IconEdit />
|
|
||||||
</ActionIcon>
|
|
||||||
<ActionIcon color="red" onClick={() => handleDeleteLicense(license.id)}>
|
|
||||||
<IconTrash />
|
|
||||||
</ActionIcon>
|
|
||||||
</Table.Td>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IDriverProps {
|
|
||||||
driver: IDriver,
|
|
||||||
handleEditDriver: (driver: IDriver) => void,
|
|
||||||
handleDeleteDriver: (id: number) => void,
|
|
||||||
handleEditLicense: (license: IDriverLicense) => void,
|
|
||||||
handleDeleteLicense: (id: number) => void
|
|
||||||
}
|
|
||||||
|
|
||||||
const Driver = ({ driver, handleEditDriver, handleDeleteDriver, handleEditLicense, handleDeleteLicense }: IDriverProps) => {
|
|
||||||
const [opened, setOpened] = useState(false)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Table.Tr style={{ cursor: 'pointer' }} onClick={() => setOpened(!opened)}>
|
|
||||||
<Table.Td>{driver.fullname}</Table.Td>
|
|
||||||
<Table.Td>{stringToSnils(driver.snils)}</Table.Td>
|
|
||||||
<Table.Td>{dateToDDMMYYYY(driver.birthday)}</Table.Td>
|
|
||||||
<Table.Td>{driver.iin}</Table.Td>
|
|
||||||
<Table.Td>
|
|
||||||
<ActionIcon color="blue" onClick={(e) => { e.stopPropagation(); handleEditDriver(driver); }} style={{ marginRight: "10px" }}>
|
|
||||||
<IconEdit />
|
|
||||||
</ActionIcon>
|
|
||||||
<ActionIcon color="red" onClick={(e) => { e.stopPropagation(); handleDeleteDriver(driver.id); }}>
|
|
||||||
<IconTrash />
|
|
||||||
</ActionIcon>
|
|
||||||
</Table.Td>
|
|
||||||
</Table.Tr>
|
|
||||||
|
|
||||||
<Transition
|
|
||||||
mounted={opened}
|
|
||||||
transition={{
|
|
||||||
in: { opacity: 1, transform: 'scaleY(1)' },
|
|
||||||
out: { opacity: 0, transform: 'scaleY(0)' },
|
|
||||||
common: { transformOrigin: 'top' },
|
|
||||||
transitionProperty: 'transform, opacity',
|
|
||||||
}}
|
|
||||||
duration={200}
|
|
||||||
timingFunction='ease'
|
|
||||||
keepMounted
|
|
||||||
>
|
|
||||||
{(transitionStyle) => (
|
|
||||||
<Table.Tr style={{ ...transitionStyle }}>
|
|
||||||
<Table.Td colSpan={5}>
|
|
||||||
<Flex justify='space-evenly'>
|
|
||||||
<Button leftSection={<IconPlus />} onClick={() => handleEditLicense({} as IDriverLicense)}>
|
|
||||||
Добавить водительские права
|
|
||||||
</Button>
|
|
||||||
<Button leftSection={<IconLinkPlus />}>Привязать существующие права</Button>
|
|
||||||
</Flex>
|
|
||||||
</Table.Td>
|
|
||||||
</Table.Tr>
|
|
||||||
)}
|
|
||||||
</Transition>
|
|
||||||
{opened && driver.license && Array.isArray(driver.license) && driver.license.map((license: IDriverLicense) => (
|
|
||||||
<Table.Tr>
|
|
||||||
<DriverLicense
|
|
||||||
key={license.id}
|
|
||||||
license={license}
|
|
||||||
handleDeleteLicense={handleDeleteLicense}
|
|
||||||
handleEditLicense={handleEditLicense}
|
|
||||||
/>
|
|
||||||
</Table.Tr>
|
|
||||||
))}
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const DriverForm = () => {
|
const DriverForm = () => {
|
||||||
const [drivers, setDrivers] = useState<IDriver[]>([])
|
const [drivers, setDrivers] = useState<IDriver[]>([])
|
||||||
@ -181,7 +91,7 @@ const DriverForm = () => {
|
|||||||
setModalOpened(true)
|
setModalOpened(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleAddLicense = async (values: any) => {
|
const handleAddLicense = async (values: any) => {
|
||||||
try {
|
try {
|
||||||
const licenseData = {
|
const licenseData = {
|
||||||
series_number: values.series_number,
|
series_number: values.series_number,
|
||||||
@ -240,9 +150,9 @@ const handleAddLicense = async (values: any) => {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error saving license:", error);
|
console.error("Error saving license:", error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleEditLicense = (license: IDriverLicense) => {
|
const handleEditLicense = (license: IDriverLicense) => {
|
||||||
// Находим водителя по имеющимся данным (без запроса к API)
|
// Находим водителя по имеющимся данным (без запроса к API)
|
||||||
const driverWithLicense = drivers.find(d =>
|
const driverWithLicense = drivers.find(d =>
|
||||||
d.license?.some((l: IDriverLicense) => l.id === license.id)
|
d.license?.some((l: IDriverLicense) => l.id === license.id)
|
||||||
@ -258,9 +168,9 @@ const handleEditLicense = (license: IDriverLicense) => {
|
|||||||
});
|
});
|
||||||
setSelectedLicense(license);
|
setSelectedLicense(license);
|
||||||
setLicenseModalOpened(true);
|
setLicenseModalOpened(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDeleteLicense = async (licenseId: number) => {
|
const handleDeleteLicense = async (licenseId: number) => {
|
||||||
try {
|
try {
|
||||||
// Удаляем только сами права (связи должны удаляться автоматически на сервере)
|
// Удаляем только сами права (связи должны удаляться автоматически на сервере)
|
||||||
await deleteDictionaryItem("driver_license", licenseId);
|
await deleteDictionaryItem("driver_license", licenseId);
|
||||||
@ -271,7 +181,7 @@ const handleDeleteLicense = async (licenseId: number) => {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error deleting license:", error);
|
console.error("Error deleting license:", error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
@ -297,9 +207,9 @@ const handleDeleteLicense = async (licenseId: number) => {
|
|||||||
</Table.Tr>
|
</Table.Tr>
|
||||||
</Table.Thead>
|
</Table.Thead>
|
||||||
<Table.Tbody>
|
<Table.Tbody>
|
||||||
{drivers.map((driver) => (
|
{drivers.map((driver, index) => (
|
||||||
<Driver
|
<Driver
|
||||||
key={driver.id}
|
key={`drvId-${driver.id}-${index}`}
|
||||||
driver={driver}
|
driver={driver}
|
||||||
handleEditDriver={handleEditDriver}
|
handleEditDriver={handleEditDriver}
|
||||||
handleDeleteDriver={handleDeleteDriver}
|
handleDeleteDriver={handleDeleteDriver}
|
||||||
@ -329,8 +239,8 @@ const handleDeleteLicense = async (licenseId: number) => {
|
|||||||
</Table.Thead>
|
</Table.Thead>
|
||||||
<Table.Tbody>
|
<Table.Tbody>
|
||||||
{drivers.flatMap(driver =>
|
{drivers.flatMap(driver =>
|
||||||
driver.license?.map(license => (
|
driver.license?.map((license: IDriverLicense, index) => (
|
||||||
<Table.Tr key={license.id}>
|
<Table.Tr key={`licId-${license.id}-${index}`}>
|
||||||
<Table.Td>{driver.fullname}</Table.Td>
|
<Table.Td>{driver.fullname}</Table.Td>
|
||||||
<DriverLicense
|
<DriverLicense
|
||||||
license={license}
|
license={license}
|
||||||
@ -393,13 +303,11 @@ const handleDeleteLicense = async (licenseId: number) => {
|
|||||||
<MaskedDateInput
|
<MaskedDateInput
|
||||||
label="Дата выдачи"
|
label="Дата выдачи"
|
||||||
required
|
required
|
||||||
flex={1}
|
|
||||||
{...licenseForm.getInputProps("form_date")}
|
{...licenseForm.getInputProps("form_date")}
|
||||||
/>
|
/>
|
||||||
<MaskedDateInput
|
<MaskedDateInput
|
||||||
label="Срок действия"
|
label="Срок действия"
|
||||||
required
|
required
|
||||||
flex={1}
|
|
||||||
{...licenseForm.getInputProps("to_date")}
|
{...licenseForm.getInputProps("to_date")}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|||||||
Reference in New Issue
Block a user