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 |
| Exercise | An individual movement within a workout (reps-based or time-based) |
| Plan | A collection of workouts spanning multiple days (up to 10 days) |
| 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 |
Creating Patient Workouts¶
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-10 scheduling with day picker (PlanDaySelector.tsx)
- Copy day functionality (duplicate exercises to another day)
- Clear day functionality (remove all exercises from a day)
- AI-generated plan support with manual customization
- Exercise block organization (warm-up, main session, etc.)
Request Body:
{
"patientId": 123,
"startDate": "2024-01-15",
"endDate": "2024-01-24",
"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"
}
]
}
Editing Patient Workouts¶
Bulk Plan Editing (Edit Plan Modal)¶
Edit upcoming workouts across a 10-day window.
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. Fetches existing workouts for the date range 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
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:
- Streaming Preview: As the AI generates modifications, exercises appear progressively with skeleton loading states
- Comparison View: Once complete, a side-by-side comparison shows:
- Original exercises on the left
- Modified exercises on the right
- NEW badge for added exercises
- CHANGED badge for modified exercises
- Exercise thumbnails for visual reference
- 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:
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:
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:
- Identifies workouts started within the last hour (protected)
- Deletes ExerciseInWorkout records for non-protected workouts
- Deletes Workout records for non-protected workouts
- 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 workout configurations that can be applied to any patient.
Backend Location: OktaPT-API/src/routes/workouts/templates.ts
| API Endpoint | Method | Auth | Purpose |
|---|---|---|---|
/v2/workouts/templates |
GET | Required | List all templates |
/v2/workouts/templates |
POST | Required | Create new template |
/v2/workouts/templates/{id} |
PUT | Required | Update template |
/v2/workouts/templates/{id} |
DELETE | Required | Soft delete template (sets isActive: false) |
/v2/workouts/templates/{id}/create-workout |
POST | Required | Create workout from template |
Template Visibility¶
- Private templates: Only visible to the creator
- Public templates: Visible to all therapists in the tenant
Creating a Template¶
{
"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"
}
]
}
Creating Workout from Template¶
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¶
- Only the upcoming 10 days can be edited via the plan editor
- Past workouts cannot be modified through the plan editor
- Completed workouts are preserved regardless of date
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[]
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)