Workout planner
A React block (a whole screen) in Beamline's Salon & fitness app.
A training plan a person lives in: the program's next session with its estimate and Start, an exercise replaced for this session only or for every later session (chosen on a strip of the coming sessions before anything changes), routines in folders, and an editor of typed set rows (weight × reps, a rep range, a time) with each exercise's sets drawn to scale; a coach's folder says who set it, what they see and what may be changed.
Use it for
- Planning repeated physical training: a program's next session, routines in folders, exercises with typed set targets, a coach's assigned routines.
- Any plan of repeated rounds with typed targets where a change can be for one occurrence or for every later one (rehab plans, practice drills).
Not for
- Logging a workout set by set while doing it (a live session screen).
- Scheduling sessions on dates (team-calendar or event-calendar).
- A team's tasks by stage (project-board).
Anatomy
- library: the program's next session (when, program and week, exercise count, estimated time), then folders that fold, each with its routines (name, exercises in a line, an Edited mark) and a menu (Duplicate, Move to folder, Move up/down); New folder at the top
- coach card inside a coach's folder: who, since when, the coach's notes, what they see, what the person may change, Remove coach access asked in place; Accept and Decline while invited
- detail header: where it comes from (program, week, day and date, or the folder), the name (editable for a routine), exercise and working-set counts and the estimated time, then the one main action: Save routine while a routine has unsaved changes, Start workout otherwise
- exercise card: superset letter, name, muscles and the sets in one line, the shape of the sets drawn to scale, a changed target's mark (80 → 82.5 kg) that opens why, the exercise menu, the note with its reach, rest after each set, and the set rows
- set rows: a type marker per set (W, 1, 2, F, D) opening the set type menu with each type's consequence; weight with its unit heading (kg or lb), reps with its heading (fixed or a range), or time; Add set
- replacement: picked in the exercise library, then a strip of the program's sessions that have the exercise, with a cut from this session only to every later one; the sentence, the counts and the button follow the cut; a receipt with Undo after
- coming up (wide room, next session): the program's following sessions with their exercises, and exercises not suggested again with Restore
- exercise library sheet: search, equipment and muscle filters with counts, each exercise with what it trains and whether it is already in the plan, its detail (how to, your history) keeping the filters and the choice; Add N exercises, or Choose for a replacement
States
- plan ready (the next session with its estimate)
- replacement search (the library opens on the replaced exercise's main muscle)
- scope undecided (the strip open, nothing changed yet; Cancel or Escape drops the replacement)
- modified draft (changed for this session only, said with Undo session changes)
- start pending (Start shows working), start failed (the reason with Try again)
- stale program conflict (Start waits for Reload; the session's changes stay)
- a workout in progress (Start waits, Resume goes back to it)
- empty library (says what a routine is, New routine), empty folder (Add a routine), empty routine (Add exercises)
- incomplete set (Save marks the set in place and says which exercise), dirty draft (Edited in the library, Discard changes), save rejected (the reason with Try again; the draft stays)
- mixed measurement types (weight × reps, reps alone, time, weight for a time) and rep ranges
- grouped exercises (a superset letter and an edge in the card)
- invitation pending, assigned plan, restricted edit (structure locked, values open, said), access revoked (the routines become the person's own)
- no matches in the library (Clear filters), selected several (Add N exercises), already added (In this plan)
- target changed (from → to, why, based on which sessions) and still learning (no previous value invented)
- loading (the library holds its shape), failed to load (the reason with Try again)
- no program (the next session's place says where it will show)
- narrow room: the library or the plan, with Back; phone: the library sheet full width and focus back on the exercise being changed
Keyboard
- every menu (routine, folder, exercise, set type, unit, reps mode) is a menu button: Enter or Space opens it, arrows move, Escape closes
- Move up and Move down in the routine and exercise menus are the keyboard and touch route for reordering
- the replacement strip's cut moves with ← and → (the consequence preview's own keys); Escape folds it with nothing changed
- set fields are number fields: ↑ ↓ step, Shift or Page Up/Down take large steps; a typed "225 lb" in a kg field converts
- the routine name is an inline edit: Enter saves, Escape cancels
- the library sheet traps focus; closing it returns focus to the exercise's menu or to Add exercises
Motion
Rows and cards keep their path when they move, arrive and leave (the shared FLIP). A set's column in the drawn shape glides to a new weight by its height (a transform) and to a new count by the shared FLIP's resize, at the fast duration; a unit change moves nothing because the stored weight did not change. Folders open to their content's height. The replacement unfolds in place and its cut follows the hand (the consequence preview's own motion). Reduced motion places everything at once.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
exercises (required) | Exercise[] | — | The exercise library. |
folders / defaultFolders / onFoldersChange | PlanFolder[] | — | Folders in order; a folder with `coach` is a coach's assignment (access pending, active or revoked; canEditStructure; what they see). |
routines / defaultRoutines / onRoutinesChange | Routine[] | — | Routines in library order with their folder id (null for none), revision and items. Moving, duplicating and saving report the whole new list. |
program / defaultProgram / onProgramChange | Program | null | — | The person's program; sessions[0] is the next session. A replacement for every later session changes it. |
excluded / defaultExcluded / onExcludedChange | string[] | — | Exercise ids the program must not suggest again; Restore takes one back. |
openPlan / defaultOpenPlan / onOpenPlanChange | "next" | string | — | What is open: the program's next session or a routine id. |
onSaveRoutine | (routine: Routine) => void | Promise | — | Saves a routine; reject with a reason to refuse it (the draft stays). |
onReplace | (replacement: { from, to, scope: "session" | "program", sessionIds }) => void | Promise | — | Stores a confirmed replacement; reject to put it back. |
onStart | (start: { from: "program", sessionId, items } | { from: "routine", routineId, items }) => void | Promise | — | Starts a workout; reject with a reason to say why it didn't. |
activeSession / onResume | { title, startedAt } | null / () => void | — | A workout already in progress: Start waits for it and Resume goes back to it. |
conflict / onReloadProgram | { by, at } | null / () => void | — | The program changed elsewhere after it loaded: Start waits for Reload; the session's own changes stay. |
onCoachAccess | (folderId, action: "accept" | "decline" | "remove") => void | Promise | — | Accept or decline a coach's invitation, or remove a coach's access. |
equipment / muscles | string[] | — | equipment: Equipment in the order the picker's filter shows it; default: as it first appears in the library. muscles: Muscles in the order the picker's filter shows them; default: as they first appear in the library. |
loading / error / onRetry | boolean / Error | null / () => void | — | The library holds its shape while loading; an error shows its message with Try again. |
product / now | string / number | — | product: Your app's name, shown at the top of the library. now: Now (ms), for "today" and relative dates; default the clock. |
equipment | string[] | — | Equipment in the order the picker's filter shows it; default: as it first appears in the library. |
muscles | string[] | — | Muscles in the order the picker's filter shows them; default: as they first appear in the library. |
folders | PlanFolder[] | — | |
defaultFolders | PlanFolder[] | — | |
onFoldersChange | ((folders: PlanFolder[]) => void) | — | |
routines | Routine[] | — | Routines in library order (within a folder, the order they show in). |
defaultRoutines | Routine[] | — | |
onRoutinesChange | ((routines: Routine[]) => void) | — | |
program | Program | null | — | The person's program; null when they follow none. |
defaultProgram | Program | null | — | |
onProgramChange | ((program: Program | null) => void) | — | |
excluded | string[] | — | Exercises the program must not suggest again (restorable from the Next up panel). |
defaultExcluded | string[] | — | |
onExcludedChange | ((ids: string[]) => void) | — | |
openPlan | string | — | What is open: "next" or a routine id. |
defaultOpenPlan | string | — | |
onOpenPlanChange | ((open: string) => void) | — | |
activeSession | { title: string; startedAt: number; } | null | — | A workout already in progress: Start waits for it, Resume goes back to it. |
onResume | (() => void) | — | |
conflict | { by: string; at: number; } | null | — | The program changed elsewhere (a coach, another device) after this screen loaded it. |
onReloadProgram | (() => void) | — | |
loading | boolean | — | |
error | Error | null | — | |
onRetry | (() => void) | — | |
product | string | — | Your app's name, shown at the top of the library. |
now | number | — | Now (ms), for "today" and relative dates; default the clock. |
className | string | — | Classes on the documented visual root; no override by default. |
ref | Ref<HTMLDivElement> | — | Receives the documented native target or imperative handle; omitted by default. |
Built from
- target-table
- button
- collapsible
- dropdown-menu
- number-field
- select
- inline-edit
- textarea
- popover
- input
- sheet
- search-field
- chip-group
- checkbox
- consequence-preview
- confirm-morph
- avatar
- badge
- alert
- empty-state
- skeleton
Dependencies
lucide-react
Import
import { WorkoutPlanner } from "@beamline/workout-planner";
Get it
Workout planner is part of Beamline UI. Install the parts you need in your React app as compiled packages or editable source. Get Beamline · Connect your agent
More in Salon & fitness
- Workout logger: A live training session: every exercise's sets as target-table rows with a done column; marking a set done fills its shape and starts the rest by itself…
- Training progress: A person's training history: the headline counts (weeks in a row, days since the last workout, workouts this month), a month calendar that names each…
- Program setup: A training program's setup and living settings: the intake (one question per step, the week drawn as you answer, a failed generation keeping every…
- Protocol builder: A practitioner's reusable protocol and the client copies made from it. Foods and habits each carry a written intent (Include, Reduce, Exclude) in its…
- Nutrition library: A nutrition practice's library. Recipes: ingredients with grams and servings, nutrients per serving summed (energy, protein, carbohydrate, fat, fibre), a…
- Packages & memberships: Prepaid packages and series, built: the packages down the side (version and how many sold, or not on sale yet), the open one beside them. Its price is a…
- Client forms: A salon's or spa's client forms and who they ask: the forms down the side with where each stands (on, set up and off, not set up yet), the open one…
- Instructor finder: Finding a flight instructor. Search by name or airfield, filter by what you want to learn (each subject with how many teach it nearby), by distance, and…
- Flight logbook: A pilot's logbook. Total time (with hours brought forward from earlier logbooks), PIC, night, the last 90 days and how many entries are still incomplete…