4 Commits

Author SHA1 Message Date
Daniel Goc
bfc488bad8 new fields in meili indexing 2026-03-23 16:43:51 +01:00
Daniel Goc
f5f23f8a27 new fields in meili indexing 2026-03-23 16:42:54 +01:00
cffe4c2f83 Merge pull request 'routing' (#16) from routing into main
Reviewed-on: #16
2026-03-23 13:09:23 +00:00
15e8626280 routing 2026-03-23 14:08:53 +01:00
18 changed files with 121 additions and 181 deletions

4
.env
View File

@@ -25,6 +25,9 @@ AUTH_REFRESH_EXPIRATION=604800
MEILISEARCH_URL=http://localhost:7700 MEILISEARCH_URL=http://localhost:7700
MEILISEARCH_API_KEY=e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 MEILISEARCH_API_KEY=e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
# OpenAI
OPENAI_KEY=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
# Google Translate Client # Google Translate Client
GOOGLE_APPLICATION_CREDENTIALS=./google-cred.json GOOGLE_APPLICATION_CREDENTIALS=./google-cred.json
GOOGLE_CLOUD_PROJECT_ID=translation-343517 GOOGLE_CLOUD_PROJECT_ID=translation-343517
@@ -33,6 +36,7 @@ GOOGLE_CLOUD_PROJECT_ID=translation-343517
OAUTH_GOOGLE_CLIENT_ID=331979954218-9vrpe08oqhhcgj6bvu6d4lds0dt630m9.apps.googleusercontent.com OAUTH_GOOGLE_CLIENT_ID=331979954218-9vrpe08oqhhcgj6bvu6d4lds0dt630m9.apps.googleusercontent.com
OAUTH_GOOGLE_CLIENT_SECRET=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 OAUTH_GOOGLE_CLIENT_SECRET=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
OAUTH_GOOGLE_REDIRECT_URL=http://localhost:3000/api/v1/public/auth/google/callback OAUTH_GOOGLE_REDIRECT_URL=http://localhost:3000/api/v1/public/auth/google/callback
# Email Configuration (SMTP) # Email Configuration (SMTP)
# Set EMAIL_ENABLED=true to require email verification # Set EMAIL_ENABLED=true to require email verification
EMAIL_ENABLED=true EMAIL_ENABLED=true

View File

@@ -1,8 +1,6 @@
package restricted package restricted
import ( import (
"strconv"
"git.ma-al.com/goc_daniel/b2b/app/config" "git.ma-al.com/goc_daniel/b2b/app/config"
"git.ma-al.com/goc_daniel/b2b/app/service/listProductsService" "git.ma-al.com/goc_daniel/b2b/app/service/listProductsService"
"git.ma-al.com/goc_daniel/b2b/app/utils/i18n" "git.ma-al.com/goc_daniel/b2b/app/utils/i18n"
@@ -52,13 +50,13 @@ func (h *ListProductsHandler) GetListing(c fiber.Ctx) error {
// "override_currency": c.Query("override_currency", ""), // "override_currency": c.Query("override_currency", ""),
// } // }
id_lang, err := strconv.Atoi(c.Cookies("lang_id", "2")) id_lang, ok := c.Locals("langID").(uint)
if err != nil { if !ok {
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)). return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)).
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute))) JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute)))
} }
listing, err := h.listProductsService.GetListing(uint(id_lang), paging, filters) listing, err := h.listProductsService.GetListing(id_lang, paging, filters)
if err != nil { if err != nil {
return c.Status(responseErrors.GetErrorStatus(err)). return c.Status(responseErrors.GetErrorStatus(err)).
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))

View File

