Compare commits

...

3 Commits

22 changed files with 7151 additions and 543 deletions

View File

@ -0,0 +1,18 @@
-- Restores legacy lesson columns. Rows will have NULL question_set_id until repopulated.
ALTER TABLE sub_module_lessons
ADD COLUMN IF NOT EXISTS question_set_id BIGINT REFERENCES question_sets(id) ON DELETE CASCADE,
ADD COLUMN IF NOT EXISTS intro_video_url TEXT;
UPDATE sub_module_lessons
SET intro_video_url = teaching_video_url
WHERE teaching_video_url IS NOT NULL;
ALTER TABLE sub_module_lessons
DROP COLUMN IF EXISTS title,
DROP COLUMN IF EXISTS description,
DROP COLUMN IF EXISTS thumbnail,
DROP COLUMN IF EXISTS teaching_text,
DROP COLUMN IF EXISTS teaching_image_url,
DROP COLUMN IF EXISTS teaching_audio_url,
DROP COLUMN IF EXISTS teaching_video_url;

View File

@ -0,0 +1,37 @@
-- Lessons are teaching content only (text, images, audio, video, thumbnail).
-- Question sets remain linked to practices, not lessons.
ALTER TABLE sub_module_lessons
ADD COLUMN IF NOT EXISTS title VARCHAR(255),
ADD COLUMN IF NOT EXISTS description TEXT,
ADD COLUMN IF NOT EXISTS thumbnail TEXT,
ADD COLUMN IF NOT EXISTS teaching_text TEXT,
ADD COLUMN IF NOT EXISTS teaching_image_url TEXT,
ADD COLUMN IF NOT EXISTS teaching_audio_url TEXT,
ADD COLUMN IF NOT EXISTS teaching_video_url TEXT;
UPDATE sub_module_lessons sml
SET
title = qs.title,
description = qs.description
FROM question_sets qs
WHERE sml.question_set_id IS NOT NULL
AND qs.id = sml.question_set_id;
UPDATE sub_module_lessons
SET title = 'Lesson'
WHERE title IS NULL OR trim(title) = '';
UPDATE sub_module_lessons
SET teaching_video_url = intro_video_url
WHERE intro_video_url IS NOT NULL;
ALTER TABLE sub_module_lessons DROP CONSTRAINT IF EXISTS sub_module_lessons_question_set_id_fkey;
ALTER TABLE sub_module_lessons DROP CONSTRAINT IF EXISTS sub_module_lessons_question_set_id_key;
ALTER TABLE sub_module_lessons DROP COLUMN IF EXISTS question_set_id;
ALTER TABLE sub_module_lessons DROP COLUMN IF EXISTS intro_video_url;
ALTER TABLE sub_module_lessons
ALTER COLUMN title SET NOT NULL,
ALTER COLUMN title SET DEFAULT 'Lesson';

View File

@ -0,0 +1,4 @@
ALTER TABLE levels
DROP COLUMN IF EXISTS title,
DROP COLUMN IF EXISTS description,
DROP COLUMN IF EXISTS thumbnail;

View File

@ -0,0 +1,11 @@
ALTER TABLE levels
ADD COLUMN IF NOT EXISTS title VARCHAR(255),
ADD COLUMN IF NOT EXISTS description TEXT,
ADD COLUMN IF NOT EXISTS thumbnail TEXT;
UPDATE levels
SET title = cefr_level
WHERE title IS NULL OR trim(title) = '';
ALTER TABLE levels
ALTER COLUMN title SET NOT NULL;

View File

@ -0,0 +1,12 @@
DROP INDEX IF EXISTS idx_sub_module_capstones_sub_module_id;
DROP TABLE IF EXISTS sub_module_capstones;
ALTER TABLE question_sets DROP CONSTRAINT IF EXISTS question_sets_set_type_check;
ALTER TABLE question_sets ADD CONSTRAINT question_sets_set_type_check
CHECK (set_type IN (
'PRACTICE',
'INITIAL_ASSESSMENT',
'QUIZ',
'EXAM',
'SURVEY'
));

View File

