package db import ( "context" "time" ) const serviceColumns = "id, tenant_id, name, duration_minutes, buffer_before_minutes, buffer_after_minutes, price_cents, intake_fields::text, created_at" func (r *PGRepository) ListServicesByTenant(ctx context.Context, tenantID string) ([]ServiceRecord, error) { rows, err := r.pool.Query(ctx, ` SELECT `+serviceColumns+` FROM services WHERE tenant_id = $1 ORDER BY created_at ASC `, tenantID) if err != nil { return nil, err } defer rows.Close() var records []ServiceRecord for rows.Next() { var record ServiceRecord if err := rows.Scan( &record.ID, &record.TenantID, &record.Name, &record.DurationMinutes, &record.BufferBeforeMinutes, &record.BufferAfterMinutes, &record.PriceCents, &record.IntakeFields, &record.CreatedAt, ); err != nil { return nil, err } records = append(records, record) } return records, rows.Err() } func (r *PGRepository) GetServiceByID(ctx context.Context, serviceID string) (ServiceRecord, error) { var record ServiceRecord err := r.pool.QueryRow(ctx, ` SELECT `+serviceColumns+` FROM services WHERE id = $1 `, serviceID).Scan( &record.ID, &record.TenantID, &record.Name, &record.DurationMinutes, &record.BufferBeforeMinutes, &record.BufferAfterMinutes, &record.PriceCents, &record.IntakeFields, &record.CreatedAt, ) return record, err } func (r *PGRepository) CreateService(ctx context.Context, params CreateServiceParams) (ServiceRecord, error) { var record ServiceRecord err := r.pool.QueryRow(ctx, ` INSERT INTO services (tenant_id, name, duration_minutes, buffer_before_minutes, buffer_after_minutes, price_cents, intake_fields) VALUES ($1, $2, $3, $4, $5, $6, $7::jsonb) RETURNING `+serviceColumns+` `, params.TenantID, params.Name, params.DurationMinutes, params.BufferBeforeMinutes, params.BufferAfterMinutes, params.PriceCents, params.IntakeFields).Scan( &record.ID, &record.TenantID, &record.Name, &record.DurationMinutes, &record.BufferBeforeMinutes, &record.BufferAfterMinutes, &record.PriceCents, &record.IntakeFields, &record.CreatedAt, ) return record, err } func (r *PGRepository) UpdateService(ctx context.Context, serviceID string, params UpdateServiceParams) (ServiceRecord, error) { var record ServiceRecord err := r.pool.QueryRow(ctx, ` UPDATE services SET name = COALESCE($2, name), duration_minutes = COALESCE($3, duration_minutes), buffer_before_minutes = COALESCE($4, buffer_before_minutes), buffer_after_minutes = COALESCE($5, buffer_after_minutes), price_cents = COALESCE($6, price_cents), intake_fields = COALESCE($7::jsonb, intake_fields) WHERE id = $1 RETURNING `+serviceColumns+` `, serviceID, params.Name, params.DurationMinutes, params.BufferBeforeMinutes, params.BufferAfterMinutes, params.PriceCents, params.IntakeFields).Scan( &record.ID, &record.TenantID, &record.Name, &record.DurationMinutes, &record.BufferBeforeMinutes, &record.BufferAfterMinutes, &record.PriceCents, &record.IntakeFields, &record.CreatedAt, ) return record, err } func (r *PGRepository) DeleteService(ctx context.Context, serviceID string) error { _, err := r.pool.Exec(ctx, `DELETE FROM services WHERE id = $1`, serviceID) return err } func (r *PGRepository) ListAvailabilityRulesByTenant(ctx context.Context, tenantID string) ([]AvailabilityRuleRecord, error) { rows, err := r.pool.Query(ctx, ` SELECT id, tenant_id, staff_id, day_of_week, starts_local, ends_local FROM availability_rules WHERE tenant_id = $1 ORDER BY day_of_week ASC, starts_local ASC `, tenantID) if err != nil { return nil, err } defer rows.Close() var records []AvailabilityRuleRecord for rows.Next() { var record AvailabilityRuleRecord if err := rows.Scan( &record.ID, &record.TenantID, &record.StaffID, &record.DayOfWeek, &record.StartsLocal, &record.EndsLocal, ); err != nil { return nil, err } records = append(records, record) } return records, rows.Err() } func (r *PGRepository) ListClassSessionsByTenant(ctx context.Context, tenantID string, from time.Time, limit int) ([]ClassSessionRecord, error) { rows, err := r.pool.Query(ctx, ` SELECT cs.id, cs.tenant_id, cs.template_id, cs.location_id, ct.title, cs.starts_at, cs.ends_at, cs.capacity, cs.schedule_id FROM class_sessions cs INNER JOIN class_templates ct ON ct.id = cs.template_id WHERE cs.tenant_id = $1 AND cs.starts_at >= $2 ORDER BY cs.starts_at ASC LIMIT $3 `, tenantID, from, limit) if err != nil { return nil, err } defer rows.Close() var records []ClassSessionRecord for rows.Next() { var record ClassSessionRecord if err := rows.Scan( &record.ID, &record.TenantID, &record.TemplateID, &record.LocationID, &record.Title, &record.StartsAt, &record.EndsAt, &record.Capacity, &record.ScheduleID, ); err != nil { return nil, err } records = append(records, record) } return records, rows.Err() }