You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

202 lines
4.5 KiB
TypeScript

// 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<DayOfWeek, string> = {
0: 'Monday',
1: 'Tuesday',
2: 'Wednesday',
3: 'Thursday',
4: 'Friday',
5: 'Saturday',
6: 'Sunday',
};
export const DAY_NAMES_SHORT: Record<DayOfWeek, string> = {
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[];
};
}