Skip to content

Full EHR View — Steer Document

Feature: Full Patient EHR Viewer (in-conversation overlay) Version: 1.0 Date: April 2026 Author: Srikanth Donthi (CPO/CTO) Status: Implemented — FullEHRDrawer.tsx live. Residual: restyle to Clerk modal pattern pending.


1. Problem Statement

The current EHR panel is a 300px slide-out panel with collapsible sections and a completeness ring. It works well for quick glances during conversation, but it's cramped for reviewing a full patient record — especially when the EHR has grown beyond basic demographics into comorbidities, diagnostic findings, medications, allergies, surgical history, and preferences. There's no way to see the complete, structured EHR without leaving the conversational UI.

What's missing: A "View Full Record" CTA on the EHR panel that opens an expanded, highly readable view of the entire patient health record — grouped logically, scannable, and dismissible without losing conversation context.


2. Design Decision: Overlay Drawer, Not Route Navigation

Decision: Full-width overlay drawer sliding in from the right (over the conversation), not a new route like /records/:id.

Rationale: - Patient must not leave the conversation. Navigating away loses conversational context, scroll position, and the mental model of "I'm talking to Curaway." - A full-screen modal or route change breaks the single-page conversation-first architecture established in Session 19–20. - A drawer overlay preserves the icon rail and allows one-click return to conversation. - The drawer pattern is consistent with how the existing EHR/Docs/Summary panels work — just wider.

Rejected alternatives: - New route (/records/:id): Breaks conversation context. User has to navigate back. Case state may need reload. - Full-screen modal: Too jarring. No access to icon rail. Feels like a different app. - Expand the existing 300px panel to full width: Conflicts with other panel interactions (docs, summary). The 300px panel should stay as the "quick glance" and the full view is a deeper drill-down.


3. UX Specification

3.1 Entry Point

A CTA button at the bottom of the existing EHR panel (EHRPanel.tsx):

  • Label: "View Full Record"
  • Style: Teal outline button, full width within the panel, 36px height, Montserrat 13px semi-bold
  • Icon: Expand/maximize icon (↗) left of text
  • Position: Pinned to the bottom of the EHR panel, above the "Last updated" timestamp
  • Only visible when EHR data exists (hide when EHR is still being constructed)

3.2 Full EHR Drawer

