package handlers import ( "net/http" "github.com/gin-gonic/gin" "github.com/oklog/ulid/v2" "github.com/timetracker/backend/internal/api/utils" dto "github.com/timetracker/backend/internal/dtos" "github.com/timetracker/backend/internal/models" ) // ActivityHandler handles activity-related API endpoints type ActivityHandler struct{} // NewActivityHandler creates a new ActivityHandler func NewActivityHandler() *ActivityHandler { return &ActivityHandler{} } // GetActivities handles GET /activities // // @Summary Get all activities // @Description Get a list of all activities // @Tags activities // @Accept json // @Produce json // @Security BearerAuth // @Success 200 {object} utils.Response{data=[]dto.ActivityDto} // @Failure 401 {object} utils.Response{error=utils.ErrorInfo} // @Failure 500 {object} utils.Response{error=utils.ErrorInfo} // @Router /activities [get] func (h *ActivityHandler) GetActivities(c *gin.Context) { // Get activities from the database activities, err := models.GetAllActivities(c.Request.Context()) if err != nil { utils.InternalErrorResponse(c, "Error retrieving activities: "+err.Error()) return } // Convert to DTOs activityDTOs := make([]dto.ActivityDto, len(activities)) for i, activity := range activities { activityDTOs[i] = convertActivityToDTO(&activity) } utils.SuccessResponse(c, http.StatusOK, activityDTOs) } // GetActivityByID handles GET /activities/:id // // @Summary Get activity by ID // @Description Get an activity by its ID // @Tags activities // @Accept json // @Produce json // @Security BearerAuth // @Param id path string true "Activity ID" // @Success 200 {object} utils.Response{data=dto.ActivityDto} // @Failure 400 {object} utils.Response{error=utils.ErrorInfo} // @Failure 401 {object} utils.Response{error=utils.ErrorInfo} // @Failure 404 {object} utils.Response{error=utils.ErrorInfo} // @Failure 500 {object} utils.Response{error=utils.ErrorInfo} // @Router /activities/{id} [get] func (h *ActivityHandler) GetActivityByID(c *gin.Context) { // Parse ID from URL idStr := c.Param("id") id, err := ulid.Parse(idStr) if err != nil { utils.BadRequestResponse(c, "Invalid activity ID format") return } // Get activity from the database activity, err := models.GetActivityByID(c.Request.Context(), models.FromULID(id)) if err != nil { utils.InternalErrorResponse(c, "Error retrieving activity: "+err.Error()) return } if activity == nil { utils.NotFoundResponse(c, "Activity not found") return } // Convert to DTO activityDTO := convertActivityToDTO(activity) utils.SuccessResponse(c, http.StatusOK, activityDTO) } // CreateActivity handles POST /activities // // @Summary Create a new activity // @Description Create a new activity // @Tags activities // @Accept json // @Produce json // @Security BearerAuth // @Param activity body dto.ActivityCreateDto true "Activity data" // @Success 201 {object} utils.Response{data=dto.ActivityDto} // @Failure 400 {object} utils.Response{error=utils.ErrorInfo} // @Failure 401 {object} utils.Response{error=utils.ErrorInfo} // @Failure 500 {object} utils.Response{error=utils.ErrorInfo} // @Router /activities [post] func (h *ActivityHandler) CreateActivity(c *gin.Context) { // Parse request body var activityCreateDTO dto.ActivityCreateDto if err := c.ShouldBindJSON(&activityCreateDTO); err != nil { utils.BadRequestResponse(c, "Invalid request body: "+err.Error()) return } // Convert DTO to model activityCreate := convertCreateActivityDTOToModel(activityCreateDTO) // Create activity in the database activity, err := models.CreateActivity(c.Request.Context(), activityCreate) if err != nil { utils.InternalErrorResponse(c, "Error creating activity: "+err.Error()) return } // Convert to DTO activityDTO := convertActivityToDTO(activity) utils.SuccessResponse(c, http.StatusCreated, activityDTO) } // UpdateActivity handles PUT /activities/:id // // @Summary Update an activity // @Description Update an existing activity // @Tags activities // @Accept json // @Produce json // @Security BearerAuth // @Param id path string true "Activity ID" // @Param activity body dto.ActivityUpdateDto true "Activity data" // @Success 200 {object} utils.Response{data=dto.ActivityDto} // @Failure 400 {object} utils.Response{error=utils.ErrorInfo} // @Failure 401 {object} utils.Response{error=utils.ErrorInfo} // @Failure 404 {object} utils.Response{error=utils.ErrorInfo} // @Failure 500 {object} utils.Response{error=utils.ErrorInfo} // @Router /activities/{id} [put] func (h *ActivityHandler) UpdateActivity(c *gin.Context) { // Parse ID from URL idStr := c.Param("id") id, err := ulid.Parse(idStr) if err != nil { utils.BadRequestResponse(c, "Invalid activity ID format") return } // Parse request body var activityUpdateDTO dto.ActivityUpdateDto if err := c.ShouldBindJSON(&activityUpdateDTO); err != nil { utils.BadRequestResponse(c, "Invalid request body: "+err.Error()) return } // Set ID from URL activityUpdateDTO.ID = id.String() // Convert DTO to model activityUpdate := convertUpdateActivityDTOToModel(activityUpdateDTO) // Update activity in the database activity, err := models.UpdateActivity(c.Request.Context(), activityUpdate) if err != nil { utils.InternalErrorResponse(c, "Error updating activity: "+err.Error()) return } if activity == nil { utils.NotFoundResponse(c, "Activity not found") return } // Convert to DTO activityDTO := convertActivityToDTO(activity) utils.SuccessResponse(c, http.StatusOK, activityDTO) } // DeleteActivity handles DELETE /activities/:id // // @Summary Delete an activity // @Description Delete an activity by its ID // @Tags activities // @Accept json // @Produce json // @Security BearerAuth // @Param id path string true "Activity ID" // @Success 204 {object} utils.Response // @Failure 400 {object} utils.Response{error=utils.ErrorInfo} // @Failure 401 {object} utils.Response{error=utils.ErrorInfo} // @Failure 500 {object} utils.Response{error=utils.ErrorInfo} // @Router /activities/{id} [delete] func (h *ActivityHandler) DeleteActivity(c *gin.Context) { // Parse ID from URL idStr := c.Param("id") id, err := ulid.Parse(idStr) if err != nil { utils.BadRequestResponse(c, "Invalid activity ID format") return } // Delete activity from the database err = models.DeleteActivity(c.Request.Context(), models.FromULID(id)) if err != nil { utils.InternalErrorResponse(c, "Error deleting activity: "+err.Error()) return } utils.SuccessResponse(c, http.StatusNoContent, nil) } // Helper functions for DTO conversion func convertActivityToDTO(activity *models.Activity) dto.ActivityDto { return dto.ActivityDto{ ID: activity.ID.String(), CreatedAt: activity.CreatedAt, UpdatedAt: activity.UpdatedAt, Name: activity.Name, BillingRate: activity.BillingRate, } } func convertCreateActivityDTOToModel(dto dto.ActivityCreateDto) models.ActivityCreate { return models.ActivityCreate{ Name: dto.Name, BillingRate: dto.BillingRate, } } func convertUpdateActivityDTOToModel(dto dto.ActivityUpdateDto) models.ActivityUpdate { id, _ := ulid.Parse(dto.ID) update := models.ActivityUpdate{ ID: models.FromULID(id), } if dto.Name != nil { update.Name = dto.Name } if dto.BillingRate != nil { update.BillingRate = dto.BillingRate } return update }