328 lines
9.1 KiB
Go
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(§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)
|
|
}
|