Skip to Content
FeaturesWorkoutWorkout Feature Guide

Workout Feature Guide

Definitive guide to the Workout feature — exercise library, workout day scheduling, set/rep tracking, muscle groups, and analytics across all platforms.

  1. Data Models & Schema
  2. API Specifications
  3. UI/UX Guidelines
  4. Implementation Checklist

Introduction

The Workout Feature is a comprehensive exercise tracking and workout management system that enables users to:

  • Create and manage personal exercise libraries
  • Schedule exercises across weekly workout days (Monday-Sunday)
  • Track workouts at three levels: workout completion, exercise completion, and individual set logging
  • Monitor progress with historical tracking and weekly summaries
  • Organize exercises by muscle groups
  • Define target sets, reps, and rest periods for each exercise

This guide establishes the standards, patterns, and business rules that govern all workout feature implementations across desktop (web/desktop apps) and mobile platforms.


Business Logic & Rules

2.1 Core Concepts

Exercise

An exercise is a fitness activity definition containing:

  • Basic Info: Name, description
  • Performance Targets: Base sets (target # of sets), base reps (target reps per set), rest seconds
  • Categorization: Muscle group (FULL_BODY, CORE, LEGS, CHEST, BACK, SHOULDERS, ARMS, GLUTES, CARDIO)
  • Optional Resources: Instructional videos, images, or documents

Workout Day

A workout day represents a scheduled training session:

  • Day of Week: Integer 1-7 (Monday-Sunday)
  • Day Name: Display name (e.g., “Monday”, “Leg Day”)
  • Time: Preferred workout time (e.g., “06:00”, “18:30”). When stored as "00:00", the notification dispatcher falls back to UserSettings.defaultWorkoutTime.
  • Exercises: Collection of exercises assigned to this day
  • Completion Status: Whether the entire workout was completed on a given date (isCompleted — used by the notification dispatcher to suppress “workout starting” reminders once done)

A workout day with a non-stub id triggers a “workout starting soon” push when its start falls within UserSettings.defaultWorkoutReminderOffsetMinutes (default 30, 0 to disable). See Default reminder offsets.

Exercise Binding

The association between exercises and workout days:

  • One exercise can be assigned to multiple days (e.g., Push-ups on Mon/Wed/Fri)
  • One day can have multiple exercises (e.g., Monday: Bench Press, Squats, Pull-ups)
  • Managed via junction table (ExercisesWithWorkoutDays)

Three-Level Tracking System

Level 1: Workout Tracking

  • Tracks completion of an entire workout day
  • Creates WorkoutTrack record
  • Used for: “I completed my Monday workout”

Level 2: Exercise Tracking

  • Tracks completion of a specific exercise within a workout
  • Creates ExerciseTrack record
  • Used for: “I completed Push-ups during Monday’s workout”

Level 3: Set Tracking (Primary)

  • Tracks individual sets within an exercise
  • Creates ExerciseSetTrack records
  • Used for: “I did 3 sets of Push-ups: 10 reps, 12 reps, 8 reps”
  • Most granular and important tracking level

Exercise Day Status

A composite view showing exercise progress:

  • exerciseTrackId: Unique identifier for the exercise session
  • exerciseId: Which exercise was performed
  • setsCountDone: Number of sets completed
  • totalRepsDone: Sum of all reps across sets
  • exerciseDone: Whether exercise marked complete
  • setsDone: Array of individual set data (id, reps, doneDateTime)

2.2 Business Rules

Exercise Management

E-001: Exercise Creation

  • Name is required (non-empty string)
  • Description is optional
  • Base sets, base reps, rest seconds are optional but must be positive integers if provided
  • Muscle group must match enum values or be null
  • User can only create exercises for themselves
  • Each exercise is owned by a single user

E-002: Exercise Updates

  • Users can only update their own exercises
  • All fields can be modified independently
  • Changing exercise parameters does not affect historical tracking data
  • Update does not affect day assignments (managed separately)

E-003: Exercise Deletion

  • Soft delete: Exercise marked as DELETED, not physically removed
  • Existing day bindings remain in database but filtered from queries
  • Historical tracking data (ExerciseTracks, ExerciseSetTracks) preserved
  • Deleted exercises not shown in exercise library or day views

E-004: Exercise Ownership

  • Exercises are user-scoped; users cannot see other users’ exercises
  • All queries must filter by user_id
  • Exercise IDs are globally unique (UUID) but always queried with user context

Workout Day Management

W-001: Workout Day Creation

  • Seven workout days (1-7 for Monday-Sunday) per user
  • Auto-created when first exercise is bound to a day
  • Day name defaults to weekday name but can be customized
  • Time defaults to null but can be set by user
  • Each user has their own set of 7 workout days

W-002: Workout Day Updates

  • Users can update day name, time, and exercise list
  • Updating exercise list replaces all bindings for that day
  • Update operation is transactional (all or nothing)
  • Cannot change day number (1-7) once created

W-003: Workout Day Completion Status

  • Completion status is date-specific (can complete Monday on 2025-12-29 and again on 2026-01-05)
  • Determined by WorkoutTrack records for that day + date
  • Completion does not prevent further exercise/set logging
  • User can have multiple completions for same day in different weeks

Exercise-Day Binding

B-001: Binding Exercises to Days

  • Exercise can be bound to 0-7 days
  • Creates ExercisesWithWorkoutDays junction record
  • If WorkoutDay doesn’t exist, auto-create it with day number
  • Duplicate bindings prevented (same exercise + same day)

B-002: Unbinding Exercises from Days

  • Removes ExercisesWithWorkoutDays junction record (hard delete)
  • Does not delete WorkoutDay (day can exist without exercises)
  • Does not affect historical tracking data
  • User can rebind same exercise later

B-003: Binding Constraints

  • Cannot bind exercise owned by different user
  • Day must be 1-7 (Monday-Sunday)
  • One exercise-day pair can only be bound once

Set Tracking (Core Workflow)

ST-001: Adding a Set

  • User specifies reps for the set
  • System finds or creates ExerciseTrack for (exercise + workout day + today’s date)
  • Creates ExerciseSetTrack within that ExerciseTrack
  • Records: reps, doneDateTime
  • Returns set ID to client for future updates/deletes

ST-002: Set Editing

  • CRITICAL RULE: Only newly created sets are editable
  • When user adds a set, backend returns the set data
  • UI enables editing ONLY for that returned set
  • Previously logged sets are read-only (displayed but not editable)
  • This prevents accidental modification of historical data

ST-003: Set Deletion

  • User can delete any set they’ve logged
  • Soft delete: ExerciseSetTrack status set to DELETED
  • If all sets deleted from ExerciseTrack, the track remains (exercise attempted but no sets completed)
  • UI removes set from display

ST-004: Exceeding Base Sets

  • If user adds more sets than baseSets, show warning dialog
  • Warning: “You’re adding set X, but target is Y sets. Continue?”
  • User can choose to proceed or cancel
  • No hard limit on set count

ST-005: Set Data Integrity

  • Reps must be positive integer
  • DoneDateTime auto-set to current timestamp
  • Each set is immutable once created (except via delete/re-add)
  • Sets are linked to ExerciseTrack, not directly to Exercise

Exercise Completion Tracking

EC-001: Completing an Exercise

  • User marks entire exercise as complete
  • Creates or updates ExerciseTrack with doneDateTime
  • Does not require any sets to be logged (user can mark complete without set data)
  • Typically used when user completes exercise but doesn’t log individual sets

EC-002: Exercise Completion vs Set Tracking

  • Set tracking is independent of exercise completion
  • User can log sets without marking exercise complete
  • User can mark exercise complete without logging sets
  • Best practice: Use set tracking as primary method, exercise completion as fallback

EC-003: Undoing Exercise Completion

  • Removes ExerciseTrack record (soft delete)
  • Does not affect ExerciseSetTracks (sets remain if logged)
  • Exercise returns to “not completed” state

Workout Completion Tracking

WC-001: Completing a Workout

  • User marks entire workout day as complete
  • Creates WorkoutTrack record with workoutDayId + doneDateTime
  • Independent of exercise/set tracking (can complete workout without logging exercises)
  • Used for high-level tracking: “I did my Monday workout”

WC-002: Workout Completion Independence

  • Workout completion does not auto-mark exercises as complete
  • Exercise/set tracking does not auto-mark workout as complete
  • User controls each level independently

WC-003: Undoing Workout Completion

  • Removes WorkoutTrack record (soft delete)
  • Does not affect exercise or set tracks
  • Workout returns to “not completed” state

WC-004: Weekly Summary

  • System provides count of workouts completed per day (0-6 representing Sun-Sat or Mon-Sun)
  • Counts WorkoutTrack records within current week
  • Used for weekly progress visualization

F-001: Exercise Text Search

  • Search by exercise name (case-insensitive, partial match)
  • Uses SQL LIKE with wildcards: %search_term%
  • Search applies across all user’s exercises regardless of day assignment

F-002: Day Filtering

  • Filter exercises by assigned day (1-7)
  • Returns only exercises bound to specified day
  • Requires JOIN with ExercisesWithWorkoutDays and WorkoutDays

F-003: Muscle Group Filtering

  • Filter by muscle group enum
  • If multiple groups selected, uses OR logic
  • If no filter, show all exercises

F-004: Date-Time Context Filtering

  • When fetching exercises for a day, optionally provide dateTime
  • Returns exercises with completion status for that specific date
  • Enables showing “completed today” vs “completed yesterday”

Pagination

PG-001: Pagination Parameters

  • limit: Number of results per page (default: 50, max: 200)
  • offset: Page number (0-indexed)
  • Actual SQL offset = offset * limit
  • Not all endpoints support pagination (small result sets return all)

PG-002: Pagination Behavior

  • Filtering applied before pagination
  • Results sorted by name (default) before pagination
  • Empty array returned if offset beyond available data

2.3 Validation Rules

V-001: Exercise Name

  • Required, non-empty
  • Max length: 255 characters
  • No special character restrictions

V-002: Exercise Numeric Fields

  • Base sets, base reps, rest seconds: all optional
  • Must be positive integers if provided (> 0)
  • No upper limit enforced (use reasonable UI constraints)

V-003: Muscle Group

  • Must match enum: FULL_BODY, CORE, LEGS, CHEST, BACK, SHOULDERS, ARMS, GLUTES, CARDIO
  • Case-sensitive
  • If invalid value provided, throw validation error (don’t auto-convert to null)
  • Optional field (null allowed)

V-004: Workout Day Number

  • Must be integer 1-7
  • 1 = Monday, 7 = Sunday
  • Required field
  • Out-of-range values rejected

V-005: Workout Day Time

  • Format: HH:mm (24-hour)
  • Examples: “06:00”, “18:30”, “23:45”
  • Optional field
  • Invalid format rejected

V-006: Set Reps

  • Must be positive integer (> 0)
  • Max recommended: 999 (UI constraint)
  • Required when adding set

V-007: DateTime Fields

  • Format: ISO 8601 with time: YYYY-MM-DDTHH:mm:ss
  • Examples: “2025-12-29T09:30:00”
  • Required for tracking operations
  • Auto-generated by client if not provided

2.4 Error Handling

ERR-001: Exercise Not Found

  • HTTP 404
  • Message: “Exercise not found”
  • Occurs when: user requests exercise that doesn’t exist or belongs to another user

ERR-002: Workout Day Not Found

  • HTTP 404
  • Message: “Workout day not found”
  • Occurs when: requesting day that doesn’t exist for user

ERR-003: Unauthorized Access

  • HTTP 403
  • Message: “Unauthorized access to exercise/workout”
  • Occurs when: user tries to modify/delete another user’s data

ERR-004: Validation Failure

  • HTTP 400
  • Message: Specific validation error (e.g., “Exercise name is required”, “Invalid muscle group”)
  • Return first validation error encountered

ERR-005: Duplicate Binding

  • HTTP 409 (Conflict)
  • Message: “Exercise already bound to this day”
  • Occurs when: trying to bind exercise to day it’s already assigned to

ERR-006: Server Error

  • HTTP 500
  • Message: “Internal server error”
  • Log full stack trace server-side
  • Don’t expose internal details to client

Desktop Design Standards

3.1 Platform Contexts

Desktop includes:

  • Desktop Application (Kotlin Compose Multiplatform - Desktop target)
  • Web Application (Kotlin Compose Multiplatform - JS/WASM target)

Desktop design prioritizes:

  • Larger screen real estate for workout planning
  • Multi-day view for weekly scheduling
  • Detailed set logging with inline editing
  • Keyboard shortcuts for quick logging
  • Visual progress indicators

3.2 Layout Standards

Workout Dashboard View

Layout Structure:

+--------------------------------------------------+ | Header: "Workout Dashboard" | | Weekly Summary: 4/7 workouts completed | +--------------------------------------------------+ | Day Selector (Chip Row) | | [Mon] [Tue] [Wed] [Thu] [Fri] [Sat] [Sun] [All]| +--------------------------------------------------+ | Selected Day: Monday | | Time: 06:00 AM [Complete Workout] | +--------------------------------------------------+ | Exercises (3) | | +--------------------------------------------+ | | | Exercise Card: Bench Press | | | | 3 sets × 10 reps • 90s rest | | | | Target muscle: CHEST | | | | [View Details] [Log Sets] | | | +--------------------------------------------+ | | | Exercise Card: Squats | | | | ... | | | +--------------------------------------------+ | +--------------------------------------------------+ | [+ Add Exercise to Monday] | +--------------------------------------------------+

Key Elements:

  • Day selector chips at top (active day highlighted)
  • “All” mode shows entire exercise library
  • Workout completion button (disabled if already completed)
  • Exercise cards expand to show set logging
  • Add exercise button to bind new exercises to selected day

Exercise Card (Expanded)

Layout Structure:

+--------------------------------------------------+ | Bench Press [Edit] [X] | | 3 sets × 10 reps • 90 seconds rest | | Target: CHEST | | Assigned to: Mon, Wed, Fri [Manage Schedule] | +--------------------------------------------------+ | Sets Logged Today: | | +--------------------------------------------+ | | | Set 1: 12 reps [Edit] [Delete] | | | | Set 2: 10 reps [Edit] [Delete] | | | | Set 3: 8 reps [Edit] [Delete] | | | +--------------------------------------------+ | | [+ Add Set] Reps: [____] [Log] | | Warning: Target is 3 sets, you're adding set 4 | +--------------------------------------------------+ | [Mark Exercise Complete] [View History] | +--------------------------------------------------+

States:

  • Collapsed: Shows summary only (name, targets, muscle group)
  • Expanded: Shows set logging area
  • Completed: Green checkmark overlay, disabled logging
  • In Progress: Some sets logged, can add more

Interactive Elements:

  • Click card header to expand/collapse
  • Edit button opens exercise editor
  • Delete (X) unbinds from current day (not in “All Exercises” mode)
  • Manage Schedule opens day binding dialog
  • Add Set: text input for reps + Log button
  • Each logged set has Edit/Delete actions

All Exercises View

Layout: Grid or List

Grid Mode (Recommended):

+--------------------------------------------------+ | All Exercises [+ New] | | [Search: ___________] [Filter] [Sort] | +--------------------------------------------------+ | [Push-ups] [Bench Press] [Squats] | | Chest/Arms Chest Legs | | M W F M W M Th | | | | [Pull-ups] [Deadlifts] [Curls] | | Back Legs/Back Arms | | M W F W F T Th S | +--------------------------------------------------+

List Mode:

  • Each row: Name | Muscle Group | Days | Actions
  • Compact for large exercise libraries
  • Sortable columns

Features:

  • Search bar for name filtering
  • Filter by muscle group
  • Sort by name, muscle group, # of days assigned
  • Each exercise card shows day badges (clickable to unbind)
  • New Exercise button prominent

Exercise Form (Create/Edit)

Form Structure:

+--------------------------------------------------+ | New Exercise [Close] | +--------------------------------------------------+ | Name: [________________________] | | | | Description: | | [_____________________________________________] | | [_____________________________________________] | | | | Performance Targets: | | Base Sets: [___] (e.g., 3) | | Base Reps: [___] (e.g., 10) | | Rest (sec): [___] (e.g., 90) | | | | Muscle Group: [Dropdown ▼] | | └─ Options: Full Body, Core, Legs, Chest... | | | | Schedule: | | [☐ Mon] [☐ Tue] [☐ Wed] [☐ Thu] | | [☐ Fri] [☐ Sat] [☐ Sun] | | | | [Cancel] [Save] | +--------------------------------------------------+

Validation:

  • Name required (highlighted if empty)
  • Numeric fields must be positive integers or empty
  • Muscle group validated against enum
  • Schedule optional (can create unscheduled exercise)

Edit Mode Differences:

  • Title: “Edit Exercise”
  • Pre-populate all fields
  • Save button text: “Update”
  • Delete button in footer (danger zone)

Workout History View

Layout: Timeline or Calendar

Timeline View:

+--------------------------------------------------+ | Workout History | | [Date Range: Dec 1 - Dec 29] [This Week] | +--------------------------------------------------+ | Monday, Dec 29, 2025 | | ✓ Workout Completed (06:15 AM) | | └─ Bench Press: 3 sets (12, 10, 8 reps) | | └─ Squats: 4 sets (15, 12, 10, 8 reps) | | | | Monday, Dec 22, 2025 | | ✓ Workout Completed (06:00 AM) | | └─ Bench Press: 3 sets (10, 10, 9 reps) | | └─ Squats: 3 sets (12, 12, 10 reps) | | | | Friday, Dec 19, 2025 | | ✗ Workout Not Completed | | └─ Deadlifts: 2 sets (8, 6 reps) [incomplete] | +--------------------------------------------------+

Calendar View (Alternative):

  • Month calendar with workout completion indicators
  • Click date to see details
  • Color coding: Green = completed, Yellow = partial, Gray = missed

3.3 Interactive Components

Day Selector Chips

Visual Design:

[Mon] [Tue] [Wed] [Thu] [Fri] [Sat] [Sun] [All Exercises]

States:

  • Default: Gray background, dark text
  • Selected: Primary color background, white text
  • Completed Today: Green border/badge overlay
  • Has Exercises: Bold text or dot indicator

Behavior:

  • Click to switch day
  • Keyboard: Arrow keys to navigate, Enter to select
  • “All Exercises” special mode (shows library, not day-specific)

Set Logging Input

Inline Form:

Add Set: Reps [____] [Log Set]

After Logging Set:

Set 3: 12 reps ✓ Logged [Edit Reps] [Delete] ↑ Editable for ~5 seconds after creation

Behavior:

  • Enter reps, click “Log Set” or press Enter
  • Backend creates set, returns set data
  • UI makes that set editable (reps can be changed)
  • After blur or timeout, set becomes read-only
  • Other sets always read-only

Warning for Exceeding Target:

⚠️ You're adding set 4, but target is 3 sets. [Add Anyway] [Cancel]

Manage Schedule Dialog

Triggered by: “Manage Schedule” button on exercise card in All Exercises mode

Layout:

+----------------------------------+ | Schedule: Bench Press [Close]| +----------------------------------+ | Select days for this exercise: | | | | [✓ Mon] [✓ Wed] [✓ Fri] | | [ Tue] [ Thu] [ Sat] | | [ Sun] | | | | Current: Mon, Wed, Fri | | | | [Cancel] [Save] | +----------------------------------+

Behavior:

  • Pre-select days where exercise is currently bound
  • Toggle days on/off
  • On Save:
    • Calculate toBind = selected - current
    • Calculate toUnbind = current - selected
    • Execute bind/unbind operations
    • Refresh exercise card to show updated day badges

3.4 View Modes

Desktop should support multiple view modes:

Mode 1: Daily Workout View

  • Purpose: Focus on today’s or specific day’s workout
  • Layout: Single day’s exercises with full set logging
  • Day Selector: Prominent at top
  • Use Case: During workout, logging sets in real-time

Mode 2: Weekly Planning View

  • Purpose: Plan entire week at a glance
  • Layout: 7 columns (one per day), each showing exercise names
  • Interaction: Drag-and-drop to assign exercises (enhancement)
  • Use Case: Planning upcoming week’s workouts

Mode 3: Exercise Library View

  • Purpose: Manage all exercises
  • Layout: Grid or list of all exercises
  • Day Context: None (shows all exercises regardless of assignment)
  • Use Case: Creating, editing, organizing exercise collection

Mode 4: History View

  • Purpose: Review past workouts
  • Layout: Timeline or calendar
  • Date Range: Selectable (week, month, all time)
  • Use Case: Tracking progress, identifying patterns

View Mode Switcher:

  • Tab bar or navigation sidebar
  • Persist selected mode in user preferences
  • Each mode has context-appropriate actions

3.5 Keyboard Navigation

Desktop must support efficient keyboard navigation:

Global Shortcuts:

  • Ctrl/Cmd + N: New Exercise
  • Ctrl/Cmd + F: Focus search input
  • Ctrl/Cmd + 1-7: Switch to day 1-7 (Mon-Sun)
  • Ctrl/Cmd + 0: Switch to All Exercises view
  • ESC: Close modals/dialogs

Exercise Card Navigation:

  • Tab: Navigate between exercise cards
  • Enter: Expand/collapse focused card
  • Space: Toggle exercise complete
  • Delete: Unbind from current day (with confirmation)

Set Logging:

  • Focus on reps input field
  • Enter: Log set
  • Tab: Navigate to Edit/Delete buttons

Form Navigation:

  • Tab: Move to next field
  • Shift + Tab: Move to previous field
  • Enter: Submit form (if not in textarea)
  • ESC: Cancel/close form

3.6 Responsive Breakpoints (for Web Desktop)

- Large Desktop (>= 1440px): 3-column exercise grid, full sidebar - Desktop (1024px - 1439px): 2-column exercise grid, full sidebar - Tablet (768px - 1023px): 1-column exercise grid, collapsible sidebar - Mobile (<768px): Use mobile design standards

Mobile Design Standards

4.1 Platform Context

Mobile includes:

  • Android Application (Kotlin Android)
  • iOS Application (Kotlin Multiplatform - iOS target, if implemented)

Mobile design prioritizes:

  • Touch-first interaction
  • Quick workout logging during exercise
  • Swipe gestures for common actions
  • Minimal taps to log sets
  • Workout timer integration
  • Rest timer between sets

4.2 Layout Standards

Workout View (Mobile)

Layout: Vertical List

+----------------------------------+ | Monday Workout 06:00 AM | | [< Back] [...] | +----------------------------------+ | Progress: 2/5 exercises done | | [■■■■□] 80% | +----------------------------------+ | Exercise Card | | +----------------------------+ | | | Bench Press [Edit] | | | | 3 × 10 reps • 90s rest | | | | ✓ Set 1: 12 reps | | | | ✓ Set 2: 10 reps | | | | Set 3: [__] reps [Log] | | | | [✓ Mark Complete] | | | +----------------------------+ | | | | Exercise Card | | +----------------------------+ | | | Squats [Edit] | | | | 4 × 12 reps • 60s rest | | | | [+ Start Logging Sets] | | | +----------------------------+ | +----------------------------------+ | [✓ Complete Workout] | +----------------------------------+

Key Characteristics:

  • Day name and time in header
  • Progress indicator at top
  • Exercise cards stacked vertically
  • Inline set logging (no modal)
  • Floating action button for complete workout

Exercise Card (Mobile - Collapsed)

+----------------------------------+ | Bench Press [Edit] | | CHEST • 3×10 • 90s rest | | Days: M W F | | [Start Logging] | +----------------------------------+

Exercise Card (Mobile - Expanded/Logging)

+----------------------------------+ | Bench Press [Edit] | | CHEST • 3×10 • 90s rest | +----------------------------------+ | Sets: | | ✓ Set 1: 12 reps [Timer: 90s]| | ✓ Set 2: 10 reps [Timer: 90s]| | > Set 3: [12] reps [LOG SET] | | | | [+ Add Another Set] | | [✓ Complete Exercise] | +----------------------------------+

Interactive Elements:

  • Tap card to expand
  • Reps input with number keyboard
  • “LOG SET” button triggers timer and adds set
  • Timer countdown overlay during rest period
  • Swipe left on logged set to delete

All Exercises View (Mobile)

Layout: Vertical List with Search

+----------------------------------+ | Exercises [+ New] | | [Search: ___________] | | [Filter ▼] [Sort ▼] | +----------------------------------+ | Bench Press | | Chest • M W F | | 3×10 • 90s rest | | ────────────────────────────── | | Squats | | Legs • M Th | | 4×12 • 60s rest | | ────────────────────────────── | | Pull-ups | | Back/Arms • M W F | | 3×8 • 120s rest | +----------------------------------+

Swipe Actions:

  • Swipe right: Quick edit
  • Swipe left: Delete/unbind options
  • Long press: Multi-select mode

Exercise Form (Mobile)

Layout: Scrollable Full-Screen

+----------------------------------+ | New Exercise [X] Close | +----------------------------------+ | Name | | [________________________] | | | | Description | | [________________________] | | [________________________] | | | | Base Sets [3] | | Base Reps [10] | | Rest (sec) [90] | | | | Muscle Group | | [Chest ▼] | | | | Assign to Days | | [✓M] [✓W] [✓F] [ T] [ T] [ S] [ S]| | | | [Save Exercise] | +----------------------------------+

Behavior:

  • Native mobile keyboard types (number, text)
  • Day toggles as large touch targets
  • Save button sticky at bottom
  • Validation inline with red underlines

Workout History (Mobile)

Layout: Timeline List

+----------------------------------+ | History [Filter] | | [This Week ▼] | +----------------------------------+ | Monday, Dec 29 | | ✓ Workout Completed | | +----------------------------+ | | | Bench Press: 3 sets | | | | Squats: 4 sets | | | | Pull-ups: 3 sets | | | +----------------------------+ | | | | Monday, Dec 22 | | ✗ Incomplete | | +----------------------------+ | | | Bench Press: 2 sets | | | +----------------------------+ | +----------------------------------+

Tap to Expand:

+----------------------------------+ | Monday, Dec 29 06:15 AM | | ✓ Workout Completed | +----------------------------------+ | Bench Press | | • Set 1: 12 reps | | • Set 2: 10 reps | | • Set 3: 8 reps | | | | Squats | | • Set 1: 15 reps | | • Set 2: 12 reps | | • Set 3: 10 reps | | • Set 4: 8 reps | +----------------------------------+

4.3 Interactive Components (Mobile)

Rest Timer Overlay

Triggered by: Logging a set

Layout:

+----------------------------------+ | | | | | Rest Timer | | | | 01:30 | | [■■■■■□□□□□] | | | | [Skip Rest] [Add 30s] | | | +----------------------------------+

Behavior:

  • Semi-transparent overlay over workout view
  • Countdown from exercise’s rest seconds
  • Vibrate/sound when timer completes
  • Tap anywhere to dismiss early
  • Swipe down to minimize to notification

Filter Bottom Sheet

Triggered by: Filter button in All Exercises view

Layout:

+----------------------------------+ | Filter Exercises [Close]| +----------------------------------+ | Muscle Groups | | [Chest] [Back] [Legs] [Arms] | | [Shoulders] [Core] [Full Body] | | (Toggle buttons) | +----------------------------------+ | Assigned Days | | [M] [T] [W] [T] [F] [S] [S] | | (Toggle buttons) | +----------------------------------+ | [Clear Filters] [Apply] | +----------------------------------+

Behavior:

  • Modal bottom sheet (slides up)
  • Multi-select muscle groups (OR logic)
  • Multi-select days
  • Apply button triggers filter
  • Clear resets all

Quick Actions Bottom Sheet

Triggered by: Long-press on exercise card

Layout:

+----------------------------------+ | Bench Press | +----------------------------------+ | [Edit Exercise] | | [Manage Schedule] | | [View History] | | [Delete Exercise] | | [Cancel] | +----------------------------------+

4.4 Gestures

Swipe Actions on Exercise Cards:

  • Swipe Right: Quick edit
  • Swipe Left: Delete/unbind options
  • Long Press: Show quick actions bottom sheet

Swipe Actions on Logged Sets:

  • Swipe Left: Delete set

Pull to Refresh:

  • Pull down on workout view to refresh data

Swipe to Dismiss:

  • Bottom sheets dismissible by swiping down

4.5 Mobile-Specific Patterns

Tab Navigation:

+----------------------------------+ | [Workout] [Exercises] [History] | +----------------------------------+ | | | (Content for selected tab) | | | +----------------------------------+

Floating Action Button:

  • Context: Workout View → “Complete Workout”
  • Context: Exercises View → “New Exercise”
  • Context: History View → “Today’s Workout”

Contextual Actions:

  • Three-dot menu for secondary actions
  • Bottom sheets for action lists
  • Native share sheet for workout summaries

Workout Mode:

  • Immersive full-screen during active workout
  • Screen stays awake
  • Volume buttons for quick set logging (optional)
  • Voice commands for hands-free logging (enhancement)

Architecture Patterns

5.1 Layer Architecture

All platforms follow Clean Architecture principles:

+--------------------------------------------------+ | PRESENTATION LAYER | | - Screens/Views (Composables) | | - ViewModels (State management) | | - State (UI state models) | +--------------------------------------------------+ +--------------------------------------------------+ | DOMAIN LAYER | | - Use Cases (Business logic) | | - Repository Interfaces | | - Domain Models | +--------------------------------------------------+ +--------------------------------------------------+ | DATA LAYER | | - Repository Implementations | | - Data Sources (Remote/Local) | | - API Clients | | - DTOs (Data Transfer Objects) | | - Mappers (DTO ↔ Domain) | +--------------------------------------------------+

5.2 State Management Pattern

Current Pattern (Desktop):

// State data class WorkoutState( val exercises: List<Exercise> = emptyList(), val workoutDays: List<WorkoutDay> = emptyList(), val completedExercises: Set<ExerciseDayStatus> = emptySet(), val exerciseDayMap: Map<String, Set<Int>> = emptyMap(), val weeklyWorkoutsCompleted: List<Int> = emptyList(), val daySelected: Int = 0, val allExerciseMode: Boolean = false, val isCompleted: Boolean = false, val isLoading: Boolean = false, val isError: Boolean = false, val errorMessage: String = "" ) // ViewModel class WorkoutViewModel : ViewModel() { private val _state = MutableStateFlow(WorkoutState()) val state: StateFlow<WorkoutState> = _state.asStateFlow() fun getExercisesByDay(day: Int) { viewModelScope.launch { _state.update { it.copy(isLoading = true) } try { val exercises = workoutService.getExercisesByDay(day, dateTime) _state.update { it.copy( exercises = exercises, daySelected = day, isLoading = false ) } } catch (e: Exception) { _state.update { it.copy( isLoading = false, isError = true, errorMessage = e.message ?: "Unknown error" ) } } } } }

Recommended Pattern (MVI - for consistency with other features):

// State data class WorkoutState( val exercises: List<Exercise> = emptyList(), val workoutDays: List<WorkoutDay> = emptyList(), val completedExercises: Set<ExerciseDayStatus> = emptySet(), val exerciseDayMap: Map<String, Set<Int>> = emptyMap(), val daySelected: Int = 0, val isLoading: Boolean = false ) // Intent sealed class WorkoutIntent { data class GetExercisesByDay(val day: Int) : WorkoutIntent() data class AddExercise(val exercise: Exercise) : WorkoutIntent() data class UpdateExercise(val exercise: Exercise) : WorkoutIntent() data class DeleteExercise(val id: String) : WorkoutIntent() data class AddSet(val exerciseId: String, val workoutDayId: String, val reps: Int) : WorkoutIntent() data class CompleteExercise(val exerciseId: String, val day: Int, val workoutDayId: String) : WorkoutIntent() data class CompleteWorkout(val dayId: Int) : WorkoutIntent() data class BindExerciseToDay(val exerciseId: String, val day: Int) : WorkoutIntent() // ... more intents } // Effect sealed class WorkoutEffect { data class ShowSnackBar(val message: String, val type: SnackBarType) : WorkoutEffect() data class NavigateToExerciseDetail(val exerciseId: String) : WorkoutEffect() data class StartRestTimer(val seconds: Int) : WorkoutEffect() // ... more effects } // ViewModel class WorkoutViewModel : BaseViewModel<WorkoutState, WorkoutIntent, WorkoutEffect>() { override fun handleIntent(intent: WorkoutIntent) { when (intent) { is WorkoutIntent.GetExercisesByDay -> getExercisesByDay(intent.day) is WorkoutIntent.AddSet -> addSet(intent.exerciseId, intent.workoutDayId, intent.reps) // ... handle other intents } } private fun addSet(exerciseId: String, workoutDayId: String, reps: Int) { viewModelScope.launch { workoutUseCases.addExerciseSet(exerciseId, workoutDayId, reps) .onSuccess { exerciseSet -> // Refresh exercises for current day getExercisesByDay(currentState.daySelected) emitEffect(WorkoutEffect.ShowSnackBar( message = "Set logged: $reps reps", type = SnackBarType.SUCCESS )) emitEffect(WorkoutEffect.StartRestTimer( seconds = getRestSecondsForExercise(exerciseId) )) } .onFailure { error -> emitEffect(WorkoutEffect.ShowSnackBar( message = "Failed to log set", type = SnackBarType.ERROR )) } } } }

5.3 Data Flow Pattern

Read Flow (Get Exercises for Day):

User Action (Select Monday) Intent: GetExercisesByDay(day = 1) ViewModel: handleIntent UseCase: getExercisesByWorkoutDay.invoke(day = 1, dateTime = now) Repository: getExercisesByDay(1, now) Remote DataSource: api.getExercisesByDay(1, now) Server API: GET /workout/byDay/1?dateTime=2025-12-29T09:00:00 Server: Query WorkoutDays → ExercisesWithWorkoutDays → Exercises Query ExerciseTracks/ExerciseSetTracks for completion status Map entities to DTOs with completion data Response: List<ExerciseDTO> (with completion status) DataSource: receives response Repository: maps DTOs to domain models UseCase: returns Result<List<Exercise>> ViewModel: updates state with exercises View: recomposes with exercises + completion status

Write Flow (Add Set):

User Action (Logs "12 reps" for Bench Press) Intent: AddSet(exerciseId, workoutDayId, reps = 12) ViewModel: handleIntent UseCase: addExerciseSet.invoke(exerciseId, workoutDayId, 12) Repository: addExerciseSet(exerciseId, workoutDayId, 12) Remote DataSource: api.completeExerciseSet(CreateExerciseSetTrackDTO) Server API: POST /workout/exercise-tracking/sets/complete Body: { exerciseId, workoutDayId, reps: 12, doneDateTime: "2025-12-29T09:30:00" } Server: 1. Find or create ExerciseTrack (exercise + day + date) 2. Create ExerciseSetTrack (reps, doneDateTime) 3. Link to ExerciseTrack 4. Return success Response: 201 Created Repository: returns Result<Unit> ViewModel: emits success effect, refreshes exercises Effect: ShowSnackBar("Set logged: 12 reps") StartRestTimer(90) // Use exercise's rest seconds View: shows success message, displays rest timer, updates exercise card

Binding Flow (Assign Exercise to Day):

User Action (Binds Push-ups to Monday) Intent: BindExerciseToDay(exerciseId = "pushups-id", day = 1) ViewModel: handleIntent UseCase: linkExerciseWithWorkoutDay.invoke(exerciseId, day) Repository: bindExerciseToDay(exerciseId, day) Remote DataSource: api.bindExerciseToDay(exerciseId, workoutDayId = 1) Server API: POST /workout/exercises/bind Body: { exerciseId: "pushups-id", workoutDayDay: 1 } Server: 1. Get or create WorkoutDay for day 1 2. Find Exercise 3. Check for duplicate binding 4. Insert ExercisesWithWorkoutDays record 5. Return success Response: 200 OK Repository: returns Result<Unit> ViewModel: emits success effect, refreshes exercise-day map Effect: ShowSnackBar("Push-ups added to Monday") View: updates day badges on exercise card

5.4 Error Handling Pattern

Layer-by-Layer Error Handling:

Data Layer:

suspend fun getExercisesByDay(day: Int, dateTime: String): Result<List<Exercise>> { return try { val response = api.getExercisesByDay(day, dateTime) Result.success(response.map { it.toDomain() }) } catch (e: Exception) { Result.failure(NetworkException("Failed to fetch exercises", e)) } }

Domain Layer:

operator fun invoke(day: Int, dateTime: String): Result<List<Exercise>> { return repository.getExercisesByDay(day, dateTime) .map { exercises -> exercises.sortedBy { it.name } // Apply domain logic } }

Presentation Layer:

private fun getExercisesByDay(day: Int) { viewModelScope.launch { emitState { copy(isLoading = true) } workoutUseCases.getExercisesByDay(day, getCurrentDateTime()).fold( onSuccess = { exercises -> emitState { copy( exercises = exercises, daySelected = day, isLoading = false ) } }, onFailure = { error -> emitState { copy(isLoading = false) } emitEffect(WorkoutEffect.ShowSnackBar( message = error.toUserMessage(), type = SnackBarType.ERROR )) } ) } } fun Throwable.toUserMessage(): String = when (this) { is NetworkException -> "Network error. Please check your connection." is ValidationException -> this.message ?: "Validation failed" else -> "An unexpected error occurred" }

5.5 Offline and data policy (Mobile)

Workout reads with a local datasource use BaseRepository.doRequest combined with DataPolicyMode (force_online, cache_fallback, remote_only) from Remote Config / env; many endpoints remain remote-only. The canonical description lives in Offline & sync (tiers, policy resolution, soft UX for tasks/habits).


Data Models & Schema

6.1 Domain Models (Shared - Kotlin Multiplatform)

File Location: /shared/src/commonMain/kotlin/com/esteban/ruano/oter/models/

@Serializable data class Exercise( val id: String? = null, val name: String, val description: String? = null, val restSecs: Int? = null, val baseSets: Int? = null, val baseReps: Int? = null, val muscleGroup: String? = null, // FULL_BODY, CORE, LEGS, CHEST, BACK, SHOULDERS, ARMS, GLUTES, CARDIO val equipment: List<Equipment>? = emptyList(), val resource: Resource? = null ) @Serializable data class WorkoutDay( val id: String, val day: Int, // 1-7 (Monday-Sunday) val time: String? = null, // HH:mm format val name: String, // "Monday", "Leg Day", etc. val exercises: List<Exercise> = emptyList(), val isCompleted: Boolean = false ) @Serializable data class ExerciseSet( val id: String, val reps: Int, val doneDateTime: String ) @Serializable data class ExerciseDayStatus( val exerciseTrackId: String, val exerciseId: String, val setsCountDone: Int, val totalRepsDone: Int, val exerciseDone: Boolean, val setsDone: List<ExerciseSet> ) @Serializable data class CreateExerciseSetTrack( val exerciseId: String, val workoutDayId: String, val reps: Int, val doneDateTime: String ) @Serializable data class CreateWorkoutTrack( val dayId: Int, val doneDateTime: String ) @Serializable data class CreateExerciseTrack( val exerciseId: String, val workoutDayId: String, val doneDateTime: String ) @Serializable data class WorkoutTrack( val id: String, val workoutDayId: String, val doneDateTime: String, val status: String = "ACTIVE" ) @Serializable data class ExerciseTrack( val id: String, val exerciseId: String, val workoutDayId: String, val doneDateTime: String? = null, val dateTime: String? = null, val status: String = "ACTIVE" ) @Serializable data class UpdateWorkoutDay( val id: String? = null, val day: Int? = null, val time: String? = null, val name: String? = null, val exercises: List<Exercise>? = null ) @Serializable data class WorkoutDashboard( val workoutDays: List<WorkoutDay>, val totalExercises: Int ) enum class MuscleGroup { FULL_BODY, CORE, LEGS, CHEST, BACK, SHOULDERS, ARMS, GLUTES, CARDIO }

6.2 Database Schema (Server - PostgreSQL)

WorkoutDays Table:

CREATE TABLE workout_days ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), name VARCHAR(255) NOT NULL, day INTEGER NOT NULL, -- 1-7 for Mon-Sun time TIME, user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, status VARCHAR(10) NOT NULL DEFAULT 'ACTIVE', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, CONSTRAINT valid_day CHECK (day >= 1 AND day <= 7), UNIQUE(user_id, day) -- One workout day per user per weekday ); CREATE INDEX idx_workout_days_user ON workout_days(user_id); CREATE INDEX idx_workout_days_day ON workout_days(day); CREATE INDEX idx_workout_days_status ON workout_days(status);

Exercises Table:

CREATE TABLE exercises ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), name VARCHAR(255) NOT NULL, description TEXT, rest_secs INTEGER, base_sets INTEGER, base_reps INTEGER, muscle_group VARCHAR(20), -- FULL_BODY, CORE, LEGS, CHEST, BACK, SHOULDERS, ARMS, GLUTES, CARDIO resource_id UUID REFERENCES resources(id), user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, status VARCHAR(10) NOT NULL DEFAULT 'ACTIVE', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX idx_exercises_user ON exercises(user_id); CREATE INDEX idx_exercises_muscle_group ON exercises(muscle_group); CREATE INDEX idx_exercises_status ON exercises(status); CREATE INDEX idx_exercises_name ON exercises(name);

ExercisesWithWorkoutDays Table (Junction):

CREATE TABLE exercises_with_workout_days ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), exercise_id UUID NOT NULL REFERENCES exercises(id) ON DELETE CASCADE, workout_day_id UUID NOT NULL REFERENCES workout_days(id) ON DELETE CASCADE, user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, UNIQUE(exercise_id, workout_day_id) -- Prevent duplicate bindings ); CREATE INDEX idx_ex_wd_exercise ON exercises_with_workout_days(exercise_id); CREATE INDEX idx_ex_wd_workout_day ON exercises_with_workout_days(workout_day_id); CREATE INDEX idx_ex_wd_user ON exercises_with_workout_days(user_id);