@@ -1,8 +1,6 @@
package restricted package restricted
import ( import (
"strconv"
"git.ma-al.com/goc_daniel/b2b/app/service/meiliService" "git.ma-al.com/goc_daniel/b2b/app/service/meiliService"
"git.ma-al.com/goc_daniel/b2b/app/utils/i18n" "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/nullable"
@@ -32,13 +30,13 @@ func MeiliSearchHandlerRoutes(r fiber.Router) fiber.Router {
} }
func (h *MeiliSearchHandler) CreateIndex(c fiber.Ctx) error { func (h *MeiliSearchHandler) CreateIndex(c fiber.Ctx) error {
id_lang, err := strconv.Atoi(c.Cookies("lang_id", "2")) id_lang, ok := c.Locals("langID").(uint)
if err != nil { if !ok {
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)). return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)).
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute))) JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute)))
} }
err = h.meiliService.CreateIndex(uint(id_lang)) err := h.meiliService.CreateIndex(id_lang)
if err != nil { if err != nil {
return c.Status(responseErrors.GetErrorStatus(err)). return c.Status(responseErrors.GetErrorStatus(err)).
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
@@ -49,13 +47,13 @@ func (h *MeiliSearchHandler) CreateIndex(c fiber.Ctx) error {
} }
func (h *MeiliSearchHandler) Test(c fiber.Ctx) error { func (h *MeiliSearchHandler) Test(c fiber.Ctx) error {
id_lang, err := strconv.Atoi(c.Cookies("lang_id", "2")) id_lang, ok := c.Locals("langID").(uint)
if err != nil { if !ok {
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)). return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)).
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute))) JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute)))
} }
test, err := h.meiliService.Test(uint(id_lang)) test, err := h.meiliService.Test(id_lang)
if err != nil { if err != nil {
return c.Status(responseErrors.GetErrorStatus(err)). return c.Status(responseErrors.GetErrorStatus(err)).
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))

View File

@@ -24,33 +24,18 @@ func MenuHandlerRoutes(r fiber.Router) fiber.Router {
handler := NewMenuHandler() handler := NewMenuHandler()
r.Get("/get-menu", handler.GetMenu) r.Get("/get-menu", handler.GetMenu)
r.Get("/get-routes", handler.GetRouting)
return r return r
} }
func (h *MenuHandler) GetMenu(c fiber.Ctx) error { func (h *MenuHandler) GetMenu(c fiber.Ctx) error {
lang_id, ok := c.Locals("langID").(uint) id_lang, ok := c.Locals("langID").(uint)
if !ok { if !ok {
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)). return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)).
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute))) JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute)))
} }
menu, err := h.menuService.GetMenu(lang_id)
if err != nil { menu, err := h.menuService.GetMenu(id_lang)
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)))
}
func (h *MenuHandler) GetRouting(c fiber.Ctx) error {
lang_id, ok := c.Locals("langID").(uint)
if !ok {
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)).
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute)))
}
menu, err := h.menuService.GetRoutes(lang_id)
if err != nil { if err != nil {
return c.Status(responseErrors.GetErrorStatus(err)). return c.Status(responseErrors.GetErrorStatus(err)).
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))

View File

@@ -110,10 +110,6 @@ func (s *Server) Setup() error {
meiliSearch := s.restricted.Group("/meili-search") meiliSearch := s.restricted.Group("/meili-search")
restricted.MeiliSearchHandlerRoutes(meiliSearch) restricted.MeiliSearchHandlerRoutes(meiliSearch)
s.api.All("*", func(c fiber.Ctx) error {
return c.SendStatus(fiber.StatusNotFound)
})
// // Restricted routes example // // Restricted routes example
// restricted := s.api.Group("/restricted") // restricted := s.api.Group("/restricted")
// restricted.Use(middleware.AuthMiddleware()) // restricted.Use(middleware.AuthMiddleware())

View File

@@ -19,10 +19,26 @@ type ProductDescription struct {
Usage string `gorm:"column:usage;type:text" json:"usage" form:"usage"` Usage string `gorm:"column:usage;type:text" json:"usage" form:"usage"`
} }
type MeiliSearchProduct struct { type ProductRow struct {
ProductID uint IDProduct int `gorm:"column:id_product"`
Name string IDShop int `gorm:"column:id_shop"`
Description string Name string `gorm:"column:name"`
DescriptionShort string Active uint8 `gorm:"column:active"`
Usage string Reference string `gorm:"column:reference"`
}
type MeiliSearchProduct struct {
ProductID uint `gorm:"column:id_product"`
Name string `gorm:"column:name"`
Active uint8 `gorm:"column:active"`
Price float64 `gorm:"column:price"`
Description string `gorm:"column:description"`
DescriptionShort string `gorm:"column:description_short"`
Usage string `gorm:"column:usage"`
EAN13 string `gorm:"column:ean13"`
Reference string `gorm:"column:reference"`
Width float64 `gorm:"column:width"`
Height float64 `gorm:"column:height"`
Depth float64 `gorm:"column:depth"`
Weight float64 `gorm:"column:weight"`
} }

View File

