Skip to content

Patient Workout Management (Therapist Perspective)

Overview

The Okta-Health system provides physical therapists with comprehensive tools to create, edit, and manage patient workout plans. This document covers all therapist-side workout management operations from the doctor dashboard.

Key Concepts

Concept Description
Workout A single exercise session scheduled for a specific date
Session An additional workout on the same day — a day can hold up to 3 sessions when the multipleSessionsPerDay flag is on (see Multiple Sessions Per Day)
Exercise An individual movement within a workout (reps-based or time-based)
Plan A collection of workouts spanning multiple days; the editors work on one week at a time (Day 1 = today) and the schedule carries it forward
Template A reusable workout configuration that can be applied to any patient
Exercise Block Workout structure category: WARM_UP, MAIN_SESSION, SUPPLEMENTAL_WORK, COOL_DOWN
Schedule Which days a patient's sessions fall on and what happens when the plan runs out or a session is missed — see Plan Scheduling

Creating Patient Workouts

A newer builder exists in beta

The HEP Builder is an opt-in, full-screen replacement for both the Initial Plan Creation Modal and the Edit Plan Modal, enabled per browser from the "New plan builder (beta)" checkbox on the doctor dashboard. It is off by default, and the flows documented on this page remain the default path for everyone who hasn't turned it on. Both write to the same underlying workout records.

Initial Plan Creation (New Patient)

For new patients, therapists create an initial multi-day plan using the Initial Plan Creation Modal.

Component: OktaPT-FE/components/plan-creation/InitialPlanCreationModal.tsx State Hook: OktaPT-FE/lib/hooks/useInitialPlanState.ts AI Generation Hook: OktaPT-FE/lib/hooks/useAIInitialPlanGeneration.ts

API Endpoint Method Auth Purpose
/v2/doctor/create-initial-plan POST Required Create initial patient plan

Features: - Days 1–7 scheduling with day picker (PlanDaySelector.tsx); Day 1 is today. Each day is a tile with its weekday and date and how many exercises it has (or Add when it has none); the day being edited is ringed in the accent, a day the patient already started says In progress and cannot be opened - Copy day functionality (duplicate exercises to another day): the copy icon in the corner of an empty tile - Clear day functionality (remove all exercises from a day): the trash icon in the corner of a filled tile - AI-generated plan support with manual customization; an AI plan says Written by AI under the title - Exercise block organization (warm-up, main session, etc.)

Look (redesigned 2026-10-21 on the design system): the plan preview is one card per day with the exercises as rows (picture, name, dose, weight, block, the note to the patient on one line); a day with several sessions lists them as sections of its card with Edit and remove per session. Opening a day shows the day's date in the title and its exercises as rows: picture and name, the dose as steppers (sets × reps or seconds, with the Reps/Time toggle and the weight when there is one), + Note for the instructions to the patient, and More for the weight, the block and the pain and effort targets; Add exercise opens the library picker and Add from template a saved template. The same rows are used by Edit session on a planned session and by Create session; leaving any of them with unsaved changes asks first.

Request Body:

{
  "patientId": 123,
  "startDate": "2024-01-15",
  "endDate": "2024-01-24",
  "schedule": { "kind": "WEEKLY", "weekdays": [1, 3, 5] },
  "autoContinue": true,
  "workouts": [
    {
      "dateScheduled": "2024-01-15",
      "exercises": [
        {
          "exerciseId": 1,
          "exerciseType": "REPS_BASED",
          "sets": 3,
          "reps": 10,
          "exerciseBlock": "MAIN_SESSION"
        }
      ]
    }
  ]
}

Single Workout Creation

For ad-hoc workouts outside of the initial plan flow.

Component: OktaPT-FE/components/WorkoutCreationModal.tsx

API Endpoint Method Auth Purpose
/create-workout POST Required Create single workout

Backend Location: OktaPT-API/src/routes/legacy.ts

Request Body:

{
  "patientId": 123,
  "dateScheduled": "2024-01-20",
  "exercises": [
    {
      "exerciseId": 1,
      "exerciseType": "REPS_BASED",
      "sets": 3,
      "reps": 12,
      "weight": 10,
      "exerciseBlock": "MAIN_SESSION",
      "doctorNotesPreExercise": "Focus on form"
    }
  ]
}