WorkoutTracks Table:

CREATE TABLE workout_tracks ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), workout_day_id UUID NOT NULL REFERENCES workout_days(id) ON DELETE CASCADE, done_date_time TIMESTAMP NOT NULL, user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, status VARCHAR(10) NOT NULL DEFAULT 'ACTIVE', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX idx_workout_tracks_workout_day ON workout_tracks(workout_day_id); CREATE INDEX idx_workout_tracks_user ON workout_tracks(user_id); CREATE INDEX idx_workout_tracks_date ON workout_tracks(done_date_time); CREATE INDEX idx_workout_tracks_status ON workout_tracks(status);

ExerciseTracks Table:

CREATE TABLE exercise_tracks ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), exercise_id UUID NOT NULL REFERENCES exercises(id) ON DELETE CASCADE, workout_day_id UUID NOT NULL REFERENCES workout_days(id) ON DELETE CASCADE, done_date_time TIMESTAMP, -- When exercise marked complete date_time TIMESTAMP, -- Tracking timestamp (alternative) user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, status VARCHAR(10) NOT NULL DEFAULT 'ACTIVE', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX idx_exercise_tracks_exercise ON exercise_tracks(exercise_id); CREATE INDEX idx_exercise_tracks_workout_day ON exercise_tracks(workout_day_id); CREATE INDEX idx_exercise_tracks_user ON exercise_tracks(user_id); CREATE INDEX idx_exercise_tracks_done_date ON exercise_tracks(done_date_time); CREATE INDEX idx_exercise_tracks_status ON exercise_tracks(status);

