diff --git a/api/app/domain/entities/scheduled_appointment.py b/api/app/domain/entities/scheduled_appointment.py index 79faf99..25f4ded 100644 --- a/api/app/domain/entities/scheduled_appointment.py +++ b/api/app/domain/entities/scheduled_appointment.py @@ -11,7 +11,7 @@ from app.domain.entities.user import UserEntity class ScheduledAppointmentEntity(BaseModel): id: Optional[int] = None scheduled_datetime: datetime.datetime - is_canceled: bool + is_canceled: Optional[bool] = False patient_id: int doctor_id: Optional[int] = None diff --git a/web-app/src/Components/Pages/AppointmentsPage/AppointmentsPage.jsx b/web-app/src/Components/Pages/AppointmentsPage/AppointmentsPage.jsx index 9e1a587..d1fefe3 100644 --- a/web-app/src/Components/Pages/AppointmentsPage/AppointmentsPage.jsx +++ b/web-app/src/Components/Pages/AppointmentsPage/AppointmentsPage.jsx @@ -131,7 +131,6 @@ const AppointmentsPage = () => { diff --git a/web-app/src/Components/Pages/AppointmentsPage/Components/AppointmentCalendarTab/useAppointmentCalendarUI.js b/web-app/src/Components/Pages/AppointmentsPage/Components/AppointmentCalendarTab/useAppointmentCalendarUI.js index 972529c..d74b550 100644 --- a/web-app/src/Components/Pages/AppointmentsPage/Components/AppointmentCalendarTab/useAppointmentCalendarUI.js +++ b/web-app/src/Components/Pages/AppointmentsPage/Components/AppointmentCalendarTab/useAppointmentCalendarUI.js @@ -1,20 +1,13 @@ -// useAppointmentCalendarUI.js import {useDispatch, useSelector} from "react-redux"; import dayjs from "dayjs"; import utc from "dayjs/plugin/utc"; import timezone from "dayjs/plugin/timezone"; -import {Form, Grid, notification} from "antd"; +import {Grid} from "antd"; import { - closeModal, openModal, setSelectedAppointment, setSelectedAppointments, - setSelectedDate, setSelectedScheduledAppointment, + setSelectedScheduledAppointment, } from "../../../../../Redux/Slices/appointmentsSlice.js"; -import {useCreateAppointmentMutation, useUpdateAppointmentMutation} from "../../../../../Api/appointmentsApi.js"; -import { - useCreateScheduledAppointmentMutation, - useUpdateScheduledAppointmentMutation -} from "../../../../../Api/scheduledAppointmentsApi.js"; dayjs.extend(utc); dayjs.extend(timezone); @@ -24,12 +17,6 @@ const {useBreakpoint} = Grid; const useAppointmentCalendarUI = (appointments, scheduledAppointments) => { const dispatch = useDispatch(); - const { - modalVisible, - selectedAppointments, - selectedAppointment, - selectedScheduledAppointment, - } = useSelector(state => state.appointmentsUI); const selectedDate = dayjs.tz(useSelector(state => state.appointmentsUI.selectedDate), 'Europe/Moscow'); const screens = useBreakpoint(); @@ -39,7 +26,7 @@ const useAppointmentCalendarUI = (appointments, scheduledAppointments) => { const onSelect = (date) => { const selectedDateStr = date.format('YYYY-MM-DD'); - dispatch(setSelectedDate(selectedDateStr)); + // dispatch(setSelectedDate(selectedDateStr)); const appointmentsForDate = appointments.filter(app => dayjs(app.appointment_datetime).format('YYYY-MM-DD') === selectedDateStr @@ -62,10 +49,9 @@ const useAppointmentCalendarUI = (appointments, scheduledAppointments) => { }; const getAppointmentsByListAndDate = (list, value, isScheduled = false) => { - const date = value.tz('Europe/Moscow').format('YYYY-MM-DD'); + const date = value.format('YYYY-MM-DD'); return list.filter(app => dayjs(isScheduled ? app.scheduled_datetime : app.appointment_datetime) - .tz('Europe/Moscow') .format('YYYY-MM-DD') === date ); }; diff --git a/web-app/src/Components/Pages/AppointmentsPage/Components/AppointmentFormModal/AppointmentFormModal.jsx b/web-app/src/Components/Pages/AppointmentsPage/Components/AppointmentFormModal/AppointmentFormModal.jsx index b7d1de5..7317afd 100644 --- a/web-app/src/Components/Pages/AppointmentsPage/Components/AppointmentFormModal/AppointmentFormModal.jsx +++ b/web-app/src/Components/Pages/AppointmentsPage/Components/AppointmentFormModal/AppointmentFormModal.jsx @@ -28,6 +28,7 @@ const AppointmentFormModal = ({onCancel}) => { onCancel, appointmentFormModalData.createAppointment, appointmentFormModalData.patients, + appointmentFormModalData.cancelAppointment, ); const editor = useRef(null); diff --git a/web-app/src/Components/Pages/AppointmentsPage/Components/AppointmentFormModal/useAppointmentFormModal.js b/web-app/src/Components/Pages/AppointmentsPage/Components/AppointmentFormModal/useAppointmentFormModal.js index ab197b2..6cf7647 100644 --- a/web-app/src/Components/Pages/AppointmentsPage/Components/AppointmentFormModal/useAppointmentFormModal.js +++ b/web-app/src/Components/Pages/AppointmentsPage/Components/AppointmentFormModal/useAppointmentFormModal.js @@ -1,6 +1,7 @@ import {useGetPatientsQuery} from "../../../../../Api/patientsApi.js"; import {useGetAppointmentTypesQuery} from "../../../../../Api/appointmentTypesApi.js"; import {useCreateAppointmentMutation, useUpdateAppointmentMutation} from "../../../../../Api/appointmentsApi.js"; +import {useCancelScheduledAppointmentMutation} from "../../../../../Api/scheduledAppointmentsApi.js"; const useAppointmentFormModal = () => { const { @@ -19,11 +20,13 @@ const useAppointmentFormModal = () => { }); const [createAppointment, {isLoading: isCreating, isError: isErrorCreating}] = useCreateAppointmentMutation(); + const [cancelAppointment] = useCancelScheduledAppointmentMutation(); return { patients, appointmentTypes, createAppointment, + cancelAppointment, isLoading: isLoadingPatients || isLoadingAppointmentTypes || isCreating, isError: isErrorPatients || isErrorAppointmentTypes || isErrorCreating, }; diff --git a/web-app/src/Components/Pages/AppointmentsPage/Components/AppointmentFormModal/useAppointmentFormModalUI.js b/web-app/src/Components/Pages/AppointmentsPage/Components/AppointmentFormModal/useAppointmentFormModalUI.js index c1a860a..8b8a74f 100644 --- a/web-app/src/Components/Pages/AppointmentsPage/Components/AppointmentFormModal/useAppointmentFormModalUI.js +++ b/web-app/src/Components/Pages/AppointmentsPage/Components/AppointmentFormModal/useAppointmentFormModalUI.js @@ -1,6 +1,6 @@ import {Form, notification} from "antd"; import {useDispatch, useSelector} from "react-redux"; -import {closeModal} from "../../../../../Redux/Slices/appointmentsSlice.js"; +import {closeModal, setSelectedScheduledAppointment} from "../../../../../Redux/Slices/appointmentsSlice.js"; import {useEffect, useMemo, useState} from "react"; import dayjs from "dayjs"; import {useGetAppointmentsQuery} from "../../../../../Api/appointmentsApi.js"; @@ -8,9 +8,9 @@ import {Grid} from "antd"; const {useBreakpoint} = Grid; -const useAppointmentFormModalUI = (onCancel, createAppointment, patients) => { +const useAppointmentFormModalUI = (onCancel, createAppointment, patients, cancelAppointment) => { const dispatch = useDispatch(); - const {modalVisible} = useSelector(state => state.appointmentsUI); + const {modalVisible, scheduledData} = useSelector(state => state.appointmentsUI); const [form] = Form.useForm(); const screens = useBreakpoint(); @@ -53,11 +53,24 @@ const useAppointmentFormModalUI = (onCancel, createAppointment, patients) => { setSearchPatientString(""); setFormValues({}); - form.setFieldsValue({ - appointment_datetime: dayjs(new Date()), - }) + if (scheduledData) { + const patient = patients.find(p => p.id === scheduledData.patient_id); + if (patient) { + setSelectedPatient(patient); + setCurrentStep(1); // Skip to appointment details step + form.setFieldsValue({ + patient_id: scheduledData.patient_id, + type_id: scheduledData.type_id, + appointment_datetime: dayjs(scheduledData.appointment_datetime), + }); + } + } else { + form.setFieldsValue({ + appointment_datetime: dayjs(new Date()), + }); + } } - }, [modalVisible, form]); + }, [modalVisible, form, scheduledData, patients]); const handleResultsChange = (newContent) => { setResults(newContent); @@ -144,8 +157,13 @@ const useAppointmentFormModalUI = (onCancel, createAppointment, patients) => { results: results, }; - await createAppointment(data).unwrap(); + + if (scheduledData) { + await cancelScheduledAppointment(scheduledData.id); + dispatch(setSelectedScheduledAppointment(null)); + } + notification.success({ message: 'Прием создан', description: 'Прием успешно создан.', @@ -166,6 +184,18 @@ const useAppointmentFormModalUI = (onCancel, createAppointment, patients) => { } }; + const cancelScheduledAppointment = async (selectedScheduledAppointmentId) => { + try { + await cancelAppointment(selectedScheduledAppointmentId); + } catch (error) { + notification.error({ + message: 'Ошибка', + description: error.data?.message || 'Не удалось отменить прием.', + placement: 'topRight', + }) + } + }; + const handleCancel = () => { form.resetFields(); setSelectedPatient(null); diff --git a/web-app/src/Components/Pages/AppointmentsPage/Components/ScheduledAppintmentFormModal/useScheduledAppointmentFormModalUI.js b/web-app/src/Components/Pages/AppointmentsPage/Components/ScheduledAppintmentFormModal/useScheduledAppointmentFormModalUI.js index e7534e9..aaa0807 100644 --- a/web-app/src/Components/Pages/AppointmentsPage/Components/ScheduledAppintmentFormModal/useScheduledAppointmentFormModalUI.js +++ b/web-app/src/Components/Pages/AppointmentsPage/Components/ScheduledAppintmentFormModal/useScheduledAppointmentFormModalUI.js @@ -25,14 +25,16 @@ const useScheduledAppointmentFormModalUI = (patients, createScheduledAppointment type_id: selectedAppointmentType }; - console.log(data) - await createScheduledAppointment(data).unwrap(); notification.success({ message: 'Прием создан', placement: 'topRight', description: 'Прием успешно запланирован.', - }) + }); + + setSelectedPatient(null); + setSelectedDateTime(dayjs(new Date()).add(1, 'day')); + setSelectedAppointmentType(null); dispatch(closeScheduledModal()); } catch (error) { diff --git a/web-app/src/Components/Pages/AppointmentsPage/Components/ScheduledAppointmentsViewModal/ScheduledAppointmentsViewModal.jsx b/web-app/src/Components/Pages/AppointmentsPage/Components/ScheduledAppointmentsViewModal/ScheduledAppointmentsViewModal.jsx index c60b404..2441c4f 100644 --- a/web-app/src/Components/Pages/AppointmentsPage/Components/ScheduledAppointmentsViewModal/ScheduledAppointmentsViewModal.jsx +++ b/web-app/src/Components/Pages/AppointmentsPage/Components/ScheduledAppointmentsViewModal/ScheduledAppointmentsViewModal.jsx @@ -2,8 +2,11 @@ import {Button, Modal, Popconfirm, Row, Typography} from "antd"; import dayjs from "dayjs"; import useScheduledAppointmentsViewModal from "./useScheduledAppointmentsViewModal.js"; import useScheduledAppointmentsViewModalUI from "./useScheduledAppointmentsViewModalUI.js"; +import { useDispatch } from "react-redux"; +import {openModalWithScheduledData} from "../../../../../Redux/Slices/appointmentsSlice.js"; const ScheduledAppointmentsViewModal = () => { + const dispatch = useDispatch(); const scheduledAppointmentsViewModalData = useScheduledAppointmentsViewModal(); const scheduledAppointmentsViewModalUI = useScheduledAppointmentsViewModalUI(scheduledAppointmentsViewModalData.cancelAppointment); @@ -11,6 +14,15 @@ const ScheduledAppointmentsViewModal = () => { return null; } + const handleConvertToAppointment = () => { + dispatch(openModalWithScheduledData({ + id: scheduledAppointmentsViewModalUI.selectedScheduledAppointment.id, + patient_id: scheduledAppointmentsViewModalUI.selectedScheduledAppointment.patient?.id, + type_id: scheduledAppointmentsViewModalUI.selectedScheduledAppointment.type?.id, + appointment_datetime: scheduledAppointmentsViewModalUI.selectedScheduledAppointment.scheduled_datetime, + })); + }; + return ( { onClick={scheduledAppointmentsViewModalUI.onCancel}> Закрыть + { - ); }; diff --git a/web-app/src/Redux/Slices/appointmentsSlice.js b/web-app/src/Redux/Slices/appointmentsSlice.js index 6a772b4..b555a98 100644 --- a/web-app/src/Redux/Slices/appointmentsSlice.js +++ b/web-app/src/Redux/Slices/appointmentsSlice.js @@ -1,72 +1,69 @@ -import { createSlice } from "@reduxjs/toolkit"; +import { createSlice } from '@reduxjs/toolkit'; const initialState = { - collapsed: true, - siderWidth: 250, - hovered: false, modalVisible: false, - scheduledModalVisible: false, + collapsed: true, + siderWidth: 300, + hovered: false, selectedAppointment: null, - scheduledAppointments: [], selectedScheduledAppointment: null, + scheduledModalVisible: false, + scheduledData: null, + selectedAppointments: [], // Новое поле для хранения массива выбранных приемов }; const appointmentsSlice = createSlice({ name: 'appointmentsUI', initialState, reducers: { - toggleSider: (state) => { - state.collapsed = !state.collapsed; - }, - setSiderWidth: (state, action) => { - state.siderWidth = action.payload; - }, - setHovered: (state, action) => { - state.hovered = action.payload; - }, - setSelectedDate: (state, action) => { - state.selectedDate = action.payload; - }, - openModal: (state) => { + openModal(state) { state.modalVisible = true; }, - closeModal: (state) => { + closeModal(state) { state.modalVisible = false; }, - openScheduledModal: (state) => { - state.scheduledModalVisible = true; + toggleSider(state) { + state.collapsed = !state.collapsed; }, - closeScheduledModal: (state) => { - state.scheduledModalVisible = false; + setSiderWidth(state, action) { + state.siderWidth = action.payload; }, - setSelectedAppointments: (state, action) => { - state.selectedAppointments = action.payload; + setHovered(state, action) { + state.hovered = action.payload; }, - setSelectedAppointment: (state, action) => { + setSelectedAppointment(state, action) { state.selectedAppointment = action.payload; }, - setScheduledAppointments: (state, action) => { - state.scheduledAppointments = action.payload; - }, - setSelectedScheduledAppointment: (state, action) => { + setSelectedScheduledAppointment(state, action) { state.selectedScheduledAppointment = action.payload; }, - } + openScheduledModal(state) { + state.scheduledModalVisible = true; + }, + closeScheduledModal(state) { + state.scheduledModalVisible = false; + }, + openModalWithScheduledData(state, action) { + state.modalVisible = true; + state.scheduledData = action.payload; + }, + setSelectedAppointments(state, action) { + state.selectedAppointments = action.payload; + }, + }, }); export const { - toggleSider, - setSiderWidth, - setHovered, - setSelectedDate, openModal, closeModal, - setSelectedAppointments, + toggleSider, + setHovered, setSelectedAppointment, setSelectedScheduledAppointment, - setScheduledAppointments, openScheduledModal, closeScheduledModal, + openModalWithScheduledData, + setSelectedAppointments, } = appointmentsSlice.actions; export default appointmentsSlice.reducer; \ No newline at end of file