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) }