Skip to Content
Living documentation — last reviewed 2026-05-28
FeaturesProgram TemplatesProgram templates — code map

Program templates — code map

API (NestJS)

Base path: /organizations/:orgId/program-templates

RouteMethodService method
POST /createValidates mode-specific shape, inserts header
GET /listFilters: deliveryMode, q, tags[]
GET /:idgetByIdReturns header + cells
PATCH /:idupdateHeader-only; mode/duration changes
DELETE /:idremoveSoft delete
POST /:id/duplicateduplicateHeader + cells deep copy with ” (copy)” suffix
POST /:id/workoutsupsertCellsReplace full cell grid
POST /:id/previewpreviewDry-run apply, returns the would-be result
POST /:id/applyapplyMaterialize into target
POST /from-historyfromHistorySave-as-template from existing materialized rows

Controller: apps/api/src/program-templates/program-templates.controller.ts Service: apps/api/src/program-templates/program-templates.service.ts (~1119 lines) Module: apps/api/src/program-templates/program-templates.module.ts

DTOs (apps/api/src/program-templates/dto/)

  • create-program-template.dto.tsCreateProgramTemplateDto
  • update-program-template.dto.tsUpdateProgramTemplateDto
  • upsert-cells.dto.tsUpsertProgramTemplateCellsDto
  • apply-template.dto.tsApplyProgramTemplateDto, PreviewProgramTemplateDto, ConflictMode
  • schedule-slot.dto.tsScheduleSlotDto, ScheduleOccurrenceDto
  • from-history.dto.tsFromHistoryDto

Key service methods

  • requireCoach(orgId, clerkId) — role gate (owner / admin / coach).
  • loadTemplateWithCells(orgId, id) — single fetch with cells, used by most read paths.
  • assertModeMatches(template, dtoMode) — apply / preview safety check.
  • applyToCoaching(template, cells, dto, orgId, authorUserId) — bulk inserts workout_assignments. Conflict modes: skip (default), abort, overwrite.
  • applyToSchedule(template, slots, dto, orgId, clerkId) — expands the slots, drops or replaces anything clashing with the live schedule, and writes class_sessions in one transaction. Same conflict modes as coaching; overwrite skips sessions that have bookings.
  • buildPlannedSessions(template, slots, dto, orgId) — the expansion, shared by preview and apply so both see the same set.
  • loadExistingSessions(classTypeIds, planned) — live sessions in the applied window plus their booking counts (two grouped queries, no correlated subquery).
  • normalizeOccurrences(occurrences) — dedupes and (day, time)-sorts a slot on every write.
  • buildCoachingPreview, buildSchedulePreview — mode-specific dry-run.
  • readHistoryCells(orgId, dto) — normalize existing materialized rows back to cells (used by from-history).
  • assertClassTypeInOrg, assertLocationInOrg, assertMembershipInOrg, assertLibraryWorkoutInOrg — referential integrity helpers.

Pure modules (no DB, unit-tested)

  • session-conflicts.tsfindSessionConflicts(planned, existing) and groupConflicts(...). The single definition of a schedule clash, shared by preview and apply.
  • addDays(date, n), toUtc(date, time, tz) — date math helpers (uses date-fns-tz fromZonedTime).

Web (Next.js)

RouteFile
Templates list (per program)apps/web/src/app/[lang]/(protected)/dashboard/programs/[id]/templates/page.tsx
Template detailapps/web/src/app/[lang]/(protected)/dashboard/programs/[id]/templates/[templateId]/page.tsx
Cell editorapps/web/src/app/[lang]/(protected)/dashboard/programs/[id]/templates/[templateId]/cells/[week]/[day]/edit/page.tsx

Components

  • apps/web/src/components/shared/program-grid/ — generic week × day grid used by templates and by workout-assignments coaching view (cell-shell.tsx, cell-menu.tsx, empty-cell-menu.tsx, note-prompt-dialog.tsx).
  • Apply / preview / save-as-template dialogs: search components/overview/programming/ (TODO: verify exact filenames — appears mixed across overview/programming and dashboard).

DB

libs/db/src/lib/schema/program-templates.ts:

  • program_templates — header rows.
  • program_template_slots — schedule recurrence: one row per class type, occurrences[] as (day, time) pairs.
  • program_template_workouts — cell rows (week × day × slot).

See data-model.md.

Shared

libs/shared/src/lib/schemas/program-template.schema.ts — Zod response shapes (TODO: verify exact exports). Delivery mode enum lives in workout.schema.ts as deliveryModeValues.

Tests

  • TODO: verify presence of program-templates.service.unit.spec.ts (not present at time of writing). The bulk apply logic is currently covered indirectly via e2e flows in apps/web/e2e/specs/coaching-grid.spec.ts and schedule-setup.spec.ts.
  • Cross-check apps/web/e2e/specs/coaching-grid.spec.ts for template-apply UI flows.