360 lines
8.6 KiB
Go
360 lines
8.6 KiB
Go
package api
|
|
|
|
import (
|
|
"net/http"
|
|
"project-dashboard/api/utils"
|
|
"project-dashboard/models"
|
|
"time"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
type CreateTaskRequest struct {
|
|
Title string `json:"title" binding:"required"`
|
|
Description string `json:"description"`
|
|
Priority string `json:"priority"`
|
|
DueDate string `json:"due_date"`
|
|
AssignedToID *uint `json:"assigned_to_id"`
|
|
}
|
|
|
|
type UpdateTaskRequest struct {
|
|
Title string `json:"title"`
|
|
Description string `json:"description"`
|
|
Status string `json:"status"`
|
|
Priority string `json:"priority"`
|
|
DueDate string `json:"due_date"`
|
|
AssignedToID *uint `json:"assigned_to_id"`
|
|
Position *int `json:"position"`
|
|
}
|
|
|
|
type CreateSubtaskRequest struct {
|
|
Title string `json:"title" binding:"required"`
|
|
Description string `json:"description"`
|
|
AssignedToID *uint `json:"assigned_to_id"`
|
|
}
|
|
|
|
type CreateCommentRequest struct {
|
|
Content string `json:"content" binding:"required"`
|
|
}
|
|
|
|
// CreateTask creates a new task
|
|
func CreateTask(c *gin.Context) {
|
|
user, exists := c.Get("user")
|
|
if !exists {
|
|
utils.ErrorResponse(c, http.StatusUnauthorized, "User not authenticated")
|
|
return
|
|
}
|
|
|
|
userObj := user.(*models.User)
|
|
project, _ := c.Get("project")
|
|
projectObj := project.(*models.Project)
|
|
|
|
var req CreateTaskRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
utils.ValidationErrorResponse(c, gin.H{
|
|
"error": err.Error(),
|
|
})
|
|
return
|
|
}
|
|
|
|
task := models.Task{
|
|
Title: req.Title,
|
|
Description: req.Description,
|
|
Priority: req.Priority,
|
|
ProjectID: projectObj.ID,
|
|
CreatedByID: userObj.ID,
|
|
AssignedToID: req.AssignedToID,
|
|
Status: "todo",
|
|
}
|
|
|
|
if task.Priority == "" {
|
|
task.Priority = "medium"
|
|
}
|
|
|
|
// Parse due date if provided
|
|
if req.DueDate != "" {
|
|
if dueDate, err := time.Parse("2006-01-02T15:04:05Z07:00", req.DueDate); err == nil {
|
|
task.DueDate = &dueDate
|
|
}
|
|
}
|
|
|
|
if err := models.GetDB().Create(&task).Error; err != nil {
|
|
utils.ErrorResponse(c, http.StatusInternalServerError, "Failed to create task")
|
|
return
|
|
}
|
|
|
|
// Load task with relationships
|
|
models.GetDB().
|
|
Preload("Project").
|
|
Preload("AssignedTo").
|
|
Preload("CreatedBy").
|
|
First(&task, task.ID)
|
|
|
|
utils.SuccessResponse(c, http.StatusCreated, "Task created successfully", task)
|
|
}
|
|
|
|
// GetTasks returns all tasks for a project
|
|
func GetTasks(c *gin.Context) {
|
|
project, exists := c.Get("project")
|
|
if !exists {
|
|
utils.ErrorResponse(c, http.StatusNotFound, "Project not found")
|
|
return
|
|
}
|
|
|
|
projectObj := project.(*models.Project)
|
|
|
|
var tasks []models.Task
|
|
|
|
if err := models.GetDB().
|
|
Preload("AssignedTo").
|
|
Preload("CreatedBy").
|
|
Preload("Comments").
|
|
Preload("Files").
|
|
Preload("Subtasks").
|
|
Preload("Labels").
|
|
Where("project_id = ?", projectObj.ID).
|
|
Order("position ASC, created_at DESC").
|
|
Find(&tasks).Error; err != nil {
|
|
utils.ErrorResponse(c, http.StatusInternalServerError, "Failed to fetch tasks")
|
|
return
|
|
}
|
|
|
|
utils.SuccessResponse(c, http.StatusOK, "Tasks retrieved successfully", tasks)
|
|
}
|
|
|
|
// GetTask returns a specific task
|
|
func GetTask(c *gin.Context) {
|
|
project, _ := c.Get("project")
|
|
projectObj := project.(*models.Project)
|
|
|
|
taskID := c.Param("task_id")
|
|
if taskID == "" {
|
|
utils.ErrorResponse(c, http.StatusBadRequest, "Task ID required")
|
|
return
|
|
}
|
|
|
|
var task models.Task
|
|
if err := models.GetDB().
|
|
Preload("Project").
|
|
Preload("AssignedTo").
|
|
Preload("CreatedBy").
|
|
Preload("Comments").
|
|
Preload("Comments.User").
|
|
Preload("Files").
|
|
Preload("Subtasks").
|
|
Preload("Subtasks.AssignedTo").
|
|
Preload("Labels").
|
|
Where("id = ? AND project_id = ?", taskID, projectObj.ID).
|
|
First(&task).Error; err != nil {
|
|
utils.ErrorResponse(c, http.StatusNotFound, "Task not found")
|
|
return
|
|
}
|
|
|
|
utils.SuccessResponse(c, http.StatusOK, "Task retrieved successfully", task)
|
|
}
|
|
|
|
// UpdateTask updates a task
|
|
func UpdateTask(c *gin.Context) {
|
|
project, _ := c.Get("project")
|
|
projectObj := project.(*models.Project)
|
|
|
|
taskID := c.Param("task_id")
|
|
if taskID == "" {
|
|
utils.ErrorResponse(c, http.StatusBadRequest, "Task ID required")
|
|
return
|
|
}
|
|
|
|
var task models.Task
|
|
if err := models.GetDB().
|
|
Where("id = ? AND project_id = ?", taskID, projectObj.ID).
|
|
First(&task).Error; err != nil {
|
|
utils.ErrorResponse(c, http.StatusNotFound, "Task not found")
|
|
return
|
|
}
|
|
|
|
var req UpdateTaskRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
utils.ValidationErrorResponse(c, gin.H{
|
|
"error": err.Error(),
|
|
})
|
|
return
|
|
}
|
|
|
|
// Update fields
|
|
if req.Title != "" {
|
|
task.Title = req.Title
|
|
}
|
|
if req.Description != "" {
|
|
task.Description = req.Description
|
|
}
|
|
if req.Status != "" {
|
|
task.Status = req.Status
|
|
if req.Status == "done" {
|
|
task.MarkAsCompleted()
|
|
}
|
|
}
|
|
if req.Priority != "" {
|
|
task.Priority = req.Priority
|
|
}
|
|
if req.AssignedToID != nil {
|
|
task.AssignedToID = req.AssignedToID
|
|
}
|
|
if req.Position != nil {
|
|
task.Position = *req.Position
|
|
}
|
|
|
|
// Parse due date if provided
|
|
if req.DueDate != "" {
|
|
if dueDate, err := time.Parse("2006-01-02T15:04:05Z07:00", req.DueDate); err == nil {
|
|
task.DueDate = &dueDate
|
|
}
|
|
}
|
|
|
|
if err := models.GetDB().Save(&task).Error; err != nil {
|
|
utils.ErrorResponse(c, http.StatusInternalServerError, "Failed to update task")
|
|
return
|
|
}
|
|
|
|
// Load updated task with relationships
|
|
models.GetDB().
|
|
Preload("AssignedTo").
|
|
Preload("CreatedBy").
|
|
Preload("Comments").
|
|
Preload("Files").
|
|
Preload("Subtasks").
|
|
Preload("Labels").
|
|
First(&task, task.ID)
|
|
|
|
utils.SuccessResponse(c, http.StatusOK, "Task updated successfully", task)
|
|
}
|
|
|
|
// DeleteTask deletes a task
|
|
func DeleteTask(c *gin.Context) {
|
|
project, _ := c.Get("project")
|
|
projectObj := project.(*models.Project)
|
|
|
|
taskID := c.Param("task_id")
|
|
if taskID == "" {
|
|
utils.ErrorResponse(c, http.StatusBadRequest, "Task ID required")
|
|
return
|
|
}
|
|
|
|
var task models.Task
|
|
if err := models.GetDB().
|
|
Where("id = ? AND project_id = ?", taskID, projectObj.ID).
|
|
First(&task).Error; err != nil {
|
|
utils.ErrorResponse(c, http.StatusNotFound, "Task not found")
|
|
return
|
|
}
|
|
|
|
if err := models.GetDB().Delete(&task).Error; err != nil {
|
|
utils.ErrorResponse(c, http.StatusInternalServerError, "Failed to delete task")
|
|
return
|
|
}
|
|
|
|
utils.SuccessResponse(c, http.StatusOK, "Task deleted successfully", nil)
|
|
}
|
|
|
|
// CreateSubtask creates a new subtask
|
|
func CreateSubtask(c *gin.Context) {
|
|
project, _ := c.Get("project")
|
|
projectObj := project.(*models.Project)
|
|
|
|
taskID := c.Param("task_id")
|
|
if taskID == "" {
|
|
utils.ErrorResponse(c, http.StatusBadRequest, "Task ID required")
|
|
return
|
|
}
|
|
|
|
// Verify task exists and belongs to project
|
|
var task models.Task
|
|
if err := models.GetDB().
|
|
Where("id = ? AND project_id = ?", taskID, projectObj.ID).
|
|
First(&task).Error; err != nil {
|
|
utils.ErrorResponse(c, http.StatusNotFound, "Task not found")
|
|
return
|
|
}
|
|
|
|
var req CreateSubtaskRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
utils.ValidationErrorResponse(c, gin.H{
|
|
"error": err.Error(),
|
|
})
|
|
return
|
|
}
|
|
|
|
subtask := models.Subtask{
|
|
Title: req.Title,
|
|
Description: req.Description,
|
|
ParentTaskID: task.ID,
|
|
AssignedToID: req.AssignedToID,
|
|
Status: "todo",
|
|
}
|
|
|
|
if err := models.GetDB().Create(&subtask).Error; err != nil {
|
|
utils.ErrorResponse(c, http.StatusInternalServerError, "Failed to create subtask")
|
|
return
|
|
}
|
|
|
|
// Load subtask with relationships
|
|
models.GetDB().
|
|
Preload("AssignedTo").
|
|
First(&subtask, subtask.ID)
|
|
|
|
utils.SuccessResponse(c, http.StatusCreated, "Subtask created successfully", subtask)
|
|
}
|
|
|
|
// AddComment adds a comment to a task
|
|
func AddComment(c *gin.Context) {
|
|
user, exists := c.Get("user")
|
|
if !exists {
|
|
utils.ErrorResponse(c, http.StatusUnauthorized, "User not authenticated")
|
|
return
|
|
}
|
|
|
|
userObj := user.(*models.User)
|
|
project, _ := c.Get("project")
|
|
projectObj := project.(*models.Project)
|
|
|
|
taskID := c.Param("task_id")
|
|
if taskID == "" {
|
|
utils.ErrorResponse(c, http.StatusBadRequest, "Task ID required")
|
|
return
|
|
}
|
|
|
|
// Verify task exists and belongs to project
|
|
var task models.Task
|
|
if err := models.GetDB().
|
|
Where("id = ? AND project_id = ?", taskID, projectObj.ID).
|
|
First(&task).Error; err != nil {
|
|
utils.ErrorResponse(c, http.StatusNotFound, "Task not found")
|
|
return
|
|
}
|
|
|
|
var req CreateCommentRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
utils.ValidationErrorResponse(c, gin.H{
|
|
"error": err.Error(),
|
|
})
|
|
return
|
|
}
|
|
|
|
comment := models.Comment{
|
|
Content: req.Content,
|
|
TaskID: task.ID,
|
|
UserID: userObj.ID,
|
|
}
|
|
|
|
if err := models.GetDB().Create(&comment).Error; err != nil {
|
|
utils.ErrorResponse(c, http.StatusInternalServerError, "Failed to create comment")
|
|
return
|
|
}
|
|
|
|
// Load comment with user
|
|
models.GetDB().
|
|
Preload("User").
|
|
First(&comment, comment.ID)
|
|
|
|
utils.SuccessResponse(c, http.StatusCreated, "Comment added successfully", comment)
|
|
}
|