package handlers import ( "context" "github.com/gin-gonic/gin" "github.com/oklog/ulid/v2" "github.com/timetracker/backend/internal/api/dto" "github.com/timetracker/backend/internal/api/responses" "github.com/timetracker/backend/internal/api/utils" "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) { utils.HandleCreate(c, createActivityWrapper, convertActivityToDTO, "activity") } // 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) { utils.HandleUpdate(c, models.UpdateActivity, convertActivityToDTO, prepareActivityUpdate, "activity") } // 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 } // prepareActivityUpdate prepares the activity update object by parsing the ID, binding the JSON, and converting the DTO to a model func prepareActivityUpdate(c *gin.Context) (models.ActivityUpdate, error) { // Parse ID from URL id, err := utils.ParseID(c, "id") if err != nil { responses.BadRequestResponse(c, "Invalid activity ID format") return models.ActivityUpdate{}, err } // Parse request body var activityUpdateDTO dto.ActivityUpdateDto if err := utils.BindJSON(c, &activityUpdateDTO); err != nil { responses.BadRequestResponse(c, err.Error()) return models.ActivityUpdate{}, err } // Set ID from URL activityUpdateDTO.ID = id.String() // Convert DTO to model return convertUpdateActivityDTOToModel(activityUpdateDTO), nil } // createActivityWrapper is a wrapper function for models.CreateActivity that takes a DTO as input func createActivityWrapper(ctx context.Context, createDTO dto.ActivityCreateDto) (*models.Activity, error) { // Convert DTO to model activityCreate := convertCreateActivityDTOToModel(createDTO) // Call the original function return models.CreateActivity(ctx, activityCreate) }