# Clinical Workflows UI Patterns (Reference) Use this reference for vital signs entry/display, medication administration, care plans, clinical notes, and order entry. Covers web (Bootstrap 5/Tabler + PHP) and Android (Jetpack Compose + Material 3). ## 1. Vital Signs Entry and Display ### Entry Form Pattern | Vital | Input Type | Unit | Range | |-------|-----------|------|-------| | Temperature | Decimal + C/F toggle | C / F | 30.0-45.0C | | Blood Pressure | Two fields (systolic/diastolic) | mmHg | 50-300 / 20-200 | | Heart Rate | Integer | bpm | 20-250 | | SpO2 | Integer | % | 50-100 | | Respiratory Rate | Integer | /min | 4-60 | | Pain Scale | Slider or number picker | 0-10 | 0-10 | | Weight | Decimal | kg / lb | 0.5-500 | | Height | Decimal | cm / ft-in | 20-300 | **BMI Auto-Calculation:** When both weight and height are present, display BMI inline: `BMI = weight(kg) / height(m)^2`. Color-code: <18.5 underweight (amber), 18.5-24.9 normal (green), 25-29.9 overweight (amber), >=30 obese (red). **Entry Source Selector:** Three-option toggle above the form: `[ Manual Entry ] [ Wearable Device ] [ Medical Equipment ]`. When Wearable or Equipment is selected, show device ID field and auto-populate supported vitals. **Timestamp:** Auto-fill current datetime. Provide override for delayed charting with datetime picker and mandatory "Reason for late entry" text field. ### Clinical Thresholds Table (Configurable Per Facility) | Vital | Normal (Green) | Warning (Amber) | Critical (Red) | |-------|---------------|-----------------|----------------| | HR | 60-100 bpm | <50 or >110 bpm | <40 or >130 bpm | | SpO2 | >=95% | 90-94% | <90% | | Temp | 36.1-37.2 C | 37.3-38.5 C | >38.5 C or <35 C | | BP Systolic | <120 | 120-129 | >=130 | | BP Diastolic | <80 | 80-89 | >=90 | | BP Crisis | -- | -- | >180 / >120 | | RR | 12-20 /min | <10 or >24 /min | <8 or >30 /min | | Pain | 0-3 | 4-6 | 7-10 | ### Display Patterns **Grid Cards (Primary View):** ``` +--------------+--------------+--------------+ | HR: 72 bpm | SpO2: 98% | Temp: 37.1C | | [Normal] | [Normal] | [Normal] | | -> stable | -> stable | -> stable | +--------------+--------------+--------------+ | BP: 120/80 | RR: 16 /min | Pain: 3/10 | | mmHg [Normal]| [Normal] | [Mild] | | -> stable | -> stable | -> down | +--------------+--------------+--------------+ ``` Each card shows: value (large, bold), unit, status badge (color-coded), trend arrow. Tap/hover reveals timestamp, source, and recording provider. **Trend Graphs:** Line charts per vital over selectable periods (24h, 7d, 30d). Shade normal range band in light green. Plot warning/critical thresholds as dashed lines. Mark data points with source icon (hand=manual, watch=wearable, monitor=equipment). **Body Chart:** Anatomical silhouette with tappable regions. Color-code: green (no issues), amber (monitoring), red (active concern). Tapping a region shows associated vitals, diagnoses, and notes. ### Web: Vitals Entry (Tabler + Chart.js) ```html

Record Vital Signs