Multiple Sessions Per Day

When the multipleSessionsPerDay tenant feature flag is enabled, therapists can schedule more than one session on a single day of a plan — for example a morning mobility routine and an evening strengthening block. The cap is MAX_SESSIONS_PER_DAY = 3 (one primary session plus up to two extras).

Each session is independently named, collapsible, editable, removable, and reorderable in the plan editor. Adding sessions works the same way in both the Initial Plan Creation Modal and the Edit Plan Modal.

Components & State:

File Role
OktaPT-FE/components/plan-creation/DayWorkoutPreview.tsx Renders a day as a card with its session (or sessions as sections); add / rename / edit / remove session controls
OktaPT-FE/components/ExerciseSelector/SelectedExerciseCard.tsx One exercise row of the day editor, Edit session and Create session: dose steppers, note, and weight / block / targets under More (the controls come from components/plan-creation/hep-builder/controls/)
OktaPT-FE/components/plan-creation/EditPlanModal.tsx Gated by multipleSessionsPerDay; wires onAddSession / onEditSession / onRemoveSession / onRenameSession
OktaPT-FE/components/plan-creation/InitialPlanCreationModal.tsx Same gating and session controls for new patients
OktaPT-FE/lib/hooks/useEditPlanState.ts, useInitialPlanState.ts Hold the primary workouts map plus an extraSessions map (sessions 2–3 per day) and the MAX_SESSIONS_PER_DAY constant

Data Model: No schema change is needed. Extra sessions are simply additional Workout rows sharing the same dateWorkoutCurrentlyScheduledFor. Same-day sessions are returned in creation order — patient upcoming workouts are ordered by (dateWorkoutCurrentlyScheduledFor ASC, id ASC) (see OktaPT-API/src/routes/workouts/patientUpcoming.ts), so the id tiebreak keeps sessions in the order they were added.

Days that already started keep the same protection described under Workout Locking — a session that's in progress can't be modified.

Editing Patient Workouts

Bulk Plan Editing (Edit Plan Modal)

Edit upcoming workouts across the coming week (Day 1 = today).

Component: OktaPT-FE/components/plan-creation/EditPlanModal.tsx State Hook: OktaPT-FE/lib/hooks/useEditPlanState.ts

API Endpoint Method Auth Purpose
/v2/doctor/edit-plan PUT Required Bulk edit patient plan

Backend Location: OktaPT-API/src/routes/doctor/controller.ts:1185 (editPlan function)

Key Constraint: Workouts started within the last hour are protected and cannot be modified. This prevents disrupting patients who are actively exercising.

Cross-Therapist Access: Any therapist within the same clinic (tenant) can edit any patient's plan, not just the therapist assigned to that patient. This allows clinics to cover for each other when a therapist is unavailable.

How It Works: 1. Calls POST /v2/doctor/patients/{id}/plan-schedule/prepare-editing, which settles missed sessions and fills the week with the sessions the plan would add anyway, then refetches the patient's workouts 2. Loads them into the day selector for editing 3. On save, deletes non-protected incomplete workouts in the range 4. Creates new workouts from the updated configuration (marked source: CLINICIAN), records the schedule, and drops system-scheduled sessions after the window so they are regenerated from the new plan

The request body accepts the same optional schedule and autoContinue fields as create-initial-plan.

AI-Assisted Plan Editing

Therapists can modify patient workout plans using natural language instructions instead of manually editing each exercise.

Component: OktaPT-FE/components/plan-creation/ai-edit/AIEditPlanPanel.tsx State Hook: OktaPT-FE/lib/hooks/useAIPlanEdit.ts Streaming Hook: OktaPT-FE/lib/hooks/useAIPlanEditStream.ts

API Endpoint Method Auth Purpose
/v2/ai/therapist-plan-edit POST Required Generate AI-modified workout plan

How to Access: 1. Open the Edit Plan Modal for a patient 2. Click the "AI Assistant" button in the modal header 3. The AI chat panel slides in from the right

Quick Actions: The AI panel provides quick action buttons for common modifications: - Make easier - Reduce intensity, sets, reps, or weight - Make harder - Increase difficulty parameters - Remove equipment - Filter out exercises requiring specific equipment - Add warm-up - Insert warm-up exercises at the start - Add cool-down - Append stretching/cool-down exercises