ExerciseSetTracks Table:

CREATE TABLE exercise_set_tracks ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), exercise_track_id UUID NOT NULL REFERENCES exercise_tracks(id) ON DELETE CASCADE, reps INTEGER NOT NULL, done_date_time TIMESTAMP NOT NULL, user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, status VARCHAR(10) NOT NULL DEFAULT 'ACTIVE', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, CONSTRAINT positive_reps CHECK (reps > 0) ); CREATE INDEX idx_exercise_set_tracks_track ON exercise_set_tracks(exercise_track_id); CREATE INDEX idx_exercise_set_tracks_user ON exercise_set_tracks(user_id); CREATE INDEX idx_exercise_set_tracks_date ON exercise_set_tracks(done_date_time); CREATE INDEX idx_exercise_set_tracks_status ON exercise_set_tracks(status);

6.3 Data Relationships

User (1) ←→ (N) WorkoutDays User (1) ←→ (N) Exercises WorkoutDay (1) ←→ (N) ExercisesWithWorkoutDays ←→ (N) Exercise (Many-to-Many: One exercise on multiple days, one day has multiple exercises) WorkoutDay (1) ←→ (N) WorkoutTracks (One day can be completed multiple times on different dates) Exercise (1) ←→ (N) ExerciseTracks WorkoutDay (1) ←→ (N) ExerciseTracks (ExerciseTrack represents one exercise session on one day) ExerciseTrack (1) ←→ (N) ExerciseSetTracks (One exercise session contains multiple sets)

