Add lessons Phase 4: lesson sessions with hybrid calendar generation

Individual lesson occurrences generated from schedule slot patterns.
Idempotent session generation with configurable rolling window.
Post-lesson notes workflow with auto-set notesCompletedAt. Status
tracking (scheduled/attended/missed/makeup/cancelled) and date/time
filtering. 13 new tests (64 total lessons tests).
This commit is contained in:
Ryan Moon
2026-03-30 09:29:03 -05:00
parent 93405af3b2
commit 73360cd478
8 changed files with 587 additions and 3 deletions

View File

@@ -91,6 +91,35 @@ export const enrollments = pgTable('enrollment', {
updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
})
export const lessonSessionStatusEnum = pgEnum('lesson_session_status', [
'scheduled',
'attended',
'missed',
'makeup',
'cancelled',
])
export const lessonSessions = pgTable('lesson_session', {
id: uuid('id').primaryKey().defaultRandom(),
enrollmentId: uuid('enrollment_id')
.notNull()
.references(() => enrollments.id),
scheduledDate: date('scheduled_date').notNull(),
scheduledTime: time('scheduled_time').notNull(),
actualStartTime: time('actual_start_time'),
actualEndTime: time('actual_end_time'),
status: lessonSessionStatusEnum('status').notNull().default('scheduled'),
instructorNotes: text('instructor_notes'),
memberNotes: text('member_notes'),
homeworkAssigned: text('homework_assigned'),
nextLessonGoals: text('next_lesson_goals'),
topicsCovered: text('topics_covered').array(),
makeupForSessionId: uuid('makeup_for_session_id'),
notesCompletedAt: timestamp('notes_completed_at', { withTimezone: true }),
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
})
// --- Type exports ---
export type Instructor = typeof instructors.$inferSelect
@@ -101,3 +130,5 @@ export type ScheduleSlot = typeof scheduleSlots.$inferSelect
export type ScheduleSlotInsert = typeof scheduleSlots.$inferInsert
export type Enrollment = typeof enrollments.$inferSelect
export type EnrollmentInsert = typeof enrollments.$inferInsert
export type LessonSession = typeof lessonSessions.$inferSelect
export type LessonSessionInsert = typeof lessonSessions.$inferInsert