Add Phase 4b: instructor blocked dates, store closures, and substitute instructors

- New tables: instructor_blocked_date, store_closure (migration 0034)
- substitute_instructor_id column added to lesson_session
- Session generation skips blocked instructor dates and store closure periods
- Substitute assignment validates sub is not blocked and has no conflicting slot
- Routes: POST/GET/DELETE /instructors/:id/blocked-dates, POST/GET/DELETE /store-closures
- 15 new integration tests covering blocked dates, store closures, and sub validation
This commit is contained in:
Ryan Moon
2026-03-30 10:29:13 -05:00
parent aae5a022a8
commit 5cd2d05983
8 changed files with 541 additions and 6 deletions

View File

@@ -104,9 +104,28 @@ export type LessonSessionNotesInput = z.infer<typeof LessonSessionNotesSchema>
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<typeof LessonSessionUpdateSchema>
// --- 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<typeof InstructorBlockedDateCreateSchema>
// --- 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<typeof StoreClosureCreateSchema>
// --- Grading Scale schemas ---
export const GradingScaleLevelInput = z.object({