import React from "react"; import { CodeableConcept, Goal, GoalTarget } from "fhir/r4"; import { SheetDescription, SheetHeader } from "../ui/sheet"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "../ui/tabs"; import { Card, CardContent, CardHeader } from "../ui/card"; import { formatDateForDisplay } from "../../lib/utils"; import { FhirReferenceDisplay } from "../../utils/fhirReference"; import { FhirAnnotationDisplay } from "../../utils/fhirAnnotation"; import { FhirQuantityDisplay } from "../../utils/fhirQuantity"; import { FhirRangeDisplay } from "../../utils/fhirRange"; import { FhirRatioDisplay } from "../../utils/fhirRatio"; import { FieldHistoryProvider, FieldHistoryScope, } from "../FieldHistory/FieldHistoryProvider"; import { ShowFieldHistoryToggle } from "../FieldHistory/ShowFieldHistoryToggle"; import { PrintRecord } from "../ui/print-record"; import { RecordEditedMarker } from "../FieldHistory/RecordEditedMarker"; import { Field } from "../FieldHistory/Field"; import { FieldGroup } from "../FieldHistory/FieldLayout"; import { StatusBadge } from "../ui/status"; /** * A concept's words, or nothing: an empty field then reads as the muted dash every other empty field * does, never the shared display's "Qualifications not provided", which was written for credentials. */ const conceptText = (concept: CodeableConcept | CodeableConcept[] | undefined): string | undefined => { const one = (c: CodeableConcept) => c.text?.trim() || c.coding?.find((coding) => coding.display)?.display; const words = (Array.isArray(concept) ? concept : concept ? [concept] : []).map(one).filter(Boolean); return words.length ? words.join(", ") : undefined; }; // Lifecycle codes a reached goal has moved past, so the fields say what the card above them says. const OPEN_LIFECYCLE = new Set(["proposed", "planned", "accepted", "active"]); interface FhirGoalDisplayProps { goal: Goal; // Set when the readings have reached the target: progress is derived at read time, not stored. reached?: boolean; isOpen: boolean; onClose: () => void; title?: string; // Set when the view sits in a page rather than a sheet: it then has no dialog to describe and // scrolls with the page instead of inside itself. inPage?: boolean; } export const FhirGoalDisplay: React.FC = ({ goal, reached = false, isOpen, title, inPage = false, }) => { if (!isOpen) return null; const Body = inPage ? "div" : SheetDescription; // start[x] is polymorphic: either a date or a CodeableConcept, never both. const renderGoalStart = (): React.ReactNode => { if (goal.startDate) { return formatDateForDisplay(goal.startDate); } if (goal.startCodeableConcept) { return conceptText(goal.startCodeableConcept); } return undefined; }; // target.detail[x] is polymorphic across 7 possible types. const renderTargetDetail = (target: GoalTarget): React.ReactNode => { if (target.detailQuantity) { return ; } if (target.detailRange) { return ; } if (target.detailCodeableConcept) { return conceptText(target.detailCodeableConcept); } if (target.detailString) { return target.detailString; } if (target.detailBoolean !== undefined) { return target.detailBoolean ? "Yes" : "No"; } if (target.detailInteger !== undefined) { return target.detailInteger.toString(); } if (target.detailRatio) { return ; } return undefined; }; // target.due[x] is polymorphic: a date or a duration. const renderTargetDue = (target: GoalTarget): React.ReactNode => { if (target.dueDate) { return formatDateForDisplay(target.dueDate); } if (target.dueDuration) { return ; } return undefined; }; /* * THE SHARED PILL, not a thirteenth hand-rolled copy of it. It takes the RAW * code and supplies the word, the tone and the shape itself - which is the point: a surface that * passes its own label is how one state ends up with three words across the product. * * IT RENDERS NOTHING FOR A CODE THE VOCABULARY DOES NOT KNOW, deliberately, where this used to * print the wire format with its dashes swapped for spaces. All six of a goal's lifecycle codes * are in the vocabulary, so nothing that renders today stops. */ const lifecycleStatus = reached && goal.lifecycleStatus && OPEN_LIFECYCLE.has(goal.lifecycleStatus) ? "completed" : goal.lifecycleStatus; const lifecycleStatusPill = lifecycleStatus ? ( ) : undefined; return (
{title || "Goal Details"}

{goal?.description?.text || "No goal name available"}

Overview Outcomes } /> } />
Targets {goal.target?.map((target, index) => (
Target {index + 1}
))}
} /> } />
); }; export default FhirGoalDisplay;