@@ -1,21 +0,0 @@
package model
type Route struct {
ID uint `gorm:"primaryKey;autoIncrement"`
Name string `gorm:"type:varchar(255);not null;unique"`
Path *string `gorm:"type:varchar(255);default:null"`
Component string `gorm:"type:varchar(255);not null;comment:path to component file"`
Layout *string `gorm:"type:varchar(50);default:'default';comment:'default | empty'"`
Meta *string `gorm:"type:longtext;default:'{}'"`
IsActive *bool `gorm:"type:tinyint;default:1"`
SortOrder *int `gorm:"type:int;default:0"`
ParentID *uint `gorm:"index"`
Parent *Route `gorm:"constraint:OnUpdate:RESTRICT,OnDelete:SET NULL;foreignKey:ParentID"`
Children []Route `gorm:"foreignKey:ParentID"`
}
func (Route) TableName() string {
return "b2b_routes"
}

View File

@@ -12,6 +12,7 @@ type UIProductDescriptionRepo interface {
GetProductDescription(productID uint, productLangID uint) (*model.ProductDescription, error) GetProductDescription(productID uint, productLangID uint) (*model.ProductDescription, error)
CreateIfDoesNotExist(productID uint, productLangID uint) error CreateIfDoesNotExist(productID uint, productLangID uint) error
UpdateFields(productID uint, productLangID uint, updates map[string]string) error UpdateFields(productID uint, productLangID uint, updates map[string]string) error
GetMeiliProducts(id_lang uint) ([]model.MeiliSearchProduct, error)
} }
type ProductDescriptionRepo struct{} type ProductDescriptionRepo struct{}
@@ -73,3 +74,35 @@ func (r *ProductDescriptionRepo) UpdateFields(productID uint, productLangID uint
return nil return nil
} }
// We assume that any user has access to all product descriptions
func (r *ProductDescriptionRepo) GetMeiliProducts(id_lang uint) ([]model.MeiliSearchProduct, error) {
var products []model.MeiliSearchProduct
err := db.DB.
Select("pl.`usage` AS `usage`").
Select(`
ps.id_product AS id_product,
pl.name AS name,
ps.active AS active,
ps.price AS price,
pl.description AS description,
pl.description_short AS description_short,
p.ean13 AS ean13,
p.reference AS reference,
p.width AS width,
p.height AS height,
p.depth AS depth,
p.weight AS weight
`).
Table("ps_product_shop AS ps").
Joins("LEFT JOIN ps_product_lang AS pl ON ps.id_product = pl.id_product AND pl.id_shop = ? AND pl.id_lang = ?", constdata.SHOP_ID, id_lang).
Joins("LEFT JOIN ps_product AS p ON p.id_product = ps.id_product").
Where("ps.id_shop = ?", constdata.SHOP_ID).
Scan(&products).Error
if err != nil {
return products, fmt.Errorf("database error: %w", err)
}
return products, nil
}

View File

@@ -1,25 +0,0 @@
package routesrepo
import (
"git.ma-al.com/goc_daniel/b2b/app/db"
"git.ma-al.com/goc_daniel/b2b/app/model"
)
type UIRoutesRepo interface {
GetRoutes(langId uint) ([]model.Route, error)
}
type RoutesRepo struct{}
func New() UIRoutesRepo {
return &RoutesRepo{}
}
func (p *RoutesRepo) GetRoutes(langId uint) ([]model.Route, error) {
routes := []model.Route{}
err := db.DB.Find(&routes).Error
if err != nil {
return nil, err
}
return routes, nil
}

View File

@@ -0,0 +1,8 @@
{
"products-openai": {
"source": "openAi",
"model": "text-embedding-3-small",
"apiKey": "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",
"documentTemplate": "{{doc.Name}} is equipment used for {{doc.Description | truncatewords: 20}}"
}
}

View File

