From 1c53632382eef77cb664bbbcbec43badd2046873 Mon Sep 17 00:00:00 2001 From: Andrei Duvakin Date: Thu, 13 Feb 2025 20:08:58 +0500 Subject: [PATCH] =?UTF-8?q?=D1=81=D0=B4=D0=B5=D0=BB=D0=B0=D0=BB=20=D1=83?= =?UTF-8?q?=D0=B4=D0=B0=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BF=D0=B0=D1=86?= =?UTF-8?q?=D0=B8=D0=B5=D0=BD=D1=82=D0=B0=20=D0=B8=20=D0=BF=D0=B5=D1=80?= =?UTF-8?q?=D0=B5=D0=BD=D0=B5=D1=81=20=D0=B4=D0=B5=D0=B9=D1=81=D0=B2=D1=82?= =?UTF-8?q?=D0=B8=D1=8F=20=D1=80=D0=B5=D0=B4=D0=B0=D0=BA=D1=82=D0=B8=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8=D1=8F=20=D0=B2=20=D0=BA=D0=BD?= =?UTF-8?q?=D0=BE=D0=BF=D0=BA=D0=B8=20=D0=BD=D0=B0=20=D0=BA=D0=B0=D1=80?= =?UTF-8?q?=D1=82=D0=BE=D1=87=D0=BA=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/app/controllers/patients_router.py | 9 +++++ api/app/infrastructure/auth_service.py | 3 +- api/app/infrastructure/patients_service.py | 11 +++++- web-app/src/api/patients/AddPatient.jsx | 2 +- web-app/src/api/patients/DeletePatient.jsx | 19 ++++++++++ web-app/src/api/patients/GetAllPatients.jsx | 1 + web-app/src/components/PatientListCard.jsx | 39 ++++++++++++++++--- web-app/src/layouts/MainLayout.jsx | 2 +- web-app/src/pages/PatientsPage.jsx | 42 +++++++++++++-------- 9 files changed, 103 insertions(+), 25 deletions(-) create mode 100644 web-app/src/api/patients/DeletePatient.jsx diff --git a/api/app/controllers/patients_router.py b/api/app/controllers/patients_router.py index e547458..5345adc 100644 --- a/api/app/controllers/patients_router.py +++ b/api/app/controllers/patients_router.py @@ -37,3 +37,12 @@ async def update_patient( ): patients_service = PatientsService(db) return await patients_service.update_patient(patient_id, patient) + +@router.delete("/patients/{patient_id}/", response_model=bool) +async def delete_patient( + patient_id: int, + db: AsyncSession = Depends(get_db), + user=Depends(get_current_user) +): + patient_service = PatientsService(db) + return await patient_service.delete_patient(patient_id) diff --git a/api/app/infrastructure/auth_service.py b/api/app/infrastructure/auth_service.py index bd48193..3aee7bd 100644 --- a/api/app/infrastructure/auth_service.py +++ b/api/app/infrastructure/auth_service.py @@ -2,6 +2,7 @@ import datetime from typing import Optional import jwt +from fastapi import HTTPException from sqlalchemy.ext.asyncio import AsyncSession from app.application.users_repository import UsersRepository @@ -21,7 +22,7 @@ class AuthService: "user_id": user.id } - return None + raise HTTPException(status_code=403, detail="Invalid login or password") @staticmethod def create_access_token(data: dict) -> str: diff --git a/api/app/infrastructure/patients_service.py b/api/app/infrastructure/patients_service.py index 6b2d137..9ea8964 100644 --- a/api/app/infrastructure/patients_service.py +++ b/api/app/infrastructure/patients_service.py @@ -1,5 +1,6 @@ from typing import Optional +from fastapi import HTTPException from sqlalchemy.ext.asyncio import AsyncSession from app.application.patients_repository import PatientsRepository @@ -81,4 +82,12 @@ class PatientsService: correction=patient_model.correction, ) - return None + raise HTTPException(status_code=404, detail="Patient not found") + + async def delete_patient(self, patient_id: int) -> Optional[bool]: + result = await self.patient_repository.delete(patient_id) is not None + + if not result: + raise HTTPException(status_code=404, detail="Patient not found") + + return result diff --git a/web-app/src/api/patients/AddPatient.jsx b/web-app/src/api/patients/AddPatient.jsx index ab6dd7a..c97b5fa 100644 --- a/web-app/src/api/patients/AddPatient.jsx +++ b/web-app/src/api/patients/AddPatient.jsx @@ -11,7 +11,7 @@ const AddPatient = async (token, patient) => { }); return response.data; } catch (error) { - throw new Error(error.response.data.message); + throw new Error(error.message); } }; diff --git a/web-app/src/api/patients/DeletePatient.jsx b/web-app/src/api/patients/DeletePatient.jsx new file mode 100644 index 0000000..a359557 --- /dev/null +++ b/web-app/src/api/patients/DeletePatient.jsx @@ -0,0 +1,19 @@ +import axios from "axios"; +import CONFIG from "../../core/Config.jsx"; + + +const DeletePatient = async (token, patient_id) => { + try { + const response = await axios.delete(`${CONFIG.BASE_URL}/patients/${patient_id}/`, { + headers: { + Authorization: `Bearer ${token}`, + }, + }); + return response.data; + } catch (error) { + throw new Error(error.message); + } +} + + +export default DeletePatient; \ No newline at end of file diff --git a/web-app/src/api/patients/GetAllPatients.jsx b/web-app/src/api/patients/GetAllPatients.jsx index 48d8b09..f6b7954 100644 --- a/web-app/src/api/patients/GetAllPatients.jsx +++ b/web-app/src/api/patients/GetAllPatients.jsx @@ -1,6 +1,7 @@ import axios from "axios"; import CONFIG from "../../core/Config.jsx"; + const getAllPatients = async (token) => { if (!token) { diff --git a/web-app/src/components/PatientListCard.jsx b/web-app/src/components/PatientListCard.jsx index 21db7c3..ff188b7 100644 --- a/web-app/src/components/PatientListCard.jsx +++ b/web-app/src/components/PatientListCard.jsx @@ -1,17 +1,43 @@ -import { Card } from "antd"; +import {Card, Modal} from "antd"; import PropTypes from "prop-types"; +import {DeleteOutlined, EditOutlined} from "@ant-design/icons"; -const PatientListCard = ({ patient }) => { +const PatientListCard = ({patient, handleEditPatient, handleDeletePatient}) => { const birthday = new Date(patient.birthday) + const deletePatientConfirm = () => { + Modal.confirm({ + title: "Удаление пациента", + content: `Вы уверены, что хотите удалить пациента ${patient.last_name} ${patient.first_name}?`, + okText: "Да, удалить", + cancelText: "Отмена", + onOk: () => handleDeletePatient(patient.id), + }); + }; + return ( { + handleEditPatient(patient); + }} + key={"editPatient"} + />, + + ]} > -

📅 Дата рождения: {birthday.toLocaleString('ru-RU', {month: 'long', day: 'numeric', year: 'numeric'})}

+

📅 Дата рождения: {birthday.toLocaleString('ru-RU', { + month: 'long', + day: 'numeric', + year: 'numeric' + })}

{patient.phone &&

📞 Телефон: {patient.phone}

} {patient.email &&

✉️ Email: {patient.email}

}
@@ -20,6 +46,7 @@ const PatientListCard = ({ patient }) => { PatientListCard.propTypes = { patient: PropTypes.shape({ + id: PropTypes.number.isRequired, last_name: PropTypes.string.isRequired, first_name: PropTypes.string.isRequired, patronymic: PropTypes.string, @@ -30,6 +57,8 @@ PatientListCard.propTypes = { diagnosis: PropTypes.string, correction: PropTypes.string, }).isRequired, + handleEditPatient: PropTypes.func.isRequired, + handleDeletePatient: PropTypes.func.isRequired, }; export default PatientListCard; diff --git a/web-app/src/layouts/MainLayout.jsx b/web-app/src/layouts/MainLayout.jsx index 643342c..0784e97 100644 --- a/web-app/src/layouts/MainLayout.jsx +++ b/web-app/src/layouts/MainLayout.jsx @@ -26,9 +26,9 @@ const MainLayout = () => { const menuItems = [ getItem("Главная", "/", ), getItem("Приёмы", "/appointments", ), + getItem("Выдачи линз", "/dispensing", ), getItem("Линзы", "/lenses", ), getItem("Пациенты", "/patients", ), - getItem("Выдачи линз", "/dispensing", ), getItem("Рассылки", "/mailing", ), {type: "divider"}, getItem("Мой профиль", "profile", , [ diff --git a/web-app/src/pages/PatientsPage.jsx b/web-app/src/pages/PatientsPage.jsx index a562e7c..69ebfa5 100644 --- a/web-app/src/pages/PatientsPage.jsx +++ b/web-app/src/pages/PatientsPage.jsx @@ -1,12 +1,13 @@ import {useEffect, useState} from "react"; -import {Input, Select, List, FloatButton, Row, Col, message, Spin} from "antd"; +import {Input, Select, List, FloatButton, Row, Col, Spin} 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/PatientListCard.jsx"; import PatientModal from "../components/PatientModal.jsx"; import updatePatient from "../api/patients/UpdatePatient.jsx"; -import addPatient from "../api/patients/AddPatient.jsx"; // Подключаем модальное окно +import addPatient from "../api/patients/AddPatient.jsx"; +import deletePatient from "../api/patients/DeletePatient.jsx"; // Подключаем модальное окно const {Option} = Select; @@ -65,11 +66,22 @@ const PatientsPage = () => { setIsModalVisible(true); }; + const handleDeletePatient = async (patient_id) => { + if (!user || !user.token) return; + + try { + await deletePatient(user.token, patient_id); + await fetchPatients(); + } catch (err) { + setError(err.message); + } + }; + const handleCancel = () => { setIsModalVisible(false); }; - const handleSubmit = async (newPatient) => { + const handleModalPatientSubmit = async (newPatient) => { if (selectedPatient) { try { @@ -83,7 +95,6 @@ const PatientsPage = () => { } - if (!selectedPatient) { try { @@ -98,6 +109,7 @@ const PatientsPage = () => { } setIsModalVisible(false); + await fetchPatients(); }; return (
@@ -127,22 +139,20 @@ const PatientsPage = () => { ( { - handleEditPatient(patient); - }} - > - - )} + renderItem={(patient) => ( + + + + )} pagination={{ current, pageSize, showSizeChanger: true, pageSizeOptions: ["5", "10", "20", "50"], - onChange: (page, newPageSize) => { - setCurrent(page); - setPageSize(newPageSize); - }, }} /> )} @@ -157,7 +167,7 @@ const PatientsPage = () => {
);