From 69a2fe9cdc7ef2c880af9048ce7356db763383bc Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sat, 8 Aug 2026 17:38:12 +0000 Subject: [PATCH 1/5] feat(forms): condense Form 1A priority facts with Act section detail Replace the redundant Source status card on Form 1A with MHA Act sections that open per-section detail sheets, and shorten the other three priority-fact cards with tap-for-detail bodies. Co-authored-by: BigSimmo --- data/forms-catalog.json | 51 ++++- src/components/forms/form-detail-page.tsx | 227 +++++++++++++++++++--- src/lib/form-catalog.ts | 96 +++++++-- src/lib/form-ranker.ts | 28 +++ tests/form-priority-facts.dom.test.tsx | 39 ++++ tests/forms.test.ts | 15 ++ 6 files changed, 419 insertions(+), 37 deletions(-) create mode 100644 tests/form-priority-facts.dom.test.tsx diff --git a/data/forms-catalog.json b/data/forms-catalog.json index 1e8a212b6d..e9531dc156 100644 --- a/data/forms-catalog.json +++ b/data/forms-catalog.json @@ -78,7 +78,56 @@ "timings": ["within 48 hours", "72 hours"], "sectionCue": "sections 26, 31, 36, 37, 41, 42", "indexedAt": "Official form PDF indexed locally" - } + }, + "priorityFacts": { + "clock": { + "title": "Usually 72 hours", + "detail": "From referral", + "body": "Usually 72 hours from referral. The referral is made within the required assessment window. Clock cues on the form pathway include within 48 hours and 72 hours — record the start time, expiry or review point, and next owner." + }, + "authority": { + "title": "MP or AMHP", + "detail": "Where the Act permits", + "body": "Medical practitioner or authorised mental health practitioner where the Act permits. Authorises referral for examination by a psychiatrist. Does not by itself authorise treatment, detention, transport, restraint, seclusion or force." + }, + "criteria": { + "title": "Reasonable suspicion of ITO criteria", + "detail": "Illness, risk, capacity, least restrictive", + "body": "Reasonable suspicion that involuntary treatment order criteria may be met, with illness features, risk, capacity and least restrictive reasoning documented." + } + }, + "actSections": [ + { + "section": "26", + "title": "Referral for examination at authorised hospital or other place", + "summary": "A medical practitioner or authorised mental health practitioner may refer a person for examination by a psychiatrist if, having regard to the section 25 criteria, they reasonably suspect the person needs an involuntary treatment order — or, if already under a community treatment order, an inpatient treatment order. Examination may be at an authorised hospital or another appropriate place, with necessary arrangements made for that place." + }, + { + "section": "31", + "title": "Revoking referral", + "summary": "An MP or AMHP may revoke a section 26 referral if satisfied the person no longer needs an involuntary treatment order. Revoking another practitioner’s referral requires consultation, or documented reasonable efforts to contact them. The person cannot continue to be detained once the referral is revoked." + }, + { + "section": "36", + "title": "Referral for examination at authorised hospital", + "summary": "For a voluntary inpatient at an authorised hospital, an MP or AMHP may refer them for psychiatrist examination at that hospital if, having regard to the section 25 criteria, they reasonably suspect the inpatient needs an involuntary treatment order." + }, + { + "section": "37", + "title": "Revoking referral", + "summary": "An MP or AMHP may revoke a section 36 referral if satisfied the voluntary inpatient no longer needs an involuntary treatment order. The same consultation rules apply when revoking another practitioner’s referral. Detention under that referral cannot continue once revoked." + }, + { + "section": "41", + "title": "Form of referral", + "summary": "A referral must be in the approved form and include the particulars required by the Act. Use only the current approved Form 1A or PSOLIS pathway; do not invent wording or rely on an outdated PDF." + }, + { + "section": "42", + "title": "Providing information contained in referral to person referred", + "summary": "The practitioner must give the referred person the required referral information (section 41(a) and (b), and usually section 41(c)) in the approved form, subject to the Act’s exception for information supplied by someone else on condition it not be disclosed to the person." + } + ] }, { "id": "form-1b", diff --git a/src/components/forms/form-detail-page.tsx b/src/components/forms/form-detail-page.tsx index 444c14a072..ac2a4d9be4 100644 --- a/src/components/forms/form-detail-page.tsx +++ b/src/components/forms/form-detail-page.tsx @@ -3,6 +3,7 @@ import { Bookmark, BookmarkCheck, + BookOpenText, CalendarDays, ChevronRight, CircleCheck, @@ -25,7 +26,7 @@ import { CircleX, type LucideIcon, } from "lucide-react"; -import { useMemo, useState, type ReactNode } from "react"; +import { useId, useMemo, useState, type ReactNode } from "react"; import { cn, @@ -41,10 +42,12 @@ import { toneSuccess, toneWarning, } from "@/components/ui-primitives"; +import { Sheet } from "@/components/ui/sheet"; import { InformationPageBreadcrumbs, InformationPageShell } from "@/components/information-page-shell"; import { FormCodeBadge, splitFormCode } from "@/components/forms/form-code-badge"; import { appModeHomeHref } from "@/lib/app-modes"; import { formCatalogDetails, formTitleForCode, type FormRecord } from "@/lib/form-catalog"; +import type { FormActSection, FormPriorityFactCard } from "@/lib/form-ranker"; import type { ServiceChipTone, ServiceContact, ServiceCriterion, ServiceSummaryCard } from "@/lib/service-ranker"; import { useAccountData } from "@/components/account-data-provider"; @@ -120,18 +123,52 @@ function formShortTitle(form: FormRecord) { function summaryIcon(card: ServiceSummaryCard) { const label = `${card.id} ${card.label} ${card.title}`.toLowerCase(); - const Icon = label.includes("clock") - ? Clock3 - : label.includes("destination") || label.includes("place") || label.includes("route") - ? MapPin - : label.includes("authority") || label.includes("maker") - ? UserRound - : label.includes("criteria") || label.includes("threshold") - ? Scale - : ClipboardList; + const Icon = + label.includes("act-section") || label.includes("act section") + ? BookOpenText + : label.includes("clock") + ? Clock3 + : label.includes("destination") || label.includes("place") || label.includes("route") + ? MapPin + : label.includes("authority") || label.includes("maker") + ? UserRound + : label.includes("criteria") || label.includes("threshold") + ? Scale + : ClipboardList; return ; } +function priorityFactBody(form: FormRecord, cardId: string): { title: string; body: string; detail?: string } | null { + const details = formCatalogDetails(form); + if (!details) return null; + + const fromCard = (fact: FormPriorityFactCard | undefined, fallbackBody: string, fallbackDetail?: string) => { + if (!fact && !fallbackBody.trim()) return null; + const body = fact?.body?.trim() || fallbackBody.trim(); + if (!body) return null; + return { + title: fact?.title?.trim() || displayText(cardId), + detail: fact?.detail?.trim() || fallbackDetail, + body, + }; + }; + + if (cardId === "clock") { + return fromCard(details.priorityFacts?.clock, details.clock, details.indexedClock); + } + if (cardId === "authority") { + return fromCard( + details.priorityFacts?.authority, + [details.maker, details.authorises, details.doesNotAuthorise].filter(Boolean).join(" "), + details.authorises, + ); + } + if (cardId === "criteria") { + return fromCard(details.priorityFacts?.criteria, details.threshold, details.doesNotAuthorise); + } + return null; +} + function summaryCardsFor(form: FormRecord): ServiceSummaryCard[] { if (form.summaryCards?.length) return form.summaryCards.slice(0, 4); @@ -211,9 +248,17 @@ function criterionToneClass(tone: ServiceCriterion["tone"]) { return toneWarning; } -function DetailCard({ card }: { card: ServiceSummaryCard }) { +function DetailCardShell({ + card, + children, + footer, +}: { + card: ServiceSummaryCard; + children?: ReactNode; + footer?: ReactNode; +}) { return ( -
+
{summaryIcon(card)} @@ -222,13 +267,158 @@ function DetailCard({ card }: { card: ServiceSummaryCard }) { {displayText(card.label, "Priority fact")}

+ {children} + {footer} +
+ ); +} + +function DetailCard({ + card, + onOpenDetail, + hasDetail, +}: { + card: ServiceSummaryCard; + onOpenDetail?: () => void; + hasDetail?: boolean; +}) { + const label = displayText(card.label, "Priority fact"); + const content = ( + <>

{displayText(card.title)}

{displayText(card.detail)}

-
+ + ); + + if (!hasDetail || !onOpenDetail) { + return {content}; + } + + return ( + Tap for detail

} + > + +
+ ); +} + +function ActSectionsCard({ + card, + sections, + onOpenSection, +}: { + card: ServiceSummaryCard; + sections: FormActSection[]; + onOpenSection: (section: string) => void; +}) { + const groupLabelId = useId(); + return ( + +

+ Mental Health Act 2014 sections +

+
+ {sections.map((entry) => ( + + ))} +
+

Tap a section

+
+ ); +} + +function PriorityFactsSection({ form, cards }: { form: FormRecord; cards: ServiceSummaryCard[] }) { + const details = formCatalogDetails(form); + const actSections = details?.actSections ?? []; + const [activeFactId, setActiveFactId] = useState(null); + const [activeSectionId, setActiveSectionId] = useState(null); + + const activeFact = activeFactId ? priorityFactBody(form, activeFactId) : null; + const activeSection = activeSectionId + ? (actSections.find((entry) => entry.section === activeSectionId) ?? null) + : null; + + return ( + <> +
+

+ Priority facts +

+
+ {cards.map((card) => { + if (card.id === "act-sections" && actSections.length) { + return ( + + ); + } + const detail = priorityFactBody(form, card.id); + const hasCondensedDetail = Boolean(details?.priorityFacts && detail); + return ( + setActiveFactId(card.id) : undefined} + /> + ); + })} +
+
+ + setActiveFactId(null)} + title={activeFact ? displayText(activeFact.title) : "Priority fact"} + description={activeFact?.detail} + testId="form-priority-fact-sheet" + mobilePlacement="bottom" + > + {activeFact ?

{activeFact.body}

: null} +
+ + setActiveSectionId(null)} + title={activeSection ? `Section ${activeSection.section}` : "Act section"} + description={activeSection?.title} + testId="form-act-section-sheet" + mobilePlacement="bottom" + > + {activeSection ? ( +
+

{activeSection.summary}

+

+ Condensed reference from the Mental Health Act 2014 (WA). Confirm against the current Act and approved + form before clinical or legal use. +

+
+ ) : null} +
+ ); } @@ -820,16 +1010,7 @@ export function FormDetailPage({ form }: { form: FormRecord }) { /> -
-

- Priority facts -

-
- {summaryCards.map((card) => ( - - ))} -
-
+