File Attachments in Chat: Therapists can attach files mid-conversation to provide additional context for the AI (e.g., a new MRI scan, updated referral notes). Files can be attached via the paperclip button or by dragging and dropping onto the chat panel. Attachment metadata (filename, type) appears as chips in the sent message bubble. Per-message attachments are sent separately from any initial-plan attachments.

Custom Instructions: Therapists can type natural language instructions such as: - "Remove all dumbbell exercises and replace with bodyweight alternatives" - "Add 5 minutes of stretching to each day" - "Make Monday and Wednesday harder, keep Friday light" - "Focus on lower body strengthening for knee rehab"

Review Workflow:

  1. Streaming Preview: As the AI generates modifications, exercises appear progressively with skeleton loading states
  2. Comparison View: Once complete, a side-by-side comparison shows:
  3. Original exercises on the left
  4. Modified exercises on the right
  5. NEW badge for added exercises
  6. CHANGED badge for modified exercises
  7. Exercise thumbnails for visual reference
  8. Accept/Reject: Therapist can accept all changes, reject all, or selectively approve individual modifications

Patient Context: The AI automatically extracts and saves patient attributes mentioned in instructions (e.g., "patient has knee pain" → saves kneeIssues: true). These attributes persist and inform future AI suggestions.

Key Components:

Component Purpose
AIEditPlanPanel.tsx Main chat interface with message history
AIQuickActions.tsx Quick action button grid
AIPlanComparisonView.tsx Before/after comparison display
AIExerciseCard.tsx Individual exercise card with change indicators
AISkeletonCard.tsx Loading placeholder during streaming

Protected Workout Logic:

const ONE_HOUR_AGO = new Date(Date.now() - 60 * 60 * 1000);

// Only preserve if started within last hour
const startedWorkouts = await tx.workout.findMany({
  where: {
    patientId,
    isWorkoutCompleted: false,
    timeWorkoutStart: {
      not: null,
      gte: ONE_HOUR_AGO,
    },
    dateWorkoutCurrentlyScheduledFor: {
      gte: startDateObj,
      lte: endDateObj,
    },
  },
});

Single Workout Editing

Edit individual workout details from the workout details view.

Component: OktaPT-FE/components/doctor/dashboard/WorkoutDetailsModalV2.tsx

API Endpoint Method Auth Purpose
/workout/{workoutId}/exercises POST Required Update workout exercises

Backend Location: OktaPT-API/src/routes/legacy.ts

Capabilities: - Add/remove/reorder exercises (drag and drop to reorder; exercises automatically reposition when their block type is changed) - Change scheduled date - Modify exercise parameters (sets, reps, duration, weight) - Add pre-exercise doctor notes - Set exercise blocks

Request Body:

{
  "dateScheduled": "2024-01-20",
  "formattedExercises": [
    {
      "exerciseInWorkoutId": 456,
      "exerciseId": 1,
      "exerciseType": "REPS_BASED",
      "sets": 3,
      "reps": 12,
      "exerciseBlock": "MAIN_SESSION"
    }
  ]
}

Adding Doctor Notes

Workout-Level Notes

Add notes that apply to the entire workout session.

Component: OktaPT-FE/components/doctor/dashboard/WorkoutNote.tsx

API Endpoint Method Auth Purpose
/workout/{workoutId}/note PATCH Required Add/update workout note

Backend Location: OktaPT-API/src/routes/legacy.ts

Request Body:

{
  "doctorNote": "Take it easy today, focus on stretching"
}

Constraints: - Maximum 1000 characters

Exercise-Level Post-Notes

Add notes after reviewing patient's exercise performance or video.

API Endpoint Method Auth Purpose
/v2/exercises/exercises-in-workout/{id}/doctor-post-notes PATCH Required Add post-exercise note
/v2/exercises/workouts/{workoutId}/exercises/doctor-post-notes PATCH Required Bulk add post-exercise notes

Backend Location: OktaPT-API/src/routes/exercises/doctorFeedback.ts

Single Exercise Request:

{
  "doctorNotesPostExercise": "Great form! Keep it up"
}

