import { z } from 'zod' /** Coerce empty strings to undefined — solves HTML form inputs sending '' for blank optional fields */ function opt(schema: T) { return z.preprocess((v) => (v === '' ? undefined : v), schema.optional()) } // --- Enums --- export const LessonFormat = z.enum(['private', 'group']) export type LessonFormat = z.infer // --- Instructor schemas --- export const InstructorCreateSchema = z.object({ userId: opt(z.string().uuid()), displayName: z.string().min(1).max(255), bio: opt(z.string()), instruments: z.array(z.string()).optional(), }) export type InstructorCreateInput = z.infer export const InstructorUpdateSchema = InstructorCreateSchema.partial() export type InstructorUpdateInput = z.infer // --- Lesson Type schemas --- export const LessonTypeCreateSchema = z.object({ name: z.string().min(1).max(255), instrument: opt(z.string().max(100)), durationMinutes: z.coerce.number().int().min(5).max(480), lessonFormat: LessonFormat.default('private'), rateWeekly: opt(z.coerce.number().min(0)), rateMonthly: opt(z.coerce.number().min(0)), rateQuarterly: opt(z.coerce.number().min(0)), }) export type LessonTypeCreateInput = z.infer export const LessonTypeUpdateSchema = LessonTypeCreateSchema.partial() export type LessonTypeUpdateInput = z.infer // --- Schedule Slot schemas --- export const ScheduleSlotCreateSchema = z.object({ instructorId: z.string().uuid(), lessonTypeId: z.string().uuid(), dayOfWeek: z.coerce.number().int().min(0).max(6), startTime: z.string().regex(/^\d{2}:\d{2}$/, 'Must be HH:MM format'), room: opt(z.string().max(100)), maxStudents: z.coerce.number().int().min(1).default(1), rateWeekly: opt(z.coerce.number().min(0)), rateMonthly: opt(z.coerce.number().min(0)), rateQuarterly: opt(z.coerce.number().min(0)), }) export type ScheduleSlotCreateInput = z.infer export const ScheduleSlotUpdateSchema = ScheduleSlotCreateSchema.partial() export type ScheduleSlotUpdateInput = z.infer // --- Enrollment schemas --- export const EnrollmentStatus = z.enum(['active', 'paused', 'cancelled', 'completed']) export type EnrollmentStatus = z.infer export const EnrollmentCreateSchema = z.object({ memberId: z.string().uuid(), accountId: z.string().uuid(), scheduleSlotId: z.string().uuid(), instructorId: z.string().uuid(), startDate: z.string().min(1), endDate: opt(z.string()), rate: opt(z.coerce.number().min(0)), billingInterval: opt(z.coerce.number().int().min(1)), billingUnit: opt(z.enum(['day', 'week', 'month', 'quarter', 'year'])), notes: opt(z.string()), }) export type EnrollmentCreateInput = z.infer export const EnrollmentUpdateSchema = EnrollmentCreateSchema.omit({ memberId: true, accountId: true, scheduleSlotId: true, instructorId: true, }).partial() export type EnrollmentUpdateInput = z.infer export const EnrollmentStatusUpdateSchema = z.object({ status: EnrollmentStatus, }) export type EnrollmentStatusUpdateInput = z.infer // --- Lesson Session schemas --- export const LessonSessionStatus = z.enum(['scheduled', 'attended', 'missed', 'makeup', 'cancelled']) export type LessonSessionStatus = z.infer export const LessonSessionStatusUpdateSchema = z.object({ status: LessonSessionStatus, }) export type LessonSessionStatusUpdateInput = z.infer export const LessonSessionNotesSchema = z.object({ instructorNotes: opt(z.string()), memberNotes: opt(z.string()), homeworkAssigned: opt(z.string()), nextLessonGoals: opt(z.string()), topicsCovered: z.array(z.string()).optional(), }) export type LessonSessionNotesInput = z.infer export const LessonSessionUpdateSchema = z.object({ actualStartTime: opt(z.string().regex(/^\d{2}:\d{2}$/, 'Must be HH:MM format')), actualEndTime: opt(z.string().regex(/^\d{2}:\d{2}$/, 'Must be HH:MM format')), substituteInstructorId: opt(z.string().uuid()), }) export type LessonSessionUpdateInput = z.infer // --- Instructor Blocked Date schemas --- export const InstructorBlockedDateCreateSchema = z.object({ startDate: z.string().min(1), endDate: z.string().min(1), reason: opt(z.string().max(255)), }) export type InstructorBlockedDateCreateInput = z.infer // --- Store Closure schemas --- export const StoreClosureCreateSchema = z.object({ name: z.string().min(1).max(255), startDate: z.string().min(1), endDate: z.string().min(1), }) export type StoreClosureCreateInput = z.infer // --- Grading Scale schemas --- export const GradingScaleLevelInput = z.object({ value: z.string().min(1).max(50), label: z.string().min(1).max(255), numericValue: z.coerce.number().int().min(0).max(100), colorHex: opt(z.string().regex(/^#[0-9A-Fa-f]{6}$/, 'Must be #RRGGBB format')), sortOrder: z.coerce.number().int(), }) export const GradingScaleCreateSchema = z.object({ name: z.string().min(1).max(255), description: opt(z.string()), isDefault: z.boolean().default(false), levels: z.array(GradingScaleLevelInput).min(1), }) export type GradingScaleCreateInput = z.infer export const GradingScaleUpdateSchema = z.object({ name: z.string().min(1).max(255).optional(), description: opt(z.string()), isDefault: z.boolean().optional(), }) export type GradingScaleUpdateInput = z.infer // --- Lesson Plan schemas --- export const LessonPlanItemStatus = z.enum(['not_started', 'in_progress', 'mastered', 'skipped']) export type LessonPlanItemStatus = z.infer const LessonPlanItemInput = z.object({ title: z.string().min(1).max(255), description: opt(z.string()), gradingScaleId: opt(z.string().uuid()), targetGradeValue: opt(z.string().max(50)), sortOrder: z.coerce.number().int(), }) const LessonPlanSectionInput = z.object({ title: z.string().min(1).max(255), description: opt(z.string()), sortOrder: z.coerce.number().int(), items: z.array(LessonPlanItemInput).default([]), }) export const LessonPlanCreateSchema = z.object({ memberId: z.string().uuid(), enrollmentId: z.string().uuid(), title: z.string().min(1).max(255), description: opt(z.string()), startedDate: opt(z.string()), sections: z.array(LessonPlanSectionInput).default([]), }) export type LessonPlanCreateInput = z.infer export const LessonPlanUpdateSchema = z.object({ title: z.string().min(1).max(255).optional(), description: opt(z.string()), isActive: z.boolean().optional(), }) export type LessonPlanUpdateInput = z.infer export const LessonPlanItemUpdateSchema = z.object({ title: z.string().min(1).max(255).optional(), description: opt(z.string()), status: LessonPlanItemStatus.optional(), gradingScaleId: opt(z.string().uuid()), currentGradeValue: opt(z.string().max(50)), targetGradeValue: opt(z.string().max(50)), notes: opt(z.string()), sortOrder: z.coerce.number().int().optional(), }) export type LessonPlanItemUpdateInput = z.infer // --- Lesson Plan Template schemas --- export const SkillLevel = z.enum(['beginner', 'intermediate', 'advanced', 'all_levels']) export type SkillLevel = z.infer const TemplateItemInput = z.object({ title: z.string().min(1).max(255), description: opt(z.string()), gradingScaleId: opt(z.string().uuid()), targetGradeValue: opt(z.string().max(50)), sortOrder: z.coerce.number().int(), }) const TemplateSectionInput = z.object({ title: z.string().min(1).max(255), description: opt(z.string()), sortOrder: z.coerce.number().int(), items: z.array(TemplateItemInput).default([]), }) export const LessonPlanTemplateCreateSchema = z.object({ name: z.string().min(1).max(255), description: opt(z.string()), instrument: opt(z.string().max(100)), skillLevel: SkillLevel.default('all_levels'), sections: z.array(TemplateSectionInput).default([]), }) export type LessonPlanTemplateCreateInput = z.infer export const LessonPlanTemplateUpdateSchema = z.object({ name: z.string().min(1).max(255).optional(), description: opt(z.string()), instrument: opt(z.string().max(100)), skillLevel: SkillLevel.optional(), isActive: z.boolean().optional(), }) export type LessonPlanTemplateUpdateInput = z.infer export const TemplateInstantiateSchema = z.object({ memberId: z.string().uuid(), enrollmentId: z.string().uuid(), title: opt(z.string().min(1).max(255)), }) export type TemplateInstantiateInput = z.infer // --- Grade History schemas --- export const GradeCreateSchema = z.object({ gradingScaleId: opt(z.string().uuid()), gradeValue: z.string().min(1).max(50), sessionId: opt(z.string().uuid()), notes: opt(z.string()), }) export type GradeCreateInput = z.infer // --- Session Plan Items schemas --- export const SessionPlanItemsSchema = z.object({ lessonPlanItemIds: z.array(z.string().uuid()).min(1), }) export type SessionPlanItemsInput = z.infer