сделал основу вкладки с наборами линз, а также методы API, и компонент модального окна
This commit is contained in:
parent
b0bca54f22
commit
033776deb6
@ -2,9 +2,9 @@ import axios from "axios";
|
|||||||
import CONFIG from "../../core/Config.jsx";
|
import CONFIG from "../../core/Config.jsx";
|
||||||
|
|
||||||
|
|
||||||
const addSetContent = async (token, set_content) => {
|
const addSetContent = async (token, set_content, set_id) => {
|
||||||
try {
|
try {
|
||||||
const response = await axios.post(`${CONFIG.BASE_URL}/set_content/`, set_content, {
|
const response = await axios.post(`${CONFIG.BASE_URL}/set_content/${set_id}/`, set_content, {
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${token}`,
|
Authorization: `Bearer ${token}`,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -2,9 +2,9 @@ import axios from "axios";
|
|||||||
import CONFIG from "../../core/Config.jsx";
|
import CONFIG from "../../core/Config.jsx";
|
||||||
|
|
||||||
|
|
||||||
const AddSet = async (token, set) => {
|
const addSet = async (token, set) => {
|
||||||
try {
|
try {
|
||||||
const response = await axios.post(`${CONFIG.API_URL}/sets`, set, {
|
const response = await axios.post(`${CONFIG.BASE_URL}/sets/`, set, {
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${token}`,
|
Authorization: `Bearer ${token}`,
|
||||||
},
|
},
|
||||||
@ -18,4 +18,4 @@ const AddSet = async (token, set) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export default AddSet;
|
export default addSet;
|
||||||
@ -1,10 +1,10 @@
|
|||||||
import { Card, Modal, Tooltip } from "antd";
|
import {Card, Modal, 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 "./LensViewModal.jsx";
|
import LensViewModal from "./LensViewModal.jsx";
|
||||||
|
|
||||||
const LensListCard = ({ lens, handleEditLens, handleDeleteLens }) => {
|
const LensListCard = ({lens, handleEditLens, handleDeleteLens}) => {
|
||||||
const [showModalInfo, setShowModalInfo] = useState(false);
|
const [showModalInfo, setShowModalInfo] = useState(false);
|
||||||
|
|
||||||
const deleteLensConfirm = () => {
|
const deleteLensConfirm = () => {
|
||||||
@ -23,15 +23,15 @@ const LensListCard = ({ lens, handleEditLens, handleDeleteLens }) => {
|
|||||||
|
|
||||||
const actions = [
|
const actions = [
|
||||||
<Tooltip title="Просмотр линзы" key={"viewLens"}>
|
<Tooltip title="Просмотр линзы" key={"viewLens"}>
|
||||||
<EyeOutlined onClick={handleViewLens} />
|
<EyeOutlined onClick={handleViewLens}/>
|
||||||
</Tooltip>,
|
</Tooltip>,
|
||||||
<Tooltip title="Редактирование линзы" key={"editLens"}>
|
<Tooltip title="Редактирование линзы" key={"editLens"}>
|
||||||
<EditOutlined onClick={() => handleEditLens(lens)} />
|
<EditOutlined onClick={() => handleEditLens(lens)}/>
|
||||||
</Tooltip>,
|
</Tooltip>,
|
||||||
<Tooltip title="Удаление линзы" key={"deleteLens"}>
|
<Tooltip title="Удаление линзы" key={"deleteLens"}>
|
||||||
<DeleteOutlined
|
<DeleteOutlined
|
||||||
onClick={deleteLensConfirm}
|
onClick={deleteLensConfirm}
|
||||||
style={{ color: "red" }}
|
style={{color: "red"}}
|
||||||
/>
|
/>
|
||||||
</Tooltip>,
|
</Tooltip>,
|
||||||
];
|
];
|
||||||
@ -50,13 +50,11 @@ const LensListCard = ({ lens, handleEditLens, handleDeleteLens }) => {
|
|||||||
{lens.issued && <p><strong>✅ Линза выдана</strong></p>}
|
{lens.issued && <p><strong>✅ Линза выдана</strong></p>}
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
{showModalInfo && (
|
<LensViewModal
|
||||||
<LensViewModal
|
visible={showModalInfo}
|
||||||
visible={showModalInfo}
|
onCancel={() => setShowModalInfo(false)}
|
||||||
onCancel={() => setShowModalInfo(false)}
|
lens={lens}
|
||||||
lens={lens}
|
/>
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -61,14 +61,12 @@ const PatientListCard = ({patient, handleEditPatient, handleDeletePatient}) => {
|
|||||||
{patient.email && <p><strong>✉️ Email:</strong> {patient.email}</p>}
|
{patient.email && <p><strong>✉️ Email:</strong> {patient.email}</p>}
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
{showModalInfo && (
|
<PatientViewModal
|
||||||
<PatientViewModal
|
visible={showModalInfo}
|
||||||
visible={showModalInfo}
|
onCancel={() => setShowModalInfo(false)}
|
||||||
onCancel={() => setShowModalInfo(false)}
|
patient={patient}
|
||||||
patient={patient}
|
/>
|
||||||
/>
|
</>
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
257
web-app/src/components/sets/SetFormModal.jsx
Normal file
257
web-app/src/components/sets/SetFormModal.jsx
Normal file
@ -0,0 +1,257 @@
|
|||||||
|
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 axios from "axios";
|
||||||
|
import getAllLensTypes from "../../api/lens_types/GetAllLensTypes.jsx";
|
||||||
|
import {useAuth} from "../../AuthContext.jsx";
|
||||||
|
import PropTypes from "prop-types";
|
||||||
|
import getSetContentBySetId from "../../api/set_content/GetSetContentBySetId.jsx";
|
||||||
|
|
||||||
|
const {Option} = Select;
|
||||||
|
|
||||||
|
const SetFormModal = ({visible, onCancel, setData, onSubmit}) => {
|
||||||
|
const {user} = 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;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const data = await getSetContentBySetId(user.token, setData.id);
|
||||||
|
setContent(data);
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
notification.error({
|
||||||
|
message: "Ошибка загрузки контента",
|
||||||
|
description: "Проверьте подключение к сети.",
|
||||||
|
placement: "topRight",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchLensTypes = async () => {
|
||||||
|
try {
|
||||||
|
const data = await getAllLensTypes(user.token);
|
||||||
|
setLensTypes(data);
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
notification.error({
|
||||||
|
message: "Ошибка загрузки типов линз",
|
||||||
|
description: "Проверьте подключение к сети.",
|
||||||
|
placement: "topRight",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const addContentItem = () => {
|
||||||
|
setContent([...content, {
|
||||||
|
id: Date.now(),
|
||||||
|
tor: 0,
|
||||||
|
trial: 0,
|
||||||
|
esa: 0,
|
||||||
|
fvc: 0,
|
||||||
|
preset_refraction: 0,
|
||||||
|
diameter: 0,
|
||||||
|
periphery_toricity: 0,
|
||||||
|
side: "left",
|
||||||
|
count: 1,
|
||||||
|
type_id: null
|
||||||
|
}]);
|
||||||
|
};
|
||||||
|
|
||||||
|
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 => {
|
||||||
|
onSubmit({...values, contents: content});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
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="left">Левая</Option>
|
||||||
|
<Option value="right">Правая</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: PropTypes.shape({
|
||||||
|
id: PropTypes.number,
|
||||||
|
title: PropTypes.string,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export default SetFormModal;
|
||||||
@ -0,0 +1,62 @@
|
|||||||
|
import PropTypes from "prop-types";
|
||||||
|
import {Card, Modal, Tooltip} from "antd";
|
||||||
|
import {DeleteOutlined, EditOutlined, PlusOutlined} from "@ant-design/icons";
|
||||||
|
|
||||||
|
const SetListCard = ({set, handleEditSet, handleAddSet, handleDeleteSet}) => {
|
||||||
|
|
||||||
|
const confirmSetDelete = () => {
|
||||||
|
Modal.confirm({
|
||||||
|
title: "Удаление набора",
|
||||||
|
content: `Вы уверены, что хотите удалить набор ${set.title}?`,
|
||||||
|
okText: "Да, удалить",
|
||||||
|
cancelText: "Отмена",
|
||||||
|
onOk: () => handleDeleteSet(set.id),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const actions = [
|
||||||
|
<Tooltip title="Добавить набор" key={"add"}>
|
||||||
|
<PlusOutlined
|
||||||
|
onClick={handleAddSet}
|
||||||
|
/>
|
||||||
|
</Tooltip>,
|
||||||
|
|
||||||
|
<Tooltip title="Редактирование набора" key={"edit"}>
|
||||||
|
<EditOutlined
|
||||||
|
onClick={() => {
|
||||||
|
handleEditSet(set);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Tooltip>,
|
||||||
|
|
||||||
|
<Tooltip title="Удаление набора" key={"delete"}>
|
||||||
|
<DeleteOutlined
|
||||||
|
style={{color: "red"}}
|
||||||
|
onClick={confirmSetDelete}
|
||||||
|
/>
|
||||||
|
</Tooltip>,
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Card
|
||||||
|
type="inner"
|
||||||
|
title={set.title}
|
||||||
|
actions={actions}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
SetListCard.propTypes = {
|
||||||
|
set: PropTypes.shape({
|
||||||
|
id: PropTypes.number.isRequired,
|
||||||
|
title: PropTypes.string.isRequired,
|
||||||
|
}).isRequired,
|
||||||
|
handleEditSet: PropTypes.func.isRequired,
|
||||||
|
handleAddSet: PropTypes.func.isRequired,
|
||||||
|
handleDeleteSet: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export default SetListCard;
|
||||||
@ -1,21 +1,10 @@
|
|||||||
import {useState, useEffect} from "react";
|
|
||||||
import {
|
import {
|
||||||
Input,
|
Tabs
|
||||||
Select,
|
|
||||||
List,
|
|
||||||
FloatButton,
|
|
||||||
Row,
|
|
||||||
Col,
|
|
||||||
Spin,
|
|
||||||
Button,
|
|
||||||
Form,
|
|
||||||
InputNumber,
|
|
||||||
Card, Grid, notification, Tabs
|
|
||||||
} from "antd";
|
} from "antd";
|
||||||
import LensesPage from "../pages/LensesPage.jsx";
|
import LensesPage from "../pages/LensesPage.jsx";
|
||||||
import {FolderViewOutlined, SwitcherOutlined} from "@ant-design/icons";
|
import {FolderViewOutlined, SwitcherOutlined} from "@ant-design/icons";
|
||||||
|
import SetLensesPage from "../pages/SetLensesPage.jsx";
|
||||||
|
|
||||||
const {Option} = Select;
|
|
||||||
|
|
||||||
const items = [
|
const items = [
|
||||||
{
|
{
|
||||||
@ -27,7 +16,7 @@ const items = [
|
|||||||
{
|
{
|
||||||
key: '2',
|
key: '2',
|
||||||
label: 'Наборы линз',
|
label: 'Наборы линз',
|
||||||
children: '1233',
|
children: <SetLensesPage/>,
|
||||||
icon: <SwitcherOutlined/>
|
icon: <SwitcherOutlined/>
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@ -124,11 +124,9 @@ const LensesPage = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleDeleteLens = async (lensId) => {
|
const handleDeleteLens = async (lensId) => {
|
||||||
if (!user || !user.token) return;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await deleteLens(user.token, lensId);
|
await deleteLens(user.token, lensId);
|
||||||
fetchLenses(user.token);
|
await fetchLenses(user.token);
|
||||||
notification.success({
|
notification.success({
|
||||||
message: "Линза удалена",
|
message: "Линза удалена",
|
||||||
description: "Линза успешно удалена.",
|
description: "Линза успешно удалена.",
|
||||||
@ -215,7 +213,6 @@ const LensesPage = () => {
|
|||||||
boxShadow: "0 1px 6px rgba(0, 0, 0, 0.15)",
|
boxShadow: "0 1px 6px rgba(0, 0, 0, 0.15)",
|
||||||
borderRadius: 8
|
borderRadius: 8
|
||||||
}}
|
}}
|
||||||
type={"inner"}
|
|
||||||
>
|
>
|
||||||
<Row gutter={16}>
|
<Row gutter={16}>
|
||||||
<Col xs={24} sm={12}>
|
<Col xs={24} sm={12}>
|
||||||
|
|||||||
@ -96,8 +96,6 @@ const PatientsPage = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleDeletePatient = async (patient_id) => {
|
const handleDeletePatient = async (patient_id) => {
|
||||||
if (!user || !user.token) return;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await deletePatient(user.token, patient_id);
|
await deletePatient(user.token, patient_id);
|
||||||
await fetchPatients();
|
await fetchPatients();
|
||||||
@ -123,19 +121,9 @@ const PatientsPage = () => {
|
|||||||
const handleModalPatientSubmit = async (newPatient) => {
|
const handleModalPatientSubmit = async (newPatient) => {
|
||||||
try {
|
try {
|
||||||
if (selectedPatient) {
|
if (selectedPatient) {
|
||||||
await updatePatient(user.token, selectedPatient.id, newPatient);
|
await editPatient(newPatient);
|
||||||
notification.success({
|
|
||||||
message: "Пациент обновлён",
|
|
||||||
description: `Данные пациента ${newPatient.first_name} ${newPatient.last_name} успешно обновлены.`,
|
|
||||||
placement: "topRight",
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
await addPatient(user.token, newPatient);
|
await addPatient(newPatient);
|
||||||
notification.success({
|
|
||||||
message: "Пациент добавлен",
|
|
||||||
description: `Пациент ${newPatient.first_name} ${newPatient.last_name} успешно добавлен.`,
|
|
||||||
placement: "topRight",
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
setIsModalVisible(false);
|
setIsModalVisible(false);
|
||||||
await fetchPatients();
|
await fetchPatients();
|
||||||
@ -150,6 +138,24 @@ const PatientsPage = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const editPatient = async (patient) => {
|
||||||
|
await updatePatient(user.token, selectedPatient.id, patient);
|
||||||
|
notification.success({
|
||||||
|
message: "Пациент обновлён",
|
||||||
|
description: `Данные пациента ${patient.first_name} ${patient.last_name} успешно обновлены.`,
|
||||||
|
placement: "topRight",
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const addPatient = async (patient) => {
|
||||||
|
await addPatient(user.token, patient);
|
||||||
|
notification.success({
|
||||||
|
message: "Пациент добавлен",
|
||||||
|
description: `Пациент ${patient.first_name} ${patient.last_name} успешно добавлен.`,
|
||||||
|
placement: "topRight",
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{padding: 20}}>
|
<div style={{padding: 20}}>
|
||||||
<Row gutter={[16, 16]} style={{marginBottom: 20}}>
|
<Row gutter={[16, 16]} style={{marginBottom: 20}}>
|
||||||
|
|||||||
@ -1,8 +1,239 @@
|
|||||||
|
import {useAuth} from "../AuthContext.jsx";
|
||||||
|
import {useEffect, useState} from "react";
|
||||||
|
import {Col, FloatButton, Input, List, notification, Row, Select, Spin, Tooltip} from "antd";
|
||||||
|
import getAllSets from "../api/sets/GetAllSets.jsx";
|
||||||
|
import {LoadingOutlined, PlusOutlined} from "@ant-design/icons";
|
||||||
|
import SetListCard from "../components/sets/SetListCard.jsx";
|
||||||
|
import SetFormModal from "../components/sets/SetFormModal.jsx";
|
||||||
|
import updateSet from "../api/sets/UpdateSet.jsx";
|
||||||
|
import addSet from "../api/sets/AddSet.jsx";
|
||||||
|
import deleteSet from "../api/sets/DeleteSet.jsx";
|
||||||
|
import addSetContent from "../api/set_content/AddSetContent.jsx";
|
||||||
|
|
||||||
|
|
||||||
const SetLensesPage = () => {
|
const SetLensesPage = () => {
|
||||||
|
const {user} = useAuth();
|
||||||
|
|
||||||
|
const [current, setCurrent] = useState(1);
|
||||||
|
const [pageSize, setPageSize] = useState(10);
|
||||||
|
|
||||||
|
const [searchText, setSearchText] = useState("");
|
||||||
|
const [sets, setSets] = useState([]);
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
const [isModalVisible, setIsModalVisible] = useState(false);
|
||||||
|
const [selectedSet, setSelectedSet] = useState(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetchSetsWithCache();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isModalVisible) {
|
||||||
|
const intervalId = setInterval(fetchSets, 5000);
|
||||||
|
return () => clearInterval(intervalId);
|
||||||
|
}
|
||||||
|
}, [user, isModalVisible]);
|
||||||
|
|
||||||
|
const fetchSetsWithCache = async () => {
|
||||||
|
const cachedData = localStorage.getItem("setsData");
|
||||||
|
const cacheTimestamp = localStorage.getItem("setsTimestamp");
|
||||||
|
|
||||||
|
if (cachedData && cacheTimestamp && (Date.now() - parseInt(cacheTimestamp)) < 60 * 1000) {
|
||||||
|
setSets(JSON.parse(cachedData));
|
||||||
|
setLoading(false);
|
||||||
|
} else {
|
||||||
|
await fetchSets();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchSets = async () => {
|
||||||
|
if (!user || !user.token) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const data = await getAllSets(user.token);
|
||||||
|
setSets(data);
|
||||||
|
|
||||||
|
localStorage.setItem("setsData", JSON.stringify(data));
|
||||||
|
localStorage.setItem("setsTimestamp", Date.now().toString());
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
notification.error({
|
||||||
|
message: "Ошибка загрузки данных",
|
||||||
|
description: "Проверьте подключение к сети.",
|
||||||
|
placement: "topRight",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const filteredSets = sets.filter(set => set.title.toLowerCase().includes(searchText.toLowerCase()));
|
||||||
|
|
||||||
|
const handleAddSet = () => {
|
||||||
|
setSelectedSet(null);
|
||||||
|
setIsModalVisible(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleEditSet = (set) => {
|
||||||
|
setSelectedSet(set);
|
||||||
|
setIsModalVisible(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleDeleteSet = async (set_id) => {
|
||||||
|
try {
|
||||||
|
await deleteSet(user.token, set_id);
|
||||||
|
notification.success({
|
||||||
|
message: "Набор удален",
|
||||||
|
description: "Набор успешно удален.",
|
||||||
|
placement: "topRight",
|
||||||
|
});
|
||||||
|
await fetchSets();
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Ошибка удаления набора:", error);
|
||||||
|
notification.error({
|
||||||
|
message: "Ошибка удаления набора",
|
||||||
|
description: "Проверьте подключение к сети.",
|
||||||
|
placement: "topRight",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleCancel = () => {
|
||||||
|
setIsModalVisible(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleModalSetSubmit = async (set, content = []) => {
|
||||||
|
try {
|
||||||
|
let refreshed_set;
|
||||||
|
|
||||||
|
if (selectedSet) {
|
||||||
|
refreshed_set = await editCurrentSet(set);
|
||||||
|
} else {
|
||||||
|
refreshed_set = await addNewSet(set);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (refreshed_set) {
|
||||||
|
await setContent(content, refreshed_set.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsModalVisible(false);
|
||||||
|
await fetchSets();
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Ошибка сохранения набора:", error);
|
||||||
|
notification.error({
|
||||||
|
message: "Ошибка сохранения набора",
|
||||||
|
description: "Проверьте подключение к сети.",
|
||||||
|
placement: "topRight",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const setContent = async (content, set_id) => {
|
||||||
|
try {
|
||||||
|
await addSetContent(user.token, content, set_id);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Ошибка сохранения набора:", error);
|
||||||
|
notification.error({
|
||||||
|
message: "Ошибка сохранения набора",
|
||||||
|
description: "Проверьте подключение к сети.",
|
||||||
|
placement: "topRight",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const editCurrentSet = async (set) => {
|
||||||
|
const refreshed_set = await updateSet(user.token, selectedSet.id, set);
|
||||||
|
notification.success({
|
||||||
|
message: "Набор обновлен",
|
||||||
|
description: "Набор успешно обновлен.",
|
||||||
|
placement: "topRight",
|
||||||
|
});
|
||||||
|
return refreshed_set;
|
||||||
|
};
|
||||||
|
|
||||||
|
const addNewSet = async (set) => {
|
||||||
|
const refreshed_set = await addSet(user.token, set);
|
||||||
|
notification.success({
|
||||||
|
message: "Набор добавлен",
|
||||||
|
description: "Набор успешно добавлен.",
|
||||||
|
placement: "topRight",
|
||||||
|
});
|
||||||
|
return refreshed_set;
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{padding: 20}}>
|
||||||
|
<Row style={{marginBottom: 20}}>
|
||||||
|
<Input
|
||||||
|
placeholder="Поиск набора"
|
||||||
|
onChange={(e) => setSearchText(e.target.value)}
|
||||||
|
style={{width: "100%"}}
|
||||||
|
allowClear
|
||||||
|
/>
|
||||||
|
</Row>
|
||||||
|
|
||||||
|
{loading ? (
|
||||||
|
<div style={{
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems: "center",
|
||||||
|
height: "100vh",
|
||||||
|
}}>
|
||||||
|
<Spin indicator={<LoadingOutlined style={{fontSize: 64, color: "#1890ff"}} spin/>}/>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<List
|
||||||
|
grid={{
|
||||||
|
gutter: 16,
|
||||||
|
xs: 1,
|
||||||
|
sm: 1,
|
||||||
|
md: 2,
|
||||||
|
lg: 2,
|
||||||
|
xl: 3,
|
||||||
|
xxl: 3,
|
||||||
|
}}
|
||||||
|
dataSource={filteredSets}
|
||||||
|
renderItem={(set) => (
|
||||||
|
<List.Item>
|
||||||
|
<SetListCard
|
||||||
|
set={set}
|
||||||
|
handleEditSet={handleEditSet}
|
||||||
|
handleAddSet={handleAddSet}
|
||||||
|
handleDeleteSet={handleDeleteSet}
|
||||||
|
/>
|
||||||
|
</List.Item>
|
||||||
|
)}
|
||||||
|
pagination={{
|
||||||
|
current,
|
||||||
|
pageSize,
|
||||||
|
showSizeChanger: true,
|
||||||
|
pageSizeOptions: ["5", "10", "20", "50"],
|
||||||
|
onChange: (page, newPageSize) => {
|
||||||
|
setCurrent(page);
|
||||||
|
setPageSize(newPageSize);
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<FloatButton
|
||||||
|
icon={<PlusOutlined/>}
|
||||||
|
type="primary"
|
||||||
|
style={{position: "fixed", bottom: 40, right: 40}}
|
||||||
|
tooltip="Добавить набор"
|
||||||
|
onClick={handleAddSet}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<SetFormModal
|
||||||
|
visible={isModalVisible}
|
||||||
|
onCancel={handleCancel}
|
||||||
|
onSubmit={handleModalSetSubmit}
|
||||||
|
setData={selectedSet}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default SetLensesPage;
|
export default SetLensesPage;
|
||||||
Loading…
x
Reference in New Issue
Block a user