From 7c03929bd695d2c9f0544f301f5f278d096bbadf Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sun, 2 Aug 2026 11:33:41 +0000 Subject: [PATCH 1/2] Add three Clinical KB guide wireframe mockups Design-scratch phone and desktop Sheet explorations for Tip Atlas, Help Hub, and Walkthrough, grounded in live GuideDialog content and tokens, with linked entry/detail/demo/skip states. Co-authored-by: BigSimmo --- .../guide-wireframes/help-hub/page.tsx | 12 + src/app/mockups/guide-wireframes/page.tsx | 13 + .../guide-wireframes/tip-atlas/page.tsx | 12 + .../guide-wireframes/walkthrough/page.tsx | 12 + src/components/guide-wireframe-mockups.tsx | 1451 +++++++++++++++++ 5 files changed, 1500 insertions(+) create mode 100644 src/app/mockups/guide-wireframes/help-hub/page.tsx create mode 100644 src/app/mockups/guide-wireframes/page.tsx create mode 100644 src/app/mockups/guide-wireframes/tip-atlas/page.tsx create mode 100644 src/app/mockups/guide-wireframes/walkthrough/page.tsx create mode 100644 src/components/guide-wireframe-mockups.tsx diff --git a/src/app/mockups/guide-wireframes/help-hub/page.tsx b/src/app/mockups/guide-wireframes/help-hub/page.tsx new file mode 100644 index 0000000000..5f6a316d93 --- /dev/null +++ b/src/app/mockups/guide-wireframes/help-hub/page.tsx @@ -0,0 +1,12 @@ +import type { Metadata } from "next"; + +import { GuideHelpHubMockup } from "@/components/guide-wireframe-mockups"; + +export const metadata: Metadata = { + title: "Guide Help Hub Wireframe - Clinical KB", + description: "Option B wireframe: fullscreen TOC help hub with nested citation demo sheet.", +}; + +export default function GuideHelpHubMockupRoute() { + return ; +} diff --git a/src/app/mockups/guide-wireframes/page.tsx b/src/app/mockups/guide-wireframes/page.tsx new file mode 100644 index 0000000000..c5c9247e9b --- /dev/null +++ b/src/app/mockups/guide-wireframes/page.tsx @@ -0,0 +1,13 @@ +import type { Metadata } from "next"; + +import { GuideWireframesIndex } from "@/components/guide-wireframe-mockups"; + +export const metadata: Metadata = { + title: "Guide Wireframes - Clinical KB", + description: + "Three design-scratch wireframe options for the Clinical KB in-app guide, with phone and desktop Sheet states.", +}; + +export default function GuideWireframesIndexRoute() { + return ; +} diff --git a/src/app/mockups/guide-wireframes/tip-atlas/page.tsx b/src/app/mockups/guide-wireframes/tip-atlas/page.tsx new file mode 100644 index 0000000000..a75f8774df --- /dev/null +++ b/src/app/mockups/guide-wireframes/tip-atlas/page.tsx @@ -0,0 +1,12 @@ +import type { Metadata } from "next"; + +import { GuideTipAtlasMockup } from "@/components/guide-wireframe-mockups"; + +export const metadata: Metadata = { + title: "Guide Tip Atlas Wireframe - Clinical KB", + description: "Option A wireframe: interactive tip-atlas guide with annotated deep-dive sheets.", +}; + +export default function GuideTipAtlasMockupRoute() { + return ; +} diff --git a/src/app/mockups/guide-wireframes/walkthrough/page.tsx b/src/app/mockups/guide-wireframes/walkthrough/page.tsx new file mode 100644 index 0000000000..2b7c964012 --- /dev/null +++ b/src/app/mockups/guide-wireframes/walkthrough/page.tsx @@ -0,0 +1,12 @@ +import type { Metadata } from "next"; + +import { GuideWalkthroughMockup } from "@/components/guide-wireframe-mockups"; + +export const metadata: Metadata = { + title: "Guide Walkthrough Wireframe - Clinical KB", + description: "Option C wireframe: first-run walkthrough sheet with skip confirm and done state.", +}; + +export default function GuideWalkthroughMockupRoute() { + return ; +} diff --git a/src/components/guide-wireframe-mockups.tsx b/src/components/guide-wireframe-mockups.tsx new file mode 100644 index 0000000000..d810c02451 --- /dev/null +++ b/src/components/guide-wireframe-mockups.tsx @@ -0,0 +1,1451 @@ +"use client"; + +import Link from "next/link"; +import { + ArrowLeft, + ArrowRight, + BookOpen, + Check, + ChevronRight, + Copy, + FileSearch, + Filter, + Layers, + Menu, + Quote, + Search, + Settings, + ShieldCheck, + Sparkles, + UploadCloud, + X, + type LucideIcon, +} from "lucide-react"; +import { useId, useState, type ReactNode } from "react"; + +import { cn, textMuted } from "@/components/ui-primitives"; + +/* -------------------------------------------------------------------------- */ +/* Content — grounded in live GuideDialog copy, expanded for wireframes */ +/* -------------------------------------------------------------------------- */ + +export const guideDirections = [ + { + slug: "tip-atlas", + letter: "A", + name: "Tip Atlas", + tagline: "Tappable tip cards that open annotated deep-dives.", + href: "/mockups/guide-wireframes/tip-atlas", + }, + { + slug: "help-hub", + letter: "B", + name: "Help Hub", + tagline: "Fullscreen TOC help with feature demos beside each topic.", + href: "/mockups/guide-wireframes/help-hub", + }, + { + slug: "walkthrough", + letter: "C", + name: "Walkthrough", + tagline: "First-run step sheet with progress and a sticky footer.", + href: "/mockups/guide-wireframes/walkthrough", + }, +] as const; + +export type GuideDirectionSlug = (typeof guideDirections)[number]["slug"]; + +type TopicId = "ask" | "citations" | "scope" | "evidence" | "upload" | "copy"; + +const topics: Array<{ + id: TopicId; + title: string; + body: string; + detail: string; + icon: LucideIcon; + productCue: string; +}> = [ + { + id: "ask", + title: "Ask and verify", + body: "Ask a focused guideline question, then verify linked citations and source passages before use.", + detail: + "Keep questions specific (drug + indication + setting). Treat the generated answer as a pointer into sources — not a final clinical note.", + icon: Search, + productCue: "Answer composer → cited response → open Top source", + }, + { + id: "citations", + title: "Top source and citations", + body: "Use Top source, citation chips, and source cards to open the relevant document page and check the retrieved evidence.", + detail: + "Citation chips jump to the retrieved passage. Source cards expose publisher, page, and governance status so you can judge currency before acting.", + icon: BookOpen, + productCue: "Top source chip · citation chips · source cards", + }, + { + id: "scope", + title: "Document scope", + body: "Use document scope controls when a question should search only selected guidelines rather than every indexed source.", + detail: + "Scope is for bounded questions (one protocol set, one service). Leave scope open for exploratory guideline search across the library.", + icon: Filter, + productCue: "Scope control → selected guidelines → ask again", + }, + { + id: "evidence", + title: "Quotes, images, sources", + body: "Bottom nav jumps to quotes, diagrams, and source passages. Empty sections had no citations.", + detail: + "Empty evidence tabs mean nothing was retrieved for that modality — not a failed load. Prefer the populated tab before widening the question.", + icon: Quote, + productCue: "Phone bottom nav: Quotes · Images · Sources", + }, + { + id: "upload", + title: "Upload and indexing", + body: "Real uploads require Supabase, OpenAI setup, the database schema, and the worker. Demo mode is synthetic only.", + detail: + "Demo mode never writes to your live corpus. Live indexing runs through the worker (OCR + captions + embeddings) before a document becomes searchable.", + icon: UploadCloud, + productCue: "Documents drawer → upload → indexing job status", + }, + { + id: "copy", + title: "Copying text", + body: "Copied drafts are not final clinical notes. Keep the provenance footer and verify source material before using copied text.", + detail: + "The provenance footer preserves what was cited. Strip it only after you have verified the primary PDF passage yourself.", + icon: Copy, + productCue: "Copy answer → provenance footer retained", + }, +]; + +const walkthroughSteps = [ + { + id: "ask", + title: "Ask a focused question", + body: "Name the drug, indication, and care setting. Vague prompts return broader, harder-to-verify answers.", + cue: "Type in the Answer composer", + }, + { + id: "verify", + title: "Verify before you trust", + body: "Open Top source and at least one citation chip. Confirm the passage matches the claim.", + cue: "Tap Top source · open a citation", + }, + { + id: "scope", + title: "Narrow with scope when needed", + body: "If the question belongs to one protocol set, limit document scope first, then re-ask.", + cue: "Open document scope controls", + }, + { + id: "evidence", + title: "Use evidence tabs", + body: "Quotes, images, and sources jump to retrieved modalities. Empty means none cited.", + cue: "Phone bottom nav evidence jumps", + }, + { + id: "copy", + title: "Copy with provenance", + body: "Copied text stays a draft. Keep the provenance footer until the PDF is checked.", + cue: "Copy control + footer", + }, +] as const; + +const focusRing = + "focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[color:var(--focus)]"; + +/* -------------------------------------------------------------------------- */ +/* Shared chrome */ +/* -------------------------------------------------------------------------- */ + +function DirectionSwitcher({ current }: { current?: GuideDirectionSlug | "index" }) { + return ( + + ); +} + +function MockupShell({ + current, + letter, + name, + headline, + intro, + children, +}: { + current: GuideDirectionSlug | "index"; + letter: string; + name: string; + headline: string; + intro: string; + children: ReactNode; +}) { + return ( +
+
+
+

+ Clinical KB guide · Option {letter} · {name} +

+

+ {headline} +

+

{intro}

+

+ Design scratch only — grounded in live{" "} + GuideDialog +{" "} + Sheet patterns. Not production. +

+
+ + {children} +
+
+ ); +} + +function DevicePair({ + title, + body, + desktop, + phone, +}: { + title: string; + body: string; + desktop: ReactNode; + phone: ReactNode; +}) { + return ( +
+
+
+

{title}

+

{body}

+
+ + Desktop + phone + +
+
+ {desktop} + {phone} +
+
+ ); +} + +function DesktopFrame({ children }: { children: ReactNode }) { + return ( +
+
+ + + + + Desktop · centered Sheet + +
+
+ {children} +
+
+ ); +} + +function PhoneFrame({ children }: { children: ReactNode }) { + return ( +
+
+
+ 19:16 + + +
+ {children} +
+
+ ); +} + +function AppChrome({ device, dimmed = false }: { device: "desktop" | "phone"; dimmed?: boolean }) { + return ( +
+ +
+ ); +} + +function Scrim({ onClick }: { onClick?: () => void }) { + return ( + + + ); +} + +function TopicIcon({ icon: Icon }: { icon: LucideIcon }) { + return ( + + + ); +} + +function NoteGrid({ items }: { items: Array<{ title: string; body: string }> }) { + return ( +
+ {items.map((item) => ( +
+

{item.title}

+

{item.body}

+
+ ))} +
+ ); +} + +/* -------------------------------------------------------------------------- */ +/* Index */ +/* -------------------------------------------------------------------------- */ + +export function GuideWireframesIndex() { + return ( + +
+

Current product baseline

+

+ Responsive Sheet: bottom sheet on phone + (drag grip, safe-area), centered dialog from sm:. Six static tip cards, no links. Entry: Settings + Help & About, plus a dashboard Guide trigger when drawers are open. +

+
    + {topics.map((topic) => ( +
  • + + + {topic.title} + {topic.body} + +
  • + ))} +
+
+ + + +
+

Open a variation

+

+ Each option is interactive inside the device frames — tap cards, TOC rows, and walkthrough controls. +

+
+ {guideDirections.map((direction) => ( + + + Option {direction.letter} + + {direction.name} + {direction.tagline} + + Open wireframe + + + ))} +
+
+
+ ); +} + +/* -------------------------------------------------------------------------- */ +/* Option A — Tip Atlas */ +/* -------------------------------------------------------------------------- */ + +type AtlasLayer = "closed" | "guide" | "topic" | "entry"; + +export function GuideTipAtlasMockup() { + const [desktopLayer, setDesktopLayer] = useState("guide"); + const [phoneLayer, setPhoneLayer] = useState("guide"); + const [desktopTopic, setDesktopTopic] = useState("ask"); + const [phoneTopic, setPhoneTopic] = useState("ask"); + + return ( + +
+ + +
+ + setDesktopLayer("guide")} + onCloseGuide={() => setDesktopLayer("entry")} + onOpenTopic={(id) => { + setDesktopTopic(id); + setDesktopLayer("topic"); + }} + onCloseTopic={() => setDesktopLayer("guide")} + /> + } + phone={ + setPhoneLayer("guide")} + onCloseGuide={() => setPhoneLayer("entry")} + onOpenTopic={(id) => { + setPhoneTopic(id); + setPhoneLayer("topic"); + }} + onCloseTopic={() => setPhoneLayer("guide")} + /> + } + /> + + +
+ ); +} + +function FrameControl({ + label, + value, + onChange, + options, +}: { + label: string; + value: T; + onChange: (next: T) => void; + options: Array<{ id: T; label: string }>; +}) { + return ( +
+ + {label} + + {options.map((option) => { + const active = option.id === value; + return ( + + ); + })} +
+ ); +} + +function AtlasDevice({ + device, + layer, + topicId, + onOpenGuide, + onCloseGuide, + onOpenTopic, + onCloseTopic, +}: { + device: "desktop" | "phone"; + layer: AtlasLayer; + topicId: TopicId; + onOpenGuide: () => void; + onCloseGuide: () => void; + onOpenTopic: (id: TopicId) => void; + onCloseTopic: () => void; +}) { + const topic = topics.find((item) => item.id === topicId) ?? topics[0]; + const showOverlay = layer === "guide" || layer === "topic" || layer === "entry"; + + return ( +
+ +
+
+

Answer canvas (dimmed)

+

+ Guide overlays sit above the composer — no second search bar. +

+
+
+ + {showOverlay ? : null} + + {layer === "entry" ? ( +
+ {device === "phone" ? ( +
+ +
+ ) : null} + +
+

+ Help & About +

+
+

Clinical Knowledge Base

+

+ Source-linked clinical reference. Verify primary guidance before acting. +

+ +
+
+
+ ) : null} + + {layer === "guide" || layer === "topic" ? ( +
+ {device === "phone" ? ( +
+ +
+ ) : null} + +
+
+ {topics.map((item) => ( + + ))} +
+
+
+ ) : null} + + {layer === "topic" ? ( +
+ {device === "phone" ? ( +
+ +
+ ) : null} + +
+ ) : null} +
+ ); +} + +function ProductCuePreview({ topicId }: { topicId: TopicId }) { + if (topicId === "citations") { + return ( +
+

Top source

+ +
+ + [1] + + + [2] + +
+
+ ); + } + if (topicId === "scope") { + return ( +
+

Document scope

+
+ + 3 selected + + + All documents + +
+
+ ); + } + if (topicId === "evidence") { + return ( +
+
+ {["Quotes", "Images", "Sources"].map((label) => ( + + {label} + + ))} +
+
+ ); + } + if (topicId === "copy") { + return ( +
+

Draft answer text…

+

+ Provenance: sources [1][2] · verify primary PDF before clinical use +

+
+ ); + } + return ( +
+
+
+
+ ); +} + +/* -------------------------------------------------------------------------- */ +/* Option B — Help Hub */ +/* -------------------------------------------------------------------------- */ + +type HubSection = TopicId | "safety" | "demo"; + +const hubNav: Array<{ id: HubSection; label: string; icon: LucideIcon }> = [ + { id: "ask", label: "Asking well", icon: Search }, + { id: "citations", label: "Sources", icon: BookOpen }, + { id: "scope", label: "Scope", icon: Filter }, + { id: "evidence", label: "Evidence", icon: Layers }, + { id: "upload", label: "Upload", icon: UploadCloud }, + { id: "copy", label: "Copy safety", icon: Copy }, + { id: "safety", label: "Safety", icon: ShieldCheck }, + { id: "demo", label: "Demo vs live", icon: Sparkles }, +]; + +export function GuideHelpHubMockup() { + const [desktopSection, setDesktopSection] = useState("ask"); + const [phoneSection, setPhoneSection] = useState("ask"); + const [desktopDemoOpen, setDesktopDemoOpen] = useState(false); + const [phoneDemoOpen, setPhoneDemoOpen] = useState(false); + + return ( + + setDesktopDemoOpen(true)} + onDemoClose={() => setDesktopDemoOpen(false)} + /> + } + phone={ + setPhoneDemoOpen(true)} + onDemoClose={() => setPhoneDemoOpen(false)} + /> + } + /> + + + + ); +} + +function HelpHubDevice({ + device, + section, + onSection, + demoOpen, + onDemoOpen, + onDemoClose, +}: { + device: "desktop" | "phone"; + section: HubSection; + onSection: (id: HubSection) => void; + demoOpen: boolean; + onDemoOpen: () => void; + onDemoClose: () => void; +}) { + return ( +
+ + +
+ undefined} + closeLabel="Close help" + leading={ + device === "phone" ? ( + + + ) : null + } + /> +
+ {device === "desktop" ? ( + + ) : ( +
+
+ {hubNav.map((item) => { + const active = item.id === section; + return ( + + ); + })} +
+
+ )} + +
+ +
+
+
+ + {demoOpen ? ( + <> + +
+ + + ) : null} + + ); +} + +function HubChapter({ section, onDemoOpen }: { section: HubSection; onDemoOpen: () => void }) { + if (section === "safety") { + return ( +
+

Safety posture

+

+ This is a clinical reference prototype, not validated decision support. Answers must remain verifiable against + linked sources. When generation fails quality gates, the product degrades to source-only citations — that is + expected, not an error. +

+
+ Never treat a fluent answer as a substitute for the primary guideline PDF. +
+
+ ); + } + if (section === "demo") { + return ( +
+

Demo vs live

+

+ Demo mode uses a synthetic corpus. Live mode needs Supabase, OpenAI, the schema, and the ingestion worker. + Uploads in demo never reach your private library. +

+
+ ); + } + const topic = topics.find((item) => item.id === section); + if (!topic) return null; + return ( +
+
+ +
+

{topic.title}

+

{topic.detail}

+
+
+
+ +
+ {topic.id === "citations" ? ( + + ) : null} +
+ ); +} + +/* -------------------------------------------------------------------------- */ +/* Option C — Walkthrough */ +/* -------------------------------------------------------------------------- */ + +type WalkLayer = "tour" | "skip" | "done"; + +export function GuideWalkthroughMockup() { + const [desktopStep, setDesktopStep] = useState(0); + const [phoneStep, setPhoneStep] = useState(0); + const [desktopLayer, setDesktopLayer] = useState("tour"); + const [phoneLayer, setPhoneLayer] = useState("tour"); + const labelId = useId(); + + return ( + + + } + phone={ + + } + /> + + + + ); +} + +function WalkDevice({ + device, + step, + layer, + labelId, + onStep, + onLayer, +}: { + device: "desktop" | "phone"; + step: number; + layer: WalkLayer; + labelId: string; + onStep: (step: number) => void; + onLayer: (layer: WalkLayer) => void; +}) { + const current = walkthroughSteps[Math.min(step, walkthroughSteps.length - 1)]; + const progress = ((step + 1) / walkthroughSteps.length) * 100; + const isLast = step >= walkthroughSteps.length - 1; + + return ( +
+ +
+
+ First-run Answer home behind the tour +
+
+ + +
+ {device === "phone" ? ( +
+ +
+ ) : null} + + {layer === "done" ? ( + <> + onLayer("tour")} + /> +
+
+
+

+ Ask focused questions, open Top source, and keep provenance on copies. Re-open the guide anytime from + Settings → Guide & help. +

+ + Browse Tip Atlas + + + Open Help Hub + + +
+ + ) : ( + <> +
+
+
+

+ Step {step + 1} of {walkthroughSteps.length} +

+

+ {current.title} +

+
+ +
+
+
+
+
+ +
+

{current.body}

+
+

+ Look for +

+

{current.cue}

+
+ +
+ +
+ + +
+ + )} +
+ + {layer === "skip" ? ( + <> + + +
+
+ + + ) : null} + + ); +} From 5d419dfe808c775c6aee07cfd6d2846db54e6974 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sun, 2 Aug 2026 11:47:36 +0000 Subject: [PATCH 2/2] Suppress shared mockup chrome on guide wireframes Keep device-frame Sheet studies free of a second composer, and document the new mockup routes in the generated site map. Co-authored-by: BigSimmo --- docs/site-map.md | 4 ++++ src/app/mockups/mockups-layout-client.tsx | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/docs/site-map.md b/docs/site-map.md index 56d819af81..e5d0ac73d8 100644 --- a/docs/site-map.md +++ b/docs/site-map.md @@ -1013,6 +1013,10 @@ This file is generated by `npm run docs:update` (or `npm run sitemap:update` dir - `/mockups/favourites-review-console` - Route discovered from app directory Source: `src/app/mockups/favourites-review-console/page.tsx`. - `/mockups/favourites-set-board` - Route discovered from app directory Source: `src/app/mockups/favourites-set-board/page.tsx`. - `/mockups/favourites-set-navigator` - Route discovered from app directory Source: `src/app/mockups/favourites-set-navigator/page.tsx`. +- `/mockups/guide-wireframes` - Route discovered from app directory Source: `src/app/mockups/guide-wireframes/page.tsx`. +- `/mockups/guide-wireframes/help-hub` - Route discovered from app directory Source: `src/app/mockups/guide-wireframes/help-hub/page.tsx`. +- `/mockups/guide-wireframes/tip-atlas` - Route discovered from app directory Source: `src/app/mockups/guide-wireframes/tip-atlas/page.tsx`. +- `/mockups/guide-wireframes/walkthrough` - Route discovered from app directory Source: `src/app/mockups/guide-wireframes/walkthrough/page.tsx`. - `/mockups/medication-prescribing` - Route discovered from app directory Source: `src/app/mockups/medication-prescribing/page.tsx`. - `/mockups/mode-dropdown` - Route discovered from app directory Source: `src/app/mockups/mode-dropdown/page.tsx`. - `/mockups/phone-inpage-navigation` - Route discovered from app directory Source: `src/app/mockups/phone-inpage-navigation/page.tsx`. diff --git a/src/app/mockups/mockups-layout-client.tsx b/src/app/mockups/mockups-layout-client.tsx index 5d7065da62..f8195578d4 100644 --- a/src/app/mockups/mockups-layout-client.tsx +++ b/src/app/mockups/mockups-layout-client.tsx @@ -39,6 +39,9 @@ export function MockupsLayoutClient({ children }: { children: ReactNode }) { // These studies render their own top bar and composer inside each device // frame. Suppress shared chrome so it cannot be mistaken for the concept. const isTherapyNavigationMockup = pathname.startsWith("/mockups/therapy-navigation-"); + // Guide wireframes paint Answer chrome + Sheet stacks inside device frames; + // the shared composer would read as a second, real search bar over the study. + const isGuideWireframesMockup = pathname.startsWith("/mockups/guide-wireframes"); // The calculators search page owns its own search input (top on desktop, docked // at the bottom on phones), so the shared universal composer is suppressed here // to avoid a second, floating search bar. @@ -74,6 +77,7 @@ export function MockupsLayoutClient({ children }: { children: ReactNode }) { !isPhoneInPageNavigationMockup && !isSearchBandDirectionsMockup && !isTherapyNavigationMockup && + !isGuideWireframesMockup && !isWarningConsolidationMockup && !isWarningLineMockup && !isAnswerHomeProposalMockup @@ -85,6 +89,7 @@ export function MockupsLayoutClient({ children }: { children: ReactNode }) { !isSearchBandDirectionsMockup && !isPhoneInPageNavigationMockup && !isTherapyNavigationMockup && + !isGuideWireframesMockup && !isWarningConsolidationMockup && !isWarningLineMockup && !isAnswerHomeProposalMockup