From c5d3935062cc4c9deed76e91b994f49f6039da65 Mon Sep 17 00:00:00 2001 From: Yared Yemane Date: Fri, 17 Apr 2026 08:33:58 -0700 Subject: [PATCH] added more structure to levels --- ...evels_title_description_thumbnail.down.sql | 4 + ..._levels_title_description_thumbnail.up.sql | 11 ++ db/query/hierarchy.sql | 19 ++- docs/docs.go | 88 +++++++++++- docs/swagger.json | 88 +++++++++++- docs/swagger.yaml | 61 ++++++++- gen/db/hierarchy.sql.go | 116 +++++++++++++--- gen/db/models.go | 3 + .../web_server/handlers/hierarchy_handler.go | 126 +++++++++++++++--- internal/web_server/routes.go | 1 + 10 files changed, 476 insertions(+), 41 deletions(-) create mode 100644 db/migrations/000034_levels_title_description_thumbnail.down.sql create mode 100644 db/migrations/000034_levels_title_description_thumbnail.up.sql diff --git a/db/migrations/000034_levels_title_description_thumbnail.down.sql b/db/migrations/000034_levels_title_description_thumbnail.down.sql new file mode 100644 index 0000000..3da7f60 --- /dev/null +++ b/db/migrations/000034_levels_title_description_thumbnail.down.sql @@ -0,0 +1,4 @@ +ALTER TABLE levels + DROP COLUMN IF EXISTS title, + DROP COLUMN IF EXISTS description, + DROP COLUMN IF EXISTS thumbnail; diff --git a/db/migrations/000034_levels_title_description_thumbnail.up.sql b/db/migrations/000034_levels_title_description_thumbnail.up.sql new file mode 100644 index 0000000..be01f79 --- /dev/null +++ b/db/migrations/000034_levels_title_description_thumbnail.up.sql @@ -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; diff --git a/db/query/hierarchy.sql b/db/query/hierarchy.sql index e3da128..19260c7 100644 --- a/db/query/hierarchy.sql +++ b/db/query/hierarchy.sql @@ -142,6 +142,9 @@ SELECT c.title AS course_title, l.id AS level_id, l.cefr_level, + l.title AS level_title, + l.description AS level_description, + l.thumbnail AS level_thumbnail, m.id AS module_id, m.title AS module_title, sm.id AS sub_module_id, @@ -206,10 +209,24 @@ OFFSET sqlc.narg('offset')::INT; INSERT INTO levels ( course_id, cefr_level, + title, + description, + thumbnail, display_order, 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 *; -- name: CreateModule :one diff --git a/docs/docs.go b/docs/docs.go index 776ddf3..bb7c100 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -1379,7 +1379,7 @@ const docTemplate = `{ } }, "post": { - "description": "Creates a CEFR level under a course", + "description": "Creates a CEFR level under a course with optional title (defaults to cefr_level), description, and thumbnail", "consumes": [ "application/json" ], @@ -1468,6 +1468,63 @@ const docTemplate = `{ } } } + }, + "put": { + "description": "Updates level title, description, thumbnail, display order, and active flag", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "course-management" + ], + "summary": "Update level", + "parameters": [ + { + "type": "integer", + "description": "Level ID", + "name": "levelId", + "in": "path", + "required": true + }, + { + "description": "Update level payload", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/handlers.updateLevelReq" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/domain.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/domain.ErrorResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/domain.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/domain.ErrorResponse" + } + } + } } }, "/api/v1/course-management/levels/{levelId}/modules": { @@ -10358,11 +10415,20 @@ const docTemplate = `{ "course_id": { "type": "integer" }, + "description": { + "type": "string" + }, "display_order": { "type": "integer" }, "is_active": { "type": "boolean" + }, + "thumbnail": { + "type": "string" + }, + "title": { + "type": "string" } } }, @@ -10987,6 +11053,26 @@ const docTemplate = `{ } } }, + "handlers.updateLevelReq": { + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "display_order": { + "type": "integer" + }, + "is_active": { + "type": "boolean" + }, + "thumbnail": { + "type": "string" + }, + "title": { + "type": "string" + } + } + }, "handlers.updatePlanReq": { "type": "object", "properties": { diff --git a/docs/swagger.json b/docs/swagger.json index 34359ee..0a0cb78 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -1371,7 +1371,7 @@ } }, "post": { - "description": "Creates a CEFR level under a course", + "description": "Creates a CEFR level under a course with optional title (defaults to cefr_level), description, and thumbnail", "consumes": [ "application/json" ], @@ -1460,6 +1460,63 @@ } } } + }, + "put": { + "description": "Updates level title, description, thumbnail, display order, and active flag", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "course-management" + ], + "summary": "Update level", + "parameters": [ + { + "type": "integer", + "description": "Level ID", + "name": "levelId", + "in": "path", + "required": true + }, + { + "description": "Update level payload", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/handlers.updateLevelReq" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/domain.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/domain.ErrorResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/domain.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/domain.ErrorResponse" + } + } + } } }, "/api/v1/course-management/levels/{levelId}/modules": { @@ -10350,11 +10407,20 @@ "course_id": { "type": "integer" }, + "description": { + "type": "string" + }, "display_order": { "type": "integer" }, "is_active": { "type": "boolean" + }, + "thumbnail": { + "type": "string" + }, + "title": { + "type": "string" } } }, @@ -10979,6 +11045,26 @@ } } }, + "handlers.updateLevelReq": { + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "display_order": { + "type": "integer" + }, + "is_active": { + "type": "boolean" + }, + "thumbnail": { + "type": "string" + }, + "title": { + "type": "string" + } + } + }, "handlers.updatePlanReq": { "type": "object", "properties": { diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 68ad4e7..75a0547 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -993,10 +993,16 @@ definitions: type: string course_id: type: integer + description: + type: string display_order: type: integer is_active: type: boolean + thumbnail: + type: string + title: + type: string type: object handlers.createModuleReq: properties: @@ -1418,6 +1424,19 @@ definitions: required: - status type: object + handlers.updateLevelReq: + properties: + description: + type: string + display_order: + type: integer + is_active: + type: boolean + thumbnail: + type: string + title: + type: string + type: object handlers.updatePlanReq: properties: currency: @@ -2839,7 +2858,8 @@ paths: post: consumes: - application/json - description: Creates a CEFR level under a course + description: Creates a CEFR level under a course with optional title (defaults + to cefr_level), description, and thumbnail parameters: - description: Create level payload in: body @@ -2896,6 +2916,45 @@ paths: summary: Get level detail tags: - course-management + put: + consumes: + - application/json + description: Updates level title, description, thumbnail, display order, and + active flag + parameters: + - description: Level ID + in: path + name: levelId + required: true + type: integer + - description: Update level payload + in: body + name: body + required: true + schema: + $ref: '#/definitions/handlers.updateLevelReq' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/domain.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/domain.ErrorResponse' + "404": + description: Not Found + schema: + $ref: '#/definitions/domain.ErrorResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/domain.ErrorResponse' + summary: Update level + tags: + - course-management /api/v1/course-management/levels/{levelId}/modules: get: description: Returns all active modules for one level diff --git a/gen/db/hierarchy.sql.go b/gen/db/hierarchy.sql.go index 3b4899c..ac8a3c5 100644 --- a/gen/db/hierarchy.sql.go +++ b/gen/db/hierarchy.sql.go @@ -56,26 +56,35 @@ const CreateLevel = `-- name: CreateLevel :one INSERT INTO levels ( course_id, cefr_level, + title, + description, + thumbnail, display_order, is_active ) -VALUES ($1, $2, COALESCE($3, 0), COALESCE($4, TRUE)) -RETURNING id, course_id, cefr_level, display_order, is_active, created_at +VALUES ($1, $2, $3, $4, $5, COALESCE($6, 0), COALESCE($7, TRUE)) +RETURNING id, course_id, cefr_level, display_order, is_active, created_at, title, description, thumbnail ` type CreateLevelParams struct { - CourseID int64 `json:"course_id"` - CefrLevel string `json:"cefr_level"` - Column3 interface{} `json:"column_3"` - Column4 interface{} `json:"column_4"` + CourseID int64 `json:"course_id"` + CefrLevel string `json:"cefr_level"` + Title string `json:"title"` + Description pgtype.Text `json:"description"` + Thumbnail pgtype.Text `json:"thumbnail"` + Column6 interface{} `json:"column_6"` + Column7 interface{} `json:"column_7"` } func (q *Queries) CreateLevel(ctx context.Context, arg CreateLevelParams) (Level, error) { row := q.db.QueryRow(ctx, CreateLevel, arg.CourseID, arg.CefrLevel, - arg.Column3, - arg.Column4, + arg.Title, + arg.Description, + arg.Thumbnail, + arg.Column6, + arg.Column7, ) var i Level err := row.Scan( @@ -85,6 +94,9 @@ func (q *Queries) CreateLevel(ctx context.Context, arg CreateLevelParams) (Level &i.DisplayOrder, &i.IsActive, &i.CreatedAt, + &i.Title, + &i.Description, + &i.Thumbnail, ) return i, err } @@ -398,7 +410,7 @@ func (q *Queries) CreateSubModuleVideo(ctx context.Context, arg CreateSubModuleV const GetAllLevels = `-- name: GetAllLevels :many SELECT COUNT(*) OVER () AS total_count, - l.id, l.course_id, l.cefr_level, l.display_order, l.is_active, l.created_at + l.id, l.course_id, l.cefr_level, l.display_order, l.is_active, l.created_at, l.title, l.description, l.thumbnail FROM levels l ORDER BY l.display_order ASC, l.id ASC LIMIT $2::INT @@ -418,6 +430,9 @@ type GetAllLevelsRow struct { 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"` } func (q *Queries) GetAllLevels(ctx context.Context, arg GetAllLevelsParams) ([]GetAllLevelsRow, error) { @@ -437,6 +452,9 @@ func (q *Queries) GetAllLevels(ctx context.Context, arg GetAllLevelsParams) ([]G &i.DisplayOrder, &i.IsActive, &i.CreatedAt, + &i.Title, + &i.Description, + &i.Thumbnail, ); err != nil { return nil, err } @@ -683,6 +701,9 @@ SELECT c.title AS course_title, l.id AS level_id, l.cefr_level, + l.title AS level_title, + l.description AS level_description, + l.thumbnail AS level_thumbnail, m.id AS module_id, m.title AS module_title, sm.id AS sub_module_id, @@ -696,14 +717,17 @@ ORDER BY l.display_order, l.id, m.display_order, m.id, sm.display_order, sm.id ` type GetFullHierarchyByCourseIDRow struct { - CourseID int64 `json:"course_id"` - CourseTitle string `json:"course_title"` - LevelID pgtype.Int8 `json:"level_id"` - CefrLevel pgtype.Text `json:"cefr_level"` - ModuleID pgtype.Int8 `json:"module_id"` - ModuleTitle pgtype.Text `json:"module_title"` - SubModuleID pgtype.Int8 `json:"sub_module_id"` - SubModuleTitle pgtype.Text `json:"sub_module_title"` + CourseID int64 `json:"course_id"` + CourseTitle string `json:"course_title"` + LevelID pgtype.Int8 `json:"level_id"` + CefrLevel pgtype.Text `json:"cefr_level"` + LevelTitle pgtype.Text `json:"level_title"` + LevelDescription pgtype.Text `json:"level_description"` + LevelThumbnail pgtype.Text `json:"level_thumbnail"` + ModuleID pgtype.Int8 `json:"module_id"` + ModuleTitle pgtype.Text `json:"module_title"` + SubModuleID pgtype.Int8 `json:"sub_module_id"` + SubModuleTitle pgtype.Text `json:"sub_module_title"` } func (q *Queries) GetFullHierarchyByCourseID(ctx context.Context, id int64) ([]GetFullHierarchyByCourseIDRow, error) { @@ -720,6 +744,9 @@ func (q *Queries) GetFullHierarchyByCourseID(ctx context.Context, id int64) ([]G &i.CourseTitle, &i.LevelID, &i.CefrLevel, + &i.LevelTitle, + &i.LevelDescription, + &i.LevelThumbnail, &i.ModuleID, &i.ModuleTitle, &i.SubModuleID, @@ -804,7 +831,7 @@ func (q *Queries) GetHumanLanguageCourseSubCategories(ctx context.Context, arg G } const GetLevelByID = `-- name: GetLevelByID :one -SELECT id, course_id, cefr_level, display_order, is_active, created_at +SELECT id, course_id, cefr_level, display_order, is_active, created_at, title, description, thumbnail FROM levels WHERE id = $1 ` @@ -819,12 +846,15 @@ func (q *Queries) GetLevelByID(ctx context.Context, id int64) (Level, error) { &i.DisplayOrder, &i.IsActive, &i.CreatedAt, + &i.Title, + &i.Description, + &i.Thumbnail, ) return i, err } const GetLevelsByCourseID = `-- name: GetLevelsByCourseID :many -SELECT id, course_id, cefr_level, display_order, is_active, created_at +SELECT id, course_id, cefr_level, display_order, is_active, created_at, title, description, thumbnail FROM levels WHERE course_id = $1 AND is_active = TRUE @@ -847,6 +877,9 @@ func (q *Queries) GetLevelsByCourseID(ctx context.Context, courseID int64) ([]Le &i.DisplayOrder, &i.IsActive, &i.CreatedAt, + &i.Title, + &i.Description, + &i.Thumbnail, ); err != nil { return nil, err } @@ -1217,6 +1250,51 @@ func (q *Queries) GetSubModulesByModuleID(ctx context.Context, moduleID int64) ( return items, nil } +const UpdateLevel = `-- name: UpdateLevel :one +UPDATE levels +SET + title = $1, + description = $2, + thumbnail = $3, + display_order = $4, + is_active = $5 +WHERE id = $6 +RETURNING id, course_id, cefr_level, display_order, is_active, created_at, title, description, thumbnail +` + +type UpdateLevelParams struct { + Title string `json:"title"` + Description pgtype.Text `json:"description"` + Thumbnail pgtype.Text `json:"thumbnail"` + DisplayOrder int32 `json:"display_order"` + IsActive bool `json:"is_active"` + ID int64 `json:"id"` +} + +func (q *Queries) UpdateLevel(ctx context.Context, arg UpdateLevelParams) (Level, error) { + row := q.db.QueryRow(ctx, UpdateLevel, + arg.Title, + arg.Description, + arg.Thumbnail, + arg.DisplayOrder, + arg.IsActive, + arg.ID, + ) + var i Level + err := row.Scan( + &i.ID, + &i.CourseID, + &i.CefrLevel, + &i.DisplayOrder, + &i.IsActive, + &i.CreatedAt, + &i.Title, + &i.Description, + &i.Thumbnail, + ) + return i, err +} + const UpdateSubModuleLesson = `-- name: UpdateSubModuleLesson :one UPDATE sub_module_lessons SET diff --git a/gen/db/models.go b/gen/db/models.go index 3015806..cfcac33 100644 --- a/gen/db/models.go +++ b/gen/db/models.go @@ -76,6 +76,9 @@ type Level struct { 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"` } type LevelToSubCourse struct { diff --git a/internal/web_server/handlers/hierarchy_handler.go b/internal/web_server/handlers/hierarchy_handler.go index d1f11e1..49dfefb 100644 --- a/internal/web_server/handlers/hierarchy_handler.go +++ b/internal/web_server/handlers/hierarchy_handler.go @@ -46,10 +46,21 @@ type updateCourseThumbnailReq struct { } type createLevelReq struct { - CourseID int64 `json:"course_id"` - CEFRLevel string `json:"cefr_level"` - DisplayOrder *int32 `json:"display_order"` - IsActive *bool `json:"is_active"` + CourseID int64 `json:"course_id"` + CEFRLevel string `json:"cefr_level"` + Title *string `json:"title"` + Description *string `json:"description"` + Thumbnail *string `json:"thumbnail"` + DisplayOrder *int32 `json:"display_order"` + IsActive *bool `json:"is_active"` +} + +type updateLevelReq struct { + Title *string `json:"title"` + Description *string `json:"description"` + Thumbnail *string `json:"thumbnail"` + DisplayOrder *int32 `json:"display_order"` + IsActive *bool `json:"is_active"` } type createModuleReq struct { @@ -1142,14 +1153,17 @@ func (h *Handler) UnifiedHierarchyByCourse(c *fiber.Ctx) error { Message: "Course hierarchy retrieved successfully", Data: []map[string]interface{}{ { - "course_id": course.ID, - "course_title": course.Title, - "level_id": nil, - "cefr_level": nil, - "module_id": nil, - "module_title": nil, - "sub_module_id": nil, - "sub_module_title": nil, + "course_id": course.ID, + "course_title": course.Title, + "level_id": nil, + "cefr_level": nil, + "level_title": nil, + "level_description": nil, + "level_thumbnail": nil, + "module_id": nil, + "module_title": nil, + "sub_module_id": nil, + "sub_module_title": nil, }, }, }) @@ -1203,7 +1217,9 @@ func (h *Handler) CourseLearningPath(c *fiber.Ctx) error { title = row.SubModuleTitle.String } level := "" - if row.CefrLevel.Valid { + if row.LevelTitle.Valid && strings.TrimSpace(row.LevelTitle.String) != "" { + level = strings.TrimSpace(row.LevelTitle.String) + } else if row.CefrLevel.Valid { level = row.CefrLevel.String } subCourseByID[subModuleID] = &domain.LearningPathSubCourse{ @@ -1381,7 +1397,7 @@ func (h *Handler) DeleteCourseSubCategory(c *fiber.Ctx) error { // CreateLevel godoc // @Summary Create level -// @Description Creates a CEFR level under a course +// @Description Creates a CEFR level under a course with optional title (defaults to cefr_level), description, and thumbnail // @Tags course-management // @Accept json // @Produce json @@ -1400,11 +1416,20 @@ func (h *Handler) CreateLevel(c *fiber.Ctx) error { if req.CourseID <= 0 || !validCEFR[req.CEFRLevel] { return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{Message: "course_id and valid cefr_level are required"}) } + title := req.CEFRLevel + if req.Title != nil { + if t := strings.TrimSpace(*req.Title); t != "" { + title = t + } + } created, err := h.analyticsDB.CreateLevel(c.Context(), dbgen.CreateLevelParams{ - CourseID: req.CourseID, - CefrLevel: req.CEFRLevel, - Column3: intOrNil(req.DisplayOrder), - Column4: boolOrNil(req.IsActive), + CourseID: req.CourseID, + CefrLevel: req.CEFRLevel, + Title: title, + Description: toText(req.Description), + Thumbnail: toText(req.Thumbnail), + Column6: intOrNil(req.DisplayOrder), + Column7: boolOrNil(req.IsActive), }) if err != nil { return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{Message: "Failed to create level", Error: err.Error()}) @@ -1412,6 +1437,71 @@ func (h *Handler) CreateLevel(c *fiber.Ctx) error { return c.Status(fiber.StatusCreated).JSON(domain.Response{Message: "Level created", Data: created}) } +// UpdateLevel godoc +// @Summary Update level +// @Description Updates level title, description, thumbnail, display order, and active flag +// @Tags course-management +// @Accept json +// @Produce json +// @Param levelId path int true "Level ID" +// @Param body body updateLevelReq true "Update level payload" +// @Success 200 {object} domain.Response +// @Failure 400 {object} domain.ErrorResponse +// @Failure 404 {object} domain.ErrorResponse +// @Failure 500 {object} domain.ErrorResponse +// @Router /api/v1/course-management/levels/{levelId} [put] +func (h *Handler) UpdateLevel(c *fiber.Ctx) error { + levelID, err := strconv.ParseInt(c.Params("levelId"), 10, 64) + if err != nil || levelID <= 0 { + return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{Message: "Invalid level ID", Error: "levelId must be a positive integer"}) + } + var req updateLevelReq + if err := c.BodyParser(&req); err != nil { + return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{Message: "Invalid request body", Error: err.Error()}) + } + + current, err := h.analyticsDB.GetLevelByID(c.Context(), levelID) + if err != nil { + return c.Status(fiber.StatusNotFound).JSON(domain.ErrorResponse{Message: "Level not found", Error: err.Error()}) + } + + targetTitle := current.Title + if req.Title != nil { + t := strings.TrimSpace(*req.Title) + if t == "" { + return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{Message: "title cannot be empty"}) + } + targetTitle = t + } + + targetDescription := mergeTextField(current.Description, req.Description) + targetThumbnail := mergeTextField(current.Thumbnail, req.Thumbnail) + + targetDisplayOrder := current.DisplayOrder + if req.DisplayOrder != nil { + targetDisplayOrder = *req.DisplayOrder + } + + targetIsActive := current.IsActive + if req.IsActive != nil { + targetIsActive = *req.IsActive + } + + updated, err := h.analyticsDB.UpdateLevel(c.Context(), dbgen.UpdateLevelParams{ + Title: targetTitle, + Description: targetDescription, + Thumbnail: targetThumbnail, + DisplayOrder: targetDisplayOrder, + IsActive: targetIsActive, + ID: levelID, + }) + if err != nil { + return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{Message: "Failed to update level", Error: err.Error()}) + } + + return c.JSON(domain.Response{Message: "Level updated", Data: updated}) +} + // CreateModule godoc // @Summary Create module // @Description Creates a module under a level diff --git a/internal/web_server/routes.go b/internal/web_server/routes.go index 5636fc3..169e6cd 100644 --- a/internal/web_server/routes.go +++ b/internal/web_server/routes.go @@ -108,6 +108,7 @@ func (a *App) initAppRoutes() { 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.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.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)