From 941fe191993a4aecc79d69c07a7c86bc3c0c83d3 Mon Sep 17 00:00:00 2001 From: BigSimmo <87357024+BigSimmo@users.noreply.github.com> Date: Fri, 3 Jul 2026 01:56:17 +0800 Subject: [PATCH 1/3] fix(ui): design-review fixes for differentials, services, favourites - differentials detail: give each Compare Basket item its own severity tag (shared likelihoodTag helper) instead of stamping the parent's status on all items; show the real related count instead of length + 8 - differentials stream: replace internal QA-scaffolding copy ("Stream helper / workflow verification only") with clinician-facing "Keep exploring" guidance - services: expand "ATSI" display labels to "Aboriginal and Torres Strait Islander" while keeping "ATSI" as a search keyword in tags; rename the duplicate "Find a service" card to "Search services" - favourites: derive saved-set counts from real items (2/2/1 vs 12/9/7), relabel the ambiguous "Active" stat to "Filters", fix "1 item" pluralization Co-Authored-By: Claude Opus 4.8 --- .../clinical-dashboard/favourites-hub.tsx | 5 +- .../favourites-prototype-data.ts | 8 +- .../differential-detail-page.tsx | 77 +++++++++++-------- .../differential-stream-page.tsx | 16 ++-- .../services/services-home-page.tsx | 4 +- src/lib/services.ts | 12 +-- 6 files changed, 66 insertions(+), 56 deletions(-) diff --git a/src/components/clinical-dashboard/favourites-hub.tsx b/src/components/clinical-dashboard/favourites-hub.tsx index aacfdd19b..eddd2c9d1 100644 --- a/src/components/clinical-dashboard/favourites-hub.tsx +++ b/src/components/clinical-dashboard/favourites-hub.tsx @@ -135,7 +135,7 @@ export function FavouritesHub({ {[ { label: "Items", value: itemCount, icon: Heart }, { label: "Sets", value: setCount, icon: Folder }, - { label: "Active", value: activeFilterCount, icon: Filter }, + { label: "Filters", value: activeFilterCount, icon: Filter }, ].map((stat) => { const Icon = stat.icon; return ( @@ -490,7 +490,8 @@ function FavouriteSetRow({ selected ? "text-[color:var(--clinical-accent)]" : "text-[color:var(--text-muted)]", )} > - {favouriteSet.count} items{compact ? "" : ` · ${favouriteSet.meta}`} + {favouriteSet.count} {favouriteSet.count === 1 ? "item" : "items"} + {compact ? "" : ` · ${favouriteSet.meta}`} diff --git a/src/components/clinical-dashboard/favourites-prototype-data.ts b/src/components/clinical-dashboard/favourites-prototype-data.ts index 9ddf9d4b8..b2269fc3e 100644 --- a/src/components/clinical-dashboard/favourites-prototype-data.ts +++ b/src/components/clinical-dashboard/favourites-prototype-data.ts @@ -94,25 +94,27 @@ export const favouriteItems: FavouriteItem[] = [ }, ]; +const countItemsInSet = (title: string) => favouriteItems.filter((item) => item.set === title).length; + export const favouriteSets: FavouriteSet[] = [ { id: "ward-round", title: "Ward round", - count: 12, + count: countItemsInSet("Ward round"), meta: "Medication pages, renal checks, forms", keywords: "ward round acamprosate lithium renal mht forms", }, { id: "prescribing-safety", title: "Prescribing safety", - count: 9, + count: countItemsInSet("Prescribing safety"), meta: "Dose limits, pregnancy, renal cautions", keywords: "prescribing safety dose pregnancy renal qt interactions", }, { id: "clozapine-clinic", title: "Clozapine clinic", - count: 7, + count: countItemsInSet("Clozapine clinic"), meta: "Monitoring, ANC table, counselling", keywords: "clozapine clinic monitoring anc table counselling", }, diff --git a/src/components/differentials/differential-detail-page.tsx b/src/components/differentials/differential-detail-page.tsx index 5bf25bf9a..9ffc2b9fa 100644 --- a/src/components/differentials/differential-detail-page.tsx +++ b/src/components/differentials/differential-detail-page.tsx @@ -87,6 +87,13 @@ function statusLabel(status: DifferentialRecord["status"]) { return "Routine"; } +/** Maps a related node's likelihood to its own severity tag, mirroring the record-status tones. */ +function likelihoodTag(likelihood: DifferentialRecord["related"][number]["likelihood"]) { + if (likelihood === "must-not-miss") return { label: "Emergent", className: statusTone.emergent }; + if (likelihood === "possible") return { label: "Urgent", className: statusTone.urgent }; + return { label: "Review", className: statusTone.routine }; +} + function SectionRow({ section }: { section: DifferentialSection }) { const Icon = sectionIcons[section.tone]; const meta = rowMeta[section.tone]; @@ -202,29 +209,28 @@ function RelatedDiagnoses({ record }: { record: DifferentialRecord }) { Related diagnoses - View all related ({record.related.length + 8}) + View all related ({record.related.length}) @@ -250,7 +256,16 @@ function CurrentPresentation({ record }: { record: DifferentialRecord }) { } function CompareBasket({ record }: { record: DifferentialRecord }) { - const items = [record.title, ...record.related.slice(0, 2).map((node) => node.label)]; + const items = [ + { + id: "self", + label: record.title, + tag: { label: statusLabel(record.status), className: statusTone[record.status] }, + }, + ...record.related + .slice(0, 2) + .map((node) => ({ id: node.id, label: node.label, tag: likelihoodTag(node.likelihood) })), + ]; return (
@@ -265,15 +280,20 @@ function CompareBasket({ record }: { record: DifferentialRecord }) {
    {items.map((item) => (
  • - {item} + {item.label} - - {statusLabel(record.status)} + + {item.tag.label}
  • ))} @@ -382,16 +402,6 @@ function HeaderChrome() { > -
    - - - -
    -

    Mode

    -

    Differentials

    -
    - -
    @@ -486,6 +496,7 @@ export function DifferentialDetailPage({ record }: { record: DifferentialRecord
    +
    {record.sections.map((section) => ( diff --git a/src/components/differentials/differential-stream-page.tsx b/src/components/differentials/differential-stream-page.tsx index 3e8e027d3..71b2e24b9 100644 --- a/src/components/differentials/differential-stream-page.tsx +++ b/src/components/differentials/differential-stream-page.tsx @@ -1,5 +1,5 @@ import Link from "next/link"; -import { ArrowLeft, ArrowRight, CircleHelp, FileText } from "lucide-react"; +import { ArrowLeft, ArrowRight, FileText } from "lucide-react"; import { appModeHomeHref } from "@/lib/app-modes"; import { @@ -67,9 +67,9 @@ export function DifferentialStreamPage({ stream, query = "" }: DifferentialStrea >

    {card.title}

    {card.description}

    -
      +
        {card.examples.map((example) => ( -
      • +
      • {example}
      • @@ -82,14 +82,10 @@ export function DifferentialStreamPage({ stream, query = "" }: DifferentialStrea
        -

        Stream helper

        +

        Keep exploring

        - This stream contains differential diagnosis content only. Use it to move from presentation clues to - diagnosis detail pages without mixing in service or referral records. -

        -

        - - Use for workflow verification only + Return to the differentials home to start from a different presentation, or open search to look up another + differential.

        diff --git a/src/components/services/services-home-page.tsx b/src/components/services/services-home-page.tsx index 796512684..df91c35ba 100644 --- a/src/components/services/services-home-page.tsx +++ b/src/components/services/services-home-page.tsx @@ -13,7 +13,7 @@ import { defaultServiceSlug, serviceRecords } from "@/lib/services"; const taskCards: ModeHomeAction[] = [ { - title: "Find a service", + title: "Search services", description: "Search by need, catchment, provider, or keyword.", icon: FileSearch, href: appModeHomeHref("services", { focus: true }), @@ -43,7 +43,7 @@ const commonPathways: ModeHomePill[] = [ href: appModeHomeHref("services", { query: "crisis support services", focus: true, run: true }), }, { - label: "ATSI-specific", + label: "Aboriginal and Torres Strait Islander", tone: "info", href: appModeHomeHref("services", { query: "Aboriginal Torres Strait Islander services", diff --git a/src/lib/services.ts b/src/lib/services.ts index 502b88c1d..43ac294c8 100644 --- a/src/lib/services.ts +++ b/src/lib/services.ts @@ -82,7 +82,7 @@ export const serviceRecords: ServiceRecord[] = [ subtitle: "Urgent contact, crisis response, or acute support pathway.", statusChips: [ { label: "Crisis / urgent", tone: "danger" }, - { label: "ATSI-specific", tone: "info" }, + { label: "Aboriginal and Torres Strait Islander", tone: "info" }, { label: "Local confirmation", tone: "warning" }, ], primaryContact: { @@ -106,7 +106,7 @@ export const serviceRecords: ServiceRecord[] = [ }, ], route: "Self phone referral", - eligibility: "ATSI callers", + eligibility: "Aboriginal and Torres Strait Islander callers", cost: "Free", referral: "Self referral by phone. Escalate emergency medical danger through emergency services.", location: "Statewide / national", @@ -120,7 +120,7 @@ export const serviceRecords: ServiceRecord[] = [ { id: "eligibility", label: "Eligibility", - title: "ATSI callers", + title: "Aboriginal and Torres Strait Islander callers", detail: "See details", }, { @@ -140,7 +140,7 @@ export const serviceRecords: ServiceRecord[] = [ { label: "Primary route", value: "Contact: 13 92 76\nSelf phone referral" }, { label: "Phone", value: "13 92 76" }, { label: "Email", value: "None listed" }, - { label: "Provider", value: "ATSI crisis support service referenced by WACHS" }, + { label: "Provider", value: "Aboriginal and Torres Strait Islander crisis support service referenced by WACHS" }, { label: "Region", value: "Statewide / national" }, { label: "Patient group", value: "Aboriginal and Torres Strait Islander people" }, { label: "Hours", value: "Not publicly stated" }, @@ -148,7 +148,7 @@ export const serviceRecords: ServiceRecord[] = [ ], bestUse: "Culturally safe crisis phone support; escalate emergency danger elsewhere.", criteria: [ - { label: "ATSI support need", tone: "meet" }, + { label: "Aboriginal and Torres Strait Islander support need", tone: "meet" }, { label: "Crisis support pathway appropriate", tone: "meet" }, { label: "Phone referral available", tone: "meet" }, { label: "Emergency medical danger present", tone: "reject" }, @@ -411,7 +411,7 @@ export const serviceRecords: ServiceRecord[] = [ title: "State-wide Specialist Aboriginal Mental Health Service", subtitle: "Great Southern WACHS service combining cultural and clinical mental health expertise.", statusChips: [ - { label: "ATSI-specific", tone: "info" }, + { label: "Aboriginal and Torres Strait Islander", tone: "info" }, { label: "Regional WA", tone: "success" }, { label: "Local confirmation", tone: "warning" }, ], From b60fd61fbd57dce7698094507c9e990ac5411e54 Mon Sep 17 00:00:00 2001 From: BigSimmo <87357024+BigSimmo@users.noreply.github.com> Date: Fri, 3 Jul 2026 02:04:07 +0800 Subject: [PATCH 2/3] fix(ui): design-review fixes for service detail, search header, sidebar, form Commits the current working-tree state of these four files, which also carries in-progress edits already present on this branch. Design-review changes here: - service detail: use break-words instead of break-all so referral values no longer split mid-word ("service" -> "servic e", "Strait" -> "Stra it") - master search header: use the magnifier submit affordance for every search-mode home; differentials no longer uses the send arrow - sidebar: expand tool labels to full words (Favourites, Differentials, Medications) for consistency with the rest of the nav - form detail: render Verification notes as proper sentences (joinNotes) instead of a bare space-joined run-on Co-Authored-By: Claude Opus 4.8 --- .../clinical-dashboard/ClinicalSidebar.tsx | 65 ++++---- .../master-search-header.tsx | 14 +- src/components/forms/form-detail-page.tsx | 40 +++-- .../services/service-detail-page.tsx | 150 +++++++----------- 4 files changed, 127 insertions(+), 142 deletions(-) diff --git a/src/components/clinical-dashboard/ClinicalSidebar.tsx b/src/components/clinical-dashboard/ClinicalSidebar.tsx index 4de23418d..351b1f6ef 100644 --- a/src/components/clinical-dashboard/ClinicalSidebar.tsx +++ b/src/components/clinical-dashboard/ClinicalSidebar.tsx @@ -5,7 +5,6 @@ import Link from "next/link"; import { BookOpen, BrainCircuit, - ChevronDown, ClipboardList, FileText, Heart, @@ -47,14 +46,18 @@ export function deriveSidebarIdentity(email: string | null | undefined): Sidebar return { displayName, initials, detail: normalized, signedIn: true }; } +function accountProfileLabel(identity: SidebarIdentity) { + return `${identity.initials} ${identity.displayName} ${identity.detail}. Open account profile`; +} + const sidebarToolItems = [ { id: "answer", label: "Answer", icon: Sparkles, href: "/?mode=answer" }, { id: "documents", label: "Documents", icon: FileText, href: "/?mode=documents" }, { id: "services", label: "Services", icon: ClipboardList, href: "/services" }, { id: "forms", label: "Forms", icon: FileText, href: "/forms" }, - { id: "favourites", label: "Faves", icon: Heart, href: "/favourites" }, - { id: "differentials", label: "Diffs", icon: BrainCircuit, href: "/differentials" }, - { id: "prescribing", label: "Meds", icon: Pill, href: "/?mode=prescribing" }, + { id: "favourites", label: "Favourites", icon: Heart, href: "/favourites" }, + { id: "differentials", label: "Differentials", icon: BrainCircuit, href: "/differentials" }, + { id: "prescribing", label: "Medications", icon: Pill, href: "/?mode=prescribing" }, { id: "tools", label: "Tools", icon: Wrench, href: "/?mode=tools" }, ] as const; @@ -100,6 +103,7 @@ export function ClinicalSidebarContent({ const visibleRecentQueries = matchingRecentQueries.slice(0, 5); const ThemeIcon = theme === "dark" ? Sun : Moon; const nextThemeLabel = theme === "dark" ? "Light mode" : "Dark mode"; + const accountLabel = accountProfileLabel(identity); return (
        @@ -114,7 +118,7 @@ export function ClinicalSidebarContent({
        - - View tools - -
        @@ -277,7 +270,7 @@ export function ClinicalSidebarContent({ }} data-testid="sidebar-account-settings" className="mt-2 flex w-full items-center gap-3 rounded-lg border border-[color:var(--border)] bg-[color:var(--surface)] px-3 py-2 text-left shadow-[var(--shadow-inset)] transition hover:border-[color:var(--clinical-accent-border)] hover:bg-[color:var(--clinical-accent-soft)]/40 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[color:var(--focus)]" - aria-label={identity.signedIn ? `Open account profile for ${identity.detail}` : "Open account profile"} + aria-label={accountLabel} > {identity.initials} @@ -299,6 +292,7 @@ export function ClinicalSidebarContent({ export function ClinicalDesktopSidebar({ collapsed, + collapseLocked = false, recentQueries, identity, activeMode, @@ -312,6 +306,7 @@ export function ClinicalDesktopSidebar({ onPrefetchApplications, }: { collapsed: boolean; + collapseLocked?: boolean; recentQueries: string[]; identity: SidebarIdentity; activeMode: AppModeId; @@ -325,6 +320,7 @@ export function ClinicalDesktopSidebar({ onPrefetchApplications: () => void; }) { const CollapsedThemeIcon = theme === "dark" ? Sun : Moon; + const accountLabel = accountProfileLabel(identity); if (collapsed) { return ( @@ -333,16 +329,27 @@ export function ClinicalDesktopSidebar({ className="hidden min-h-0 border-r border-[color:var(--border)] bg-[color:var(--surface-lux)] py-4 shadow-[var(--shadow-soft)] lg:flex lg:w-[5.25rem] lg:flex-col lg:items-center" >
        - + {collapseLocked ? ( + + + + ) : ( + + )}
        @@ -358,7 +365,7 @@ export function ClinicalDesktopSidebar({ diff --git a/src/components/clinical-dashboard/master-search-header.tsx b/src/components/clinical-dashboard/master-search-header.tsx index e287531d2..b38e91531 100644 --- a/src/components/clinical-dashboard/master-search-header.tsx +++ b/src/components/clinical-dashboard/master-search-header.tsx @@ -246,7 +246,8 @@ export function MasterSearchHeader({ const isWorkflowHeader = headerVariant === "workflow"; const isMobileBottomComposer = searchComposerVisible && mobileSearchPlacement === "bottom" && !isAnswerFooterComposer; const isHeroDesktopComposer = desktopSearchPlacement === "hero" && isMobileBottomComposer; - const canRunLocalSearch = selectedSearch.kind === "tools" || selectedSearch.kind === "favourites"; + const canRunLocalSearch = + selectedSearch.kind === "services" || selectedSearch.kind === "tools" || selectedSearch.kind === "favourites"; const canAsk = trimmedQuery.length >= 1 && !loading && selectedSearchable && (realDataReady || canRunLocalSearch); const indexedDocumentTotal = documentTotal ?? documents.length; const hasUnloadedDocuments = indexedDocumentTotal > documents.length; @@ -355,7 +356,6 @@ export function MasterSearchHeader({ searchMode === "prescribing" ? medicationModeActionItems : modeActionItemsFor(actionMenuSetId); const actionMenuTitle = selectedAppMode.label; const actionMenuButtonLabel = `Open ${selectedAppMode.label.toLowerCase()} options`; - const isStandaloneModeHomeHeader = Boolean(desktopHomeComposerSlotId); const useMobileBackControl = mobileLeadingAction === "back"; function currentUsesScopeSheet() { @@ -887,7 +887,9 @@ export function MasterSearchHeader({ const usesAnswerFooterStyle = isAnswerFooterComposer && !isDesktopHomeComposer; const usesMobileBottomStyle = isMobileBottomComposer && !isDesktopHomeComposer; const usesUniversalFooterStyle = usesAnswerFooterStyle || (usesMobileBottomStyle && usesPhoneSearchLayout); - const usesSendAffordance = usesAnswerFooterStyle || (isStandaloneModeHomeHeader && searchMode === "differentials"); + const showFooterSearchChips = usesUniversalFooterStyle && searchMode === "answer"; + // Only the Answer chat composer uses the send affordance; every search-mode home uses the magnifier. + const usesSendAffordance = usesAnswerFooterStyle; const composerPlaceholder = usesMobileBottomStyle && searchMode === "differentials" ? "Search a presentation" : queryPlaceholder; @@ -997,7 +999,7 @@ export function MasterSearchHeader({ {submitLabel}
        - {usesUniversalFooterStyle ? ( + {showFooterSearchChips ? (
        - ))} -
    - -
    - - - - - - - - - - - - - - - - {rows.map((row, index) => ( - - - - - - ))} - -
    Service referral information
    - Field - - Detail - - Action -
    - - - {renderRowIcon(row.label)} - - - - {row.label} - - {index === 0 ? ( - - Primary access route - - ) : null} - - - -

    - {displayText(row.value)} -

    -
    - -
    -
    - + ); + })} +
    ); } @@ -473,12 +430,14 @@ function CriteriaGroup({ } function TagList({ items, emptyLabel }: { items: string[]; emptyLabel: string }) { - if (!items.length) return

    {emptyLabel}

    ; + const uniqueItems = dedupeTagItems(items); + + if (!uniqueItems.length) return

    {emptyLabel}

    ; return (
    - {items.map((item) => ( - + {uniqueItems.map((item) => ( + {item} ))} @@ -573,7 +532,7 @@ export function ServiceDetailPage({ service }: { service: ServiceRecord }) { return (
    {notice ? ( @@ -661,7 +620,7 @@ export function ServiceDetailPage({ service }: { service: ServiceRecord }) {

    Contact

    -

    +

    Contact: {displayText(primaryContact?.value)}

    @@ -694,15 +653,16 @@ export function ServiceDetailPage({ service }: { service: ServiceRecord }) {

    - + ))} +
    + +
    + + + + + + + + + + + + + + + + {rows.map((row, index) => ( + + + + + + ))} + +
    Service referral information
    + Field + + Detail + + Action +
    + + + {renderRowIcon(row.label)} + + + + {row.label} + + {index === 0 ? ( + + Primary access route + + ) : null} + + + +

    + {displayText(row.value)} +

    +
    + +
    +
    + ); } @@ -430,14 +473,12 @@ function CriteriaGroup({ } function TagList({ items, emptyLabel }: { items: string[]; emptyLabel: string }) { - const uniqueItems = dedupeTagItems(items); - - if (!uniqueItems.length) return

    {emptyLabel}

    ; + if (!items.length) return

    {emptyLabel}

    ; return (
    - {uniqueItems.map((item) => ( - + {items.map((item) => ( + {item} ))} @@ -532,7 +573,7 @@ export function ServiceDetailPage({ service }: { service: ServiceRecord }) { return (
    {notice ? ( @@ -620,7 +661,7 @@ export function ServiceDetailPage({ service }: { service: ServiceRecord }) {

    Contact

    -

    +

    Contact: {displayText(primaryContact?.value)}

    @@ -653,16 +694,15 @@ export function ServiceDetailPage({ service }: { service: ServiceRecord }) {

-
+