studioId. They all cascade from Client, and any query must first establish that the client belongs to the caller’s studio.
Trainee “days” follow the trainee device’s time zone (Client.timezone), not the studio’s. @db.Date columns hold the civil day. Timestamp columns hold instants.
WorkoutLog
Table workout_logs. One recorded workout session.
Indexes:
clientId, programId, (clientId, performedOn), (clientId, completed, performedOn).
entries
logWorkoutBody.entries accepts an array of free-form records. The app writes this shape from mobile/src/features/workouts/presentation/WorkoutDayScreen.tsx:
- A set the trainee never performed keeps its entry and
done: false, withrepsandweightasnull. Targets and placeholders are never written as if performed. - A performed set with no weight logs
weight: null. That is also how a bodyweight set is logged. riris a string and isnullwhen the trainee did not touch it.
readWorkoutLog(entries) in apps/core-api/src/modules/trainee/workout-log-view.ts is the tolerant reader. It skips non-objects, coerces numeric strings, counts a set as performed when done is true or, on older logs with no done flag, when it has reps or weight, and computes setCount and volumeKg (weight times reps over performed sets).
CardioLog
Table cardio_logs. A single aerobic session the trainee timed in the app or added by hand.
Index:
(clientId, performedOn).
DailyMetric
Table daily_metrics. One row per trainee per day.
Unique on
(clientId, date). Index on clientId.
measurements
Keys accepted by logMeasurementsBody: chest, waist, hips, arm, thigh, neck. Each is a positive number up to 500. At least one is required per write. A form’s body measurement field merges into the same object on submit.
HealthSample
Table health_samples. Raw samples from Apple Health or Health Connect, from the first version of the health sync.
Unique on
(clientId, type, source, externalId). Index on (clientId, type, startedAt).
The current sync is covered in Apple Health and Health Connect.
WeightEntry
Table weight_entries. Dedicated weigh-in history, separate from DailyMetric. It allows several entries per day and powers the weight view on the client card.
Indexes:
clientId, (clientId, recordedAt).
ClientAppDay
Table client_app_days. One row per day the trainee opened the app.
Unique on
(clientId, date). Client.lastCheckInAt only keeps the latest open, so “opened the app but logged nothing” needs this history.
ClientPhoto
Table client_photos. The trainee’s progress photo gallery.
Indexes:
clientId, (clientId, takenAt).
MealLog
Table meal_logs. One logged meal.
Indexes:
clientId, (clientId, consumedAt).
NutritionDayLog
Table nutrition_day_logs. The app’s snapshot of one nutrition day.
Unique on
(clientId, date).
FavoriteMeal
Table favorite_meals. A meal the trainee saved to log again.
Same columns as MealLog minus consumedAt: name (required), source (default MANUAL), note, items (nullable), the four macro totals and photoUrl. Index on clientId.
TechniqueVideo
Table technique_videos. A video the trainee filmed for coach review.
Indexes:
clientId, status, (clientId, exerciseId), (clientId, createdAt).
TechniqueVideoStatus
Which exercises ask for a video is configured in the training plan. See Training plan JSON.
Derived values that are not stored
- Personal records, last set history and the workout time estimate are computed from
WorkoutLog.entrieson read. - The movement summary (cardio and steps progress) is computed from
CardioLog,DailyMetric.stepsand the plan requirement. - The water goal comes from the nutrition plan day’s
targets.water.