From 0f494d3abd2339343c163190fe0821be45142f3c Mon Sep 17 00:00:00 2001
From: Marek Goc
Date: Mon, 23 Mar 2026 14:04:53 +0100
Subject: [PATCH] routing
---
app/delivery/web/api/restricted/menu.go | 25 +++-
app/delivery/web/init.go | 4 +
app/model/product.go | 29 ++--
app/model/routing.go | 21 +++
app/repos/categoriesRepo/categoriesRepo.go | 15 +--
app/repos/routesRepo/routesRepo.go | 25 ++++
app/service/menuService/menuService.go | 25 ++--
bo/components.d.ts | 1 +
bo/src/layouts/default.vue | 47 ++++++-
bo/src/layouts/empty.vue | 2 +-
bo/src/router/index.ts | 81 ++++++++---
bo/src/router/menu.ts | 17 +++
bo/src/types/menu.d.ts | 26 ++++
bo/src/views/CategoryView.vue | 21 +++
bo/src/views/LoginView.vue | 5 +-
bo/src/views/NotFoundView.vue | 7 +
bo/src/views/PasswordRecoveryView.vue | 119 ++++++++--------
bo/src/views/RegisterView.vue | 4 +
bo/src/views/ResetPasswordForm.vue | 147 ++++++++++----------
bo/src/views/VerifyEmailView.vue | 150 +++++++++++----------
i18n/migrations/20260302163100_routes.sql | 8 +-
21 files changed, 515 insertions(+), 264 deletions(-)
create mode 100644 app/model/routing.go
create mode 100644 app/repos/routesRepo/routesRepo.go
create mode 100644 bo/src/router/menu.ts
create mode 100644 bo/src/types/menu.d.ts
create mode 100644 bo/src/views/CategoryView.vue
create mode 100644 bo/src/views/NotFoundView.vue
diff --git a/app/delivery/web/api/restricted/menu.go b/app/delivery/web/api/restricted/menu.go
index d20cd45..74fa700 100644
--- a/app/delivery/web/api/restricted/menu.go
+++ b/app/delivery/web/api/restricted/menu.go
@@ -1,8 +1,6 @@
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"
@@ -26,18 +24,33 @@ func MenuHandlerRoutes(r fiber.Router) fiber.Router {
handler := NewMenuHandler()
r.Get("/get-menu", handler.GetMenu)
+ r.Get("/get-routes", handler.GetRouting)
return r
}
func (h *MenuHandler) GetMenu(c fiber.Ctx) error {
- id_lang, err := strconv.Atoi(c.Cookies("lang_id", "2"))
- if err != nil {
+ 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.GetMenu(uint(id_lang))
+ menu, err := h.menuService.GetMenu(lang_id)
+ 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)))
+}
+
+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 {
return c.Status(responseErrors.GetErrorStatus(err)).
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
diff --git a/app/delivery/web/init.go b/app/delivery/web/init.go
index bc61539..c18aaf0 100644
--- a/app/delivery/web/init.go
+++ b/app/delivery/web/init.go
@@ -110,6 +110,10 @@ func (s *Server) Setup() error {
meiliSearch := s.restricted.Group("/meili-search")
restricted.MeiliSearchHandlerRoutes(meiliSearch)
+ s.api.All("*", func(c fiber.Ctx) error {
+ return c.SendStatus(fiber.StatusNotFound)
+ })
+
// // Restricted routes example
// restricted := s.api.Group("/restricted")
// restricted.Use(middleware.AuthMiddleware())
diff --git a/app/model/product.go b/app/model/product.go
index eb63f68..3c8faae 100644
--- a/app/model/product.go
+++ b/app/model/product.go
@@ -82,18 +82,27 @@ type ProductFilters struct {
}
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"`
+ 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"`
+ LinkRewrite string `gorm:"column:link_rewrite"`
+ IsoCode string `gorm:"column:iso_code"`
}
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"`
+ CategoryID uint `json:"category_id" form:"category_id"`
+ Label string `json:"label" form:"label"`
+ // Active bool `json:"active" form:"active"`
+ Params CategpryParams `json:"params" form:"params"`
+ Children []Category `json:"children" form:"children"`
+}
+
+type CategpryParams struct {
+ CategoryID uint `json:"category_id" form:"category_id"`
+ LinkRewrite string `json:"link_rewrite" form:"link_rewrite"`
+ Locale string `json:"locale" form:"locale"`
}
type FeatVal = map[uint][]uint
diff --git a/app/model/routing.go b/app/model/routing.go
new file mode 100644
index 0000000..135f427
--- /dev/null
+++ b/app/model/routing.go
@@ -0,0 +1,21 @@
+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"
+}
diff --git a/app/repos/categoriesRepo/categoriesRepo.go b/app/repos/categoriesRepo/categoriesRepo.go
index 1713441..1556f59 100644
--- a/app/repos/categoriesRepo/categoriesRepo.go
+++ b/app/repos/categoriesRepo/categoriesRepo.go
@@ -26,15 +26,14 @@ func (repo *CategoriesRepo) GetAllCategories(id_lang uint) ([]model.ScannedCateg
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
+ ps_category.is_root_category AS is_root_category,
+ ps_category_lang.link_rewrite AS link_rewrite,
+ ps_lang.iso_code AS iso_code
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 = ?`,
+ 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 = ?
+ JOIN ps_lang ON ps_lang.id_lang = ps_category_lang.id_lang
+ `,
constdata.SHOP_ID, id_lang, constdata.SHOP_ID).
Scan(&allCategories).Error
diff --git a/app/repos/routesRepo/routesRepo.go b/app/repos/routesRepo/routesRepo.go
new file mode 100644
index 0000000..c70ba86
--- /dev/null
+++ b/app/repos/routesRepo/routesRepo.go
@@ -0,0 +1,25 @@
+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
+}
diff --git a/app/service/menuService/menuService.go b/app/service/menuService/menuService.go
index 13a6a89..f4b9994 100644
--- a/app/service/menuService/menuService.go
+++ b/app/service/menuService/menuService.go
@@ -5,23 +5,26 @@ import (
"git.ma-al.com/goc_daniel/b2b/app/model"
"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"
)
type MenuService struct {
categoriesRepo categoriesRepo.UICategoriesRepo
+ routesRepo routesRepo.UIRoutesRepo
}
func New() *MenuService {
return &MenuService{
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)
if err != nil {
- return model.Category{}, err
+ return &model.Category{}, err
}
// find the root
@@ -35,7 +38,7 @@ func (s *MenuService) GetMenu(id_lang uint) (model.Category, error) {
}
}
if !root_found {
- return model.Category{}, responseErrors.ErrNoRootFound
+ return &model.Category{}, responseErrors.ErrNoRootFound
}
// now create the children and reorder them according to position
@@ -57,25 +60,31 @@ func (s *MenuService) GetMenu(id_lang uint) (model.Category, error) {
// finally, create the tree
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 {
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))
+ node.Children = append(node.Children, s.createTree((*children_indices)[index][i].Index, all_categories, children_indices))
}
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 {
var normal model.Category
- normal.Active = scanned.Active
+ // normal.Active = scanned.Active
normal.CategoryID = scanned.CategoryID
- normal.Name = scanned.Name
- normal.Subcategories = []model.Category{}
+ normal.Label = scanned.Name
+ // normal.Active = scanned.Active == 1
+ normal.Params = model.CategpryParams{CategoryID: normal.CategoryID, LinkRewrite: scanned.LinkRewrite, Locale: scanned.IsoCode}
+ normal.Children = []model.Category{}
return normal
}
diff --git a/bo/components.d.ts b/bo/components.d.ts
index 622aa17..3425316 100644
--- a/bo/components.d.ts
+++ b/bo/components.d.ts
@@ -41,6 +41,7 @@ declare module 'vue' {
UInput: typeof import('./node_modules/@nuxt/ui/dist/runtime/components/Input.vue')['default']
UInputNumber: typeof import('./node_modules/@nuxt/ui/dist/runtime/components/InputNumber.vue')['default']
UModal: typeof import('./node_modules/@nuxt/ui/dist/runtime/components/Modal.vue')['default']
+ UNavigationMenu: typeof import('./node_modules/@nuxt/ui/dist/runtime/components/NavigationMenu.vue')['default']
UPagination: typeof import('./node_modules/@nuxt/ui/dist/runtime/components/Pagination.vue')['default']
USelect: typeof import('./node_modules/@nuxt/ui/dist/runtime/components/Select.vue')['default']
USelectMenu: typeof import('./node_modules/@nuxt/ui/dist/runtime/components/SelectMenu.vue')['default']
diff --git a/bo/src/layouts/default.vue b/bo/src/layouts/default.vue
index 1e86a0a..0705f3e 100644
--- a/bo/src/layouts/default.vue
+++ b/bo/src/layouts/default.vue
@@ -1,14 +1,51 @@
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
diff --git a/bo/src/layouts/empty.vue b/bo/src/layouts/empty.vue
index e8cfec0..e665e66 100644
--- a/bo/src/layouts/empty.vue
+++ b/bo/src/layouts/empty.vue
@@ -1,7 +1,7 @@
-
+
\ No newline at end of file
diff --git a/bo/src/views/LoginView.vue b/bo/src/views/LoginView.vue
index 20cec7b..f85d2c3 100644
--- a/bo/src/views/LoginView.vue
+++ b/bo/src/views/LoginView.vue
@@ -4,8 +4,8 @@ import { useRouter, useRoute } from 'vue-router'
import { useAuthStore } from '@/stores/auth'
import { useValidation } from '@/composable/useValidation'
import type { FormError } from '@nuxt/ui'
-import { useI18n } from 'vue-i18n'
import { i18n } from '@/plugins/02_i18n'
+import Empty from '@/layouts/empty.vue'
const router = useRouter()
const route = useRoute()
@@ -52,9 +52,11 @@ const PrivacyComponent = computed(() =>
import(`@/components/terms/${i18n.locale.value}_PrivacyPolicyView.vue`).catch(() => import('@/components/terms/en_PrivacyPolicyView.vue')),
),
)
+
+
@@ -163,4 +165,5 @@ const PrivacyComponent = computed(() =>
+
diff --git a/bo/src/views/NotFoundView.vue b/bo/src/views/NotFoundView.vue
new file mode 100644
index 0000000..4e3e4fc
--- /dev/null
+++ b/bo/src/views/NotFoundView.vue
@@ -0,0 +1,7 @@
+
+
+
404
+
Page not found
+
Go back home
+
+
\ No newline at end of file
diff --git a/bo/src/views/PasswordRecoveryView.vue b/bo/src/views/PasswordRecoveryView.vue
index dd8fada..265011c 100644
--- a/bo/src/views/PasswordRecoveryView.vue
+++ b/bo/src/views/PasswordRecoveryView.vue
@@ -5,6 +5,7 @@ import { useAuthStore } from '@/stores/auth'
import { useValidation } from '@/composable/useValidation'
import type { FormError } from '@nuxt/ui'
import { i18n } from '@/plugins/02_i18n'
+import Empty from '@/layouts/empty.vue'
const router = useRouter()
const authStore = useAuthStore()
@@ -36,70 +37,74 @@ function validate(): FormError[] {
-
-
-
-
+
+
-
+
-
-
-
-
{{ $t('general.check_your_email') }}
-
- {{ $t('general.password_reset_link_sent_notice') }}
-
-
- {{ $t('general.back_to_sign_in') }}
-
-
-
+
+
+
+
{{ $t('general.check_your_email') }}
+
+ {{ $t('general.password_reset_link_sent_notice') }}
+
+
+ {{ $t('general.back_to_sign_in') }}
+
+
+
-
-
-
- {{ $t('general.enter_email_for_password_reset') }}
-
-
+
+
+
+ {{ $t('general.enter_email_for_password_reset') }}
+
+
-
-
+
+
-
-
-
+
+
+
-
- {{ $t('general.send_password_reset_link') }}
-
-
+
+ {{ $t('general.send_password_reset_link') }}
+
+
-
-
-
- {{ $t('general.dont_have_an_account') }}
-
+
+
-
+
diff --git a/bo/src/views/RegisterView.vue b/bo/src/views/RegisterView.vue
index 71dc802..e329afc 100644
--- a/bo/src/views/RegisterView.vue
+++ b/bo/src/views/RegisterView.vue
@@ -1,4 +1,5 @@
+
@@ -110,6 +111,7 @@
+
-
-
-
-
-
-
-
-
- {{ $t('general.password_updated') }}
-
-
- {{ $t('general.password_updated_description') }}
-
-
- {{ $t('general.back_to_sign_in') }}
-
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ $t('general.reset_password') }}
-
-
-
-
-
+
+
+
+
+ {{ $t('general.password_updated') }}
+
+
+ {{ $t('general.password_updated_description') }}
+
+
{{ $t('general.back_to_sign_in') }}
-
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t('general.reset_password') }}
+
+
+
+
+
+ {{ $t('general.back_to_sign_in') }}
+
+
+
+
+
-
+
diff --git a/bo/src/views/VerifyEmailView.vue b/bo/src/views/VerifyEmailView.vue
index 11b594c..2aa281a 100644
--- a/bo/src/views/VerifyEmailView.vue
+++ b/bo/src/views/VerifyEmailView.vue
@@ -4,6 +4,7 @@ import { useRouter, useRoute } from 'vue-router'
import { useI18n } from 'vue-i18n'
import { useFetchJson } from '@/composable/useFetchJson'
import { i18n } from '@/plugins/02_i18n'
+import Empty from '@/layouts/empty.vue'
const { t, te } = useI18n()
const router = useRouter()
@@ -66,81 +67,84 @@ function goToLogin() {
-
-
-
-
-
-
+
+
+
+
+
-
TimeTracker
+
+
+
+
+
+
+
+ {{ $t('verify_email.verifying') }}
+
+
+
+
+
+
+
+ {{ $t('verify_email.success_title') }}
+
+
+ {{ $t('verify_email.success_message') }}
+
+
+
+
+
+
+
+ {{ $t('verify_email.error_title') }}
+
+
+ {{ $t('verify_email.error_message') }}
+
+
+
+
+
+
+
{{ $t('verify_email.redirect_message') }}
+
{{ $t('verify_email.go_to_login') }}
+
+
+
+ {{ $t('verify_email.go_to_login') }}
+
+
+
+
{{ $t('verify_email.please_wait') }}
+
+
+
+
+
+ {{ $t('verify_email.already_registered') }}
+ {{
+ $t('general.sign_in')
+ }}
+
+
+
+
+
-
-
-
-
-
-
-
- {{ $t('verify_email.verifying') }}
-
-
-
-
-
-
-
- {{ $t('verify_email.success_title') }}
-
-
- {{ $t('verify_email.success_message') }}
-
-
-
-
-
-
-
- {{ $t('verify_email.error_title') }}
-
-
- {{ $t('verify_email.error_message') }}
-
-
-
-
-
-
-
{{ $t('verify_email.redirect_message') }}
-
{{ $t('verify_email.go_to_login') }}
-
-
-
- {{ $t('verify_email.go_to_login') }}
-
-
-
-
{{ $t('verify_email.please_wait') }}
-
-
-
-
-
- {{ $t('verify_email.already_registered') }}
- {{ $t('general.sign_in')
- }}
-
-
-
-
-
-
+
diff --git a/i18n/migrations/20260302163100_routes.sql b/i18n/migrations/20260302163100_routes.sql
index a2f41a7..42904e4 100644
--- a/i18n/migrations/20260302163100_routes.sql
+++ b/i18n/migrations/20260302163100_routes.sql
@@ -1,6 +1,6 @@
-- +goose Up
-- create routes table
-CREATE TABLE IF NOT EXISTS b2b_tracker_routes (
+CREATE TABLE IF NOT EXISTS b2b_routes (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL UNIQUE,
path VARCHAR(255) NULL,
@@ -13,11 +13,11 @@ CREATE TABLE IF NOT EXISTS b2b_tracker_routes (
CONSTRAINT fk_parent
FOREIGN KEY (parent_id)
- REFERENCES b2b_tracker_routes(id)
+ REFERENCES b2b_routes(id)
ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-INSERT IGNORE INTO b2b_tracker_routes
+INSERT IGNORE INTO b2b_routes
(name, path, component, layout, meta, is_active, sort_order, parent_id)
VALUES
('root', '', '', 'default', '{"trans": "route.root"}', 0, 0, 0),
@@ -30,5 +30,5 @@ VALUES
-- +goose Down
-DROP TABLE IF EXISTS b2b_tracker_routes;
+DROP TABLE IF EXISTS b2b_routes;