начал разделять компоненты и логику в отдельные файлы
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'],
|
||||
endpoints: (builder) => ({
|
||||
getLensIssues: builder.query({
|
||||
query: () => '/LensIssues/',
|
||||
query: () => '/lens_issues/',
|
||||
providesTags: ['LensIssues'],
|
||||
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'],
|
||||
endpoints: (builder) => ({
|
||||
getLenses: builder.query({
|
||||
query: () => '/Lenses/',
|
||||
query: () => '/lenses/',
|
||||
providesTags: ['Lens'],
|
||||
refetchOnMountOrArgChange: 5
|
||||
}),
|
||||
|
||||
@ -14,7 +14,7 @@ export const patientsApi = createApi({
|
||||
tagTypes: ['Patient'],
|
||||
endpoints: (builder) => ({
|
||||
getPatients: builder.query({
|
||||
query: () => '/Patients/',
|
||||
query: () => '/patients/',
|
||||
providesTags: ['Patient'],
|
||||
refetchOnMountOrArgChange: 5
|
||||
}),
|
||||
|
||||
@ -14,7 +14,7 @@ export const setContentApi = createApi({
|
||||
tagTypes: ['SetContent'],
|
||||
endpoints: (builder) => ({
|
||||
getSetContent: builder.query({
|
||||
query: (setId) => `/set_content/${setId}`,
|
||||
query: (setId) => `/set_content/${setId}/`,
|
||||
providesTags: ['SetContent'],
|
||||
}),
|
||||
addSetContent: builder.mutation({
|
||||
|
||||
@ -14,7 +14,7 @@ export const setsApi = createApi({
|
||||
tagTypes: ['Set'],
|
||||
endpoints: (builder) => ({
|
||||
getSets: builder.query({
|
||||
query: () => '/Sets/',
|
||||
query: () => '/sets/',
|
||||
providesTags: ['Set'],
|
||||
refetchOnMountOrArgChange: 5
|
||||
}),
|
||||
|
||||
@ -2,7 +2,7 @@ import {Card, Popconfirm, Tooltip} from "antd";
|
||||
import PropTypes from "prop-types";
|
||||
import {DeleteOutlined, EditOutlined, EyeOutlined} from "@ant-design/icons";
|
||||
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";
|
||||
|
||||
const LensListCard = ({lens, handleEditLens, handleDeleteLens}) => {
|
||||
|
||||
@ -2,7 +2,7 @@ import {Card, Modal, Popconfirm, Tooltip} from "antd";
|
||||
import PropTypes from "prop-types";
|
||||
import {DeleteOutlined, EditOutlined, EyeOutlined} from "@ant-design/icons";
|
||||
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";
|
||||
|
||||
const PatientListCard = ({patient, handleEditPatient, handleDeletePatient}) => {
|
||||
|
||||
@ -1,63 +1,28 @@
|
||||
import {Col, Form, InputNumber, Modal, notification, Row, Select} from "antd";
|
||||
import {useEffect, useState} from "react";
|
||||
import {Col, Form, InputNumber, Modal, Row, Select} from "antd";
|
||||
import PropTypes from "prop-types";
|
||||
import getAllLensTypes from "../../old_api/lens_types/getAllLensTypes.js";
|
||||
import {useAuth} from "../../Hooks/AuthContext.jsx";
|
||||
import {LensPropType} from "../../Types/lensPropType.js";
|
||||
import {LensPropType} from "../../../../../Types/lensPropType.js";
|
||||
import useLensFormModal from "./useLensFormModal.js";
|
||||
import useLensFormModalUI from "./useLensFormModalUI.js";
|
||||
|
||||
|
||||
const LensFormModal = ({visible, onCancel, onSubmit, lens}) => {
|
||||
const {api} = useAuth();
|
||||
const [form] = Form.useForm();
|
||||
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);
|
||||
}
|
||||
};
|
||||
const lensFormData = useLensFormModal();
|
||||
const lensFormUI = useLensFormModalUI(lensFormData.lensTypes, visible, onCancel, onSubmit, lens);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title={lens ? "Редактировать лину" : "Добавить линзу"}
|
||||
open={visible}
|
||||
onCancel={() => {
|
||||
form.resetFields();
|
||||
onCancel();
|
||||
}}
|
||||
onOk={handleOk}
|
||||
onCancel={lensFormUI.handleCancel}
|
||||
onOk={lensFormUI.handleOk}
|
||||
okText={"Сохранить"}
|
||||
cancelText={"Отмена"}
|
||||
maskClosable={false}
|
||||
forceRender={true}
|
||||
style={{top: 20}}
|
||||
style={lensFormUI.modalStyle}
|
||||
centered
|
||||
>
|
||||
<Form form={form} layout={"vertical"}>
|
||||
<Form form={lensFormUI.form} layout={"vertical"}>
|
||||
<Row gutter={16}>
|
||||
<Col xs={24} md={12}>
|
||||
<Form.Item
|
||||
@ -66,9 +31,8 @@ const LensFormModal = ({visible, onCancel, onSubmit, lens}) => {
|
||||
rules={[{required: true, message: "Введите тор"}]}
|
||||
>
|
||||
<InputNumber
|
||||
style={{width: '100%'}}
|
||||
style={lensFormUI.formItemStyle}
|
||||
step={0.1}
|
||||
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
@ -79,7 +43,7 @@ const LensFormModal = ({visible, onCancel, onSubmit, lens}) => {
|
||||
rules={[{required: true, message: "Введите остроту зрения"}]}
|
||||
>
|
||||
<InputNumber
|
||||
style={{width: '100%'}}
|
||||
style={lensFormUI.formItemStyle}
|
||||
step={0.1}
|
||||
|
||||
/>
|
||||
@ -94,7 +58,7 @@ const LensFormModal = ({visible, onCancel, onSubmit, lens}) => {
|
||||
rules={[{required: true, message: "Введите esa"}]}
|
||||
>
|
||||
<InputNumber
|
||||
style={{width: '100%'}}
|
||||
style={lensFormUI.formItemStyle}
|
||||
step={0.1}
|
||||
|
||||
/>
|
||||
@ -107,7 +71,7 @@ const LensFormModal = ({visible, onCancel, onSubmit, lens}) => {
|
||||
rules={[{required: true, message: "Введите fvc"}]}
|
||||
>
|
||||
<InputNumber
|
||||
style={{width: '100%'}}
|
||||
style={lensFormUI.formItemStyle}
|
||||
step={0.1}
|
||||
|
||||
/>
|
||||
@ -122,7 +86,7 @@ const LensFormModal = ({visible, onCancel, onSubmit, lens}) => {
|
||||
rules={[{required: true, message: "Введите пресетную рефракцию"}]}
|
||||
>
|
||||
<InputNumber
|
||||
style={{width: '100%'}}
|
||||
style={lensFormUI.formItemStyle}
|
||||
step={0.1}
|
||||
|
||||
/>
|
||||
@ -135,7 +99,7 @@ const LensFormModal = ({visible, onCancel, onSubmit, lens}) => {
|
||||
rules={[{required: true, message: "Введите диаметр"}]}
|
||||
>
|
||||
<InputNumber
|
||||
style={{width: '100%'}}
|
||||
style={lensFormUI.formItemStyle}
|
||||
step={0.1}
|
||||
|
||||
/>
|
||||
@ -150,7 +114,7 @@ const LensFormModal = ({visible, onCancel, onSubmit, lens}) => {
|
||||
rules={[{required: true, message: "Введите периферию торичность"}]}
|
||||
>
|
||||
<InputNumber
|
||||
style={{width: '100%'}}
|
||||
style={lensFormUI.formItemStyle}
|
||||
step={0.1}
|
||||
|
||||
/>
|
||||
@ -162,7 +126,7 @@ const LensFormModal = ({visible, onCancel, onSubmit, lens}) => {
|
||||
label="Сторона"
|
||||
rules={[{required: true, message: "Выберите сторону"}]}
|
||||
>
|
||||
<Select style={{width: '100%'}}>
|
||||
<Select style={lensFormUI.formItemStyle}>
|
||||
<Select.Option value="левая">Левая</Select.Option>
|
||||
<Select.Option value="правая">Правая</Select.Option>
|
||||
</Select>
|
||||
@ -174,8 +138,8 @@ const LensFormModal = ({visible, onCancel, onSubmit, lens}) => {
|
||||
label="Тип линзы"
|
||||
rules={[{required: true, message: "Выберите тип линзы"}]}
|
||||
>
|
||||
<Select style={{width: '100%'}}>
|
||||
{lensTypes.map((type) => (
|
||||
<Select style={lensFormUI.formItemStyle}>
|
||||
{lensFormData.lensTypes.map((type) => (
|
||||
<Select.Option key={type.id} value={type.id}>
|
||||
{type.title}
|
||||
</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 PropTypes from "prop-types";
|
||||
import {LensPropType} from "../../Types/lensPropType.js";
|
||||
import {LensPropType} from "../../../../../Types/lensPropType.js";
|
||||
|
||||
const {Text, Title} = Typography;
|
||||
|
||||
@ -23,7 +23,7 @@ import {
|
||||
BuildOutlined
|
||||
} from "@ant-design/icons";
|
||||
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 LoadingIndicator from "../../Widgets/LoadingIndicator.jsx";
|
||||
import useLenses from "./useLenses.js";
|
||||
|
||||
@ -1,64 +1,30 @@
|
||||
import {useEffect} from "react";
|
||||
import {Modal, Form, Input, DatePicker, notification} from "antd";
|
||||
import {Modal, Form, Input, DatePicker} from "antd";
|
||||
import PropTypes from "prop-types";
|
||||
import locale from "antd/es/date-picker/locale/ru_RU";
|
||||
import validator from "validator";
|
||||
import {MaskedInput} from "antd-mask-input";
|
||||
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 PatientFormModal = ({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]);
|
||||
|
||||
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 patientFormModalUI = usePatientFormModalUI(visible, onCancel, onSubmit, patient);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title={patient ? "Редактировать пациента" : "Добавить пациента"}
|
||||
title={patientFormModalUI.modalTitle}
|
||||
open={visible}
|
||||
onCancel={() => {
|
||||
form.resetFields();
|
||||
onCancel();
|
||||
}}
|
||||
onOk={handleOk}
|
||||
onCancel={patientFormModalUI.handleCancel}
|
||||
onOk={patientFormModalUI.handleOk}
|
||||
okText={"Сохранить"}
|
||||
cancelText={"Отмена"}
|
||||
maskClosable={false}
|
||||
forceRender={true}
|
||||
style={{marginTop: 20, marginBottom: 50}}
|
||||
style={patientFormModalUI.modalStyle}
|
||||
centered
|
||||
>
|
||||
<Form form={form} layout={"vertical"}>
|
||||
<Form form={patientFormModalUI.form} layout={"vertical"}>
|
||||
<Form.Item
|
||||
name="first_name"
|
||||
label="Имя"
|
||||
@ -101,12 +67,7 @@ const PatientFormModal = ({visible, onCancel, onSubmit, patient}) => {
|
||||
label="Email"
|
||||
rules={[
|
||||
{
|
||||
validator: (_, value) => {
|
||||
if (value && !validator.isEmail(value)) {
|
||||
return Promise.reject("Некорректный email");
|
||||
}
|
||||
return Promise.resolve();
|
||||
},
|
||||
validator: patientFormModalUI.emailValidator,
|
||||
},
|
||||
]}
|
||||
>
|
||||
@ -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 PropTypes from "prop-types";
|
||||
import {PatientPropType} from "../../Types/patientPropType.js";
|
||||
import {PatientPropType} from "../../../../../Types/patientPropType.js";
|
||||
|
||||
const { Text, Title } = Typography;
|
||||
|
||||
const PatientViewModal = ({ visible, onCancel, patient }) => {
|
||||
if (!patient) return null;
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title="Просмотр пациента"
|
||||
@ -73,7 +71,7 @@ const PatientViewModal = ({ visible, onCancel, patient }) => {
|
||||
PatientViewModal.propTypes = {
|
||||
visible: PropTypes.bool.isRequired,
|
||||
onCancel: PropTypes.func.isRequired,
|
||||
patient: PatientPropType.isRequired,
|
||||
patient: PatientPropType,
|
||||
};
|
||||
|
||||
export default PatientViewModal;
|
||||
@ -19,7 +19,7 @@ import {
|
||||
TeamOutlined
|
||||
} from "@ant-design/icons";
|
||||
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 LoadingIndicator from "../../Widgets/LoadingIndicator.jsx";
|
||||
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 {PlusOutlined, SwitcherOutlined} from "@ant-design/icons";
|
||||
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 useSets from "./useSets.js";
|
||||
import useSetsUI from "./useSetsUI.js";
|
||||
@ -74,6 +74,7 @@ const SetsPage = () => {
|
||||
onSubmit={setsData.handleModalSetSubmit}
|
||||
setData={setsUI.selectedSet}
|
||||
/>
|
||||
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@ -16,7 +16,7 @@ const useSets = () => {
|
||||
selectedSet,
|
||||
} = useSelector(state => state.setsUI);
|
||||
|
||||
const {data: sets = [], isLoading, isError} = useGetSetsQuery({
|
||||
const {data: sets = [], isLoading, isError} = useGetSetsQuery(undefined, {
|
||||
pollingInterval: 20000,
|
||||
});
|
||||
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 {lensIssuesApi} from "../Api/lensIssuesApi.js";
|
||||
import lensIssuesReducer from "./Slices/lensIssuesSlice.js";
|
||||
import {lensTypesApi} from "../Api/lensTypesApi.js";
|
||||
|
||||
export const store = configureStore({
|
||||
reducer: {
|
||||
@ -22,6 +23,8 @@ export const store = configureStore({
|
||||
|
||||
[setContentApi.reducerPath]: setContentApi.reducer,
|
||||
|
||||
[lensTypesApi.reducerPath]: lensTypesApi.reducer,
|
||||
|
||||
[lensIssuesApi.reducerPath]: lensIssuesApi.reducer,
|
||||
lensIssuesUI: lensIssuesReducer,
|
||||
},
|
||||
@ -31,9 +34,10 @@ export const store = configureStore({
|
||||
lensesApi.middleware,
|
||||
setsApi.middleware,
|
||||
setContentApi.middleware,
|
||||
lensTypesApi.middleware,
|
||||
lensIssuesApi.middleware,
|
||||
)
|
||||
)
|
||||
});
|
||||
|
||||
export default store;
|
||||
export default store;
|
||||
Loading…
x
Reference in New Issue
Block a user