API Specifications

7.1 Base URL

Production: https://api.oterapp.com/workout Development: http://localhost:8080/workout

7.2 Authentication

All endpoints require JWT bearer token:

Authorization: Bearer <token>

User ID extracted from token, not passed in request.

7.3 Endpoints

GET /workout/dashboard

Description: Get workout dashboard summary

Response:

{ "workoutDays": [ { "id": "uuid", "day": 1, "time": "06:00", "name": "Monday", "exercises": [...], "isCompleted": false } ], "totalExercises": 24 }

GET /workout/days

Description: Get all workout days with exercises

Response:

[ { "id": "uuid", "day": 1, "time": "06:00", "name": "Monday", "exercises": [ { "id": "uuid", "name": "Bench Press", "description": "Chest exercise", "restSecs": 90, "baseSets": 3, "baseReps": 10, "muscleGroup": "CHEST", "equipment": [], "resource": null } ], "isCompleted": false } ]

GET /workout/days/{workoutDayId}

Description: Get specific workout day

Path Parameters:

  • workoutDayId (UUID): Workout day ID

Response: Single WorkoutDay object (same structure as above)

Error: 404 if not found

PATCH /workout/days/{workoutDayId}

Description: Update workout day

Path Parameters:

  • workoutDayId (UUID): Workout day ID