Bulk Request:

{
  "exercises": [
    {
      "exerciseInWorkoutId": 123,
      "doctorNotesPostExercise": "Improve hip mobility"
    }
  ]
}

Deleting/Clearing Workouts

Clear Day (Within Plan Editor)

UI Components: - OktaPT-FE/components/plan-creation/PlanDaySelector.tsx - Day picker with clear button - OktaPT-FE/components/plan-creation/DayButton.tsx - Individual day with trash icon

How It Works: 1. Therapist clicks trash icon on a day card 2. window.confirm() dialog asks for confirmation 3. Workout is removed from local state 4. On plan save, the backend handles deletion

Backend Deletion on Save

When saving an edited plan, the backend:

  1. Identifies workouts started within the last hour (protected)
  2. Deletes ExerciseInWorkout records for non-protected workouts
  3. Deletes Workout records for non-protected workouts
  4. Creates new workouts from the submitted configuration

Deletion Logic (in transaction):

// Delete exercises first (foreign key constraint)
await tx.exerciseInWorkout.deleteMany({
  where: {
    workout: {
      patientId,
      isWorkoutCompleted: false,
      id: { notIn: preservedWorkoutIds },
      dateWorkoutCurrentlyScheduledFor: {
        gte: startDateObj,
        lte: endDateObj,
      },
    },
  },
});

// Then delete workouts
await tx.workout.deleteMany({
  where: {
    patientId,
    isWorkoutCompleted: false,
    id: { notIn: preservedWorkoutIds },
    dateWorkoutCurrentlyScheduledFor: {
      gte: startDateObj,
      lte: endDateObj,
    },
  },
});

Workout Templates

Reusable sessions a therapist can drop into any patient's plan. A therapist sees three kinds in one list, on the Workout Templates page and in the template picker of the plan builders:

  • Ready-made templates (system templates in the code and the admin area) — sessions every clinic has (a knee program after surgery, a low back routine, a balance program and so on), built from the platform exercise library and managed by the platform team in the admin area. They are read-only: opening one shows every exercise with its dose, block and the note the patient would get (each exercise opens its details and video, as in the exercise library), and Copy and edit there gives the therapist a private copy of their own, in their language, to change freely. On the page they sit in their own section as cards (name, category, length, level, a line about the program and its exercises as thumbnails) with category filters.
  • Your templates — the therapist's own, as rows: open one to edit it, or copy and delete it from the row.
  • From your clinic — colleagues' public templates, which open the same way and can be copied.

The template picker of the plan builders lists the same three kinds, each card with the template's first exercises and their doses. When a template goes into a plan, each exercise keeps its type (reps or time), dose (per-set targets included), block and the note to the patient; a Russian-speaking therapist gets the Russian text of a ready-made template.

Backend Location: OktaPT-API/src/routes/workouts/templates.ts (therapist side), OktaPT-API/src/services/systemTemplateService.ts and OktaPT-API/src/routes/admin/systemTemplates.controllers.ts (admin side). Internals: System Templates.

API Endpoint Method Auth Purpose
/v2/workouts/templates GET Required The templates the therapist can use: their own and the clinic's public ones; with ?include=system also the system ones (isSystem: true, nameRu / descriptionRu set). A client that doesn't ask (a web bundle from before system templates) gets none
/v2/workouts/templates POST Required Create new template
/v2/workouts/templates/{id}/copy POST Doctor Copy any visible template into a private one of the therapist's own (per-set targets and block notes included); body { "lang": "en" \| "ru" } picks the language of the copied text
/v2/workouts/templates/{id} PUT Required Update one of the therapist's own templates (every templateExerciseId must belong to it, every exerciseId must be in the clinic's library)
/v2/workouts/templates/{id} DELETE Required Soft delete one of the therapist's own templates (sets isActive: false)
/v2/workouts/templates/{id}/create-workout POST Doctor Create a session from any visible template for one of the doctor's patients (patientId, dateScheduled, optional lang); not used by the apps, which put a template into the plan builder instead

