fix: company filter

This commit is contained in:
Samuel Tariku 2025-07-02 19:41:29 +03:00
parent c4328dedf0
commit c2a9de6671
16 changed files with 431 additions and 36 deletions

View File

@ -285,7 +285,9 @@ CREATE TABLE companies (
id BIGSERIAL PRIMARY KEY,
name TEXT NOT NULL,
admin_id BIGINT NOT NULL,
wallet_id BIGINT NOT NULL
wallet_id BIGINT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE leagues (
id BIGINT PRIMARY KEY,

View File

@ -87,7 +87,8 @@ SET name = COALESCE(sqlc.narg(name), name),
branch_manager_id = COALESCE(sqlc.narg(branch_manager_id), branch_manager_id),
company_id = COALESCE(sqlc.narg(company_id), company_id),
is_self_owned = COALESCE(sqlc.narg(is_self_owned), is_self_owned),
is_active = COALESCE(sqlc.narg(is_active), is_active)
is_active = COALESCE(sqlc.narg(is_active), is_active),
updated_at = CURRENT_TIMESTAMP
WHERE id = $1
RETURNING *;
-- name: DeleteBranch :exec

View File

@ -8,7 +8,22 @@ VALUES ($1, $2, $3)
RETURNING *;
-- name: GetAllCompanies :many
SELECT *
FROM companies_details;
FROM companies_details
WHERE (
name ILIKE '%' || sqlc.narg('query') || '%'
OR admin_first_name ILIKE '%' || sqlc.narg('query') || '%'
OR admin_last_name ILIKE '%' || sqlc.narg('query') || '%'
OR admin_phone_number ILIKE '%' || sqlc.narg('query') || '%'
OR sqlc.narg('query') IS NULL
)
AND (
created_at > sqlc.narg('created_before')
OR sqlc.narg('created_before') IS NULL
)
AND (
created_at < sqlc.narg('created_after')
OR sqlc.narg('created_after') IS NULL
);
-- name: GetCompanyByID :one
SELECT *
FROM companies_details
@ -20,7 +35,8 @@ WHERE name ILIKE '%' || $1 || '%';
-- name: UpdateCompany :one
UPDATE companies
SET name = COALESCE(sqlc.narg(name), name),
admin_id = COALESCE(sqlc.narg(admin_id), admin_id)
admin_id = COALESCE(sqlc.narg(admin_id), admin_id),
updated_at = CURRENT_TIMESTAMP
WHERE id = $1
RETURNING *;
-- name: DeleteCompany :exec

View File

@ -490,7 +490,8 @@ SET name = COALESCE($2, name),
branch_manager_id = COALESCE($4, branch_manager_id),
company_id = COALESCE($5, company_id),
is_self_owned = COALESCE($6, is_self_owned),
is_active = COALESCE($7, is_active)
is_active = COALESCE($7, is_active),
updated_at = CURRENT_TIMESTAMP
WHERE id = $1
RETURNING id, name, location, is_active, wallet_id, branch_manager_id, company_id, is_self_owned, created_at, updated_at
`

View File

@ -18,7 +18,7 @@ INSERT INTO companies (
wallet_id
)
VALUES ($1, $2, $3)
RETURNING id, name, admin_id, wallet_id
RETURNING id, name, admin_id, wallet_id, created_at, updated_at
`
type CreateCompanyParams struct {
@ -35,6 +35,8 @@ func (q *Queries) CreateCompany(ctx context.Context, arg CreateCompanyParams) (C
&i.Name,
&i.AdminID,
&i.WalletID,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}
@ -50,12 +52,33 @@ func (q *Queries) DeleteCompany(ctx context.Context, id int64) error {
}
const GetAllCompanies = `-- name: GetAllCompanies :many
SELECT id, name, admin_id, wallet_id, balance, is_active, admin_first_name, admin_last_name, admin_phone_number
SELECT id, name, admin_id, wallet_id, created_at, updated_at, balance, is_active, admin_first_name, admin_last_name, admin_phone_number
FROM companies_details
WHERE (
name ILIKE '%' || $1 || '%'
OR admin_first_name ILIKE '%' || $1 || '%'
OR admin_last_name ILIKE '%' || $1 || '%'
OR admin_phone_number ILIKE '%' || $1 || '%'
OR $1 IS NULL
)
AND (
created_at > $2
OR $2 IS NULL
)
AND (
created_at < $3
OR $3 IS NULL
)
`
func (q *Queries) GetAllCompanies(ctx context.Context) ([]CompaniesDetail, error) {
rows, err := q.db.Query(ctx, GetAllCompanies)
type GetAllCompaniesParams struct {
Query pgtype.Text `json:"query"`
CreatedBefore pgtype.Timestamp `json:"created_before"`
CreatedAfter pgtype.Timestamp `json:"created_after"`
}
func (q *Queries) GetAllCompanies(ctx context.Context, arg GetAllCompaniesParams) ([]CompaniesDetail, error) {
rows, err := q.db.Query(ctx, GetAllCompanies, arg.Query, arg.CreatedBefore, arg.CreatedAfter)
if err != nil {
return nil, err
}
@ -68,6 +91,8 @@ func (q *Queries) GetAllCompanies(ctx context.Context) ([]CompaniesDetail, error
&i.Name,
&i.AdminID,
&i.WalletID,
&i.CreatedAt,
&i.UpdatedAt,
&i.Balance,
&i.IsActive,
&i.AdminFirstName,
@ -85,7 +110,7 @@ func (q *Queries) GetAllCompanies(ctx context.Context) ([]CompaniesDetail, error
}
const GetCompanyByID = `-- name: GetCompanyByID :one
SELECT id, name, admin_id, wallet_id, balance, is_active, admin_first_name, admin_last_name, admin_phone_number
SELECT id, name, admin_id, wallet_id, created_at, updated_at, balance, is_active, admin_first_name, admin_last_name, admin_phone_number
FROM companies_details
WHERE id = $1
`
@ -98,6 +123,8 @@ func (q *Queries) GetCompanyByID(ctx context.Context, id int64) (CompaniesDetail
&i.Name,
&i.AdminID,
&i.WalletID,
&i.CreatedAt,
&i.UpdatedAt,
&i.Balance,
&i.IsActive,
&i.AdminFirstName,
@ -108,7 +135,7 @@ func (q *Queries) GetCompanyByID(ctx context.Context, id int64) (CompaniesDetail
}
const SearchCompanyByName = `-- name: SearchCompanyByName :many
SELECT id, name, admin_id, wallet_id, balance, is_active, admin_first_name, admin_last_name, admin_phone_number
SELECT id, name, admin_id, wallet_id, created_at, updated_at, balance, is_active, admin_first_name, admin_last_name, admin_phone_number
FROM companies_details
WHERE name ILIKE '%' || $1 || '%'
`
@ -127,6 +154,8 @@ func (q *Queries) SearchCompanyByName(ctx context.Context, dollar_1 pgtype.Text)
&i.Name,
&i.AdminID,
&i.WalletID,
&i.CreatedAt,
&i.UpdatedAt,
&i.Balance,
&i.IsActive,
&i.AdminFirstName,
@ -146,9 +175,10 @@ func (q *Queries) SearchCompanyByName(ctx context.Context, dollar_1 pgtype.Text)
const UpdateCompany = `-- name: UpdateCompany :one
UPDATE companies
SET name = COALESCE($2, name),
admin_id = COALESCE($3, admin_id)
admin_id = COALESCE($3, admin_id),
updated_at = CURRENT_TIMESTAMP
WHERE id = $1
RETURNING id, name, admin_id, wallet_id
RETURNING id, name, admin_id, wallet_id, created_at, updated_at
`
type UpdateCompanyParams struct {
@ -165,6 +195,8 @@ func (q *Queries) UpdateCompany(ctx context.Context, arg UpdateCompanyParams) (C
&i.Name,
&i.AdminID,
&i.WalletID,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}

View File

@ -183,6 +183,8 @@ type CompaniesDetail struct {
Name string `json:"name"`
AdminID int64 `json:"admin_id"`
WalletID int64 `json:"wallet_id"`
CreatedAt pgtype.Timestamp `json:"created_at"`
UpdatedAt pgtype.Timestamp `json:"updated_at"`
Balance int64 `json:"balance"`
IsActive bool `json:"is_active"`
AdminFirstName string `json:"admin_first_name"`
@ -195,6 +197,8 @@ type Company struct {
Name string `json:"name"`
AdminID int64 `json:"admin_id"`
WalletID int64 `json:"wallet_id"`
CreatedAt pgtype.Timestamp `json:"created_at"`
UpdatedAt pgtype.Timestamp `json:"updated_at"`
}
type CustomerWallet struct {

View File

@ -253,9 +253,16 @@ WHERE wallet_id = $1
LIMIT 1
`
func (q *Queries) GetCompanyByWalletID(ctx context.Context, walletID int64) (Company, error) {
type GetCompanyByWalletIDRow struct {
ID int64 `json:"id"`
Name string `json:"name"`
AdminID int64 `json:"admin_id"`
WalletID int64 `json:"wallet_id"`
}
func (q *Queries) GetCompanyByWalletID(ctx context.Context, walletID int64) (GetCompanyByWalletIDRow, error) {
row := q.db.QueryRow(ctx, GetCompanyByWalletID, walletID)
var i Company
var i GetCompanyByWalletIDRow
err := row.Scan(
&i.ID,
&i.Name,

View File

@ -10,6 +10,13 @@ type Company struct {
WalletID int64
}
type CompanyFilter struct {
IsActive ValidBool
Query ValidString
CreatedBefore ValidTime
CreatedAfter ValidTime
}
type GetCompany struct {
ID int64
Name string

View File

@ -400,7 +400,7 @@ func (s *Store) GetBranchDetails(ctx context.Context, filter domain.ReportFilter
// In internal/repository/branch.go
func (s *Store) GetAllCompaniesBranch(ctx context.Context) ([]domain.Company, error) {
dbCompanies, err := s.queries.GetAllCompanies(ctx)
dbCompanies, err := s.queries.GetAllCompanies(ctx, dbgen.GetAllCompaniesParams{})
if err != nil {
return nil, fmt.Errorf("failed to get all companies: %w", err)
}

View File

@ -70,8 +70,21 @@ func (s *Store) CreateCompany(ctx context.Context, company domain.CreateCompany)
return convertDBCompany(dbCompany), nil
}
func (s *Store) GetAllCompanies(ctx context.Context) ([]domain.GetCompany, error) {
dbCompanies, err := s.queries.GetAllCompanies(ctx)
func (s *Store) GetAllCompanies(ctx context.Context, filter domain.CompanyFilter) ([]domain.GetCompany, error) {
dbCompanies, err := s.queries.GetAllCompanies(ctx, dbgen.GetAllCompaniesParams{
Query: pgtype.Text{
String: filter.Query.Value,
Valid: filter.Query.Valid,
},
CreatedBefore: pgtype.Timestamp{
Time: filter.CreatedBefore.Value,
Valid: filter.CreatedBefore.Valid,
},
CreatedAfter: pgtype.Timestamp{
Time: filter.CreatedAfter.Value,
Valid: filter.CreatedAfter.Valid,
},
})
if err != nil {
return nil, err
}

View File

@ -108,7 +108,7 @@ func (r *Repository) ListNotifications(ctx context.Context, recipientID int64, l
return nil, err
}
var result []domain.Notification
var result []domain.Notification = make([]domain.Notification, 0, len(dbNotifications))
for _, dbNotif := range dbNotifications {
domainNotif := r.mapDBToDomain(&dbNotif)
result = append(result, *domainNotif)

View File

@ -8,7 +8,7 @@ import (
type CompanyStore interface {
CreateCompany(ctx context.Context, company domain.CreateCompany) (domain.Company, error)
GetAllCompanies(ctx context.Context) ([]domain.GetCompany, error)
GetAllCompanies(ctx context.Context, filter domain.CompanyFilter) ([]domain.GetCompany, error)
SearchCompanyByName(ctx context.Context, name string) ([]domain.GetCompany, error)
GetCompanyByID(ctx context.Context, id int64) (domain.GetCompany, error)
UpdateCompany(ctx context.Context, company domain.UpdateCompany) (domain.Company, error)

View File

@ -19,8 +19,8 @@ func NewService(companyStore CompanyStore) *Service {
func (s *Service) CreateCompany(ctx context.Context, company domain.CreateCompany) (domain.Company, error) {
return s.companyStore.CreateCompany(ctx, company)
}
func (s *Service) GetAllCompanies(ctx context.Context) ([]domain.GetCompany, error) {
return s.companyStore.GetAllCompanies(ctx)
func (s *Service) GetAllCompanies(ctx context.Context, filter domain.CompanyFilter) ([]domain.GetCompany, error) {
return s.companyStore.GetAllCompanies(ctx, filter)
}
func (s *Service) GetCompanyByID(ctx context.Context, id int64) (domain.GetCompany, error) {

View File

@ -2,6 +2,7 @@ package handlers
import (
"strconv"
"time"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response"
@ -137,8 +138,45 @@ func (h *Handler) CreateCompany(c *fiber.Ctx) error {
// @Failure 500 {object} response.APIResponse
// @Router /company [get]
func (h *Handler) GetAllCompanies(c *fiber.Ctx) error {
searchQuery := c.Query("query")
searchString := domain.ValidString{
Value: searchQuery,
Valid: searchQuery != "",
}
companies, err := h.companySvc.GetAllCompanies(c.Context())
createdBeforeQuery := c.Query("created_before")
var createdBefore domain.ValidTime
if createdBeforeQuery != "" {
createdBeforeParsed, err := time.Parse(time.RFC3339, createdBeforeQuery)
if err != nil {
h.logger.Error("invalid start_time format", "error", err)
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid start_time format", nil, nil)
}
createdBefore = domain.ValidTime{
Value: createdBeforeParsed,
Valid: true,
}
}
createdAfterQuery := c.Query("created_after")
var createdAfter domain.ValidTime
if createdAfterQuery != "" {
createdAfterParsed, err := time.Parse(time.RFC3339, createdAfterQuery)
if err != nil {
h.logger.Error("invalid start_time format", "error", err)
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid start_time format", nil, nil)
}
createdAfter = domain.ValidTime{
Value: createdAfterParsed,
Valid: true,
}
}
companies, err := h.companySvc.GetAllCompanies(c.Context(), domain.CompanyFilter{
Query: searchString,
CreatedBefore: createdBefore,
CreatedAfter: createdAfter,
})
if err != nil {
h.logger.Error("Failed to get companies", "error", err)
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to get companies", err, nil)

View File

@ -0,0 +1,270 @@
package handlers
import (
"strconv"
"time"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/authentication"
"github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response"
"github.com/gofiber/fiber/v2"
)
type CustomersRes struct {
ID int64 `json:"id"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
Email string `json:"email"`
PhoneNumber string `json:"phone_number"`
Role domain.Role `json:"role"`
EmailVerified bool `json:"email_verified"`
PhoneVerified bool `json:"phone_verified"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
LastLogin time.Time `json:"last_login"`
SuspendedAt time.Time `json:"suspended_at"`
Suspended bool `json:"suspended"`
}
// GetAllCustomers godoc
// @Summary Get all Customers
// @Description Get all Customers
// @Tags customer
// @Accept json
// @Produce json
// @Param page query int false "Page number"
// @Param page_size query int false "Page size"
// @Success 200 {object} CustomersRes
// @Failure 400 {object} response.APIResponse
// @Failure 401 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /customer [get]
func (h *Handler) GetAllCustomers(c *fiber.Ctx) error {
role := c.Locals("role").(domain.Role)
companyId := c.Locals("company_id").(domain.ValidInt64)
// Checking to make sure that admin user has a company id in the token
if role != domain.RoleSuperAdmin && !companyId.Valid {
return fiber.NewError(fiber.StatusInternalServerError, "Cannot get company ID")
}
searchQuery := c.Query("query")
searchString := domain.ValidString{
Value: searchQuery,
Valid: searchQuery != "",
}
createdBeforeQuery := c.Query("created_before")
var createdBefore domain.ValidTime
if createdBeforeQuery != "" {
createdBeforeParsed, err := time.Parse(time.RFC3339, createdBeforeQuery)
if err != nil {
h.logger.Error("invalid start_time format", "error", err)
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid start_time format", nil, nil)
}
createdBefore = domain.ValidTime{
Value: createdBeforeParsed,
Valid: true,
}
}
createdAfterQuery := c.Query("created_after")
var createdAfter domain.ValidTime
if createdAfterQuery != "" {
createdAfterParsed, err := time.Parse(time.RFC3339, createdAfterQuery)
if err != nil {
h.logger.Error("invalid start_time format", "error", err)
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid start_time format", nil, nil)
}
createdAfter = domain.ValidTime{
Value: createdAfterParsed,
Valid: true,
}
}
filter := domain.UserFilter{
Role: string(domain.RoleCustomer),
CompanyID: companyId,
Page: domain.ValidInt{
Value: c.QueryInt("page", 1) - 1,
Valid: true,
},
PageSize: domain.ValidInt{
Value: c.QueryInt("page_size", 10),
Valid: true,
},
Query: searchString,
CreatedBefore: createdBefore,
CreatedAfter: createdAfter,
}
valErrs, ok := h.validator.Validate(c, filter)
if !ok {
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil)
}
customers, total, err := h.userSvc.GetAllUsers(c.Context(), filter)
if err != nil {
h.logger.Error("GetAllCustomers failed", "error", err)
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to get Customers", err, nil)
}
var result []CustomersRes = make([]CustomersRes, len(customers))
for index, customer := range customers {
lastLogin, err := h.authSvc.GetLastLogin(c.Context(), customer.ID)
if err != nil {
if err == authentication.ErrRefreshTokenNotFound {
lastLogin = &customer.CreatedAt
} else {
h.logger.Error("Failed to get user last login", "userID", customer.ID, "error", err)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve user last login")
}
}
result[index] = CustomersRes{
ID: customer.ID,
FirstName: customer.FirstName,
LastName: customer.LastName,
Email: customer.Email,
PhoneNumber: customer.PhoneNumber,
Role: customer.Role,
EmailVerified: customer.EmailVerified,
PhoneVerified: customer.PhoneVerified,
CreatedAt: customer.CreatedAt,
UpdatedAt: customer.UpdatedAt,
SuspendedAt: customer.SuspendedAt,
Suspended: customer.Suspended,
LastLogin: *lastLogin,
}
}
return response.WritePaginatedJSON(c, fiber.StatusOK, "Customers retrieved successfully", result, nil, filter.Page.Value, int(total))
}
// GetCustomerByID godoc
// @Summary Get customer by id
// @Description Get a single customer by id
// @Tags customer
// @Accept json
// @Produce json
// @Param id path int true "User ID"
// @Success 200 {object} CustomersRes
// @Failure 400 {object} response.APIResponse
// @Failure 401 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /customer/{id} [get]
func (h *Handler) GetCustomerByID(c *fiber.Ctx) error {
userIDstr := c.Params("id")
userID, err := strconv.ParseInt(userIDstr, 10, 64)
if err != nil {
return fiber.NewError(fiber.StatusBadRequest, "Invalid customers ID")
}
user, err := h.userSvc.GetUserByID(c.Context(), userID)
if err != nil {
return fiber.NewError(fiber.StatusInternalServerError, "Failed to get customers")
}
lastLogin, err := h.authSvc.GetLastLogin(c.Context(), user.ID)
if err != nil {
if err != authentication.ErrRefreshTokenNotFound {
h.logger.Error("Failed to get user last login", "userID", user.ID, "error", err)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve user last login")
}
lastLogin = &user.CreatedAt
}
res := CustomersRes{
ID: user.ID,
FirstName: user.FirstName,
LastName: user.LastName,
Email: user.Email,
PhoneNumber: user.PhoneNumber,
Role: user.Role,
EmailVerified: user.EmailVerified,
PhoneVerified: user.PhoneVerified,
CreatedAt: user.CreatedAt,
UpdatedAt: user.UpdatedAt,
SuspendedAt: user.SuspendedAt,
Suspended: user.Suspended,
LastLogin: *lastLogin,
}
return response.WriteJSON(c, fiber.StatusOK, "User retrieved successfully", res, nil)
}
type updateCustomerReq struct {
FirstName string `json:"first_name" example:"John"`
LastName string `json:"last_name" example:"Doe"`
Suspended bool `json:"suspended" example:"false"`
CompanyID *int64 `json:"company_id,omitempty" example:"1"`
}
// UpdateCustomers godoc
// @Summary Update Customers
// @Description Update Customers
// @Tags customer
// @Accept json
// @Produce json
// @Param Customers body updateCustomerReq true "Update Customers"
// @Success 200 {object} response.APIResponse
// @Failure 400 {object} response.APIResponse
// @Failure 401 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /customer/{id} [put]
func (h *Handler) UpdateCustomer(c *fiber.Ctx) error {
var req updateCustomerReq
if err := c.BodyParser(&req); err != nil {
h.logger.Error("UpdateCustomers failed", "error", err)
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", nil, nil)
}
valErrs, ok := h.validator.Validate(c, req)
if !ok {
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil)
}
CustomersIdStr := c.Params("id")
CustomersId, err := strconv.ParseInt(CustomersIdStr, 10, 64)
if err != nil {
h.logger.Error("UpdateCustomers failed", "error", err)
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid Customers ID", nil, nil)
}
// var companyID domain.ValidInt64
// role := c.Locals("role").(domain.Role)
// if req.CompanyID != nil {
// if role != domain.RoleSuperAdmin {
// h.logger.Error("UpdateCustomers failed", "error", err)
// return response.WriteJSON(c, fiber.StatusUnauthorized, "This user role cannot modify company ID", nil, nil)
// }
// companyID = domain.ValidInt64{
// Value: *req.CompanyID,
// Valid: true,
// }
// }
err = h.userSvc.UpdateUser(c.Context(), domain.UpdateUserReq{
UserId: CustomersId,
FirstName: domain.ValidString{
Value: req.FirstName,
Valid: req.FirstName != "",
},
LastName: domain.ValidString{
Value: req.LastName,
Valid: req.LastName != "",
},
Suspended: domain.ValidBool{
Value: req.Suspended,
Valid: true,
},
},
)
if err != nil {
h.logger.Error("UpdateCustomers failed", "error", err)
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to update Customers", nil, nil)
}
return response.WriteJSON(c, fiber.StatusOK, "Customers updated successfully", nil, nil)
}

View File

@ -123,6 +123,10 @@ func (a *App) initAppRoutes() {
a.fiber.Post("/cashiers", a.authMiddleware, h.CreateCashier)
a.fiber.Put("/cashiers/:id", a.authMiddleware, h.UpdateCashier)
a.fiber.Get("/customer", a.authMiddleware, a.SuperAdminOnly, h.GetAllCustomers)
a.fiber.Get("/customer/:id", a.authMiddleware, a.SuperAdminOnly, h.GetCustomerByID)
a.fiber.Put("/customer/:id", a.authMiddleware, a.SuperAdminOnly, h.UpdateCustomer)
a.fiber.Get("/admin", a.authMiddleware, h.GetAllAdmins)
a.fiber.Get("/admin/:id", a.authMiddleware, h.GetAdminByID)
a.fiber.Post("/admin", a.authMiddleware, h.CreateAdmin)