package api import ( "net/http" "project-dashboard/api/utils" "project-dashboard/models" "strconv" "github.com/gin-gonic/gin" ) type CreateProjectRequest struct { Name string `json:"name" binding:"required"` Description string `json:"description"` Color string `json:"color"` } type UpdateProjectRequest struct { Name string `json:"name"` Description string `json:"description"` Status string `json:"status"` Color string `json:"color"` } type AddMemberRequest struct { UserID uint `json:"user_id" binding:"required"` Role string `json:"role"` } // CreateProject creates a new project func CreateProject(c *gin.Context) { user, exists := c.Get("user") if !exists { utils.ErrorResponse(c, http.StatusUnauthorized, "User not authenticated") return } userObj := user.(*models.User) var req CreateProjectRequest if err := c.ShouldBindJSON(&req); err != nil { utils.ValidationErrorResponse(c, gin.H{ "error": err.Error(), }) return } project := models.Project{ Name: req.Name, Description: req.Description, Color: req.Color, OwnerID: userObj.ID, Status: "active", } if project.Color == "" { project.Color = "#3b82f6" } if err := models.GetDB().Create(&project).Error; err != nil { utils.ErrorResponse(c, http.StatusInternalServerError, "Failed to create project") return } // Add owner as project member member := models.ProjectMember{ ProjectID: project.ID, UserID: userObj.ID, Role: "owner", } models.GetDB().Create(&member) // Load project with relationships models.GetDB().Preload("Owner").Preload("Members").First(&project, project.ID) utils.SuccessResponse(c, http.StatusCreated, "Project created successfully", project) } // GetProjects returns all projects for the current user func GetProjects(c *gin.Context) { user, exists := c.Get("user") if !exists { utils.ErrorResponse(c, http.StatusUnauthorized, "User not authenticated") return } userObj := user.(*models.User) var projects []models.Project // Get projects where user is owner or member if err := models.GetDB(). Preload("Owner"). Preload("Members"). Where("owner_id = ? OR id IN (SELECT project_id FROM project_members WHERE user_id = ?)", userObj.ID, userObj.ID). Find(&projects).Error; err != nil { utils.ErrorResponse(c, http.StatusInternalServerError, "Failed to fetch projects") return } utils.SuccessResponse(c, http.StatusOK, "Projects retrieved successfully", projects) } // GetProject returns a specific project func GetProject(c *gin.Context) { project, exists := c.Get("project") if !exists { utils.ErrorResponse(c, http.StatusNotFound, "Project not found") return } projectObj := project.(*models.Project) // Load additional relationships models.GetDB(). Preload("Owner"). Preload("Members"). Preload("Tasks"). Preload("Files"). First(projectObj, projectObj.ID) utils.SuccessResponse(c, http.StatusOK, "Project retrieved successfully", projectObj) } // UpdateProject updates a project func UpdateProject(c *gin.Context) { project, exists := c.Get("project") if !exists { utils.ErrorResponse(c, http.StatusNotFound, "Project not found") return } projectObj := project.(*models.Project) user, _ := c.Get("user") userObj := user.(*models.User) // Check if user is owner or manager role := projectObj.GetMemberRole(userObj.ID) if role != "owner" && role != "manager" { utils.ErrorResponse(c, http.StatusForbidden, "Insufficient permissions") return } var req UpdateProjectRequest if err := c.ShouldBindJSON(&req); err != nil { utils.ValidationErrorResponse(c, gin.H{ "error": err.Error(), }) return } // Update fields if req.Name != "" { projectObj.Name = req.Name } if req.Description != "" { projectObj.Description = req.Description } if req.Status != "" { projectObj.Status = req.Status } if req.Color != "" { projectObj.Color = req.Color } if err := models.GetDB().Save(projectObj).Error; err != nil { utils.ErrorResponse(c, http.StatusInternalServerError, "Failed to update project") return } utils.SuccessResponse(c, http.StatusOK, "Project updated successfully", projectObj) } // DeleteProject deletes a project func DeleteProject(c *gin.Context) { project, exists := c.Get("project") if !exists { utils.ErrorResponse(c, http.StatusNotFound, "Project not found") return } projectObj := project.(*models.Project) user, _ := c.Get("user") userObj := user.(*models.User) // Only owner can delete project if !projectObj.IsOwner(userObj.ID) { utils.ErrorResponse(c, http.StatusForbidden, "Only project owner can delete project") return } if err := models.GetDB().Delete(projectObj).Error; err != nil { utils.ErrorResponse(c, http.StatusInternalServerError, "Failed to delete project") return } utils.SuccessResponse(c, http.StatusOK, "Project deleted successfully", nil) } // AddMember adds a member to the project func AddMember(c *gin.Context) { project, exists := c.Get("project") if !exists { utils.ErrorResponse(c, http.StatusNotFound, "Project not found") return } projectObj := project.(*models.Project) user, _ := c.Get("user") userObj := user.(*models.User) // Check if user has permission to add members role := projectObj.GetMemberRole(userObj.ID) if role != "owner" && role != "manager" { utils.ErrorResponse(c, http.StatusForbidden, "Insufficient permissions") return } var req AddMemberRequest if err := c.ShouldBindJSON(&req); err != nil { utils.ValidationErrorResponse(c, gin.H{ "error": err.Error(), }) return } // Check if user exists var targetUser models.User if err := models.GetDB().First(&targetUser, req.UserID).Error; err != nil { utils.ErrorResponse(c, http.StatusNotFound, "User not found") return } // Check if user is already a member if projectObj.HasMember(req.UserID) { utils.ErrorResponse(c, http.StatusConflict, "User is already a member of this project") return } // Set default role if req.Role == "" { req.Role = "member" } member := models.ProjectMember{ ProjectID: projectObj.ID, UserID: req.UserID, Role: req.Role, } if err := models.GetDB().Create(&member).Error; err != nil { utils.ErrorResponse(c, http.StatusInternalServerError, "Failed to add member") return } // Load updated project models.GetDB().Preload("Members").First(projectObj, projectObj.ID) utils.SuccessResponse(c, http.StatusOK, "Member added successfully", projectObj) } // RemoveMember removes a member from the project func RemoveMember(c *gin.Context) { project, exists := c.Get("project") if !exists { utils.ErrorResponse(c, http.StatusNotFound, "Project not found") return } projectObj := project.(*models.Project) user, _ := c.Get("user") userObj := user.(*models.User) // Check if user has permission to remove members role := projectObj.GetMemberRole(userObj.ID) if role != "owner" && role != "manager" { utils.ErrorResponse(c, http.StatusForbidden, "Insufficient permissions") return } userIDStr := c.Param("user_id") if userIDStr == "" { utils.ErrorResponse(c, http.StatusBadRequest, "User ID required") return } userID, err := strconv.ParseUint(userIDStr, 10, 32) if err != nil { utils.ErrorResponse(c, http.StatusBadRequest, "Invalid user ID") return } // Check if user is a member if !projectObj.HasMember(uint(userID)) { utils.ErrorResponse(c, http.StatusNotFound, "User is not a member of this project") return } // Remove member if err := models.GetDB(). Where("project_id = ? AND user_id = ?", projectObj.ID, uint(userID)). Delete(&models.ProjectMember{}).Error; err != nil { utils.ErrorResponse(c, http.StatusInternalServerError, "Failed to remove member") return } // Load updated project models.GetDB().Preload("Members").First(projectObj, projectObj.ID) utils.SuccessResponse(c, http.StatusOK, "Member removed successfully", projectObj) }