Dose-Pattern Visualization System
12 dose-pattern kinds with auto-detection + rich bilingual visualizations in the order dialog summary card.
Overview
The Dose Pattern Visualization System is a comprehensive medication scheduling visualization engine built into the medOS order dialog. It automatically detects and renders rich, interactive visualizations for 12 distinct dose-pattern kinds, covering the full range of clinical medication administration patterns — from simple fixed schedules to complex PCA (patient-controlled analgesia) and sliding scale protocols.
The system is fully bilingual (Thai + English) and integrates into the existing SummaryCard component within the dynamic scheduler, providing clinicians with an immediate visual summary of how a medication will be administered.
Architecture
Order Data (frequency, route, priority, infusionRate, pcaConfig, ...)
│
▼
┌─────────────────────────┐
│ detectDosePattern() │ ← Priority-based pattern detection engine
│ (dosePatternKind.ts) │
└────────┬────────────────┘
│ DetectedPattern { kind, adminProfile, doseDerivation, params }
▼
┌─────────────────────────┐
│ PatternBadge │ ← Color-coded chip with bilingual label
│ + Visualization │ ← Pattern-specific rich visualization
│ (TimePerDay.tsx) │
└─────────────────────────┘
Key Files
| File | Purpose |
|---|---|
packages/ui-kit/src/components/dynamic-scheduler-2/summary-container/dosePatternKind.ts |
Type definitions, detection engine, metadata registry |
packages/ui-kit/src/components/dynamic-scheduler-2/summary-container/TimePerDay.tsx |
All 12 visualization components + PatternBadge + PatternChip |
packages/ui-kit/src/components/dynamic-scheduler-2/summary-container/SummaryCard.tsx |
Parent container — passes orderData to TimePerDay for detection |
The 12 Dose-Pattern Kinds
1. Fixed Schedule (fixed)
Use case: Standard scheduled medications (TID, QID, BID, etc.)
Visual: Circadian clock with sun/moon/dawn/dusk icons and green dose dots at each administration hour.
| Property | Value |
|---|---|
| Badge color | Green (#1b5e20 on #e8f5e9) |
| Icon | schedule |
| Detection | frequency.timePerDay > 0 (fallback) |
| Label TH | ตารางคงที่ |
| Label EN | Fixed Schedule |
How it renders:
- Parses the RRULE string to extract
BYHOURvalues - Maps each hour to a time-of-day period (night 0-5, morning 6-11, afternoon 12-17, evening 18-23)
- Renders icons: moon, sun, sunset, night for each period
- Green filled dots mark administration times; gray dots fill the rest of the 12-dot row per period
Example data:
frequency: {
timePerDay: 3,
abbrName: 'TID',
rruleString: 'FREQ=DAILY;BYHOUR=8,12,18'
}
2. PRN — As Needed (prn)
Use case: Pain relief, anti-emetics, and other symptom-triggered medications.
Visual: Chip flow: PRN → Indication → Min Interval with arrow connectors.
| Property | Value |
|---|---|
| Badge color | Orange (#e65100 on #fff3e0) |
| Icon | notifications_active |
| Detection | frequency.timePerDay === 0 or frequency.type === 'prn' or /prn/i in abbreviation |
| Label TH | เมื่อจำเป็น |
| Label EN | PRN (As Needed) |
Features:
- Auto-maps medication use-time codes to Thai/English indications (e.g.,
SP4= ปวด/Pain,SN2= คลื่นไส้/Nausea) - Shows minimum interval between doses when specified
- 14 indication codes mapped: cough, constipation, dyspnea, nausea, nasal congestion, pain, headache/fever
Example data:
frequency: {
timePerDay: 0,
type: 'prn',
abbrName: 'PRN q4hr',
medUseTime: 'SP4', // → Pain
typeMedUseTime: '4' // → every 4 hours minimum
}
3. One-Time / Bolus (one_time)
Use case: Stat doses, single administrations, pre-procedure medications.
Visual: STAT chip → Administration Profile chip (IV Push, Slow Push, Oral, etc.)
| Property | Value |
|---|---|
| Badge color | Purple (#4a148c on #f3e5f5) |
| Icon | bolt |
| Detection | Priority = 'Stat' or /stat|once/i in abbreviation |
| Label TH | ครั้งเดียว |
| Label EN | One-Time / Bolus |
Administration profiles detected:
push— IV Push (seconds)slow_push— IV Slow Push (1-5 min)short_infusion— Short Infusion (15-60 min)oral— Oral single doseother— Single administration
Example data:
frequency: { timePerDay: 1, abbrName: 'Stat' },
orderData: { priority: { value: 'Stat' }, route: 'IV' }
4. Continuous Infusion (continuous)
Use case: IV drips, constant-rate infusions (vasopressors, sedation, TPN).
Visual: Continuous → Rate → Duration chips with animated blue drip progress bar.
| Property | Value |
|---|---|
| Badge color | Blue (#01579b on #e1f5fe) |
| Icon | water_drop |
| Detection | infusionRate > 0 + IV route, or keyword match (drip|infusion|continuous) |
| Label TH | หยดต่อเนื่อง |
| Label EN | Continuous Infusion |
Features:
- Animated CSS gradient simulates flowing drip
- Shows rate, unit, and duration when available
- Calculates: Qty = rate x duration
Example data:
orderData: {
route: 'IV drip',
infusionRate: 125,
infusionRateUnit: 'mL/hr',
infusionDuration: 480
}
5. Taper (taper)
Use case: Steroid tapers, antibiotic step-down, gradual dose reduction.
Visual: Taper chip + step count chip + descending bar chart (orange, fading opacity).
| Property | Value |
|---|---|
| Badge color | Orange (#e65100 on #fff3e0) |
| Icon | trending_down |
| Detection | taperSteps array present, or keyword match (taper|ลดขนาด|step.?down|wean) |
| Label TH | ลดขนาดยา |
| Label EN | Taper |
Features:
- Each bar represents a taper step with proportional height
- Hover tooltip shows dose and duration per step
- Interactive hover highlights individual steps
- Summary shows start→end dose and total duration
Example data:
orderData: {
taperSteps: [
{ day: 1, dose: 60, unit: 'mg' },
{ day: 3, dose: 40, unit: 'mg' },
{ day: 5, dose: 20, unit: 'mg' },
{ day: 7, dose: 10, unit: 'mg' },
]
}
6. Titration (titration)
Use case: Insulin dose adjustments, blood pressure medications, pain management.
Visual: Titration → ปรับตามผล (adjust by result) chips + ascending bar chart (blue).
| Property | Value |
|---|---|
| Badge color | Blue (#1565c0 on #e3f2fd) |
| Icon | trending_up |
| Detection | Explicit dosePattern: 'titration' or keyword match (titrat|ไตเตรท|adjust|ปรับขนาด) |
| Label TH | ไตเตรท |
| Label EN | Titration |
Features:
- 7-step ascending visualization showing dose escalation concept
- Hover tooltip per step
- Complementary to Taper (ascending vs descending)
7. Sliding Scale (sliding_scale)
Use case: Insulin sliding scale, heparin protocols.
Visual: Sliding Scale chip + Glucose/Dose data table.
| Property | Value |
|---|---|
| Badge color | Purple (#6a1b9a on #f3e5f5) |
| Icon | table_chart |
| Detection | slidingScaleTable array present, or keyword match (sliding.?scale) |
| Label TH | Sliding Scale |
| Label EN | Sliding Scale |
Features:
- Compact 2-column grid table (Glucose range → Dose)
- Purple-highlighted dose cells for non-zero doses
- Falls back to default 6-tier insulin scale if no data provided
Example data:
orderData: {
slidingScaleTable: [
{ rangeLow: 150, rangeHigh: 200, dose: 2, unit: 'units' },
{ rangeLow: 201, rangeHigh: 250, dose: 4, unit: 'units' },
{ rangeLow: 251, rangeHigh: 300, dose: 6, unit: 'units' },
{ rangeLow: 301, rangeHigh: 350, dose: 8, unit: 'units' },
]
}
8. Cyclic / Pulse (cyclic)
Use case: Chemotherapy cycles, pulse steroids, on/off antibiotic schedules.
Visual: Cyclic + Xd on / Yd off chips + striped on/off bar visualization.
| Property | Value |
|---|---|
| Badge color | Teal (#00695c on #e0f2f1) |
| Icon | loop |
| Detection | cycleDaysOn && cycleDaysOff present, or keyword match (cycl|pulse|on.?off) |
| Label TH | รอบ/พัลส์ |
| Label EN | Cyclic / Pulse |
Features:
- Renders 3 complete cycles as a bar chart
- Green bars = treatment days; gray/light bars = rest days
- Hover tooltip shows cycle number and day number
- Summary calculates total treatment duration
Example data:
orderData: {
cycleDaysOn: 21,
cycleDaysOff: 7
}
// → "21 days on, 7 days off — repeat 3 cycles (84 days total)"
9. Loading + Maintenance (loading_maintenance)
Use case: Antibiotics (vancomycin), anticoagulants (heparin), antiarrhythmics (amiodarone).
Visual: Loading → Maintenance chips + split bar (small loading block + large maintenance block).
| Property | Value |
|---|---|
| Badge color | Red-brown (#bf360c on #fbe9e7) |
| Icon | rocket_launch |
| Detection | loadingDose && maintenanceDose present, or keyword match (loading|โหลด) |
| Label TH | โหลด+บำรุงรักษา |
| Label EN | Loading + Maintenance |
Features:
- Split bar: dark purple loading portion + light green maintenance portion
- Shows actual doses in each segment
- Calculates: Qty = loading + (maintenance x freq x days)
Example data:
orderData: {
loadingDose: 500,
maintenanceDose: 250
}
10. Bolus + Infusion (bolus_infusion)
Use case: tPA for stroke, heparin bolus+drip, antibiotics with loading push.
Visual: Bolus → Drip Rate chips + split bar (BOLUS block + infusion block).
| Property | Value |
|---|---|
| Badge color | Indigo (#283593 on #e8eaf6) |
| Icon | bolt |
| Detection | infusionRate > 0 + IV route + Stat priority or bolus keyword |
| Label TH | โบลัส+หยด |
| Label EN | Bolus + Infusion |
Features:
- Split bar visualization: compact dark “BOLUS” segment + wide light “rate” segment
- Shows infusion rate and unit
- Calculates: Qty = bolus + (rate x duration)
Example data:
orderData: {
route: 'IV',
method: 'bolus',
infusionRate: 50,
infusionRateUnit: 'mL/hr',
priority: { value: 'Stat' }
}
11. PCA — Patient-Controlled Analgesia (pca)
Use case: Post-operative pain management, cancer pain, severe acute pain.
Visual: PCA chip + 4-quadrant config card (Basal Rate, Demand Dose, Lockout, 1-hr Cap).
| Property | Value |
|---|---|
| Badge color | Pink (#ad1457 on #fce4ec) |
| Icon | touch_app |
| Detection | pcaConfig object present, or keyword match (pca|patient.controlled) |
| Label TH | PCA |
| Label EN | Patient-Controlled Analgesia |
Features:
- 2x2 grid showing all PCA parameters at a glance
- Fields: Basal Rate, Demand Dose, Lockout interval, 1-hour Cap
- Calculates: Qty = (basal + max demand/hr) x duration
Example data:
orderData: {
pcaConfig: {
demand: 1,
lockout: 8,
basal: 0.5,
hourLimit: 10,
unit: 'mg'
}
}
12. Range Order (range)
Use case: Nurse-titrated analgesics, sedation ranges, PRN with dose discretion.
Visual: Range → Min–Max chips + gradient bar from min to max.
| Property | Value |
|---|---|
| Badge color | Green (#33691e on #f1f8e9) |
| Icon | swap_horiz |
| Detection | doseMin != null && doseMax != null && doseMin !== doseMax |
| Label TH | ช่วงขนาดยา |
| Label EN | Range Order |
Features:
- Green gradient bar spanning min→max range
- Labels at each end show the boundaries
- Summary explains nurse decision authority and dispensing max
Example data:
orderData: {
doseMin: 2,
doseMax: 5
}
// → "Nurse picks 2–5mg per dose — dispense for max (5mg)"
Detection Priority Chain
The detectDosePattern() function uses a priority-based chain to resolve the most specific pattern from order data. Earlier matches take precedence:
1. Explicit dosePattern field → Use as-is
2. PCA config / keywords → pca
3. Sliding scale table / keywords → sliding_scale
4. Loading + maintenance doses → loading_maintenance
5. Taper steps / keywords → taper
6. Min/Max dose range → range
7. Cycle on/off days / keywords → cyclic
8. IV infusion rate + bolus → bolus_infusion
9. IV infusion rate alone → continuous
10. Drip/infusion keywords → continuous
11. Titration keywords → titration
12. Taper keywords → taper
13. Cyclic keywords → cyclic
14. Loading keywords → loading_maintenance
15. Bolus keywords → one_time (with push profile)
16. Stat/once priority → one_time
17. PRN frequency → prn
18. timePerDay > 0 → fixed
19. Fallback → fixed
Type System
Core Types
type DosePatternKind =
| 'fixed' | 'prn' | 'one_time' | 'continuous'
| 'taper' | 'titration' | 'sliding_scale' | 'cyclic'
| 'loading_maintenance' | 'bolus_infusion' | 'pca' | 'range';
type AdministrationProfile =
| 'push' | 'slow_push' | 'short_infusion'
| 'infusion' | 'drip' | 'oral' | 'other';
type DoseDerivation =
| 'literal' | 'weight_based' | 'bsa_based'
| 'renal_adjusted' | 'age_based' | 'protocol_lookup';
interface DetectedPattern {
kind: DosePatternKind;
adminProfile?: AdministrationProfile;
doseDerivation?: DoseDerivation;
params: Record<string, any>;
}
Metadata Registry
Each pattern kind has a DosePatternMeta entry with:
label— Bilingual name ({ th, en })color/bgcolor/borderColor— Visual themeicon— Material icon namedescription— Bilingual tooltip description
Access via: DOSE_PATTERN_META[kind]
Shared UI Components
PatternBadge
Renders the detected pattern’s bilingual label as a color-coded MUI Chip using metadata from DOSE_PATTERN_META.
<PatternBadge kind="pca" />
// → Pink chip: "Patient-Controlled Analgesia"
PatternChip
Lower-level chip with optional icon, used to build chip-flow visualizations.
<PatternChip
icon={<BoltIcon />}
label="STAT"
color="#4a148c"
bgcolor="#f3e5f5"
borderColor="#ce93d8"
/>
Arrow
Simple directional connector between chips: →
PatternLabel
Caption-style description text below the visualization.
Integration Guide
How it works in the Order Dialog
- User selects a medication in the order list
- The order edit dialog (
DialogNewEditOrder2026.tsx) rendersSummaryCard SummaryCardpasses the full order object asorderDatatoTimePerDayTimePerDaycallsdetectDosePattern(orderData)to resolve the pattern kind- The matching visualization component renders automatically
Adding a new pattern kind
- Add the kind to
DosePatternKindunion indosePatternKind.ts - Add metadata to
DOSE_PATTERN_META(label, colors, icon, description) - Add detection logic in
detectDosePattern()at the appropriate priority level - Create a visualization component in
TimePerDay.tsx(follow existing patterns) - Wire it into the
Schedulecomponent’s switch/if chain
Testing in the sandbox
A standalone test target renders all 12 patterns side-by-side:
cd web
pnpm dev:sandbox
# Open: http://localhost:5179/?target=DosePattern
The target is at sandbox/targets/DosePatternTarget.tsx and can be extended with additional test scenarios.
Route & Injection Detection
The system automatically detects IV and injection routes to determine administration profiles:
IV Routes: iv, intravenous, iv push, iv drip, iv infusion, ivpb, iv piggyback
Injection Routes: im, intramuscular, sc, subcutaneous, subcut, sq, id, intradermal
Keyword Detection (Bilingual)
Pattern detection supports Thai and English keywords:
| Pattern | English | Thai |
|---|---|---|
| Continuous | drip, infusion, continuous |
หยดต่อเนื่อง, ให้ทางสายน้ำเกลือ |
| Titration | titrat, adjust |
ไตเตรท, ปรับขนาด |
| Taper | taper, step down, wean |
ลดขนาด |
| Cyclic | cycl, pulse, on/off |
รอบ |
| Loading | loading |
โหลด |
| Bolus | bolus |
โบลัส |
| Stat | stat, once |
ครั้งเดียว |
Design Tokens
| Token | Value | Usage |
|---|---|---|
| Chip font weight | 600 | All PatternChip labels |
| Chip font size | 12px | Small chips |
| Bar chart height | 40-48px | Taper, titration, cyclic bars |
| Drip bar height | 8px | Continuous infusion progress |
| Grid gap | 1px | Sliding scale table, split bars |
| Border radius | 4px (bars), 1px (tables) | Consistent rounding |
| Transition | 300ms ease | Bar hover effects |
Color Palette by Pattern
| Kind | Primary | Background | Border |
|---|---|---|---|
| fixed | #1b5e20 |
#e8f5e9 |
#a5d6a7 |
| prn | #e65100 |
#fff3e0 |
#ffcc80 |
| one_time | #4a148c |
#f3e5f5 |
#ce93d8 |
| continuous | #01579b |
#e1f5fe |
#81d4fa |
| taper | #e65100 |
#fff3e0 |
#ffcc80 |
| titration | #1565c0 |
#e3f2fd |
#90caf9 |
| sliding_scale | #6a1b9a |
#f3e5f5 |
#ce93d8 |
| cyclic | #00695c |
#e0f2f1 |
#80cbc4 |
| loading_maintenance | #bf360c |
#fbe9e7 |
#ffab91 |
| bolus_infusion | #283593 |
#e8eaf6 |
#9fa8da |
| pca | #ad1457 |
#fce4ec |
#f48fb1 |
| range | #33691e |
#f1f8e9 |
#aed581 |