@ -0,0 +1,29 @@
-- Capstone assessments: sub-module scoped, backed by question_sets (type CAPSTONE).
ALTER TABLE question_sets DROP CONSTRAINT IF EXISTS question_sets_set_type_check;
ALTER TABLE question_sets ADD CONSTRAINT question_sets_set_type_check
CHECK (set_type IN (
'PRACTICE',
'INITIAL_ASSESSMENT',
'QUIZ',
'EXAM',
'SURVEY',
'CAPSTONE'
));
CREATE TABLE IF NOT EXISTS sub_module_capstones (
id BIGSERIAL PRIMARY KEY,
sub_module_id BIGINT NOT NULL REFERENCES sub_modules(id) ON DELETE CASCADE,
title VARCHAR(255) NOT NULL,
description TEXT,
tips TEXT,
thumbnail TEXT,
question_set_id BIGINT NOT NULL REFERENCES question_sets(id) ON DELETE CASCADE,
display_order INT NOT NULL DEFAULT 0,
is_active BOOLEAN NOT NULL DEFAULT TRUE,
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
UNIQUE (question_set_id)
);
CREATE INDEX IF NOT EXISTS idx_sub_module_capstones_sub_module_id
ON sub_module_capstones (sub_module_id);

View File

@ -0,0 +1,4 @@
DROP INDEX IF EXISTS idx_module_capstones_module_id;
DROP TABLE IF EXISTS module_capstones;
ALTER TABLE modules DROP COLUMN IF EXISTS icon_url;

View File

@ -0,0 +1,19 @@
ALTER TABLE modules
ADD COLUMN IF NOT EXISTS icon_url TEXT;
CREATE TABLE IF NOT EXISTS module_capstones (
id BIGSERIAL PRIMARY KEY,
module_id BIGINT NOT NULL REFERENCES modules(id) ON DELETE CASCADE,
title VARCHAR(255) NOT NULL,
description TEXT,
tips TEXT,
thumbnail TEXT,
question_set_id BIGINT NOT NULL REFERENCES question_sets(id) ON DELETE CASCADE,
display_order INT NOT NULL DEFAULT 0,
is_active BOOLEAN NOT NULL DEFAULT TRUE,
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
UNIQUE (question_set_id)
);
CREATE INDEX IF NOT EXISTS idx_module_capstones_module_id
ON module_capstones (module_id);

View File

@ -0,0 +1,3 @@
ALTER TABLE sub_modules
DROP COLUMN IF EXISTS tips,
DROP COLUMN IF EXISTS thumbnail;

View File

@ -0,0 +1,3 @@
ALTER TABLE sub_modules
ADD COLUMN IF NOT EXISTS thumbnail TEXT,
ADD COLUMN IF NOT EXISTS tips TEXT;

View File

