import React, { useState, useEffect, useCallback } from 'react'; import { useNavigate, useParams } from 'react-router-dom'; import { DismissibleAlert } from '../../../../../components/ui/dismissible-alert'; import { Spacer } from '../../../../../components/ui/spacer'; import { useForm, FormProvider } from 'react-hook-form'; import { MedicationAdministration } from 'fhir/r4'; import { Check, FileText } from 'lucide-react'; import { useAuthServiceContext, useMedicationAdministrationServiceContext, usePatientServiceContext } from '../../../../../services'; import { AppHeader, AppSpinner, ProfileLayoutContainer, ScrollableTab, HistoryNavigator, FhirMedicationAdministration, FhirMedicationAdministrationDisplay } from '../../../../../components'; import { PrintableRegion, PrintRecord } from '../../../../../components/ui/print-record'; import { useFhirMedicationAdministrationTargetedBroadcast } from '../../../../../hooks'; import { useArchive } from '../../../../../hooks/useArchive'; import ArchiveConfirmModal from '../../../../../components/ArchiveConfirmModal/ArchiveConfirmModal'; import { describeWriteFailure } from '../../../../../utils/fetchFailureMessage'; import { patientCacheGuard } from '../../../../../utils/cacheOwner'; const EditMedicationAdministrationPage: React.FC = () => { const { medicationAdministrationId } = useParams<{ medicationAdministrationId: string }>(); const navigate = useNavigate(); const { userRole, USER_ROLES } = useAuthServiceContext(); const { patientState, encounterState } = usePatientServiceContext(); const { cachedForPatientId: medicationAdministrationsOwner, fetchMedicationAdministration, transformToFhirMedicationAdministration, transformFromFhirMedicationAdministration, createMedicationAdministration, updateMedicationAdministration, archiveMedicationAdministration, medicationAdministrationsList: rawMedicationAdministrationsList } = useMedicationAdministrationServiceContext(); // The cache outlives a change of patient: read only the patient on screen. const medicationAdministrationsGuard = patientCacheGuard(medicationAdministrationsOwner, patientState?.id); const medicationAdministrationsList = medicationAdministrationsGuard.list(rawMedicationAdministrationsList); const [loading, setLoading] = useState(false); const [submitting, setSubmitting] = useState(false); const [success, setSuccess] = useState(); const [error, setError] = useState(); const [medicationAdministration, setMedicationAdministration] = useState(); const isPatient = [USER_ROLES.PATIENT, USER_ROLES.RELATED_PERSON].includes(userRole as string); const isNew = medicationAdministrationId === 'new'; const spacerMargin = '24px'; const methods = useForm({ defaultValues: { identifier: [], instantiates: [], partOf: [], status: '', statusReason: [], category: null, medicationType: 'codeableConcept', medicationCodeableConcept: null, medicationReference: null, subject: null, context: null, supportingInformation: [], effectiveDateTime: '', effectivePeriod: null, performer: [], reasonCode: [], reasonReference: [], request: null, device: [], note: [], dosage: null, eventHistory: [] } }); const { handleSubmit, reset } = methods; const init = useCallback((medicationAdministration: MedicationAdministration) => { reset(transformFromFhirMedicationAdministration(medicationAdministration)); setMedicationAdministration(medicationAdministration); }, [transformFromFhirMedicationAdministration]); useFhirMedicationAdministrationTargetedBroadcast({ medicationAdministration, medicationAdministrationId, onUpdate: init, onDelete: () => navigate('..', { relative: 'path' }) }); const archive = useArchive((reason, note) => archiveMedicationAdministration(medicationAdministration!, reason, note)); useEffect(() => { const loadMedicationAdministration = async () => { if (isNew || medicationAdministration) return; const foundMedicationAdministration = medicationAdministrationsList?.find(ma => ma.id === medicationAdministrationId); if (foundMedicationAdministration) { init(foundMedicationAdministration); return; } setLoading(true); try { const fetchedMedicationAdministration = await fetchMedicationAdministration(medicationAdministrationId as string); init(fetchedMedicationAdministration); } catch (err) { console.error('Error loading medication administration:', err); setError('Could not load medication administration. Try again.'); } finally { setLoading(false); } }; loadMedicationAdministration(); }, [medicationAdministration, medicationAdministrationsList, medicationAdministrationId, fetchMedicationAdministration, init]); const onSubmit = async (data: any) => { if (isPatient) return; if (!patientState) { setError('Patient information is missing. Please try again.'); return; } setSubmitting(true); setSuccess(undefined); setError(undefined); try { if (isNew) { await createMedicationAdministration(transformToFhirMedicationAdministration(data, patientState, encounterState)); navigate('..', { relative: 'path' }); } else { await updateMedicationAdministration({ id: medicationAdministration?.id, ...transformToFhirMedicationAdministration(data, patientState, encounterState) }); } setSuccess(`Medication administration ${isNew ? 'created' : 'updated'} successfully.`); setTimeout(() => setSuccess(undefined), 3_000); } catch (err: any) { console.error(`Error ${isNew ? 'creating' : 'updating'} medication administration:`, err); setError(describeWriteFailure(err, `Failed to ${isNew ? 'create' : 'update'} medication administration`)); } finally { setSubmitting(false); } }; if (loading) { return ( ); } return ( resourceType="MedicationAdministration" resourceId={medicationAdministrationId || ''}> {(showHistory) => (
{!isNew && ( )} {!isPatient && ( )} {!isNew && !isPatient && ( )} {/* The record has to be able to leave the screen. Without this a patient sending it to somebody not on Akashic photographs a scrolling page or prints the browser window, navigation and all. */} {isPatient && medicationAdministration && } {success && ( <> setSuccess(undefined)}> {success} )} {error && ( <> setError(undefined)}>
{error}
)} {/* THE READER GETS THE RECORD, NOT THE FORM SWITCHED OFF. `FhirMedicationAdministrationDisplay` already renders this resource for the history viewer, the reference preview and the patient snapshot, so a patient was the one audience shown the edit form instead of the view written for reading it. The old shape wrapped the form in `inert`, which is worse than it looks: the fields stay in the accessibility tree, so a screen reader still reads them as inputs, and a text range over the subtree returns an EMPTY STRING - a patient could not select or copy their own record. */} {isPatient && medicationAdministration ? ( undefined}/> ) : ( )} { if (!o) archive.close(); }} resourceLabel="Medication Administration" submitting={archive.submitting} error={archive.error} onConfirm={async (reason, note) => { if (await archive.submit(reason, note)) { navigate('..', { relative: 'path' }); } }} />
)} ); }; export default EditMedicationAdministrationPage;