import React, { useState, useEffect, useCallback } from 'react'; import { scheduleNoticeSentence } from '../utils/scheduleNotice'; import { Flex, FlexItem } from '../components/ui/flex'; import { DismissibleAlert } from '../components/ui/dismissible-alert'; import { Modal } from '../components/ui/wp-modal-shim'; import { useForm, FormProvider } from 'react-hook-form'; import { Schedule } from 'fhir/r4'; import { useScheduleServiceContext, useAuthServiceContext } from '../services'; import { FhirSchedule } from '../components'; import { useFhirScheduleTargetedBroadcast } from '../hooks'; import { describeWriteFailure } from '../utils/fetchFailureMessage'; import { invalidSubmitMessage } from '../utils/forms/invalidSubmitMessage'; interface ScheduleModalProps { isOpen: boolean; onClose: () => void; schedule?: Schedule; onSuccess?: (schedule: Schedule) => void; onError?: (error: string) => void; title?: string; } export const ScheduleModal: React.FC = ({ isOpen, onClose, schedule, onSuccess, onError, title }) => { const { userRole, USER_ROLES } = useAuthServiceContext(); const { transformToFhirSchedule, transformFromFhirSchedule, createSchedule, updateSchedule } = useScheduleServiceContext(); const [submitting, setSubmitting] = useState(false); const [success, setSuccess] = useState(); const [error, setError] = useState(); const isNew = !schedule; const isPatient = userRole === USER_ROLES.PATIENT; const modalTitle = title || `${isNew ? 'Create' : 'Edit'} Schedule`; const methods = useForm({ defaultValues: { active: true, comment: '', serviceCategory: [], serviceType: [], specialty: [], identifier: [], practitionerRole: null, location: null, healthcareService: null, planningHorizon: null } }); const { handleSubmit, reset } = methods; // A REFUSED CLICK SAYS SO WHERE IT HAPPENED. The button is in the modal header, above a form that // can be several screens tall, and react-hook-form reports each problem inline beside its field - // so somebody scrolled past the first card saw nothing change at all. This fills the error slot // this modal already has, at the top of the form, next to the button they pressed. const onInvalid = (errors: unknown) => setError(invalidSubmitMessage(errors)); const init = useCallback((schedule: Schedule) => { reset(transformFromFhirSchedule(schedule)); }, [transformFromFhirSchedule, reset]); useFhirScheduleTargetedBroadcast({ schedule, isOpen, onUpdate: init, onDelete: onClose }); useEffect(() => { if (schedule) { init(schedule); } }, [schedule, init]); const onSubmit = async (data: any) => { if (isPatient) return; setSubmitting(true); setSuccess(undefined); setError(undefined); try { let result: Schedule; let noticeOutcome: string | null = null; const { practitioner, role } = data.practitionerRole; if (isNew) { ({ schedule: result, noticeOutcome } = await createSchedule(transformToFhirSchedule(data, practitioner, role))); } else { ({ schedule: result, noticeOutcome } = await updateSchedule({ id: schedule?.id, ...transformToFhirSchedule(data) })); } // The provider is emailed when their hours move. If that did not happen, the person who // caused the change is the one who can fix it - so the success line says so rather than // reporting a plain success the server already knows is incomplete. const successMsg = scheduleNoticeSentence(noticeOutcome) ?? `Schedule ${isNew ? 'created' : 'updated'} successfully.`; setSuccess(successMsg); onSuccess?.(result); setTimeout(() => { setSuccess(undefined); onClose(); }, 1500); } catch (err: any) { console.error(`Error ${isNew ? 'creating' : 'updating'} schedule:`, err); const errorMsg = describeWriteFailure(err, `Failed to ${isNew ? 'create' : 'update'} schedule`); setError(errorMsg); onError?.(errorMsg); } finally { setSubmitting(false); } }; if (!isOpen) return null; return ( ]} >
{success && ( setSuccess(undefined)} className="mb-4"> {success} )} {error && ( setError(undefined)} className="mb-4"> {error} )}
); };