@ -83,43 +83,17 @@ WHERE sub_module_id = $1
ORDER BY display_order ASC, id ASC; ORDER BY display_order ASC, id ASC;
-- name: GetSubModuleLessons :many -- name: GetSubModuleLessons :many
SELECT SELECT *
smp.id, FROM sub_module_lessons
smp.sub_module_id, WHERE sub_module_id = $1
smp.question_set_id, AND is_active = TRUE
smp.intro_video_url, ORDER BY display_order ASC, id ASC;
smp.display_order,
smp.is_active,
qs.title,
qs.description,
qs.status,
qs.set_type,
(SELECT COUNT(*) FROM question_set_items qsi WHERE qsi.set_id = qs.id) AS question_count
FROM sub_module_lessons smp
JOIN question_sets qs ON qs.id = smp.question_set_id
WHERE smp.sub_module_id = $1
AND smp.is_active = TRUE
AND qs.set_type = 'QUIZ'
ORDER BY smp.display_order ASC, smp.id ASC;
-- name: GetSubModuleLessonByID :one -- name: GetSubModuleLessonByID :one
SELECT SELECT *
smp.id, FROM sub_module_lessons
smp.sub_module_id, WHERE id = $1
smp.question_set_id, AND is_active = TRUE;
smp.intro_video_url,
smp.display_order,
smp.is_active,
qs.title,
qs.description,
qs.status,
qs.set_type,
(SELECT COUNT(*) FROM question_set_items qsi WHERE qsi.set_id = qs.id) AS question_count
FROM sub_module_lessons smp
JOIN question_sets qs ON qs.id = smp.question_set_id
WHERE smp.id = $1
AND smp.is_active = TRUE
AND qs.set_type = 'QUIZ';
-- name: GetSubModulePractices :many -- name: GetSubModulePractices :many
SELECT SELECT
@ -162,16 +136,71 @@ WHERE smp.id = $1
AND smp.is_active = TRUE AND smp.is_active = TRUE
AND qs.set_type = 'PRACTICE'; AND qs.set_type = 'PRACTICE';
-- name: GetSubModuleCapstones :many
SELECT
smc.id,
smc.sub_module_id,
smc.title,
smc.description,
smc.tips,
smc.thumbnail,
smc.question_set_id,
smc.display_order,
smc.is_active,
qs.status,
qs.set_type,
qs.time_limit_minutes,
qs.passing_score,
qs.shuffle_questions,
(SELECT COUNT(*) FROM question_set_items qsi WHERE qsi.set_id = qs.id) AS question_count
FROM sub_module_capstones smc
JOIN question_sets qs ON qs.id = smc.question_set_id
WHERE smc.sub_module_id = $1
AND smc.is_active = TRUE
AND qs.set_type = 'CAPSTONE'
ORDER BY smc.display_order ASC, smc.id ASC;
-- name: GetSubModuleCapstoneByID :one
SELECT
smc.id,
smc.sub_module_id,
smc.title,
smc.description,
smc.tips,
smc.thumbnail,
smc.question_set_id,
smc.display_order,
smc.is_active,
qs.status,
qs.set_type,
qs.time_limit_minutes,
qs.passing_score,
qs.shuffle_questions,
(SELECT COUNT(*) FROM question_set_items qsi WHERE qsi.set_id = qs.id) AS question_count
FROM sub_module_capstones smc
JOIN question_sets qs ON qs.id = smc.question_set_id
WHERE smc.id = $1
AND smc.is_active = TRUE
AND qs.set_type = 'CAPSTONE';
-- name: GetFullHierarchyByCourseID :many -- name: GetFullHierarchyByCourseID :many
SELECT SELECT
c.id AS course_id, c.id AS course_id,
c.title AS course_title, c.title AS course_title,
l.id AS level_id, l.id AS level_id,
l.cefr_level, l.cefr_level,
l.title AS level_title,
l.description AS level_description,
l.thumbnail AS level_thumbnail,
m.id AS module_id, m.id AS module_id,
m.title AS module_title, m.title AS module_title,
m.icon_url AS module_icon_url,
sm.id AS sub_module_id, sm.id AS sub_module_id,
sm.title AS sub_module_title sm.title AS sub_module_title,
sm.description AS sub_module_description,
sm.thumbnail AS sub_module_thumbnail,
sm.tips AS sub_module_tips,
sm.display_order AS sub_module_display_order
FROM courses c FROM courses c
LEFT JOIN levels l ON l.course_id = c.id AND l.is_active = TRUE LEFT JOIN levels l ON l.course_id = c.id AND l.is_active = TRUE
LEFT JOIN modules m ON m.level_id = l.id AND m.is_active = TRUE LEFT JOIN modules m ON m.level_id = l.id AND m.is_active = TRUE
@ -232,10 +261,24 @@ OFFSET sqlc.narg('offset')::INT;
INSERT INTO levels ( INSERT INTO levels (
course_id, course_id,
cefr_level, cefr_level,
title,
description,
thumbnail,
display_order, display_order,
is_active is_active
) )
VALUES ($1, $2, COALESCE($3, 0), COALESCE($4, TRUE)) VALUES ($1, $2, $3, $4, $5, COALESCE($6, 0), COALESCE($7, TRUE))
RETURNING *;
-- name: UpdateLevel :one
UPDATE levels
SET
title = $1,
description = $2,
thumbnail = $3,
display_order = $4,
is_active = $5
WHERE id = $6
RETURNING *; RETURNING *;
-- name: CreateModule :one -- name: CreateModule :one
@ -243,10 +286,22 @@ INSERT INTO modules (
level_id, level_id,
title, title,
description, description,
icon_url,
display_order, display_order,
is_active is_active
) )
VALUES ($1, $2, $3, COALESCE($4, 0), COALESCE($5, TRUE)) VALUES ($1, $2, $3, $4, COALESCE($5, 0), COALESCE($6, TRUE))
RETURNING *;
-- name: UpdateModule :one
UPDATE modules
SET
title = $1,
description = $2,
icon_url = $3,
display_order = $4,
is_active = $5
WHERE id = $6
RETURNING *; RETURNING *;
-- name: CreateSubModule :one -- name: CreateSubModule :one
@ -254,10 +309,24 @@ INSERT INTO sub_modules (
module_id, module_id,
title, title,
description, description,
thumbnail,
tips,
display_order, display_order,
is_active is_active
) )
VALUES ($1, $2, $3, COALESCE($4, 0), COALESCE($5, TRUE)) VALUES ($1, $2, $3, $4, $5, COALESCE($6, 0), COALESCE($7, TRUE))
RETURNING *;
-- name: UpdateSubModule :one
UPDATE sub_modules
SET
title = $1,
description = $2,
thumbnail = $3,
tips = $4,
display_order = $5,
is_active = $6
WHERE id = $7
RETURNING *; RETURNING *;
-- name: CreateSubModuleVideo :one -- name: CreateSubModuleVideo :one
@ -289,26 +358,47 @@ VALUES (
) )
RETURNING *; RETURNING *;
-- name: AttachQuestionSetLessonToSubModule :one -- name: CreateSubModuleLesson :one
INSERT INTO sub_module_lessons ( INSERT INTO sub_module_lessons (
sub_module_id, sub_module_id,
question_set_id, title,
intro_video_url, description,
thumbnail,
teaching_text,
teaching_image_url,
teaching_audio_url,
teaching_video_url,
display_order, display_order,
is_active is_active
) )
VALUES ($1, $2, $3, COALESCE($4, 0), COALESCE($5, TRUE)) VALUES (
$1,
$2,
$3,
$4,
$5,
$6,
$7,
$8,
COALESCE($9, 0),
COALESCE($10, TRUE)
)
RETURNING *; RETURNING *;
-- name: UpdateSubModuleLesson :one -- name: UpdateSubModuleLesson :one
UPDATE sub_module_lessons UPDATE sub_module_lessons
SET SET
sub_module_id = $1, sub_module_id = $1,
question_set_id = $2, title = $2,
intro_video_url = $3, description = $3,
display_order = $4, thumbnail = $4,
is_active = $5 teaching_text = $5,
WHERE id = $6 teaching_image_url = $6,
teaching_audio_url = $7,
teaching_video_url = $8,
display_order = $9,
is_active = $10
WHERE id = $11
RETURNING *; RETURNING *;
-- name: CreateSubModulePractice :one -- name: CreateSubModulePractice :one
@ -325,3 +415,102 @@ INSERT INTO sub_module_practices (
VALUES ($1, $2, $3, $4, $5, $6, COALESCE($7, 0), COALESCE($8, TRUE)) VALUES ($1, $2, $3, $4, $5, $6, COALESCE($7, 0), COALESCE($8, TRUE))
RETURNING *; RETURNING *;
-- name: CreateSubModuleCapstone :one
INSERT INTO sub_module_capstones (
sub_module_id,
title,
description,
tips,
thumbnail,
question_set_id,
display_order,
is_active
)
VALUES ($1, $2, $3, $4, $5, $6, COALESCE($7, 0), COALESCE($8, TRUE))
RETURNING *;
-- name: UpdateSubModuleCapstone :one
UPDATE sub_module_capstones
SET
title = $1,
description = $2,
tips = $3,
thumbnail = $4,
display_order = $5,
is_active = $6
WHERE id = $7
RETURNING *;
-- name: GetModuleCapstones :many
SELECT
mc.id,
mc.module_id,
mc.title,
mc.description,
mc.tips,
mc.thumbnail,
mc.question_set_id,
mc.display_order,
mc.is_active,
qs.status,
qs.set_type,
qs.time_limit_minutes,
qs.passing_score,
qs.shuffle_questions,
(SELECT COUNT(*) FROM question_set_items qsi WHERE qsi.set_id = qs.id) AS question_count
FROM module_capstones mc
JOIN question_sets qs ON qs.id = mc.question_set_id
WHERE mc.module_id = $1
AND mc.is_active = TRUE
AND qs.set_type = 'CAPSTONE'
ORDER BY mc.display_order ASC, mc.id ASC;
-- name: GetModuleCapstoneByID :one
SELECT
mc.id,
mc.module_id,
mc.title,
mc.description,
mc.tips,
mc.thumbnail,
mc.question_set_id,
mc.display_order,
mc.is_active,
qs.status,
qs.set_type,
qs.time_limit_minutes,
qs.passing_score,
qs.shuffle_questions,
(SELECT COUNT(*) FROM question_set_items qsi WHERE qsi.set_id = qs.id) AS question_count
FROM module_capstones mc
JOIN question_sets qs ON qs.id = mc.question_set_id
WHERE mc.id = $1
AND mc.is_active = TRUE
AND qs.set_type = 'CAPSTONE';
-- name: CreateModuleCapstone :one
INSERT INTO module_capstones (
module_id,
title,
description,
tips,
thumbnail,
question_set_id,
display_order,
is_active
)
VALUES ($1, $2, $3, $4, $5, $6, COALESCE($7, 0), COALESCE($8, TRUE))
RETURNING *;
-- name: UpdateModuleCapstone :one
UPDATE module_capstones
SET
title = $1,
description = $2,
tips = $3,
thumbnail = $4,
display_order = $5,
is_active = $6
WHERE id = $7
RETURNING *;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -8,7 +8,7 @@ import (
func (q *Queries) GetSubModuleByIDCompat(ctx context.Context, id int64) (SubModule, error) { func (q *Queries) GetSubModuleByIDCompat(ctx context.Context, id int64) (SubModule, error) {
row := q.db.QueryRow(ctx, ` row := q.db.QueryRow(ctx, `
SELECT id, module_id, title, description, display_order, is_active, created_at, legacy_sub_course_id SELECT id, module_id, title, description, display_order, is_active, created_at, legacy_sub_course_id, thumbnail, tips
FROM sub_modules FROM sub_modules
WHERE id = $1 WHERE id = $1
`, id) `, id)
@ -22,19 +22,24 @@ WHERE id = $1
&i.IsActive, &i.IsActive,
&i.CreatedAt, &i.CreatedAt,
&i.LegacySubCourseID, &i.LegacySubCourseID,
&i.Thumbnail,
&i.Tips,
) )
return i, err return i, err
} }
func (q *Queries) UpdateSubModuleCompat(ctx context.Context, id int64, title string, description string, isActive bool) error { func (q *Queries) UpdateSubModuleCompat(ctx context.Context, id int64, title string, description string, thumbnail string, tips string, displayOrder int32, isActive bool) error {
_, err := q.db.Exec(ctx, ` _, err := q.db.Exec(ctx, `
UPDATE sub_modules UPDATE sub_modules
SET SET
title = $1, title = $1,
description = NULLIF($2, ''), description = NULLIF($2, ''),
is_active = $3 thumbnail = NULLIF($3, ''),
WHERE id = $4 tips = NULLIF($4, ''),
`, title, description, isActive, id) display_order = $5,
is_active = $6
WHERE id = $7
`, title, description, thumbnail, tips, displayOrder, isActive, id)
return err return err
} }
@ -119,6 +124,24 @@ func (q *Queries) DeletePracticeCompat(ctx context.Context, id int64) error {
return err return err
} }
// DeleteCapstoneCompat removes the backing question set (and cascades sub_module_capstones).
func (q *Queries) DeleteCapstoneCompat(ctx context.Context, capstoneID int64) error {
_, err := q.db.Exec(ctx, `
DELETE FROM question_sets
WHERE id = (SELECT question_set_id FROM sub_module_capstones WHERE id = $1)
`, capstoneID)
return err
}
// DeleteModuleCapstoneCompat removes the backing question set (and cascades module_capstones).
func (q *Queries) DeleteModuleCapstoneCompat(ctx context.Context, capstoneID int64) error {
_, err := q.db.Exec(ctx, `
DELETE FROM question_sets
WHERE id = (SELECT question_set_id FROM module_capstones WHERE id = $1)
`, capstoneID)
return err
}
func (q *Queries) CreateCourseCompat( func (q *Queries) CreateCourseCompat(
ctx context.Context, ctx context.Context,
categoryID int64, categoryID int64,

File diff suppressed because it is too large Load Diff

View File

@ -76,6 +76,9 @@ type Level struct {
DisplayOrder int32 `json:"display_order"` DisplayOrder int32 `json:"display_order"`
IsActive bool `json:"is_active"` IsActive bool `json:"is_active"`
CreatedAt pgtype.Timestamptz `json:"created_at"` CreatedAt pgtype.Timestamptz `json:"created_at"`
Title string `json:"title"`
Description pgtype.Text `json:"description"`
Thumbnail pgtype.Text `json:"thumbnail"`
} }
type LevelToSubCourse struct { type LevelToSubCourse struct {
@ -91,6 +94,20 @@ type Module struct {
DisplayOrder int32 `json:"display_order"` DisplayOrder int32 `json:"display_order"`
IsActive bool `json:"is_active"` IsActive bool `json:"is_active"`
CreatedAt pgtype.Timestamptz `json:"created_at"` CreatedAt pgtype.Timestamptz `json:"created_at"`
IconUrl pgtype.Text `json:"icon_url"`
}
type ModuleCapstone struct {
ID int64 `json:"id"`
ModuleID int64 `json:"module_id"`
Title string `json:"title"`
Description pgtype.Text `json:"description"`
Tips pgtype.Text `json:"tips"`
Thumbnail pgtype.Text `json:"thumbnail"`
QuestionSetID int64 `json:"question_set_id"`
DisplayOrder int32 `json:"display_order"`
IsActive bool `json:"is_active"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
} }
type ModuleToSubCourse struct { type ModuleToSubCourse struct {
@ -353,18 +370,38 @@ type SubModule struct {
IsActive bool `json:"is_active"` IsActive bool `json:"is_active"`
CreatedAt pgtype.Timestamptz `json:"created_at"` CreatedAt pgtype.Timestamptz `json:"created_at"`
LegacySubCourseID pgtype.Int8 `json:"legacy_sub_course_id"` LegacySubCourseID pgtype.Int8 `json:"legacy_sub_course_id"`
Thumbnail pgtype.Text `json:"thumbnail"`
Tips pgtype.Text `json:"tips"`
} }
type SubModuleLesson struct { type SubModuleCapstone struct {
ID int64 `json:"id"` ID int64 `json:"id"`
SubModuleID int64 `json:"sub_module_id"` SubModuleID int64 `json:"sub_module_id"`
Title string `json:"title"`
Description pgtype.Text `json:"description"`
Tips pgtype.Text `json:"tips"`
Thumbnail pgtype.Text `json:"thumbnail"`
QuestionSetID int64 `json:"question_set_id"` QuestionSetID int64 `json:"question_set_id"`
IntroVideoUrl pgtype.Text `json:"intro_video_url"`
DisplayOrder int32 `json:"display_order"` DisplayOrder int32 `json:"display_order"`
IsActive bool `json:"is_active"` IsActive bool `json:"is_active"`
CreatedAt pgtype.Timestamptz `json:"created_at"` CreatedAt pgtype.Timestamptz `json:"created_at"`
} }
type SubModuleLesson struct {
ID int64 `json:"id"`
SubModuleID int64 `json:"sub_module_id"`
DisplayOrder int32 `json:"display_order"`
IsActive bool `json:"is_active"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
Title string `json:"title"`
Description pgtype.Text `json:"description"`
Thumbnail pgtype.Text `json:"thumbnail"`
TeachingText pgtype.Text `json:"teaching_text"`
TeachingImageUrl pgtype.Text `json:"teaching_image_url"`
TeachingAudioUrl pgtype.Text `json:"teaching_audio_url"`
TeachingVideoUrl pgtype.Text `json:"teaching_video_url"`
}
type SubModulePractice struct { type SubModulePractice struct {
ID int64 `json:"id"` ID int64 `json:"id"`
SubModuleID int64 `json:"sub_module_id"` SubModuleID int64 `json:"sub_module_id"`

View File

@ -146,6 +146,7 @@ type LearningPathSubCourse struct {
Title string `json:"title"` Title string `json:"title"`
Description *string `json:"description,omitempty"` Description *string `json:"description,omitempty"`
Thumbnail *string `json:"thumbnail,omitempty"` Thumbnail *string `json:"thumbnail,omitempty"`
Tips *string `json:"tips,omitempty"`
DisplayOrder int32 `json:"display_order"` DisplayOrder int32 `json:"display_order"`
Level string `json:"level"` Level string `json:"level"`
SubLevel string `json:"sub_level"` SubLevel string `json:"sub_level"`

View File

@ -27,6 +27,7 @@ const (
QuestionSetTypeQuiz QuestionSetType = "QUIZ" QuestionSetTypeQuiz QuestionSetType = "QUIZ"
QuestionSetTypeExam QuestionSetType = "EXAM" QuestionSetTypeExam QuestionSetType = "EXAM"
QuestionSetTypeSurvey QuestionSetType = "SURVEY" QuestionSetTypeSurvey QuestionSetType = "SURVEY"
QuestionSetTypeCapstone QuestionSetType = "CAPSTONE"
) )
type PracticeAccessBlock struct { type PracticeAccessBlock struct {

File diff suppressed because it is too large Load Diff

View File

@ -515,7 +515,7 @@ func (h *Handler) DeleteQuestion(c *fiber.Ctx) error {
type createQuestionSetReq struct { type createQuestionSetReq struct {
Title string `json:"title" validate:"required"` Title string `json:"title" validate:"required"`
Description *string `json:"description"` Description *string `json:"description"`
SetType string `json:"set_type" validate:"required,oneof=PRACTICE INITIAL_ASSESSMENT QUIZ EXAM SURVEY"` SetType string `json:"set_type" validate:"required,oneof=PRACTICE INITIAL_ASSESSMENT QUIZ EXAM SURVEY CAPSTONE"`
OwnerType *string `json:"owner_type"` OwnerType *string `json:"owner_type"`
OwnerID *int64 `json:"owner_id"` OwnerID *int64 `json:"owner_id"`
BannerImage *string `json:"banner_image"` BannerImage *string `json:"banner_image"`
@ -791,7 +791,7 @@ func (h *Handler) GetQuestionSetByID(c *fiber.Ctx) error {
// @Description Returns a paginated list of question sets filtered by type // @Description Returns a paginated list of question sets filtered by type
// @Tags question-sets // @Tags question-sets
// @Produce json // @Produce json
// @Param set_type query string true "Set type (PRACTICE, INITIAL_ASSESSMENT, QUIZ, EXAM, SURVEY)" // @Param set_type query string true "Set type (PRACTICE, INITIAL_ASSESSMENT, QUIZ, EXAM, SURVEY, CAPSTONE)"
// @Param limit query int false "Limit" default(10) // @Param limit query int false "Limit" default(10)
// @Param offset query int false "Offset" default(0) // @Param offset query int false "Offset" default(0)
// @Success 200 {object} domain.Response // @Success 200 {object} domain.Response

View File

@ -108,7 +108,9 @@ func (a *App) initAppRoutes() {
groupV1.Post("/course-management/sub-categories", a.authMiddleware, a.RequirePermission("course_categories.create"), h.CreateCourseSubCategory) groupV1.Post("/course-management/sub-categories", a.authMiddleware, a.RequirePermission("course_categories.create"), h.CreateCourseSubCategory)
groupV1.Delete("/course-management/sub-categories/:subCategoryId", a.authMiddleware, a.RequirePermission("course_categories.delete"), h.DeleteCourseSubCategory) groupV1.Delete("/course-management/sub-categories/:subCategoryId", a.authMiddleware, a.RequirePermission("course_categories.delete"), h.DeleteCourseSubCategory)
groupV1.Post("/course-management/levels", a.authMiddleware, a.RequirePermission("subcourses.create"), h.CreateLevel) groupV1.Post("/course-management/levels", a.authMiddleware, a.RequirePermission("subcourses.create"), h.CreateLevel)
groupV1.Put("/course-management/levels/:levelId", a.authMiddleware, a.RequirePermission("subcourses.update"), h.UpdateLevel)
groupV1.Post("/course-management/modules", a.authMiddleware, a.RequirePermission("subcourses.create"), h.CreateModule) groupV1.Post("/course-management/modules", a.authMiddleware, a.RequirePermission("subcourses.create"), h.CreateModule)
groupV1.Put("/course-management/modules/:moduleId", a.authMiddleware, a.RequirePermission("subcourses.update"), h.UpdateModule)
groupV1.Delete("/course-management/modules/:moduleId", a.authMiddleware, a.RequirePermission("subcourses.delete"), h.DeleteModule) groupV1.Delete("/course-management/modules/:moduleId", a.authMiddleware, a.RequirePermission("subcourses.delete"), h.DeleteModule)
groupV1.Post("/course-management/sub-modules", a.authMiddleware, a.RequirePermission("subcourses.create"), h.CreateSubModule) groupV1.Post("/course-management/sub-modules", a.authMiddleware, a.RequirePermission("subcourses.create"), h.CreateSubModule)
groupV1.Put("/course-management/sub-modules/:subModuleId", a.authMiddleware, a.RequirePermission("subcourses.update"), h.UpdateSubModule) groupV1.Put("/course-management/sub-modules/:subModuleId", a.authMiddleware, a.RequirePermission("subcourses.update"), h.UpdateSubModule)
@ -116,15 +118,25 @@ func (a *App) initAppRoutes() {
groupV1.Post("/course-management/sub-module-videos", a.authMiddleware, a.RequirePermission("videos.create"), h.CreateSubModuleVideo) groupV1.Post("/course-management/sub-module-videos", a.authMiddleware, a.RequirePermission("videos.create"), h.CreateSubModuleVideo)
groupV1.Put("/course-management/sub-module-videos/:videoId", a.authMiddleware, a.RequirePermission("videos.update"), h.UpdateSubModuleVideo) groupV1.Put("/course-management/sub-module-videos/:videoId", a.authMiddleware, a.RequirePermission("videos.update"), h.UpdateSubModuleVideo)
groupV1.Delete("/course-management/sub-module-videos/:videoId", a.authMiddleware, a.RequirePermission("videos.delete"), h.DeleteSubModuleVideo) groupV1.Delete("/course-management/sub-module-videos/:videoId", a.authMiddleware, a.RequirePermission("videos.delete"), h.DeleteSubModuleVideo)
groupV1.Get("/course-management/sub-modules/:subModuleId/lessons", a.authMiddleware, a.RequirePermission("question_sets.list"), h.GetSubModuleLessons) groupV1.Get("/course-management/sub-modules/:subModuleId/lessons", a.authMiddleware, a.RequirePermission("learning_tree.get"), h.GetSubModuleLessons)
groupV1.Get("/course-management/sub-module-lessons/:lessonId", a.authMiddleware, a.RequirePermission("question_sets.get"), h.GetSubModuleLessonByID) groupV1.Get("/course-management/sub-module-lessons/:lessonId", a.authMiddleware, a.RequirePermission("learning_tree.get"), h.GetSubModuleLessonByID)
groupV1.Put("/course-management/sub-module-lessons/:lessonId", a.authMiddleware, a.RequirePermission("question_sets.update"), h.UpdateSubModuleLesson) groupV1.Put("/course-management/sub-module-lessons/:lessonId", a.authMiddleware, a.RequirePermission("subcourses.update"), h.UpdateSubModuleLesson)
groupV1.Post("/course-management/sub-module-lessons", a.authMiddleware, a.RequirePermission("question_sets.update"), h.AttachSubModuleLesson) groupV1.Post("/course-management/sub-module-lessons", a.authMiddleware, a.RequirePermission("subcourses.create"), h.CreateSubModuleLesson)
groupV1.Get("/course-management/sub-modules/:subModuleId/practices", a.authMiddleware, a.RequirePermission("question_sets.list"), h.GetSubModulePractices) groupV1.Get("/course-management/sub-modules/:subModuleId/practices", a.authMiddleware, a.RequirePermission("question_sets.list"), h.GetSubModulePractices)
groupV1.Get("/course-management/practices/:practiceId", a.authMiddleware, a.RequirePermission("question_sets.get"), h.GetSubModulePracticeByID) groupV1.Get("/course-management/practices/:practiceId", a.authMiddleware, a.RequirePermission("question_sets.get"), h.GetSubModulePracticeByID)
groupV1.Post("/course-management/sub-module-practices", a.authMiddleware, a.RequirePermission("question_sets.update"), h.CreateSubModulePractice) groupV1.Post("/course-management/sub-module-practices", a.authMiddleware, a.RequirePermission("question_sets.update"), h.CreateSubModulePractice)
groupV1.Put("/course-management/practices/:practiceId", a.authMiddleware, a.RequirePermission("question_sets.update"), h.UpdatePractice) groupV1.Put("/course-management/practices/:practiceId", a.authMiddleware, a.RequirePermission("question_sets.update"), h.UpdatePractice)
groupV1.Delete("/course-management/practices/:practiceId", a.authMiddleware, a.RequirePermission("question_sets.delete"), h.DeletePractice) groupV1.Delete("/course-management/practices/:practiceId", a.authMiddleware, a.RequirePermission("question_sets.delete"), h.DeletePractice)
groupV1.Get("/course-management/sub-modules/:subModuleId/capstones", a.authMiddleware, a.RequirePermission("question_sets.list"), h.GetSubModuleCapstones)
groupV1.Get("/course-management/capstones/:capstoneId", a.authMiddleware, a.RequirePermission("question_sets.get"), h.GetSubModuleCapstoneByID)
groupV1.Post("/course-management/sub-module-capstones", a.authMiddleware, a.RequirePermission("question_sets.update"), h.CreateSubModuleCapstone)
groupV1.Put("/course-management/capstones/:capstoneId", a.authMiddleware, a.RequirePermission("question_sets.update"), h.UpdateSubModuleCapstone)
groupV1.Delete("/course-management/capstones/:capstoneId", a.authMiddleware, a.RequirePermission("question_sets.delete"), h.DeleteCapstone)
groupV1.Get("/course-management/modules/:moduleId/capstones", a.authMiddleware, a.RequirePermission("question_sets.list"), h.GetModuleCapstones)
groupV1.Get("/course-management/module-capstones/:moduleCapstoneId", a.authMiddleware, a.RequirePermission("question_sets.get"), h.GetModuleCapstoneByID)
groupV1.Post("/course-management/module-capstones", a.authMiddleware, a.RequirePermission("question_sets.update"), h.CreateModuleCapstone)
groupV1.Put("/course-management/module-capstones/:moduleCapstoneId", a.authMiddleware, a.RequirePermission("question_sets.update"), h.UpdateModuleCapstone)
groupV1.Delete("/course-management/module-capstones/:moduleCapstoneId", a.authMiddleware, a.RequirePermission("question_sets.delete"), h.DeleteModuleCapstone)
// Questions // Questions
groupV1.Post("/questions", a.authMiddleware, a.RequirePermission("questions.create"), h.CreateQuestion) groupV1.Post("/questions", a.authMiddleware, a.RequirePermission("questions.create"), h.CreateQuestion)