/
``` ### Android: Vitals Composables ```kotlin @Composable fun VitalSignCard(label: String, value: String, unit: String, status: VitalStatus, trend: TrendDirection, modifier: Modifier = Modifier) { val statusColor = when (status) { VitalStatus.NORMAL -> Color(0xFF059669) VitalStatus.WARNING -> Color(0xFFD97706) VitalStatus.CRITICAL -> Color(0xFFDC2626) } Card(modifier = modifier, colors = CardDefaults.cardColors( containerColor = statusColor.copy(alpha = 0.08f)), border = BorderStroke(1.dp, statusColor.copy(alpha = 0.3f))) { Column(Modifier.padding(12.dp)) { Text(label, style = MaterialTheme.typography.labelMedium) Row(verticalAlignment = Alignment.Bottom) { Text(value, style = MaterialTheme.typography.headlineMedium, fontWeight = FontWeight.Bold) Spacer(Modifier.width(4.dp)) Text(unit, style = MaterialTheme.typography.bodySmall) } Row(verticalAlignment = Alignment.CenterVertically) { StatusBadge(status); Spacer(Modifier.width(4.dp)); TrendArrow(trend) } } } } @Composable fun VitalsEntryForm(onSubmit: (VitalsRecord) -> Unit) { var temperature by remember { mutableStateOf("") } var systolic by remember { mutableStateOf("") } var diastolic by remember { mutableStateOf("") } var source by remember { mutableStateOf(EntrySource.MANUAL) } Column(Modifier.verticalScroll(rememberScrollState())) { SourceSelector(selected = source, onSelect = { source = it }) OutlinedTextField(value = temperature, onValueChange = { temperature = it }, label = { Text("Temperature") }, suffix = { Text("C") }, keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Decimal)) Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) { OutlinedTextField(value = systolic, onValueChange = { systolic = it }, label = { Text("Systolic") }, modifier = Modifier.weight(1f)) Text("/", Modifier.align(Alignment.CenterVertically)) OutlinedTextField(value = diastolic, onValueChange = { diastolic = it }, label = { Text("Diastolic") }, modifier = Modifier.weight(1f)) } // HR, SpO2, RR, Pain, Weight, Height: same OutlinedTextField pattern } } ``` ## 2. Medication Administration ### 5 Rights Verification UI Sequential verification steps -- UI must enforce completion order: 1. **Right Patient:** Display photo + full name + MRN + DOB. Barcode scan for wristband. Green checkmark on confirmation. 2. **Right Medication:** Drug name + form (tablet/liquid/injection) + strength. Barcode scan option. Auto cross-check against allergy list. 3. **Right Dose:** Prescribed dose + calculated dose with formula: `Dose = [dose/kg] x [weight] = [result] [unit]`. Flag if outside safe range. 4. **Right Time:** Scheduled vs current time. Amber if +/-15min, red if +/-30min. Require reason if outside window. 5. **Right Route:** Visual icon selector (Oral/IV/IM/SC/Topical/Inhaled). Must match prescribed route. Mismatch blocks progression. **Override Flow:** Block on verification failure. Override requires supervisor PIN + mandatory reason text. Log all overrides in audit trail. ### Medication List View Three sections: **Current** (drug + next-due time + Administer button), **PRN** (as-needed meds with last-given time + Give button), **Held/Discontinued** (drug + reason). Each section visually separated. **Drug Interaction Alerts:** Inline amber banner when active medications interact. Show severity (mild/moderate/severe) and recommendation. **Allergy Cross-Check:** Red blocking alert if prescribed drug matches or is in same class as documented allergy. Requires pharmacist/physician override. ### Web: Step Wizard (Tabler) ```html

Medication Administration

