feat(classes): weekly recurring schedules with lazy session materialization

class_schedules rules generate concrete session rows for a rolling 28-day
horizon — no cron; materialize runs idempotently on dashboard and public
reads via a partial unique index on (schedule_id, starts_at). Rule delete
stops future sessions; existing sessions keep bookings and overrides.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
Tomas DvorakandDevin committed 2026-09-18 17:31:17 +02:00
1 parent 7b9080eb1c
commit ad60167a31
15 files changed
+1029 -7

No files matched your search

+25
View File
@@ -405,6 +405,9 @@ type ClassSession struct {
BookedCount int `json:"bookedCount"`
// WaitlistCount is the number of customers queued for a free seat.
WaitlistCount int `json:"waitlistCount"`
// ScheduleID links the session back to the recurring rule that generated
// it; nil for manually created sessions.
ScheduleID *string `json:"scheduleId,omitempty"`
}
// SessionRosterEntry is one booking on a class session roster. WaitlistPosition
@@ -447,6 +450,28 @@ type CreateClassSessionRequest struct {
Capacity *int `json:"capacity,omitempty"`
}
// ClassSchedule is a weekly recurring rule for a class template. Concrete
// sessions are materialized for a rolling horizon; DayOfWeek follows Go's
// time.Weekday (Sunday=0) and StartsLocal is an "HH:MM" tenant-local time.
type ClassSchedule struct {
ID string `json:"id"`
TenantID string `json:"tenantId"`
TemplateID string `json:"templateId"`
TemplateTitle string `json:"templateTitle"`
LocationID *string `json:"locationId,omitempty"`
LocationName string `json:"locationName,omitempty"`
DayOfWeek int `json:"dayOfWeek"`
StartsLocal string `json:"startsLocal"`
Active bool `json:"active"`
}
type CreateClassScheduleRequest struct {
TemplateID string `json:"templateId" binding:"required"`
LocationID *string `json:"locationId,omitempty"`
DayOfWeek int `json:"dayOfWeek" binding:"min=0,max=6"`
StartsLocal string `json:"startsLocal" binding:"required"` // "HH:MM"
}
// ============================================
// CUSTOMER MODELS
// ============================================