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
Sidebar Column (right, 35%)¶
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_resultsarray — EHR builder currently stores lab observations as flat key-value pairs insidediagnostic_findings. Needs refactoring to produce structuredlab_resultswithpanel_name, individualparameters(value/unit/reference_range/interpretation), andloinc_codeper parameter. The 13 parameter sets from Session 29 already define the panel groupings.snomed_code+confidenceon coded entities — Clinical Context Agent'smap_to_medical_codesnode already produces SNOMED codes and confidence scores, but these may not be persisted through toehr_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_idandfhir_validatedon clinical entities — Thestore_resourcesnode in the Clinical Context Agent creates validated FHIR resources. These IDs need to flow through toehr_snapshotso the UI can display validation status.auto_detected_conditionson 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.