Request Body:

{ "id": "uuid", "day": 1, "time": "07:00", "name": "Leg Day", "exercises": [...] }

Response:

  • 200 OK: Updated workout day
  • 404 Not Found
  • 403 Forbidden

GET /workout/byDay/{day}

Description: Get exercises for specific day with completion status

Path Parameters:

  • day (integer 1-7): Day of week

Query Parameters:

  • dateTime (ISO datetime, optional): Date to check completion status (default: now)

Response:

[ { "id": "uuid", "name": "Bench Press", "description": "Chest exercise", "restSecs": 90, "baseSets": 3, "baseReps": 10, "muscleGroup": "CHEST", "equipment": [], "resource": null } ]

Note: This returns exercises only, not WorkoutDay wrapper. Use GET /workout/exercise-tracking/completed/{workoutDayId} to get completion status.

GET /workout/exercises

Description: Get paginated, filtered exercises

Query Parameters:

  • filter (string): Search by name
  • limit (integer, default: 50): Results per page
  • offset (integer, default: 0): Page number

Response:

[ { "id": "uuid", "name": "Bench Press", ... } ]

GET /workout/exercises/{id}

Description: Get single exercise by ID

Path Parameters:

  • id (UUID): Exercise ID

Response: Single Exercise object

Error: 404 if not found or unauthorized

