Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/design-system/COMPONENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -976,7 +976,7 @@ This generated snapshot is a local source-derived inventory. It does not assert
| `Breadcrumb` | layout | yes | yes | inherited-global-root | yes | no | 1 |
| `Button` | controls | yes | yes | inherited-global-root | yes | no | 13 |
| `Checkbox` | controls | yes | yes | no | yes | no | 0 |
| `Chip` | controls | yes | yes | inherited-global-root | yes | no | 4 |
| `Chip` | controls | yes | yes | inherited-global-root | yes | no | 5 |
| `Citation` | source | yes | yes | no | yes | no | 0 |
| `CitationList` | source | yes | yes | no | yes | no | 0 |
| `ConfirmDialog` | layout | yes | yes | no | yes | no | 0 |
Expand Down
2 changes: 2 additions & 0 deletions docs/design-system/adoption-manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -375,12 +375,14 @@
"directImportFiles": [
"src/components/applications-launcher-page.tsx",
"src/components/clinical-dashboard/favourites-command-library-page.tsx",
"src/components/therapy-compass/screens/pathways-screen.tsx",
"src/components/therapy-compass/ui.tsx",
"src/components/tools/tools-search-results-page.tsx"
],
"productImportFiles": [
"src/components/applications-launcher-page.tsx",
"src/components/clinical-dashboard/favourites-command-library-page.tsx",
"src/components/therapy-compass/screens/pathways-screen.tsx",
"src/components/therapy-compass/ui.tsx",
"src/components/tools/tools-search-results-page.tsx"
],
Expand Down
4 changes: 3 additions & 1 deletion src/components/mode-nav/registry-mode-nav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import {
BookOpenText,
BookMarked,
ClipboardCheck,
ClipboardList,
GitCompareArrows,
ListChecks,
Expand Down Expand Up @@ -36,7 +37,7 @@ export const registryModeNavDensityProfiles = {
formulation: "compact-four",
differentials: "balanced-four",
factsheets: "two-item",
"therapy-compass": "balanced-four",
"therapy-compass": "extended",
// Four destinations with medium labels, the same family as differentials:
// "Compare" and "Sources" are well inside the budget calibrated for
// "Presentations". `extended` was chosen when the mode had five destinations
Expand All @@ -53,6 +54,7 @@ export const registryModeNavDensityProfiles = {
*/
const iconByItemId: Record<RoutedModeSecondaryNavigationId, LucideIcon> = {
search: Search,
review: ClipboardCheck,
diagnoses: Stethoscope,
presentations: ClipboardList,
compare: GitCompareArrows,
Expand Down
33 changes: 28 additions & 5 deletions src/components/therapy-compass/screens/pathways-screen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { useMemo } from "react";
import { Check, ChevronRight, Copy, FileText, ListChecks, Scale, TriangleAlert, Waypoints } from "lucide-react";

import { cardSurface } from "@/components/card-recipes";
import { Chip } from "@/components/ui/chip";
import { PageHeader } from "@/components/ui/page-header";
import { cn, pageContainer } from "@/components/ui-primitives";
import { Button } from "@/components/ui/button";
Expand All @@ -18,6 +19,9 @@ export function PathwaysScreen() {
const b = useTcBindings();
const bySlug = useMemo(() => new Map(b.therapies.map((t) => [t.slug, t])), [b.therapies]);
const pathway = b.selectedPathway;
// Header counts. Summed here rather than in the header markup so the chips
// stay declarative and the totals survive a data shape that grows a step.
const linkedStepCount = useMemo(() => b.pathways.reduce((total, p) => total + p.steps.length, 0), [b.pathways]);
// Called before the early return below, because it is a hook.
const { copied, copy } = useClipboard();

Expand All @@ -41,14 +45,33 @@ export function PathwaysScreen() {

return (
<section data-screen-label="Pathways" className={pageContainer}>
{/* The Review queue lived here as the page's one action. It is a curation
surface — which records still need source review — not something a
clinician reads a pathway to reach, so the header no longer carries it;
`/therapy-compass/review` still serves it directly. What replaces it is
Comment thread
BigSimmo marked this conversation as resolved.
scale, not another control: the two counts say how much of the
catalogue this page covers, and the accent chip is the only colour a
phone gets here, since `PageHeader` hides its icon tile below `sm`.

The description loses "generated from imported therapy records"
because that provenance is already stated twice further down the same
screen — the note under the pathway list and the caution banner — and
on a 390px viewport it was the line that wrapped into the drawer
handle. */}
<PageHeader
className="mb-[22px]"
icon={Waypoints}
title="Clinical Pathways"
description="Problem-based workflows generated from imported therapy records."
actions={
<Button variant="secondary" icon={ListChecks} onClick={b.goReview}>
Review queue
</Button>
description="Step-by-step workflows for common clinical problems."
meta={
<>
<Chip appearance={{ kind: "information", tone: "accent" }} icon={Waypoints}>
{b.pathways.length} {b.pathways.length === 1 ? "pathway" : "pathways"}
</Chip>
<Chip appearance={{ kind: "information", tone: "inset" }} icon={ListChecks}>
{linkedStepCount} {linkedStepCount === 1 ? "linked step" : "linked steps"}
</Chip>
</>
}
/>

Expand Down
2 changes: 2 additions & 0 deletions src/lib/mode-secondary-navigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ export const modeSecondaryNavigationRegistry = {
{ id: "recommend", label: "Recommend", href: "/therapy-compass/recommend" },
{ id: "compare", label: "Compare", href: "/therapy-compass/compare" },
{ id: "pathways", label: "Pathways", href: "/therapy-compass/pathways" },
{ id: "review", label: "Review", href: "/therapy-compass/review" },
],
// Two genuinely distinct surfaces: the mode home and `/factsheets/search`, a
// separate component with filters, a view toggle and result rows.
Expand Down Expand Up @@ -191,6 +192,7 @@ export function activeModeSecondaryNavigationId(modeId: AppModeId, pathname: str
if (pathname === "/therapy-compass/recommend") return "recommend";
if (pathname === "/therapy-compass/compare") return "compare";
if (pathname === "/therapy-compass/pathways") return "pathways";
if (pathname === "/therapy-compass/review") return "review";
return null;
}
if (modeId === "dictionary") {
Expand Down
17 changes: 10 additions & 7 deletions tests/mode-nav-contract.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,12 +188,13 @@ describe("ModeNav item contract", () => {
expect(modeNavSource).not.toMatch(/onClick\?:/);
});

it("gives Therapy the four shared workspace destinations in declared order", () => {
it("gives Therapy the five shared workspace destinations in declared order", () => {
expect(modeSecondaryNavigationEntries("therapy-compass").map((entry) => entry.id)).toEqual([
"search",
"recommend",
"compare",
"pathways",
"review",
]);
});

Expand Down Expand Up @@ -301,10 +302,12 @@ describe("ModeNav density coverage", () => {
for (const modeId of MODE_NAV_ADOPTED_MODES) {
expect(covered.has(modeId), `${modeId} adopted the bar but the density spec never loads it`).toBe(true);
}
// Therapy now uses the shared registry and deliberately exposes only the
// four workspace destinations. Record-owned outputs require a selected
// therapy and therefore stay off the global mode bar.
expect(covered.get("therapy-compass")).toBe(4);
// Therapy uses the shared registry and exposes five workspace
// destinations: the four clinical ones plus Review, the curation queue that
// would otherwise have no inbound link once this PR takes it off the
// Pathways header. Record-owned outputs (briefs, patient sheets) still
// require a selected therapy and stay off the global mode bar.
expect(covered.get("therapy-compass")).toBe(5);
});

it("keeps each mode's declared destination count in step with the registry", () => {
Expand All @@ -323,8 +326,8 @@ describe("ModeNav density coverage", () => {
expect(MODE_NAV_DENSITY_PROFILES).toContain(profile);
expect(coveredProfiles.get(modeId), `${modeId} browser profile`).toBe(profile);
}
expect(coveredProfiles.get("therapy-compass")).toBe("balanced-four");
expect(registryModeNavSource).toContain('"therapy-compass": "balanced-four"');
expect(coveredProfiles.get("therapy-compass")).toBe("extended");
expect(registryModeNavSource).toContain('"therapy-compass": "extended"');
});
});

Expand Down
3 changes: 2 additions & 1 deletion tests/mode-secondary-navigation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const expectedLabels: Record<AppModeId, string[]> = {
prescribing: [],
tools: [],
calculators: [],
"therapy-compass": ["Search", "Recommend", "Compare", "Pathways"],
"therapy-compass": ["Search", "Recommend", "Compare", "Pathways", "Review"],
factsheets: ["Topics", "Search"],
dictionary: ["Terms", "Topics", "Compare", "Sources"],
};
Expand Down Expand Up @@ -302,6 +302,7 @@ describe("mode secondary navigation registry", () => {
expect(activeModeSecondaryNavigationId("therapy-compass", "/therapy-compass/recommend")).toBe("recommend");
expect(activeModeSecondaryNavigationId("therapy-compass", "/therapy-compass/compare")).toBe("compare");
expect(activeModeSecondaryNavigationId("therapy-compass", "/therapy-compass/pathways")).toBe("pathways");
expect(activeModeSecondaryNavigationId("therapy-compass", "/therapy-compass/review")).toBe("review");
expect(activeModeSecondaryNavigationId("therapy-compass", "/therapy-compass/cbt")).toBeNull();

// Dictionary's Search and Browse were one catalogue behind two routes and
Expand Down
2 changes: 1 addition & 1 deletion tests/therapy-compass-responsive-contract.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ describe("Therapy Compass responsive contract", () => {
// bar, which folds its overflow into a sheet rather than off the screen
// edge. Its own density and centring contract lives in mode-nav-contract.
expect(registryModeNavSource).toContain("ModeNav");
expect(registryModeNavSource).toContain('"therapy-compass": "balanced-four"');
expect(registryModeNavSource).toContain('"therapy-compass": "extended"');
expect(registryModeNavSource).not.toContain("overflow-x-auto");
expect(registryModeNavSource).not.toContain("w-fit");
expect(globalsSource).toContain("position: relative;");
Expand Down
3 changes: 2 additions & 1 deletion tests/ui-mode-nav-density.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ const gotoTherapySearch = (page: Page) => gotoTherapy(page);
* offline rather than leaving a mode uncovered.
*/
const MODES = [
{ modeId: "therapy-compass", route: "/therapy-compass/search?q=CBT&run=1", items: 4, profile: "balanced-four" },
{ modeId: "therapy-compass", route: "/therapy-compass/search?q=CBT&run=1", items: 5, profile: "extended" },
{ modeId: "dsm", route: "/dsm/compare", items: 2, profile: "two-item" },
{ modeId: "specifiers", route: "/specifiers/compare", items: 4, profile: "compact-four" },
{ modeId: "formulation", route: "/formulation/compare", items: 4, profile: "compact-four" },
Expand Down Expand Up @@ -284,6 +284,7 @@ test.describe("ModeNav density", () => {
["/therapy-compass/compare", "Compare"],
["/therapy-compass/recommend", "Recommend"],
["/therapy-compass/pathways", "Pathways"],
["/therapy-compass/review", "Review"],
] as const) {
await gotoTherapy(page, route);
await expect.poll(async () => (await readNav(page)).state, { timeout: 10_000 }).toBe("bar");
Expand Down
Loading