Dimensions: - Width: calc(100vw - 56px) — full viewport minus the icon rail (icon rail stays visible) - Height: 100vh - Entry animation: translateX(100%) → translateX(0), 300ms ease - Background: white (#FFFFFF) - Z-index: above all panels and conversation, below the icon rail - Scroll: vertical scroll on the content area, fixed header

Header (sticky, 64px): - Left: Back arrow (←) + "Patient Health Record" in 18px Montserrat semi-bold, teal-dark (#004D4D) - Center: Patient name + case number pill badge (teal-light bg) - Right: Completeness ring (48px, same as EHR panel) + "X% Complete" label + Close (×) button - Bottom border: 1px solid gray-200

Backdrop: Semi-transparent overlay (rgba(0,0,0,0.3)) over the conversation area. Click backdrop to close the drawer.

3.3 Content Layout

Two-column layout on desktop (>1024px): 65% main content + 35% sidebar. Single column on tablet/mobile.

Main Column (left, 65%)

Content max-width: 720px. Padding: 32px. Sections separated by 24px gap.

Each section follows a consistent pattern: - Section header: 14px Montserrat semi-bold uppercase, teal (#008B8B), with a left teal border (3px) - Section status indicator: Green dot (complete) / Amber dot (partial) / Gray dot (no data) - Content: 14px body text, line-height 1.6, color teal-dark (#004D4D) - Collapsible: All sections expandable/collapsible. Default: all expanded on first open, remember state per session.

Section order and grouping:

Group 1 — Identity & Context 1. Patient Demographics — Name, age/DOB, gender, nationality, location (city/country), languages spoken (pills), contact preference 2. Insurance & Coverage — Insurance status, provider name, policy details, pre-authorization status (if captured)

Group 2 — Clinical Picture 3. Primary Condition — Condition name, ICD-10 code pill + SNOMED code pill (both with AI confidence badge: "94%" in teal for ≥80%, amber for 50-79%, red for <50%), severity badge (green/amber/red), duration, laterality, onset description. Data source tag below: "Source: [document name]" or "Source: Intake conversation" with relative timestamp. 4. Procedure Sought — Procedure name, CPT code (code pill), urgency level badge, clinical justification (AI-generated summary). FHIR validation status: "FHIR R4 Validated ✓" green badge when resource passed schema validation. 5. Comorbidities — Table: condition name | ICD-10 code | SNOMED code | confidence % | risk level (color badge) | relevance to procedure | source (doc/chat icon). Sort by risk level descending. Auto-detected comorbidities from lab analyzer (e.g., "Prediabetes — detected from HbA1c 6.3%") show a "Lab-detected" tag. 6. Surgical History — Timeline format: date | procedure | facility | outcome. Most recent first.

Group 3 — Diagnostics & Lab Results 7. Lab Results — Dedicated section for structured lab values extracted from blood/urine reports. Table layout per panel (e.g., "Complete Blood Count", "Basic Metabolic Panel", "HbA1c" — matching the 13 parameter sets defined in Session 29). Columns: parameter name | value | unit | reference range | interpretation flag (↑ High in red, ↓ Low in amber, ✓ Normal in green, ⚠ Critical in red bold). LOINC code shown as subtle monospace pill on each parameter. Group by source document with extraction date. Show the document that contributed each panel (linked to R2). Auto-detected comorbidities from the lab analyzer (HbA1c→diabetes, eGFR→CKD, hemoglobin→anemia, etc.) render as inline amber callout cards below the relevant value: "⚠ HbA1c 6.3% indicates Prediabetes (auto-detected)". 8. Diagnostic Findings (Imaging & Clinical) — Grouped by source document. Each shows: document name (linked to R2 via presigned URL), document_id, extraction date, key findings as structured key-value pairs, data source tag ("Extracted from: [filename]" or "Reported in conversation on [date]"). Imaging findings get purple left-border, clinical notes get gray. Each extracted entity shows its FHIR resource validation status: "FHIR Observation ✓" or "FHIR Condition ✓" as a subtle green checkmark. 9. Medications — Table: medication name | dosage | frequency | duration | prescribing context | source (doc icon / chat icon). Active medications highlighted, discontinued grayed out with strikethrough. Source column distinguishes: extracted from uploaded document vs stated in conversation by patient vs detected in FHIR MedicationStatement. Chat-extracted medications (from Session 30 chat extractor) show a "From conversation" tag. 10. Allergies — Severity-sorted cards: allergy name | type (drug/food/environmental) | severity (red badge for severe, amber for moderate, green for mild) | reaction description | source tag (document/conversation)

Group 4 — Preferences & Travel 11. Patient Preferences — Structured display: budget range (formatted currency), preferred destinations (country flags + names), language requirements, dietary restrictions, cultural/religious considerations, accommodation preferences. Each preference shows source: "Stated in conversation" or "Extracted from intake form". Chat-extracted preferences (from Session 30 chat extractor + LLM requirement matcher) are first-class data here. 12. Travel Readiness — Passport status, visa requirements for matched corridors, travel companion, mobility considerations

Group 5 — AI Analysis 13. Clinical Summary — The AI-generated narrative in a highlighted card (teal-light bg, teal left border 4px). This is the prose summary the clinical context agent produces. 14. Risk Assessment — AI-identified risk factors with severity indicators. Each shows: risk factor | level (badge) | clinical relevance | mitigation note 15. Missing Information — Amber warning cards listing what's still needed, each with a "Ask patient" action that sends a prompt back to the conversation

Sticky positioning (scrolls with the page until it hits the top, then stays fixed).

Card 1: Record Completeness - Large circular progress ring (80px), percentage in center - Below: breakdown by section — mini progress bars per group (Identity, Clinical, Diagnostics, Preferences, AI Analysis) - Color coding: green ≥80%, amber 50-79%, red <50%

Card 2: Data Sources - List of all documents that contributed to this EHR - Each shows: filename, upload date, extraction status badge - Click opens document in a new tab (R2 presigned URL)

Card 3: Record Timeline - Compact timeline of EHR construction events: "Demographics captured from intake" → "X-ray report analyzed" → "Comorbidities identified" → etc. - Most recent at top - Timestamps in relative format ("2 hours ago")

Card 4: Quick Actions - "Download as PDF" button (generates a formatted PDF of the EHR — future feature, show as disabled with tooltip "Coming soon") - "Share with Provider" button (disabled unless providers selected, triggers consent flow — future feature) - "Report Issue" link (opens a feedback form or chat message)

3.4 Empty & Loading States

Loading: Skeleton loader matching the section layout. Staggered shimmer animation. Header loads immediately with patient name from case data.

Partial EHR: Sections with no data show a single-line message: "Not yet captured" in gray-400 italic. Section status dot is gray. The section is collapsed by default.

No EHR at all: The "View Full Record" button should not appear on the EHR panel if no EHR data exists. This state is handled by the existing "Your health record is being built" message in the EHR panel.

3.5 Responsive Behavior

Desktop (>1024px): Two-column layout as described. Tablet (768-1024px): Single column. Sidebar cards collapse into a horizontal scrollable strip at the top, below the header. Mobile (<768px): Full-screen overlay (drawer covers entire viewport including icon rail). Back button in header is the only way to close. Sidebar cards stack below main content.

3.6 Accessibility

  • Drawer traps focus when open (tab cycling within drawer)
  • Escape key closes the drawer
  • All section headers are <h2> with proper heading hierarchy
  • Collapsible sections use aria-expanded, aria-controls
  • Color coding always paired with text labels (never color-only indicators)
  • Screen reader: announce "Patient Health Record opened" on entry, "closed" on exit

4. Data Contract

4.1 API Endpoint

Existing endpoint is sufficient: GET /api/v1/cases/{case_id}/ehr

The current EHR endpoint returns the ehr_snapshot JSONB blob which already contains all the structured data needed. The full view is a frontend-only feature — no new backend endpoints required.

If the current ehr_snapshot doesn't include all fields listed above, the missing fields should be added to the EHR builder agent's output schema (separate backend task, not part of this feature).

4.2 Expected EHR Snapshot Structure

interface DataSource {
  type: 'document' | 'conversation' | 'lab_analyzer' | 'fhir';
  document_name?: string;
  document_id?: string;
  message_timestamp?: string;
  extraction_date?: string;
}

interface CodedEntity {
  icd_code: string;
  snomed_code?: string;
  icd_confidence?: number;    // 0-1, from Clinical Context Agent map_to_medical_codes
  snomed_confidence?: number; // 0-1
  fhir_resource_id?: string;  // links to validated FHIR resource in PostgreSQL
  fhir_validated?: boolean;   // true if passed HL7 R4 schema validation
}

interface EHRSnapshot {
  patient_demographics: {
    name: string;
    age: number;
    date_of_birth: string;
    gender: string;
    nationality: string;
    location: { city: string; country: string; state_province?: string };
    languages: string[];
    contact_preference?: string;
    source: DataSource;        // where demographics were captured from
  };
  insurance?: {
    status: string;
    provider_name?: string;
    policy_number?: string;
    pre_authorization?: string;
  };
  primary_condition: CodedEntity & {
    name: string;
    severity: 'mild' | 'moderate' | 'severe';
    duration: string;
    laterality?: string;
    onset_description?: string;
    source: DataSource;
  };
  procedure: {
    name: string;
    cpt_code: string;
    urgency: 'elective' | 'urgent' | 'emergency';
    clinical_justification?: string;
    fhir_resource_id?: string;
    fhir_validated?: boolean;
  };
  comorbidities: Array<CodedEntity & {
    name: string;
    risk_level: 'low' | 'moderate' | 'high';
    relevance: string;
    detection_method?: 'clinical_extraction' | 'lab_analyzer' | 'chat_extraction';
    detection_detail?: string;  // e.g., "HbA1c 6.3% indicates Prediabetes"
    source: DataSource;
  }>;
  surgical_history: Array<{
    date: string;
    procedure: string;
    facility?: string;
    outcome?: string;
    source: DataSource;
  }>;
  lab_results: Array<{
    panel_name: string;         // e.g., "Complete Blood Count", "Basic Metabolic Panel"
    source_document: string;
    document_id?: string;
    collection_date?: string;
    parameters: Array<{
      name: string;             // e.g., "Hemoglobin", "HbA1c", "eGFR"
      value: number | string;
      unit: string;
      reference_range?: string; // e.g., "13.5-17.5"
      interpretation: 'normal' | 'low' | 'high' | 'critical';
      loinc_code?: string;      // e.g., "718-7" for Hemoglobin
      fhir_resource_id?: string;
      fhir_validated?: boolean;
    }>;
    auto_detected_conditions?: Array<{
      condition: string;        // e.g., "Prediabetes"
      triggering_parameter: string;
      triggering_value: string;
      rule: string;             // e.g., "HbA1c 5.7-6.4% → Prediabetes"
    }>;
  }>;
  diagnostic_findings: Array<{
    source_document: string;
    document_id?: string;
    extraction_date: string;
    finding_type: 'imaging' | 'clinical';
    findings: Record<string, string>;
    fhir_resource_ids?: string[];
    source: DataSource;
  }>;
  medications: Array<{
    name: string;
    dosage: string;
    frequency: string;
    duration?: string;
    status: 'active' | 'discontinued';
    prescribing_context?: string;
    source: DataSource;         // document extraction vs chat extraction vs FHIR
  }>;
  allergies: Array<{
    name: string;
    type: 'drug' | 'food' | 'environmental';
    severity: 'mild' | 'moderate' | 'severe';
    reaction?: string;
    source: DataSource;
  }>;
  preferences: {
    budget_range?: { min: number; max: number; currency: string };
    preferred_destinations?: string[];
    language_requirements?: string[];
    dietary_restrictions?: string[];
    cultural_considerations?: string[];
    accommodation_preferences?: string[];
  };
  travel_readiness?: {
    passport_status?: string;
    visa_requirements?: Record<string, string>;
    travel_companion?: boolean;
    mobility_considerations?: string;
  };
  clinical_summary: string;
  risk_factors: Array<{
    factor: string;
    level: 'low' | 'moderate' | 'high';
    relevance: string;
    mitigation?: string;
  }>;
  missing_information: string[];
  completeness_score: number;
  last_updated: string;
  construction_events?: Array<{
    event: string;
    timestamp: string;
    source?: string;
  }>;
}

4.3 Graceful Degradation

The full EHR view must render whatever data exists. Every section handles null/undefined/empty arrays gracefully — show "Not yet captured" and collapse by default. The view should never crash or show empty white space because a field is missing.


5. Feature Flag

Flag name: full_ehr_view Default: true (enabled for MVP) Behavior when disabled: The "View Full Record" button is hidden from the EHR panel. No other changes.


6. Analytics Events (PostHog)

Event Properties Trigger
ehr_full_view_opened case_id, completeness_score, section_count User clicks "View Full Record"
ehr_full_view_closed case_id, time_spent_seconds, close_method (button/backdrop/escape) User closes the drawer
ehr_section_toggled case_id, section_name, expanded (bool) User expands/collapses a section
ehr_missing_info_clicked case_id, missing_field User clicks "Ask patient" on a missing info item

7. Relationship to Existing Components

Component Change
EHRPanel.tsx Add "View Full Record" CTA button at bottom
ConversationApp.tsx Add state for full EHR drawer open/close
NEW FullEHRDrawer.tsx The main drawer component
NEW EHRSection.tsx Reusable collapsible section component
NEW EHRSidebar.tsx Completeness ring, data sources, timeline, actions
NEW EHRDemographics.tsx Section: patient demographics
NEW EHRClinical.tsx Section: condition + procedure + comorbidities + surgical history
NEW EHRLabResults.tsx Section: structured lab panels with LOINC codes, values, reference ranges, abnormal flags, auto-detected conditions
NEW EHRDiagnostics.tsx Section: imaging/clinical findings + medications + allergies (all with source provenance)
NEW EHRPreferences.tsx Section: preferences + travel readiness
NEW EHRAIAnalysis.tsx Section: clinical summary + risk assessment + missing info

8. Out of Scope (This Feature)

  • PDF export of EHR (future — button shown disabled)
  • Share with provider (future — requires consent flow integration)
  • Edit EHR fields directly from the full view (EHR is AI-constructed, not manually editable)
  • FHIR resource raw view (developer/admin feature, not patient-facing)
  • Printing optimizations

9. Backend Prerequisites (Separate Task)

The full EHR view is frontend-only, but the ehr_snapshot JSONB may need backend enrichment to supply all fields specified above. The following are not part of this frontend feature but must be tracked as a separate backend task:

  • lab_results array — EHR builder currently stores lab observations as flat key-value pairs inside diagnostic_findings. Needs refactoring to produce structured lab_results with panel_name, individual parameters (value/unit/reference_range/interpretation), and loinc_code per parameter. The 13 parameter sets from Session 29 already define the panel groupings.
  • snomed_code + confidence on coded entities — Clinical Context Agent's map_to_medical_codes node already produces SNOMED codes and confidence scores, but these may not be persisted through to ehr_snapshot. Verify and wire if missing.
  • source (DataSource) on every data section — EHR builder needs to tag each data point with its origin: document_id + name for document-extracted data, message timestamp for chat-extracted data, "lab_analyzer" for rule-based detections.
  • fhir_resource_id and fhir_validated on clinical entities — The store_resources node in the Clinical Context Agent creates validated FHIR resources. These IDs need to flow through to ehr_snapshot so the UI can display validation status.
  • auto_detected_conditions on lab panels — Lab analyzer (Session 21) already detects comorbidities from lab values. The detection reasoning (triggering parameter, value, rule) needs to be persisted in structured form.

Graceful degradation applies: The frontend renders whatever data exists. Missing fields (e.g., no SNOMED code, no source tag, no LOINC) result in those UI elements simply not rendering — never a crash or empty space. The full EHR view works on day one with current data, and progressively improves as the backend enriches ehr_snapshot.