EncounterPage Functional Workflow Documentation
Purpose: Complete workflow breakdown of the EncounterPage focused on user-facing behavior, data flow, and functional logic.
Scope: The EncounterPage is a multi-specialty clinical documentation interface enabling healthcare practitioners to document patient encounters across 35+ clinical domains.
1. Page Purpose & Entry Point
What the Page Does
The EncounterPage is the primary clinical documentation interface for healthcare encounters. It allows a practitioner (doctor, physiotherapist, dietitian, etc.) to document a patient visit by filling out specialty-specific forms organized into tabs, saving the data, and optionally finalizing the encounter.
How a User Arrives at This Page
A user navigates to this page by clicking into an existing encounter from the dashboard or queue. The page requires:
- Encounter ID: Identifies which clinical encounter is being documented
- Patient ID: Identifies the patient (may come from URL params, query params, or the encounter itself)
- Appointment ID: Optional, enables AI Scribe integration when present
Entry URL pattern:
/encounter/:encounterId?patientId=X&appointmentId=Y&practitionerType=Z2. Practitioner Type & Tab Configuration
How the System Determines What Tabs to Show
On page load, the system reads the practitionerType from three sources in priority order:
- URL query parameter
practitionerType - The
practitioner_typefield stored on the encounter record - Default fallback:
"doctor"
Using this practitioner type, the system fetches an Encounter Type Configuration — a set of rules that determines:
- Which tabs are visible
- Which tab is shown by default
- Whether prescriptions and investigations tabs are enabled
Tab Visibility Rules
Each specialty domain has its own set of tabs. However, two tabs have additional conditional logic:
- Medications tab: Hidden if
encounterTypeConfig.has_prescriptions === false - Investigations tab: Hidden if
encounterTypeConfig.has_investigations === false
3. Data Loading & Initialization Flow
Step-by-Step Load Sequence
1. Page URL is parsed for encounterId, patientId, appointmentId, practitionerType
2. Practitioner type is resolved from URL → encounter data → default
3. Encounter Type Config is fetched for the practitioner type
4. Encounter metadata is fetched from the clinical API (status, start time, practitioner, facility)
5. Patient details are fetched from the clinical API (name, age, gender, allergies, conditions)
6. All clinical data is fetched from the clinical API (vitals, observations, medications, etc.)
7. Clinical data is "seeded" into local state — the page becomes interactiveData Seeding Behavior
The system uses a one-time seed pattern:
- While clinical data is loading, local state remains at defaults
- Once loading completes, if any clinical data exists, local state is initialized with it
- A
hasSeededFromAPIflag prevents re-seeding on subsequent renders - Even if no data exists, the seed still runs (with empty arrays) to mark initialization complete
Patient Data Normalization
Patient data from the API has two possible sources and gets normalized:
- From the patient API: Full demographics (name, age, gender, phone, email, MRN, allergies, conditions)
- From the encounter's embedded patient object: Fallback if patient API fails (display name, MRN only)
The system prioritizes patient API data and falls back to encounter-embedded data if the patient fetch fails.
4. Tabs & Sections
Complete Tab Inventory
The page contains 35+ tabs organized into clinical specialty domains:
Universal / Core Tabs (shown to all practitioners)
| Tab ID | Name | Purpose |
|---|---|---|
vitals | Vitals | Record vital signs (BP, heart rate, temperature, weight, etc.) |
observations | Observations | Clinical findings, symptoms, assessments |
medications | Medications | Prescribe medications with drug interaction checking |
investigations | Investigations | Order lab tests, imaging, referrals |
advice | Advice | Provide patient advice and schedule follow-ups |
Dental
| Tab ID | Name | Purpose |
|---|---|---|
dental | Dental | Tooth-by-tooth findings and treatment planning |
Mental Health
| Tab ID | Name | Purpose |
|---|---|---|
clinical-history | Clinical History | Psychiatric history, substance use, working diagnoses |
session-notes | Session Notes | Session narrative and therapist notes |
assessments | Assessments | Standardized mental health assessments |
treatment-plan | Treatment Plan | Treatment goals and objectives |
homework | Homework | Patient assignments between sessions |
Physiotherapy
| Tab ID | Name | Purpose |
|---|---|---|
assessment | Assessment | Pain, range of motion, muscle strength, functional status |
exercises | Exercises | Exercise prescriptions |
progress | Progress | Progress notes and tracking entries |
Dermatology
| Tab ID | Name | Purpose |
|---|---|---|
skin-examination | Skin Examination | Lesion mapping and dermatological findings |
procedures | Procedures | Dermatological procedures performed |
photo-documentation | Photo Documentation | Clinical photography upload |
Aesthetic Medicine
| Tab ID | Name | Purpose |
|---|---|---|
skin-assessment | Skin Assessment | Fitzpatrick type, skin concerns, analysis |
cosmetic-procedures | Cosmetic Procedures | Aesthetic procedures (Botox, fillers, etc.) |
consent | Consent | Patient consent management for procedures |
Cardiology
| Tab ID | Name | Purpose |
|---|---|---|
cardiac-examination | Cardiac Exam | Heart sounds, murmurs, rhythm examination |
ecg-interpretation | ECG Interpretation | ECG findings and interpretation |
cardiac-risk | Cardiac Risk | Cardiovascular risk assessment |
Cardiac Surgery
| Tab ID | Name | Purpose |
|---|---|---|
surgical-procedures | Surgical Procedures | Cardiac surgery procedures |
post-op-assessment | Post-Op Assessment | Post-operative wound status, complications |
Dietitian
| Tab ID | Name | Purpose |
|---|---|---|
nutritional-assessment | Nutritional Assessment | Dietary assessment and restrictions |
meal-plan | Meal Plan | Structured meal planning |
goals | Goals | Nutrition goals |
dietitian-progress | Progress | Dietitian progress tracking |
Ophthalmology
| Tab ID | Name | Purpose |
|---|---|---|
eye-examination | Eye Examination | Visual acuity, fundoscopy findings |
vision-assessment | Vision Assessment | Comprehensive vision testing |
ENT
| Tab ID | Name | Purpose |
|---|---|---|
ent-examination | ENT Examination | Ear, nose, throat examination |
hearing-assessment | Hearing Assessment | Audiometry and hearing evaluation |
Orthopedic
| Tab ID | Name | Purpose |
|---|---|---|
orthopedic-examination | Orthopedic Exam | Joint-by-joint musculoskeletal examination |
orthopedic-procedures | Procedures | Orthopedic procedures |
Gynecology
| Tab ID | Name | Purpose |
|---|---|---|
gynec-examination | Gynec Exam | Gynecological examination findings |
obstetric-assessment | Obstetric Assessment | Pregnancy and obstetric assessment |
Pediatric
| Tab ID | Name | Purpose |
|---|---|---|
pediatric-examination | Pediatric Exam | Pediatric milestones and development |
immunization | Immunization | Vaccination record |
growth-assessment | Growth Assessment | Growth percentiles and tracking |
Pulmonology
| Tab ID | Name | Purpose |
|---|---|---|
pulmonary-examination | Pulmonary Exam | Respiratory examination findings |
respiratory-assessment | Respiratory Assessment | PFT results and respiratory evaluation |
5. Forms & Data Entry
Form Data Model
Each tab manages a specific domain object stored in the single clinicalData local state:
| Domain Object | Stored As | Default Value |
|---|---|---|
| Vitals | clinicalData.vitals | Empty array |
| Observations | clinicalData.observations | Empty array |
| Chief Complaint Duration | clinicalData.chiefComplaintDuration | Undefined |
| Medications | clinicalData.medications | Empty array |
| Investigations | clinicalData.investigations | Empty array |
| Advice | clinicalData.advice | Empty array |
| Follow-ups | clinicalData.followUps | Empty array |
| Dental Findings | clinicalData.dentalFindings | Empty array |
| Dental Treatments | clinicalData.dentalTreatments | Empty array |
| Session Note | clinicalData.sessionNote | Empty object {} |
| Assessments | clinicalData.assessments | Empty array |
| Treatment Goals | clinicalData.treatmentGoals | Empty array |
| Homework Assignments | clinicalData.homeworkAssignments | Empty array |
| Physio Assessment | clinicalData.physioAssessment | Structured default with 4 empty arrays |
| Exercises | clinicalData.exercises | Empty array |
| Progress Entries | clinicalData.progressEntries | Empty array |
| Clinical History | clinicalData.clinicalHistory | Object with 5 empty arrays |
| Skin Examination | clinicalData.skinExamination | Object with empty lesions array |
| Derm Procedures | clinicalData.dermProcedures | Empty array |
| Photo Documentation | clinicalData.photoDocumentation | Object with empty photos array |
| Skin Assessment | clinicalData.skinAssessment | Object with empty skinConcerns array |
| Cosmetic Procedures | clinicalData.cosmeticProcedures | Empty array |
| Consent Management | clinicalData.consentManagement | Object with empty records array |
| Cardiac Examination | clinicalData.cardiacExamination | Structured default |
| ECG Interpretation | clinicalData.ecgInterpretation | Structured default |
| Cardiac Risk Assessment | clinicalData.cardiacRiskAssessment | Object with empty riskFactors array |
| Cardiac Procedures | clinicalData.cardiacProcedures | Empty array |
| Surgical Procedures | clinicalData.surgicalProcedures | Empty array |
| Post-Op Assessment | clinicalData.postOpAssessment | Structured default |
| Nutritional Assessment | clinicalData.nutritionalAssessment | Object with empty dietaryRestrictions array |
| Meal Plan | clinicalData.mealPlan | Object with empty meals array |
| Nutrition Goals | clinicalData.nutritionGoals | Empty array |
| Dietitian Progress | clinicalData.dietitianProgressEntries | Empty array |
| Eye Examination | clinicalData.eyeExamination | Empty object {} |
| Vision Assessment | clinicalData.visionAssessment | Empty object {} |
| Eye Procedures | clinicalData.eyeProcedures | Empty array |
| ENT Examination | clinicalData.entExamination | Empty object {} |
| Hearing Assessment | clinicalData.hearingAssessment | Empty object {} |
| ENT Procedures | clinicalData.entProcedures | Empty array |
| Orthopedic Examination | clinicalData.orthopedicExamination | Object with empty joints array |
| Orthopedic Procedures | clinicalData.orthopedicProcedures | Empty array |
| Gynec Examination | clinicalData.gynecExamination | Empty object {} |
| Obstetric Assessment | clinicalData.obstetricAssessment | Empty object {} |
| Gynec Procedures | clinicalData.gynecProcedures | Empty array |
| Pediatric Examination | clinicalData.pediatricExamination | Object with empty milestones array |
| Growth Assessment | clinicalData.growthAssessment | Object with empty currentPercentiles |
| Immunization Record | clinicalData.immunizationRecord | Object with empty vaccinations array |
| Pediatric Procedures | clinicalData.pediatricProcedures | Empty array |
| Pulmonary Examination | clinicalData.pulmonaryExamination | Empty object {} |
| Respiratory Assessment | clinicalData.respiratoryAssessment | Empty object {} |
| Pulmonary Procedures | clinicalData.pulmonaryProcedures | Empty array |
Form Initialization
Each tab receives its data from local state and renders a form. When the user makes changes:
- The tab calls its
onChangehandler (e.g.,onVitalsChange,onMedicationsChange) - The handler updates the corresponding field in
clinicalData - The handler sets
hasUnsavedChanges = true
Form Validation
No explicit validation is performed by the EncounterPage itself. Validation rules are implemented within individual tab components. The page trusts the tab-level components to handle field-level validation.
Form Reset Behavior
There is no form reset functionality on the page. The data persists in local state until:
- The user navigates away (data is lost if not saved)
- The page is reloaded (data re-seeds from API)
6. APIs & Data Flow
All API Calls Made by This Page
| Call | Trigger | Purpose |
|---|---|---|
useClinicalEncounter(encounterId) | Page load | Fetch encounter metadata (status, times, practitioner, facility) |
useClinicalPatient(patientId) | Page load | Fetch patient demographics and medical info |
useEncounterTypeConfig(practitionerType) | Page load | Fetch tab visibility rules |
useEncounterClinicalData(...) | Page load (conditional) | Fetches all clinical data for active tabs |
saveBatch(clinicalData, tabIds) | Manual save action | Persists all clinical data to the API |
completeEncounter() | Finalize action | Transitions encounter status to completed |
refetchEncounter() | After finalize | Re-fetch encounter to show updated status |
Clinical Data Fetching Strategy
The system uses a conditional query strategy:
- On page load, the list of active tab IDs is derived from
encounterTypeConfig useEncounterClinicalDatafires queries for all clinical data domains- Only tabs that are active receive their data; inactive tabs are not queried
- Queries run in parallel when possible
Batch Save Flow
When the user clicks Save:
1. User clicks "Save" button
2. Button shows loading spinner (isSaving = true)
3. System identifies which tabs are active for the current practitioner type
4. All clinical data is sent to the batch save endpoint via saveBatch()
5. Batch endpoint persists vitals, observations, medications, investigations, etc.
6. If investigations were saved, the response contains server-assigned UUIDs
7. Local investigation state is updated with these UUIDs (prevents stale deletes)
8. hasUnsavedChanges = false
9. Success alert shown for 3 seconds
10. If error: error alert shown for 5 secondsEncounter Finalization Flow
When the user clicks "Finalize":
1. User clicks "Finalize" button
2. Button shows loading spinner (isCompleting = true)
3. completeEncounter() is called
4. Encounter status transitions to "completed" on the server
5. Encounter is re-fetched to show updated status
6. Success alert shown for 3 seconds
7. Page remains viewable but in read-only mode
8. If error: error alert shown for 5 secondsStatus Mapping
The clinical API uses different status strings than the internal model:
| Clinical API Status | Internal EncounterStatus |
|---|---|
in_progress | in-progress |
finished | completed |
cancelled | cancelled |
entered_in_error | cancelled |
| (anything else) | draft |
Error Handling
- Encounter not found: Shows error state with "Encounter Not Found" message and "Back to Dashboard" button
- Patient not found: Same error state
- Save failure: Error alert with "Failed to save clinical data", auto-dismisses after 5 seconds
- Finalize failure: Error alert with "Failed to complete encounter", auto-dismisses after 5 seconds
Loading States
- While any of (encounter loading, patient loading, clinical data loading): Full-page skeleton shown
- During save: Save button shows spinner, page remains interactive
- During finalize: Finalize button shows spinner, page remains interactive
7. User Workflow
Complete End-to-End Journey
┌─────────────────────────────────────────────────────────────┐
│ STEP 1: Page Load │
├─────────────────────────────────────────────────────────────┤
│ User arrives from dashboard/queue with encounterId │
│ │
│ System parses URL → encounterId, patientId, appointmentId │
│ System determines practitioner type (URL → encounter → default)
│ System fetches: encounter metadata, patient details, │
│ encounter type config, all clinical data │
│ │
│ Loading state: Full-page skeleton │
│ │
│ Data arrives → Clinical data seeded into local state │
│ Default tab set from encounter type config │
│ Page becomes interactive │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ STEP 2: Review Patient Context │
├─────────────────────────────────────────────────────────────┤
│ Patient header shows: name, age, gender, phone │
│ Medical context: allergies (highlighted), diseases │
│ Encounter context: status badge, start time, practitioner │
│ │
│ User can toggle AI Scribe sidebar (if appointmentId exists) │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ STEP 3: Navigate Tabs & Fill Clinical Data │
├─────────────────────────────────────────────────────────────┤
│ User clicks through tabs (Vitals → Observations → etc.) │
│ Each tab pre-populated with seeded data │
│ │
│ On every change: │
│ → Local state updated │
│ → hasUnsavedChanges = true │
│ → Unsaved indicator appears below tabs │
│ │
│ Tab-specific behavior: │
│ • Vitals: Age/gender-aware reference ranges │
│ • Observations: AI suggestions integration │
│ • Medications: Allergy alerts, drug interaction checking │
│ • Investigations: Lab/imaging/referral categorization │
│ • Dental: Tooth chart selection, ADA codes │
│ • Clinical History: Psychiatric history structured input │
│ • Physical exams: Structured findings by system │
│ • Procedures: Procedure codes, dates, notes │
│ • Photo Documentation: File upload │
│ • Consent: Procedure consent tracking │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ STEP 4: AI Scribe (Optional) │
├─────────────────────────────────────────────────────────────┤
│ If appointmentId exists and AI Scribe is enabled: │
│ User opens AI Scribe sidebar │
│ Live transcription of consultation │
│ AI generates suggestions: │
│ • Medication suggestions → click to add to Medications tab
│ • Test suggestions → click to add to Investigations tab │
│ • Diagnosis suggestions → appended to Observations tab │
│ │
│ Accept: Applies suggestion, marks as accepted │
│ Reject: Dismisses suggestion, marks as rejected │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ STEP 5: Save Clinical Data │
├─────────────────────────────────────────────────────────────┤
│ User clicks "Save" button │
│ │
│ Save button: Always visible, enabled even with no changes │
│ Spinner shown while saving │
│ │
│ Success: Green alert "Encounter saved successfully" │
│ hasUnsavedChanges = false │
│ │
│ Error: Red alert "Failed to save clinical data" │
│ Data remains in local state, user can retry │
│ │
│ Note: User should save before navigating away — │
│ unsaved local state is lost on navigation │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ STEP 6: Finalize Encounter (Optional) │
├─────────────────────────────────────────────────────────────┤
│ User clicks "Finalize" button │
│ │
│ Only shown for practitioners with appropriate permissions │
│ │
│ On finalize: │
│ → Encounter status → "completed" on server │
│ → Encounter re-fetched to sync status │
│ → "Finalized" badge shown in header │
│ → Further edits may be restricted depending on settings │
│ │
│ Note: Finalization is a one-way transition (to some states) │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ STEP 7: Navigate Away │
├─────────────────────────────────────────────────────────────┤
│ User clicks "Cancel" or browser back │
│ │
│ If hasUnsavedChanges = true: │
│ → No confirmation dialog is shown │
│ → Data is simply lost │
│ → No warning to user │
│ │
│ If hasUnsavedChanges = false: │
│ → Clean navigation to dashboard │
└─────────────────────────────────────────────────────────────┘8. Unsaved Changes Handling
Tracking Unsaved Changes
Every change handler (onVitalsChange, onMedicationsChange, etc.) sets hasUnsavedChanges = true. This flag drives:
- Visual indicator: Amber badge below tabs showing "Unsaved changes" with pulsing dot
- Save prompt: None — no browser beforeunload dialog is shown
Key Behavioral Note
There is NO confirmation dialog when navigating away with unsaved changes. The user receives no browser prompt, no modal, no warning. The unsaved data is silently discarded. This is a known gap — the system only shows the visual indicator.
9. AI Scribe Integration
How AI Scribe Works
- Sidebar visibility: Controlled by
isAIScribeOpenstate andsettings.showAIScribe - Appointment link: Requires valid
appointmentIdin URL — without it, sidebar won't render - Live transcription: AIScribeSidebar receives
onTranscriptionUpdatecallbacks as audio is processed - Suggestion acceptance: When user accepts a suggestion:
- Medication: New medication added to
clinicalData.medicationswith auto-generated ID - Test: Console log (investigation not yet implemented)
- Diagnosis: Appended to observations with code "Assessment"
- Medication: New medication added to
10. Role-Based & Conditional Behavior Summary
| Condition | Behavior |
|---|---|
No appointmentId | AI Scribe sidebar not rendered |
settings.showAIScribe = false | AI Scribe sidebar not rendered |
encounterTypeConfig.has_prescriptions = false | Medications tab hidden |
encounterTypeConfig.has_investigations = false | Investigations tab hidden |
encounter.status = completed | "Finalized" badge shown |
hasUnsavedChanges = true | Amber unsaved indicator shown |
| No patient found (API fails) | Error page with back button |
| No encounter found (API fails) | Error page with back button |
| Practitioner type = "doctor" (default) | Standard 5 tabs + full specialty set |
| Other practitioner types | Tab set filtered by encounter type config |
11. Data Architecture Summary
State Hierarchy
URL Parameters
├── encounterId (required)
├── patientId (optional — fallback to encounter.patient_id)
├── appointmentId (optional — enables AI Scribe)
└── practitionerType (optional — fallback to encounter.practitioner_type → "doctor")
External API Data (fetched on load)
├── Encounter metadata (status, times, practitioner, facility)
├── Patient demographics (name, age, gender, allergies, conditions)
├── Encounter Type Config (tab visibility, defaults)
└── Clinical Data (all domain objects)
Local State (clinicalData)
├── 45+ domain objects managed via individual onChange handlers
├── Seeded once from API, then mutated locally
└── Persisted via batch save
UI State
├── activeTab: which tab is currently shown
├── hasUnsavedChanges: tracks if local edits need saving
├── saveSuccess / saveError: save feedback
├── isCompleting: finalize loading state
├── isAIScribeOpen: sidebar visibility
└── aiSuggestions: list of AI-generated suggestionsKey Data Flow Principle
The EncounterPage follows an Optimistic Local Edit pattern:
- All edits are applied immediately to local state
- Local state is the source of truth for rendering
- A single batch save persists everything to the server
- After save, server-assigned UUIDs are reconciled back into local state
Document version: 1.0 — Based on EncounterPage.tsx as of 2026-03-24