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

@@ -115,11 +115,31 @@ export const lessonSessions = pgTable('lesson_session', {
nextLessonGoals: text('next_lesson_goals'),
topicsCovered: text('topics_covered').array(),
makeupForSessionId: uuid('makeup_for_session_id'),
substituteInstructorId: uuid('substitute_instructor_id').references(() => instructors.id),
notesCompletedAt: timestamp('notes_completed_at', { withTimezone: true }),
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
})
export const instructorBlockedDates = pgTable('instructor_blocked_date', {
id: uuid('id').primaryKey().defaultRandom(),
instructorId: uuid('instructor_id')
.notNull()
.references(() => instructors.id),
startDate: date('start_date').notNull(),
endDate: date('end_date').notNull(),
reason: varchar('reason', { length: 255 }),
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
})
export const storeClosures = pgTable('store_closure', {
id: uuid('id').primaryKey().defaultRandom(),
name: varchar('name', { length: 255 }).notNull(),
startDate: date('start_date').notNull(),
endDate: date('end_date').notNull(),
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
})
export const gradingScales = pgTable('grading_scale', {
id: uuid('id').primaryKey().defaultRandom(),
name: varchar('name', { length: 255 }).notNull(),
@@ -210,6 +230,10 @@ export type Enrollment = typeof enrollments.$inferSelect
export type EnrollmentInsert = typeof enrollments.$inferInsert
export type LessonSession = typeof lessonSessions.$inferSelect
export type LessonSessionInsert = typeof lessonSessions.$inferInsert
export type InstructorBlockedDate = typeof instructorBlockedDates.$inferSelect
export type InstructorBlockedDateInsert = typeof instructorBlockedDates.$inferInsert
export type StoreClosure = typeof storeClosures.$inferSelect
export type StoreClosureInsert = typeof storeClosures.$inferInsert
export type GradingScale = typeof gradingScales.$inferSelect
export type GradingScaleInsert = typeof gradingScales.$inferInsert
export type GradingScaleLevel = typeof gradingScaleLevels.$inferSelect