POST /workout/exercises

Description: Create new exercise

Request Body:

{ "name": "Bench Press", "description": "Chest exercise", "restSecs": 90, "baseSets": 3, "baseReps": 10, "muscleGroup": "CHEST" }

Response:

  • 201 Created
  • Location header: /workout/exercises/{id}
  • Body: Created exercise

Errors:

  • 400 Bad Request: Validation errors

PATCH /workout/exercises/{id}

Description: Update existing exercise

Path Parameters:

  • id (UUID): Exercise ID

Request Body: Same as POST

Response:

  • 200 OK: Updated exercise
  • 404 Not Found
  • 403 Forbidden

DELETE /workout/exercises/{id}

Description: Soft delete exercise

Path Parameters:

  • id (UUID): Exercise ID

Response:

  • 204 No Content
  • 404 Not Found
  • 403 Forbidden

POST /workout/exercises/bind

Description: Bind exercise to workout day

Request Body:

{ "exerciseId": "uuid", "workoutDayDay": 1 }

Response:

  • 200 OK
  • 409 Conflict: Already bound

POST /workout/exercises/unbind

Description: Unbind exercise from workout day

Request Body:

{ "exerciseId": "uuid", "workoutDayDay": 1 }

Response:

  • 200 OK
  • 404 Not Found

