-- Sequential order for programs, courses, modules, and lessons (1 = first in each scope). -- Progress tables mark completion; API enforces prerequisites for learners (STUDENT role). ALTER TABLE programs ADD COLUMN sort_order INTEGER NOT NULL DEFAULT 0; ALTER TABLE courses ADD COLUMN sort_order INTEGER NOT NULL DEFAULT 0; ALTER TABLE modules ADD COLUMN sort_order INTEGER NOT NULL DEFAULT 0; ALTER TABLE lessons ADD COLUMN sort_order INTEGER NOT NULL DEFAULT 0; -- Program order (one global sequence): Beginner -> Intermediate -> Advanced; others by id UPDATE programs SET sort_order = v.so FROM ( VALUES ('Beginner', 1), ('Intermediate', 2), ('Advanced', 3) ) AS v (name, so) WHERE programs.name = v.name; UPDATE programs SET sort_order = 1000 + r.rn FROM ( SELECT id, row_number() OVER ( ORDER BY id ) AS rn FROM programs WHERE sort_order = 0 ) AS r WHERE programs.id = r.id; -- CEFR courses: A1..C2; remaining courses in each program: stable order UPDATE courses SET sort_order = CASE name WHEN 'A1' THEN 1 WHEN 'A2' THEN 2 WHEN 'B1' THEN 3 WHEN 'B2' THEN 4 WHEN 'C1' THEN 5 WHEN 'C2' THEN 6 ELSE 0 END WHERE name IN ('A1', 'A2', 'B1', 'B2', 'C1', 'C2'); UPDATE courses c SET sort_order = 2000 + s.rn FROM ( SELECT id, row_number() OVER ( PARTITION BY program_id ORDER BY id ) AS rn FROM courses WHERE sort_order = 0 ) AS s WHERE c.id = s.id; UPDATE modules m SET sort_order = r.rn FROM ( SELECT id, row_number() OVER ( PARTITION BY course_id ORDER BY id ) AS rn FROM modules ) AS r WHERE m.id = r.id; UPDATE lessons l SET sort_order = r.rn FROM ( SELECT id, row_number() OVER ( PARTITION BY module_id ORDER BY id ) AS rn FROM lessons ) AS r WHERE l.id = r.id; CREATE UNIQUE INDEX uq_programs_sort_order ON programs (sort_order); CREATE UNIQUE INDEX uq_courses_program_sort ON courses (program_id, sort_order); CREATE UNIQUE INDEX uq_modules_course_sort ON modules (course_id, sort_order); CREATE UNIQUE INDEX uq_lessons_module_sort ON lessons (module_id, sort_order); CREATE TABLE lms_user_lesson_progress ( user_id BIGINT NOT NULL REFERENCES users (id) ON DELETE CASCADE, lesson_id BIGINT NOT NULL REFERENCES lessons (id) ON DELETE CASCADE, completed_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (user_id, lesson_id) ); CREATE INDEX idx_lms_user_lesson_progress_user ON lms_user_lesson_progress (user_id); CREATE INDEX idx_lms_user_lesson_progress_lesson ON lms_user_lesson_progress (lesson_id); CREATE TABLE lms_user_module_progress ( user_id BIGINT NOT NULL REFERENCES users (id) ON DELETE CASCADE, module_id BIGINT NOT NULL REFERENCES modules (id) ON DELETE CASCADE, completed_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (user_id, module_id) ); CREATE INDEX idx_lms_user_module_progress_user ON lms_user_module_progress (user_id); CREATE INDEX idx_lms_user_module_progress_module ON lms_user_module_progress (module_id); CREATE TABLE lms_user_course_progress ( user_id BIGINT NOT NULL REFERENCES users (id) ON DELETE CASCADE, course_id BIGINT NOT NULL REFERENCES courses (id) ON DELETE CASCADE, completed_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (user_id, course_id) ); CREATE INDEX idx_lms_user_course_progress_user ON lms_user_course_progress (user_id); CREATE INDEX idx_lms_user_course_progress_course ON lms_user_course_progress (course_id); CREATE TABLE lms_user_program_progress ( user_id BIGINT NOT NULL REFERENCES users (id) ON DELETE CASCADE, program_id BIGINT NOT NULL REFERENCES programs (id) ON DELETE CASCADE, completed_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (user_id, program_id) ); CREATE INDEX idx_lms_user_program_progress_user ON lms_user_program_progress (user_id); CREATE INDEX idx_lms_user_program_progress_program ON lms_user_program_progress (program_id);