package handlers import ( "net/http" "strconv" "strings" "OpsMastery.v5/internal/database" "OpsMastery.v5/internal/models" "github.com/gofiber/fiber/v2" "gorm.io/gorm" ) type sectionPayload struct { Title string `json:"title"` TitleAlt string `json:"Title"` Position *int `json:"position"` PositionAlt *int `json:"Position"` } type taskPayload struct { Title string `json:"title"` TitleAlt string `json:"Title"` Description string `json:"description"` DescriptionAlt string `json:"Description"` Position *int `json:"position"` PositionAlt *int `json:"Position"` SectionID *uint `json:"section_id"` SectionIDAlt *uint `json:"SectionID"` } func (p sectionPayload) normalizedTitle() string { title := strings.TrimSpace(p.Title) if title == "" { title = strings.TrimSpace(p.TitleAlt) } return title } func (p sectionPayload) normalizedPosition() int { if p.Position != nil { return *p.Position } if p.PositionAlt != nil { return *p.PositionAlt } return 0 } func (p taskPayload) normalizedTask() models.Tasks { title := strings.TrimSpace(p.Title) if title == "" { title = strings.TrimSpace(p.TitleAlt) } description := p.Description if description == "" { description = p.DescriptionAlt } position := 0 if p.Position != nil { position = *p.Position } else if p.PositionAlt != nil { position = *p.PositionAlt } sectionID := uint(0) if p.SectionID != nil { sectionID = *p.SectionID } else if p.SectionIDAlt != nil { sectionID = *p.SectionIDAlt } return models.Tasks{ Title: title, Description: description, Position: position, SectionID: sectionID, } } func sectionExists(sectionID uint) (bool, error) { var count int64 err := database.DB().Model(&models.TaskSection{}).Where("id = ?", sectionID).Count(&count).Error if err != nil { return false, err } return count > 0, nil } func CreateTaskSection(c *fiber.Ctx) error { var payload sectionPayload if err := c.BodyParser(&payload); err != nil { return c.Status(http.StatusBadRequest).JSON(fiber.Map{"error": err.Error()}) } section := models.TaskSection{ Title: payload.normalizedTitle(), Position: payload.normalizedPosition(), } if section.Title == "" { return c.Status(http.StatusBadRequest).JSON(fiber.Map{"error": "title is required"}) } if err := database.DB().Create(§ion).Error; err != nil { return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()}) } return c.Status(http.StatusCreated).JSON(section) } func ListTaskSections(c *fiber.Ctx) error { var sections []models.TaskSection err := database.DB(). Preload("Tasks", func(db *gorm.DB) *gorm.DB { return db.Order("position asc, id asc") }). Order("position asc, id asc"). Find(§ions).Error if err != nil { return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()}) } return c.JSON(sections) } func UpdateTaskSectionByID(c *fiber.Ctx) error { id := c.Params("id") sectionID, err := strconv.ParseUint(id, 10, 32) if err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid section ID"}) } var payload sectionPayload if err := c.BodyParser(&payload); err != nil { return c.Status(http.StatusBadRequest).JSON(fiber.Map{"error": err.Error()}) } updates := map[string]any{} if title := payload.normalizedTitle(); title != "" { updates["title"] = title } if payload.Position != nil || payload.PositionAlt != nil { updates["position"] = payload.normalizedPosition() } if len(updates) == 0 { return c.Status(http.StatusBadRequest).JSON(fiber.Map{"error": "at least one field is required"}) } result := database.DB().Model(&models.TaskSection{}).Where("id = ?", sectionID).Updates(updates) if result.Error != nil { return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"error": result.Error.Error()}) } if result.RowsAffected == 0 { return c.Status(http.StatusNotFound).JSON(fiber.Map{"error": "Section not found"}) } var section models.TaskSection if err := database.DB().First(§ion, sectionID).Error; err != nil { return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()}) } return c.JSON(section) } func DeleteTaskSectionByID(c *fiber.Ctx) error { id := c.Params("id") sectionID, err := strconv.ParseUint(id, 10, 32) if err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid section ID"}) } result := database.DB().Unscoped().Delete(&models.TaskSection{}, sectionID) if result.RowsAffected == 0 { return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "Section not found"}) } if result.Error != nil { return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"error": result.Error.Error()}) } return c.Status(http.StatusOK).JSON(fiber.Map{"message": "Section deleted successfully"}) } func CreateTask(c *fiber.Ctx) error { var payload taskPayload if err := c.BodyParser(&payload); err != nil { return c.Status(http.StatusBadRequest).JSON(fiber.Map{"error": err.Error()}) } task := payload.normalizedTask() if task.Title == "" || task.SectionID == 0 { return c.Status(http.StatusBadRequest).JSON(fiber.Map{"error": "title and section_id are required"}) } exists, err := sectionExists(task.SectionID) if err != nil { return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()}) } if !exists { return c.Status(http.StatusBadRequest).JSON(fiber.Map{"error": "section not found"}) } if err := database.DB().Create(&task).Error; err != nil { return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()}) } return c.Status(http.StatusCreated).JSON(task) } func ListTasks(c *fiber.Ctx) error { var tasks []models.Tasks err := database.DB(). Preload("Section"). Order("position asc, id asc"). Find(&tasks).Error if err != nil { return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()}) } return c.JSON(tasks) } func GetTaskByID(c *fiber.Ctx) error { id := c.Params("id") var task models.Tasks taskID, err := strconv.ParseUint(id, 10, 32) if err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid task ID"}) } if err := database.DB().Preload("Section").First(&task, taskID).Error; err != nil { if err == gorm.ErrRecordNotFound { return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "Task not found"}) } return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()}) } return c.JSON(task) } func DeleteTaskByID(c *fiber.Ctx) error { id := c.Params("id") taskID, err := strconv.ParseUint(id, 10, 32) if err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid task ID"}) } result := database.DB().Unscoped().Delete(&models.Tasks{}, taskID) if result.RowsAffected == 0 { return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "Task not found"}) } if result.Error != nil { return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"error": result.Error.Error()}) } return c.Status(http.StatusOK).JSON(fiber.Map{"message": "Task deleted successfully"}) } func UpdateTaskByID(c *fiber.Ctx) error { id := c.Params("id") taskID, err := strconv.ParseUint(id, 10, 32) if err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid task ID"}) } var payload taskPayload if err := c.BodyParser(&payload); err != nil { return c.Status(http.StatusBadRequest).JSON(fiber.Map{"error": err.Error()}) } updates := map[string]any{} title := strings.TrimSpace(payload.Title) if title == "" { title = strings.TrimSpace(payload.TitleAlt) } if title != "" { updates["title"] = title } if payload.Description != "" || payload.DescriptionAlt != "" { description := payload.Description if description == "" { description = payload.DescriptionAlt } updates["description"] = description } if payload.Position != nil { updates["position"] = *payload.Position } else if payload.PositionAlt != nil { updates["position"] = *payload.PositionAlt } sectionID := uint(0) if payload.SectionID != nil { sectionID = *payload.SectionID } else if payload.SectionIDAlt != nil { sectionID = *payload.SectionIDAlt } if sectionID != 0 { exists, err := sectionExists(sectionID) if err != nil { return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()}) } if !exists { return c.Status(http.StatusBadRequest).JSON(fiber.Map{"error": "section not found"}) } updates["section_id"] = sectionID } if len(updates) == 0 { return c.Status(http.StatusBadRequest).JSON(fiber.Map{"error": "at least one field is required"}) } result := database.DB().Model(&models.Tasks{}).Where("id = ?", taskID).Updates(updates) if result.Error != nil { return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"error": result.Error.Error()}) } if result.RowsAffected == 0 { return c.Status(http.StatusNotFound).JSON(fiber.Map{"error": "Task not found"}) } var task models.Tasks if err := database.DB().Preload("Section").First(&task, taskID).Error; err != nil { return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()}) } return c.JSON(task) }