import { availableRecordEmpty, useChartRecordStanding } from "../../../../hooks/useRecordAccessStanding"; import { useEffect, useState } from "react"; import { useNavigate } from "react-router-dom"; import { MedicationStatement } from "fhir/r4"; import { FhirMedicationStatementDisplay } from "../../../../components"; import { useFhirClinicalLists, useFhirMedicationStatementBroadcast, useFhirMedicationStatements, useFhirMedicationStatementTargetedBroadcast, } from "../../../../hooks"; import { useAuthServiceContext, useListServiceContext, useMedicationStatementServiceContext, usePatientServiceContext } from "../../../../services"; import { ChevronRight, Edit, FileText, Pill, Plus } from "lucide-react"; import { Button } from "../../../../components/ui/button.tsx"; import { Skeleton } from "../../../../components/ui/skeleton.tsx"; import { Sheet, SheetContent } from "../../../../components/ui/sheet.tsx"; import { toast } from "sonner"; import { describeWriteFailure } from "../../../../utils/fetchFailureMessage"; import { DismissibleAlert } from "../../../../components/ui/dismissible-alert"; import { useMyProxies } from "../../../../hooks/useMyProxies"; import { describeProxyCannotAddToRecord, mayAddToRecordAsProxy, proxyStanding } from "../../../../utils/proxyFormAuthority"; import { SourcePractice } from '../../../../components/ui/source-practice'; import { medicationNameOf } from '../../../../utils/medicationName'; import { RecordPracticeNote, recordPracticeId, useRecordPracticeView, useRetireOnPracticeChange } from '../../../../contexts/RecordPracticeContext'; const MedicationStatements = ({ isHomePage = false, }: { isHomePage?: boolean; elevated?: boolean; }) => { const navigate = useNavigate(); const { userRole, USER_ROLES, actingAs } = useAuthServiceContext(); // Below the forms tier a helper cannot add here or attest to none. The tier is known before the press, // so the page says so first, as the task page does. const { proxies, listAnswered } = useMyProxies(); const proxyTier = actingAs ? proxyStanding(proxies, actingAs.relatedPersonId, listAnswered) : undefined; const proxyCannotAdd = !!actingAs && !mayAddToRecordAsProxy(proxyTier); const { patientState } = usePatientServiceContext(); const { confirmNoCurrentMedications } = useListServiceContext(); const { loading, error, medicationStatementsList, freshness: recordFreshness } = useFhirMedicationStatements(); const { hasConfirmedNoCurrentMedications } = useFhirClinicalLists(); const { standing: recordStanding } = useChartRecordStanding(); // THE OPEN DETAIL BELONGS TO WHAT OPENED IT: the service's display scope (the viewer and practice, and a list still // current) and the patient on screen. Once either moves on it is not shown, and it is let go for good, so coming back // does not revive it. A new opening is a deliberate press. const { displayScope } = useMedicationStatementServiceContext(); const [opening, setOpening] = useState<{ statement: MedicationStatement; scope: number; patientId?: string } | null>(null); const selectedMedicationStatement = opening && opening.scope === displayScope && opening.patientId === patientState?.id ? opening.statement : null; const setSelectedMedicationStatement = (statement: MedicationStatement | null) => setOpening(statement ? { statement, scope: displayScope, patientId: patientState?.id } : null); const [showDisplayModal, setShowDisplayModal] = useState(false); // The record's practice context: its own medicines under a named practice; the empty state speaks for the whole record. const practiceView = useRecordPracticeView(medicationStatementsList, recordPracticeId); useRetireOnPracticeChange(() => { setShowDisplayModal(false); setSelectedMedicationStatement(null); }); const [confirmingNoMedications, setConfirmingNoMedications] = useState(false); const retired = Boolean(opening) && !selectedMedicationStatement; useEffect(() => { if (!retired) return; setOpening(null); setShowDisplayModal(false); }, [retired]); useFhirMedicationStatementBroadcast(); useFhirMedicationStatementTargetedBroadcast({ medicationStatement: selectedMedicationStatement || undefined, isOpen: showDisplayModal, onUpdate: (updatedMedicationStatement) => { // Refills the opening it was admitted for; it never reopens one let go meanwhile. setOpening((prev) => (prev ? { ...prev, statement: updatedMedicationStatement } : prev)); }, onDelete: () => { setShowDisplayModal(false); setSelectedMedicationStatement(null); }, }); const viewMedicationStatement = ( medicationStatement: MedicationStatement, ) => { setSelectedMedicationStatement(medicationStatement); setShowDisplayModal(true); }; const editMedicationStatement = ( event: React.MouseEvent, medicationStatement: MedicationStatement, ) => { event.stopPropagation(); const destination = isHomePage ? `../patient/medications/medication-statements/${medicationStatement.id}` : (medicationStatement.id as string); navigate(destination, { relative: "path" }); }; const addMedicationStatement = () => { const destination = isHomePage ? `../patient/medications/medication-statements/new` : "new"; navigate(destination, { relative: "path" }); }; const confirmNoMedications = async () => { if (!patientState) { toast.error("Patient context is not available yet."); return; } setConfirmingNoMedications(true); try { await confirmNoCurrentMedications(patientState); toast.success("Recorded that you are not currently taking medications."); } catch (error) { console.error("Failed to record no current medications:", error); // THE TWIN OF THE ALLERGIES CONTROL, and it fails the same way for the same reason. // "Not currently taking medications" is an assertion somebody prescribes against, so a // refusal that invites another press leaves the record saying nothing while the screen // says it tried. `describeWriteFailure` drops the retry on a 401/403 and replaces the // gateway's own raw sentence. toast.error(describeWriteFailure(error, "Unable to record no current medications")); } finally { setConfirmingNoMedications(false); } }; const isAuthorizedToEdit = () => { return ( !proxyCannotAdd && userRole && [ USER_ROLES.PATIENT, USER_ROLES.RELATED_PERSON, USER_ROLES.PRACTITIONER, ].includes(userRole as string) ); }; // Whose screen this is. Unlike prescriptions, a patient CAN add here - so the empty state's // controls were already honest and only the naming needed the audience split. const isPatientOrProxy = [USER_ROLES.PATIENT, USER_ROLES.RELATED_PERSON].includes(userRole as string); const renderContent = () => { return (
{proxyCannotAdd && ( {describeProxyCannotAddToRecord(actingAs?.subjectName)} )}
{/* The nav row that opens this says "Medicines you told us about"; the page said "Medication Statements" - a resource type, and a different name than the row the patient tapped. The subtitle was written about patients rather than to them ("reported by patients, clinicians, or related persons"). A clinician reading the same page is a different audience and keeps their wording. */}

{isPatientOrProxy ? "Medicines you told us about" : "Medication Statements"}

{isPatientOrProxy ? "Medicines you have told your practices you take, including anything bought without a prescription." : "Records of medication usage reported by patients, clinicians, or related persons"}

{isAuthorizedToEdit() && ( )}
{error ? (
{" "}

Could not load Medication statements

Try again later.

) : !loading && medicationStatementsList.length === 0 ? (

{hasConfirmedNoCurrentMedications ? "No current medications reported" : availableRecordEmpty("No medications added yet", recordStanding, recordFreshness)}

{hasConfirmedNoCurrentMedications ? "You indicated that you are not currently taking any medications." : isAuthorizedToEdit() ? "Add medications you take, or record that you take none." : "Medications will appear here once they are added."}

{isAuthorizedToEdit() && (
{!hasConfirmedNoCurrentMedications && ( )}
)}
) : loading ? (
{[...Array(7)].map((_, index) => (
))}
) : ( medicationStatementsList.length !== 0 && (
{practiceView.shown.map( (medicationStatement: MedicationStatement) => { return (
viewMedicationStatement(medicationStatement) } onKeyDown={(event: React.KeyboardEvent) => { if (event.key !== 'Enter' && event.key !== ' ') return; event.preventDefault(); viewMedicationStatement(medicationStatement); }} >

{medicationNameOf(medicationStatement) ?? "No name provided"} {medicationStatement.status ? medicationStatement.status : "Status unknown"}

{medicationStatement.category?.text && medicationStatement.category.text}{" "} {medicationStatement.dosage?.length && " | " + medicationStatement.dosage?.length + " Dosage"}

{" "}

{isAuthorizedToEdit() && (
)}
); }, )}
) )} {selectedMedicationStatement && ( { if (!open) setSelectedMedicationStatement(null); }} > { setShowDisplayModal(false); setSelectedMedicationStatement(null); }} /> )}
); }; return <>{renderContent()}; }; export default MedicationStatements;