Compare commits
6 Commits
99fe11fbeb
...
mailisearc
| Author | SHA1 | Date | |
|---|---|---|---|
| 8e07daac66 | |||
| 6408b93e5c | |||
| 27fa88b076 | |||
|
|
b67c4e3aef | ||
|
|
0d29d8f6a2 | ||
|
|
884e15bb8a |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -5,3 +5,4 @@ bin/
|
|||||||
i18n/*.json
|
i18n/*.json
|
||||||
*_templ.go
|
*_templ.go
|
||||||
tmp/main
|
tmp/main
|
||||||
|
test.go
|
||||||
12
Taskfile.yml
12
Taskfile.yml
@@ -73,10 +73,22 @@ vars:
|
|||||||
MP_SMTP_AUTH_ALLOW_INSECURE: true
|
MP_SMTP_AUTH_ALLOW_INSECURE: true
|
||||||
MP_ENABLE_SPAMASSASSIN: postmark
|
MP_ENABLE_SPAMASSASSIN: postmark
|
||||||
MP_VERBOSE: true
|
MP_VERBOSE: true
|
||||||
|
meilisearch:
|
||||||
|
image: getmeili/meilisearch:latest
|
||||||
|
container_name: meilisearch
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- 7700:7700
|
||||||
|
volumes:
|
||||||
|
- meilisearch:/data.ms
|
||||||
|
environment:
|
||||||
|
MEILI_MASTER_KEY: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||||
|
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
db_data:
|
db_data:
|
||||||
mailpit_data:
|
mailpit_data:
|
||||||
|
meilisearch:
|
||||||
|
|
||||||
|
|
||||||
includes:
|
includes:
|
||||||
|
|||||||
55
app/delivery/web/api/restricted/menu.go
Normal file
55
app/delivery/web/api/restricted/menu.go
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
package restricted
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/app/service/menuService"
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/app/utils/i18n"
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/app/utils/nullable"
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/app/utils/response"
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/app/utils/responseErrors"
|
||||||
|
"github.com/gofiber/fiber/v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MenuHandler struct {
|
||||||
|
menuService *menuService.MenuService
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMenuHandler() *MenuHandler {
|
||||||
|
menuService := menuService.New()
|
||||||
|
return &MenuHandler{
|
||||||
|
menuService: menuService,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func MenuHandlerRoutes(r fiber.Router) fiber.Router {
|
||||||
|
handler := NewMenuHandler()
|
||||||
|
|
||||||
|
r.Get("/get-menu", handler.GetMenu)
|
||||||
|
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *MenuHandler) GetMenu(c fiber.Ctx) error {
|
||||||
|
|
||||||
|
id_shop_attribute := c.Query("shopID")
|
||||||
|
id_shop, err := strconv.Atoi(id_shop_attribute)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute)))
|
||||||
|
}
|
||||||
|
|
||||||
|
id_lang, err := strconv.Atoi(c.Cookies("lang_id", "2"))
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute)))
|
||||||
|
}
|
||||||
|
|
||||||
|
menu, err := h.menuService.GetMenu(uint(id_shop), uint(id_lang))
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(response.Make(&menu, 0, i18n.T_(c, response.Message_OK)))
|
||||||
|
}
|
||||||
@@ -102,6 +102,10 @@ func (s *Server) Setup() error {
|
|||||||
langsAndCountries := s.restricted.Group("/langs-and-countries")
|
langsAndCountries := s.restricted.Group("/langs-and-countries")
|
||||||
restricted.LangsAndCountriesHandlerRoutes(langsAndCountries)
|
restricted.LangsAndCountriesHandlerRoutes(langsAndCountries)
|
||||||
|
|
||||||
|
// menu (restricted)
|
||||||
|
menu := s.restricted.Group("/menu")
|
||||||
|
restricted.MenuHandlerRoutes(menu)
|
||||||
|
|
||||||
// // Restricted routes example
|
// // Restricted routes example
|
||||||
// restricted := s.api.Group("/restricted")
|
// restricted := s.api.Group("/restricted")
|
||||||
// restricted.Use(middleware.AuthMiddleware())
|
// restricted.Use(middleware.AuthMiddleware())
|
||||||
|
|||||||
@@ -62,11 +62,11 @@ type Product struct {
|
|||||||
DeliveryDays uint `gorm:"column:delivery_days" json:"delivery_days" form:"delivery_days"`
|
DeliveryDays uint `gorm:"column:delivery_days" json:"delivery_days" form:"delivery_days"`
|
||||||
}
|
}
|
||||||
type ProductInList struct {
|
type ProductInList struct {
|
||||||
ID uint `gorm:"column:id_product;primaryKey" json:"product_id" form:"product_id"`
|
ProductID uint `gorm:"column:ID;primaryKey" json:"product_id" form:"product_id"`
|
||||||
Name string `gorm:"column:name;default:'no name'" json:"name" form:"name"`
|
Name string `gorm:"column:name" json:"name" form:"name"`
|
||||||
Price float64 `gorm:"column:price;default:0.0" json:"price" form:"price"`
|
ImageID uint `gorm:"column:id_image"`
|
||||||
ActiveAsProduct uint `gorm:"column:active;default:0" json:"active_as_product" form:"active_as_product"`
|
LinkRewrite string `gorm:"column:link_rewrite"`
|
||||||
ActiveInShop uint `gorm:"column:active;default:0" json:"active_in_shop" form:"active_in_shop"`
|
Active uint `gorm:"column:active" json:"active" form:"active"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ProductFilters struct {
|
type ProductFilters struct {
|
||||||
@@ -81,4 +81,19 @@ type ProductFilters struct {
|
|||||||
InStock uint `query:"stock,omitempty"`
|
InStock uint `query:"stock,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ScannedCategory struct {
|
||||||
|
CategoryID uint `gorm:"column:ID;primaryKey"`
|
||||||
|
Name string `gorm:"column:name"`
|
||||||
|
Active uint `gorm:"column:active"`
|
||||||
|
Position uint `gorm:"column:position"`
|
||||||
|
ParentID uint `gorm:"column:id_parent"`
|
||||||
|
IsRoot uint `gorm:"column:is_root_category"`
|
||||||
|
}
|
||||||
|
type Category struct {
|
||||||
|
CategoryID uint `json:"category_id" form:"category_id"`
|
||||||
|
Name string `json:"name" form:"name"`
|
||||||
|
Active uint `json:"active" form:"active"`
|
||||||
|
Subcategories []Category `json:"subcategories" form:"subcategories"`
|
||||||
|
}
|
||||||
|
|
||||||
type FeatVal = map[uint][]uint
|
type FeatVal = map[uint][]uint
|
||||||
|
|||||||
90
app/service/menuService/menuService.go
Normal file
90
app/service/menuService/menuService.go
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
package menuService
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sort"
|
||||||
|
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/app/model"
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/app/utils/responseErrors"
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/repository/categoriesRepo"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MenuService struct {
|
||||||
|
categoriesRepo categoriesRepo.UICategoriesRepo
|
||||||
|
}
|
||||||
|
|
||||||
|
func New() *MenuService {
|
||||||
|
return &MenuService{
|
||||||
|
categoriesRepo: categoriesRepo.New(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MenuService) GetMenu(id_shop uint, id_lang uint) (model.Category, error) {
|
||||||
|
all_categories, err := s.categoriesRepo.GetAllCategories(id_shop, id_lang)
|
||||||
|
if err != nil {
|
||||||
|
return model.Category{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// find the root
|
||||||
|
root_index := 0
|
||||||
|
root_found := false
|
||||||
|
for i := 0; i < len(all_categories); i++ {
|
||||||
|
if all_categories[i].IsRoot == 1 {
|
||||||
|
root_index = i
|
||||||
|
root_found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !root_found {
|
||||||
|
return model.Category{}, responseErrors.ErrNoRootFound
|
||||||
|
}
|
||||||
|
|
||||||
|
// now create the children and reorder them according to position
|
||||||
|
id_to_index := make(map[uint]int)
|
||||||
|
for i := 0; i < len(all_categories); i++ {
|
||||||
|
id_to_index[all_categories[i].CategoryID] = i
|
||||||
|
}
|
||||||
|
|
||||||
|
children_indices := make(map[int][]ChildWithPosition)
|
||||||
|
for i := 0; i < len(all_categories); i++ {
|
||||||
|
parent_index := id_to_index[all_categories[i].ParentID]
|
||||||
|
children_indices[parent_index] = append(children_indices[parent_index], ChildWithPosition{Index: i, Position: all_categories[i].Position})
|
||||||
|
}
|
||||||
|
|
||||||
|
for key := range children_indices {
|
||||||
|
sort.Sort(ByPosition(children_indices[key]))
|
||||||
|
}
|
||||||
|
|
||||||
|
// finally, create the tree
|
||||||
|
tree := s.createTree(root_index, &all_categories, &children_indices)
|
||||||
|
|
||||||
|
return tree, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MenuService) createTree(index int, all_categories *([]model.ScannedCategory), children_indices *(map[int][]ChildWithPosition)) model.Category {
|
||||||
|
node := s.scannedToNormalCategory((*all_categories)[index])
|
||||||
|
|
||||||
|
for i := 0; i < len((*children_indices)[index]); i++ {
|
||||||
|
node.Subcategories = append(node.Subcategories, s.createTree((*children_indices)[index][i].Index, all_categories, children_indices))
|
||||||
|
}
|
||||||
|
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MenuService) scannedToNormalCategory(scanned model.ScannedCategory) model.Category {
|
||||||
|
var normal model.Category
|
||||||
|
normal.Active = scanned.Active
|
||||||
|
normal.CategoryID = scanned.CategoryID
|
||||||
|
normal.Name = scanned.Name
|
||||||
|
normal.Subcategories = []model.Category{}
|
||||||
|
return normal
|
||||||
|
}
|
||||||
|
|
||||||
|
type ChildWithPosition struct {
|
||||||
|
Index int
|
||||||
|
Position uint
|
||||||
|
}
|
||||||
|
type ByPosition []ChildWithPosition
|
||||||
|
|
||||||
|
func (a ByPosition) Len() int { return len(a) }
|
||||||
|
func (a ByPosition) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||||
|
func (a ByPosition) Less(i, j int) bool { return a[i].Position < a[j].Position }
|
||||||
@@ -48,6 +48,9 @@ var (
|
|||||||
|
|
||||||
// Typed errors for product list handler
|
// Typed errors for product list handler
|
||||||
ErrBadPaging = errors.New("bad or missing paging attribute value in header")
|
ErrBadPaging = errors.New("bad or missing paging attribute value in header")
|
||||||
|
|
||||||
|
// Typed errors for menu handler
|
||||||
|
ErrNoRootFound = errors.New("no root found in categories table")
|
||||||
)
|
)
|
||||||
|
|
||||||
// Error represents an error with HTTP status code
|
// Error represents an error with HTTP status code
|
||||||
@@ -135,6 +138,9 @@ func GetErrorCode(c fiber.Ctx, err error) string {
|
|||||||
case errors.Is(err, ErrBadPaging):
|
case errors.Is(err, ErrBadPaging):
|
||||||
return i18n.T_(c, "error.err_bad_paging")
|
return i18n.T_(c, "error.err_bad_paging")
|
||||||
|
|
||||||
|
case errors.Is(err, ErrNoRootFound):
|
||||||
|
return i18n.T_(c, "error.no_root_found")
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return i18n.T_(c, "error.err_internal_server_error")
|
return i18n.T_(c, "error.err_internal_server_error")
|
||||||
}
|
}
|
||||||
@@ -169,7 +175,8 @@ func GetErrorStatus(err error) int {
|
|||||||
errors.Is(err, ErrBadAttribute),
|
errors.Is(err, ErrBadAttribute),
|
||||||
errors.Is(err, ErrBadField),
|
errors.Is(err, ErrBadField),
|
||||||
errors.Is(err, ErrInvalidXHTML),
|
errors.Is(err, ErrInvalidXHTML),
|
||||||
errors.Is(err, ErrBadPaging):
|
errors.Is(err, ErrBadPaging),
|
||||||
|
errors.Is(err, ErrNoRootFound):
|
||||||
return fiber.StatusBadRequest
|
return fiber.StatusBadRequest
|
||||||
case errors.Is(err, ErrEmailExists):
|
case errors.Is(err, ErrEmailExists):
|
||||||
return fiber.StatusConflict
|
return fiber.StatusConflict
|
||||||
|
|||||||
@@ -6,16 +6,16 @@ CREATE TABLE IF NOT EXISTS b2b_tracker_routes (
|
|||||||
path VARCHAR(255) NULL,
|
path VARCHAR(255) NULL,
|
||||||
component VARCHAR(255) NOT NULL COMMENT 'path to component file',
|
component VARCHAR(255) NOT NULL COMMENT 'path to component file',
|
||||||
layout VARCHAR(50) DEFAULT 'default' COMMENT "'default' | 'empty'",
|
layout VARCHAR(50) DEFAULT 'default' COMMENT "'default' | 'empty'",
|
||||||
meta JSON DEFAULT '{}' ,
|
meta JSON DEFAULT '{}',
|
||||||
is_active BOOLEAN DEFAULT TRUE,
|
is_active BOOLEAN DEFAULT TRUE,
|
||||||
sort_order INT DEFAULT 0,
|
sort_order INT DEFAULT 0,
|
||||||
parent_id INT NULL
|
parent_id INT NULL,
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
||||||
|
|
||||||
ALTER TABLE b2b_tracker_routes
|
CONSTRAINT fk_parent
|
||||||
ADD CONSTRAINT fk_parent
|
FOREIGN KEY (parent_id)
|
||||||
FOREIGN KEY (parent_id) REFERENCES b2b_tracker_routes(id)
|
REFERENCES b2b_tracker_routes(id)
|
||||||
ON DELETE SET NULL;
|
ON DELETE SET NULL
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
|
|
||||||
INSERT IGNORE INTO b2b_tracker_routes
|
INSERT IGNORE INTO b2b_tracker_routes
|
||||||
(name, path, component, layout, meta, is_active, sort_order, parent_id)
|
(name, path, component, layout, meta, is_active, sort_order, parent_id)
|
||||||
|
|||||||
@@ -113,13 +113,6 @@ CREATE UNIQUE INDEX IF NOT EXISTS uk_refresh_tokens_token_hash ON b2b_refresh_to
|
|||||||
CREATE INDEX IF NOT EXISTS idx_refresh_tokens_customer_id ON b2b_refresh_tokens (customer_id);
|
CREATE INDEX IF NOT EXISTS idx_refresh_tokens_customer_id ON b2b_refresh_tokens (customer_id);
|
||||||
|
|
||||||
|
|
||||||
-- insert sample admin user admin@ma-al.com/Maal12345678
|
|
||||||
|
|
||||||
INSERT IGNORE INTO b2b_customers (id, email, password, first_name, last_name, role, provider, provider_id, avatar_url, is_active, email_verified, email_verification_token, email_verification_expires, password_reset_token, password_reset_expires, last_password_reset_request, last_login_at, lang_id, country_id, created_at, updated_at, deleted_at)
|
|
||||||
VALUES
|
|
||||||
(1, 'admin@ma-al.com', '$2a$10$Owy9DjrS0l3Fz4XoOvh5pulgmOMqdwXmb7hYE9BovnSuWS2plGr82', 'Super', 'Admin', 'admin', 'local', '', '', 1, 1, NULL, NULL, '', NULL, NULL, NULL, 1, 1, '2026-03-02 16:55:10.252740', '2026-03-02 16:55:10.252740', NULL);
|
|
||||||
ALTER TABLE b2b_customers AUTO_INCREMENT = 1;
|
|
||||||
|
|
||||||
-- countries
|
-- countries
|
||||||
CREATE TABLE IF NOT EXISTS b2b_countries (
|
CREATE TABLE IF NOT EXISTS b2b_countries (
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
|||||||
41
repository/categoriesRepo/categoriesRepo.go
Normal file
41
repository/categoriesRepo/categoriesRepo.go
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
package categoriesRepo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/app/db"
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/app/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UICategoriesRepo interface {
|
||||||
|
GetAllCategories(id_shop uint, id_lang uint) ([]model.ScannedCategory, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type CategoriesRepo struct{}
|
||||||
|
|
||||||
|
func New() UICategoriesRepo {
|
||||||
|
return &CategoriesRepo{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *CategoriesRepo) GetAllCategories(id_shop uint, id_lang uint) ([]model.ScannedCategory, error) {
|
||||||
|
var allCategories []model.ScannedCategory
|
||||||
|
|
||||||
|
err := db.DB.Raw(`
|
||||||
|
SELECT
|
||||||
|
ps_category.id_category AS ID,
|
||||||
|
ps_category_lang.name AS name,
|
||||||
|
ps_category.active AS active,
|
||||||
|
ps_category_shop.position AS position,
|
||||||
|
ps_category.id_parent AS id_parent,
|
||||||
|
ps_category.is_root_category AS is_root_category
|
||||||
|
FROM ps_category
|
||||||
|
LEFT JOIN ps_category_lang
|
||||||
|
ON ps_category_lang.id_category = ps_category.id_category
|
||||||
|
AND ps_category_lang.id_shop = ?
|
||||||
|
AND ps_category_lang.id_lang = ?
|
||||||
|
LEFT JOIN ps_category_shop
|
||||||
|
ON ps_category_shop.id_category = ps_category.id_category
|
||||||
|
AND ps_category_shop.id_shop = ?`,
|
||||||
|
id_shop, id_lang, id_shop).
|
||||||
|
Scan(&allCategories).Error
|
||||||
|
|
||||||
|
return allCategories, err
|
||||||
|
}
|
||||||
@@ -21,15 +21,6 @@ func (repo *ListProductsRepo) GetListing(id_shop uint, id_lang uint, p find.Pagi
|
|||||||
var listing []model.ProductInList
|
var listing []model.ProductInList
|
||||||
var total int64
|
var total int64
|
||||||
|
|
||||||
// Apply filters here
|
|
||||||
q := db.DB.Table("ps_product").
|
|
||||||
Select("ps_product.id_product AS id_product", "ps_product_lang.name AS name", "ps_product_shop.price AS price", "ps_product.active AS active_as_product", "ps_product_shop.active AS active_in_shop").
|
|
||||||
Joins("LEFT JOIN ps_product_shop ON ps_product.id_product = ps_product_shop.id_product").
|
|
||||||
Joins("LEFT JOIN ps_product_lang ON ps_product.id_product = ps_product_lang.id_product").
|
|
||||||
Where("ps_product_shop.id_shop = ?", id_shop).
|
|
||||||
Where("ps_product_lang.id_shop = ?", id_shop).
|
|
||||||
Where("ps_product_lang.id_lang = ?", id_lang)
|
|
||||||
|
|
||||||
// var resultIDs []uint
|
// var resultIDs []uint
|
||||||
// q := db.DB.
|
// q := db.DB.
|
||||||
// // SQL_CALC_FOUND_ROWS is a neat trick which works on MariaDB and
|
// // SQL_CALC_FOUND_ROWS is a neat trick which works on MariaDB and
|
||||||
@@ -44,15 +35,41 @@ func (repo *ListProductsRepo) GetListing(id_shop uint, id_lang uint, p find.Pagi
|
|||||||
// Limit(p.Limit()).
|
// Limit(p.Limit()).
|
||||||
// Offset(p.Offset())
|
// Offset(p.Offset())
|
||||||
|
|
||||||
err := q.Count(&total).Error
|
err := db.DB.Raw(`
|
||||||
|
SELECT
|
||||||
|
ps_product.id_product AS ID,
|
||||||
|
ps_product_lang.name AS name,
|
||||||
|
ps_product.active AS active,
|
||||||
|
ps_product_lang.link_rewrite AS link_rewrite,
|
||||||
|
COALESCE (
|
||||||
|
ps_image_shop.id_image, any_image.id_image
|
||||||
|
) AS id_image
|
||||||
|
FROM ps_product
|
||||||
|
LEFT JOIN ps_product_lang
|
||||||
|
ON ps_product_lang.id_product = ps_product.id_product
|
||||||
|
AND ps_product_lang.id_shop = ?
|
||||||
|
AND ps_product_lang.id_lang = ?
|
||||||
|
LEFT JOIN ps_image_shop
|
||||||
|
ON ps_image_shop.id_product = ps_product.id_product
|
||||||
|
AND ps_image_shop.id_shop = ?
|
||||||
|
AND ps_image_shop.cover = 1
|
||||||
|
LEFT JOIN (
|
||||||
|
SELECT id_product, MIN(id_image) AS id_image
|
||||||
|
FROM ps_image
|
||||||
|
GROUP BY id_product
|
||||||
|
) any_image
|
||||||
|
ON ps_product.id_product = any_image.id_product
|
||||||
|
LIMIT ? OFFSET ?`,
|
||||||
|
id_shop, id_lang, id_shop, p.Limit(), p.Offset()).
|
||||||
|
Scan(&listing).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return find.Found[model.ProductInList]{}, err
|
return find.Found[model.ProductInList]{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = q.
|
err = db.DB.Raw(`
|
||||||
Limit(p.Limit()).
|
SELECT COUNT(*)
|
||||||
Offset(p.Offset()).
|
FROM ps_product`).
|
||||||
Scan(&listing).Error
|
Scan(&total).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return find.Found[model.ProductInList]{}, err
|
return find.Found[model.ProductInList]{}, err
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user