Note: Endpoint naming inconsistency - should be DELETE /workout/exercises/bind

POST /workout/tracking/complete

Description: Mark workout as complete

Request Body:

{ "dayId": 1, "doneDateTime": "2025-12-29T06:30:00" }

Response:

  • 201 Created
  • Body: Track ID

DELETE /workout/tracking/{trackId}

Description: Undo workout completion

Path Parameters:

  • trackId (UUID): Workout track ID

Response:

  • 204 No Content
  • 404 Not Found

GET /workout/tracking/week

Description: Get workouts completed per day this week

Response:

[0, 1, 1, 0, 1, 0, 0]

Format: Array of 7 integers (counts for Sun-Sat or Mon-Sun)

GET /workout/tracking/range

Description: Get workout tracks by date range

Query Parameters:

  • startDate (ISO date): Start date
  • endDate (ISO date): End date

Response:

[ { "id": "uuid", "workoutDayId": "uuid", "doneDateTime": "2025-12-29T06:30:00", "status": "ACTIVE" } ]

DELETE /workout/tracking/track/{trackId}

Description: Delete specific workout track

Path Parameters:

  • trackId (UUID): Track ID

Response:

  • 204 No Content
  • 404 Not Found

POST /workout/exercise-tracking/sets/complete

Description: Log exercise set

Request Body:

{ "exerciseId": "uuid", "workoutDayId": "uuid", "reps": 12, "doneDateTime": "2025-12-29T09:30:00" }

Response:

  • 201 Created
  • Body: Set ID

DELETE /workout/exercise-tracking/sets/{setTrackId}

Description: Delete exercise set

Path Parameters:

  • setTrackId (UUID): Set track ID

Response:

  • 204 No Content
  • 404 Not Found

POST /workout/exercise-tracking/complete

Description: Mark exercise as complete

Request Body:

{ "exerciseId": "uuid", "workoutDayId": "uuid", "doneDateTime": "2025-12-29T09:45:00" }

Response:

  • 201 Created
  • Body: Exercise track ID

DELETE /workout/exercise-tracking/{trackId}

Description: Undo exercise completion

Path Parameters:

  • trackId (UUID): Exercise track ID

Response:

  • 204 No Content
  • 404 Not Found

GET /workout/exercise-tracking/range

Description: Get exercise tracks by date range

Query Parameters:

  • startDate (ISO date): Start date
  • endDate (ISO date): End date

Response:

[ { "id": "uuid", "exerciseId": "uuid", "workoutDayId": "uuid", "doneDateTime": "2025-12-29T09:45:00", "dateTime": "2025-12-29T09:45:00", "status": "ACTIVE" } ]

GET /workout/exercise-tracking/completed/{workoutDayId}

Description: Get completed exercises for workout day

Path Parameters:

  • workoutDayId (UUID): Workout day ID

Query Parameters:

  • dateTime (ISO datetime, optional): Date to check (default: now)

Response:

[ { "exerciseTrackId": "uuid", "exerciseId": "uuid", "setsCountDone": 3, "totalRepsDone": 30, "exerciseDone": true, "setsDone": [ { "id": "uuid", "reps": 12, "doneDateTime": "2025-12-29T09:30:00" }, { "id": "uuid", "reps": 10, "doneDateTime": "2025-12-29T09:32:00" }, { "id": "uuid", "reps": 8, "doneDateTime": "2025-12-29T09:34:00" } ] } ]

UI/UX Guidelines

8.1 Visual Design Principles

Color Coding

  • Exercise Completed: Green checkmark, green border
  • Set Logged: Blue accent
  • Workout Completed: Gold/yellow star or badge
  • In Progress: Orange/amber accent
  • Not Started: Neutral/gray
  • Error State: Red accents

Muscle Group Colors (Optional Enhancement)

  • CHEST: Blue
  • BACK: Green
  • LEGS: Purple
  • ARMS: Orange
  • SHOULDERS: Teal
  • CORE: Yellow
  • FULL_BODY: Gray
  • GLUTES: Pink
  • CARDIO: Red

Typography

  • Heading 1 (Screen title): Bold, 24-28px
  • Heading 2 (Day name): Semi-bold, 20-22px
  • Heading 3 (Exercise name): Semi-bold, 16-18px
  • Body: Regular, 14-16px
  • Caption (metadata): Regular, 12-14px
  • Set Data: Monospace, 14px (for alignment)

8.2 Iconography

Recommended Icons:

  • Add Exercise: Plus icon
  • Edit: Pencil icon
  • Delete: Trash icon
  • Complete Exercise: Checkmark or check-circle
  • Complete Workout: Star or trophy
  • Log Set: Plus-circle or clipboard
  • Rest Timer: Clock or timer icon
  • Muscle Group: Muscle/body icon
  • Schedule: Calendar icon
  • History: Clock-arrow or history icon

8.3 Loading States

Skeleton Screens:

  • Exercise cards with shimmer effect
  • Match layout of actual content
  • Show immediately while data loads

Progress Indicators:

  • Indeterminate spinner for indefinite operations
  • Progress bar for known operations (e.g., syncing offline data)

8.4 Empty States

No Exercises in Library:

+----------------------------------+ | [Illustration: Dumbbell] | | | | No exercises yet | | Create your first exercise to | | start tracking workouts | | | | [Create Exercise button] | +----------------------------------+

No Exercises for Selected Day:

+----------------------------------+ | [Illustration: Calendar] | | | | No exercises for Monday | | Assign exercises to this day | | | | [View All Exercises button] | +----------------------------------+

No Workout History:

+----------------------------------+ | [Illustration: Chart] | | | | No workout history yet | | Start logging your workouts | | | | [Go to Workout button] | +----------------------------------+

8.5 Feedback & Notifications

Success Messages:

  • “Exercise created successfully”
  • “Set logged: 12 reps”
  • “Exercise completed”
  • “Workout completed”
  • “Exercise added to Monday”

Error Messages:

  • “Failed to load exercises. Please try again.”
  • “Failed to log set. Check your connection.”
  • “Exercise not found”
  • “You don’t have permission to edit this exercise”

Confirmation Dialogs:

  • Delete Exercise: “Are you sure you want to delete [Exercise Name]? This action cannot be undone.”
  • Exceed Base Sets: “You’re adding set 4, but target is 3 sets. Continue?”
  • Discard Changes: “You have unsaved changes. Discard them?“

8.6 Accessibility

Contrast:

  • Ensure WCAG AA compliance (4.5:1 for normal text, 3:1 for large text)
  • Use color AND icons/text to convey state

Screen Reader Support:

  • Label all interactive elements
  • Announce set logging: “Set 3 logged: 12 reps”
  • Announce timer: “Rest timer: 90 seconds remaining”

Keyboard Navigation:

  • All actions accessible via keyboard
  • Visible focus indicators
  • Logical tab order

