HEP Builder (Beta)¶
Overview¶
The HEP Builder is a full-screen replacement for the classic plan-creation and plan-edit modals. The goal it was designed against: as fast as writing a program on paper, as structured as software — a therapist should be able to say "this one a couple of times a week, this one every day, twice a day" without fighting a per-day grid, while keeping everything the platform gives them (multi-session days, video demos, the exercise library, RTM time tracking).
It is currently opt-in and off by default. A "New plan builder (beta)" checkbox sits above the patient table on the doctor dashboard; ticking it swaps both the create and the edit flow over to the builder. The preference is stored per browser (localStorage key oktapt.useNewBuilder) and syncs across tabs. The classic InitialPlanCreationModal and EditPlanModal are untouched and remain the default path — see Patient Workout Management for those.
The central architectural decision: cadence is a projection, not storage. Plans are still stored as a day-indexed map of workouts plus extra sessions. The builder derives "prescription rows" from that map on the fly and translates row edits back into map mutations. Full mechanics in the detailed change doc.
Since 2026-09-29 the week the builder saves is also what the plan repeats: the builder loads and saves Day 1–7 only, and the patient's stored plan schedule, read off those days, carries that week forward. There is no schedule control in the builder.
The Three Views¶
A segmented control in the header switches between them.
List view¶
The prescription view, and the one that makes the builder fast. Exercises are grouped into colour-coded blocks — Warm-up, Main, Supplemental, Cool-down — with one card per prescription rather than one card per day. Empty blocks are hidden until a drag is in flight, at which point they become drop targets. Dragging a card between blocks re-blocks it; dragging within a block reorders it.
Week view¶
Seven day rows (Day 1 is today), each with AM / PM / third-session lanes and compact exercise chips. Exercises are dropped from the palette straight into a specific lane, and a chip expands in place to edit its dose.
Split view¶
On screens 1536px and wider, List and a live Week mirror sit side by side — edit a prescription on the left, watch the week update on the right. This is the default at that width, falling back to the List/Week toggle on narrower screens.
Prescription Rows¶
A row represents every instance of an exercise across the week that shares an identical prescription. Change the dose on the row and every scheduled instance changes with it. If two instances of the same exercise genuinely differ — say 3×10 on Monday and 2×15 on Thursday — they simply appear as two rows; there is no special "split" mode to enter or leave.
Each row card carries:
- Dose —
Sets × RepsorSets × Time, with large steppers whose values can also be typed directly. A compact Reps/Time toggle switches the exercise type, defaulting to 10 reps or 30 seconds. Where the clinic has the workout logbook, Vary by set gives each set its own target: a number, a range (from–to) or Max; for a timed exercise, its own seconds. The row then reads "3 × 12 / 10 / 8", and the Reps (or Time) box reads By set while it is on. Each set is a small card in a row that wraps (three to a row here), with + Set as the last, dashed card; a set can carry its own weight once the exercise has one, or after + weight under the cards, and the row's detail line shows the range ("20–25 kg"). Rows whose sets differ are different prescriptions, so they stay separate rows. - Cadence — a preset control (Daily / Weekdays / M·W·F / Custom) sitting above seven day dots labelled with the real, locale-aware weekday letters for that plan's dates. Presets are computed from actual weekdays, and the active preset is detected from the row's day set, so a row set up by hand snaps to "Weekdays" if that is what it amounts to. Choosing Custom reveals the dots inline; otherwise they stay tucked under Advanced.
- Sessions per day — 1× / 2× / 3× per day. When a row's sessions-per-day differ across days, the control renders as mixed with nothing selected; one tap unifies it. The first session of a multi-session day is auto-named "AM".
- Note — + Note expands an inline textarea for the pre-exercise doctor note.
- Advanced — weight (in the clinic's unit: kg for OKTA clinics, lbs for OTERA clinics; By set when the sets' own weights differ), block assignment, 0–10 dot scales for target pain and target exertion (tap the active dot again to clear), and since 2026-10-26 the rest of the prescription: Hold (seconds at the end of each rep), Rest (seconds between sets), Side (left, right, each, alternating), Tempo, Distance (metres or feet by clinic), As needed and Every N hours. The row reads them under the dose ("hold 5 s · left side"), and so does the patient, on every screen and in the PDF.
Block notes¶
With the workout logbook on, each block header in the List view carries Note for this block — a line the patient reads above the block ("Warm up until you feel warm, no pain"). It is written once and stored on every session of the week that has an exercise in that block; when the days disagree the field says Differs by day, and saving it applies the note to all of them. A session that gains its first exercise of a block later does not pick the note up until it is saved again.
Adding Exercises¶
A persistent exercise palette runs down the side of the builder — the library is fetched once and filtered client-side by search text, scope (all / mine / favourites), and tags, using the same tag modal as the main exercise library. It also offers AI search, with AI matches ringed in the brand colour, and a Templates button that injects a saved workout template as daily rows. With the hepSmartSearch tenant flag a search by meaning runs as you type (its first answer pinned on top, later finds at the end) and the on-demand search is labelled Extended search. Since 2026-10-06 the palette and the exercise library page share the text matcher (lib/utils/exerciseSearch.ts) and one result order: Extended-search matches, the smart search's first answer, the regular results, then the smart search's later finds (buildExerciseDisplayItems in lib/utils/exercise.ts). Only the search box and the "Adding to…" strip stay pinned: the scope buttons, Templates and Filter by tags (one row of small buttons), the chosen tags and the AI results line are the grid's header and scroll away with the cards, so a short laptop window keeps two rows in view.
Where an exercise lands depends on where you are:
- List view — added daily, with a toast confirming "Added X — daily".
- Week view — clicking adds to the selected day; dragging drops into the exact lane, with a toast like "Added X → Wed · PM".
- Armed targets — clicking an empty day or + session arms that lane as a one-shot destination. The palette shows an "Adding to Wed · PM ✕" chip, and the next exercise lands there. On mobile, arming also opens the palette sheet.
Small accelerators: search autofocuses, Enter runs the AI search (Extended search where hepSmartSearch is on; adding is always a click or a drag, never a keystroke), and newly added items flash and scroll into view.
Copying a Day¶
Each week-view day row has a Copy to… control — multi-select the days to copy into. Days that already have exercises are flagged with a warning, because copying replaces the target day rather than merging into it; that warning doubles as the confirmation. Days whose workouts the patient has already started are locked out as destinations, though copying from a started day is allowed.
Sessions that are already in the past are copied from outside the builder: Repeat on a past session in the patient popup's Workouts tab offers the same week, locks and warning, and copies only the prescription. See Repeating a past session.
Locking¶
In edit mode, days the patient has already started are read-only. Any row touching one shows a lock chip and has its drag, remove, and edit controls disabled, and every cadence mutation skips locked days.
AI Assistant¶
Where the clinic has the planAssistant flag, the column is the plan assistant: the therapist says what should change and it happens on the canvas as they watch, one change at a time, with Undo; the canvas is never taken over, and a first week can be built from the notes the same way. The rest of this section describes the older column, which clinics without the flag keep.
An AI column docks on the right. It is seeded from the AI-notes step — the doctor's notes, any attachments, and the generated plan summary — and, where the clinic has guidelines enabled, shows Guidelines the AI will use: the documents that apply to the patient, pre-selected, with the reason for each (see Guidelines System). While generating, it takes over the canvas with a streaming preview; when it proposes changes, the canvas shows an old→new comparison to accept or reject. Accepted days are written into the plan, skipping any started days.
The create flow always begins at the AI-notes step, but Skip & create manually opens the builder blank — AI assists and never blocks.
RTM time tracking runs while the builder is open, recording initial-plan-creation or edit-plan time exactly as the classic modals do.
Key Files¶
| File | Role |
|---|---|
OktaPT-FE/components/plan-creation/hep-builder/HepBuilder.tsx |
Orchestrator — shell, view switching, drag routing, palette wiring, armed targets, AI column, save/discard, RTM |
OktaPT-FE/components/plan-creation/hep-builder/ListView.tsx |
Block sections as sortable/droppable containers |
OktaPT-FE/components/plan-creation/hep-builder/PrescriptionRowCard.tsx |
The prescription row card and all its controls |
OktaPT-FE/components/plan-creation/hep-builder/WeekView.tsx |
Seven-day grid; compact variant for the split mirror |
OktaPT-FE/components/plan-creation/hep-builder/DayRow.tsx |
One day — header, lock state, copy-to, session lanes |
OktaPT-FE/components/plan-creation/hep-builder/SessionLane.tsx |
A droppable (day, session) lane with sortable chips |
OktaPT-FE/components/plan-creation/hep-builder/WeekExerciseCard.tsx |
Week chip with inline dose editing and cadence badge |
OktaPT-FE/components/plan-creation/hep-builder/CopyDayPopover.tsx |
Multi-day copy picker with replace warning |
OktaPT-FE/components/plan-creation/hep-builder/ExercisePalette.tsx |
Persistent library — search, scope, tags, AI search, templates |
OktaPT-FE/components/plan-creation/hep-builder/controls/ |
DoseControl, Stepper, Segmented, DayDots, AmPmToggle, NoteExpander, ScalePicker |
OktaPT-FE/lib/hooks/useCadenceRows.ts |
Translation layer between rows and the canonical workout maps |
OktaPT-FE/lib/utils/cadence/deriveRows.ts |
Pure projection from maps to prescription rows |
OktaPT-FE/lib/utils/cadence/groupKey.ts |
The editable-field identity that decides what merges into one row |
OktaPT-FE/lib/utils/cadence/weekdays.ts |
Weekday/date formatting, cadence presets, preset detection |
OktaPT-FE/lib/utils/cadence/fromAiPlan.ts |
AI plan → canonical maps, preserving per-day assignment |
OktaPT-FE/lib/hooks/useNewBuilderPref.ts |
The beta preference and its cross-tab sync |
OktaPT-FE/pages/doctor/dashboard.tsx |
Beta toggle and the branch between builder and classic modals |
OktaPT-FE/components/RequestConnection.tsx |
Same branch in the patient onboarding entry point |