package controller import ( "errors" "net/http" "github.com/Caknoooo/go-gin-clean-starter/modules/menu/dto" "github.com/Caknoooo/go-gin-clean-starter/modules/menu/query" "github.com/Caknoooo/go-gin-clean-starter/modules/menu/service" "github.com/Caknoooo/go-gin-clean-starter/pkg/constants" "github.com/Caknoooo/go-gin-clean-starter/pkg/utils" "github.com/Caknoooo/go-pagination" "github.com/gin-gonic/gin" "github.com/samber/do" "gorm.io/gorm" ) type ( MenuController interface { CreateMenu(ctx *gin.Context) GetMenuByID(ctx *gin.Context) GetMenuByName(ctx *gin.Context) GetMenus(ctx *gin.Context) UpdateMenu(ctx *gin.Context) DeleteMenu(ctx *gin.Context) } menuController struct { menuService service.MenuService db *gorm.DB } ) // CreateMenu implements MenuController. func (m *menuController) CreateMenu(ctx *gin.Context) { var req dto.MenuCreateRequest if err := ctx.ShouldBind(&req); err != nil { res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_GET_DATA_FROM_BODY, err.Error(), nil) ctx.AbortWithStatusJSON(http.StatusBadRequest, res) return } in := dto.MenuResponse{ Name: req.Name, IconUrl: req.IconUrl, Url: req.Url, Sequence: req.Sequence, Mode: req.Mode, Status: req.Status, } if req.ParentID != nil { in.Parent = &dto.MenuParentResponse{ID: req.ParentID.String()} } res, err := m.menuService.Create(ctx.Request.Context(), in) if err != nil { resp := utils.BuildResponseFailed(dto.MESSAGE_FAILED_CREATE_MENU, err.Error(), nil) ctx.JSON(http.StatusBadRequest, resp) return } resp := utils.BuildResponseSuccess(dto.MESSAGE_SUCCESS_CREATE_MENU, res) ctx.JSON(http.StatusOK, resp) } // GetMenus implements MenuController. func (m *menuController) GetMenus(ctx *gin.Context) { _ = ctx.MustGet("client_id").(string) var filter = &query.MenuFilter{ // ClientID: clientId, Name: ctx.Query("name"), ParentID: ctx.Query("parent_id"), Includes: ctx.QueryArray("includes"), } filter.BindPagination(ctx) ctx.ShouldBindQuery(filter) menus, total, err := pagination.PaginatedQueryWithIncludable[query.M_Menu](m.db, filter) if err != nil { res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_GET_LIST_MENU, err.Error(), nil) ctx.JSON(http.StatusBadRequest, res) return } menuResponses := ToMenuResponses(menus) paginationResponse := pagination.CalculatePagination(filter.Pagination, total) response := pagination.NewPaginatedResponse(http.StatusOK, dto.MESSAGE_SUCCESS_GET_LIST_MENU, menuResponses, paginationResponse) ctx.JSON(http.StatusOK, response) } // GetMenuByID implements MenuController. func (m *menuController) GetMenuByID(ctx *gin.Context) { id := ctx.Param("id") if id == "" { res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_GET_MENU, "id is required", nil) ctx.JSON(http.StatusBadRequest, res) return } res, err := m.menuService.GetById(ctx.Request.Context(), id) if err != nil { resErr := utils.BuildResponseFailed(dto.MESSAGE_FAILED_GET_MENU, err.Error(), nil) ctx.JSON(http.StatusBadRequest, resErr) return } resp := utils.BuildResponseSuccess(dto.MESSAGE_SUCCESS_GET_MENU, res) ctx.JSON(http.StatusOK, resp) } // GetMenuByName implements MenuController. func (m *menuController) GetMenuByName(ctx *gin.Context) { name := ctx.Query("name") if name == "" { res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_GET_MENU, "name query is required", nil) ctx.JSON(http.StatusBadRequest, res) return } res, err := m.menuService.GetByName(ctx.Request.Context(), name) if err != nil { resErr := utils.BuildResponseFailed(dto.MESSAGE_FAILED_GET_MENU, err.Error(), nil) ctx.JSON(http.StatusBadRequest, resErr) return } resp := utils.BuildResponseSuccess(dto.MESSAGE_SUCCESS_GET_MENU, res) ctx.JSON(http.StatusOK, resp) } // UpdateMenu implements MenuController. func (m *menuController) UpdateMenu(ctx *gin.Context) { var req dto.MenuUpdateRequest if err := ctx.ShouldBind(&req); err != nil { res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_GET_DATA_FROM_BODY, err.Error(), nil) ctx.AbortWithStatusJSON(http.StatusBadRequest, res) return } menuId := ctx.Param("id") result, err := m.menuService.Update(ctx.Request.Context(), menuId, req) if err != nil { res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_UPDATE_MENU, err.Error(), nil) ctx.JSON(http.StatusBadRequest, res) return } resp := utils.BuildResponseSuccess(dto.MESSAGE_SUCCESS_UPDATE_MENU, result) ctx.JSON(http.StatusOK, resp) } // DeleteMenu implements MenuController. func (m *menuController) DeleteMenu(ctx *gin.Context) { id := ctx.Param("id") if id == "" { res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_DELETE_MENU, "id is required", nil) ctx.JSON(http.StatusBadRequest, res) return } if err := m.menuService.Delete(ctx.Request.Context(), id); err != nil { // if record not found, return 404 if errors.Is(err, gorm.ErrRecordNotFound) { ctx.JSON(http.StatusNotFound, gin.H{"error": "menu not found"}) return } res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_DELETE_MENU, err.Error(), nil) ctx.JSON(http.StatusBadRequest, res) return } resp := utils.BuildResponseSuccess(dto.MESSAGE_SUCCESS_DELETE_MENU, nil) ctx.JSON(http.StatusOK, resp) } func NewMenuController(i *do.Injector, ms service.MenuService) MenuController { db := do.MustInvokeNamed[*gorm.DB](i, constants.DB) return &menuController{ menuService: ms, db: db, } } // Fungsi transform dari entities ke DTO func ToMenuResponse(menu query.M_Menu) dto.MenuResponse { response := dto.MenuResponse{ ID: menu.ID.String(), Name: menu.Name, IconUrl: menu.IconUrl, Url: menu.Url, Sequence: menu.Sequence, Mode: menu.Mode, Status: menu.Status, } // Handle parent jika ada if menu.Parent != nil { response.Parent = &dto.MenuParentResponse{ ID: menu.Parent.ID.String(), Name: menu.Parent.Name, } } return response } func ToMenuResponses(menus []query.M_Menu) []dto.MenuResponse { responses := make([]dto.MenuResponse, len(menus)) for i, menu := range menus { responses[i] = ToMenuResponse(menu) } return responses }