Touch Targets:

  • Minimum 44x44 points (iOS) or 48x48 dp (Android)
  • Adequate spacing between set log buttons

Implementation Checklist

9.1 Backend Implementation Checklist

  • Database Schema

    • Create WorkoutDays table
    • Create Exercises table
    • Create ExercisesWithWorkoutDays table
    • Create WorkoutTracks table
    • Create ExerciseTracks table
    • Create ExerciseSetTracks table
    • Set up foreign keys and cascades
    • Create indexes for performance
    • Add constraints (day 1-7, positive reps, etc.)
  • Server Models (DTOs)

    • Define ExerciseDTO
    • Define WorkoutDayDTO
    • Define UpdateWorkoutDayDTO
    • Define WorkoutTrackDTO
    • Define ExerciseTrackDTO
    • Define CreateExerciseSetTrackDTO
    • Define CreateWorkoutTrackDTO
    • Define CreateExerciseTrackDTO
    • Define ExerciseDayStatusDTO
    • Define WorkoutDashboardDTO
  • Repository Layer

    • Implement WorkoutRepository
    • Implement CRUD methods for exercises
    • Implement CRUD methods for workout days
    • Implement binding/unbinding methods
    • Implement tracking methods (workout, exercise, set)
  • Service Layer

    • Implement WorkoutService
    • Implement getDashboard
    • Implement getWorkoutDays / getWorkoutDayById
    • Implement getExercises with filtering/pagination
    • Implement createExercise / updateExercise / deleteExercise
    • Implement getExercisesByDay with completion status
    • Implement bindExerciseToDay / unbindExerciseFromDay
    • Implement completeWorkout / unCompleteWorkout
    • Implement completeExercise / unCompleteExercise
    • Implement completeExerciseSet / unCompleteExerciseSet
    • Implement getWorkoutsCompletedPerDayThisWeek
    • Implement getWorkoutTracksByDateRange
    • Implement getExerciseTracksByDateRange
    • Implement getExerciseSetStatusForDay
  • Routing

    • Implement GET /workout/dashboard
    • Implement GET /workout/days
    • Implement GET /workout/days/{workoutDayId}
    • Implement PATCH /workout/days/{workoutDayId}
    • Implement GET /workout/byDay/{day}
    • Implement GET /workout/exercises
    • Implement GET /workout/exercises/{id}
    • Implement POST /workout/exercises
    • Implement PATCH /workout/exercises/{id}
    • Implement DELETE /workout/exercises/{id}
    • Implement POST /workout/exercises/bind
    • Implement POST /workout/exercises/unbind (fix to DELETE)
    • Implement POST /workout/tracking/complete
    • Implement DELETE /workout/tracking/{trackId}
    • Implement GET /workout/tracking/week
    • Implement GET /workout/tracking/range
    • Implement DELETE /workout/tracking/track/{trackId}
    • Implement POST /workout/exercise-tracking/sets/complete
    • Implement DELETE /workout/exercise-tracking/sets/{setTrackId}
    • Implement POST /workout/exercise-tracking/complete
    • Implement DELETE /workout/exercise-tracking/{trackId}
    • Implement GET /workout/exercise-tracking/range
    • Implement GET /workout/exercise-tracking/completed/{workoutDayId}
    • Add authentication to all endpoints
    • Add request validation
  • Converters

    • Implement Exercise entity to DTO converter
    • Implement WorkoutDay entity to DTO converter
    • Implement WorkoutTrack entity to DTO converter
    • Implement ExerciseTrack entity to DTO converter
    • Implement ExerciseSetTrack entity to DTO converter
    • Implement DTO to entity converters
  • Testing

    • Unit tests for service layer
    • Integration tests for API endpoints
    • Test binding/unbinding logic
    • Test three-level tracking (workout, exercise, set)
    • Test completion status queries
    • Test authentication and authorization
    • Test soft delete behavior

9.2 Desktop Implementation Checklist

  • Shared Models

    • Define Exercise model
    • Define WorkoutDay model
    • Define ExerciseSet model
    • Define ExerciseDayStatus model
    • Define CreateExerciseSetTrack model
    • Define CreateWorkoutTrack model
    • Define CreateExerciseTrack model
    • Define WorkoutTrack model
    • Define ExerciseTrack model
    • Define UpdateWorkoutDay model
    • Define WorkoutDashboard model
    • Define MuscleGroup enum
  • Data Layer

    • Create WorkoutApi interface
    • Implement API client
    • Create WorkoutDataSource
    • Create WorkoutRepository implementation
    • Implement DTO to domain model mappers
    • Add error handling
  • Domain Layer

    • Define WorkoutRepository interface
    • Create GetWorkoutDashboard use case
    • Create GetWorkoutDays use case
    • Create GetWorkoutDayById use case
    • Create GetExercisesByDay use case
    • Create GetAllExercises use case
    • Create AddExercise use case
    • Create UpdateExercise use case
    • Create DeleteExercise use case
    • Create BindExerciseToDay use case
    • Create UnbindExerciseFromDay use case
    • Create AddExerciseSet use case
    • Create DeleteExerciseSet use case
    • Create CompleteExercise use case
    • Create UncompleteExercise use case
    • Create CompleteWorkout use case
    • Create UncompleteWorkout use case
    • Create GetWorkoutsCompletedThisWeek use case
  • Presentation Layer (MVI Pattern)

    • Define WorkoutState
    • Define WorkoutIntent
    • Define WorkoutEffect
    • Create WorkoutViewModel
    • Define ExerciseFormState
    • Define ExerciseFormIntent
    • Create ExerciseFormViewModel
  • UI Components

    • Create DaySelectorChips composable
    • Create ExerciseCard composable
    • Create ExerciseCardExpanded composable (with set logging)
    • Create SetLoggingInput composable
    • Create ExerciseForm dialog/screen
    • Create ManageScheduleDialog composable
    • Create WorkoutSummary composable
    • Create RestTimerOverlay composable (optional)
    • Create ExerciseList composable
    • Create ExerciseGrid composable
  • Screens

    • Create WorkoutDashboardScreen
    • Create DailyWorkoutScreen (day view)
    • Create AllExercisesScreen (library view)
    • Create WorkoutHistoryScreen
    • Implement navigation between screens
  • Features

    • Implement day selection (1-7 + All)
    • Implement exercise filtering (name search, muscle group)
    • Implement exercise sorting
    • Implement pagination for exercise list
    • Implement set logging with warnings
    • Implement set editing (newly created only)
    • Implement set deletion
    • Implement exercise completion
    • Implement workout completion
    • Implement exercise binding/unbinding
    • Implement exercise CRUD operations
    • Implement weekly summary display
    • Implement exercise-day mapping
  • Testing

    • Unit tests for ViewModels
    • Unit tests for use cases
    • UI tests for critical flows (log set, complete workout)

9.3 Mobile Implementation Checklist

(Similar to desktop, but with mobile-specific considerations)

  • Mobile-Specific UI

    • Implement mobile ExerciseCard (compact)
    • Implement mobile SetLoggingInput (inline)
    • Implement mobile FilterBottomSheet
    • Implement mobile QuickActionsBottomSheet
    • Implement mobile RestTimerOverlay (full-screen or notification)
    • Implement swipe gestures on exercise cards
    • Implement swipe gestures on logged sets
    • Implement pull-to-refresh
    • Implement floating action button
  • Mobile Enhancements

    • Rest timer with vibration/sound
    • Volume buttons for quick set logging
    • Screen keep-awake during workout
    • Voice commands for hands-free logging
    • Workout mode (immersive full-screen)
  • Offline Support (if applicable)

    • Create local database (Room/SQLDelight)
    • Create LocalDataSource
    • Implement caching logic for exercises and days
    • Implement sync queue for offline set logging
    • Add offline mode toggle in settings
    • Conflict resolution strategy

End of Workout Feature Expert Guide


This document serves as the single source of truth for all workout feature implementations. Developers should refer to this guide when building or modifying workout-related functionality across any platform.

For questions or clarifications, contact the architecture team or submit a documentation update request.