Daily programming — code map
API
Base path: /organizations/:orgId/daily-programming
| Route | Method | Service method |
|---|---|---|
PUT / | Whole-day replace | set |
GET /?weekStart= | Week read | findByWeek |
GET /?date= | Date read | findByDate |
PATCH /status | Flip draft↔published | setStatus |
DELETE /?classTypeId&date | Physical remove | remove |
GET /board?weekStart=&programId= | Week board; programId narrows rows to one program’s class types, optional | getBoard |
POST /copy-week | Copy last week’s published cells as drafts | copyWeek |
POST /publish-week | Publish every draft cell on a week | publishWeek |
POST /quick-workout | Board quick freeform note → publish | createQuickWorkout |
Plus GET /organizations/:orgId/workouts/posted?weekStart=&classTypeId=&page= on WorkoutsController, delegating to DailyProgrammingService.listPosted (WorkoutsModule imports DailyProgrammingModule for this).
Controller: apps/api/src/daily-programming/daily-programming.controller.ts
Service: apps/api/src/daily-programming/daily-programming.service.ts
Module: apps/api/src/daily-programming/daily-programming.module.ts
Cross-feature internal export: findByDateRange(orgId, startDate, endDate, publishedOnly) is consumed by ClassSessionsService. Section payloads come from the shared toWorkoutSectionResponses in apps/api/src/common/workout-section.ts — the one serializer every member-facing section read uses. getSessionCountsInWindow (private) is the shared “does this class type have a live session on this date” lookup getBoard uses.
DTOs
apps/api/src/daily-programming/dto/set-daily-programming.dto.ts—SetDailyProgrammingDto(classTypeId,date,status?,entry?: 'pick'|'quick_note'|'paste',workouts:[{workoutId, sortOrder}]).- Inline
SetStatusDtoin the controller —classTypeId,date,status: 'draft'|'published'. dto/copy-week.dto.ts,dto/publish-week.dto.ts,dto/quick-workout.dto.ts— the board’s three new writes.
Tracking
libs/shared/src/lib/analytics/programming-events.ts — ProgrammingEvents.WOD_PUBLISHED ('wod_published') and ProgrammingEvents.WOD_POST_COMPLETED ('wod_post_completed'), emitted via EventTrackingService.track. See the README’s Analytics bullet for exactly which writes fire which.
Web
| Path | File |
|---|---|
| Class session detail (consumer) | apps/web/src/app/[lang]/(protected)/dashboard/schedule/sessions/[sessionId]/page.tsx |
| Paste page (board round-trip) | apps/web/src/app/[lang]/(protected)/dashboard/workouts/new/paste/page.tsx → paste-workout-view.tsx. Publish rides on the parse commit request itself (publishTo) rather than a second PUT /daily-programming call. |
There is no standalone Programming route. The Schedule surface owns the Programming matrix beside Calendar and Condensed.
Components
apps/web/src/hooks/use-programming-week-actions.ts—useProgrammingWeekActions({ orgId, weekStartIso, programId?, programName?, rows, sessions }): the Spotter handler forWeeklyCalendar’s toolbar. Carries onlydraftWithSpotter— copy-week/publish-week mutations were removed with the standalone board, their only caller.draftWithSpotteris async: it best-effort fetches last week’s board and the program’s saved schedule templates (useApi().fetchWithAuth, each swallowed on failure — the affected section is just omitted), builds the message withbuildSpotterWeekPrompt(apps/web/src/lib/programming/spotter-week-prompt.ts, unit-tested pure function — locale-aware date/day formatting, per-class-type status line composition, last-wee…apps/web/src/components/overview/schedule/weekly-calendar.tsx— withenableClassStudioExtras(the Schedule screen), the calendar exposes Calendar, Condensed, and Programming display modes. Programming consumes the existing board query, renders its class-type × day matrix, writesPUT /daily-programming, and clears same-day session overrides so every live session inherits the shared workout.apps/web/src/components/overview/schedule/daily-programming-view.tsx— presentational programming matrix. It renders an assignment target only when a class type has live sessions on that date.apps/web/src/components/overview/schedule/weekly-condensed.tsx— each condensed row shows the session’s workout name (data-testid="condensed-session-workout"); no board-specific props remain.apps/web/src/components/overview/schedule/setup-timetable-menu.tsx— ashowClassTypesprop (Schedule screen only) adds a “Class types” item (data-testid="schedule-setup-class-types") opening a sheet withProgramClassTypesTabfor the schedule program.apps/web/src/components/overview/programming/week-grid.tsx— presentational “row × 7 days” grid shared with the coaching program grid (member-week-grid.tsx, see the workout-assignments code map). Owns the header (translated day labels, today highlight), sticky first column, cell box model/skeleton (weekGridCellBaseClass); callers supplyrows,renderCell, and optionallyonCellClick/renderRowActions. Its only current caller ismember-week-grid.tsx— the class-type × day board that used it (programming-week-board.tsx) was deleted with the standalone board.apps/web/src/components/overview/workouts/posted-by-day-view.tsx— the Workouts page’s “Posted by day” segmented view; toggled fromapps/web/src/app/[lang]/(protected)/dashboard/workouts/page.tsx.enableClassStudioExtras(passed only by the Schedule screen) decides whetherWeeklyCalendarfetches the board and shows its actions at all.
DB
libs/db/src/lib/schema/scheduling.ts:
daily_programmingtable. See data-model.md. No schema change for the board — it composes existingdaily_programming+class_sessions+workouts.is_library.
Shared
libs/shared/src/lib/schemas/daily-programming.schema.ts — response/request types for the existing endpoints plus the board (ProgrammingBoardResponse, BoardCellState, CopyWeekResponse, PublishWeekResponse, PostedWorkoutRow/PostedWorkoutsResponse).
libs/shared/src/lib/agent-schemas/daily-programming.ts — the daily_programming agent router’s input schemas. libs/shared/src/lib/agent-schemas/class-sessions.ts — classSessionsCopyWeekInputSchema (programId, fromWeekStart, toWeekStart), the Spotter-facing mirror of CopyWeekDto.
Tests
daily-programming.service.unit.spec.ts—findByDateRangelive/dead-programme filtering.daily-programming.week-board.unit.spec.ts—getBoardcell-state derivation,copyWeek,publishWeek.daily-programming.events.unit.spec.ts—wod_published/wod_post_completedemission fromset, andcreateQuickWorkout.apps/api/src/ai/agent/tools/leaves/class-sessions.copy-week.tools.unit.spec.ts—class_sessions.copy_week’s decorator metadata (router/action/scope/sideEffects/confirm/audit) and delegation toClassSessionsService.copyWeek.apps/web/src/lib/programming/spotter-week-prompt.unit.spec.ts—buildSpotterWeekPrompt: locale-aware date/day formatting (en/he/ru), per-class-type status line composition and omissions, the “no sessions this week” case, the ran-last-week diff.apps/web/src/components/overview/schedule/weekly-calendar.int.spec.tsx(describe('Schedule screen extras')) — the folded-in toolbar (Spotter button in both views, draft-session bulk actions inside the timetable menu, the condensed Add-a-session row — all gated onenableClassStudioExtras), and the Class types menu item (gated onenableClassStudioExtrasalone).apps/web/src/components/overview/workouts/posted-by-day-view.int.spec.tsx,.../workouts/workouts-page.int.spec.tsx— Posted-by-day listing, Save to Workouts, and the segmented control.- Apply-from-template path is exercised by:
apps/web/e2e/specs/schedule-setup.spec.tsapps/web/e2e/specs/session-sheets.spec.ts