visus-plus/web-app/src/pages/PatientsPage.jsx

236 lines
8.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import {useEffect, useState} from "react";
import {Input, Select, List, FloatButton, Row, Col, Spin, notification} from "antd";
import {LoadingOutlined, PlusOutlined} from "@ant-design/icons";
import {useAuth} from "../AuthContext.jsx";
import getAllPatients from "../api/patients/GetAllPatients.jsx";
import PatientListCard from "../components/patients/PatientListCard.jsx";
import PatientFormModal from "../components/patients/PatientFormModal.jsx";
import updatePatient from "../api/patients/UpdatePatient.jsx";
import addPatient from "../api/patients/AddPatient.jsx";
import deletePatient from "../api/patients/DeletePatient.jsx";
const {Option} = Select;
const PatientsPage = () => {
const {user} = useAuth();
const [searchText, setSearchText] = useState("");
const [sortOrder, setSortOrder] = useState("asc");
const [patients, setPatients] = useState([]);
const [current, setCurrent] = useState(1);
const [pageSize, setPageSize] = useState(10);
const [isModalVisible, setIsModalVisible] = useState(false);
const [selectedPatient, setSelectedPatient] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetchPatientsWithCache();
}, []);
useEffect(() => {
if (!isModalVisible) {
const intervalId = setInterval(fetchPatients, 5000);
return () => clearInterval(intervalId);
}
}, [user, isModalVisible]);
const fetchPatientsWithCache = async () => {
const cachedData = localStorage.getItem("patientsData");
const cacheTimestamp = localStorage.getItem("patientsTimestamp");
if (cachedData && cacheTimestamp && (Date.now() - parseInt(cacheTimestamp)) < 60 * 1000) {
setPatients(JSON.parse(cachedData));
setLoading(false);
return;
}
await fetchPatients();
};
const fetchPatients = async () => {
if (!user || !user.token) return;
try {
const data = await getAllPatients(user.token);
setPatients(data);
localStorage.setItem("patientsData", JSON.stringify(data));
localStorage.setItem("patientsTimestamp", Date.now().toString());
} catch (error) {
console.log(error);
notification.error({
message: "Ошибка загрузки данных",
description: "Проверьте подключение к сети.",
placement: "topRight",
})
}
if (loading) {
setLoading(false);
}
};
const filteredPatients = patients
.filter((patient) => {
const searchLower = searchText.toLowerCase();
return Object.values(patient)
.filter(value => typeof value === "string")
.some(value => value.toLowerCase().includes(searchLower));
})
.sort((a, b) => {
const fullNameA = `${a.last_name} ${a.first_name}`;
const fullNameB = `${b.last_name} ${b.first_name}`;
return sortOrder === "asc" ? fullNameA.localeCompare(fullNameB) : fullNameB.localeCompare(fullNameA);
});
const handleAddPatient = () => {
setSelectedPatient(null);
setIsModalVisible(true);
};
const handleEditPatient = (patient) => {
setSelectedPatient(patient);
setIsModalVisible(true);
};
const handleDeletePatient = async (patient_id) => {
if (!user || !user.token) return;
try {
await deletePatient(user.token, patient_id);
await fetchPatients();
notification.success({
message: "Пациент удалён",
description: "Пациент успешно удалён из базы.",
placement: "topRight",
});
} catch (error) {
console.log(error);
notification.error({
message: "Ошибка удаления",
description: "Не удалось удалить пациента.",
placement: "topRight",
});
}
};
const handleCancel = () => {
setIsModalVisible(false);
};
const handleModalPatientSubmit = async (newPatient) => {
try {
if (selectedPatient) {
await updatePatient(user.token, selectedPatient.id, newPatient);
notification.success({
message: "Пациент обновлён",
description: `Данные пациента ${newPatient.first_name} ${newPatient.last_name} успешно обновлены.`,
placement: "topRight",
});
} else {
await addPatient(user.token, newPatient);
notification.success({
message: "Пациент добавлен",
description: `Пациент ${newPatient.first_name} ${newPatient.last_name} успешно добавлен.`,
placement: "topRight",
});
}
setIsModalVisible(false);
await fetchPatients();
} catch (error) {
notification.error({
message: "Ошибка",
description: error.response?.status === 401
? "Ошибка авторизации: пользователь не найден или токен недействителен"
: "Не удалось сохранить данные пациента.",
placement: "topRight",
});
}
};
return (
<div style={{padding: 20}}>
<Row gutter={[16, 16]} style={{marginBottom: 20}}>
<Col xs={24} sm={16}>
<Input
placeholder="Поиск пациента"
onChange={(e) => setSearchText(e.target.value)}
style={{width: "100%"}}
allowClear
/>
</Col>
<Col xs={24} sm={8}>
<Select
value={sortOrder}
onChange={(value) => setSortOrder(value)}
style={{width: "100%"}}
>
<Option value="asc">А-Я</Option>
<Option value="desc">Я-А</Option>
</Select>
</Col>
</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={filteredPatients}
renderItem={(patient) => (
<List.Item>
<PatientListCard
patient={patient}
handleEditPatient={handleEditPatient}
handleDeletePatient={handleDeletePatient}
/>
</List.Item>
)}
pagination={{
current,
pageSize,
showSizeChanger: true,
pageSizeOptions: ["5", "10", "20", "50"],
onChange: (page, newPageSize) => {
setCurrent(page);
setPageSize(newPageSize);
},
}}
/>
)}
<FloatButton
icon={<PlusOutlined/>}
style={{position: "fixed", bottom: 20, right: 20}}
onClick={handleAddPatient}
/>
<PatientFormModal
visible={isModalVisible}
onCancel={handleCancel}
onSubmit={handleModalPatientSubmit}
patient={selectedPatient}
/>
</div>
);
};
export default PatientsPage;