diff --git a/internal/web_server/handlers/hierarchy_handler.go b/internal/web_server/handlers/hierarchy_handler.go index 7feb9a2..647ad5b 100644 --- a/internal/web_server/handlers/hierarchy_handler.go +++ b/internal/web_server/handlers/hierarchy_handler.go @@ -18,6 +18,11 @@ type createCourseSubCategoryReq struct { IsActive *bool `json:"is_active"` } +type createCourseCategoryReq struct { + Name string `json:"name"` + IsActive *bool `json:"is_active"` +} + type createLevelReq struct { CourseID int64 `json:"course_id"` CEFRLevel string `json:"cefr_level"` @@ -111,6 +116,68 @@ func intOrNil(v *int32) interface{} { return *v } +// ListCourseCategories godoc +// @Summary List course categories +// @Description Legacy-compatible endpoint for listing course categories +// @Tags course-management +// @Produce json +// @Success 200 {object} domain.Response +// @Failure 500 {object} domain.ErrorResponse +// @Router /api/v1/course-management/categories [get] +func (h *Handler) ListCourseCategories(c *fiber.Ctx) error { + rows, err := h.analyticsDB.GetAllCourseCategories(c.Context(), dbgen.GetAllCourseCategoriesParams{ + Offset: pgtype.Int4{Int32: 0, Valid: true}, + Limit: pgtype.Int4{Int32: 10000, Valid: true}, + }) + if err != nil { + return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{Message: "Failed to load categories", Error: err.Error()}) + } + + total := 0 + if len(rows) > 0 { + total = int(rows[0].TotalCount) + } + + return c.JSON(domain.Response{ + Message: "Categories retrieved successfully", + Data: map[string]interface{}{ + "categories": rows, + "total_count": total, + }, + }) +} + +// CreateCourseCategory godoc +// @Summary Create course category +// @Description Legacy-compatible endpoint for creating a course category +// @Tags course-management +// @Accept json +// @Produce json +// @Param body body createCourseCategoryReq true "Create category payload" +// @Success 201 {object} domain.Response +// @Failure 400 {object} domain.ErrorResponse +// @Failure 500 {object} domain.ErrorResponse +// @Router /api/v1/course-management/categories [post] +func (h *Handler) CreateCourseCategory(c *fiber.Ctx) error { + var req createCourseCategoryReq + if err := c.BodyParser(&req); err != nil { + return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{Message: "Invalid request body", Error: err.Error()}) + } + if strings.TrimSpace(req.Name) == "" { + return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{Message: "name is required"}) + } + + created, err := h.analyticsDB.CreateCourseCategory(c.Context(), dbgen.CreateCourseCategoryParams{ + Name: req.Name, + Column2: boolOrNil(req.IsActive), + }) + if err != nil { + return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{Message: "Failed to create category", Error: err.Error()}) + } + + return c.Status(fiber.StatusCreated).JSON(domain.Response{Message: "Course category created", Data: created}) +} + // UnifiedHierarchy godoc // @Summary Get unified course hierarchy // @Description Returns full hierarchy: category -> sub-category -> course diff --git a/internal/web_server/routes.go b/internal/web_server/routes.go index a278acb..567e536 100644 --- a/internal/web_server/routes.go +++ b/internal/web_server/routes.go @@ -79,6 +79,8 @@ func (a *App) initAppRoutes() { groupV1.Get("/assessment/questions/:id", h.GetAssessmentQuestionByID) // Unified Course Management (single hierarchy model) + groupV1.Get("/course-management/categories", a.authMiddleware, a.RequirePermission("learning_tree.get"), h.ListCourseCategories) + groupV1.Post("/course-management/categories", a.authMiddleware, a.RequirePermission("course_categories.create"), h.CreateCourseCategory) groupV1.Get("/course-management/hierarchy", a.authMiddleware, a.RequirePermission("learning_tree.get"), h.UnifiedHierarchy) groupV1.Get("/course-management/courses/:courseId/hierarchy", a.authMiddleware, a.RequirePermission("learning_tree.get"), h.UnifiedHierarchyByCourse) groupV1.Post("/course-management/sub-categories", a.authMiddleware, a.RequirePermission("course_categories.create"), h.CreateCourseSubCategory)