Files
OpsMastery.v5/internal/handlers/taskHandler.go
2026-07-12 20:26:16 -04:00

328 lines
9.1 KiB
Go

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(&section).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(&sections).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(&section, 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)
}