The tracking module returns raw tracking rows for one trainee. It is read-only. The trainee app writes these rows through the trainee lane, and the richer computed views (adherence, personal records, weekly averages) live in Client tracking and Check-ins. The module follows the five-file pattern: tracking.routes.ts, tracking.controller.ts, tracking.service.ts, tracking.repository.ts, tracking.schema.ts.

Shared behaviour

All three endpoints parse the query with the same Zod schema, trackingListQuery, and run the same steps:
  1. The controller reads req.auth.studioId. A missing value throws UNAUTHORIZED.
  2. The service calls repo.clientInStudio(studioId, clientId). If the trainee is not in the caller’s studio it throws NOT_FOUND with the message client not found.
  3. The repository runs a findMany and a count in parallel with the same where, and returns items and total.
The tenancy check is on the trainee only. The module does not apply coach permission scoping from middleware/coach-access.ts, so a coach restricted to assigned trainees can still read any trainee in the studio through these routes.

Query parameters

string
required
The trainee id. Minimum length 1.
date
Lower bound, inclusive. Parsed with z.coerce.date(), so an ISO date or datetime string works.
date
Upper bound, inclusive. Parsed with z.coerce.date().
integer
default:"1"
Page number, positive.
integer
default:"20"
Rows per page. Positive, maximum 500.

Response envelope

Every endpoint returns the same wrapper inside data:
items are full Prisma rows with no field selection, so every column of the model is returned.

Errors

Endpoints

GET /v1/web/tracking/daily-metrics

Lists DailyMetric rows for a trainee, newest date first. Auth: web lane, any role. from and to filter on date, a @db.Date column with one row per trainee per day (@@unique([clientId, date])). Rows are ordered by date descending. Response: data.items is an array of DailyMetric rows.
waterLogs and measurements are Json columns. Their inner shape is written by the trainee module, not by this one, so the example values above are illustrative only.

GET /v1/web/tracking/meal-logs

Lists MealLog rows for a trainee, newest first. Auth: web lane, any role. from and to filter on consumedAt. Rows are ordered by consumedAt descending. Response: data.items is an array of MealLog rows.
source is the Prisma MealSource enum and defaults to PLAN. items is a required Json column holding the logged foods. Its inner shape is owned by the trainee module.

GET /v1/web/tracking/workout-logs

Lists WorkoutLog rows for a trainee, newest first. Auth: web lane, any role. from and to filter on performedOn. Rows are ordered by performedOn descending. Both completed and unfinished logs are returned. There is no filter on completed. Response: data.items is an array of WorkoutLog rows.
entries is a required Json column holding the logged exercises and sets. Its inner shape is owned by the trainee module.