feat(availability): per-staff working hours and class/session management

- Working-hours repo accepts nullable staff scope (pgx + memory); staff
  rules live in availability_rules and drive public slot generation
- Protected routes for staff working hours, class templates, and class
  sessions with tenant-ownership checks and input validation
- Sessions derive endsAt/capacity from templates; list resolves location
  names and live booked counts; template delete cascades sessions,
  session delete preserves bookings via SET NULL (mirrored in memory)
- Zones page: staff-scope selector on working hours, new Classes tab
  with template/session CRUD, CS+EN strings, mobile tab wrapping
- Normalize availability_rules times to HH:MM in the service layer

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 11:01:01 +02:00
1 parent d9aa29dbcb
commit 0405106587
12 files changed
+2069 -58

No files matched your search

+39
View File
@@ -368,6 +368,45 @@ type UpdateBlockedDayRequest struct {
Type string `json:"type,omitempty"`
}
// ============================================
// CLASS MODELS
// ============================================
type ClassTemplate struct {
ID string `json:"id"`
TenantID string `json:"tenantId"`
Title string `json:"title"`
DurationMinutes int `json:"durationMinutes"`
Capacity int `json:"capacity"`
CreatedAt time.Time `json:"createdAt"`
}
type UpsertClassTemplateRequest struct {
Title string `json:"title" binding:"required"`
DurationMinutes int `json:"durationMinutes" binding:"required"`
Capacity int `json:"capacity" binding:"required"`
}
type ClassSession 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"`
StartsAt time.Time `json:"startsAt"`
EndsAt time.Time `json:"endsAt"`
Capacity int `json:"capacity"`
BookedCount int `json:"bookedCount"`
}
type CreateClassSessionRequest struct {
TemplateID string `json:"templateId" binding:"required"`
LocationID *string `json:"locationId,omitempty"`
StartsAt string `json:"startsAt" binding:"required"` // RFC3339
Capacity *int `json:"capacity,omitempty"`
}
// ============================================
// CUSTOMER MODELS
// ============================================