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
  • 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.
  • applyToFeed(template, cells, dto, orgId, authorUserId) — bulk inserts workout_feed_posts. Skips non-workout cells.
  • applyToSchedule(template, cells, _dto, orgId, authorUserId) — bulk inserts class_sessions + daily_programming in one transaction.
  • buildCoachingPreview, buildFeedPreview, 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.
  • 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_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.