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" "github.com/timetracker/backend/internal/types" ) // 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) { utils.HandleGetAll(c, models.GetAllActivities, convertActivityToDTO, "activities") } // 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) { utils.HandleGetByID(c, models.GetActivityByID, convertActivityToDTO, "activity") } // 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 := utils.BindJSON(c, &activityCreateDTO); err != nil { utils.BadRequestResponse(c, 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 id, err := utils.ParseID(c, "id") if err != nil { utils.BadRequestResponse(c, "Invalid activity ID format") return } // Parse request body var activityUpdateDTO dto.ActivityUpdateDto if err := utils.BindJSON(c, &activityUpdateDTO); err != nil { utils.BadRequestResponse(c, 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) { utils.HandleDelete(c, models.DeleteActivity, "activity") } // 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: types.FromULID(id), } if dto.Name != nil { update.Name = dto.Name } if dto.BillingRate != nil { update.BillingRate = dto.BillingRate } return update }