diff --git a/src/app/specifiers/[slug]/page.tsx b/src/app/specifiers/[slug]/page.tsx index 646a7314a..4fdb68283 100644 --- a/src/app/specifiers/[slug]/page.tsx +++ b/src/app/specifiers/[slug]/page.tsx @@ -1,25 +1,29 @@ -import { notFound } from "next/navigation"; import type { Metadata } from "next"; +import { notFound } from "next/navigation"; import { SpecifierRecordPage } from "@/components/specifiers/specifier-record-page"; import { findSpecifier, specifierRecords } from "@/lib/specifiers"; +type SpecifierDetailRouteProps = { + params: Promise<{ slug: string }>; +}; + export function generateStaticParams() { return specifierRecords.map((record) => ({ slug: record.slug })); } -export async function generateMetadata({ params }: { params: Promise<{ slug: string }> }): Promise { +export async function generateMetadata({ params }: SpecifierDetailRouteProps): Promise { const { slug } = await params; const record = findSpecifier(slug); - if (!record) return {}; + if (!record) return { title: "Specifier not found - Clinical KB" }; return { - title: record.name, + title: `${record.name} - Psychiatric specifier - Clinical KB`, description: record.summary, }; } -export default async function SpecifierDetailRoute({ params }: { params: Promise<{ slug: string }> }) { +export default async function SpecifierDetailRoute({ params }: SpecifierDetailRouteProps) { const { slug } = await params; const record = findSpecifier(slug); if (!record) notFound(); diff --git a/src/app/specifiers/compare/page.tsx b/src/app/specifiers/compare/page.tsx index 9fcf70616..8ed8ce519 100644 --- a/src/app/specifiers/compare/page.tsx +++ b/src/app/specifiers/compare/page.tsx @@ -15,17 +15,14 @@ export async function generateMetadata({ searchParams }: CompareRouteProps): Pro const params = searchParams ? await searchParams : {}; const left = findSpecifier(first(params.a) ?? ""); const right = findSpecifier(first(params.b) ?? ""); - - if (left && right) { - return { - title: `Compare ${left.shortName} and ${right.shortName}`, - description: `Side-by-side comparison of ${left.name} and ${right.name} specifiers.`, - }; - } + const names = [left?.shortName, right?.shortName].filter(Boolean); return { - title: "Compare specifiers", - description: "Compare psychiatric specifiers side by side to understand the deciding features.", + title: `${names.length ? `Compare ${names.join(" and ")}` : "Compare psychiatric specifiers"} - Clinical KB`, + description: + left && right + ? `Compare ${left.name} and ${right.name} side by side, including clinical signals, timing, and cautions.` + : "Compare psychiatric specifiers side by side, including clinical signals, timing, and cautions.", }; } diff --git a/src/app/specifiers/map/page.tsx b/src/app/specifiers/map/page.tsx index 50dcf3057..cb14ce43a 100644 --- a/src/app/specifiers/map/page.tsx +++ b/src/app/specifiers/map/page.tsx @@ -2,18 +2,18 @@ import type { Metadata } from "next"; import { SpecifierMapPage } from "@/components/specifiers/specifier-map-page"; -type MapRouteProps = { - searchParams?: Promise<{ selected?: string | string[] }>; -}; - export const metadata: Metadata = { - title: "Specifier map", + title: "Psychiatric specifier map - Clinical KB", description: "Browse psychiatric specifiers by diagnostic architecture: episode features, course and onset, severity and remission.", }; +type MapRouteProps = { + searchParams?: Promise<{ selected?: string | string[] }>; +}; + export default async function SpecifierMapRoute({ searchParams }: MapRouteProps) { const params = searchParams ? await searchParams : {}; const selected = Array.isArray(params.selected) ? params.selected[0] : params.selected; - return ; + return ; } diff --git a/src/components/clinical-dashboard/universal-search-command-surface.tsx b/src/components/clinical-dashboard/universal-search-command-surface.tsx index 34a5a1517..b52b97a04 100644 --- a/src/components/clinical-dashboard/universal-search-command-surface.tsx +++ b/src/components/clinical-dashboard/universal-search-command-surface.tsx @@ -726,16 +726,16 @@ export function UniversalSearchCommandSurface({ ? "favourites" : modeId === "differentials" ? "differentials" - : modeId === "dsm" - ? "dsm" - : modeId === "answer" - ? "answer" - : modeId === "tools" - ? "tools" - : modeId === "specifiers" - ? "specifiers" - : modeId === "formulation" - ? "formulation" + : modeId === "specifiers" + ? "specifiers" + : modeId === "formulation" + ? "formulation" + : modeId === "dsm" + ? "dsm" + : modeId === "answer" + ? "answer" + : modeId === "tools" + ? "tools" : null; if (actionSetId) { diff --git a/src/components/specifiers/specifier-builder-page.tsx b/src/components/specifiers/specifier-builder-page.tsx index d73b5675e..56c437fbf 100644 --- a/src/components/specifiers/specifier-builder-page.tsx +++ b/src/components/specifiers/specifier-builder-page.tsx @@ -43,26 +43,17 @@ function wordingSegment(record: SpecifierRecord) { } export function SpecifierBuilderPage({ initialSpecifiers = [] }: { initialSpecifiers?: string[] }) { - const normalizedInitial = normalizeSpecifierSelection(initialSpecifiers); - - // Determine the best initial diagnosis from the deep-linked specifiers - const inferredDiagnosis = - normalizedInitial.length > 0 - ? (diagnosisPresets.find((preset) => - normalizedInitial.every((slug) => { - const record = specifierRecords.find((candidate) => candidate.slug === slug); - return record ? specifierAppliesToBuilderDiagnosis(record, preset.id) : false; - }), - ) ?? diagnosisPresets[0]) - : diagnosisPresets[0]; - - const validInitial = normalizedInitial.filter((slug) => { - const record = specifierRecords.find((candidate) => candidate.slug === slug); - return record ? specifierAppliesToBuilderDiagnosis(record, inferredDiagnosis.id) : false; - }); - const [diagnosisId, setDiagnosisId] = useState(inferredDiagnosis.id); + const validInitial = normalizeSpecifierSelection(initialSpecifiers); + const initialDiagnosis = + diagnosisPresets.find((preset) => + validInitial.every((slug) => { + const record = specifierRecords.find((candidate) => candidate.slug === slug); + return record ? specifierAppliesToBuilderDiagnosis(record, preset.id) : false; + }), + ) ?? diagnosisPresets[0]; + const [diagnosisId, setDiagnosisId] = useState(initialDiagnosis.id); const [selected, setSelected] = useState(validInitial); - const diagnosis = diagnosisPresets.find((preset) => preset.id === diagnosisId) ?? inferredDiagnosis; + const diagnosis = diagnosisPresets.find((preset) => preset.id === diagnosisId) ?? initialDiagnosis; const selectedRecords = useMemo( () => selected diff --git a/src/components/specifiers/specifier-map-page.tsx b/src/components/specifiers/specifier-map-page.tsx index b01f95440..b302d12ad 100644 --- a/src/components/specifiers/specifier-map-page.tsx +++ b/src/components/specifiers/specifier-map-page.tsx @@ -17,26 +17,9 @@ import { cn, eyebrowText } from "@/components/ui-primitives"; import { findSpecifier, specifierFamilies, specifierRecords } from "@/lib/specifiers"; export function SpecifierMapPage({ initialSlug }: { initialSlug?: string }) { - // Derive valid initial slug from props - const validInitialSlug = findSpecifier(initialSlug ?? "")?.slug ?? specifierRecords[0].slug; - - // Track selected slug with derived initial state pattern (useState with function) - const [state, setState] = useState({ selectedSlug: validInitialSlug, lastInitialSlug: initialSlug }); - - // Derive state: if initialSlug prop changed, reset to new initial; otherwise keep current selection - const selectedSlug = state.lastInitialSlug !== initialSlug ? validInitialSlug : state.selectedSlug; - - // Update state if derived slug differs from stored state - if (selectedSlug !== state.selectedSlug || state.lastInitialSlug !== initialSlug) { - setState({ selectedSlug, lastInitialSlug: initialSlug }); - } - + const [selectedSlug, setSelectedSlug] = useState(findSpecifier(initialSlug ?? "")?.slug ?? specifierRecords[0].slug); const selected = findSpecifier(selectedSlug) ?? specifierRecords[0]; - const setSelectedSlug = (slug: string) => { - setState({ selectedSlug: slug, lastInitialSlug: initialSlug }); - }; - return (
diff --git a/src/lib/search-command-surface.ts b/src/lib/search-command-surface.ts index 0b4b81792..f387fa44c 100644 --- a/src/lib/search-command-surface.ts +++ b/src/lib/search-command-surface.ts @@ -130,14 +130,14 @@ const searchCommandSurfaceByMode: Partial { - await gotoApp(page, "/specifiers/builder?specifier=with-rapid-cycling"); +test("infers a compatible diagnosis for non-MDD builder deep links", async ({ page }) => { + await gotoApp(page, "/specifiers/builder?specifier=with-rapid-cycling&specifier=with-psychotic-features"); const rapidCycling = page.getByRole("checkbox", { name: /Rapid cycling/ }); - await expect(rapidCycling).toBeChecked(); await expect(page.getByRole("combobox", { name: "Diagnostic phrase" })).toHaveValue("bipolar-i-depressed"); + await expect(rapidCycling).toBeEnabled(); + await expect(rapidCycling).toBeChecked(); await expect( - page.getByText("Bipolar I disorder, current episode depressed, with rapid cycling", { exact: true }), + page.getByText( + "Bipolar I disorder, current episode depressed, severe with psychotic features, with rapid cycling", + { exact: true }, + ), ).toBeVisible(); -}); -test("labels search results using text-relevance language instead of clinical-fit language", async ({ page }) => { - await gotoApp(page, "/specifiers?q=racing+thoughts&run=1"); - - await expect(page.getByRole("heading", { name: /Matches for “racing thoughts”/ })).toBeVisible(); - await expect(page.getByText("Top match", { exact: true })).toBeVisible(); - await expect(page.getByText(/Results ranked by text relevance/i)).toBeVisible(); - await expect(page.getByText(/clinical fit/i)).toHaveCount(0); + await page.getByRole("combobox", { name: "Diagnostic phrase" }).selectOption("bipolar-i-manic"); + await expect(rapidCycling).toBeEnabled(); + await expect(rapidCycling).toBeChecked(); });