Template Visibility

  • Private templates: Only visible to the creator
  • Public templates: Visible to all therapists in the same clinic (tenant)
  • System templates ("Ready-made templates" in the app): Visible to every therapist in every clinic; edited only in the admin area. The tenant middleware reads them next to the clinic's own rows (WorkoutTemplate.tenantId is NULL), and writes through the tenant client never reach them.

Creating a Template

Create template (and editing one of your own) opens the template editor, full screen like the plan builder: the name, a category and a length (one row on a wide screen) and a description on the left with the exercise rows under them, and the exercise library beside them on a wide screen (on a phone, Add exercises opens it as a sheet). The library is the same picker as the HEP builder's: search by name (the ranked matcher, and search by meaning as you type where the clinic has hepSmartSearch), All / My exercises / Favorites, the tag filter, and Search with AI (or Extended search) at the end of the grid; see exercise-ai-search.md. Only the search box stays pinned: the scope buttons, the tag filter and the AI results line scroll away with the cards, and the grid fits as many cards per row as its column allows (four on a big monitor), so on a 13–14" laptop two rows of cards stay in view with each name and its + together. + on a card adds the exercise to the template without closing anything, the card then shows Added, and a thumbnail or name opens the exercise's details and video. Each row sets the type (reps or time), sets, reps or seconds, the block and a note to the patient; rows keep their block order (warm-up, main session, supplemental, cool-down) and move up or down within their block. A row with different targets per set (see the workout logbook) shows them ("Sets differ: 12 / 10 / 8") and keeps them until its sets, reps or seconds are changed.

The request behind the editor:

{
  "name": "Lower Body Strength",
  "description": "Focus on quad and glute activation",
  "category": "strength",
  "difficultyLevel": "INTERMEDIATE",
  "estimatedDuration": 45,
  "suitableConditions": ["knee_rehab", "hip_mobility"],
  "isPublic": false,
  "exercises": [
    {
      "exerciseId": 1,
      "exerciseType": "REPS_BASED",
      "sets": 3,
      "reps": 10,
      "exerciseBlock": "MAIN_SESSION"
    }
  ]
}

Copying a Template

POST /v2/workouts/templates/{id}/copy with { "lang": "ru" } answers 201 with the new template: the therapist's own (creatorId, their tenant), private, named like the source (a copy of the therapist's own template gets " (copy)" added), with every exercise row and the note in the chosen language. The web app opens the copy in the editor right away.

Creating Workout from Template

{
  "patientId": 123,
  "dateScheduled": "2024-01-20"
}

Key Constraints & Rules

Workout Locking

Condition Can Edit? Rationale
Not started Yes Patient hasn't begun
Started > 1 hour ago Yes Likely abandoned
Started within 1 hour No Patient may be exercising
Completed No Historical record

Plan Edit Window

  • Every plan editor (classic modals, HEP Builder, mobile Manage HEP) edits the coming week: today through six days ahead
  • Past workouts cannot be modified through the plan editor
  • Completed workouts are preserved regardless of date
  • Sessions the system scheduled after the week are dropped on save and regenerated from the saved plan; a clinician's own sessions out there are kept

After the Plan Runs Out

Sessions are added on the patient's schedule when fewer than three session days remain, as copies of the current plan (AI-adapted when the tenant and therapists allow it). Missed sessions move to the next scheduled day in order. The full rules, the data model and the endpoints are in Plan Scheduling.

Exercise Configuration

Required fields for each exercise: - exerciseId - Reference to exercise definition - exerciseType - Either REPS_BASED or TIME_BASED - sets - Minimum 1 (auto-corrected from 0)

Optional fields: - reps - For REPS_BASED exercises - duration - For TIME_BASED exercises (in seconds) - weight - In kilograms - exerciseBlock - Workout structure category - prescribedPainToleranceLevel - 1-10 scale - prescribedPhysicalExertionLevel - 1-10 scale - doctorNotesPreExercise - Instructions for patient

Legacy Code Notes

WorkoutDetailsModal.tsx (Legacy)

Path: OktaPT-FE/components/doctor/dashboard/WorkoutDetailsModal.tsx Status: Superseded by WorkoutDetailsModalV2.tsx

Differences from V2: - Does not support exerciseBlock field in exercise mapping - Uses PatientSummary type instead of PatientSummaryV2 - Action buttons may be in different positions

