package handlers import ( "fmt" "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" ) // CustomerHandler handles customer-related API endpoints type CustomerHandler struct{} // NewCustomerHandler creates a new CustomerHandler func NewCustomerHandler() *CustomerHandler { return &CustomerHandler{} } // GetCustomers handles GET /customers // // @Summary Get all customers // @Description Get a list of all customers // @Tags customers // @Accept json // @Produce json // @Security BearerAuth // @Success 200 {object} utils.Response{data=[]dto.CustomerDto} // @Failure 401 {object} utils.Response{error=utils.ErrorInfo} // @Failure 500 {object} utils.Response{error=utils.ErrorInfo} // @Router /customers [get] func (h *CustomerHandler) GetCustomers(c *gin.Context) { // Get customers from the database customers, err := models.GetAllCustomers(c.Request.Context()) if err != nil { utils.InternalErrorResponse(c, "Error retrieving customers: "+err.Error()) return } // Convert to DTOs customerDTOs := make([]dto.CustomerDto, len(customers)) for i, customer := range customers { customerDTOs[i] = convertCustomerToDTO(&customer) } utils.SuccessResponse(c, http.StatusOK, customerDTOs) } // GetCustomerByID handles GET /customers/:id // // @Summary Get customer by ID // @Description Get a customer by its ID // @Tags customers // @Accept json // @Produce json // @Security BearerAuth // @Param id path string true "Customer ID" // @Success 200 {object} utils.Response{data=dto.CustomerDto} // @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 /customers/{id} [get] func (h *CustomerHandler) GetCustomerByID(c *gin.Context) { // Parse ID from URL idStr := c.Param("id") id, err := ulid.Parse(idStr) if err != nil { utils.BadRequestResponse(c, "Invalid customer ID format") return } // Get customer from the database customer, err := models.GetCustomerByID(c.Request.Context(), models.FromULID(id)) if err != nil { utils.InternalErrorResponse(c, "Error retrieving customer: "+err.Error()) return } if customer == nil { utils.NotFoundResponse(c, "Customer not found") return } // Convert to DTO customerDTO := convertCustomerToDTO(customer) utils.SuccessResponse(c, http.StatusOK, customerDTO) } // GetCustomersByCompanyID handles GET /customers/company/:companyId // // @Summary Get customers by company ID // @Description Get a list of customers for a specific company // @Tags customers // @Accept json // @Produce json // @Security BearerAuth // @Param companyId path int true "Company ID" // @Success 200 {object} utils.Response{data=[]dto.CustomerDto} // @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 /customers/company/{companyId} [get] func (h *CustomerHandler) GetCustomersByCompanyID(c *gin.Context) { // Parse company ID from URL companyIDStr := c.Param("companyId") companyID, err := parseCompanyID(companyIDStr) if err != nil { utils.BadRequestResponse(c, "Invalid company ID format") return } // Get customers from the database customers, err := models.GetCustomersByCompanyID(c.Request.Context(), companyID) if err != nil { utils.InternalErrorResponse(c, "Error retrieving customers: "+err.Error()) return } // Convert to DTOs customerDTOs := make([]dto.CustomerDto, len(customers)) for i, customer := range customers { customerDTOs[i] = convertCustomerToDTO(&customer) } utils.SuccessResponse(c, http.StatusOK, customerDTOs) } // CreateCustomer handles POST /customers // // @Summary Create a new customer // @Description Create a new customer // @Tags customers // @Accept json // @Produce json // @Security BearerAuth // @Param customer body dto.CustomerCreateDto true "Customer data" // @Success 201 {object} utils.Response{data=dto.CustomerDto} // @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 /customers [post] func (h *CustomerHandler) CreateCustomer(c *gin.Context) { // Parse request body var customerCreateDTO dto.CustomerCreateDto if err := c.ShouldBindJSON(&customerCreateDTO); err != nil { utils.BadRequestResponse(c, "Invalid request body: "+err.Error()) return } // Convert DTO to model customerCreate := convertCreateCustomerDTOToModel(customerCreateDTO) // Create customer in the database customer, err := models.CreateCustomer(c.Request.Context(), customerCreate) if err != nil { utils.InternalErrorResponse(c, "Error creating customer: "+err.Error()) return } // Convert to DTO customerDTO := convertCustomerToDTO(customer) utils.SuccessResponse(c, http.StatusCreated, customerDTO) } // UpdateCustomer handles PUT /customers/:id // // @Summary Update a customer // @Description Update an existing customer // @Tags customers // @Accept json // @Produce json // @Security BearerAuth // @Param id path string true "Customer ID" // @Param customer body dto.CustomerUpdateDto true "Customer data" // @Success 200 {object} utils.Response{data=dto.CustomerDto} // @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 /customers/{id} [put] func (h *CustomerHandler) UpdateCustomer(c *gin.Context) { // Parse ID from URL idStr := c.Param("id") id, err := ulid.Parse(idStr) if err != nil { utils.BadRequestResponse(c, "Invalid customer ID format") return } // Parse request body var customerUpdateDTO dto.CustomerUpdateDto if err := c.ShouldBindJSON(&customerUpdateDTO); err != nil { utils.BadRequestResponse(c, "Invalid request body: "+err.Error()) return } // Set ID from URL customerUpdateDTO.ID = id.String() // Convert DTO to model customerUpdate := convertUpdateCustomerDTOToModel(customerUpdateDTO) // Update customer in the database customer, err := models.UpdateCustomer(c.Request.Context(), customerUpdate) if err != nil { utils.InternalErrorResponse(c, "Error updating customer: "+err.Error()) return } if customer == nil { utils.NotFoundResponse(c, "Customer not found") return } // Convert to DTO customerDTO := convertCustomerToDTO(customer) utils.SuccessResponse(c, http.StatusOK, customerDTO) } // DeleteCustomer handles DELETE /customers/:id // // @Summary Delete a customer // @Description Delete a customer by its ID // @Tags customers // @Accept json // @Produce json // @Security BearerAuth // @Param id path string true "Customer 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 /customers/{id} [delete] func (h *CustomerHandler) DeleteCustomer(c *gin.Context) { // Parse ID from URL idStr := c.Param("id") id, err := ulid.Parse(idStr) if err != nil { utils.BadRequestResponse(c, "Invalid customer ID format") return } // Delete customer from the database err = models.DeleteCustomer(c.Request.Context(), models.FromULID(id)) if err != nil { utils.InternalErrorResponse(c, "Error deleting customer: "+err.Error()) return } utils.SuccessResponse(c, http.StatusNoContent, nil) } // Helper functions for DTO conversion func convertCustomerToDTO(customer *models.Customer) dto.CustomerDto { return dto.CustomerDto{ ID: customer.ID.String(), CreatedAt: customer.CreatedAt, UpdatedAt: customer.UpdatedAt, Name: customer.Name, CompanyID: customer.CompanyID, } } func convertCreateCustomerDTOToModel(dto dto.CustomerCreateDto) models.CustomerCreate { return models.CustomerCreate{ Name: dto.Name, CompanyID: dto.CompanyID, } } func convertUpdateCustomerDTOToModel(dto dto.CustomerUpdateDto) models.CustomerUpdate { id, _ := ulid.Parse(dto.ID) update := models.CustomerUpdate{ ID: models.FromULID(id), } if dto.Name != nil { update.Name = dto.Name } if dto.CompanyID != nil { update.CompanyID = dto.CompanyID } return update } // Helper function to parse company ID from string func parseCompanyID(idStr string) (int, error) { var id int _, err := fmt.Sscanf(idStr, "%d", &id) return id, err }