``` ### Android: Stepper Composable ```kotlin @Composable fun MedAdminStepper(medication: Medication, patient: Patient) { var currentStep by remember { mutableIntStateOf(0) } val steps = listOf("Patient", "Medication", "Dose", "Time", "Route") val verified = remember { mutableStateListOf(false, false, false, false, false) } Column { StepIndicator(steps = steps, current = currentStep, verified = verified) when (currentStep) { 0 -> PatientVerification(patient) { verified[0] = true; currentStep++ } 1 -> MedicationVerification(medication, patient.allergies) { verified[1] = true; currentStep++ } 2 -> DoseVerification(medication, patient.weight) { verified[2] = true; currentStep++ } 3 -> TimeVerification(medication.scheduledTime) { verified[3] = true; currentStep++ } 4 -> RouteVerification(medication.prescribedRoute) { verified[4] = true } } if (verified.all { it }) { Button(onClick = { /* confirm and log */ }, colors = ButtonDefaults.buttonColors(containerColor = Color(0xFF059669))) { Icon(Icons.Default.Check, null); Text("Confirm Administration") } } } } ``` ## 3. Care Plan Builder ### POGI Framework Each care plan item follows Problem-Objective-Goal-Intervention: ``` +---------------------------------------------------------------+ | PROBLEM: Uncontrolled Type 2 Diabetes (E11.65) | | OBJECTIVE: Reduce HbA1c from 9.2% to <7.0% within 6 months | | GOAL: Patient self-manages glucose with <2 hypos/month | | INTERVENTION: | | - Endocrinology consult (Dr. Lee) -- Due: Mar 15 | | - Dietitian referral -- Due: Mar 10 | | - Daily glucose log review by nurse -- Ongoing | | STATUS: [====------] 40% -- At Risk (amber) | +---------------------------------------------------------------+ ``` **Status Colors:** On Track (green, >=70%), At Risk (amber, 40-69%), Behind (red, <40%). **AI-Assisted Templates:** On new diagnosis, suggest matching care plan templates as selectable cards with name, duration, and intervention count. **Progress Tracking:** Timeline view with milestones showing date, responsible provider, completion status. Notes and attachments at each milestone. ### Web: Draggable Cards (SortableJS) ```html
Problem: Uncontrolled Diabetes
At Risk
``` ### Android: Reorderable LazyColumn ```kotlin @Composable fun CarePlanCard(item: CarePlanItem) { val statusColor = when (item.status) { PlanStatus.ON_TRACK -> Color(0xFF059669) PlanStatus.AT_RISK -> Color(0xFFD97706) PlanStatus.BEHIND -> Color(0xFFDC2626) } Card(Modifier.fillMaxWidth().padding(horizontal = 16.dp, vertical = 4.dp)) { Column(Modifier.padding(16.dp)) { Text(item.problem, style = MaterialTheme.typography.titleSmall) Text("Objective: ${item.objective}", style = MaterialTheme.typography.bodySmall) LinearProgressIndicator(progress = { item.progressPercent / 100f }, color = statusColor, modifier = Modifier.fillMaxWidth().padding(top = 8.dp)) Text("${item.progressPercent}% - ${item.status.label}", color = statusColor, style = MaterialTheme.typography.labelSmall) } } } ``` ## 4. Clinical Notes / Documentation ### SOAP Note Template ``` +------------------------------------------------------+ | SOAP Note - Visit: 2026-02-23 09:30 | +------------------------------------------------------+ | S (Subjective) | | Chief Complaint: [text] HPI: [rich text] | | ROS: [checklist: General, HEENT, CV, Resp, GI...] | | O (Objective) | | Vitals: [auto-populated] PE: [findings/system] | | A (Assessment) | | Diagnosis: [ICD-10 search + multi-select] | | P (Plan) | | Orders: [CPOE link] Referrals: [specialty search] | | Follow-up: [date picker + interval selector] | +------------------------------------------------------+ | Auto-saved 30s ago | v3 | [View History] | [Sign] | +------------------------------------------------------+ ``` **Quick Templates:** Dropdown pre-fills SOAP for common visit types (Annual Physical, Follow-up, Urgent, Post-Op). Editable after selection. **Voice-to-Text:** Microphone button per text section. Speech-to-text API integration. Transcribed text editable before save. **Auto-Save:** Draft saved every 30 seconds with "Saved" indicator. Version history via "View History" link. **E-Signature:** "Sign" captures provider name, credentials (MD/DO/NP/PA), timestamp, IP. Signed notes become read-only; amendments create addendums. ### Web: Accordion SOAP Sections ```html

Clinical Note

