Add lessons Phase 6: lesson plans with curriculum tracking
Structured lesson plans with nested sections and items per enrollment. Deep create in one request, one-active-per-enrollment constraint, auto-set startedDate/masteredDate on status transitions, progress % calculation (skipped items excluded). 8 new tests (84 total).
This commit is contained in:
@@ -15,8 +15,11 @@ import {
|
||||
LessonSessionUpdateSchema,
|
||||
GradingScaleCreateSchema,
|
||||
GradingScaleUpdateSchema,
|
||||
LessonPlanCreateSchema,
|
||||
LessonPlanUpdateSchema,
|
||||
LessonPlanItemUpdateSchema,
|
||||
} from '@lunarfront/shared/schemas'
|
||||
import { InstructorService, LessonTypeService, ScheduleSlotService, EnrollmentService, LessonSessionService, GradingScaleService } from '../../services/lesson.service.js'
|
||||
import { InstructorService, LessonTypeService, ScheduleSlotService, EnrollmentService, LessonSessionService, GradingScaleService, LessonPlanService, LessonPlanItemService } from '../../services/lesson.service.js'
|
||||
|
||||
export const lessonRoutes: FastifyPluginAsync = async (app) => {
|
||||
// --- Instructors ---
|
||||
@@ -322,4 +325,58 @@ export const lessonRoutes: FastifyPluginAsync = async (app) => {
|
||||
if (!scale) return reply.status(404).send({ error: { message: 'Grading scale not found', statusCode: 404 } })
|
||||
return reply.send(scale)
|
||||
})
|
||||
|
||||
// --- Lesson Plans ---
|
||||
|
||||
app.post('/lesson-plans', { preHandler: [app.authenticate, app.requirePermission('lessons.edit')] }, async (request, reply) => {
|
||||
const parsed = LessonPlanCreateSchema.safeParse(request.body)
|
||||
if (!parsed.success) {
|
||||
return reply.status(400).send({ error: { message: 'Validation failed', details: parsed.error.flatten(), statusCode: 400 } })
|
||||
}
|
||||
const plan = await LessonPlanService.create(app.db, parsed.data, request.user.id)
|
||||
return reply.status(201).send(plan)
|
||||
})
|
||||
|
||||
app.get('/lesson-plans', { preHandler: [app.authenticate, app.requirePermission('lessons.view')] }, async (request, reply) => {
|
||||
const query = request.query as Record<string, string | undefined>
|
||||
const params = PaginationSchema.parse(query)
|
||||
const filters = {
|
||||
enrollmentId: query.enrollmentId,
|
||||
memberId: query.memberId,
|
||||
isActive: query.isActive === 'true' ? true : query.isActive === 'false' ? false : undefined,
|
||||
}
|
||||
const result = await LessonPlanService.list(app.db, params, filters)
|
||||
return reply.send(result)
|
||||
})
|
||||
|
||||
app.get('/lesson-plans/:id', { preHandler: [app.authenticate, app.requirePermission('lessons.view')] }, async (request, reply) => {
|
||||
const { id } = request.params as { id: string }
|
||||
const plan = await LessonPlanService.getById(app.db, id)
|
||||
if (!plan) return reply.status(404).send({ error: { message: 'Lesson plan not found', statusCode: 404 } })
|
||||
return reply.send(plan)
|
||||
})
|
||||
|
||||
app.patch('/lesson-plans/:id', { preHandler: [app.authenticate, app.requirePermission('lessons.edit')] }, async (request, reply) => {
|
||||
const { id } = request.params as { id: string }
|
||||
const parsed = LessonPlanUpdateSchema.safeParse(request.body)
|
||||
if (!parsed.success) {
|
||||
return reply.status(400).send({ error: { message: 'Validation failed', details: parsed.error.flatten(), statusCode: 400 } })
|
||||
}
|
||||
const plan = await LessonPlanService.update(app.db, id, parsed.data)
|
||||
if (!plan) return reply.status(404).send({ error: { message: 'Lesson plan not found', statusCode: 404 } })
|
||||
return reply.send(plan)
|
||||
})
|
||||
|
||||
// --- Lesson Plan Items ---
|
||||
|
||||
app.patch('/lesson-plan-items/:id', { preHandler: [app.authenticate, app.requirePermission('lessons.edit')] }, async (request, reply) => {
|
||||
const { id } = request.params as { id: string }
|
||||
const parsed = LessonPlanItemUpdateSchema.safeParse(request.body)
|
||||
if (!parsed.success) {
|
||||
return reply.status(400).send({ error: { message: 'Validation failed', details: parsed.error.flatten(), statusCode: 400 } })
|
||||
}
|
||||
const item = await LessonPlanItemService.update(app.db, id, parsed.data)
|
||||
if (!item) return reply.status(404).send({ error: { message: 'Lesson plan item not found', statusCode: 404 } })
|
||||
return reply.send(item)
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user