diff --git a/api/app/application/scheduled_appointments_repository.py b/api/app/application/scheduled_appointments_repository.py index 9bebcc8..6692e84 100644 --- a/api/app/application/scheduled_appointments_repository.py +++ b/api/app/application/scheduled_appointments_repository.py @@ -17,7 +17,7 @@ class ScheduledAppointmentsRepository: .options(joinedload(ScheduledAppointment.type)) .options(joinedload(ScheduledAppointment.patient)) .options(joinedload(ScheduledAppointment.doctor)) - .order_by(desc(ScheduledAppointment.appointment_datetime)) + .order_by(desc(ScheduledAppointment.scheduled_datetime)) ) result = await self.db.execute(stmt) return result.scalars().all() @@ -29,7 +29,7 @@ class ScheduledAppointmentsRepository: .options(joinedload(ScheduledAppointment.patient)) .options(joinedload(ScheduledAppointment.doctor)) .filter(ScheduledAppointment.doctor_id == doctor_id) - .order_by(desc(ScheduledAppointment.appointment_datetime)) + .order_by(desc(ScheduledAppointment.scheduled_datetime)) ) result = await self.db.execute(stmt) return result.scalars().all() @@ -41,7 +41,7 @@ class ScheduledAppointmentsRepository: .options(joinedload(ScheduledAppointment.patient)) .options(joinedload(ScheduledAppointment.doctor)) .filter(ScheduledAppointment.patient_id == patient_id) - .order_by(desc(ScheduledAppointment.appointment_datetime)) + .order_by(desc(ScheduledAppointment.scheduled_datetime)) ) result = await self.db.execute(stmt) return result.scalars().all() diff --git a/web-app/src/components/PrivateRoute.jsx b/web-app/src/components/PrivateRoute.jsx index f6e6b84..c63bd7c 100644 --- a/web-app/src/components/PrivateRoute.jsx +++ b/web-app/src/components/PrivateRoute.jsx @@ -12,4 +12,4 @@ const PrivateRoute = () => { }; -export default PrivateRoute; +export default PrivateRoute; \ No newline at end of file diff --git a/web-app/src/components/SelectViewMode.jsx b/web-app/src/components/SelectViewMode.jsx index cc48c28..7ce9659 100644 --- a/web-app/src/components/SelectViewMode.jsx +++ b/web-app/src/components/SelectViewMode.jsx @@ -2,6 +2,7 @@ import {BuildOutlined, TableOutlined} from "@ant-design/icons"; import {Select, Tooltip} from "antd"; import PropTypes from "prop-types"; import {cacheInfo} from "../utils/cachedInfoUtils.jsx"; +import {ViewModPropType} from "../types/viewModPropType.jsx"; const {Option} = Select; @@ -35,11 +36,7 @@ SelectViewMode.propTypes = { setViewMode: PropTypes.func.isRequired, localStorageKey: PropTypes.string.isRequired, toolTipText: PropTypes.string.isRequired, - viewModes: PropTypes.arrayOf(PropTypes.shape({ - value: PropTypes.string.isRequired, - label: PropTypes.string.isRequired, - icon: PropTypes.element, - })).isRequired, + viewModes: PropTypes.arrayOf(ViewModPropType).isRequired, }; export default SelectViewMode; \ No newline at end of file diff --git a/web-app/src/components/appointments/AppointmentCellViewModal.jsx b/web-app/src/components/appointments/AppointmentCellViewModal.jsx new file mode 100644 index 0000000..65892cb --- /dev/null +++ b/web-app/src/components/appointments/AppointmentCellViewModal.jsx @@ -0,0 +1,7 @@ + + + + +const AppointmentCellViewModal = ({appointment}) => { + +}; \ No newline at end of file diff --git a/web-app/src/components/appointments/CalendarCell.jsx b/web-app/src/components/appointments/CalendarCell.jsx new file mode 100644 index 0000000..591360c --- /dev/null +++ b/web-app/src/components/appointments/CalendarCell.jsx @@ -0,0 +1,109 @@ +import {useEffect, useRef, useState} from "react"; +import {Badge, Col, Tag, Tooltip, Typography} from "antd"; +import dayjs from "dayjs"; +import PropTypes from "prop-types"; +import {AppointmentPropType} from "../../types/appointmentPropType.jsx"; +import {ScheduledAppointmentPropType} from "../../types/scheduledAppointmentPropType.jsx"; + + +const CalendarCell = ({appointments, scheduledAppointments, onCellClick, onItemClick}) => { + const containerRef = useRef(null); + const [isCompressed, setIsCompressed] = useState(false); + const COMPRESSION_THRESHOLD = 70; + + useEffect(() => { + if (!containerRef.current) return; + + const observer = new ResizeObserver((entries) => { + const width = entries[0].contentRect.width; + setIsCompressed(width < COMPRESSION_THRESHOLD); + }); + + observer.observe(containerRef.current); + return () => observer.disconnect(); + }, []); + + return ( +
+ {!isCompressed && ( + + )} + {isCompressed && ( +
+ {appointments.length + scheduledAppointments.length > 0 && `+${appointments.length + scheduledAppointments.length}`} +
+ )} +
+ ); +}; + +CalendarCell.propTypes = { + appointments: PropTypes.arrayOf(AppointmentPropType).isRequired, + scheduledAppointments: PropTypes.arrayOf(ScheduledAppointmentPropType).isRequired, + onCellClick: PropTypes.func.isRequired, + onItemClick: PropTypes.func.isRequired, +}; + +export default CalendarCell; \ No newline at end of file diff --git a/web-app/src/components/lens_issues/LensIssueFormModal.jsx b/web-app/src/components/lens_issues/LensIssueFormModal.jsx index 6b7abd1..93a20cf 100644 --- a/web-app/src/components/lens_issues/LensIssueFormModal.jsx +++ b/web-app/src/components/lens_issues/LensIssueFormModal.jsx @@ -36,27 +36,13 @@ const LensIssueFormModal = ({visible, onCancel, onSubmit}) => { }, [visible]); const fetchPatients = async () => { - try { - const data = await getAllPatients(api); - setPatients(data); - } catch (error) { - console.error(error); - notification.error({ - message: "Ошибка загрузки пациентов", description: "Проверьте подключение к сети.", - }); - } + const data = await getAllPatients(api); + setPatients(data); }; const fetchLenses = async () => { - try { - const data = await getNotIssuedLenses(api); - setLenses(data); - } catch (error) { - console.error(error); - notification.error({ - message: "Ошибка загрузки линз", description: "Проверьте подключение к сети.", - }); - } + const data = await getNotIssuedLenses(api); + setLenses(data); }; const handleOk = async () => { @@ -324,7 +310,9 @@ const LensIssueFormModal = ({visible, onCancel, onSubmit}) => { }; LensIssueFormModal.propTypes = { - visible: PropTypes.bool.isRequired, onCancel: PropTypes.func.isRequired, onSubmit: PropTypes.func.isRequired, + visible: PropTypes.bool.isRequired, + onCancel: PropTypes.func.isRequired, + onSubmit: PropTypes.func.isRequired, }; export default LensIssueFormModal; diff --git a/web-app/src/components/lens_issues/LensIssueViewModal.jsx b/web-app/src/components/lens_issues/LensIssueViewModal.jsx index 85f7362..5d9612c 100644 --- a/web-app/src/components/lens_issues/LensIssueViewModal.jsx +++ b/web-app/src/components/lens_issues/LensIssueViewModal.jsx @@ -1,5 +1,6 @@ import {Collapse, Modal} from "antd"; import PropTypes from "prop-types"; +import {LensIssuePropType} from "../../types/lensIssuePropType.jsx"; const LensIssueViewModal = ({visible, onCancel, lensIssue}) => { @@ -69,39 +70,9 @@ const LensIssueViewModal = ({visible, onCancel, lensIssue}) => { }; LensIssueViewModal.propTypes = { - visible: PropTypes.bool, - onCancel: PropTypes.func, - lensIssue: PropTypes.shape({ - issue_date: PropTypes.string, - patient: PropTypes.shape({ - first_name: PropTypes.string, - last_name: PropTypes.string, - patronymic: PropTypes.string, - birthday: PropTypes.string, - address: PropTypes.string, - email: PropTypes.string, - phone: PropTypes.string, - diagnosis: PropTypes.string, - correction: PropTypes.string, - }), - doctor: PropTypes.shape({ - last_name: PropTypes.string, - first_name: PropTypes.string, - login: PropTypes.string, - }), - lens: PropTypes.shape({ - id: PropTypes.number.isRequired, - tor: PropTypes.number.isRequired, - diameter: PropTypes.number.isRequired, - esa: PropTypes.number.isRequired, - fvc: PropTypes.number.isRequired, - preset_refraction: PropTypes.number.isRequired, - periphery_toricity: PropTypes.number.isRequired, - side: PropTypes.string.isRequired, - issued: PropTypes.bool.isRequired, - trial: PropTypes.number.isRequired, - }), - }), + visible: PropTypes.bool.isRequired, + onCancel: PropTypes.func.isRequired, + lensIssue: LensIssuePropType.isRequired, }; export default LensIssueViewModal; \ No newline at end of file diff --git a/web-app/src/components/lenses/LensFormModal.jsx b/web-app/src/components/lenses/LensFormModal.jsx index 4d0dbdb..6762d04 100644 --- a/web-app/src/components/lenses/LensFormModal.jsx +++ b/web-app/src/components/lenses/LensFormModal.jsx @@ -3,6 +3,7 @@ import {useEffect, useState} from "react"; import PropTypes from "prop-types"; import getAllLensTypes from "../../api/lens_types/getAllLensTypes.jsx"; import {useAuth} from "../../AuthContext.jsx"; +import {LensPropType} from "../../types/lensPropType.jsx"; const LensFormModal = ({visible, onCancel, onSubmit, lens}) => { @@ -26,17 +27,8 @@ const LensFormModal = ({visible, onCancel, onSubmit, lens}) => { }, [visible, lens]); const fetchLensTypes = async () => { - try { - const data = await getAllLensTypes(api); - setLensTypes(data); - } catch (error) { - console.log(error); - notification.error({ - message: "Ошибка загрузки типов линз", - description: "Проверьте подключение к сети.", - placement: "topRight", - }); - } + const data = await getAllLensTypes(api); + setLensTypes(data); }; const handleOk = async () => { @@ -199,18 +191,7 @@ LensFormModal.propTypes = { visible: PropTypes.bool.isRequired, onCancel: PropTypes.func.isRequired, onSubmit: PropTypes.func.isRequired, - lens: PropTypes.shape({ - tor: PropTypes.number.isRequired, - trial: PropTypes.number.isRequired, - esa: PropTypes.number.isRequired, - fvc: PropTypes.number.isRequired, - preset_refraction: PropTypes.number.isRequired, - diameter: PropTypes.number.isRequired, - periphery_toricity: PropTypes.number.isRequired, - side: PropTypes.string.isRequired, - issued: PropTypes.bool.isRequired, - type_id: PropTypes.number.isRequired, - }), + lens: LensPropType.isRequired, } export default LensFormModal; \ No newline at end of file diff --git a/web-app/src/components/lenses/LensListCard.jsx b/web-app/src/components/lenses/LensListCard.jsx index 3b6fccb..cefce5c 100644 --- a/web-app/src/components/lenses/LensListCard.jsx +++ b/web-app/src/components/lenses/LensListCard.jsx @@ -3,6 +3,7 @@ import PropTypes from "prop-types"; import {DeleteOutlined, EditOutlined, EyeOutlined} from "@ant-design/icons"; import {useState} from "react"; import LensViewModal from "./LensViewModal.jsx"; +import {LensPropType} from "../../types/lensPropType.jsx"; const LensListCard = ({lens, handleEditLens, handleDeleteLens}) => { const [showModalInfo, setShowModalInfo] = useState(false); @@ -61,18 +62,7 @@ const LensListCard = ({lens, handleEditLens, handleDeleteLens}) => { }; LensListCard.propTypes = { - lens: PropTypes.shape({ - id: PropTypes.number.isRequired, - tor: PropTypes.number.isRequired, - trial: PropTypes.number, - esa: PropTypes.number, - fvc: PropTypes.number, - preset_refraction: PropTypes.number.isRequired, - diameter: PropTypes.number.isRequired, - periphery_toricity: PropTypes.number.isRequired, - side: PropTypes.string.isRequired, - issued: PropTypes.bool.isRequired, - }).isRequired, + lens: LensPropType.isRequired, handleEditLens: PropTypes.func.isRequired, handleDeleteLens: PropTypes.func.isRequired, }; diff --git a/web-app/src/components/lenses/LensViewModal.jsx b/web-app/src/components/lenses/LensViewModal.jsx index 394c9c1..54e6ad3 100644 --- a/web-app/src/components/lenses/LensViewModal.jsx +++ b/web-app/src/components/lenses/LensViewModal.jsx @@ -1,5 +1,6 @@ import {Button, Col, Modal, Row, Typography} from "antd"; import PropTypes from "prop-types"; +import {LensPropType} from "../../types/lensPropType.jsx"; const {Text, Title} = Typography; @@ -72,17 +73,7 @@ const LensViewModal = ({visible, onCancel, lens}) => { LensViewModal.propTypes = { visible: PropTypes.bool.isRequired, onCancel: PropTypes.func.isRequired, - lens: PropTypes.shape({ - tor: PropTypes.number.isRequired, - diameter: PropTypes.number.isRequired, - esa: PropTypes.number.isRequired, - fvc: PropTypes.number.isRequired, - preset_refraction: PropTypes.number.isRequired, - periphery_toricity: PropTypes.number.isRequired, - side: PropTypes.string.isRequired, - issued: PropTypes.bool.isRequired, - trial: PropTypes.number.isRequired, - }), + lens: LensPropType.isRequired, }; export default LensViewModal; diff --git a/web-app/src/components/patients/PatientFormModal.jsx b/web-app/src/components/patients/PatientFormModal.jsx index 6aad3c5..ce7c0c0 100644 --- a/web-app/src/components/patients/PatientFormModal.jsx +++ b/web-app/src/components/patients/PatientFormModal.jsx @@ -5,6 +5,7 @@ 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.jsx"; const {TextArea} = Input; @@ -141,17 +142,7 @@ PatientFormModal.propTypes = { visible: PropTypes.bool.isRequired, onCancel: PropTypes.func.isRequired, onSubmit: PropTypes.func.isRequired, - patient: PropTypes.shape({ - first_name: PropTypes.string, - last_name: PropTypes.string, - patronymic: PropTypes.string, - birthday: PropTypes.string, - address: PropTypes.string, - email: PropTypes.string, - phone: PropTypes.string, - diagnosis: PropTypes.string, - correction: PropTypes.string, - }), + patient: PatientPropType.isRequired, }; export default PatientFormModal; diff --git a/web-app/src/components/patients/PatientListCard.jsx b/web-app/src/components/patients/PatientListCard.jsx index 60f4ff8..cb8a3a7 100644 --- a/web-app/src/components/patients/PatientListCard.jsx +++ b/web-app/src/components/patients/PatientListCard.jsx @@ -3,6 +3,7 @@ import PropTypes from "prop-types"; import {DeleteOutlined, EditOutlined, EyeOutlined} from "@ant-design/icons"; import {useState} from "react"; import PatientViewModal from "./PatientViewModal.jsx"; +import {PatientPropType} from "../../types/patientPropType.jsx"; const PatientListCard = ({patient, handleEditPatient, handleDeletePatient}) => { const [showModalInfo, setShowModalInfo] = useState(false); @@ -73,20 +74,9 @@ const PatientListCard = ({patient, handleEditPatient, handleDeletePatient}) => { }; PatientListCard.propTypes = { - patient: PropTypes.shape({ - id: PropTypes.number.isRequired, - last_name: PropTypes.string.isRequired, - first_name: PropTypes.string.isRequired, - patronymic: PropTypes.string, - birthday: PropTypes.string.isRequired, - address: PropTypes.string, - email: PropTypes.string, - phone: PropTypes.string, - diagnosis: PropTypes.string, - correction: PropTypes.string, - }).isRequired, + patient: PatientPropType.isRequired, handleEditPatient: PropTypes.func.isRequired, handleDeletePatient: PropTypes.func.isRequired, }; -export default PatientListCard; +export default PatientListCard; \ No newline at end of file diff --git a/web-app/src/components/patients/PatientViewModal.jsx b/web-app/src/components/patients/PatientViewModal.jsx index 328e007..8262fcf 100644 --- a/web-app/src/components/patients/PatientViewModal.jsx +++ b/web-app/src/components/patients/PatientViewModal.jsx @@ -1,5 +1,6 @@ import {Button, Col, Modal, Row, Typography, Divider} from "antd"; import PropTypes from "prop-types"; +import {PatientPropType} from "../../types/patientPropType.jsx"; const { Text, Title } = Typography; @@ -72,17 +73,7 @@ const PatientViewModal = ({ visible, onCancel, patient }) => { PatientViewModal.propTypes = { visible: PropTypes.bool.isRequired, onCancel: PropTypes.func.isRequired, - patient: PropTypes.shape({ - first_name: PropTypes.string, - last_name: PropTypes.string, - patronymic: PropTypes.string, - birthday: PropTypes.string, - address: PropTypes.string, - email: PropTypes.string, - phone: PropTypes.string, - diagnosis: PropTypes.string, - correction: PropTypes.string, - }), + patient: PatientPropType.isRequired, }; export default PatientViewModal; diff --git a/web-app/src/components/sets/SetFormModal.jsx b/web-app/src/components/sets/SetFormModal.jsx index 38805f6..f1eda0e 100644 --- a/web-app/src/components/sets/SetFormModal.jsx +++ b/web-app/src/components/sets/SetFormModal.jsx @@ -6,6 +6,7 @@ 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"; +import {SetPropType} from "../../types/setPropType.jsx"; const {Option} = Select; @@ -32,31 +33,13 @@ const SetFormModal = ({visible, onCancel, setData, onSubmit}) => { const fetchSetContents = async () => { if (!setData) return; - try { - const data = await getSetContentBySetId(api, setData.id); - setContent(data); - } catch (error) { - console.log(error); - notification.error({ - message: "Ошибка загрузки контента", - description: "Проверьте подключение к сети.", - placement: "topRight", - }); - } + const data = await getSetContentBySetId(api, setData.id); + setContent(data); }; const fetchLensTypes = async () => { - try { - const data = await getAllLensTypes(api); - setLensTypes(data); - } catch (error) { - console.log(error); - notification.error({ - message: "Ошибка загрузки типов линз", - description: "Проверьте подключение к сети.", - placement: "topRight", - }); - } + const data = await getAllLensTypes(api); + setLensTypes(data); }; const addContentItem = () => { @@ -272,10 +255,7 @@ SetFormModal.propTypes = { visible: PropTypes.bool.isRequired, onCancel: PropTypes.func.isRequired, onSubmit: PropTypes.func.isRequired, - setData: PropTypes.shape({ - id: PropTypes.number, - title: PropTypes.string, - }), + setData: SetPropType.isRequired, } -export default SetFormModal; +export default SetFormModal; \ No newline at end of file diff --git a/web-app/src/components/sets/SetListCard.jsx b/web-app/src/components/sets/SetListCard.jsx index c84614a..7b16af6 100644 --- a/web-app/src/components/sets/SetListCard.jsx +++ b/web-app/src/components/sets/SetListCard.jsx @@ -1,6 +1,7 @@ import PropTypes from "prop-types"; import {Card, Modal, Popconfirm, Tooltip} from "antd"; import {DeleteOutlined, EditOutlined, PlusOutlined} from "@ant-design/icons"; +import {SetPropType} from "../../types/setPropType.jsx"; const SetListCard = ({set, handleEditSet, handleDeleteSet, handleAppendSet}) => { const deleteSet = () => { @@ -59,10 +60,7 @@ const SetListCard = ({set, handleEditSet, handleDeleteSet, handleAppendSet}) => }; SetListCard.propTypes = { - set: PropTypes.shape({ - id: PropTypes.number.isRequired, - title: PropTypes.string.isRequired, - }).isRequired, + set: SetPropType.isRequired, handleEditSet: PropTypes.func.isRequired, handleAppendSet: PropTypes.func.isRequired, handleDeleteSet: PropTypes.func.isRequired, diff --git a/web-app/src/layouts/AppointmentsLayout.jsx b/web-app/src/layouts/AppointmentsLayout.jsx index 425fab3..a8534b6 100644 --- a/web-app/src/layouts/AppointmentsLayout.jsx +++ b/web-app/src/layouts/AppointmentsLayout.jsx @@ -1,5 +1,5 @@ import {useEffect, useState} from "react"; -import {Button, Grid, notification, Tabs, Typography} from "antd"; +import {Button, Grid, Tabs, Typography} from "antd"; import {Splitter} from "antd"; import { CalendarOutlined, TableOutlined, MenuFoldOutlined, MenuUnfoldOutlined, @@ -62,17 +62,9 @@ const AppointmentsLayout = () => { }; const fetchAppointments = async () => { - try { - const data = await getAllAppointments(api); - setAppointments(data); - - cacheInfo("appointmentsData", data); - } catch (error) { - console.log(error); - notification.error({ - message: "Ошибка загрузки данных", description: "Проверьте подключение к сети.", placement: "topRight", - }); - } + const data = await getAllAppointments(api); + setAppointments(data); + cacheInfo("appointmentsData", data); }; const fetchScheduledAppointmentsWithCache = async () => { @@ -88,17 +80,9 @@ const AppointmentsLayout = () => { }; const fetchScheduledAppointments = async () => { - try { - const data = await getAllScheduledAppointments(api); - setScheduledAppointments(data); - - cacheInfo("scheduledAppointmentsData", data); - } catch (error) { - console.log(error); - notification.error({ - message: "Ошибка загрузки данных", description: "Проверьте подключение к сети.", placement: "topRight", - }); - } + const data = await getAllScheduledAppointments(api); + setScheduledAppointments(data); + cacheInfo("scheduledAppointmentsData", data); }; const items = [{ @@ -179,4 +163,4 @@ const AppointmentsLayout = () => { ); }; -export default AppointmentsLayout; +export default AppointmentsLayout; \ No newline at end of file diff --git a/web-app/src/layouts/MainLayout.jsx b/web-app/src/layouts/MainLayout.jsx index d3e09c9..9362f95 100644 --- a/web-app/src/layouts/MainLayout.jsx +++ b/web-app/src/layouts/MainLayout.jsx @@ -92,4 +92,4 @@ const MainLayout = () => { ); }; -export default MainLayout; +export default MainLayout; \ No newline at end of file diff --git a/web-app/src/pages/IssuesPage.jsx b/web-app/src/pages/IssuesPage.jsx index 22ff090..0c965f1 100644 --- a/web-app/src/pages/IssuesPage.jsx +++ b/web-app/src/pages/IssuesPage.jsx @@ -92,39 +92,20 @@ const IssuesPage = () => { }; const handleSubmitFormModal = async (issue_date, patient_id, lens_id) => { - try { - await addLensIssue(api, {issue_date, patient_id, lens_id}); - setIsModalVisible(false); - notification.success({ - message: "Линза выдана", - description: "Линза успешно выдана пациенту.", - placement: "topRight", - }); - await fetchLensIssues(); - } catch (error) { - console.log(error); - notification.error({ - message: "Ошибка добавления", - description: "Не удалось выдать линзу пациенту.", - placement: "topRight", - }); - } + setIsModalVisible(false); + await addLensIssue(api, {issue_date, patient_id, lens_id}); + notification.success({ + message: "Линза выдана", + description: "Линза успешно выдана пациенту.", + placement: "topRight", + }); + await fetchLensIssues(); }; const fetchLensIssues = async () => { - try { - const data = await getAllLensIssues(api); - setLensIssues(data); - setLoading(false); - } catch (error) { - console.log(error); - notification.error({ - message: "Ошибка загрузки данных", - description: "Проверьте подключение к сети.", - placement: "topRight", - }); - setLoading(false); - } + const data = await getAllLensIssues(api); + setLensIssues(data); + setLoading(false); }; const handleSearch = (e) => { diff --git a/web-app/src/pages/PatientsPage.jsx b/web-app/src/pages/PatientsPage.jsx index 10ab986..b61345b 100644 --- a/web-app/src/pages/PatientsPage.jsx +++ b/web-app/src/pages/PatientsPage.jsx @@ -75,20 +75,10 @@ const PatientsPage = () => { }; const fetchPatients = async () => { - try { - const data = await getAllPatients(api); - setPatients(data); - - cacheInfo("patientsData", data); - } catch (error) { - console.log(error); - notification.error({ - message: "Ошибка загрузки данных", - description: "Проверьте подключение к сети.", - placement: "topRight", - }) - } + const data = await getAllPatients(api); + setPatients(data); + cacheInfo("patientsData", data); setLoading(false); }; @@ -124,22 +114,13 @@ const PatientsPage = () => { }; const handleDeletePatient = async (patient_id) => { - try { - await deletePatient(api, patient_id); - await fetchPatients(); - notification.success({ - message: "Пациент удалён", - description: "Пациент успешно удалён из базы.", - placement: "topRight", - }); - } catch (error) { - console.log(error); - notification.error({ - message: "Ошибка удаления", - description: "Не удалось удалить пациента.", - placement: "topRight", - }); - } + await deletePatient(api, patient_id); + await fetchPatients(); + notification.success({ + message: "Пациент удалён", + description: "Пациент успешно удалён из базы.", + placement: "topRight", + }); }; const handleCancel = () => { @@ -147,24 +128,14 @@ const PatientsPage = () => { }; const handleModalPatientSubmit = async (newPatient) => { - try { - if (selectedPatient) { - await editPatient(newPatient); - } else { - await addNewPatient(newPatient); - } - setIsModalVisible(false); - await fetchPatients(); - } catch (error) { - console.log(error); - notification.error({ - message: "Ошибка", - description: error.response?.status === 401 - ? "Ошибка авторизации: пользователь не найден или токен недействителен" - : "Не удалось сохранить данные пациента.", - placement: "topRight", - }); + setIsModalVisible(false); + + if (selectedPatient) { + await editPatient(newPatient); + } else { + await addNewPatient(newPatient); } + await fetchPatients(); }; const editPatient = async (patient) => { @@ -330,6 +301,7 @@ const PatientsPage = () => { allowClear /> + {viewMode === "tile" && ( { ); }; -export default PatientsPage; +export default PatientsPage; \ No newline at end of file diff --git a/web-app/src/pages/appointments_layout/AppointmentsCalendarPage.jsx b/web-app/src/pages/appointments_layout/AppointmentsCalendarPage.jsx index df0fe3e..26ca66c 100644 --- a/web-app/src/pages/appointments_layout/AppointmentsCalendarPage.jsx +++ b/web-app/src/pages/appointments_layout/AppointmentsCalendarPage.jsx @@ -1,10 +1,13 @@ -import {Calendar, Grid, ConfigProvider, Badge, Modal} from "antd"; +import {Calendar, Grid, ConfigProvider, Badge, Modal, Tag, Tooltip} from "antd"; import {useState} from "react"; import dayjs from "dayjs"; import 'dayjs/locale/ru'; import locale from 'antd/es/locale/ru_RU'; import updateLocale from 'dayjs/plugin/updateLocale'; -import PropTypes from "prop-types"; +import PropTypes, {arrayOf} from "prop-types"; +import CalendarCell from "../../components/appointments/CalendarCell.jsx"; +import {AppointmentPropType} from "../../types/appointmentPropType.jsx"; +import {ScheduledAppointmentPropType} from "../../types/scheduledAppointmentPropType.jsx"; const {useBreakpoint} = Grid; @@ -18,6 +21,7 @@ const AppointmentsCalendarPage = ({appointments, scheduledAppointments}) => { const [selectedDate, setSelectedDate] = useState(dayjs(new Date())); const [modalVisible, setModalVisible] = useState(false); const [selectedAppointments, setSelectedAppointments] = useState([]); + const [selectedAppointment, setSelectedAppointment] = useState(null); const dateCellRender = (value) => { const date = value.format('YYYY-MM-DD'); @@ -29,19 +33,14 @@ const AppointmentsCalendarPage = ({appointments, scheduledAppointments}) => { ); return ( - + { + }} + onItemClick={() => { + }} + /> ); }; @@ -67,83 +66,15 @@ const AppointmentsCalendarPage = ({appointments, scheduledAppointments}) => { onSelect={onSelect} cellRender={dateCellRender} /> - setModalVisible(false)} - footer={null} - > - {selectedAppointments.map(app => ( -
-

{app.appointment_datetime ? 'Прием' : 'Запланировано'}: {dayjs(app.appointment_datetime || app.scheduled_datetime).format('HH:mm')}

-

Пациент: {app.patient?.name || 'Не указан'}

-

Врач: {app.doctor?.name || 'Не указан'}

-

Тип: {app.type?.name || 'Не указан'}

- {app.results &&

Результаты: {app.results}

} -
-
- ))} -
+ ); }; AppointmentsCalendarPage.propTypes = { - appointments: PropTypes.arrayOf( - PropTypes.shape({ - id: PropTypes.number.isRequired, - results: PropTypes.string, - days_until_the_next_appointment: PropTypes.number, - appointment_datetime: PropTypes.string.isRequired, - patient: PropTypes.shape({ - first_name: PropTypes.string, - last_name: PropTypes.string, - patronymic: PropTypes.string, - birthday: PropTypes.string, - address: PropTypes.string, - email: PropTypes.string, - phone: PropTypes.string, - diagnosis: PropTypes.string, - correction: PropTypes.string, - }), - doctor: PropTypes.shape({ - last_name: PropTypes.string, - first_name: PropTypes.string, - login: PropTypes.string, - }), - type: PropTypes.shape({ - title: PropTypes.string, - }) - }) - ), - scheduledAppointments: PropTypes.arrayOf( - PropTypes.shape( - { - id: PropTypes.number.isRequired, - scheduled_datetime: PropTypes.string.isRequired, - patient: PropTypes.shape({ - first_name: PropTypes.string, - last_name: PropTypes.string, - patronymic: PropTypes.string, - birthday: PropTypes.string, - address: PropTypes.string, - email: PropTypes.string, - phone: PropTypes.string, - diagnosis: PropTypes.string, - correction: PropTypes.string, - }), - doctor: PropTypes.shape({ - last_name: PropTypes.string, - first_name: PropTypes.string, - login: PropTypes.string, - }), - type: PropTypes.shape({ - title: PropTypes.string, - }), - } - ) - ) + appointments: PropTypes.arrayOf(AppointmentPropType).isRequired, + scheduledAppointments: PropTypes.arrayOf(ScheduledAppointmentPropType).isRequired, }; -export default AppointmentsCalendarPage; +export default AppointmentsCalendarPage; \ No newline at end of file diff --git a/web-app/src/pages/lenses_layout/LensesPage.jsx b/web-app/src/pages/lenses_layout/LensesPage.jsx index bcec68d..337b1e7 100644 --- a/web-app/src/pages/lenses_layout/LensesPage.jsx +++ b/web-app/src/pages/lenses_layout/LensesPage.jsx @@ -92,19 +92,9 @@ const LensesPage = () => { }; const fetchLenses = async () => { - try { - const data = await getAllLenses(api); - setLenses(data); - setLoading(false); - } catch (error) { - console.error("Ошибка загрузки линз:", error); - notification.error({ - message: "Ошибка загрузки линз", - description: "Проверьте подключение к сети.", - placement: "topRight", - }) - setLoading(false); - } + const data = await getAllLenses(api); + setLenses(data); + setLoading(false); }; const fetchViewModeFromCache = () => { @@ -147,51 +137,33 @@ const LensesPage = () => { }; const handleDeleteLens = async (lensId) => { - try { - await deleteLens(api, lensId); - await fetchLenses(api); - notification.success({ - message: "Линза удалена", - description: "Линза успешно удалена.", - placement: "topRight", - }) - } catch (error) { - console.error("Ошибка удаления линзы:", error); - notification.error({ - message: "Ошибка удаления линзы", - description: "Проверьте подключение к сети.", - placement: "topRight", - }); - } + await deleteLens(api, lensId); + await fetchLenses(api); + notification.success({ + message: "Линза удалена", + description: "Линза успешно удалена.", + placement: "topRight", + }) }; const handleModalSubmit = async (lensData) => { - try { - if (selectedLens) { - await updateLens(api, selectedLens.id, lensData); - notification.success({ - message: "Линза обновлена", - description: "Линза успешно обновлена.", - placement: "topRight", - }); - } else { - await addLens(api, lensData); - notification.success({ - message: "Линза добавлена", - description: "Линза успешно добавлена.", - placement: "topRight", - }); - } - setIsModalVisible(false); - await fetchLenses(); - } catch (error) { - console.error("Ошибка сохранения линзы:", error); - notification.error({ - message: "Ошибка сохранения линзы", - description: "Проверьте подключение к сети.", + setIsModalVisible(false); + if (selectedLens) { + await updateLens(api, selectedLens.id, lensData); + notification.success({ + message: "Линза обновлена", + description: "Линза успешно обновлена.", + placement: "topRight", + }); + } else { + await addLens(api, lensData); + notification.success({ + message: "Линза добавлена", + description: "Линза успешно добавлена.", placement: "topRight", }); } + await fetchLenses(); }; const toggleAdvancedSearch = () => { diff --git a/web-app/src/pages/lenses_layout/SetLensesPage.jsx b/web-app/src/pages/lenses_layout/SetLensesPage.jsx index f569d3a..fbf5692 100644 --- a/web-app/src/pages/lenses_layout/SetLensesPage.jsx +++ b/web-app/src/pages/lenses_layout/SetLensesPage.jsx @@ -53,23 +53,10 @@ const SetLensesPage = () => { }; const fetchSets = async () => { - try { - const data = await getAllSets(api); - setSets(data); - - cacheInfo("setsData", data); - } catch (error) { - console.log(error); - notification.error({ - message: "Ошибка загрузки данных", - description: "Проверьте подключение к сети.", - placement: "topRight", - }) - } - - if (loading) { - setLoading(false); - } + const data = await getAllSets(api); + setSets(data); + setLoading(false); + cacheInfo("setsData", data); }; const filteredSets = sets.filter(set => set.title.toLowerCase().includes(searchText.toLowerCase())); @@ -82,102 +69,57 @@ const SetLensesPage = () => { const handleEditSet = (set) => { setSelectedSet(set); setIsModalVisible(true); - } + }; const handleDeleteSet = async (set_id) => { - try { - await deleteSet(api, set_id); - notification.success({ - message: "Набор удален", - description: "Набор успешно удален.", - placement: "topRight", - }); - await fetchSets(); - } catch (error) { - console.error("Ошибка удаления набора:", error); - notification.error({ - message: "Ошибка удаления набора", - description: "Проверьте подключение к сети.", - placement: "topRight", - }); - } - } + await deleteSet(api, set_id); + notification.success({ + message: "Набор удален", + description: "Набор успешно удален.", + placement: "topRight", + }); + await fetchSets(); + }; const handleCancel = () => { setIsModalVisible(false); }; const handleAppendSet = async (set) => { - try { - await appendLensesFromSet(api, set.id); - notification.success({ - message: "Линзы добавлены", - description: "Линзы успешно добавлены.", - placement: "topRight", - }); - } catch (error) { - console.error("Ошибка добавления линз:", error); - notification.error({ - message: "Ошибка добавления линз", - description: "Проверьте подключение к сети.", - placement: "topRight", - }); - } + await appendLensesFromSet(api, set.id); + notification.success({ + message: "Линзы добавлены", + description: "Линзы успешно добавлены.", + placement: "topRight", + }); }; const handleModalSetSubmit = async (set, content = []) => { - try { - let refreshed_set; + setIsModalVisible(false); - if (selectedSet) { - refreshed_set = await editCurrentSet(set); - } else { - refreshed_set = await addNewSet(set); - } + let refreshed_set; - if (refreshed_set && selectedSet) { - await updateContent(content, refreshed_set.id); - } else if (refreshed_set && !selectedSet) { - await setContent(content, refreshed_set.id); - } - - setIsModalVisible(false); - await fetchSets(); - } catch (error) { - console.error("Ошибка сохранения набора:", error); - notification.error({ - message: "Ошибка сохранения набора", - description: "Проверьте подключение к сети.", - placement: "topRight", - }); + if (selectedSet) { + refreshed_set = await editCurrentSet(set); + } else { + refreshed_set = await addNewSet(set); } + + if (refreshed_set && selectedSet) { + await updateContent(content, refreshed_set.id); + } else if (refreshed_set && !selectedSet) { + await setContent(content, refreshed_set.id); + } + + await fetchSets(); }; const setContent = async (content, set_id) => { - try { - console.log(content); - await addSetContent(api, content, set_id); - } catch (error) { - console.error("Ошибка сохранения набора:", error); - notification.error({ - message: "Ошибка сохранения набора", - description: "Проверьте подключение к сети.", - placement: "topRight", - }); - } + await addSetContent(api, content, set_id); }; const updateContent = async (content, set_id) => { - try { - await updateSetContent(api, content, set_id); - } catch (error) { - console.error("Ошибка сохранения набора:", error); - notification.error({ - message: "Ошибка сохранения набора", - description: "Проверьте подключение к сети.", - placement: "topRight", - }); - } + await updateSetContent(api, content, set_id); }; const editCurrentSet = async (set) => { diff --git a/web-app/src/types/appointmentPropType.jsx b/web-app/src/types/appointmentPropType.jsx new file mode 100644 index 0000000..39002cf --- /dev/null +++ b/web-app/src/types/appointmentPropType.jsx @@ -0,0 +1,17 @@ +import PropTypes from "prop-types"; +import {PatientPropType} from "./patientPropType.jsx"; +import {UserPropType} from "./userPropType.jsx"; +import {AppointmentTypePropType} from "./appointmentTypePropType.jsx"; + +export const AppointmentPropType = PropTypes.shape({ + id: PropTypes.number, + results: PropTypes.string, + days_until_the_next_appointment: PropTypes.number, + appointment_datetime: PropTypes.string.isRequired, + patient_id: PropTypes.number.isRequired, + doctor_id: PropTypes.number.isRequired, + type_id: PropTypes.number.isRequired, + patient: PatientPropType, + doctor: UserPropType, + type: AppointmentTypePropType, +}); \ No newline at end of file diff --git a/web-app/src/types/appointmentTypePropType.jsx b/web-app/src/types/appointmentTypePropType.jsx new file mode 100644 index 0000000..d81c5c9 --- /dev/null +++ b/web-app/src/types/appointmentTypePropType.jsx @@ -0,0 +1,6 @@ +import PropTypes from "prop-types"; + +export const AppointmentTypePropType = PropTypes.shape({ + id: PropTypes.number, + title: PropTypes.string.isRequired, +}) \ No newline at end of file diff --git a/web-app/src/types/lensIssuePropType.jsx b/web-app/src/types/lensIssuePropType.jsx new file mode 100644 index 0000000..0ee25d2 --- /dev/null +++ b/web-app/src/types/lensIssuePropType.jsx @@ -0,0 +1,15 @@ +import PropTypes from "prop-types"; +import {PatientPropType} from "./patientPropType.jsx"; +import {UserPropType} from "./userPropType.jsx"; +import {LensPropType} from "./lensPropType.jsx"; + +export const LensIssuePropType = PropTypes.shape({ + id: PropTypes.number, + issue_date: PropTypes.string.isRequired, + patient_id: PropTypes.number.isRequired, + doctor_id: PropTypes.number, + lens_id: PropTypes.number.isRequired, + patient: PatientPropType, + doctor: UserPropType, + lens: LensPropType, +}); \ No newline at end of file diff --git a/web-app/src/types/lensPropType.jsx b/web-app/src/types/lensPropType.jsx new file mode 100644 index 0000000..5f8ae1c --- /dev/null +++ b/web-app/src/types/lensPropType.jsx @@ -0,0 +1,15 @@ +import PropTypes from "prop-types"; + +export const LensPropType = PropTypes.shape({ + id: PropTypes.number, + tor: PropTypes.number.isRequired, + trial: PropTypes.number.isRequired, + esa: PropTypes.number.isRequired, + fvc: PropTypes.number.isRequired, + preset_refraction: PropTypes.number.isRequired, + diameter: PropTypes.number.isRequired, + periphery_toricity: PropTypes.number.isRequired, + side: PropTypes.string.isRequired, + issued: PropTypes.bool.isRequired, + type_id: PropTypes.number.isRequired, +}); \ No newline at end of file diff --git a/web-app/src/types/lensTypePropType.jsx b/web-app/src/types/lensTypePropType.jsx new file mode 100644 index 0000000..0ef7c94 --- /dev/null +++ b/web-app/src/types/lensTypePropType.jsx @@ -0,0 +1,6 @@ +import PropTypes from "prop-types"; + +export const LensTypePropType = PropTypes.shape({ + id: PropTypes.number, + title: PropTypes.string.isRequired, +}); \ No newline at end of file diff --git a/web-app/src/types/patientPropType.jsx b/web-app/src/types/patientPropType.jsx new file mode 100644 index 0000000..a998460 --- /dev/null +++ b/web-app/src/types/patientPropType.jsx @@ -0,0 +1,14 @@ +import PropTypes from "prop-types"; + +export const PatientPropType = PropTypes.shape({ + id: PropTypes.number, + first_name: PropTypes.string.isRequired, + last_name: PropTypes.string.isRequired, + patronymic: PropTypes.string, + birthday: PropTypes.string.isRequired, + address: PropTypes.string, + email: PropTypes.string, + phone: PropTypes.string, + diagnosis: PropTypes.string, + correction: PropTypes.string, +}); \ No newline at end of file diff --git a/web-app/src/types/scheduledAppointmentPropType.jsx b/web-app/src/types/scheduledAppointmentPropType.jsx new file mode 100644 index 0000000..8546500 --- /dev/null +++ b/web-app/src/types/scheduledAppointmentPropType.jsx @@ -0,0 +1,15 @@ +import PropTypes from "prop-types"; +import {PatientPropType} from "./patientPropType.jsx"; +import {UserPropType} from "./userPropType.jsx"; +import {AppointmentTypePropType} from "./appointmentTypePropType.jsx"; + +export const ScheduledAppointmentPropType = PropTypes.shape({ + id: PropTypes.number, + scheduled_datetime: PropTypes.string.isRequired, + patient_id: PropTypes.number.isRequired, + doctor_id: PropTypes.number.isRequired, + type_id: PropTypes.number.isRequired, + patient: PatientPropType, + doctor: UserPropType, + type: AppointmentTypePropType, +}) \ No newline at end of file diff --git a/web-app/src/types/setContentPropType.jsx b/web-app/src/types/setContentPropType.jsx new file mode 100644 index 0000000..cf63433 --- /dev/null +++ b/web-app/src/types/setContentPropType.jsx @@ -0,0 +1,15 @@ +import PropTypes from "prop-types"; + +export const SetContentPropType = PropTypes.shape({ + id: PropTypes.number, + tor: PropTypes.number.isRequired, + trial: PropTypes.number.isRequired, + esa: PropTypes.number.isRequired, + fvc: PropTypes.number.isRequired, + preset_refraction: PropTypes.number.isRequired, + diameter: PropTypes.number.isRequired, + side: PropTypes.string.isRequired, + count: PropTypes.number.isRequired, + type_id: PropTypes.number.isRequired, + set_id: PropTypes.number, +}); \ No newline at end of file diff --git a/web-app/src/types/setPropType.jsx b/web-app/src/types/setPropType.jsx new file mode 100644 index 0000000..d98249b --- /dev/null +++ b/web-app/src/types/setPropType.jsx @@ -0,0 +1,6 @@ +import PropTypes from "prop-types"; + +export const SetPropType = PropTypes.shape({ + id: PropTypes.number, + title: PropTypes.string.isRequired, +}); \ No newline at end of file diff --git a/web-app/src/types/userPropType.jsx b/web-app/src/types/userPropType.jsx new file mode 100644 index 0000000..1bcf853 --- /dev/null +++ b/web-app/src/types/userPropType.jsx @@ -0,0 +1,10 @@ +import PropTypes from "prop-types"; + +export const UserPropType = PropTypes.shape({ + id: PropTypes.number, + first_name: PropTypes.string.isRequired, + last_name: PropTypes.string.isRequired, + patronymic: PropTypes.string, + login: PropTypes.string.isRequired, + role_id: PropTypes.number.isRequired, +}); \ No newline at end of file diff --git a/web-app/src/types/viewModPropType.jsx b/web-app/src/types/viewModPropType.jsx new file mode 100644 index 0000000..23843fc --- /dev/null +++ b/web-app/src/types/viewModPropType.jsx @@ -0,0 +1,7 @@ +import PropTypes from "prop-types"; + +export const ViewModPropType = PropTypes.shape({ + value: PropTypes.string.isRequired, + label: PropTypes.string.isRequired, + icon: PropTypes.element, +}); \ No newline at end of file