Add Phase 8: lesson plan templates with deep-copy instantiation
- New tables: lesson_plan_template, lesson_plan_template_section, lesson_plan_template_item - skill_level enum: beginner, intermediate, advanced, all_levels - Templates are reusable curriculum definitions independent of any member/enrollment - POST /lesson-plan-templates/:id/create-plan deep-copies the template into a member plan - Instantiation uses template name as default plan title, accepts custom title override - Instantiation deactivates any existing active plan on the enrollment (one-active rule) - Plan items are independent copies — renaming the template does not affect existing plans - 11 new integration tests
This commit is contained in:
@@ -200,6 +200,51 @@ export const LessonPlanItemUpdateSchema = z.object({
|
||||
})
|
||||
export type LessonPlanItemUpdateInput = z.infer<typeof LessonPlanItemUpdateSchema>
|
||||
|
||||
// --- Lesson Plan Template schemas ---
|
||||
|
||||
export const SkillLevel = z.enum(['beginner', 'intermediate', 'advanced', 'all_levels'])
|
||||
export type SkillLevel = z.infer<typeof SkillLevel>
|
||||
|
||||
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<typeof LessonPlanTemplateCreateSchema>
|
||||
|
||||
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<typeof LessonPlanTemplateUpdateSchema>
|
||||
|
||||
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<typeof TemplateInstantiateSchema>
|
||||
|
||||
// --- Grade History schemas ---
|
||||
|
||||
export const GradeCreateSchema = z.object({
|
||||
|
||||
Reference in New Issue
Block a user