From 9336cdfa2859e3cc2bb170dce4f40f8719e30715 Mon Sep 17 00:00:00 2001 From: Marek Goc Date: Thu, 26 Mar 2026 18:39:13 +0100 Subject: [PATCH] fix top menu embeding struct --- .env | 2 +- app/config/config.go | 12 ++ .../web/api/restricted/meiliSearch.go | 40 +++-- app/model/productDescription.go | 4 +- app/model/topMenu.go | 20 +-- .../productDescriptionRepo.go | 80 ++++++++-- app/service/meiliService/meiliService.go | 147 ++++++++---------- app/service/menuService/menuService.go | 43 +++-- i18n/migrations/20260302163100_routes.sql | 26 +--- 9 files changed, 203 insertions(+), 171 deletions(-) diff --git a/.env b/.env index bc5aa8c..8f59da3 100644 --- a/.env +++ b/.env @@ -57,4 +57,4 @@ FILE_MAAL_PL_USER=git_operator FILE_MAAL_PL_PASSWORD=1FnwqcEgIUjQHjt1 IMAGE_PREFIX=https://www.naluconcept.com # remove prefix to serv them from same host as presta -CORS_ORGIN=https://www.naluconcept.com \ No newline at end of file +CORS_ORGIN=https://www.naluconcept.com diff --git a/app/config/config.go b/app/config/config.go index f68d2fd..586a182 100644 --- a/app/config/config.go +++ b/app/config/config.go @@ -24,6 +24,7 @@ type Config struct { GoogleTranslate GoogleTranslateConfig Image ImageConfig Cors CorsConfig + MailiSearch MeiliSearchConfig } type I18n struct { @@ -38,6 +39,11 @@ type CorsConfig struct { Origins []string `env:"CORS_ORGIN"` } +type MeiliSearchConfig struct { + ServerURL string `env:"MEILISEARCH_URL"` + ApiKey string `env:"MEILISEARCH_API_KEY"` +} + type ImageConfig struct { ImagePrefix string `env:"IMAGE_PREFIX"` } @@ -186,6 +192,12 @@ func load() *Config { if err != nil { slog.Error("not possible to load env variables for google translate : ", err.Error(), "") } + + err = loadEnv(&cfg.MailiSearch) + if err != nil { + slog.Error("not possible to load env variables for google translate : ", err.Error(), "") + } + return cfg } diff --git a/app/delivery/web/api/restricted/meiliSearch.go b/app/delivery/web/api/restricted/meiliSearch.go index b07ba38..675e53b 100644 --- a/app/delivery/web/api/restricted/meiliSearch.go +++ b/app/delivery/web/api/restricted/meiliSearch.go @@ -28,6 +28,7 @@ func MeiliSearchHandlerRoutes(r fiber.Router) fiber.Router { // for testing purposes only. Must be removed before proper release. r.Get("/create-index", handler.CreateIndex) r.Get("/test", handler.Test) + r.Get("/settings", handler.GetSettings) // for all users r.Get("/search", handler.Search) @@ -77,13 +78,6 @@ func (h *MeiliSearchHandler) Search(c fiber.Ctx) error { query := c.Query("query") - limit_attribute := c.Query("limit") - limit, err := strconv.Atoi(limit_attribute) - if err != nil { - return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)). - JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute))) - } - id_category_attribute := c.Query("id_category", "0") id_category, err := strconv.Atoi(id_category_attribute) if err != nil { @@ -91,21 +85,7 @@ func (h *MeiliSearchHandler) Search(c fiber.Ctx) error { JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute))) } - price_lower_bound_attribute := c.Query("price_lower_bound", "-1.0") - price_lower_bound, err := strconv.ParseFloat(price_lower_bound_attribute, 64) - if err != nil { - return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)). - JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute))) - } - - price_upper_bound_attribute := c.Query("price_upper_bound", "-1.0") - price_upper_bound, err := strconv.ParseFloat(price_upper_bound_attribute, 64) - if err != nil { - return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)). - JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute))) - } - - meili_response, err := h.meiliService.Search(id_lang, query, uint(limit), uint(id_category), price_lower_bound, price_upper_bound) + meili_response, err := h.meiliService.Search(id_lang, query, uint(id_category)) if err != nil { return c.Status(responseErrors.GetErrorStatus(err)). JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) @@ -113,3 +93,19 @@ func (h *MeiliSearchHandler) Search(c fiber.Ctx) error { return c.JSON(response.Make(&meili_response, 0, i18n.T_(c, response.Message_OK))) } + +func (h *MeiliSearchHandler) GetSettings(c fiber.Ctx) error { + id_lang, 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))) + } + + settings, err := h.meiliService.GetIndexSettings(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(&settings, 0, i18n.T_(c, response.Message_OK))) +} diff --git a/app/model/productDescription.go b/app/model/productDescription.go index 7bfa078..a2c7466 100644 --- a/app/model/productDescription.go +++ b/app/model/productDescription.go @@ -30,8 +30,6 @@ type ProductRow struct { 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:used_for"` @@ -44,4 +42,6 @@ type MeiliSearchProduct struct { CategoryID uint `gorm:"column:id_category"` CategoryName string `gorm:"column:category_name"` Variations uint `gorm:"column:variations"` + CategoryIDs []uint `gorm:"-"` // All category IDs including children for filtering + CoverImage string `gorm:"-"` // Cover image URL (not indexed) } diff --git a/app/model/topMenu.go b/app/model/topMenu.go index 9e82e9f..5147781 100644 --- a/app/model/topMenu.go +++ b/app/model/topMenu.go @@ -1,15 +1,17 @@ package model -type B2BTopMenu struct { - MenuID int `gorm:"column:menu_id;primaryKey;autoIncrement" json:"menu_id"` - Label string `gorm:"column:label;type:longtext;not null;default:'{}'" json:"label"` - ParentID *int `gorm:"column:parent_id;index:FK_b2b_top_menu_parent_id" json:"parent_id,omitempty"` - Params string `gorm:"column:params;type:longtext;not null;default:'{}'" json:"params"` - Active int8 `gorm:"column:active;type:tinyint;not null;default:1" json:"active"` - Position int `gorm:"column:position;not null;default:1" json:"position"` +import "encoding/json" - Parent *B2BTopMenu `gorm:"foreignKey:ParentID;references:MenuID;constraint:OnDelete:RESTRICT,OnUpdate:RESTRICT" json:"parent,omitempty"` - Children []B2BTopMenu `gorm:"foreignKey:ParentID" json:"children,omitempty"` +type B2BTopMenu struct { + MenuID int `gorm:"column:menu_id;primaryKey;autoIncrement" json:"menu_id"` + Label json.RawMessage `gorm:"column:label;type:longtext;not null;default:'{}'" json:"label"` + ParentID *int `gorm:"column:parent_id;index:FK_b2b_top_menu_parent_id" json:"parent_id,omitempty"` + Params string `gorm:"column:params;type:longtext;not null;default:'{}'" json:"params"` + Active int8 `gorm:"column:active;type:tinyint;not null;default:1" json:"active"` + Position int `gorm:"column:position;not null;default:1" json:"position"` + + Parent *B2BTopMenu `gorm:"foreignKey:ParentID;references:MenuID;constraint:OnDelete:RESTRICT,OnUpdate:RESTRICT" json:"parent,omitempty"` + Children []*B2BTopMenu `gorm:"foreignKey:ParentID" json:"children,omitempty"` } func (B2BTopMenu) TableName() string { diff --git a/app/repos/productDescriptionRepo/productDescriptionRepo.go b/app/repos/productDescriptionRepo/productDescriptionRepo.go index d97020a..8879779 100644 --- a/app/repos/productDescriptionRepo/productDescriptionRepo.go +++ b/app/repos/productDescriptionRepo/productDescriptionRepo.go @@ -79,36 +79,96 @@ func (r *ProductDescriptionRepo) UpdateFields(productID uint, productLangID uint func (r *ProductDescriptionRepo) GetMeiliProducts(id_lang uint) ([]model.MeiliSearchProduct, error) { var products []model.MeiliSearchProduct - err := db.DB. + err := db.DB.Debug(). + // Select(` + // ps.id_product AS id_product, + // pl.name AS name, + // ps.price AS price, + // pl.description AS description, + // pl.description_short AS description_short, + // pl.usage AS used_for, + // p.ean13 AS ean13, + // p.reference AS reference, + // p.width AS width, + // p.height AS height, + // p.depth AS depth, + // p.weight AS weight, + // ps.id_category_default AS id_category, + // cl.name AS category_name, + // COUNT(DISTINCT pas.id_product_attribute) AS variations + // `). + // 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"). + // Joins("LEFT JOIN ps_category_lang AS cl ON cl.id_category = ps.id_category_default AND cl.id_shop = ? AND cl.id_lang = ?", constdata.SHOP_ID, id_lang). + // Joins("LEFT JOIN ps_product_attribute_shop AS pas ON pas.id_product = ps.id_product AND pas.id_shop = ?", constdata.SHOP_ID). + // Where("ps.id_shop = ? AND ps.active = 1", constdata.SHOP_ID). + // Group("ps.id_product"). + 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, pl.usage AS used_for, p.ean13 AS ean13, p.reference AS reference, - p.width AS width, - p.height AS height, - p.depth AS depth, - p.weight AS weight, ps.id_category_default AS id_category, cl.name AS category_name, - COUNT(DISTINCT pas.id_product_attribute) AS variations + cl.link_rewrite as link_rewrite, + COUNT(DISTINCT pas.id_product_attribute) AS variations, + pis.id_image AS id_image, + GROUP_CONCAT(DISTINCT pcp.id_category) AS category_ids `). 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"). Joins("LEFT JOIN ps_category_lang AS cl ON cl.id_category = ps.id_category_default AND cl.id_shop = ? AND cl.id_lang = ?", constdata.SHOP_ID, id_lang). Joins("LEFT JOIN ps_product_attribute_shop AS pas ON pas.id_product = ps.id_product AND pas.id_shop = ?", constdata.SHOP_ID). - Where("ps.id_shop = ?", constdata.SHOP_ID). - Group("ps.id_product"). + Joins("JOIN ps_image_shop AS pis ON pis.id_product = ps.id_product AND pis.cover = 1"). + Joins("JOIN ps_category_product AS pcp ON pcp.id_product = ps.id_product"). + Where("ps.id_shop = ? AND ps.active = 1", constdata.SHOP_ID). + Group("pcp.id_product, ps.id_product"). Scan(&products).Error if err != nil { return products, fmt.Errorf("database error: %w", err) } + // Get all category IDs for each product (including child categories) + for i := range products { + var categoryIDs []uint + // Find all parent categories and their children using nested set + err := db.DB. + Table("ps_category AS c"). + Select("c.id_category"). + Joins("JOIN ps_category_product AS cp ON cp.id_category = c.id_category"). + Joins("JOIN ps_category AS parent ON c.nleft >= parent.nleft AND c.nright <= parent.nright AND parent.id_category = ?", products[i].CategoryID). + Where("cp.id_product = ?", products[i].ProductID). + Group("c.id_category"). + Pluck("c.id_category", &categoryIDs).Error + if err != nil { + continue // Skip if error, use default category + } + if len(categoryIDs) > 0 { + products[i].CategoryIDs = categoryIDs + } else { + products[i].CategoryIDs = []uint{products[i].CategoryID} + } + + // Get cover image for the product + var imageID int + err = db.DB. + Table("ps_image AS i"). + Select("i.id_image"). + Joins("LEFT JOIN ps_image_shop AS iss ON iss.id_image = i.id_image AND iss.id_shop = ?", constdata.SHOP_ID). + Where("i.id_product = ? AND (i.cover = 1 OR i.cover IS TRUE)", products[i].ProductID). + Order("i.position ASC"). + Limit(1). + Pluck("i.id_image", &imageID).Error + if err == nil && imageID > 0 { + products[i].CoverImage = fmt.Sprintf("%d/%d.jpg", products[i].ProductID, imageID) + } + } + return products, nil } diff --git a/app/service/meiliService/meiliService.go b/app/service/meiliService/meiliService.go index 182d3d4..1fdbe39 100644 --- a/app/service/meiliService/meiliService.go +++ b/app/service/meiliService/meiliService.go @@ -1,14 +1,13 @@ package meiliService import ( - "encoding/xml" "fmt" - "io" - "os" + "regexp" "strconv" "strings" "time" + "git.ma-al.com/goc_daniel/b2b/app/config" "git.ma-al.com/goc_daniel/b2b/app/repos/productDescriptionRepo" constdata "git.ma-al.com/goc_daniel/b2b/app/utils/const_data" "github.com/meilisearch/meilisearch-go" @@ -20,12 +19,10 @@ type MeiliService struct { } func New() *MeiliService { - meiliURL := os.Getenv("MEILISEARCH_URL") - meiliAPIKey := os.Getenv("MEILISEARCH_API_KEY") client := meilisearch.New( - meiliURL, - meilisearch.WithAPIKey(meiliAPIKey), + config.Get().MailiSearch.ServerURL, + meilisearch.WithAPIKey(config.Get().MailiSearch.ApiKey), ) return &MeiliService{ @@ -40,32 +37,9 @@ func (s *MeiliService) CreateIndex(id_lang uint) error { products, err := s.productDescriptionRepo.GetMeiliProducts(id_lang) for i := 0; i < len(products); i++ { - products[i].Description, err = cleanHTML(products[i].Description) - if err != nil { - fmt.Printf("products[i].Description: %v\n", products[i].Description) - fmt.Printf("products[i].ProductID: %v\n", products[i].ProductID) - fmt.Println("failed at description") - fmt.Printf("err: %v\n", err) - return err - } - - products[i].DescriptionShort, err = cleanHTML(products[i].DescriptionShort) - if err != nil { - fmt.Printf("products[i].DescriptionShort: %v\n", products[i].DescriptionShort) - fmt.Printf("products[i].ProductID: %v\n", products[i].ProductID) - fmt.Println("failed at description short") - fmt.Printf("err: %v\n", err) - return err - } - - products[i].Usage, err = cleanHTML(products[i].Usage) - if err != nil { - fmt.Printf("products[i].Usage: %v\n", products[i].Usage) - fmt.Printf("products[i].ProductID: %v\n", products[i].ProductID) - fmt.Println("failed at usage") - fmt.Printf("err: %v\n", err) - return err - } + products[i].Description = cleanHTML(products[i].Description) + products[i].DescriptionShort = cleanHTML(products[i].DescriptionShort) + products[i].Usage = cleanHTML(products[i].Usage) } primaryKey := "ProductID" @@ -83,7 +57,7 @@ func (s *MeiliService) CreateIndex(id_lang uint) error { filterableAttributes := []interface{}{ "CategoryID", - "Price", + "CategoryIDs", } task, err = s.meiliClient.Index(indexName).UpdateFilterableAttributes(&filterableAttributes) if err != nil { @@ -96,11 +70,10 @@ func (s *MeiliService) CreateIndex(id_lang uint) error { displayedAttributes := []string{ "ProductID", "Name", - "Active", - "Price", "EAN13", "Reference", "Variations", + "CoverImage", } task, err = s.meiliClient.Index(indexName).UpdateDisplayedAttributes(&displayedAttributes) if err != nil { @@ -154,28 +127,17 @@ func (s *MeiliService) Test(id_lang uint) (meilisearch.SearchResponse, error) { } // Search performs a full-text search on the specified index -func (s *MeiliService) Search(id_lang uint, query string, limit uint, id_category uint, price_lower_bound float64, price_upper_bound float64) (meilisearch.SearchResponse, error) { +func (s *MeiliService) Search(id_lang uint, query string, id_category uint) (meilisearch.SearchResponse, error) { indexName := "meili_products_shop" + strconv.FormatInt(constdata.SHOP_ID, 10) + "_lang" + strconv.FormatInt(int64(id_lang), 10) - filter_query := "" + filter_query := "Active = 1" if id_category != 0 { - filter_query = "CategoryID = " + strconv.FormatUint(uint64(id_category), 10) - } - if price_lower_bound > 0.0 { - if filter_query != "" { - filter_query += " AND " - } - filter_query += "Price >= " + strconv.FormatFloat(price_lower_bound, 'f', -1, 64) - } - if price_upper_bound > 0.0 { - if filter_query != "" { - filter_query += " AND " - } - filter_query += "Price <= " + strconv.FormatFloat(price_upper_bound, 'f', -1, 64) + // Use CategoryIDs to include products from child categories + filter_query += fmt.Sprintf(" AND CategoryIDs = %d", id_category) } searchReq := &meilisearch.SearchRequest{ - Limit: int64(limit), + Limit: 30, Facets: []string{ "CategoryID", }, @@ -201,40 +163,59 @@ func (s *MeiliService) HealthCheck() (*meilisearch.Health, error) { return health, nil } -// remove all tags from HTML text -func cleanHTML(s string) (string, error) { - r := strings.NewReader(s) - d := xml.NewDecoder(r) +// GetIndexSettings retrieves the current settings for a specific index +func (s *MeiliService) GetIndexSettings(id_lang uint) (map[string]interface{}, error) { + indexName := "meili_products_shop" + strconv.FormatInt(constdata.SHOP_ID, 10) + "_lang" + strconv.FormatInt(int64(id_lang), 10) - text := "" + index := s.meiliClient.Index(indexName) - // Configure the decoder for HTML; leave off strict and autoclose for XHTML - d.Strict = true - d.AutoClose = xml.HTMLAutoClose - d.Entity = xml.HTMLEntity - for { - token, err := d.Token() - if err == io.EOF { - break - } else if err != nil { - return text, err - } + result := make(map[string]interface{}) - switch v := token.(type) { - case xml.StartElement: - if len(text) > 0 && text[len(text)-1] != '\n' { - text += " \n " - } - case xml.EndElement: - case xml.CharData: - if strings.TrimSpace(string(v)) != "" { - text += string(v) - } - case xml.Comment: - case xml.ProcInst: - case xml.Directive: - } + // Get searchable attributes + searchable, err := index.GetSearchableAttributes() + if err == nil { + result["searchableAttributes"] = searchable } - return text, nil + // Get filterable attributes + filterable, err := index.GetFilterableAttributes() + if err == nil { + result["filterableAttributes"] = filterable + } + + // Get displayed attributes + displayed, err := index.GetDisplayedAttributes() + if err == nil { + result["displayedAttributes"] = displayed + } + + // Get ranking rules + ranking, err := index.GetRankingRules() + if err == nil { + result["rankingRules"] = ranking + } + + // Get distinct attribute + distinct, err := index.GetDistinctAttribute() + if err == nil && distinct != nil { + result["distinctAttribute"] = *distinct + } + + // Get typo tolerance + typo, err := index.GetTypoTolerance() + if err == nil { + result["typoTolerance"] = typo + } + + return result, nil +} + +// remove all tags from HTML text +func cleanHTML(s string) string { + // Simple regex to remove all HTML tags + re := regexp.MustCompile(`<[^>]*>`) + result := re.ReplaceAllString(s, "") + // Replace multiple spaces with single space + result = regexp.MustCompile(`\s+`).ReplaceAllString(result, " ") + return strings.TrimSpace(result) } diff --git a/app/service/menuService/menuService.go b/app/service/menuService/menuService.go index c21a660..e689ede 100644 --- a/app/service/menuService/menuService.go +++ b/app/service/menuService/menuService.go @@ -98,40 +98,37 @@ 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 } -func (s *MenuService) GetTopMenu(id uint) ([]model.B2BTopMenu, error) { +func (s *MenuService) GetTopMenu(id uint) ([]*model.B2BTopMenu, error) { items, err := s.routesRepo.GetTopMenu(id) if err != nil { return nil, err } - if len(items) == 0 { - return []model.B2BTopMenu{}, nil - } + menuMap := make(map[int]*model.B2BTopMenu, len(items)) + roots := make([]*model.B2BTopMenu, 0) - // Build a map with empty children slices - itemMap := make(map[int]model.B2BTopMenu, len(items)) for i := range items { - items[i].Children = []model.B2BTopMenu{} - itemMap[items[i].MenuID] = items[i] + menu := &items[i] + menu.Children = make([]*model.B2BTopMenu, 0) + menuMap[menu.MenuID] = menu } - // Build the tree - var roots []model.B2BTopMenu - for _, item := range itemMap { - if item.ParentID == nil || *item.ParentID == 0 { - roots = append(roots, itemMap[item.MenuID]) - } else { - parentID := *item.ParentID - if parent, exists := itemMap[parentID]; exists { - parent.Children = append(parent.Children, item) - itemMap[parentID] = parent - } + for i := range items { + menu := &items[i] + + if menu.ParentID == nil { + roots = append(roots, menu) + continue } - } - // Update roots with children - for i := range roots { - roots[i] = itemMap[roots[i].MenuID] + parent, ok := menuMap[*menu.ParentID] + if !ok { + // fallback for orphaned nodes + roots = append(roots, menu) + continue + } + + parent.Children = append(parent.Children, menu) } return roots, nil diff --git a/i18n/migrations/20260302163100_routes.sql b/i18n/migrations/20260302163100_routes.sql index 7414d85..c0f5a4f 100644 --- a/i18n/migrations/20260302163100_routes.sql +++ b/i18n/migrations/20260302163100_routes.sql @@ -43,32 +43,16 @@ CREATE TABLE IF NOT EXISTS b2b_top_menu ( INDEX FK_b2b_top_menu_parent_id_idx (parent_id ASC) ) ENGINE = InnoDB; -INSERT IGNORE INTO `b2b_top_menu` (`menu_id`, `label`, `parent_id`, `params`) -VALUES - -- ROOT - (1, JSON_COMPACT('{"name":"root","trans":{"pl":"Menu główne","en":"Main Menu","de":"Hauptmenü"}}'), NULL, '{}'), - -- LEVEL 1 - (2, JSON_COMPACT('{"name":"dashboard","trans":{"pl":"Panel","en":"Dashboard","de":"Dashboard"}}'), 1, '{}'), - (3, JSON_COMPACT('{"name":"orders","trans":{"pl":"Zamówienia","en":"Orders","de":"Bestellungen"}}'), 1, '{}'), - (4, JSON_COMPACT('{"name":"customers","trans":{"pl":"Klienci","en":"Customers","de":"Kunden"}}'), 1, '{}'), - (5, JSON_COMPACT('{"name":"products","trans":{"pl":"Produkty","en":"Products","de":"Produkte"}}'), 1, '{}'), - (6, JSON_COMPACT('{"name":"reports","trans":{"pl":"Raporty","en":"Reports","de":"Berichte"}}'), 1, '{}'), +INSERT INTO `b2b_top_menu` (`menu_id`, `label`, `parent_id`, `params`, `active`, `position`) VALUES +(1, JSON_COMPACT('{"name":"root","trans":{"pl":{"label":"Menu główne"},"en":{"label":"Main Menu"},"de":{"label":"Hauptmenü"}}}'),NULL,'{}',1,1), +(3, JSON_COMPACT('{"name":"admin-products","trans":{"pl":{"label":"admin-products"},"en":{"label":"admin-products"},"de":{"label":"admin-products"}}}'),1,'{}',1,1), +(9, JSON_COMPACT('{"name":"carts","trans":{"pl":{"label":"Koszyki"},"en":{"label":"Carts"},"de":{"label":"Warenkörbe"}}}'),3,'{}',1,1); - -- LEVEL 2 (Orders) - (7, JSON_COMPACT('{"name":"order_list","trans":{"pl":"Lista zamówień","en":"Order List","de":"Bestellliste"}}'), 3, '{}'), - (8, JSON_COMPACT('{"name":"pending_orders","trans":{"pl":"Oczekujące zamówienia","en":"Pending Orders","de":"Ausstehende Bestellungen"}}'), 3, '{}'), - (9, JSON_COMPACT('{"name":"carts","trans":{"pl":"Koszyki","en":"Carts","de":"Warenkörbe"}}'), 3, '{}'), - -- LEVEL 2 (Products) - (10, JSON_COMPACT('{"name":"product_list","trans":{"pl":"Lista produktów","en":"Product List","de":"Produktliste"}}'), 5, '{}'), - (11, JSON_COMPACT('{"name":"categories","trans":{"pl":"Kategorie","en":"Categories","de":"Kategorien"}}'), 5, '{}'), - (12, JSON_COMPACT('{"name":"inventory","trans":{"pl":"Magazyn","en":"Inventory","de":"Lagerbestand"}}'), 5, '{}'), - -- LEVEL 2 (Customers) - (13, JSON_COMPACT('{"name":"customer_list","trans":{"pl":"Lista klientów","en":"Customer List","de":"Kundenliste"}}'), 4, '{}'), - (14, JSON_COMPACT('{"name":"customer_groups","trans":{"pl":"Grupy klientów","en":"Customer Groups","de":"Kundengruppen"}}'), 4, '{}'); -- +goose Down DROP TABLE IF EXISTS b2b_routes; +DROP TABLE IF EXISTS b2b_top_menu;