Training progress

A React block (a whole screen) in Beamline's Salon & fitness app.

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 day's workouts with a tick per working set (Year and All shade a day by its workouts; days before history began hatched, never shown as rest), the chosen day as a full-height panel with each set drawn as its shape (weight as length against the exercise's best, a segment per rep) and the exercises logged most, and any exercise opened into its history and its records (bests that open the session they were set in; estimates said as estimates). Beside the calendar: Statistics (working sets per muscle, a week or a month a bar over a chosen range, a muscle's series hidden from its legend, a bucket before history missing rather than zero), the program's report (week by week against its goal, this week said as so far, each exercise's direction), Body (dated measurements with blanks kept, photos compared by date) and, in an exercise, Friends (a leaderboard with ties said) and Share (a preview of the record or graph card).

Use it for

  • Looking back at repeated training: which days someone trained, what they did in a session, how an exercise has gone and its bests.
  • Any history of repeated practice where a day's sessions and an item's best so far matter (rehab, music practice, drills).

Not for

Anatomy

  • head: Progress; the headline counts, each a large number with what it counts under it (9 weeks · in a row with a workout; 3 days · since your last workout; 13 workouts · in October); the section (Calendar, Statistics, Program when a program is given, Body when body is given); on Calendar, the density (Month, Year, All)
  • month (left, filling the height): the month's name with previous and next; seven columns of days, each its date, its sessions' names (+n past two) and a quiet ink tick per working set along its foot; days before history hatched, today's date ringed, the chosen day the selected paint
  • year and all: one cell per day shaded by its workouts (the look's accent ramp), days before history hatched, a key that says both
  • day panel (right, full height, a line apart): the date with Log another; its sessions (title, start time, duration or 'time not recorded'; each exercise its sets in words and each set drawn as target-table draws it, against the exercise's best, warm-ups faint, an unknown weight hatched) or No workout this day with Log a workout
  • most logged (under the day): the exercises with the most working sets in the range shown, ranked, each opening its exercise
  • exercise sheet: History (sessions newest first, each a link that opens it in full, its sets with headings repeated; Load older) and Records (a measure switch, a trend line in ink with a table view, bests with the session each was set in, best weight at each rep count)
  • statistics (the whole page under the head): Sets per muscle with its range (3 months, 6 months, All) and its bar (Week, Month) as two choices; stacked columns drawn by the block in the look's series, the six muscles with most sets each a series and the rest one; the legend hides a series; a finished bucket with no sets is an ink tick on the baseline (never a series colour), the bucket still going is outlined with a dotted tick ('so far'), one before history has an empty slot read as not recorded; hovering or focusing a column (←/→, Home/End) reads it above the plot, at rest the last finished one; where history starts said when the range reaches before it; under it a tile per muscle with its sets and its average per finished week or month
  • program report: the program week by week (a tick per workout against the goal's outlines, time with untimed sessions said, volume, sets; this week 'so far'; a week before history 'Not recorded', no numbers); volume each week as bars (a missing week hatched); beside it each exercise from its first program week to now, with its top set's line
  • body: the photos side by side by date (two date pickers and Swap; the dates are the pictures' labels and move with them; the same date twice said; one photo said as one); under the photos, what was measured on each photo's day and the change between them; the measurements as dated rows (a blank 'Not taken', never 0 or a dash), Edit and New entry opening a form in place (the date kept unless changed; a photo kept, replaced or removed; a failed save keeps everything typed); under them a measurement's trend with gaps for blanks
  • friends (an exercise's tab when given): ranked by the heaviest set, ties as =n, you on the open row's paint, a value opening its session (yours in History, a friend's through your app) or saying the session is no longer shared; those who haven't logged it last
  • share (in Records): the record card (the best of the chosen measure, its unit, its day; an estimate says so) or the graph card (the same dates and units as the export), your app's name on it; Share hands it to your app

States

  • recorded history, a day with one session, a day with two sessions (both named in the month, their sets added in its ticks)
  • empty day (No workout this day, Log a workout), a day before history (said, not counted as rest)
  • missing history distinct from zero (hatched before history began)
  • no sessions yet: weeks in a row and days since the last workout Not known, no Most logged
  • loading (the month holds its shape with a placeholder per day; the heatmap its own), failed to load (the reason with Try again; the counts Not known, no day, no Most logged)
  • duration not recorded; long held times in minutes (30 min), short in seconds
  • history: no sets, mixed set types, older history to load, a session opened in full (its rows read only) and Back to the same place
  • records: no history, a measure with no value (Not known), an estimated measure (said)
  • narrow room: the counts as a row of three under the title; a day in the month shows only its ticks (its names are in its label); the day and Most logged under the month; the sheet full width
  • statistics: a bucket before history (missing), a recorded zero, a hidden series, a muscle with under 0.1 sets a week
  • program: this week partial, weeks before history, a week with an untimed session
  • body: no photos, one photo, the same photo twice, a blank measurement, a failed save, a new entry
  • friends: a tie, a session no longer shared, someone who hasn't logged it

Keyboard

  • month: one tab stop on the chosen day; ←/→ a day (mirrored right to left), ↑/↓ a week, Page Up/Down a month; the day follows focus; days after today can't be chosen
  • year and all: the heatmap's grid, arrows by day and week, Enter chooses
  • the density and measure switches are segmented controls (arrows)
  • an exercise (from a session or Most logged) opens its sheet; closing it returns focus to the row that opened it
  • History's session link opens it in full; Back returns focus to that session's link
  • the section switch is a segmented control (arrows)
  • the photo dates are selects; Swap is a button; the slider between the photos moves with the arrows

Motion

Days take their hover and selected paint on the motion tokens; the sheet slides in; tabs glide their indicator. Nothing moves at rest. Reduced motion places at once.

Props

PropTypeDefaultDescription
sessions (required)LoggedSession[]—The sessions loaded, any order.
exercises (required)ProgressExercise[]—{ id, name, measure, muscles? } (Statistics counts sets against the first muscle).
historyStart (required)string—The first day your app has history for (ISO date): before it, nothing was recorded (hatched), never a rest.
weeklyStreaknumber | null—Weeks in a row with at least one session, as your app counts them; null when it can't tell.
day / defaultDay / onDayChangestring—The chosen day (ISO date); default the latest session's day.
density / defaultDensity / onDensityChange"month" | "year" | "all"—How much of the calendar shows.
onLogWorkout(day: string) => void—Starts a workout on a day with none (or adds another).
hasOlder / onLoadOlderboolean / () => void—More history before the loaded sessions.
today / weekStartsOnstring / 0 | 1—today: Today (ISO date); default the clock's. weekStartsOn: First weekday of the calendar: 0 Sunday, 1 Monday. Default 1.
loading / error / onRetryboolean / Error | null / () => void—Loading holds the calendar's shape; an error shows its message with Try again.
todaystring—Today (ISO date); default the clock's.
weeklyStreaknumber | null—Weeks in a row with at least one session, as your app counts them; null when it can't tell.
daystring—The chosen day (ISO date).
defaultDaystring—
onDayChange((day: string) => void)—
densityProgressDensity—
defaultDensityProgressDensity—
onDensityChange((density: ProgressDensity) => void)—
weekStartsOn0 | 1—First weekday of the calendar: 0 Sunday, 1 Monday. Default 1.
hasOlderboolean—More history exists before the loaded sessions: History offers Load older.
onLoadOlder(() => void)—
loadingboolean—
errorError | null—
onRetry(() => void)—
classNamestring—Classes on the documented visual root; no override by default.
refRef<HTMLDivElement>—Receives the documented native target or imperative handle; omitted by default.
section / defaultSection / onSectionChange"calendar" | "statistics" | "program" | "body"—The view under the head. Default calendar.
program{ title, startedOn, weeks, weeklyGoal } | null—The program being followed: Program shows its weeks against its goal. Without it there is no Program view.
body{ fields: BodyField[]; entries: BodyEntry[]; onSave(entry, photo: File | null | "remove") => Promise<BodyEntry> }—Body measurements and photos: the fields measured, the entries, and saving one (new or edited). Without it there is no Body view.
friends / onOpenFriendSessionRecord<exerciseId, FriendRow[]> / (row, exerciseId) => void—friends: Each exercise's leaderboard among friends, by exercise id: an exercise with one has a Friends tab. onOpenFriendSession: Opens the shared session behind a friend's value (one still shared).
onShare / shareBrand(request: { kind, exerciseId, metric }) => void / string—onShare: Shares a record or graph card the person previewed; posting it is your app's. shareBrand: Your app's name, carried by a share card.
sectionProgressSection—The view: the calendar, Statistics (sets per muscle), the program's report, Body.
defaultSectionProgressSection—
onSectionChange((section: ProgressSection) => void)—
friendsRecord<string, FriendRow[]>—Each exercise's leaderboard among friends, by exercise id: an exercise with one has a Friends tab.
onOpenFriendSession((row: FriendRow, exerciseId: string) => void)—Opens the shared session behind a friend's value (one still shared).
onShare((request: ShareRequest) => void)—Shares a record or graph card the person previewed; posting it is your app's.
shareBrandstring—Your app's name, carried by a share card.

Built from

Dependencies

lucide-react

Import

import { TrainingProgress } from "@beamline/training-progress";

Get it

Training progress 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 planner: 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…
  • 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…
  • 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…

Part of Beamline UI: polished, editable React components and complete screens for custom applications. Build your own style from any look; tokens, fonts, radii, motion; not only colours.