сделал создание запланированного приема
This commit is contained in:
parent
c2a846f433
commit
264ac5063a
@ -65,7 +65,7 @@ async def create_appointment(
|
||||
user=Depends(get_current_user),
|
||||
):
|
||||
appointment_service = ScheduledAppointmentsService(db)
|
||||
return await appointment_service.create_scheduled_appointment(appointment)
|
||||
return await appointment_service.create_scheduled_appointment(appointment, user.id)
|
||||
|
||||
|
||||
@router.put(
|
||||
|
||||
@ -13,7 +13,7 @@ class ScheduledAppointmentEntity(BaseModel):
|
||||
scheduled_datetime: datetime.datetime
|
||||
|
||||
patient_id: int
|
||||
doctor_id: int
|
||||
doctor_id: Optional[int] = None
|
||||
type_id: int
|
||||
|
||||
patient: Optional[PatientEntity] = None
|
||||
|
||||
@ -63,7 +63,8 @@ class ScheduledAppointmentsService:
|
||||
for scheduled_appointment in scheduled_appointments
|
||||
]
|
||||
|
||||
async def create_scheduled_appointment(self, scheduled_appointment: ScheduledAppointmentEntity) -> Optional[
|
||||
async def create_scheduled_appointment(self, scheduled_appointment: ScheduledAppointmentEntity, doctor_id: int) -> \
|
||||
Optional[
|
||||
ScheduledAppointmentEntity
|
||||
]:
|
||||
patient = await self.patients_repository.get_by_id(scheduled_appointment.patient_id)
|
||||
@ -74,7 +75,7 @@ class ScheduledAppointmentsService:
|
||||
detail='The patient with this ID was not found',
|
||||
)
|
||||
|
||||
doctor = await self.users_repository.get_by_id(scheduled_appointment.doctor_id)
|
||||
doctor = await self.users_repository.get_by_id(doctor_id)
|
||||
|
||||
if not doctor:
|
||||
raise HTTPException(
|
||||
@ -82,6 +83,8 @@ class ScheduledAppointmentsService:
|
||||
detail='The doctor/user with this ID was not found',
|
||||
)
|
||||
|
||||
scheduled_appointment.doctor_id = doctor_id
|
||||
|
||||
appointment_type = await self.appointment_types_repository.get_by_id(scheduled_appointment.type_id)
|
||||
|
||||
if not appointment_type:
|
||||
|
||||
@ -13,6 +13,7 @@ import {useDispatch} from "react-redux";
|
||||
import {closeModal, openModal} from "../../../Redux/Slices/appointmentsSlice.js";
|
||||
import AppointmentViewModal
|
||||
from "./Components/AppointmentViewModal/AppointmentViewModal.jsx";
|
||||
import ScheduledAppointmentFormModal from "./Components/ScheduledAppintmentFormModal/ScheduledAppointmentFormModal.jsx";
|
||||
|
||||
const AppointmentsPage = () => {
|
||||
const appointmentsData = useAppointments();
|
||||
@ -110,6 +111,7 @@ const AppointmentsPage = () => {
|
||||
</Button>
|
||||
</div>
|
||||
<FloatButton.Group
|
||||
placement={"left"}
|
||||
trigger="hover"
|
||||
type="primary"
|
||||
icon={<PlusOutlined/>}
|
||||
@ -136,6 +138,8 @@ const AppointmentsPage = () => {
|
||||
visible={appointmentsPageUI.selectedAppointment !== null}
|
||||
onCancel={appointmentsPageUI.handleCancelViewModal}
|
||||
/>
|
||||
|
||||
<ScheduledAppointmentFormModal/>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
|
||||
@ -1,16 +1,9 @@
|
||||
import {Calendar} from "antd";
|
||||
import 'dayjs/locale/ru';
|
||||
import dayjs from 'dayjs';
|
||||
import utc from 'dayjs/plugin/utc';
|
||||
import timezone from 'dayjs/plugin/timezone';
|
||||
import CalendarCell from "../../../../Widgets/CalendarCell.jsx";
|
||||
import useAppointments from "../../useAppointments.js";
|
||||
import useAppointmentCalendarUI from "./useAppointmentCalendarUI.js";
|
||||
|
||||
dayjs.extend(utc);
|
||||
dayjs.extend(timezone);
|
||||
dayjs.tz.setDefault('Asia/Almaty');
|
||||
|
||||
const AppointmentsCalendarTab = () => {
|
||||
const appointmentsData = useAppointments();
|
||||
const appointmentsCalendarUI = useAppointmentCalendarUI(
|
||||
|
||||
@ -27,7 +27,6 @@ const AppointmentFormModal = ({onCancel}) => {
|
||||
const appointmentFormModalUI = useAppointmentFormModalUI(
|
||||
onCancel,
|
||||
appointmentFormModalData.createAppointment,
|
||||
appointmentFormModalData.updateAppointment,
|
||||
appointmentFormModalData.patients,
|
||||
);
|
||||
|
||||
|
||||
@ -19,15 +19,13 @@ const useAppointmentFormModal = () => {
|
||||
});
|
||||
|
||||
const [createAppointment, {isLoading: isCreating, isError: isErrorCreating}] = useCreateAppointmentMutation();
|
||||
const [updateAppointment, {isLoading: isUpdating, isError: isErrorUpdating}] = useUpdateAppointmentMutation();
|
||||
|
||||
return {
|
||||
patients,
|
||||
appointmentTypes,
|
||||
createAppointment,
|
||||
updateAppointment,
|
||||
isLoading: isLoadingPatients || isLoadingAppointmentTypes || isCreating || isUpdating,
|
||||
isError: isErrorPatients || isErrorAppointmentTypes || isErrorCreating || isErrorUpdating,
|
||||
isLoading: isLoadingPatients || isLoadingAppointmentTypes || isCreating,
|
||||
isError: isErrorPatients || isErrorAppointmentTypes || isErrorCreating,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@ import {Grid} from "antd";
|
||||
|
||||
const {useBreakpoint} = Grid;
|
||||
|
||||
const useAppointmentFormModalUI = (onCancel, createAppointment, updateAppointment, patients) => {
|
||||
const useAppointmentFormModalUI = (onCancel, createAppointment, patients) => {
|
||||
const dispatch = useDispatch();
|
||||
const {modalVisible} = useSelector(state => state.appointmentsUI);
|
||||
const [form] = Form.useForm();
|
||||
@ -57,7 +57,7 @@ const useAppointmentFormModalUI = (onCancel, createAppointment, updateAppointmen
|
||||
appointment_datetime: dayjs(new Date()),
|
||||
})
|
||||
}
|
||||
}, [modalVisible, form, appointments]);
|
||||
}, [modalVisible, form]);
|
||||
|
||||
const handleResultsChange = (newContent) => {
|
||||
setResults(newContent);
|
||||
|
||||
@ -0,0 +1,107 @@
|
||||
import useScheduledAppointmentFormModal from "./useScheduledAppointmentFormModal.js";
|
||||
import useScheduledAppointmentFormModalUI from "./useScheduledAppointmentFormModalUI.js";
|
||||
import {Button, Collapse, DatePicker, Input, Modal, Select, Space, Typography} from "antd";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
|
||||
const ScheduledAppointmentFormModal = () => {
|
||||
const scheduledAppointmentFormModalData = useScheduledAppointmentFormModal();
|
||||
const scheduledAppointmentFormModalUI = useScheduledAppointmentFormModalUI(scheduledAppointmentFormModalData.patients, scheduledAppointmentFormModalData.createScheduledAppointment);
|
||||
|
||||
const patientsItems = scheduledAppointmentFormModalUI.filteredPatients.map((patient) => ({
|
||||
key: patient.id,
|
||||
label: `${patient.last_name} ${patient.first_name} (${scheduledAppointmentFormModalUI.getDateString(patient.birthday)})`,
|
||||
children: (
|
||||
<div>
|
||||
<p><b>Пациент:</b> {patient.last_name} {patient.first_name}</p>
|
||||
<p><b>Дата рождения:</b> {scheduledAppointmentFormModalUI.getDateString(patient.birthday)}</p>
|
||||
<p><b>Диагноз:</b> {patient.diagnosis || 'Не указан'}</p>
|
||||
<p><b>Email:</b> {patient.email || 'Не указан'}</p>
|
||||
<p><b>Телефон:</b> {patient.phone || 'Не указан'}</p>
|
||||
<Button type="primary" onClick={() => scheduledAppointmentFormModalUI.setSelectedPatient(patient)}>
|
||||
Выбрать
|
||||
</Button>
|
||||
</div>
|
||||
),
|
||||
}));
|
||||
|
||||
return (
|
||||
<>
|
||||
<Modal
|
||||
title="Планирование приема"
|
||||
open={scheduledAppointmentFormModalUI.scheduledModalVisible}
|
||||
onCancel={scheduledAppointmentFormModalUI.handleCancelModal}
|
||||
footer={null}
|
||||
width={scheduledAppointmentFormModalUI.modalWidth}
|
||||
>
|
||||
<Space direction="vertical">
|
||||
<Typography.Text>Тип приема</Typography.Text>
|
||||
<Select
|
||||
placeholder="Тип приема"
|
||||
value={scheduledAppointmentFormModalUI.selectedAppointmentType}
|
||||
onChange={scheduledAppointmentFormModalUI.handleSetSelectedAppointmentType}
|
||||
>
|
||||
{scheduledAppointmentFormModalData.appointmentTypes.map((appointmentType) => (
|
||||
<Select.Option key={appointmentType.id} value={appointmentType.id}>
|
||||
{appointmentType.title}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
<Typography.Text>Планируемая дата приема</Typography.Text>
|
||||
<DatePicker
|
||||
showTime
|
||||
format="DD.MM.YYYY HH:mm"
|
||||
placeholder="Дата приема"
|
||||
style={scheduledAppointmentFormModalUI.datePickerStyle}
|
||||
value={scheduledAppointmentFormModalUI.selectedDateTime}
|
||||
onChange={scheduledAppointmentFormModalUI.handleSetDateTime}
|
||||
minDate={dayjs(new Date()).add(1, 'day')}
|
||||
/>
|
||||
</Space>
|
||||
{scheduledAppointmentFormModalUI.selectedPatient ? (
|
||||
<div style={scheduledAppointmentFormModalUI.blockStepStyle}>
|
||||
<Typography.Text strong>
|
||||
{scheduledAppointmentFormModalUI.selectedPatient.last_name} {scheduledAppointmentFormModalUI.selectedPatient.first_name}
|
||||
</Typography.Text>
|
||||
<p><b>Дата
|
||||
рождения:</b> {scheduledAppointmentFormModalUI.getSelectedPatientBirthdayString()}
|
||||
</p>
|
||||
<p><b>Email:</b> {scheduledAppointmentFormModalUI.selectedPatient.email || 'Не указан'}</p>
|
||||
<p><b>Телефон:</b> {scheduledAppointmentFormModalUI.selectedPatient.phone || 'Не указан'}
|
||||
</p>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={scheduledAppointmentFormModalUI.resetPatient}
|
||||
danger
|
||||
>
|
||||
Выбрать другого пациента
|
||||
</Button>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<Input
|
||||
placeholder="Поиск пациента"
|
||||
value={scheduledAppointmentFormModalUI.searchPatientString}
|
||||
onChange={scheduledAppointmentFormModalUI.handleSetSearchPatientString}
|
||||
style={scheduledAppointmentFormModalUI.searchInputStyle}
|
||||
allowClear
|
||||
/>
|
||||
<div style={scheduledAppointmentFormModalUI.chooseContainerStyle}>
|
||||
<Collapse items={patientsItems}/>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<Button
|
||||
type="primary"
|
||||
disabled={scheduledAppointmentFormModalUI.selectedPatient === null || scheduledAppointmentFormModalUI.selectedDateTime === null || scheduledAppointmentFormModalUI.selectedAppointmentType === null}
|
||||
onClick={scheduledAppointmentFormModalUI.handleCreateScheduledAppointment}
|
||||
style={scheduledAppointmentFormModalUI.buttonStyle}
|
||||
>
|
||||
Запланировать
|
||||
</Button>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ScheduledAppointmentFormModal;
|
||||
@ -0,0 +1,34 @@
|
||||
import {useGetPatientsQuery} from "../../../../../Api/patientsApi.js";
|
||||
import {useGetAppointmentTypesQuery} from "../../../../../Api/appointmentTypesApi.js";
|
||||
import {useCreateScheduledAppointmentMutation} from "../../../../../Api/scheduledAppointmentsApi.js";
|
||||
|
||||
|
||||
const useScheduledAppointmentFormModal = () => {
|
||||
const {
|
||||
data: patients = [],
|
||||
isLoading: isLoadingPatients,
|
||||
isError: isErrorPatients,
|
||||
} = useGetPatientsQuery(undefined, {
|
||||
pollingInterval: 20000,
|
||||
});
|
||||
|
||||
const {
|
||||
data: appointmentTypes = [],
|
||||
isLoading: isLoadingAppointmentTypes,
|
||||
isError: isErrorAppointmentTypes,
|
||||
} = useGetAppointmentTypesQuery(undefined, {
|
||||
pollingInterval: 20000,
|
||||
});
|
||||
|
||||
const [createScheduledAppointment, {isLoading: isCreating, isError: isErrorCreating}] = useCreateScheduledAppointmentMutation();
|
||||
|
||||
return {
|
||||
patients,
|
||||
appointmentTypes,
|
||||
createScheduledAppointment,
|
||||
isLoading: isLoadingPatients || isLoadingAppointmentTypes || isCreating,
|
||||
isError: isErrorPatients || isErrorAppointmentTypes || isErrorCreating,
|
||||
};
|
||||
};
|
||||
|
||||
export default useScheduledAppointmentFormModal;
|
||||
@ -0,0 +1,112 @@
|
||||
import {useDispatch, useSelector} from "react-redux";
|
||||
import {closeScheduledModal} from "../../../../../Redux/Slices/appointmentsSlice.js";
|
||||
import {useMemo, useState} from "react";
|
||||
import dayjs from "dayjs";
|
||||
import {notification} from "antd";
|
||||
|
||||
|
||||
const useScheduledAppointmentFormModalUI = (patients, createScheduledAppointment) => {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const {
|
||||
scheduledModalVisible,
|
||||
} = useSelector(state => state.appointmentsUI);
|
||||
|
||||
const [searchPatientString, setSearchPatientString] = useState('');
|
||||
const [selectedPatient, setSelectedPatient] = useState(null);
|
||||
const [selectedDateTime, setSelectedDateTime] = useState(dayjs(new Date()).add(1, 'day'));
|
||||
const [selectedAppointmentType, setSelectedAppointmentType] = useState(null);
|
||||
|
||||
const handleCreateScheduledAppointment = async () => {
|
||||
try {
|
||||
const data = {
|
||||
scheduled_datetime: selectedDateTime.format('YYYY-MM-DD HH:mm:ss'),
|
||||
patient_id: selectedPatient.id,
|
||||
type_id: selectedAppointmentType
|
||||
};
|
||||
|
||||
console.log(data)
|
||||
|
||||
await createScheduledAppointment(data).unwrap();
|
||||
notification.success({
|
||||
message: 'Прием создан',
|
||||
placement: 'topRight',
|
||||
description: 'Прием успешно запланирован.',
|
||||
})
|
||||
|
||||
dispatch(closeScheduledModal());
|
||||
} catch (error) {
|
||||
notification.error({
|
||||
message: 'Ошибка',
|
||||
description: error.data?.message || 'Не удалось сохранить прием.',
|
||||
placement: 'topRight',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const filteredPatients = useMemo(() => patients.filter((patient) => {
|
||||
const searchLower = searchPatientString.toLowerCase();
|
||||
|
||||
return Object.values(patient)
|
||||
.filter(value => typeof value === "string")
|
||||
.some(value => value.toLowerCase().includes(searchLower));
|
||||
}), [patients, searchPatientString]);
|
||||
|
||||
const handleSetDateTime = (date) => {
|
||||
setSelectedDateTime(date);
|
||||
};
|
||||
|
||||
const handleSetSearchPatientString = (e) => {
|
||||
setSearchPatientString(e.target.value);
|
||||
};
|
||||
|
||||
const handleSetSelectedAppointmentType = (type) => {
|
||||
setSelectedAppointmentType(type);
|
||||
};
|
||||
|
||||
const getDateString = (date) => {
|
||||
return date ? dayjs(date).format('DD.MM.YYYY') : 'Не указано';
|
||||
};
|
||||
|
||||
const getSelectedPatientBirthdayString = () => {
|
||||
return selectedPatient ? getDateString(selectedPatient.birthday) : 'Не выбран';
|
||||
};
|
||||
|
||||
const resetPatient = () => {
|
||||
setSelectedPatient(null);
|
||||
};
|
||||
|
||||
const searchInputStyle = {marginBottom: 16};
|
||||
|
||||
const modalWidth = 700;
|
||||
|
||||
const handleCancelModal = () => {
|
||||
dispatch(closeScheduledModal());
|
||||
};
|
||||
|
||||
const datePickerStyle = {marginBottom: 16};
|
||||
const buttonStyle = {marginTop: 8};
|
||||
|
||||
return {
|
||||
scheduledModalVisible,
|
||||
filteredPatients,
|
||||
selectedPatient,
|
||||
setSelectedPatient,
|
||||
modalWidth,
|
||||
selectedDateTime,
|
||||
searchInputStyle,
|
||||
datePickerStyle,
|
||||
buttonStyle,
|
||||
selectedAppointmentType,
|
||||
handleCancelModal,
|
||||
handleSetSearchPatientString,
|
||||
resetPatient,
|
||||
getSelectedPatientBirthdayString,
|
||||
getDateString,
|
||||
handleSetDateTime,
|
||||
handleSetSelectedAppointmentType,
|
||||
handleCreateScheduledAppointment,
|
||||
}
|
||||
};
|
||||
|
||||
export default useScheduledAppointmentFormModalUI
|
||||
@ -4,7 +4,8 @@ import {
|
||||
setHovered,
|
||||
setSelectedAppointment,
|
||||
setSelectedScheduledAppointment,
|
||||
toggleSider
|
||||
toggleSider,
|
||||
openScheduledModal,
|
||||
} from "../../../Redux/Slices/appointmentsSlice.js";
|
||||
import { useEffect, useMemo } from "react";
|
||||
import dayjs from "dayjs";
|
||||
@ -60,8 +61,7 @@ const useAppointmentsUI = (appointments, scheduledAppointments) => {
|
||||
};
|
||||
|
||||
const openCreateScheduledAppointmentModal = () => {
|
||||
// Логика для запланированных приемов будет добавлена позже
|
||||
console.log('Открыть модальное окно для запланированного приема');
|
||||
dispatch(openScheduledModal());
|
||||
};
|
||||
|
||||
const siderButtonText = useMemo(() => hovered ? (collapsed ? "Показать предстоящие события" : "Скрыть предстоящие события") : "", [collapsed, hovered]);
|
||||
|
||||
@ -5,6 +5,7 @@ const initialState = {
|
||||
siderWidth: 250,
|
||||
hovered: false,
|
||||
modalVisible: false,
|
||||
scheduledModalVisible: false,
|
||||
selectedAppointment: null,
|
||||
scheduledAppointments: [],
|
||||
selectedScheduledAppointment: null,
|
||||
@ -32,6 +33,15 @@ const appointmentsSlice = createSlice({
|
||||
closeModal: (state) => {
|
||||
state.modalVisible = false;
|
||||
},
|
||||
openScheduledModal: (state) => {
|
||||
state.scheduledModalVisible = true;
|
||||
},
|
||||
closeScheduledModal: (state) => {
|
||||
state.scheduledModalVisible = false;
|
||||
},
|
||||
setSelectedAppointments: (state, action) => {
|
||||
state.selectedAppointments = action.payload;
|
||||
},
|
||||
setSelectedAppointment: (state, action) => {
|
||||
state.selectedAppointment = action.payload;
|
||||
},
|
||||
@ -55,6 +65,8 @@ export const {
|
||||
setSelectedAppointment,
|
||||
setSelectedScheduledAppointment,
|
||||
setScheduledAppointments,
|
||||
openScheduledModal,
|
||||
closeScheduledModal,
|
||||
} = appointmentsSlice.actions;
|
||||
|
||||
export default appointmentsSlice.reducer;
|
||||
Loading…
x
Reference in New Issue
Block a user