@@ -9,13 +9,13 @@ import (
"strings" "strings"
"time" "time"
"git.ma-al.com/goc_daniel/b2b/app/db" "git.ma-al.com/goc_daniel/b2b/app/repos/productDescriptionRepo"
"git.ma-al.com/goc_daniel/b2b/app/model"
constdata "git.ma-al.com/goc_daniel/b2b/app/utils/const_data" constdata "git.ma-al.com/goc_daniel/b2b/app/utils/const_data"
"github.com/meilisearch/meilisearch-go" "github.com/meilisearch/meilisearch-go"
) )
type MeiliService struct { type MeiliService struct {
productDescriptionRepo productDescriptionRepo.UIProductDescriptionRepo
meiliClient meilisearch.ServiceManager meiliClient meilisearch.ServiceManager
} }
@@ -30,56 +30,41 @@ func New() *MeiliService {
return &MeiliService{ return &MeiliService{
meiliClient: client, meiliClient: client,
productDescriptionRepo: productDescriptionRepo.New(),
} }
} }
// ==================================== FOR SUPERADMIN ONLY ==================================== // ==================================== FOR SUPERADMIN ONLY ====================================
func (s *MeiliService) CreateIndex(id_lang uint) error { func (s *MeiliService) CreateIndex(id_lang uint) error {
var products []model.ProductDescription products, err := s.productDescriptionRepo.GetMeiliProducts(id_lang)
err := db.DB.
Table("ps_product_lang").
Where("id_shop = ? AND id_lang = ?", constdata.SHOP_ID, id_lang).
Scan(&products).Error
if err != nil {
return fmt.Errorf("database error: %w", err)
}
var meiliProducts []model.MeiliSearchProduct
for i := 0; i < len(products); i++ { for i := 0; i < len(products); i++ {
var nextMeiliProduct model.MeiliSearchProduct products[i].Description, err = cleanHTML(products[i].Description)
nextMeiliProduct.ProductID = products[i].ProductID
nextMeiliProduct.Name = products[i].Name
nextMeiliProduct.Description, err = cleanHTML(products[i].Description)
if err != nil { if err != nil {
fmt.Printf("nextMeiliProduct.Description: %v\n", nextMeiliProduct.Description) fmt.Printf("products[i].Description: %v\n", products[i].Description)
fmt.Printf("products[i].ProductID: %v\n", products[i].ProductID) fmt.Printf("products[i].ProductID: %v\n", products[i].ProductID)
fmt.Println("failed at description") fmt.Println("failed at description")
fmt.Printf("err: %v\n", err) fmt.Printf("err: %v\n", err)
return err return err
} }
nextMeiliProduct.DescriptionShort, err = cleanHTML(products[i].DescriptionShort) products[i].DescriptionShort, err = cleanHTML(products[i].DescriptionShort)
if err != nil { if err != nil {
fmt.Printf("nextMeiliProduct.DescriptionShort: %v\n", nextMeiliProduct.DescriptionShort) fmt.Printf("products[i].DescriptionShort: %v\n", products[i].DescriptionShort)
fmt.Printf("products[i].ProductID: %v\n", products[i].ProductID) fmt.Printf("products[i].ProductID: %v\n", products[i].ProductID)
fmt.Println("failed at description short") fmt.Println("failed at description short")
fmt.Printf("err: %v\n", err) fmt.Printf("err: %v\n", err)
return err return err
} }
nextMeiliProduct.Usage, err = cleanHTML(products[i].Usage) products[i].Usage, err = cleanHTML(products[i].Usage)
if err != nil { if err != nil {
fmt.Printf("nextMeiliProduct.Usage: %v\n", nextMeiliProduct.Usage) fmt.Printf("products[i].Usage: %v\n", products[i].Usage)
fmt.Printf("products[i].ProductID: %v\n", products[i].ProductID) fmt.Printf("products[i].ProductID: %v\n", products[i].ProductID)
fmt.Println("failed at usage") fmt.Println("failed at usage")
fmt.Printf("err: %v\n", err) fmt.Printf("err: %v\n", err)
return err return err
} }
meiliProducts = append(meiliProducts, nextMeiliProduct)
} }
indexName := "meili_products_shop" + strconv.FormatInt(constdata.SHOP_ID, 10) + "_lang" + strconv.FormatInt(int64(id_lang), 10) indexName := "meili_products_shop" + strconv.FormatInt(constdata.SHOP_ID, 10) + "_lang" + strconv.FormatInt(int64(id_lang), 10)
@@ -89,7 +74,7 @@ func (s *MeiliService) CreateIndex(id_lang uint) error {
SkipCreation: false, SkipCreation: false,
} }
task, err := s.meiliClient.Index(indexName).AddDocuments(meiliProducts, docOptions) task, err := s.meiliClient.Index(indexName).AddDocuments(products, docOptions)
if err != nil { if err != nil {
return fmt.Errorf("meili AddDocuments error: %w", err) return fmt.Errorf("meili AddDocuments error: %w", err)
} }

View File

@@ -5,26 +5,23 @@ import (
"git.ma-al.com/goc_daniel/b2b/app/model" "git.ma-al.com/goc_daniel/b2b/app/model"
"git.ma-al.com/goc_daniel/b2b/app/repos/categoriesRepo" "git.ma-al.com/goc_daniel/b2b/app/repos/categoriesRepo"
routesRepo "git.ma-al.com/goc_daniel/b2b/app/repos/routesRepo"
"git.ma-al.com/goc_daniel/b2b/app/utils/responseErrors" "git.ma-al.com/goc_daniel/b2b/app/utils/responseErrors"
) )
type MenuService struct { type MenuService struct {
categoriesRepo categoriesRepo.UICategoriesRepo categoriesRepo categoriesRepo.UICategoriesRepo
routesRepo routesRepo.UIRoutesRepo
} }
func New() *MenuService { func New() *MenuService {
return &MenuService{ return &MenuService{
categoriesRepo: categoriesRepo.New(), categoriesRepo: categoriesRepo.New(),
routesRepo: routesRepo.New(),
} }
} }
func (s *MenuService) GetMenu(id_lang uint) (*model.Category, error) { func (s *MenuService) GetMenu(id_lang uint) (model.Category, error) {
all_categories, err := s.categoriesRepo.GetAllCategories(id_lang) all_categories, err := s.categoriesRepo.GetAllCategories(id_lang)
if err != nil { if err != nil {
return &model.Category{}, err return model.Category{}, err
} }
// find the root // find the root
@@ -38,7 +35,7 @@ func (s *MenuService) GetMenu(id_lang uint) (*model.Category, error) {
} }
} }
if !root_found { if !root_found {
return &model.Category{}, responseErrors.ErrNoRootFound return model.Category{}, responseErrors.ErrNoRootFound
} }
// now create the children and reorder them according to position // now create the children and reorder them according to position
@@ -60,7 +57,7 @@ func (s *MenuService) GetMenu(id_lang uint) (*model.Category, error) {
// finally, create the tree // finally, create the tree
tree := s.createTree(root_index, &all_categories, &children_indices) tree := s.createTree(root_index, &all_categories, &children_indices)
return &tree, nil return tree, nil
} }
func (s *MenuService) createTree(index int, all_categories *([]model.ScannedCategory), children_indices *(map[int][]ChildWithPosition)) model.Category { func (s *MenuService) createTree(index int, all_categories *([]model.ScannedCategory), children_indices *(map[int][]ChildWithPosition)) model.Category {
@@ -73,10 +70,6 @@ func (s *MenuService) createTree(index int, all_categories *([]model.ScannedCate
return node return node
} }
func (s *MenuService) GetRoutes(id_lang uint) ([]model.Route, error) {
return s.routesRepo.GetRoutes(id_lang)
}
func (s *MenuService) scannedToNormalCategory(scanned model.ScannedCategory) model.Category { func (s *MenuService) scannedToNormalCategory(scanned model.ScannedCategory) model.Category {
var normal model.Category var normal model.Category
// normal.Active = scanned.Active // normal.Active = scanned.Active

View File

@@ -70,7 +70,7 @@ func New() *ProductDescriptionService {
log.Fatalf("productDescriptionService: cannot create Translation client: %v", err) log.Fatalf("productDescriptionService: cannot create Translation client: %v", err)
} }
openAIClient := openai.NewClient(option.WithAPIKey("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"), openAIClient := openai.NewClient(option.WithAPIKey(os.Getenv("OPENAI_KEY")),
option.WithHTTPClient(&http.Client{Timeout: 300 * time.Second})) // five minutes timeout option.WithHTTPClient(&http.Client{Timeout: 300 * time.Second})) // five minutes timeout
return &ProductDescriptionService{ return &ProductDescriptionService{

View File

@@ -2,19 +2,20 @@
import TopBar from '@/components/TopBar.vue'; import TopBar from '@/components/TopBar.vue';
import { getMenu } from '@/router/menu' import { getMenu } from '@/router/menu'
import type { NavigationMenuItem } from '@nuxt/ui'; import type { NavigationMenuItem } from '@nuxt/ui';
import { on } from 'events';
import { ref } from 'vue'; import { ref } from 'vue';
let menu = await getMenu() as NavigationMenuItem[] let menu = await getMenu() as NavigationMenuItem[]
const openAll = ref(false) function adaptMenu(menu) {
function adaptMenu(menu: NavigationMenuItem[]) {
for (const item of menu) { for (const item of menu) {
if (item.children && item.children.length > 0) { if (item.children && item.children.length > 0) {
console.log(item);
adaptMenu(item.children); adaptMenu(item.children);
item.open = openAll.value // item.children.unshift({ label: item.label, icon: 'i-lucide-book-open', popover: item.label ,to: { name: 'category', params: item.params }})
item.children.unshift({ label: item.label, icon: 'i-lucide-book-open', popover: item.label ,to: { name: 'category', params: item.params }}) // item.type = 'trigger'
// item.icon = 'i-lucide-book-open'
// item.onSelect(e){ console.log(e, 'adasdasdas'); }
} else { } else {
// console.log(item.params);
item.to = { name: 'category', params: item.params }; item.to = { name: 'category', params: item.params };
item.icon = 'i-lucide-file-text' item.icon = 'i-lucide-file-text'
} }
@@ -22,12 +23,14 @@ function adaptMenu(menu: NavigationMenuItem[]) {
return menu; return menu;
} }
menu = adaptMenu(menu) menu = adaptMenu(menu.children)
console.log(menu);
const items = ref<NavigationMenuItem[][]>([ const items = ref<NavigationMenuItem[][]>([
[ [
...menu as NavigationMenuItem[] ...menu
], ],
]) ])
@@ -38,13 +41,15 @@ const items = ref<NavigationMenuItem[][]>([
<template> <template>
<div class="h-screen grid grid-rows-[auto_1fr_auto]"> <div class="h-screen grid grid-rows-[auto_1fr_auto]">
<main class="p-10"> <main class="p-10">
<TopBar/> <div class="mt-24 bg-accented w-1/5 rounded-2xl">
<div class="mt-24 w-1/4 bg-accented rounded-2xl"> <UNavigationMenu orientation="vertical" type="single" :items="items" class="p-4">
<button @click="openAll = !openAll">open all</button>
<UNavigationMenu arrow orientation="vertical" :items="items" class="p-4" :key="openAll">
</UNavigationMenu> </UNavigationMenu>
</div> </div>
<!-- <template #item>{{ item }}</template> -->
<TopBar/>
<slot></slot> <slot></slot>
</main> </main>
</div> </div>

View File

@@ -12,7 +12,7 @@ function isAuthenticated(): boolean {
await getSettings() await getSettings()
const routes = await getMenu() // await getMenu()
const router = createRouter({ const router = createRouter({

View File

@@ -1,16 +1,7 @@
import { useFetchJson } from "@/composable/useFetchJson"; import { useFetchJson } from "@/composable/useFetchJson";
import type { MenuItem, Route } from "@/types/menu";
export const getMenu = async () => { export const getMenu = async () => {
const resp = await useFetchJson<MenuItem>('/api/v1/restricted/menu/get-menu'); const resp = await useFetchJson('/api/v1/restricted/menu/get-menu');
return resp.items.children
}
export const getRoutes = async () => {
const resp = await useFetchJson<Route[]>('/api/v1/restricted/menu/get-routes');
return resp.items return resp.items

View File

@@ -1,26 +0,0 @@
export interface MenuItem {
category_id: number
label: string
params: Params
children: MenuItem[]
}
export interface Params {
category_id?: number
link_rewrite?: string
locale?: string
}
export interface Route {
ID: number
Name: string
Path: string
Component: string
Layout: string
Meta: string
IsActive: boolean
SortOrder: number
ParentID: any
Parent: any
Children: any
}

View File

@@ -1,6 +1,6 @@
-- +goose Up -- +goose Up
-- create routes table -- create routes table
CREATE TABLE IF NOT EXISTS b2b_routes ( CREATE TABLE IF NOT EXISTS b2b_tracker_routes (
id INT AUTO_INCREMENT PRIMARY KEY, id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL UNIQUE, name VARCHAR(255) NOT NULL UNIQUE,
path VARCHAR(255) NULL, path VARCHAR(255) NULL,
@@ -13,11 +13,11 @@ CREATE TABLE IF NOT EXISTS b2b_routes (
CONSTRAINT fk_parent CONSTRAINT fk_parent
FOREIGN KEY (parent_id) FOREIGN KEY (parent_id)
REFERENCES b2b_routes(id) REFERENCES b2b_tracker_routes(id)
ON DELETE SET NULL ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
INSERT IGNORE INTO b2b_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)
VALUES VALUES
('root', '', '', 'default', '{"trans": "route.root"}', 0, 0, 0), ('root', '', '', 'default', '{"trans": "route.root"}', 0, 0, 0),
@@ -30,5 +30,5 @@ VALUES
-- +goose Down -- +goose Down
DROP TABLE IF EXISTS b2b_routes; DROP TABLE IF EXISTS b2b_tracker_routes;