package user import ( "enaklo-pos-be/internal/constants/role" "fmt" "net/http" "strconv" "strings" "time" "github.com/gin-gonic/gin" "github.com/go-playground/validator/v10" "github.com/xuri/excelize/v2" "enaklo-pos-be/internal/common/errors" "enaklo-pos-be/internal/entity" "enaklo-pos-be/internal/handlers/request" "enaklo-pos-be/internal/handlers/response" "enaklo-pos-be/internal/services" ) type Handler struct { service services.User } func (h *Handler) Route(group *gin.RouterGroup, jwt gin.HandlerFunc) { route := group.Group("/user") route.POST("/", jwt, h.Create) route.POST("/customer", jwt, h.CreateCustomer) route.POST("/customer-bulk-excel", jwt, h.CreateCustomersFromExcel) route.GET("/list", jwt, h.GetAll) route.GET("/customer/list", jwt, h.GetAllCustomer) route.GET("/:id", jwt, h.GetByID) route.PUT("/:id", jwt, h.Update) route.PUT("/customer/:id", jwt, h.UpdateCustomer) route.DELETE("/customer/:id", jwt, h.DeleteCustomer) route.DELETE("/:id", jwt, h.Delete) } func NewHandler(service services.User) *Handler { return &Handler{ service: service, } } // Create handles the creation of a new user. // @Summary Create a new user // @Description Create a new user based on the provided data. // @Accept json // @Produce json // @Param Authorization header string true "JWT token" // @Param req body request.User true "New user details" // @Success 200 {object} response.BaseResponse{data=response.User} "User created successfully" // @Failure 400 {object} response.BaseResponse{data=errors.Error} "Bad request" // @Failure 401 {object} response.BaseResponse{data=errors.Error} "Unauthorized" // @Router /api/v1/user [post] // @Tags User APIs func (h *Handler) Create(c *gin.Context) { ctx := request.GetMyContext(c) var req request.User if err := c.ShouldBindJSON(&req); err != nil { response.ErrorWrapper(c, errors.ErrorBadRequest) return } if !ctx.IsSuperAdmin() { req.PartnerID = ctx.GetPartnerID() if err := req.Validate(); err != nil { response.ErrorWrapper(c, errors.ErrorInvalidRequest) return } } if req.RoleID == role.Casheer && req.SiteID == nil { response.ErrorWrapper(c, errors.NewServiceException("site id is required for cashier")) return } res, err := h.service.Create(ctx, req.ToEntity(ctx, "")) if err != nil { response.ErrorWrapper(c, err) return } resp := response.User{ ID: res.ID, Name: res.Name, Email: res.Email, RoleID: int64(res.RoleID), PartnerID: res.PartnerID, Status: string(res.Status), } c.JSON(http.StatusOK, response.BaseResponse{ Success: true, Status: http.StatusOK, Data: resp, }) } func (h *Handler) CreateCustomer(c *gin.Context) { ctx := request.GetMyContext(c) var req request.User if err := c.ShouldBindJSON(&req); err != nil { response.ErrorWrapper(c, errors.ErrorBadRequest) return } res, err := h.service.Create(ctx, req.ToEntity(ctx, "CUSTOMER")) if err != nil { response.ErrorWrapper(c, err) return } resp := response.User{ ID: res.ID, Name: res.Name, Email: res.Email, RoleID: int64(res.RoleID), PartnerID: res.PartnerID, Status: string(res.Status), PhoneNumber: res.PhoneNumber, } c.JSON(http.StatusOK, response.BaseResponse{ Success: true, Status: http.StatusOK, Data: resp, }) } func (h *Handler) CreateCustomersFromExcel(c *gin.Context) { ctx := request.GetMyContext(c) // var req request.User var responseData []response.User file, err := c.FormFile("file") if err != nil { fmt.Println("err", err.Error()) response.ErrorWrapper(c, errors.ErrorBadRequest) return } f, err := file.Open() if err != nil { fmt.Println("err", err.Error()) response.ErrorWrapper(c, errors.ErrorBadRequest) return } defer f.Close() excel, err := excelize.OpenReader(f) if err != nil { fmt.Println("err", err.Error()) response.ErrorWrapper(c, errors.ErrorBadRequest) // c.JSON(http.StatusInternalServerError, gin.H{"success": false, "message": "Failed to read Excel file"}) return } // sheetName := excel.GetSheetName(1) // Assuming the data is in the first sheet rows, err := excel.GetRows("Sheet1") if err != nil { fmt.Println("err", err.Error()) response.ErrorWrapper(c, errors.ErrorBadRequest) // c.JSON(http.StatusInternalServerError, gin.H{"success": false, "message": "Failed to read rows from Excel"}) return } var phoneNumbers []string for _, row := range rows { if len(row) > 0 { phoneNumber := strings.TrimSpace(row[0]) if phoneNumber != "" { phoneNumbers = append(phoneNumbers, phoneNumber) } } } // c.JSON(http.StatusOK, response.BaseResponse{ // Success: true, // Status: http.StatusOK, // Data: phoneNumbers, // }) // return if len(phoneNumbers) == 0 { response.ErrorWrapper(c, errors.ErrorBadRequest) // c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "No phone numbers found in the Excel file"}) return } // var createdCustomers []CustomerResponse for _, phone := range phoneNumbers { req := request.User{ PhoneNumber: phone, Email: "", } res, err := h.service.Create(ctx, req.ToEntity(ctx, "CUSTOMER")) // res, err := h.service.Create(c, customer) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{ "success": false, "message": fmt.Sprintf("Failed to create customer for phone number %s: %v", phone, err), }) return } responseData = append(responseData, response.User{PhoneNumber: res.PhoneNumber}) } c.JSON(http.StatusOK, response.BaseResponse{ Success: true, Status: http.StatusOK, Data: responseData, }) } // Update handles the update of an existing user. // @Summary Update an existing user // @Description Update the details of an existing user based on the provided ID. // @Accept json // @Produce json // @Param Authorization header string true "JWT token" // @Param id path int64 true "User ID to update" // @Param req body request.User true "Updated user details" // @Success 200 {object} response.BaseResponse{data=response.User} "User updated successfully" // @Failure 400 {object} response.BaseResponse{data=errors.Error} "Bad request" // @Failure 401 {object} response.BaseResponse{data=errors.Error} "Unauthorized" // @Router /api/v1/user/{id} [put] // @Tags User APIs func (h *Handler) Update(c *gin.Context) { ctx := request.GetMyContext(c) id := c.Param("id") userID, err := strconv.ParseInt(id, 10, 64) if err != nil { response.ErrorWrapper(c, errors.ErrorBadRequest) return } var req request.User if err := c.ShouldBindJSON(&req); err != nil { response.ErrorWrapper(c, errors.ErrorBadRequest) return } validate := validator.New() if err := validate.Struct(req); err != nil { response.ErrorWrapper(c, err) return } updatedUser, err := h.service.Update(ctx, userID, req.ToEntity(ctx, "")) if err != nil { response.ErrorWrapper(c, err) return } c.JSON(http.StatusOK, response.BaseResponse{ Success: true, Status: http.StatusOK, Data: h.toUserResponse(updatedUser), }) } func (h *Handler) UpdateCustomer(c *gin.Context) { ctx := request.GetMyContext(c) id := c.Param("id") userID, err := strconv.ParseInt(id, 10, 64) if err != nil { response.ErrorWrapper(c, errors.ErrorBadRequest) return } var req request.User if err := c.ShouldBindJSON(&req); err != nil { response.ErrorWrapper(c, errors.ErrorBadRequest) return } updatedUser, err := h.service.Update(ctx, userID, req.ToEntity(ctx, "CUSTOMER")) if err != nil { response.ErrorWrapper(c, err) return } c.JSON(http.StatusOK, response.BaseResponse{ Success: true, Status: http.StatusOK, Data: h.toUserResponse(updatedUser), }) } // GetAll retrieves a list of users. // @Summary Get a list of users // @Description Get a paginated list of users based on query parameters. // @Accept json // @Produce json // @Param Authorization header string true "JWT token" // @Param Limit query int false "Number of items to retrieve (default 10)" // @Param Offset query int false "Offset for pagination (default 0)" // @Success 200 {object} response.BaseResponse{data=response.UserList} "List of users" // @Failure 400 {object} response.BaseResponse{data=errors.Error} "Bad request" // @Failure 401 {object} response.BaseResponse{data=errors.Error} "Unauthorized" // @Router /api/v1/user/list [get] // @Tags User APIs func (h *Handler) GetAll(c *gin.Context) { ctx := request.GetMyContext(c) var req request.UserParam if err := c.ShouldBindQuery(&req); err != nil { response.ErrorWrapper(c, err) return } users, total, err := h.service.GetAll(ctx, req.ToEntity(ctx)) if err != nil { response.ErrorWrapper(c, err) return } c.JSON(http.StatusOK, response.BaseResponse{ Success: true, Status: http.StatusOK, Data: h.toUserResponseList(users, int64(total), req), }) } func (h *Handler) GetAllCustomer(c *gin.Context) { ctx := request.GetMyContext(c) var req request.CustomerParam if err := c.ShouldBindQuery(&req); err != nil { response.ErrorWrapper(c, err) return } users, total, err := h.service.GetAllCustomer(ctx, req.ToEntity(ctx)) if err != nil { response.ErrorWrapper(c, err) return } c.JSON(http.StatusOK, response.BaseResponse{ Success: true, Status: http.StatusOK, Data: h.toCustomerResponseList(users, int64(total), req), }) } // GetByID retrieves details of a specific user by ID. // @Summary Get details of a user by ID // @Description Get details of a user based on the provided ID. // @Accept json // @Produce json // @Param Authorization header string true "JWT token" // @Param id path int64 true "User ID to retrieve" // @Success 200 {object} response.BaseResponse{data=response.User} "User details" // @Failure 400 {object} response.BaseResponse{data=errors.Error} "Bad request" // @Failure 401 {object} response.BaseResponse{data=errors.Error} "Unauthorized" // @Router /api/v1/user/{id} [get] // @Tags User APIs func (h *Handler) GetByID(c *gin.Context) { ctx := request.GetMyContext(c) id := c.Param("id") // Parse the ID into a uint userID, err := strconv.ParseInt(id, 10, 64) if err != nil { response.ErrorWrapper(c, errors.ErrorBadRequest) return } res, err := h.service.GetByID(ctx, userID) if err != nil { c.JSON(http.StatusInternalServerError, response.BaseResponse{ Success: false, Status: http.StatusInternalServerError, Message: err.Error(), Data: nil, }) return } c.JSON(http.StatusOK, response.BaseResponse{ Success: true, Status: http.StatusOK, Data: h.toUserResponse(res), }) } // Delete handles the deletion of a user by ID. // @Summary Delete a user by ID // @Description Delete a user based on the provided ID. // @Accept json // @Produce json // @Param Authorization header string true "JWT token" // @Param id path int64 true "User ID to delete" // @Success 200 {object} response.BaseResponse "User deleted successfully" // @Failure 400 {object} response.BaseResponse{data=errors.Error} "Bad request" // @Failure 401 {object} response.BaseResponse{data=errors.Error} "Unauthorized" // @Router /api/v1/user/{id} [delete] // @Tags User APIs func (h *Handler) Delete(c *gin.Context) { ctx := request.GetMyContext(c) id := c.Param("id") // Parse the ID into a uint userID, err := strconv.ParseInt(id, 10, 64) if err != nil { response.ErrorWrapper(c, errors.ErrorBadRequest) return } err = h.service.Delete(ctx, userID) if err != nil { c.JSON(http.StatusInternalServerError, response.BaseResponse{ Success: false, Status: http.StatusInternalServerError, Message: err.Error(), Data: nil, }) return } c.JSON(http.StatusOK, response.BaseResponse{ Success: true, Status: http.StatusOK, Data: nil, }) } func (h *Handler) DeleteCustomer(c *gin.Context) { ctx := request.GetMyContext(c) id := c.Param("id") // Parse the ID into an int64 (or whatever type is used for customer ID) customerID, err := strconv.ParseInt(id, 10, 64) if err != nil { // Handle invalid ID format error response.ErrorWrapper(c, errors.ErrorBadRequest) return } // Call the service to delete the customer err = h.service.Delete(ctx, customerID) if err != nil { // If there was an error deleting, return a 500 Internal Server Error c.JSON(http.StatusInternalServerError, response.BaseResponse{ Success: false, Status: http.StatusInternalServerError, Message: err.Error(), Data: nil, }) return } // Return a success response after deletion c.JSON(http.StatusOK, response.BaseResponse{ Success: true, Status: http.StatusOK, Message: "Customer deleted successfully", // Added success message Data: nil, }) } func (h *Handler) toUserResponse(resp *entity.User) response.User { return response.User{ ID: resp.ID, Name: resp.Name, Email: resp.Email, PhoneNumber: resp.PhoneNumber, NIK: resp.NIK, Status: string(resp.Status), RoleID: int64(resp.RoleID), RoleName: resp.RoleName, PartnerID: resp.PartnerID, PartnerName: resp.PartnerName, CreatedAt: resp.CreatedAt.Format(time.RFC3339), UpdatedAt: resp.CreatedAt.Format(time.RFC3339), SiteID: resp.SiteID, } } func (h *Handler) toUserResponseList(resp []*entity.User, total int64, req request.UserParam) response.UserList { var users []response.User for _, b := range resp { users = append(users, h.toUserResponse(b)) } return response.UserList{ Users: users, Total: total, Limit: req.Limit, Offset: req.Offset, } } func (h *Handler) toCustomerResponse(resp *entity.Customer) response.Customer { return response.Customer{ ID: resp.ID, Name: resp.Name, Email: resp.Email, PhoneNumber: resp.PhoneNumber, Status: string(resp.Status), RoleID: int64(resp.RoleID), RoleName: resp.RoleName, PartnerID: resp.PartnerID, PartnerName: resp.PartnerName, CreatedAt: resp.CreatedAt.Format(time.RFC3339), UpdatedAt: resp.CreatedAt.Format(time.RFC3339), } } func (h *Handler) toCustomerResponseList(resp []*entity.Customer, total int64, req request.CustomerParam) response.CustomerList { var users []response.Customer for _, b := range resp { users = append(users, h.toCustomerResponse(b)) } return response.CustomerList{ Users: users, Total: total, Limit: req.Limit, Offset: req.Offset, } }