форма добавления водительских прав
This commit is contained in:
@ -19,4 +19,8 @@ export const updateDictionaryItem = async (directory: string, id: number, data:
|
||||
|
||||
export const deleteDictionaryItem = async (directory: string, id: number) => {
|
||||
await axios.delete(`${API_BASE_URL}/${directory}/${id}`);
|
||||
};
|
||||
|
||||
export const deleteDictionaryItemByName = async (directory: string, id: number) => {
|
||||
await axios.delete(`${API_BASE_URL}/${directory}/id_license=${id}`);
|
||||
};
|
||||
@ -12,7 +12,14 @@ export interface IDriverLicense {
|
||||
form_date: string
|
||||
to_date: string
|
||||
is_actual: boolean
|
||||
categories: IDriverLicenseCategory[]
|
||||
connectionId?: number; // ID связи с водителем
|
||||
categories?: Array<{
|
||||
id: number;
|
||||
name: string;
|
||||
name_short: string;
|
||||
connectionId: number; // ID связи с категорией
|
||||
}>
|
||||
driverId: number
|
||||
}
|
||||
|
||||
export interface IDriverLicenseCategory {
|
||||
|
||||
@ -1,31 +1,46 @@
|
||||
import 'dayjs/locale/ru'
|
||||
import { useState, useEffect } from "react"
|
||||
import { Button, TextInput, Table, ActionIcon, Modal, Checkbox, Tabs, Flex, Transition } from "@mantine/core"
|
||||
import { Button, TextInput, Table, ActionIcon, Modal, Checkbox, Tabs, Flex, Transition, Select, MultiSelect } from "@mantine/core"
|
||||
import { useForm } from "@mantine/form"
|
||||
import { IconEdit, IconLinkPlus, IconPlus, IconTrash } from "@tabler/icons-react"
|
||||
import { fetchDictionary, createDictionaryItem, updateDictionaryItem, deleteDictionaryItem } from "../api/api"
|
||||
import MaskedDateInput from '../components/MaskedDateInput'
|
||||
import { IDriver, IDriverLicense } from '../interfaces/Driver'
|
||||
import { IDriver, IDriverLicense, IDriverLicenseCategory } from '../interfaces/Driver'
|
||||
import { dateToDDMMYYYY, dateToYYYYMMDD } from '../utils/date'
|
||||
import { stringToSnils } from '../utils/format'
|
||||
|
||||
const DriverLicense = ({ license, handleDeleteLicense }: { license: IDriverLicense, handleDeleteLicense: any }) => {
|
||||
const DriverLicense = ({ license, handleDeleteLicense, handleEditLicense }: {
|
||||
license: IDriverLicense,
|
||||
handleDeleteLicense: (id: number) => void,
|
||||
handleEditLicense: (license: IDriverLicense) => void
|
||||
}) => {
|
||||
return (
|
||||
<Table.Tr>
|
||||
<>
|
||||
<Table.Td>{license.series_number}</Table.Td>
|
||||
<Table.Td>{license.form_date}</Table.Td>
|
||||
<Table.Td>{license.to_date}</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>
|
||||
</Table.Tr>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const Driver = ({ driver, handleEditDriver, handleDeleteDriver }: { driver: any, handleEditDriver: any, handleDeleteDriver: any }) => {
|
||||
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 (
|
||||
@ -36,10 +51,10 @@ const Driver = ({ driver, handleEditDriver, handleDeleteDriver }: { driver: any,
|
||||
<Table.Td>{dateToDDMMYYYY(driver.birthday)}</Table.Td>
|
||||
<Table.Td>{driver.iin}</Table.Td>
|
||||
<Table.Td>
|
||||
<ActionIcon color="blue" onClick={() => handleEditDriver(driver)} style={{ marginRight: "10px" }}>
|
||||
<ActionIcon color="blue" onClick={(e) => { e.stopPropagation(); handleEditDriver(driver); }} style={{ marginRight: "10px" }}>
|
||||
<IconEdit />
|
||||
</ActionIcon>
|
||||
<ActionIcon color="red" onClick={() => handleDeleteDriver(driver.id)}>
|
||||
<ActionIcon color="red" onClick={(e) => { e.stopPropagation(); handleDeleteDriver(driver.id); }}>
|
||||
<IconTrash />
|
||||
</ActionIcon>
|
||||
</Table.Td>
|
||||
@ -61,17 +76,23 @@ const Driver = ({ driver, handleEditDriver, handleDeleteDriver }: { driver: any,
|
||||
<Table.Tr style={{ ...transitionStyle }}>
|
||||
<Table.Td colSpan={5}>
|
||||
<Flex justify='space-evenly'>
|
||||
<Button leftSection={<IconPlus />}>Добавить водительские права</Button>
|
||||
<Button leftSection={<IconLinkPlus />}>Привязать водительские права</Button>
|
||||
<Button leftSection={<IconPlus />} onClick={() => handleEditLicense({} as IDriverLicense)}>
|
||||
Добавить водительские права
|
||||
</Button>
|
||||
<Button leftSection={<IconLinkPlus />}>Привязать существующие права</Button>
|
||||
</Flex>
|
||||
</Table.Td>
|
||||
</Table.Tr>
|
||||
</Table.Td>
|
||||
</Table.Tr>
|
||||
)}
|
||||
</Transition>
|
||||
|
||||
{driver.license && Array.isArray(driver.license) && driver.license.map((dr: IDriver) => (
|
||||
{opened && driver.license && Array.isArray(driver.license) && driver.license.map((license: IDriverLicense) => (
|
||||
<Table.Tr>
|
||||
<Table.Td>{JSON.stringify(dr)}</Table.Td>
|
||||
<DriverLicense
|
||||
key={license.id}
|
||||
license={license}
|
||||
handleDeleteLicense={handleDeleteLicense}
|
||||
handleEditLicense={handleEditLicense}
|
||||
/>
|
||||
</Table.Tr>
|
||||
))}
|
||||
</>
|
||||
@ -79,23 +100,27 @@ const Driver = ({ driver, handleEditDriver, handleDeleteDriver }: { driver: any,
|
||||
}
|
||||
|
||||
const DriverForm = () => {
|
||||
const [drivers, setDrivers] = useState<any[]>([])
|
||||
const [licenseCategories, setLicenseCategories] = useState<{ value: string; label: string }[]>([])
|
||||
const [selectedDriver, setSelectedDriver] = useState<any | null>(null)
|
||||
const [selectedLicense, setSelectedLicense] = useState<any | null>(null)
|
||||
const [drivers, setDrivers] = useState<IDriver[]>([])
|
||||
const [licenseCategories, setLicenseCategories] = useState<IDriverLicenseCategory[]>([])
|
||||
const [selectedDriver, setSelectedDriver] = useState<IDriver | null>(null)
|
||||
const [selectedLicense, setSelectedLicense] = useState<IDriverLicense | null>(null)
|
||||
const [modalOpened, setModalOpened] = useState(false)
|
||||
const [licenseModalOpened, setLicenseModalOpened] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
fetchDictionary("driver_license_category").then((data) => {
|
||||
const options = data.map((item: any) => ({
|
||||
value: String(item.id),
|
||||
label: item.name_short, // Берем короткое название (например, "B", "C", "D")
|
||||
}));
|
||||
setLicenseCategories(options);
|
||||
});
|
||||
|
||||
fetchDictionary("driver").then(setDrivers)
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const [categories, driversData] = await Promise.all([
|
||||
fetchDictionary("driver_license_category"),
|
||||
fetchDictionary("driver")
|
||||
]);
|
||||
setLicenseCategories(categories)
|
||||
setDrivers(driversData)
|
||||
} catch (error) {
|
||||
console.error("Error fetching data:", error)
|
||||
}
|
||||
}
|
||||
fetchData()
|
||||
}, [])
|
||||
|
||||
const driverForm = useForm<Partial<IDriver>>({
|
||||
@ -105,113 +130,163 @@ const DriverForm = () => {
|
||||
birthday: "",
|
||||
iin: "",
|
||||
},
|
||||
});
|
||||
})
|
||||
|
||||
const licenseForm = useForm({
|
||||
initialValues: {
|
||||
driverId: "",
|
||||
series_number: "",
|
||||
form_date: "",
|
||||
to_date: "",
|
||||
categories: [],
|
||||
frontPhoto: null,
|
||||
backPhoto: null,
|
||||
is_actual: true,
|
||||
categories: [] as string[],
|
||||
driverId: "",
|
||||
},
|
||||
});
|
||||
})
|
||||
|
||||
const handleAddDriver = async (values: any) => {
|
||||
const date = new Date(Date.parse(values.birthday))
|
||||
const handleAddDriver = async (values: Partial<IDriver>) => {
|
||||
try {
|
||||
const date = new Date(Date.parse(values.birthday || ""))
|
||||
const newValues = {
|
||||
...values,
|
||||
birthday: dateToYYYYMMDD(date),
|
||||
}
|
||||
|
||||
const newValues = {
|
||||
fullname: values.fullname,
|
||||
snils: values.snils,
|
||||
birthday: dateToYYYYMMDD(date),
|
||||
iin: values.iin
|
||||
if (selectedDriver) {
|
||||
const updatedDriver = await updateDictionaryItem("driver", selectedDriver.id, newValues)
|
||||
setDrivers(prev => prev.map(d => d.id === selectedDriver.id ? updatedDriver : d))
|
||||
} else {
|
||||
const newDriver = await createDictionaryItem("driver", newValues)
|
||||
setDrivers(prev => [...prev, newDriver])
|
||||
}
|
||||
setModalOpened(false)
|
||||
driverForm.reset()
|
||||
setSelectedDriver(null)
|
||||
} catch (error) {
|
||||
console.error("Error saving driver:", error)
|
||||
}
|
||||
|
||||
if (selectedDriver) {
|
||||
const updatedDriver = await updateDictionaryItem("driver", selectedDriver.id, newValues);
|
||||
setDrivers((prev) => prev.map((d) => (d.id === selectedDriver.id ? updatedDriver : d)));
|
||||
} else {
|
||||
const newDriver = await createDictionaryItem("driver", newValues);
|
||||
setDrivers((prev) => [...prev, newDriver]);
|
||||
}
|
||||
setModalOpened(false);
|
||||
driverForm.reset();
|
||||
setSelectedDriver(null);
|
||||
};
|
||||
}
|
||||
|
||||
const handleDeleteDriver = async (id: number) => {
|
||||
await deleteDictionaryItem("driver", id)
|
||||
setDrivers((prev) => prev.filter((driver) => driver.id !== id))
|
||||
};
|
||||
try {
|
||||
await deleteDictionaryItem("driver", id)
|
||||
setDrivers(prev => prev.filter(driver => driver.id !== id))
|
||||
} catch (error) {
|
||||
console.error("Error deleting driver:", error)
|
||||
}
|
||||
}
|
||||
|
||||
const handleEditDriver = (driver: any) => {
|
||||
const handleEditDriver = (driver: IDriver) => {
|
||||
driverForm.setValues(driver)
|
||||
setSelectedDriver(driver)
|
||||
setModalOpened(true)
|
||||
}
|
||||
|
||||
const handleAddLicense = async (values: any) => {
|
||||
const handleAddLicense = async (values: any) => {
|
||||
try {
|
||||
const licenseData = {
|
||||
series_number: values.series_number,
|
||||
form_date: dateToYYYYMMDD(values.form_date),
|
||||
to_date: dateToYYYYMMDD(values.to_date),
|
||||
is_actual: values.is_actual,
|
||||
};
|
||||
|
||||
if (selectedLicense) {
|
||||
const updatedLicense = await updateDictionaryItem("driver_license", selectedLicense.id, values);
|
||||
setDrivers((prev) =>
|
||||
prev.map((driver) =>
|
||||
driver.id === values.driverId
|
||||
? {
|
||||
...driver,
|
||||
license: driver.license.map((lic: IDriverLicense) => (lic.id === selectedLicense.id ? updatedLicense : lic)),
|
||||
}
|
||||
: driver
|
||||
// 1. Обновляем данные прав
|
||||
const updatedLicense = await updateDictionaryItem("driver_license", selectedLicense.id, licenseData);
|
||||
|
||||
// 2. Для редактирования просто создаем новые связи (старые должны удаляться клиентом)
|
||||
await Promise.all(
|
||||
values.categories.map((categoryId: string) =>
|
||||
createDictionaryItem("driver_license_connection", {
|
||||
driver_license_id: selectedLicense.id,
|
||||
driver_license_category_id: parseInt(categoryId),
|
||||
})
|
||||
)
|
||||
);
|
||||
} else {
|
||||
const newLicense = await createDictionaryItem("driver_license", values)
|
||||
// const updatedDriver = await updateDictionaryItem("driver_connection", {
|
||||
// driver_id: values.driverId,
|
||||
// driver_license_id: newLicense.id,
|
||||
// })
|
||||
|
||||
setDrivers((prev) =>
|
||||
prev.map((driver) =>
|
||||
driver.id === values.driverId
|
||||
? { ...driver, license: [...(driver.license || []), newLicense] }
|
||||
: driver
|
||||
// 3. Обновляем связь с водителем
|
||||
await createDictionaryItem("driver_connection", {
|
||||
driver_id: parseInt(values.driverId),
|
||||
driver_license_id: selectedLicense.id,
|
||||
});
|
||||
} else {
|
||||
// 1. Создаем новые права
|
||||
const newLicense = await createDictionaryItem("driver_license", licenseData);
|
||||
|
||||
// 2. Создаем связи категорий
|
||||
await Promise.all(
|
||||
values.categories.map((categoryId: string) =>
|
||||
createDictionaryItem("driver_license_connection", {
|
||||
driver_license_id: newLicense.id,
|
||||
driver_license_category_id: parseInt(categoryId),
|
||||
})
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
// 3. Создаем связь с водителем
|
||||
await createDictionaryItem("driver_connection", {
|
||||
driver_id: parseInt(values.driverId),
|
||||
driver_license_id: newLicense.id,
|
||||
});
|
||||
}
|
||||
|
||||
// Обновляем данные
|
||||
const updatedDrivers = await fetchDictionary("driver");
|
||||
setDrivers(updatedDrivers);
|
||||
|
||||
setLicenseModalOpened(false);
|
||||
licenseForm.reset();
|
||||
setSelectedLicense(null);
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error saving license:", error);
|
||||
}
|
||||
};
|
||||
|
||||
const handleDeleteLicense = async (licenseId: number) => {
|
||||
const handleEditLicense = (license: IDriverLicense) => {
|
||||
// Находим водителя по имеющимся данным (без запроса к API)
|
||||
const driverWithLicense = drivers.find(d =>
|
||||
d.license?.some((l: IDriverLicense) => l.id === license.id)
|
||||
);
|
||||
|
||||
licenseForm.setValues({
|
||||
series_number: license.series_number,
|
||||
form_date: license.form_date,
|
||||
to_date: license.to_date,
|
||||
is_actual: license.is_actual,
|
||||
categories: license.categories?.map(c => c.id.toString()) || [],
|
||||
driverId: driverWithLicense?.id.toString() || "",
|
||||
});
|
||||
setSelectedLicense(license);
|
||||
setLicenseModalOpened(true);
|
||||
};
|
||||
|
||||
const handleDeleteLicense = async (licenseId: number) => {
|
||||
try {
|
||||
// Удаляем только сами права (связи должны удаляться автоматически на сервере)
|
||||
await deleteDictionaryItem("driver_license", licenseId);
|
||||
setDrivers((prev) =>
|
||||
prev.map((driver) =>
|
||||
({ ...driver, license: driver.license.filter((lic: IDriverLicense) => lic.id !== licenseId) })
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
// Обновляем данные
|
||||
const updatedDrivers = await fetchDictionary("driver");
|
||||
setDrivers(updatedDrivers);
|
||||
} catch (error) {
|
||||
console.error("Error deleting license:", error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Tabs defaultValue='drivers'>
|
||||
<Tabs.List>
|
||||
<Tabs.Tab value='drivers'>
|
||||
Водители
|
||||
</Tabs.Tab>
|
||||
|
||||
<Tabs.Tab value='licenses'>
|
||||
Водительские права
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab value='drivers'>Водители</Tabs.Tab>
|
||||
<Tabs.Tab value='licenses'>Водительские права</Tabs.Tab>
|
||||
</Tabs.List>
|
||||
|
||||
<Tabs.Panel value='drivers'>
|
||||
<h2>Водители</h2>
|
||||
<Button onClick={() => setModalOpened(true)}>Добавить водителя</Button>
|
||||
<Table striped highlightOnHover withColumnBorders mt="md">
|
||||
<Button onClick={() => { setSelectedDriver(null); driverForm.reset(); setModalOpened(true); }}>
|
||||
Добавить водителя
|
||||
</Button>
|
||||
<Table highlightOnHover withColumnBorders mt="md">
|
||||
<Table.Thead>
|
||||
<Table.Tr>
|
||||
<Table.Th>ФИО</Table.Th>
|
||||
@ -223,7 +298,14 @@ const DriverForm = () => {
|
||||
</Table.Thead>
|
||||
<Table.Tbody>
|
||||
{drivers.map((driver) => (
|
||||
<Driver key={driver.id} driver={driver} handleEditDriver={handleEditDriver} handleDeleteDriver={handleDeleteDriver} />
|
||||
<Driver
|
||||
key={driver.id}
|
||||
driver={driver}
|
||||
handleEditDriver={handleEditDriver}
|
||||
handleDeleteDriver={handleDeleteDriver}
|
||||
handleEditLicense={handleEditLicense}
|
||||
handleDeleteLicense={handleDeleteLicense}
|
||||
/>
|
||||
))}
|
||||
</Table.Tbody>
|
||||
</Table>
|
||||
@ -231,8 +313,10 @@ const DriverForm = () => {
|
||||
|
||||
<Tabs.Panel value='licenses'>
|
||||
<h2>Водительские права</h2>
|
||||
<Button onClick={() => setLicenseModalOpened(true)}>Добавить права</Button>
|
||||
<Table striped highlightOnHover withColumnBorders mt="md">
|
||||
<Button onClick={() => { setSelectedLicense(null); licenseForm.reset(); setLicenseModalOpened(true); }}>
|
||||
Добавить права
|
||||
</Button>
|
||||
<Table highlightOnHover withColumnBorders mt="md">
|
||||
<Table.Thead>
|
||||
<Table.Tr>
|
||||
<Table.Th>ФИО</Table.Th>
|
||||
@ -244,9 +328,16 @@ const DriverForm = () => {
|
||||
</Table.Tr>
|
||||
</Table.Thead>
|
||||
<Table.Tbody>
|
||||
{drivers.flatMap((driver) =>
|
||||
driver.license?.map((license: IDriverLicense) => (
|
||||
<DriverLicense license={license} handleDeleteLicense={handleDeleteLicense} />
|
||||
{drivers.flatMap(driver =>
|
||||
driver.license?.map(license => (
|
||||
<Table.Tr key={license.id}>
|
||||
<Table.Td>{driver.fullname}</Table.Td>
|
||||
<DriverLicense
|
||||
license={license}
|
||||
handleDeleteLicense={handleDeleteLicense}
|
||||
handleEditLicense={handleEditLicense}
|
||||
/>
|
||||
</Table.Tr>
|
||||
))
|
||||
)}
|
||||
</Table.Tbody>
|
||||
@ -254,7 +345,7 @@ const DriverForm = () => {
|
||||
</Tabs.Panel>
|
||||
</Tabs>
|
||||
|
||||
<Modal opened={modalOpened} onClose={() => setModalOpened(false)} title="Добавить / Редактировать водителя">
|
||||
<Modal opened={modalOpened} onClose={() => setModalOpened(false)} title={selectedDriver ? "Редактировать водителя" : "Добавить водителя"}>
|
||||
<form onSubmit={driverForm.onSubmit(handleAddDriver)}>
|
||||
<TextInput label="ФИО" {...driverForm.getInputProps("fullname")} required />
|
||||
<TextInput label="СНИЛС" {...driverForm.getInputProps("snils")} required />
|
||||
@ -271,27 +362,69 @@ const DriverForm = () => {
|
||||
</form>
|
||||
</Modal>
|
||||
|
||||
<Modal opened={licenseModalOpened} onClose={() => setLicenseModalOpened(false)} title="Добавить / Редактировать водительские права">
|
||||
<Modal
|
||||
opened={licenseModalOpened}
|
||||
onClose={() => {
|
||||
setLicenseModalOpened(false)
|
||||
licenseForm.reset()
|
||||
setSelectedLicense(null)
|
||||
}}
|
||||
title={selectedLicense ? "Редактировать водительские права" : "Добавить водительские права"}
|
||||
size="md"
|
||||
>
|
||||
<form onSubmit={licenseForm.onSubmit(handleAddLicense)}>
|
||||
<TextInput label="Серия и номер" {...licenseForm.getInputProps("series_number")} required />
|
||||
<MaskedDateInput
|
||||
label="Дата выдачи"
|
||||
<Select
|
||||
label="Водитель"
|
||||
value={licenseForm.values.driverId}
|
||||
onChange={(value) => licenseForm.setFieldValue('driverId', value || '')}
|
||||
data={drivers.map(driver => ({
|
||||
value: driver.id.toString(),
|
||||
label: driver.fullname,
|
||||
}))}
|
||||
required
|
||||
{...licenseForm.getInputProps("form_date")}
|
||||
/>
|
||||
<MaskedDateInput
|
||||
label="Срок действия"
|
||||
<TextInput
|
||||
label="Серия и номер"
|
||||
{...licenseForm.getInputProps("series_number")}
|
||||
required
|
||||
mt="sm"
|
||||
/>
|
||||
<Flex gap="sm" mt="sm" justify="center">
|
||||
<MaskedDateInput
|
||||
label="Дата выдачи"
|
||||
required
|
||||
flex={1}
|
||||
{...licenseForm.getInputProps("form_date")}
|
||||
/>
|
||||
<MaskedDateInput
|
||||
label="Срок действия"
|
||||
required
|
||||
flex={1}
|
||||
{...licenseForm.getInputProps("to_date")}
|
||||
/>
|
||||
</Flex>
|
||||
<Checkbox
|
||||
label="Действующее"
|
||||
{...licenseForm.getInputProps("is_actual", { type: 'checkbox' })}
|
||||
mt="sm"
|
||||
/>
|
||||
<MultiSelect
|
||||
label="Категории"
|
||||
data={licenseCategories.map(category => ({
|
||||
value: category.id.toString(),
|
||||
label: `${category.name_short} (${category.name})`,
|
||||
}))}
|
||||
{...licenseForm.getInputProps("categories")}
|
||||
required
|
||||
{...licenseForm.getInputProps("to_date")}
|
||||
mt="sm"
|
||||
/>
|
||||
<Checkbox defaultChecked label="Действующее" {...licenseForm.getInputProps("is_actual")} style={{ marginTop: "10px" }} />
|
||||
<Button fullWidth mt="md" type="submit">
|
||||
Сохранить
|
||||
</Button>
|
||||
</form>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
export default DriverForm;
|
||||
export default DriverForm
|
||||
Reference in New Issue
Block a user