// Slot Definition Types based on Backend API Contract // Contract: docs/owners/payloads/slot-definition-api-contract.md export type DayOfWeek = 0 | 1 | 2 | 3 | 4 | 5 | 6; // 0=Monday, 6=Sunday export interface SlotDefinitionRule { weekly: boolean; description?: string; } export interface SlotDefinitionRequest { court_id: number; dow: DayOfWeek; starts_at: string; // HH:MM:SS format duration_minutes: number; capacity: number; valid_from: string; // YYYY-MM-DD valid_to?: string; // YYYY-MM-DD, optional rule?: SlotDefinitionRule; } export interface SlotDefinition extends SlotDefinitionRequest { slot_definition_id: number; created_at: string; // ISO 8601 updated_at: string; // ISO 8601 updated_by_app_user_id: number; } export interface ValidationError { field: string; message: string; } export interface SlotDefinitionError { type: string; title: string; status: number; detail: string; code: string; errors?: ValidationError[]; } // Helper constants export const DAY_NAMES: Record = { 0: 'Monday', 1: 'Tuesday', 2: 'Wednesday', 3: 'Thursday', 4: 'Friday', 5: 'Saturday', 6: 'Sunday', }; export const DAY_NAMES_SHORT: Record = { 0: 'Mon', 1: 'Tue', 2: 'Wed', 3: 'Thu', 4: 'Fri', 5: 'Sat', 6: 'Sun', }; // Helper to format time for display export function formatTime(timeStr: string): string { // Convert HH:MM:SS to HH:MM return timeStr.substring(0, 5); } // Helper to format time for API export function formatTimeForAPI(timeStr: string): string { // Ensure HH:MM:SS format if (timeStr.length === 5) { return `${timeStr}:00`; } return timeStr; } // Helper to calculate end time export function calculateEndTime(startTime: string, durationMinutes: number): string { const [hours, minutes] = startTime.split(':').map(Number); const totalMinutes = hours * 60 + minutes + durationMinutes; const endHours = Math.floor(totalMinutes / 60); const endMinutes = totalMinutes % 60; return `${String(endHours).padStart(2, '0')}:${String(endMinutes).padStart(2, '0')}`; } // ============================================================================ // Bulk Operations (Generate & Clone) // ============================================================================ /** * Available presets for generate endpoint */ export type SlotDefinitionPreset = | 'workday_standard' // Mon-Fri, 8am-10pm, 90min | 'weekend_extended' // Sat-Sun, 7am-11pm, 90min | 'all_week_uniform' // Every day, 8am-10pm, 90min | 'hourly_daytime'; // Mon-Fri, 9am-6pm, 60min /** * Pattern details from preset */ export interface PresetPattern { days: DayOfWeek[]; start_time: string; // HH:MM:SS end_time: string; // HH:MM:SS duration_minutes: number; interval_minutes: number; capacity: number; } /** * Preset info from API (localized) */ export interface PresetInfo { key: SlotDefinitionPreset; title: string; // Localized title description: string; // Localized description pattern: PresetPattern; } /** * Get presets endpoint response */ export interface GetPresetsResponse { status: 'success'; data: { locale: string; presets: PresetInfo[]; }; } /** * Pattern overrides for customizing presets */ export interface PatternOverrides { days?: DayOfWeek[]; start_time?: string; // HH:MM:SS end_time?: string; // HH:MM:SS duration_minutes?: number; interval_minutes?: number; capacity?: number; } /** * Generate endpoint request */ export interface GenerateSlotDefinitionsRequest { pattern_type: SlotDefinitionPreset; court_ids: number[]; pattern_overrides?: PatternOverrides; valid_from?: string; // YYYY-MM-DD valid_to?: string; // YYYY-MM-DD } /** * Skipped slot info */ export interface SkippedSlot { court_id: number; dow: DayOfWeek; starts_at: string; reason: string; } /** * Generate endpoint response */ export interface GenerateSlotDefinitionsResponse { status: 'success'; data: { created_count: number; skipped_count?: number; created_definitions: SlotDefinition[]; skipped?: SkippedSlot[]; }; } /** * Clone endpoint request */ export interface CloneSlotDefinitionRequest { target_court_ids: number[]; target_days?: DayOfWeek[]; valid_from?: string; // YYYY-MM-DD valid_to?: string; // YYYY-MM-DD } /** * Clone endpoint response */ export interface CloneSlotDefinitionResponse { status: 'success'; data: { created_count: number; skipped_count?: number; created_definitions: SlotDefinition[]; skipped?: SkippedSlot[]; }; }