From e7263ea0a7e1aef30d6f5f8b15ad4fd7079e5a46 Mon Sep 17 00:00:00 2001 From: BigSimmo <87357024+BigSimmo@users.noreply.github.com> Date: Fri, 3 Jul 2026 04:06:08 +0800 Subject: [PATCH] feat(tools): Command Center uplift on the applications launcher Layer the chosen Command Center design onto the existing launcher while keeping its bones and test contracts: - KPI stat chips (Tools / Review due / Recent) in both variants - Source-aware filters as wrapping pills: All tools / Pinned / Review due / Source-backed - Source-backed shield indicators in rows + a Source-backed/area badge in the detail rail - Fast-actions rail (Open recent work / Review source status / Pin to daily tools; pin wired) - Complete the 7-tool set (add differentials + favourites) + area/sourceBacked on the model - Update ui-tools.spec.ts for the 7-tool count and launchable favourites Co-Authored-By: Claude Opus 4.8 --- src/components/applications-launcher-page.tsx | 178 ++++++++++++++++-- tests/ui-tools.spec.ts | 6 +- 2 files changed, 165 insertions(+), 19 deletions(-) diff --git a/src/components/applications-launcher-page.tsx b/src/components/applications-launcher-page.tsx index 9406747327..58ab0e95b2 100644 --- a/src/components/applications-launcher-page.tsx +++ b/src/components/applications-launcher-page.tsx @@ -3,11 +3,14 @@ import Link from "next/link"; import { BookOpen, + Brain, ChevronRight, ClipboardList, + Clock3, ExternalLink, FileText, Grid2X2, + ListChecks, MoreVertical, Pill, Pin, @@ -15,6 +18,7 @@ import { Plus, Search, Send, + ShieldCheck, Star, X, type LucideIcon, @@ -33,6 +37,7 @@ import { } from "@/components/ui-primitives"; type LauncherStatus = "ready" | "recent" | "review_due"; type LauncherCategory = "clinical" | "admin" | "recent"; +type LauncherArea = "reference" | "assessment" | "care" | "coordination" | "personal"; type LauncherApp = { id: string; @@ -44,9 +49,11 @@ type LauncherApp = { external: boolean; icon: LucideIcon; category: LauncherCategory; + area: LauncherArea; workflow: string; lastUsed: string; status: LauncherStatus; + sourceBacked: boolean; sourceToolId?: string; relatedIds: string[]; quickActions: string[]; @@ -66,15 +73,25 @@ const statusLabels: Record = { }; const filterOptions = [ - { id: "all", label: "All" }, - { id: "clinical", label: "Clinical" }, - { id: "admin", label: "Admin" }, - { id: "recent", label: "Recent" }, + { id: "all", label: "All tools", icon: Grid2X2 }, + { id: "pinned", label: "Pinned", icon: Pin }, + { id: "review_due", label: "Review due", icon: Clock3 }, + { id: "source_backed", label: "Source-backed", icon: ShieldCheck }, ] as const; +const areaLabels: Record = { + reference: "Reference", + assessment: "Assessment", + care: "Treatment", + coordination: "Coordination", + personal: "Saved", +}; + const launcherApps: LauncherApp[] = [ { id: "medication-prescribing", + area: "care", + sourceBacked: true, title: "Medication Prescribing", shortTitle: "Medication", description: "Search, prescribe and review medications.", @@ -97,6 +114,8 @@ const launcherApps: LauncherApp[] = [ }, { id: "documents", + area: "reference", + sourceBacked: true, title: "Documents", description: "Access and manage clinical documents.", detail: "Search indexed PDFs, guidelines, policies, notes, and source documents.", @@ -117,6 +136,8 @@ const launcherApps: LauncherApp[] = [ }, { id: "services", + area: "coordination", + sourceBacked: true, title: "Services", description: "Review source-backed service records and access pathways.", detail: @@ -138,6 +159,8 @@ const launcherApps: LauncherApp[] = [ }, { id: "forms", + area: "coordination", + sourceBacked: true, title: "Forms", description: "Find clinical forms and source-backed form pathways.", detail: "Open the forms home for form search, readiness checks, pathway tasks, and source-backed records.", @@ -158,6 +181,8 @@ const launcherApps: LauncherApp[] = [ }, { id: "clinical-kb-search", + area: "reference", + sourceBacked: true, title: "Clinical KB Search", description: "Search the knowledge base content.", detail: "Search the Clinical KB for source-backed guidance, answers, and evidence.", @@ -176,6 +201,55 @@ const launcherApps: LauncherApp[] = [ { title: "Clozapine source check", date: "May 10, 2025" }, ], }, + { + id: "differentials", + area: "assessment", + sourceBacked: true, + title: "Differentials", + description: "Build and compare diagnostic possibilities.", + detail: "Compare source-aware differentials, weigh must-not-miss risks, and open the presentation view.", + href: "/differentials", + external: false, + icon: Brain, + category: "clinical", + workflow: "Assessment", + lastUsed: "Today, 8:40 AM", + status: "recent", + relatedIds: ["clinical-kb-search", "documents", "medication-prescribing"], + quickActions: [ + "Compare differentials", + "Open presentation view", + "Review must-not-miss risks", + "Copy after review", + ], + recentWorkflows: [ + { title: "Acute confusion comparison", date: "Today, 8:40 AM" }, + { title: "Chest pain differentials", date: "May 12, 2025" }, + { title: "Headache red flags", date: "May 9, 2025" }, + ], + }, + { + id: "favourites", + area: "personal", + sourceBacked: false, + title: "Favourites", + description: "Return to saved clinical work and sources.", + detail: "Resume saved answers, pinned sources, and repeated workflows.", + href: "/favourites", + external: false, + icon: Star, + category: "recent", + workflow: "Saved", + lastUsed: "Today, 8:45 AM", + status: "recent", + relatedIds: ["clinical-kb-search", "services", "documents"], + quickActions: ["Resume saved work", "Open pinned sources", "Review recent workflows", "Manage pins"], + recentWorkflows: [ + { title: "Saved: Lithium monitoring", date: "Today, 8:45 AM" }, + { title: "Pinned: Safety plan", date: "May 12, 2025" }, + { title: "Recent: Clozapine protocol", date: "May 10, 2025" }, + ], + }, ]; const seedPinnedIds = ["clinical-kb-search", "services", "medication-prescribing", "documents"]; @@ -268,6 +342,32 @@ function StatusPill({ status }: { status: LauncherStatus }) { ); } +function StatsStrip() { + const stats = [ + { label: "Tools", value: launcherApps.length, icon: Grid2X2 }, + { label: "Review due", value: launcherApps.filter((app) => app.status === "review_due").length, icon: Clock3 }, + { label: "Recent", value: launcherApps.filter((app) => app.status === "recent").length, icon: Star }, + ]; + return ( +
+ {stats.map(({ label, value, icon: Icon }) => ( +
+ + + + + {value} + {label} + +
+ ))} +
+ ); +} + function AppIcon({ app, compact = false }: { app: LauncherApp; compact?: boolean }) { const Icon = app.icon; return ( @@ -315,9 +415,10 @@ function HeaderFilter({ onFilterChange: (filter: (typeof filterOptions)[number]["id"]) => void; }) { return ( -
+
{filterOptions.map((option) => { const active = option.id === activeFilter; + const Icon = option.icon; return ( ); @@ -435,7 +537,12 @@ function ApplicationRow({ + ))} +
+ +

Overview

{app.detail}

@@ -680,17 +819,21 @@ export function ApplicationsLauncherWorkspace({ return launcherApps.filter((app) => { const matchesFilter = activeFilter === "all" || - (activeFilter === "recent" - ? app.status === "recent" || app.category === "recent" - : app.category === activeFilter); + (activeFilter === "pinned" + ? pinnedIds.includes(app.id) + : activeFilter === "review_due" + ? app.status === "review_due" + : activeFilter === "source_backed" + ? app.sourceBacked + : true); const matchesQuery = !normalizedQuery || - [app.title, app.description, app.workflow, app.detail].some((value) => + [app.title, app.description, app.workflow, app.detail, areaLabels[app.area]].some((value) => value.toLowerCase().includes(normalizedQuery), ); return matchesFilter && matchesQuery; }); - }, [activeFilter, normalizedQuery]); + }, [activeFilter, normalizedQuery, pinnedIds]); function togglePin(id: string) { setPinnedIds((current) => (current.includes(id) ? current.filter((item) => item !== id) : [id, ...current])); @@ -733,6 +876,8 @@ export function ApplicationsLauncherWorkspace({ ) : null}
+ +
{normalizedQuery ? ( @@ -758,7 +903,8 @@ export function ApplicationsLauncherWorkspace({

{copy.description}

-
+
+
diff --git a/tests/ui-tools.spec.ts b/tests/ui-tools.spec.ts index 273b33c497..d70580acfa 100644 --- a/tests/ui-tools.spec.ts +++ b/tests/ui-tools.spec.ts @@ -81,7 +81,7 @@ test.describe("Clinical KB applications launcher", () => { await expect(page.locator('a[aria-label="Launch Documents"]').first()).toHaveAttribute("href", "/?mode=documents"); await expect(page.locator('a[aria-label="Launch Services"]').first()).toHaveAttribute("href", "/services"); await expect(page.locator('a[aria-label="Launch Forms"]').first()).toHaveAttribute("href", "/forms"); - await expect(page.locator('a[aria-label="Launch Favourites"]').first()).toHaveCount(0); + await expect(page.locator('a[aria-label="Launch Favourites"]').first()).toHaveAttribute("href", "/favourites"); await expect(page.locator('a[aria-label="Launch Clinical KB Search"]').first()).toHaveAttribute( "href", "/?mode=answer", @@ -98,7 +98,7 @@ test.describe("Clinical KB applications launcher", () => { await expect(page.getByTestId("application-row-medication-prescribing")).toBeVisible(); await expect(page.getByTestId("application-row-documents")).toBeHidden(); - await expect(page.getByText("Showing 1 to 1 of 5 applications")).toBeVisible(); + await expect(page.getByText("Showing 1 to 1 of 7 applications")).toBeVisible(); await expectNoPageHorizontalOverflow(page); }); @@ -116,7 +116,7 @@ test.describe("Clinical KB applications launcher", () => { await expect(toolsHub.getByRole("heading", { name: "All tools" })).toBeVisible(); await expect(toolsHub.getByTestId("application-row-medication-prescribing")).toBeVisible(); await expect(toolsHub.getByTestId("application-row-documents")).toBeHidden(); - await expect(toolsHub.getByText("Showing 1 to 1 of 5 tools")).toBeVisible(); + await expect(toolsHub.getByText("Showing 1 to 1 of 7 tools")).toBeVisible(); await expect(toolsHub.getByTestId("selected-application-panel")).toContainText("Selected tool"); await expect(toolsHub.getByTestId("tool-mode-result-medications")).toHaveCount(0);