начал разделять компоненты и логику в отдельные файлы
This commit is contained in:
parent
a1cf2f7c88
commit
a5a6d1d7fa
21
web-app/src/Api/authApi.js
Normal file
21
web-app/src/Api/authApi.js
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import {createApi, fetchBaseQuery} from "@reduxjs/toolkit/query/react";
|
||||||
|
import CONFIG from "../Core/сonfig.js";
|
||||||
|
|
||||||
|
|
||||||
|
export const authApi = createApi({
|
||||||
|
reducerPath: 'authApi',
|
||||||
|
baseQuery: fetchBaseQuery({
|
||||||
|
baseUrl: CONFIG.BASE_URL,
|
||||||
|
}),
|
||||||
|
endpoints: (builder) => ({
|
||||||
|
login: builder.mutation({
|
||||||
|
query: (credentials) => ({
|
||||||
|
url: '/auth/login/',
|
||||||
|
method: 'POST',
|
||||||
|
body: credentials
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const {useLoginMutation} = authApi;
|
||||||
@ -15,7 +15,7 @@ export const lensIssuesApi = createApi({
|
|||||||
tagTypes: ['LensIssues'],
|
tagTypes: ['LensIssues'],
|
||||||
endpoints: (builder) => ({
|
endpoints: (builder) => ({
|
||||||
getLensIssues: builder.query({
|
getLensIssues: builder.query({
|
||||||
query: () => '/LensIssues/',
|
query: () => '/lens_issues/',
|
||||||
providesTags: ['LensIssues'],
|
providesTags: ['LensIssues'],
|
||||||
refetchOnMountOrArgChange: 5
|
refetchOnMountOrArgChange: 5
|
||||||
}),
|
}),
|
||||||
|
|||||||
22
web-app/src/Api/lensTypesApi.js
Normal file
22
web-app/src/Api/lensTypesApi.js
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import CONFIG from "../Core/сonfig.js";
|
||||||
|
import {createApi, fetchBaseQuery} from "@reduxjs/toolkit/query/react";
|
||||||
|
|
||||||
|
|
||||||
|
export const lensTypesApi = createApi({
|
||||||
|
reducerPath: 'lensTypesApi',
|
||||||
|
baseQuery: fetchBaseQuery({
|
||||||
|
baseUrl: CONFIG.BASE_URL,
|
||||||
|
prepareHeaders: (headers) => {
|
||||||
|
const token = localStorage.getItem('access_token');
|
||||||
|
if (token) headers.set('Authorization', `Bearer ${token}`);
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
endpoints: (builder) => ({
|
||||||
|
getLensTypes: builder.query({
|
||||||
|
query: () => `/lens_types/`,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const {useGetLensTypesQuery} = lensTypesApi;
|
||||||
@ -15,7 +15,7 @@ export const lensesApi = createApi({
|
|||||||
tagTypes: ['Lens'],
|
tagTypes: ['Lens'],
|
||||||
endpoints: (builder) => ({
|
endpoints: (builder) => ({
|
||||||
getLenses: builder.query({
|
getLenses: builder.query({
|
||||||
query: () => '/Lenses/',
|
query: () => '/lenses/',
|
||||||
providesTags: ['Lens'],
|
providesTags: ['Lens'],
|
||||||
refetchOnMountOrArgChange: 5
|
refetchOnMountOrArgChange: 5
|
||||||
}),
|
}),
|
||||||
|
|||||||
@ -14,7 +14,7 @@ export const patientsApi = createApi({
|
|||||||
tagTypes: ['Patient'],
|
tagTypes: ['Patient'],
|
||||||
endpoints: (builder) => ({
|
endpoints: (builder) => ({
|
||||||
getPatients: builder.query({
|
getPatients: builder.query({
|
||||||
query: () => '/Patients/',
|
query: () => '/patients/',
|
||||||
providesTags: ['Patient'],
|
providesTags: ['Patient'],
|
||||||
refetchOnMountOrArgChange: 5
|
refetchOnMountOrArgChange: 5
|
||||||
}),
|
}),
|
||||||
|
|||||||
@ -14,7 +14,7 @@ export const setContentApi = createApi({
|
|||||||
tagTypes: ['SetContent'],
|
tagTypes: ['SetContent'],
|
||||||
endpoints: (builder) => ({
|
endpoints: (builder) => ({
|
||||||
getSetContent: builder.query({
|
getSetContent: builder.query({
|
||||||
query: (setId) => `/set_content/${setId}`,
|
query: (setId) => `/set_content/${setId}/`,
|
||||||
providesTags: ['SetContent'],
|
providesTags: ['SetContent'],
|
||||||
}),
|
}),
|
||||||
addSetContent: builder.mutation({
|
addSetContent: builder.mutation({
|
||||||
|
|||||||
@ -14,7 +14,7 @@ export const setsApi = createApi({
|
|||||||
tagTypes: ['Set'],
|
tagTypes: ['Set'],
|
||||||
endpoints: (builder) => ({
|
endpoints: (builder) => ({
|
||||||
getSets: builder.query({
|
getSets: builder.query({
|
||||||
query: () => '/Sets/',
|
query: () => '/sets/',
|
||||||
providesTags: ['Set'],
|
providesTags: ['Set'],
|
||||||
refetchOnMountOrArgChange: 5
|
refetchOnMountOrArgChange: 5
|
||||||
}),
|
}),
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import {Card, Popconfirm, Tooltip} from "antd";
|
|||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import {DeleteOutlined, EditOutlined, EyeOutlined} from "@ant-design/icons";
|
import {DeleteOutlined, EditOutlined, EyeOutlined} from "@ant-design/icons";
|
||||||
import {useState} from "react";
|
import {useState} from "react";
|
||||||
import LensViewModal from "../Widgets/LensViewModal.jsx";
|
import LensViewModal from "../Pages/LensesPage/Components/LensViewModal/LensViewModal.jsx";
|
||||||
import {LensPropType} from "../../Types/lensPropType.js";
|
import {LensPropType} from "../../Types/lensPropType.js";
|
||||||
|
|
||||||
const LensListCard = ({lens, handleEditLens, handleDeleteLens}) => {
|
const LensListCard = ({lens, handleEditLens, handleDeleteLens}) => {
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import {Card, Modal, Popconfirm, Tooltip} from "antd";
|
|||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import {DeleteOutlined, EditOutlined, EyeOutlined} from "@ant-design/icons";
|
import {DeleteOutlined, EditOutlined, EyeOutlined} from "@ant-design/icons";
|
||||||
import {useState} from "react";
|
import {useState} from "react";
|
||||||
import PatientViewModal from "../Widgets/PatientViewModal.jsx";
|
import PatientViewModal from "../Pages/PatientsPage/Components/PatientViewModal/PatientViewModal.jsx";
|
||||||
import {PatientPropType} from "../../Types/patientPropType.js";
|
import {PatientPropType} from "../../Types/patientPropType.js";
|
||||||
|
|
||||||
const PatientListCard = ({patient, handleEditPatient, handleDeletePatient}) => {
|
const PatientListCard = ({patient, handleEditPatient, handleDeletePatient}) => {
|
||||||
|
|||||||
@ -1,63 +1,28 @@
|
|||||||
import {Col, Form, InputNumber, Modal, notification, Row, Select} from "antd";
|
import {Col, Form, InputNumber, Modal, Row, Select} from "antd";
|
||||||
import {useEffect, useState} from "react";
|
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import getAllLensTypes from "../../old_api/lens_types/getAllLensTypes.js";
|
import {LensPropType} from "../../../../../Types/lensPropType.js";
|
||||||
import {useAuth} from "../../Hooks/AuthContext.jsx";
|
import useLensFormModal from "./useLensFormModal.js";
|
||||||
import {LensPropType} from "../../Types/lensPropType.js";
|
import useLensFormModalUI from "./useLensFormModalUI.js";
|
||||||
|
|
||||||
|
|
||||||
const LensFormModal = ({visible, onCancel, onSubmit, lens}) => {
|
const LensFormModal = ({visible, onCancel, onSubmit, lens}) => {
|
||||||
const {api} = useAuth();
|
const lensFormData = useLensFormModal();
|
||||||
const [form] = Form.useForm();
|
const lensFormUI = useLensFormModalUI(lensFormData.lensTypes, visible, onCancel, onSubmit, lens);
|
||||||
const [lensTypes, setLensTypes] = useState([]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
fetchLensTypes();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (visible) {
|
|
||||||
form.resetFields();
|
|
||||||
if (lens) {
|
|
||||||
form.setFieldsValue({
|
|
||||||
...lens,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [visible, lens]);
|
|
||||||
|
|
||||||
const fetchLensTypes = async () => {
|
|
||||||
const data = await getAllLensTypes(api);
|
|
||||||
setLensTypes(data);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleOk = async () => {
|
|
||||||
try {
|
|
||||||
const values = await form.validateFields();
|
|
||||||
onSubmit(values);
|
|
||||||
form.resetFields();
|
|
||||||
} catch (error) {
|
|
||||||
console.log("Validation Failed:", error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
title={lens ? "Редактировать лину" : "Добавить линзу"}
|
title={lens ? "Редактировать лину" : "Добавить линзу"}
|
||||||
open={visible}
|
open={visible}
|
||||||
onCancel={() => {
|
onCancel={lensFormUI.handleCancel}
|
||||||
form.resetFields();
|
onOk={lensFormUI.handleOk}
|
||||||
onCancel();
|
|
||||||
}}
|
|
||||||
onOk={handleOk}
|
|
||||||
okText={"Сохранить"}
|
okText={"Сохранить"}
|
||||||
cancelText={"Отмена"}
|
cancelText={"Отмена"}
|
||||||
maskClosable={false}
|
maskClosable={false}
|
||||||
forceRender={true}
|
forceRender={true}
|
||||||
style={{top: 20}}
|
style={lensFormUI.modalStyle}
|
||||||
centered
|
centered
|
||||||
>
|
>
|
||||||
<Form form={form} layout={"vertical"}>
|
<Form form={lensFormUI.form} layout={"vertical"}>
|
||||||
<Row gutter={16}>
|
<Row gutter={16}>
|
||||||
<Col xs={24} md={12}>
|
<Col xs={24} md={12}>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
@ -66,9 +31,8 @@ const LensFormModal = ({visible, onCancel, onSubmit, lens}) => {
|
|||||||
rules={[{required: true, message: "Введите тор"}]}
|
rules={[{required: true, message: "Введите тор"}]}
|
||||||
>
|
>
|
||||||
<InputNumber
|
<InputNumber
|
||||||
style={{width: '100%'}}
|
style={lensFormUI.formItemStyle}
|
||||||
step={0.1}
|
step={0.1}
|
||||||
|
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Col>
|
</Col>
|
||||||
@ -79,7 +43,7 @@ const LensFormModal = ({visible, onCancel, onSubmit, lens}) => {
|
|||||||
rules={[{required: true, message: "Введите остроту зрения"}]}
|
rules={[{required: true, message: "Введите остроту зрения"}]}
|
||||||
>
|
>
|
||||||
<InputNumber
|
<InputNumber
|
||||||
style={{width: '100%'}}
|
style={lensFormUI.formItemStyle}
|
||||||
step={0.1}
|
step={0.1}
|
||||||
|
|
||||||
/>
|
/>
|
||||||
@ -94,7 +58,7 @@ const LensFormModal = ({visible, onCancel, onSubmit, lens}) => {
|
|||||||
rules={[{required: true, message: "Введите esa"}]}
|
rules={[{required: true, message: "Введите esa"}]}
|
||||||
>
|
>
|
||||||
<InputNumber
|
<InputNumber
|
||||||
style={{width: '100%'}}
|
style={lensFormUI.formItemStyle}
|
||||||
step={0.1}
|
step={0.1}
|
||||||
|
|
||||||
/>
|
/>
|
||||||
@ -107,7 +71,7 @@ const LensFormModal = ({visible, onCancel, onSubmit, lens}) => {
|
|||||||
rules={[{required: true, message: "Введите fvc"}]}
|
rules={[{required: true, message: "Введите fvc"}]}
|
||||||
>
|
>
|
||||||
<InputNumber
|
<InputNumber
|
||||||
style={{width: '100%'}}
|
style={lensFormUI.formItemStyle}
|
||||||
step={0.1}
|
step={0.1}
|
||||||
|
|
||||||
/>
|
/>
|
||||||
@ -122,7 +86,7 @@ const LensFormModal = ({visible, onCancel, onSubmit, lens}) => {
|
|||||||
rules={[{required: true, message: "Введите пресетную рефракцию"}]}
|
rules={[{required: true, message: "Введите пресетную рефракцию"}]}
|
||||||
>
|
>
|
||||||
<InputNumber
|
<InputNumber
|
||||||
style={{width: '100%'}}
|
style={lensFormUI.formItemStyle}
|
||||||
step={0.1}
|
step={0.1}
|
||||||
|
|
||||||
/>
|
/>
|
||||||
@ -135,7 +99,7 @@ const LensFormModal = ({visible, onCancel, onSubmit, lens}) => {
|
|||||||
rules={[{required: true, message: "Введите диаметр"}]}
|
rules={[{required: true, message: "Введите диаметр"}]}
|
||||||
>
|
>
|
||||||
<InputNumber
|
<InputNumber
|
||||||
style={{width: '100%'}}
|
style={lensFormUI.formItemStyle}
|
||||||
step={0.1}
|
step={0.1}
|
||||||
|
|
||||||
/>
|
/>
|
||||||
@ -150,7 +114,7 @@ const LensFormModal = ({visible, onCancel, onSubmit, lens}) => {
|
|||||||
rules={[{required: true, message: "Введите периферию торичность"}]}
|
rules={[{required: true, message: "Введите периферию торичность"}]}
|
||||||
>
|
>
|
||||||
<InputNumber
|
<InputNumber
|
||||||
style={{width: '100%'}}
|
style={lensFormUI.formItemStyle}
|
||||||
step={0.1}
|
step={0.1}
|
||||||
|
|
||||||
/>
|
/>
|
||||||
@ -162,7 +126,7 @@ const LensFormModal = ({visible, onCancel, onSubmit, lens}) => {
|
|||||||
label="Сторона"
|
label="Сторона"
|
||||||
rules={[{required: true, message: "Выберите сторону"}]}
|
rules={[{required: true, message: "Выберите сторону"}]}
|
||||||
>
|
>
|
||||||
<Select style={{width: '100%'}}>
|
<Select style={lensFormUI.formItemStyle}>
|
||||||
<Select.Option value="левая">Левая</Select.Option>
|
<Select.Option value="левая">Левая</Select.Option>
|
||||||
<Select.Option value="правая">Правая</Select.Option>
|
<Select.Option value="правая">Правая</Select.Option>
|
||||||
</Select>
|
</Select>
|
||||||
@ -174,8 +138,8 @@ const LensFormModal = ({visible, onCancel, onSubmit, lens}) => {
|
|||||||
label="Тип линзы"
|
label="Тип линзы"
|
||||||
rules={[{required: true, message: "Выберите тип линзы"}]}
|
rules={[{required: true, message: "Выберите тип линзы"}]}
|
||||||
>
|
>
|
||||||
<Select style={{width: '100%'}}>
|
<Select style={lensFormUI.formItemStyle}>
|
||||||
{lensTypes.map((type) => (
|
{lensFormData.lensTypes.map((type) => (
|
||||||
<Select.Option key={type.id} value={type.id}>
|
<Select.Option key={type.id} value={type.id}>
|
||||||
{type.title}
|
{type.title}
|
||||||
</Select.Option>
|
</Select.Option>
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
import {useGetLensTypesQuery} from "../../../../../Api/lensTypesApi.js";
|
||||||
|
|
||||||
|
|
||||||
|
const useLensFormModal = () => {
|
||||||
|
const {data: lensTypes = [], isLoading, isError} = useGetLensTypesQuery(undefined);
|
||||||
|
|
||||||
|
return {lensTypes, isLoading, isError};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useLensFormModal;
|
||||||
@ -0,0 +1,48 @@
|
|||||||
|
import {useEffect} from "react";
|
||||||
|
import {Form} from "antd";
|
||||||
|
|
||||||
|
|
||||||
|
const useLensFormModalUI = (lensTypes, visible, onCancel, onSubmit, lens) => {
|
||||||
|
const [form] = Form.useForm();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (visible) {
|
||||||
|
form.resetFields();
|
||||||
|
if (lens) {
|
||||||
|
form.setFieldsValue({
|
||||||
|
...lens,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [visible, lens, form]);
|
||||||
|
|
||||||
|
const modalStyle = {
|
||||||
|
marginTop: 20,
|
||||||
|
};
|
||||||
|
const formItemStyle = { width: "100%" };
|
||||||
|
|
||||||
|
const handleOk = async () => {
|
||||||
|
try {
|
||||||
|
const values = await form.validateFields();
|
||||||
|
onSubmit(values);
|
||||||
|
form.resetFields();
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Validation Failed:", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCancel = () => {
|
||||||
|
form.resetFields();
|
||||||
|
onCancel();
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
form,
|
||||||
|
modalStyle,
|
||||||
|
formItemStyle,
|
||||||
|
handleOk,
|
||||||
|
handleCancel,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useLensFormModalUI;
|
||||||
@ -1,6 +1,6 @@
|
|||||||
import {Button, Col, Modal, Row, Typography} from "antd";
|
import {Button, Col, Modal, Row, Typography} from "antd";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import {LensPropType} from "../../Types/lensPropType.js";
|
import {LensPropType} from "../../../../../Types/lensPropType.js";
|
||||||
|
|
||||||
const {Text, Title} = Typography;
|
const {Text, Title} = Typography;
|
||||||
|
|
||||||
@ -23,7 +23,7 @@ import {
|
|||||||
BuildOutlined
|
BuildOutlined
|
||||||
} from "@ant-design/icons";
|
} from "@ant-design/icons";
|
||||||
import LensCard from "../../Dummies/LensListCard.jsx";
|
import LensCard from "../../Dummies/LensListCard.jsx";
|
||||||
import LensFormModal from "../../Widgets/LensFormModal.jsx";
|
import LensFormModal from "./Components/LensFormModal/LensFormModal.jsx";
|
||||||
import SelectViewMode from "../../Widgets/SelectViewMode.jsx";
|
import SelectViewMode from "../../Widgets/SelectViewMode.jsx";
|
||||||
import LoadingIndicator from "../../Widgets/LoadingIndicator.jsx";
|
import LoadingIndicator from "../../Widgets/LoadingIndicator.jsx";
|
||||||
import useLenses from "./useLenses.js";
|
import useLenses from "./useLenses.js";
|
||||||
|
|||||||
@ -1,64 +1,30 @@
|
|||||||
import {useEffect} from "react";
|
import {Modal, Form, Input, DatePicker} from "antd";
|
||||||
import {Modal, Form, Input, DatePicker, notification} from "antd";
|
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import locale from "antd/es/date-picker/locale/ru_RU";
|
import locale from "antd/es/date-picker/locale/ru_RU";
|
||||||
import validator from "validator";
|
|
||||||
import {MaskedInput} from "antd-mask-input";
|
import {MaskedInput} from "antd-mask-input";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import {PatientPropType} from "../../Types/patientPropType.js";
|
import {PatientPropType} from "../../../../../Types/patientPropType.js";
|
||||||
|
import usePatientFormModalUI from "./usePatientFormModalUI.js";
|
||||||
|
|
||||||
const {TextArea} = Input;
|
const {TextArea} = Input;
|
||||||
|
|
||||||
const PatientFormModal = ({visible, onCancel, onSubmit, patient}) => {
|
const PatientFormModal = ({visible, onCancel, onSubmit, patient}) => {
|
||||||
const [form] = Form.useForm();
|
const patientFormModalUI = usePatientFormModalUI(visible, onCancel, onSubmit, patient);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (visible) {
|
|
||||||
form.resetFields();
|
|
||||||
if (patient) {
|
|
||||||
form.setFieldsValue({
|
|
||||||
...patient,
|
|
||||||
birthday: patient.birthday ? dayjs(patient.birthday, "YYYY-MM-DD") : null,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [visible, patient]);
|
|
||||||
|
|
||||||
const handleOk = async () => {
|
|
||||||
try {
|
|
||||||
const values = await form.validateFields();
|
|
||||||
if (values.birthday) {
|
|
||||||
values.birthday = values.birthday.format("YYYY-MM-DD");
|
|
||||||
}
|
|
||||||
onSubmit(values);
|
|
||||||
form.resetFields();
|
|
||||||
} catch (error) {
|
|
||||||
console.log("Validation Failed:", error);
|
|
||||||
notification.error({
|
|
||||||
message: "Ошибка валидации",
|
|
||||||
description: "Проверьте правильность заполнения полей.",
|
|
||||||
placement: "topRight",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
title={patient ? "Редактировать пациента" : "Добавить пациента"}
|
title={patientFormModalUI.modalTitle}
|
||||||
open={visible}
|
open={visible}
|
||||||
onCancel={() => {
|
onCancel={patientFormModalUI.handleCancel}
|
||||||
form.resetFields();
|
onOk={patientFormModalUI.handleOk}
|
||||||
onCancel();
|
|
||||||
}}
|
|
||||||
onOk={handleOk}
|
|
||||||
okText={"Сохранить"}
|
okText={"Сохранить"}
|
||||||
cancelText={"Отмена"}
|
cancelText={"Отмена"}
|
||||||
maskClosable={false}
|
maskClosable={false}
|
||||||
forceRender={true}
|
forceRender={true}
|
||||||
style={{marginTop: 20, marginBottom: 50}}
|
style={patientFormModalUI.modalStyle}
|
||||||
centered
|
centered
|
||||||
>
|
>
|
||||||
<Form form={form} layout={"vertical"}>
|
<Form form={patientFormModalUI.form} layout={"vertical"}>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name="first_name"
|
name="first_name"
|
||||||
label="Имя"
|
label="Имя"
|
||||||
@ -101,12 +67,7 @@ const PatientFormModal = ({visible, onCancel, onSubmit, patient}) => {
|
|||||||
label="Email"
|
label="Email"
|
||||||
rules={[
|
rules={[
|
||||||
{
|
{
|
||||||
validator: (_, value) => {
|
validator: patientFormModalUI.emailValidator,
|
||||||
if (value && !validator.isEmail(value)) {
|
|
||||||
return Promise.reject("Некорректный email");
|
|
||||||
}
|
|
||||||
return Promise.resolve();
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
@ -0,0 +1,68 @@
|
|||||||
|
import {Form, notification} from "antd";
|
||||||
|
import {useEffect} from "react";
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
import validator from "validator";
|
||||||
|
|
||||||
|
|
||||||
|
const usePatientFormModalUI = (visible, onCancel, onSubmit, patient) => {
|
||||||
|
const [form] = Form.useForm();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (visible) {
|
||||||
|
form.resetFields();
|
||||||
|
if (patient) {
|
||||||
|
form.setFieldsValue({
|
||||||
|
...patient,
|
||||||
|
birthday: patient.birthday ? dayjs(patient.birthday, "YYYY-MM-DD") : null,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [visible, patient, form]);
|
||||||
|
|
||||||
|
const modalStyle = {
|
||||||
|
marginTop: 20,
|
||||||
|
marginBottom: 50,
|
||||||
|
};
|
||||||
|
const modalTitle = patient ? "Редактировать пациента" : "Добавить пациента";
|
||||||
|
|
||||||
|
const emailValidator = (_, value) => {
|
||||||
|
if (value && !validator.isEmail(value)) {
|
||||||
|
return Promise.reject("Некорректный email");
|
||||||
|
}
|
||||||
|
return Promise.resolve();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleOk = async () => {
|
||||||
|
try {
|
||||||
|
const values = await form.validateFields();
|
||||||
|
if (values.birthday) {
|
||||||
|
values.birthday = values.birthday.format("YYYY-MM-DD");
|
||||||
|
}
|
||||||
|
onSubmit(values);
|
||||||
|
form.resetFields();
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Validation Failed:", error);
|
||||||
|
notification.error({
|
||||||
|
message: "Ошибка валидации",
|
||||||
|
description: "Проверьте правильность заполнения полей.",
|
||||||
|
placement: "topRight",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCancel = () => {
|
||||||
|
form.resetFields();
|
||||||
|
onCancel();
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
form,
|
||||||
|
modalStyle,
|
||||||
|
modalTitle,
|
||||||
|
handleOk,
|
||||||
|
handleCancel,
|
||||||
|
emailValidator,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default usePatientFormModalUI;
|
||||||
@ -1,12 +1,10 @@
|
|||||||
import {Button, Col, Modal, Row, Typography, Divider} from "antd";
|
import {Button, Col, Modal, Row, Typography, Divider} from "antd";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import {PatientPropType} from "../../Types/patientPropType.js";
|
import {PatientPropType} from "../../../../../Types/patientPropType.js";
|
||||||
|
|
||||||
const { Text, Title } = Typography;
|
const { Text, Title } = Typography;
|
||||||
|
|
||||||
const PatientViewModal = ({ visible, onCancel, patient }) => {
|
const PatientViewModal = ({ visible, onCancel, patient }) => {
|
||||||
if (!patient) return null;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
title="Просмотр пациента"
|
title="Просмотр пациента"
|
||||||
@ -73,7 +71,7 @@ const PatientViewModal = ({ visible, onCancel, patient }) => {
|
|||||||
PatientViewModal.propTypes = {
|
PatientViewModal.propTypes = {
|
||||||
visible: PropTypes.bool.isRequired,
|
visible: PropTypes.bool.isRequired,
|
||||||
onCancel: PropTypes.func.isRequired,
|
onCancel: PropTypes.func.isRequired,
|
||||||
patient: PatientPropType.isRequired,
|
patient: PatientPropType,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default PatientViewModal;
|
export default PatientViewModal;
|
||||||
@ -19,7 +19,7 @@ import {
|
|||||||
TeamOutlined
|
TeamOutlined
|
||||||
} from "@ant-design/icons";
|
} from "@ant-design/icons";
|
||||||
import PatientListCard from "../../Dummies/PatientListCard.jsx";
|
import PatientListCard from "../../Dummies/PatientListCard.jsx";
|
||||||
import PatientFormModal from "../../Widgets/PatientFormModal.jsx";
|
import PatientFormModal from "./Components/PatientFormModal/PatientFormModal.jsx";
|
||||||
import SelectViewMode from "../../Widgets/SelectViewMode.jsx";
|
import SelectViewMode from "../../Widgets/SelectViewMode.jsx";
|
||||||
import LoadingIndicator from "../../Widgets/LoadingIndicator.jsx";
|
import LoadingIndicator from "../../Widgets/LoadingIndicator.jsx";
|
||||||
import usePatients from "./usePatients.js";
|
import usePatients from "./usePatients.js";
|
||||||
|
|||||||
@ -0,0 +1,180 @@
|
|||||||
|
import {Modal, Button, Form, Input, Table, InputNumber, Select, Space, Result} from "antd";
|
||||||
|
import {PlusOutlined, DeleteOutlined} from "@ant-design/icons";
|
||||||
|
import PropTypes from "prop-types";
|
||||||
|
import {SetPropType} from "../../../../../Types/setPropType.js";
|
||||||
|
import useSetFormModal from "./useSetFormModal.js";
|
||||||
|
import useSetFormModalUI from "./useSetFormModalUI.js";
|
||||||
|
|
||||||
|
const {Option} = Select;
|
||||||
|
|
||||||
|
const SetFormModal = ({visible, onCancel, setData, onSubmit}) => {
|
||||||
|
const setFormModalData = useSetFormModal(setData);
|
||||||
|
const setFormModalUI = useSetFormModalUI(visible, onCancel, setData, onSubmit, setFormModalData.setContents, setFormModalData.lensTypes);
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: "Tor",
|
||||||
|
dataIndex: "tor",
|
||||||
|
render: (_, record, index) => <InputNumber
|
||||||
|
step={setFormModalUI.numberInputStep}
|
||||||
|
value={record.tor}
|
||||||
|
onChange={value => setFormModalUI.updateContentItem(index, "tor", value)}
|
||||||
|
/>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Trial",
|
||||||
|
dataIndex: "trial",
|
||||||
|
render: (_, record, index) => <InputNumber
|
||||||
|
step={setFormModalUI.numberInputStep}
|
||||||
|
value={record.trial}
|
||||||
|
onChange={value => setFormModalUI.updateContentItem(index, "trial", value)}
|
||||||
|
/>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "ESA",
|
||||||
|
dataIndex: "esa",
|
||||||
|
render: (_, record, index) => <InputNumber
|
||||||
|
step={setFormModalUI.numberInputStep}
|
||||||
|
value={record.esa}
|
||||||
|
onChange={value => setFormModalUI.updateContentItem(index, "esa", value)}
|
||||||
|
/>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "FVC",
|
||||||
|
dataIndex: "fvc",
|
||||||
|
render: (_, record, index) => <InputNumber
|
||||||
|
step={setFormModalUI.numberInputStep}
|
||||||
|
value={record.fvc}
|
||||||
|
onChange={value => setFormModalUI.updateContentItem(index, "fvc", value)}
|
||||||
|
/>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Пресетная рефракция",
|
||||||
|
dataIndex: "preset_refraction",
|
||||||
|
render: (_, record, index) => <InputNumber
|
||||||
|
step={setFormModalUI.numberInputStep}
|
||||||
|
value={record.preset_refraction}
|
||||||
|
onChange={value => setFormModalUI.updateContentItem(index, "preset_refraction", value)}
|
||||||
|
/>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Диаметр",
|
||||||
|
dataIndex: "diameter",
|
||||||
|
render: (_, record, index) => <InputNumber
|
||||||
|
step={setFormModalUI.numberInputStep}
|
||||||
|
value={record.diameter}
|
||||||
|
onChange={value => setFormModalUI.updateContentItem(index, "diameter", value)}
|
||||||
|
/>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Периферийная торичность",
|
||||||
|
dataIndex: "periphery_toricity",
|
||||||
|
render: (_, record, index) => <InputNumber
|
||||||
|
step={setFormModalUI.numberInputStep}
|
||||||
|
value={record.periphery_toricity}
|
||||||
|
onChange={value => setFormModalUI.updateContentItem(index, "periphery_toricity", value)}
|
||||||
|
/>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Сторона",
|
||||||
|
dataIndex: "side",
|
||||||
|
render: (_, record, index) => <Select
|
||||||
|
value={record.side}
|
||||||
|
onChange={value => setFormModalUI.updateContentItem(index, "side", value)}
|
||||||
|
>
|
||||||
|
<Option value="левая">Левая</Option>
|
||||||
|
<Option value="правая">Правая</Option>
|
||||||
|
</Select>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Количество",
|
||||||
|
dataIndex: "count",
|
||||||
|
render: (_, record, index) => <InputNumber
|
||||||
|
min={1}
|
||||||
|
value={record.count}
|
||||||
|
onChange={value => setFormModalUI.updateContentItem(index, "count", value)}
|
||||||
|
/>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Тип",
|
||||||
|
dataIndex: "type_id",
|
||||||
|
render: (_, record, index) => (
|
||||||
|
<Select
|
||||||
|
value={record.type_id}
|
||||||
|
onChange={value => setFormModalUI.updateContentItem(index, "type_id", value)}
|
||||||
|
style={setFormModalUI.selectTypeLensFieldStyle}
|
||||||
|
defaultValue={setFormModalData.lensTypes[0]?.id || null}
|
||||||
|
>
|
||||||
|
{setFormModalData.lensTypes.map(lensType =>
|
||||||
|
<Option key={lensType.id} value={lensType.id}>{lensType.title}</Option>
|
||||||
|
)}
|
||||||
|
</Select>
|
||||||
|
),
|
||||||
|
width: 200,
|
||||||
|
minWidth: 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Действие",
|
||||||
|
dataIndex: "actions",
|
||||||
|
render: (_, __, index) => (
|
||||||
|
<Button danger icon={<DeleteOutlined/>} onClick={() => setFormModalUI.removeContentItem(index)}/>
|
||||||
|
),
|
||||||
|
minWidth: 200,
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
title={setFormModalUI.modalTitle}
|
||||||
|
open={visible}
|
||||||
|
onCancel={setFormModalUI.onCancelModal}
|
||||||
|
onOk={setFormModalUI.handleSubmit}
|
||||||
|
okText={"Сохранить"}
|
||||||
|
cancelText={"Отмена"}
|
||||||
|
width="90%"
|
||||||
|
style={setFormModalUI.modalStyle}
|
||||||
|
>
|
||||||
|
|
||||||
|
{setFormModalData.isError ? (
|
||||||
|
<Result
|
||||||
|
status="error"
|
||||||
|
title="Ошибка"
|
||||||
|
subTitle="Произошла ошибка в работе страницы"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<Form form={setFormModalUI.form} layout="vertical">
|
||||||
|
<Form.Item label="Название набора" name="title"
|
||||||
|
rules={[{required: true, message: "Введите название"}]}>
|
||||||
|
<Input/>
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
|
||||||
|
<div style={setFormModalUI.tableStyle}>
|
||||||
|
<Table
|
||||||
|
dataSource={setFormModalUI.currentContent}
|
||||||
|
columns={columns}
|
||||||
|
rowKey="id"
|
||||||
|
pagination={false}
|
||||||
|
scroll={{x: "max-content", y: 300}}
|
||||||
|
locale={{emptyText: "Добавьте элементы"}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Space style={setFormModalUI.addContentButtonSpaceStyle}>
|
||||||
|
<Button icon={<PlusOutlined/>} onClick={setFormModalUI.addContentItem}>Добавить элемент</Button>
|
||||||
|
</Space>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
SetFormModal.propTypes = {
|
||||||
|
visible: PropTypes.bool.isRequired,
|
||||||
|
onCancel: PropTypes.func.isRequired,
|
||||||
|
onSubmit: PropTypes.func.isRequired,
|
||||||
|
setData: SetPropType,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SetFormModal;
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
import {useGetLensTypesQuery} from "../../../../../Api/lensTypesApi.js";
|
||||||
|
import {useGetSetContentQuery} from "../../../../../Api/setContentApi.js";
|
||||||
|
|
||||||
|
|
||||||
|
const useSetFormModal = (setData) => {
|
||||||
|
const {data: lensTypes = [], isLoading: isLoadingLensTypes, isError: isErrorLensTypes} =
|
||||||
|
useGetLensTypesQuery(undefined);
|
||||||
|
|
||||||
|
const setId = setData?.id;
|
||||||
|
|
||||||
|
const {
|
||||||
|
data: setContents = [],
|
||||||
|
isLoading: isLoadingSetContents,
|
||||||
|
isError: isErrorSetContents
|
||||||
|
} = useGetSetContentQuery(setId, {
|
||||||
|
skip: !setId,
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
lensTypes,
|
||||||
|
setContents,
|
||||||
|
isLoading: isLoadingLensTypes || (setId && isLoadingSetContents),
|
||||||
|
isError: isErrorLensTypes || (setId && isErrorSetContents),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useSetFormModal;
|
||||||
@ -0,0 +1,115 @@
|
|||||||
|
import {Form, notification} from "antd";
|
||||||
|
import {useEffect, useState} from "react";
|
||||||
|
|
||||||
|
|
||||||
|
const useSetFormModalUI = (visible, onCancel, setData, onSubmit, content, lensTypes) => {
|
||||||
|
const [form] = Form.useForm();
|
||||||
|
const [currentContent, setCurrentContent] = useState([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (visible && setData && content?.length) {
|
||||||
|
setCurrentContent(content);
|
||||||
|
}
|
||||||
|
}, [visible, setData?.id, setData, content]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (setData) {
|
||||||
|
form.setFieldsValue({title: setData.title || ""});
|
||||||
|
}
|
||||||
|
}, [setData, form]);
|
||||||
|
|
||||||
|
const numberInputStep = 0.1;
|
||||||
|
const modalTitle = setData ? "Редактирование набора" : "Создание набора";
|
||||||
|
|
||||||
|
const modalStyle = {maxWidth: 2500};
|
||||||
|
const tableStyle = {maxWidth: "100%", overflowX: "auto"};
|
||||||
|
const addContentButtonSpaceStyle = {marginTop: 101};
|
||||||
|
const selectTypeLensFieldStyle = {width: "100%"};
|
||||||
|
|
||||||
|
const addContentItem = () => {
|
||||||
|
setCurrentContent([...currentContent, {
|
||||||
|
id: Date.now(),
|
||||||
|
tor: 0,
|
||||||
|
trial: 0,
|
||||||
|
esa: 0,
|
||||||
|
fvc: 0,
|
||||||
|
preset_refraction: 0,
|
||||||
|
diameter: 0,
|
||||||
|
periphery_toricity: 0,
|
||||||
|
side: "левая",
|
||||||
|
count: 1,
|
||||||
|
type_id: lensTypes ? lensTypes[0].id : null
|
||||||
|
}]);
|
||||||
|
};
|
||||||
|
|
||||||
|
const validateContent = () => {
|
||||||
|
for (const item of content) {
|
||||||
|
if (
|
||||||
|
item.tor === null ||
|
||||||
|
item.trial === null ||
|
||||||
|
item.esa === null ||
|
||||||
|
item.fvc === null ||
|
||||||
|
item.preset_refraction === null ||
|
||||||
|
item.diameter === null ||
|
||||||
|
item.periphery_toricity === null ||
|
||||||
|
item.side === null ||
|
||||||
|
item.count === null ||
|
||||||
|
item.type_id === null
|
||||||
|
) {
|
||||||
|
notification.error({
|
||||||
|
message: "Ошибка валидации",
|
||||||
|
description: "Все поля в таблице должны быть заполнены перед сохранением.",
|
||||||
|
placement: "topRight",
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateContentItem = (index, field, value) => {
|
||||||
|
const updated = [...currentContent];
|
||||||
|
updated[index][field] = value;
|
||||||
|
setCurrentContent(updated);
|
||||||
|
};
|
||||||
|
|
||||||
|
const removeContentItem = (index) => {
|
||||||
|
setCurrentContent(currentContent.filter((_, i) => i !== index));
|
||||||
|
};
|
||||||
|
|
||||||
|
const resetModal = () => {
|
||||||
|
form.resetFields();
|
||||||
|
setCurrentContent([]);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubmit = () => {
|
||||||
|
form.validateFields().then(values => {
|
||||||
|
if (!validateContent()) return;
|
||||||
|
const sanitizedContent = currentContent.map(({id, ...rest}) => rest);
|
||||||
|
onSubmit({...values}, sanitizedContent);
|
||||||
|
}).then(resetModal);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onCancelModal = () => {
|
||||||
|
resetModal();
|
||||||
|
onCancel();
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
form,
|
||||||
|
currentContent,
|
||||||
|
numberInputStep,
|
||||||
|
modalTitle,
|
||||||
|
modalStyle,
|
||||||
|
tableStyle,
|
||||||
|
addContentButtonSpaceStyle,
|
||||||
|
selectTypeLensFieldStyle,
|
||||||
|
addContentItem,
|
||||||
|
updateContentItem,
|
||||||
|
removeContentItem,
|
||||||
|
handleSubmit,
|
||||||
|
onCancelModal,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useSetFormModalUI;
|
||||||
@ -1,7 +1,7 @@
|
|||||||
import {FloatButton, Input, List, Result, Row, Typography} from "antd";
|
import {FloatButton, Input, List, Result, Row, Typography} from "antd";
|
||||||
import {PlusOutlined, SwitcherOutlined} from "@ant-design/icons";
|
import {PlusOutlined, SwitcherOutlined} from "@ant-design/icons";
|
||||||
import SetListCard from "../../Dummies/SetListCard.jsx";
|
import SetListCard from "../../Dummies/SetListCard.jsx";
|
||||||
import SetFormModal from "../../Widgets/SetFormModal.jsx";
|
import SetFormModal from "./Components/SetFormModal/SetFormModal.jsx";
|
||||||
import LoadingIndicator from "../../Widgets/LoadingIndicator.jsx";
|
import LoadingIndicator from "../../Widgets/LoadingIndicator.jsx";
|
||||||
import useSets from "./useSets.js";
|
import useSets from "./useSets.js";
|
||||||
import useSetsUI from "./useSetsUI.js";
|
import useSetsUI from "./useSetsUI.js";
|
||||||
@ -74,6 +74,7 @@ const SetsPage = () => {
|
|||||||
onSubmit={setsData.handleModalSetSubmit}
|
onSubmit={setsData.handleModalSetSubmit}
|
||||||
setData={setsUI.selectedSet}
|
setData={setsUI.selectedSet}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -16,7 +16,7 @@ const useSets = () => {
|
|||||||
selectedSet,
|
selectedSet,
|
||||||
} = useSelector(state => state.setsUI);
|
} = useSelector(state => state.setsUI);
|
||||||
|
|
||||||
const {data: sets = [], isLoading, isError} = useGetSetsQuery({
|
const {data: sets = [], isLoading, isError} = useGetSetsQuery(undefined, {
|
||||||
pollingInterval: 20000,
|
pollingInterval: 20000,
|
||||||
});
|
});
|
||||||
const [addSet] = useAddSetMutation();
|
const [addSet] = useAddSetMutation();
|
||||||
|
|||||||
@ -1,260 +0,0 @@
|
|||||||
import {useState, useEffect} from "react";
|
|
||||||
import {Modal, Button, Form, Input, Table, InputNumber, Select, Space, notification} from "antd";
|
|
||||||
import {PlusOutlined, DeleteOutlined} from "@ant-design/icons";
|
|
||||||
import getAllLensTypes from "../../old_api/lens_types/getAllLensTypes.js";
|
|
||||||
import {useAuth} from "../../Hooks/AuthContext.jsx";
|
|
||||||
import PropTypes from "prop-types";
|
|
||||||
import getSetContentBySetId from "../../old_api/set_content/getSetContentBySetId.js";
|
|
||||||
import {SetPropType} from "../../Types/setPropType.js";
|
|
||||||
|
|
||||||
const {Option} = Select;
|
|
||||||
|
|
||||||
const SetFormModal = ({visible, onCancel, setData, onSubmit}) => {
|
|
||||||
const {api} = useAuth();
|
|
||||||
|
|
||||||
const [form] = Form.useForm();
|
|
||||||
const [content, setContent] = useState([]);
|
|
||||||
const [lensTypes, setLensTypes] = useState([]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
fetchLensTypes();
|
|
||||||
fetchSetContents();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (setData) {
|
|
||||||
form.setFieldsValue({title: setData.title || ""});
|
|
||||||
}
|
|
||||||
fetchSetContents();
|
|
||||||
}, [setData, form]);
|
|
||||||
|
|
||||||
const fetchSetContents = async () => {
|
|
||||||
if (!setData) return;
|
|
||||||
|
|
||||||
const data = await getSetContentBySetId(api, setData.id);
|
|
||||||
setContent(data);
|
|
||||||
};
|
|
||||||
|
|
||||||
const fetchLensTypes = async () => {
|
|
||||||
const data = await getAllLensTypes(api);
|
|
||||||
setLensTypes(data);
|
|
||||||
};
|
|
||||||
|
|
||||||
const addContentItem = () => {
|
|
||||||
setContent([...content, {
|
|
||||||
id: Date.now(),
|
|
||||||
tor: 0,
|
|
||||||
trial: 0,
|
|
||||||
esa: 0,
|
|
||||||
fvc: 0,
|
|
||||||
preset_refraction: 0,
|
|
||||||
diameter: 0,
|
|
||||||
periphery_toricity: 0,
|
|
||||||
side: "левая",
|
|
||||||
count: 1,
|
|
||||||
type_id: lensTypes ? lensTypes[0].id : null
|
|
||||||
}]);
|
|
||||||
};
|
|
||||||
|
|
||||||
const validateContent = () => {
|
|
||||||
for (const item of content) {
|
|
||||||
if (
|
|
||||||
item.tor === null ||
|
|
||||||
item.trial === null ||
|
|
||||||
item.esa === null ||
|
|
||||||
item.fvc === null ||
|
|
||||||
item.preset_refraction === null ||
|
|
||||||
item.diameter === null ||
|
|
||||||
item.periphery_toricity === null ||
|
|
||||||
item.side === null ||
|
|
||||||
item.count === null ||
|
|
||||||
item.type_id === null
|
|
||||||
) {
|
|
||||||
notification.error({
|
|
||||||
message: "Ошибка валидации",
|
|
||||||
description: "Все поля в таблице должны быть заполнены перед сохранением.",
|
|
||||||
placement: "topRight",
|
|
||||||
});
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
const updateContentItem = (index, field, value) => {
|
|
||||||
const updated = [...content];
|
|
||||||
updated[index][field] = value;
|
|
||||||
setContent(updated);
|
|
||||||
};
|
|
||||||
|
|
||||||
const removeContentItem = (index) => {
|
|
||||||
setContent(content.filter((_, i) => i !== index));
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSubmit = () => {
|
|
||||||
form.validateFields().then(values => {
|
|
||||||
if (!validateContent()) return;
|
|
||||||
const sanitizedContent = content.map(({id, ...rest}) => rest);
|
|
||||||
onSubmit({...values}, sanitizedContent);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const columns = [
|
|
||||||
{
|
|
||||||
title: "Tor",
|
|
||||||
dataIndex: "tor",
|
|
||||||
render: (_, record, index) => <InputNumber
|
|
||||||
step={0.1}
|
|
||||||
value={record.tor}
|
|
||||||
onChange={value => updateContentItem(index, "tor", value)}
|
|
||||||
/>,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Trial",
|
|
||||||
dataIndex: "trial",
|
|
||||||
render: (_, record, index) => <InputNumber
|
|
||||||
step={0.1}
|
|
||||||
value={record.trial}
|
|
||||||
onChange={value => updateContentItem(index, "trial", value)}
|
|
||||||
/>,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "ESA",
|
|
||||||
dataIndex: "esa",
|
|
||||||
render: (_, record, index) => <InputNumber
|
|
||||||
step={0.1}
|
|
||||||
value={record.esa}
|
|
||||||
onChange={value => updateContentItem(index, "esa", value)}
|
|
||||||
/>,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "FVC",
|
|
||||||
dataIndex: "fvc",
|
|
||||||
render: (_, record, index) => <InputNumber
|
|
||||||
step={0.1}
|
|
||||||
value={record.fvc}
|
|
||||||
onChange={value => updateContentItem(index, "fvc", value)}
|
|
||||||
/>,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Пресетная рефракция",
|
|
||||||
dataIndex: "preset_refraction",
|
|
||||||
render: (_, record, index) => <InputNumber
|
|
||||||
step={0.1}
|
|
||||||
value={record.preset_refraction}
|
|
||||||
onChange={value => updateContentItem(index, "preset_refraction", value)}
|
|
||||||
/>,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Диаметр",
|
|
||||||
dataIndex: "diameter",
|
|
||||||
render: (_, record, index) => <InputNumber
|
|
||||||
step={0.1}
|
|
||||||
value={record.diameter}
|
|
||||||
onChange={value => updateContentItem(index, "diameter", value)}
|
|
||||||
/>,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Периферийная торичность",
|
|
||||||
dataIndex: "periphery_toricity",
|
|
||||||
render: (_, record, index) => <InputNumber
|
|
||||||
step={0.1}
|
|
||||||
value={record.periphery_toricity}
|
|
||||||
onChange={value => updateContentItem(index, "periphery_toricity", value)}
|
|
||||||
/>,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Сторона",
|
|
||||||
dataIndex: "side",
|
|
||||||
render: (_, record, index) => <Select
|
|
||||||
value={record.side}
|
|
||||||
onChange={value => updateContentItem(index, "side", value)}
|
|
||||||
>
|
|
||||||
<Option value="левая">Левая</Option>
|
|
||||||
<Option value="правая">Правая</Option>
|
|
||||||
</Select>,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Количество",
|
|
||||||
dataIndex: "count",
|
|
||||||
render: (_, record, index) => <InputNumber
|
|
||||||
min={1}
|
|
||||||
value={record.count}
|
|
||||||
onChange={value => updateContentItem(index, "count", value)}
|
|
||||||
/>,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Тип",
|
|
||||||
dataIndex: "type_id",
|
|
||||||
render: (_, record, index) => (
|
|
||||||
<Select
|
|
||||||
value={record.type_id}
|
|
||||||
onChange={value => updateContentItem(index, "type_id", value)}
|
|
||||||
style={{width: "100%"}}
|
|
||||||
defaultValue={lensTypes[0]?.id || null}
|
|
||||||
>
|
|
||||||
{lensTypes.map(lensType =>
|
|
||||||
<Option key={lensType.id} value={lensType.id}>{lensType.title}</Option>
|
|
||||||
)}
|
|
||||||
</Select>
|
|
||||||
),
|
|
||||||
width: 200,
|
|
||||||
minWidth: 100
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Действие",
|
|
||||||
dataIndex: "actions",
|
|
||||||
render: (_, __, index) => (
|
|
||||||
<Button danger icon={<DeleteOutlined/>} onClick={() => removeContentItem(index)}/>
|
|
||||||
),
|
|
||||||
minWidth: 200,
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Modal
|
|
||||||
title={setData ? "Редактировать набор" : "Создать набор"}
|
|
||||||
open={visible}
|
|
||||||
onCancel={() => {
|
|
||||||
form.resetFields();
|
|
||||||
setContent([]);
|
|
||||||
onCancel();
|
|
||||||
}}
|
|
||||||
onOk={handleSubmit}
|
|
||||||
okText="Сохранить"
|
|
||||||
cancelText={"Отмена"}
|
|
||||||
width="90vw"
|
|
||||||
style={{maxWidth: 2500}}
|
|
||||||
>
|
|
||||||
<Form form={form} layout="vertical">
|
|
||||||
<Form.Item label="Название набора" name="title" rules={[{required: true, message: "Введите название"}]}>
|
|
||||||
<Input/>
|
|
||||||
</Form.Item>
|
|
||||||
</Form>
|
|
||||||
|
|
||||||
<div style={{maxWidth: "100%", overflowX: "auto"}}>
|
|
||||||
<Table
|
|
||||||
dataSource={content}
|
|
||||||
columns={columns}
|
|
||||||
rowKey="id"
|
|
||||||
pagination={false}
|
|
||||||
scroll={{x: "max-content", y: 300}}
|
|
||||||
locale={{emptyText: "Добавьте элементы"}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Space style={{marginTop: 10}}>
|
|
||||||
<Button icon={<PlusOutlined/>} onClick={addContentItem}>Добавить элемент</Button>
|
|
||||||
</Space>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
SetFormModal.propTypes = {
|
|
||||||
visible: PropTypes.bool.isRequired,
|
|
||||||
onCancel: PropTypes.func.isRequired,
|
|
||||||
onSubmit: PropTypes.func.isRequired,
|
|
||||||
setData: SetPropType,
|
|
||||||
}
|
|
||||||
|
|
||||||
export default SetFormModal;
|
|
||||||
@ -8,6 +8,7 @@ import setsUIReducer from './Slices/setsSlice.js';
|
|||||||
import {setContentApi} from "../Api/setContentApi.js";
|
import {setContentApi} from "../Api/setContentApi.js";
|
||||||
import {lensIssuesApi} from "../Api/lensIssuesApi.js";
|
import {lensIssuesApi} from "../Api/lensIssuesApi.js";
|
||||||
import lensIssuesReducer from "./Slices/lensIssuesSlice.js";
|
import lensIssuesReducer from "./Slices/lensIssuesSlice.js";
|
||||||
|
import {lensTypesApi} from "../Api/lensTypesApi.js";
|
||||||
|
|
||||||
export const store = configureStore({
|
export const store = configureStore({
|
||||||
reducer: {
|
reducer: {
|
||||||
@ -22,6 +23,8 @@ export const store = configureStore({
|
|||||||
|
|
||||||
[setContentApi.reducerPath]: setContentApi.reducer,
|
[setContentApi.reducerPath]: setContentApi.reducer,
|
||||||
|
|
||||||
|
[lensTypesApi.reducerPath]: lensTypesApi.reducer,
|
||||||
|
|
||||||
[lensIssuesApi.reducerPath]: lensIssuesApi.reducer,
|
[lensIssuesApi.reducerPath]: lensIssuesApi.reducer,
|
||||||
lensIssuesUI: lensIssuesReducer,
|
lensIssuesUI: lensIssuesReducer,
|
||||||
},
|
},
|
||||||
@ -31,9 +34,10 @@ export const store = configureStore({
|
|||||||
lensesApi.middleware,
|
lensesApi.middleware,
|
||||||
setsApi.middleware,
|
setsApi.middleware,
|
||||||
setContentApi.middleware,
|
setContentApi.middleware,
|
||||||
|
lensTypesApi.middleware,
|
||||||
lensIssuesApi.middleware,
|
lensIssuesApi.middleware,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
export default store;
|
export default store;
|
||||||
Loading…
x
Reference in New Issue
Block a user