This commit is contained in:
Aditya Siregar
2025-07-30 23:18:20 +07:00
parent 4a921df55d
commit a759e0f57c
57 changed files with 3633 additions and 190 deletions
+3 -1
View File
@@ -1,6 +1,7 @@
package handler
import (
"apskel-pos-be/internal/util"
"net/http"
"strings"
@@ -51,7 +52,8 @@ func (h *AuthHandler) Login(c *gin.Context) {
}
logger.FromContext(c.Request.Context()).Infof("AuthHandler::Login -> Successfully logged in user = %s", loginResponse.User.Email)
c.JSON(http.StatusOK, loginResponse)
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(loginResponse), "AuthHandler::Login")
}
func (h *AuthHandler) Logout(c *gin.Context) {
+385
View File
@@ -0,0 +1,385 @@
package handler
import (
"apskel-pos-be/internal/contract"
"apskel-pos-be/internal/service"
"net/http"
"strconv"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
)
type TableHandler struct {
tableService *service.TableService
}
func NewTableHandler(tableService *service.TableService) *TableHandler {
return &TableHandler{
tableService: tableService,
}
}
// CreateTable godoc
// @Summary Create a new table
// @Description Create a new table for the organization
// @Tags tables
// @Accept json
// @Produce json
// @Param table body contract.CreateTableRequest true "Table data"
// @Success 201 {object} contract.TableResponse
// @Failure 400 {object} contract.ResponseError
// @Failure 401 {object} contract.ResponseError
// @Failure 500 {object} contract.ResponseError
// @Router /tables [post]
func (h *TableHandler) Create(c *gin.Context) {
var req contract.CreateTableRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, contract.ResponseError{
Error: "Invalid request body",
Message: err.Error(),
})
return
}
organizationID := c.GetString("organization_id")
orgID, err := uuid.Parse(organizationID)
if err != nil {
c.JSON(http.StatusBadRequest, contract.ResponseError{
Error: "Invalid organization ID",
Message: err.Error(),
})
return
}
response, err := h.tableService.Create(c.Request.Context(), req, orgID)
if err != nil {
c.JSON(http.StatusInternalServerError, contract.ResponseError{
Error: "Failed to create table",
Message: err.Error(),
})
return
}
c.JSON(http.StatusCreated, response)
}
// GetTable godoc
// @Summary Get table by ID
// @Description Get table details by ID
// @Tags tables
// @Produce json
// @Param id path string true "Table ID"
// @Success 200 {object} contract.TableResponse
// @Failure 400 {object} contract.ResponseError
// @Failure 404 {object} contract.ResponseError
// @Failure 500 {object} contract.ResponseError
// @Router /tables/{id} [get]
func (h *TableHandler) GetByID(c *gin.Context) {
id := c.Param("id")
tableID, err := uuid.Parse(id)
if err != nil {
c.JSON(http.StatusBadRequest, contract.ResponseError{
Error: "Invalid table ID",
Message: err.Error(),
})
return
}
response, err := h.tableService.GetByID(c.Request.Context(), tableID)
if err != nil {
c.JSON(http.StatusNotFound, contract.ResponseError{
Error: "Table not found",
Message: err.Error(),
})
return
}
c.JSON(http.StatusOK, response)
}
// UpdateTable godoc
// @Summary Update table
// @Description Update table details
// @Tags tables
// @Accept json
// @Produce json
// @Param id path string true "Table ID"
// @Param table body contract.UpdateTableRequest true "Table update data"
// @Success 200 {object} contract.TableResponse
// @Failure 400 {object} contract.ResponseError
// @Failure 404 {object} contract.ResponseError
// @Failure 500 {object} contract.ResponseError
// @Router /tables/{id} [put]
func (h *TableHandler) Update(c *gin.Context) {
id := c.Param("id")
tableID, err := uuid.Parse(id)
if err != nil {
c.JSON(http.StatusBadRequest, contract.ResponseError{
Error: "Invalid table ID",
Message: err.Error(),
})
return
}
var req contract.UpdateTableRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, contract.ResponseError{
Error: "Invalid request body",
Message: err.Error(),
})
return
}
response, err := h.tableService.Update(c.Request.Context(), tableID, req)
if err != nil {
c.JSON(http.StatusInternalServerError, contract.ResponseError{
Error: "Failed to update table",
Message: err.Error(),
})
return
}
c.JSON(http.StatusOK, response)
}
// DeleteTable godoc
// @Summary Delete table
// @Description Delete table by ID
// @Tags tables
// @Produce json
// @Param id path string true "Table ID"
// @Success 204 "No Content"
// @Failure 400 {object} contract.ResponseError
// @Failure 404 {object} contract.ResponseError
// @Failure 500 {object} contract.ResponseError
// @Router /tables/{id} [delete]
func (h *TableHandler) Delete(c *gin.Context) {
id := c.Param("id")
tableID, err := uuid.Parse(id)
if err != nil {
c.JSON(http.StatusBadRequest, contract.ResponseError{
Error: "Invalid table ID",
Message: err.Error(),
})
return
}
err = h.tableService.Delete(c.Request.Context(), tableID)
if err != nil {
c.JSON(http.StatusInternalServerError, contract.ResponseError{
Error: "Failed to delete table",
Message: err.Error(),
})
return
}
c.Status(http.StatusNoContent)
}
// ListTables godoc
// @Summary List tables
// @Description Get paginated list of tables
// @Tags tables
// @Produce json
// @Param organization_id query string false "Organization ID"
// @Param outlet_id query string false "Outlet ID"
// @Param status query string false "Table status"
// @Param is_active query string false "Is active"
// @Param search query string false "Search term"
// @Param page query int false "Page number" default(1)
// @Param limit query int false "Page size" default(10)
// @Success 200 {object} contract.ListTablesResponse
// @Failure 400 {object} contract.ResponseError
// @Failure 500 {object} contract.ResponseError
// @Router /tables [get]
func (h *TableHandler) List(c *gin.Context) {
query := contract.ListTablesQuery{
OrganizationID: c.Query("organization_id"),
OutletID: c.Query("outlet_id"),
Status: c.Query("status"),
IsActive: c.Query("is_active"),
Search: c.Query("search"),
Page: 1,
Limit: 10,
}
if pageStr := c.Query("page"); pageStr != "" {
if page, err := strconv.Atoi(pageStr); err == nil && page > 0 {
query.Page = page
}
}
if limitStr := c.Query("limit"); limitStr != "" {
if limit, err := strconv.Atoi(limitStr); err == nil && limit > 0 && limit <= 100 {
query.Limit = limit
}
}
response, err := h.tableService.List(c.Request.Context(), query)
if err != nil {
c.JSON(http.StatusInternalServerError, contract.ResponseError{
Error: "Failed to list tables",
Message: err.Error(),
})
return
}
c.JSON(http.StatusOK, response)
}
// OccupyTable godoc
// @Summary Occupy table
// @Description Occupy a table with an order
// @Tags tables
// @Accept json
// @Produce json
// @Param id path string true "Table ID"
// @Param request body contract.OccupyTableRequest true "Occupy table data"
// @Success 200 {object} contract.TableResponse
// @Failure 400 {object} contract.ResponseError
// @Failure 404 {object} contract.ResponseError
// @Failure 500 {object} contract.ResponseError
// @Router /tables/{id}/occupy [post]
func (h *TableHandler) OccupyTable(c *gin.Context) {
id := c.Param("id")
tableID, err := uuid.Parse(id)
if err != nil {
c.JSON(http.StatusBadRequest, contract.ResponseError{
Error: "Invalid table ID",
Message: err.Error(),
})
return
}
var req contract.OccupyTableRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, contract.ResponseError{
Error: "Invalid request body",
Message: err.Error(),
})
return
}
response, err := h.tableService.OccupyTable(c.Request.Context(), tableID, req)
if err != nil {
c.JSON(http.StatusInternalServerError, contract.ResponseError{
Error: "Failed to occupy table",
Message: err.Error(),
})
return
}
c.JSON(http.StatusOK, response)
}
// ReleaseTable godoc
// @Summary Release table
// @Description Release a table and record payment amount
// @Tags tables
// @Accept json
// @Produce json
// @Param id path string true "Table ID"
// @Param request body contract.ReleaseTableRequest true "Release table data"
// @Success 200 {object} contract.TableResponse
// @Failure 400 {object} contract.ResponseError
// @Failure 404 {object} contract.ResponseError
// @Failure 500 {object} contract.ResponseError
// @Router /tables/{id}/release [post]
func (h *TableHandler) ReleaseTable(c *gin.Context) {
id := c.Param("id")
tableID, err := uuid.Parse(id)
if err != nil {
c.JSON(http.StatusBadRequest, contract.ResponseError{
Error: "Invalid table ID",
Message: err.Error(),
})
return
}
var req contract.ReleaseTableRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, contract.ResponseError{
Error: "Invalid request body",
Message: err.Error(),
})
return
}
response, err := h.tableService.ReleaseTable(c.Request.Context(), tableID, req)
if err != nil {
c.JSON(http.StatusInternalServerError, contract.ResponseError{
Error: "Failed to release table",
Message: err.Error(),
})
return
}
c.JSON(http.StatusOK, response)
}
// GetAvailableTables godoc
// @Summary Get available tables
// @Description Get list of available tables for an outlet
// @Tags tables
// @Produce json
// @Param outlet_id path string true "Outlet ID"
// @Success 200 {array} contract.TableResponse
// @Failure 400 {object} contract.ResponseError
// @Failure 500 {object} contract.ResponseError
// @Router /outlets/{outlet_id}/tables/available [get]
func (h *TableHandler) GetAvailableTables(c *gin.Context) {
outletIDStr := c.Param("outlet_id")
outletID, err := uuid.Parse(outletIDStr)
if err != nil {
c.JSON(http.StatusBadRequest, contract.ResponseError{
Error: "Invalid outlet ID",
Message: err.Error(),
})
return
}
tables, err := h.tableService.GetAvailableTables(c.Request.Context(), outletID)
if err != nil {
c.JSON(http.StatusInternalServerError, contract.ResponseError{
Error: "Failed to get available tables",
Message: err.Error(),
})
return
}
c.JSON(http.StatusOK, tables)
}
// GetOccupiedTables godoc
// @Summary Get occupied tables
// @Description Get list of occupied tables for an outlet
// @Tags tables
// @Produce json
// @Param outlet_id path string true "Outlet ID"
// @Success 200 {array} contract.TableResponse
// @Failure 400 {object} contract.ResponseError
// @Failure 500 {object} contract.ResponseError
// @Router /outlets/{outlet_id}/tables/occupied [get]
func (h *TableHandler) GetOccupiedTables(c *gin.Context) {
outletIDStr := c.Param("outlet_id")
outletID, err := uuid.Parse(outletIDStr)
if err != nil {
c.JSON(http.StatusBadRequest, contract.ResponseError{
Error: "Invalid outlet ID",
Message: err.Error(),
})
return
}
tables, err := h.tableService.GetOccupiedTables(c.Request.Context(), outletID)
if err != nil {
c.JSON(http.StatusInternalServerError, contract.ResponseError{
Error: "Failed to get occupied tables",
Message: err.Error(),
})
return
}
c.JSON(http.StatusOK, tables)
}
+44 -2
View File
@@ -8,6 +8,7 @@ import (
"apskel-pos-be/internal/contract"
"apskel-pos-be/internal/logger"
"apskel-pos-be/internal/transformer"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
)
@@ -288,8 +289,49 @@ func (h *UserHandler) DeactivateUser(c *gin.Context) {
return
}
logger.FromContext(c).Info("UserHandler::DeactivateUser -> Successfully deactivated user")
c.JSON(http.StatusOK, transformer.CreateSuccessResponse("User deactivated successfully", nil))
logger.FromContext(c).Infof("UserHandler::DeactivateUser -> Successfully deactivated user with ID: %s", userID.String())
c.JSON(http.StatusOK, gin.H{"message": "User deactivated successfully"})
}
func (h *UserHandler) UpdateUserOutlet(c *gin.Context) {
userIDStr := c.Param("id")
userID, err := uuid.Parse(userIDStr)
if err != nil {
logger.FromContext(c).WithError(err).Error("UserHandler::UpdateUserOutlet -> Invalid user ID")
h.sendValidationErrorResponse(c, "Invalid user ID", constants.MalformedFieldErrorCode)
return
}
validationError, validationErrorCode := h.userValidator.ValidateUserID(userID)
if validationError != nil {
logger.FromContext(c).WithError(validationError).Error("UserHandler::UpdateUserOutlet -> user ID validation failed")
h.sendValidationErrorResponse(c, validationError.Error(), validationErrorCode)
return
}
var req contract.UpdateUserOutletRequest
if err := c.ShouldBindJSON(&req); err != nil {
logger.FromContext(c).WithError(err).Error("UserHandler::UpdateUserOutlet -> request binding failed")
h.sendValidationErrorResponse(c, "Invalid request body", constants.MissingFieldErrorCode)
return
}
validationError, validationErrorCode = h.userValidator.ValidateUpdateUserOutletRequest(&req)
if validationError != nil {
logger.FromContext(c).WithError(validationError).Error("UserHandler::UpdateUserOutlet -> request validation failed")
h.sendValidationErrorResponse(c, validationError.Error(), validationErrorCode)
return
}
userResponse, err := h.userService.UpdateUserOutlet(c.Request.Context(), userID, &req)
if err != nil {
logger.FromContext(c).WithError(err).Error("UserHandler::UpdateUserOutlet -> Failed to update user outlet from service")
h.sendErrorResponse(c, err.Error(), http.StatusInternalServerError)
return
}
logger.FromContext(c).Infof("UserHandler::UpdateUserOutlet -> Successfully updated user outlet = %+v", userResponse)
c.JSON(http.StatusOK, userResponse)
}
func (h *UserHandler) sendErrorResponse(c *gin.Context, message string, statusCode int) {
+1
View File
@@ -16,4 +16,5 @@ type UserService interface {
ChangePassword(ctx context.Context, userID uuid.UUID, req *contract.ChangePasswordRequest) error
ActivateUser(ctx context.Context, userID uuid.UUID) error
DeactivateUser(ctx context.Context, userID uuid.UUID) error
UpdateUserOutlet(ctx context.Context, userID uuid.UUID, req *contract.UpdateUserOutletRequest) (*contract.UserResponse, error)
}
+2
View File
@@ -2,6 +2,7 @@ package handler
import (
"apskel-pos-be/internal/contract"
"github.com/google/uuid"
)
@@ -11,4 +12,5 @@ type UserValidator interface {
ValidateListUsersRequest(req *contract.ListUsersRequest) (error, string)
ValidateChangePasswordRequest(req *contract.ChangePasswordRequest) (error, string)
ValidateUserID(userID uuid.UUID) (error, string)
ValidateUpdateUserOutletRequest(req *contract.UpdateUserOutletRequest) (error, string)
}