``` ### Android: Expandable SOAP Form ```kotlin @Composable fun SoapNoteForm(note: SoapNote, onSign: (SoapNote) -> Unit) { val expanded = remember { mutableStateMapOf("subjective" to true, "objective" to true, "assessment" to false, "plan" to false) } LazyColumn(Modifier.padding(16.dp)) { item { SoapSection("Subjective", expanded["subjective"] == true, { expanded["subjective"] = !(expanded["subjective"] ?: false) }) { OutlinedTextField(note.chiefComplaint, { note.chiefComplaint = it }, label = { Text("Chief Complaint") }, modifier = Modifier.fillMaxWidth()) OutlinedTextField(note.hpi, { note.hpi = it }, label = { Text("HPI") }, minLines = 4, modifier = Modifier.fillMaxWidth()) } } // Objective, Assessment, Plan: same SoapSection composable item { Button(onClick = { onSign(note) }, Modifier.fillMaxWidth()) { Icon(Icons.Default.Draw, null); Spacer(Modifier.width(8.dp)); Text("Sign Note") } } } } ``` ## 5. Order Entry (CPOE) ### Order Types | Order Type | Required Fields | |-----------|----------------| | **Lab** | Test name, urgency (Routine/Stat/ASAP), specimen type, clinical indication | | **Imaging** | Modality (X-ray/CT/MRI/US), body part, indication, contrast Y/N | | **Medication** | Drug, dose, route, frequency, duration, diagnosis | | **Referral** | Specialty, preferred provider, reason, urgency, supporting docs | ### Clinical Decision Support (CDS) Alerts fire automatically during order entry: - **Duplicate Detection:** "Active CBC order exists (2h ago). Continue?" -- amber warning - **Drug Interaction:** "Warfarin + Aspirin: increased bleeding risk." -- red block if severe - **Dose Validation:** "Amoxicillin 2000mg exceeds max dose (1000mg) for 70kg." -- red block - **Allergy Alert:** "Allergic to Penicillin. Amoxicillin is penicillin-class." -- red block, override required ### Web: Modal Order Form ```html ``` ### Android: Bottom Sheet Order Form ```kotlin @OptIn(ExperimentalMaterial3Api::class) @Composable fun OrderEntryBottomSheet(patient: Patient, onDismiss: () -> Unit, onSubmit: (Order) -> Unit) { var selectedType by remember { mutableStateOf(OrderType.LAB) } var searchQuery by remember { mutableStateOf("") } var cdsAlerts by remember { mutableStateOf(emptyList()) } ModalBottomSheet(onDismissRequest = onDismiss) { Column(Modifier.padding(16.dp)) { Text("New Order", style = MaterialTheme.typography.titleLarge) SingleChoiceSegmentedButtonRow(Modifier.fillMaxWidth()) { OrderType.entries.forEachIndexed { index, type -> SegmentedButton(selected = selectedType == type, onClick = { selectedType = type }, shape = SegmentedButtonDefaults.itemShape(index, OrderType.entries.size) ) { Text(type.label) } } } OutlinedTextField(searchQuery, { searchQuery = it }, label = { Text("Search ${selectedType.label}") }, leadingIcon = { Icon(Icons.Default.Search, null) }, modifier = Modifier.fillMaxWidth()) cdsAlerts.forEach { CdsAlertBanner(it) } Spacer(Modifier.height(16.dp)) Button({ /* validate and submit */ }, Modifier.fillMaxWidth()) { Text("Place Order") } } } } @Composable fun CdsAlertBanner(alert: CdsAlert) { val (bgColor, icon) = when (alert.severity) { CdsSeverity.BLOCKING -> Color(0xFFDC2626).copy(0.1f) to Icons.Default.Block CdsSeverity.WARNING -> Color(0xFFD97706).copy(0.1f) to Icons.Default.Warning CdsSeverity.INFO -> Color(0xFF0284C7).copy(0.1f) to Icons.Default.Info } Surface(color = bgColor, shape = RoundedCornerShape(8.dp), modifier = Modifier.fillMaxWidth().padding(vertical = 4.dp)) { Row(Modifier.padding(12.dp), verticalAlignment = Alignment.CenterVertically) { Icon(icon, null); Spacer(Modifier.width(8.dp)) Column { Text(alert.title, fontWeight = FontWeight.Bold) Text(alert.message, style = MaterialTheme.typography.bodySmall) } } } } ``` ## Cross-Workflow Rules - **Audit Logging:** Every clinical action (vital entry, med admin, note signing, order placement) must log user ID, timestamp, patient MRN, and action type. - **Confirmation Dialogs:** All medication, order, and signature actions require explicit confirmation. Never auto-submit. - **Allergy Visibility:** Patient allergy banner must remain visible across all workflow screens. - **Offline Support:** Vital signs entry and clinical notes must support offline drafting with sync-on-reconnect. - **Session Timeout:** 15-minute inactivity timeout. Save drafts before logout. Re-authenticate to resume. - **Accessibility:** Keyboard navigation for all forms. Color-coded statuses must include text labels. Touch targets minimum 48x48dp (Android).