Recommendation: Use WorkoutDetailsModalV2.tsx for new development.

API Reference Table

Endpoint Method Auth Purpose
/v2/doctor/create-initial-plan POST Required Create initial patient plan
/v2/doctor/edit-plan PUT Required Bulk edit patient plan
/v2/ai/therapist-plan-edit POST Required Generate AI-modified workout plan
/create-workout POST Required Create single workout
/workout/{id}/exercises POST Required Update workout exercises
/workout/{id}/note PATCH Required Add/update workout note
/v2/exercises/exercises-in-workout/{id}/doctor-post-notes PATCH Required Add post-exercise note
/v2/exercises/workouts/{id}/exercises/doctor-post-notes PATCH Required Bulk add post-exercise notes
/v2/workouts/templates GET Required List templates
/v2/workouts/templates POST Required Create template
/v2/workouts/templates/{id} PUT Required Update template
/v2/workouts/templates/{id} DELETE Required Soft delete template
/v2/workouts/templates/{id}/create-workout POST Required Create workout from template

Database Models

Workout

model Workout {
  id                               Int                 @id @default(autoincrement())
  name                             String?
  category                         String?
  doctor                           User                @relation("DoctorWorkouts", ...)
  doctorId                         Int
  patient                          User?               @relation("PatientWorkouts", ...)
  patientId                        Int?
  isTemplate                       Boolean
  dateWorkoutScheduled             DateTime
  dateWorkoutCurrentlyScheduledFor DateTime            @default(now())
  timeWorkoutStart                 DateTime?
  timeWorkoutEnd                   DateTime?
  isWorkoutCompleted               Boolean
  doctorNote                       String?
  description                      String?
  exercises                        ExerciseInWorkout[]
  source                           WorkoutSource?      // who put the session on the plan
  continuedFromWorkoutId           Int?                // the session a plan-continuation copy repeats
  tenantId                         Int                 @default(1)
}

ExerciseInWorkout

model ExerciseInWorkout {
  id                              Int           @id @default(autoincrement())
  workout                         Workout       @relation(...)
  workoutId                       Int
  exercise                        Exercise      @relation(...)
  exerciseId                      Int
  exerciseType                    ExerciseType? @default(REPS_BASED)
  plannedSets                     Int
  actualSets                      Int?
  plannedReps                     Int?
  actualReps                      Int?
  plannedWeight                   Float?
  actualWeight                    Float?
  plannedDuration                 Int?
  actualDuration                  Int?
  doctorNotesPreExercise          String?
  doctorNotesPreExerciseAt        DateTime?
  doctorNotesPostExercise         String?
  doctorNotesPostExerciseAt       DateTime?
  prescribedPainToleranceLevel    Int?
  prescribedPhysicalExertionLevel Int?
  isExerciseCompleted             Boolean
  orderInWorkout                  Int
  exerciseBlock                   ExerciseBlock? @default(MAIN_SESSION)
  tenantId                        Int            @default(1)
}

Testing Checklist

Initial Plan Creation

  • [ ] Create plan for new patient with AI generation
  • [ ] Manually add exercises to multiple days
  • [ ] Copy day exercises to another day
  • [ ] Clear a day and verify exercises removed
  • [ ] Save plan and verify workouts created in database

Single Workout Creation

  • [ ] Create workout from doctor dashboard
  • [ ] Select date and add exercises
  • [ ] Verify patient notification sent

Plan Editing

  • [ ] Edit existing patient plan
  • [ ] Verify protected workouts (started within 1 hour) cannot be modified
  • [ ] Add exercises to empty days
  • [ ] Remove exercises from existing days
  • [ ] Save and verify changes persisted

Single Workout Editing

  • [ ] Edit workout exercises
  • [ ] Change scheduled date
  • [ ] Add pre-exercise notes
  • [ ] Verify incomplete workout can be edited
  • [ ] Verify completed workout cannot be edited

Doctor Notes

  • [ ] Add workout-level note
  • [ ] Add post-exercise note after reviewing performance
  • [ ] Verify note length validation (max 1000 chars)

Templates

  • [ ] Create private template
  • [ ] Create public template
  • [ ] Apply template to patient
  • [ ] Edit template exercises
  • [ ] Delete template (verify soft delete)