import { useMemo } from "react";
import { useNavigate } from "react-router-dom";
import type { MedicationRequest, MedicationStatement } from "fhir/r4";
import { ChevronRight, Pill, Plus } from "lucide-react";
import { useFhirMedicationRequests, useFhirMedicationStatements } from "../../../../hooks";
import { EmptyState } from "../../../../components/ui/empty-state";
import { OrgBadge } from "../../../../components/ui/org-badge";
import { Skeleton } from "../../../../components/ui/skeleton";
import { relayServerFailure } from "../../../../utils/fetchFailureMessage";
import { usePracticeToday } from "../../../../hooks/usePracticeToday";
import { buildMedicationLists, currentSummary, fillByLine, type MedicationRow } from "./medicationRows";
import { RecordPracticeNote, recordPracticeId, useRecordPracticeView, useRetireOnPracticeChange } from "../../../../contexts/RecordPracticeContext";
import "./MedicationsOverview.css";
type MedicationRecord = MedicationRequest | MedicationStatement;
const isRequest = (record: MedicationRecord): record is MedicationRequest => record.resourceType === "MedicationRequest";
const isStatement = (record: MedicationRecord): record is MedicationStatement => record.resourceType === "MedicationStatement";
// A dose given in the room is not listed here: it belongs to the visit that gave it, and the page says so.
/** The fill deadline, said only once the prescribing practice's calendar is known. */
const FillByLine = ({ row }: { row: MedicationRow }) => {
const practice = usePracticeToday(row.validUntil ? row.organizationId : undefined);
if (practice.state !== "ready") return null;
const line = fillByLine(row, practice.today, practice.zone);
return line ? {line} : null;
};
const MedicationsOverview = () => {
const navigate = useNavigate();
const requests = useFhirMedicationRequests();
const statements = useFhirMedicationStatements();
// A read that has not answered is still loading, never an empty list.
const loading = requests.loading || statements.loading || !requests.answered || !statements.answered;
const failure = requests.error || statements.error;
// A 403 cannot be retried into a list, so it is named rather than offered again.
const refused = Boolean((requests.error && requests.refused) || (statements.error && statements.refused));
// The record's practice context: its own medicines under a named practice; the empty state speaks for the whole record.
// Read from the records themselves, since a statement's row does not carry the practice it was written at.
const records = useMemo(
() => [...requests.medicationRequestsList, ...statements.medicationStatementsList],
[requests.medicationRequestsList, statements.medicationStatementsList],
);
const practiceView = useRecordPracticeView(records, recordPracticeId);
// A medicine opens on its own page, so nothing on this list is left open to close.
useRetireOnPracticeChange(() => undefined);
const wholeRecord = buildMedicationLists(
requests.medicationRequestsList,
statements.medicationStatementsList,
);
const lists = buildMedicationLists(
practiceView.shown.filter(isRequest),
practiceView.shown.filter(isStatement),
);
const rowButton = (row: MedicationRow) => {
const className = `ak-card ak-card--edge ak-meds__row${row.own ? " ak-meds__row--own" : ""}`;
const body = (
{row.name}
{row.schedule && {row.schedule}}
{/* The practice travels with whoever is named (Spencer, 2026-08-29), so a medicine the
patient added names none - there is no clinician on it to travel with. */}
{row.organization && !row.own && (
{row.organization}
)}
{row.provenance}
);
// A record with no id is still shown by its name; it has no page to open.
return row.path ? (
) : (
{body}
);
};
// Nothing current is not nothing recorded: other and past records may still be on the list below.
const anyRecorded = wholeRecord.current.length + wholeRecord.other.length + wholeRecord.past.length > 0;
const addButton = (
);
if (failure) {
return (
);
}
return (
Current prescriptions and reported use
{currentSummary(lists)}
{/* The fetch polls, so `loading` outlives the first result. Only the list waits - the
headings and the right-hand column are the same whether anything has arrived. */}
{lists.current.length > 0 ? (
lists.current.map(rowButton)
) : loading ? (