Compare commits
25 Commits
52c17d7017
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bfc488bad8 | ||
|
|
f5f23f8a27 | ||
| cffe4c2f83 | |||
| 15e8626280 | |||
|
|
528f12b065 | ||
|
|
25ad592be3 | ||
|
|
26e6a3c384 | ||
| a4c1773415 | |||
| 8e07daac66 | |||
| 6408b93e5c | |||
| 27fa88b076 | |||
|
|
b67c4e3aef | ||
|
|
0d29d8f6a2 | ||
|
|
884e15bb8a | ||
|
|
1ea50af96a | ||
|
|
b6bf6ed5c6 | ||
| 7a66d6f429 | |||
| 43f856ee8d | |||
| 506c64e240 | |||
| 718b4d23f1 | |||
| 22e8556c9d | |||
| c79e08dbb8 | |||
| 789d59b0c9 | |||
| 7388d0f828 | |||
| e30088209e |
8
.env
8
.env
@@ -21,6 +21,13 @@ AUTH_JWT_SECRET=5c020e6ed3d8d6e67e5804d67c83c4bd5ae474df749af6d63d8f20e7e2ba29b3
|
||||
AUTH_JWT_EXPIRATION=86400
|
||||
AUTH_REFRESH_EXPIRATION=604800
|
||||
|
||||
# Meili search
|
||||
MEILISEARCH_URL=http://localhost:7700
|
||||
MEILISEARCH_API_KEY=e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
|
||||
|
||||
# OpenAI
|
||||
OPENAI_KEY=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
|
||||
|
||||
# Google Translate Client
|
||||
GOOGLE_APPLICATION_CREDENTIALS=./google-cred.json
|
||||
GOOGLE_CLOUD_PROJECT_ID=translation-343517
|
||||
@@ -29,6 +36,7 @@ GOOGLE_CLOUD_PROJECT_ID=translation-343517
|
||||
OAUTH_GOOGLE_CLIENT_ID=331979954218-9vrpe08oqhhcgj6bvu6d4lds0dt630m9.apps.googleusercontent.com
|
||||
OAUTH_GOOGLE_CLIENT_SECRET=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
|
||||
OAUTH_GOOGLE_REDIRECT_URL=http://localhost:3000/api/v1/public/auth/google/callback
|
||||
|
||||
# Email Configuration (SMTP)
|
||||
# Set EMAIL_ENABLED=true to require email verification
|
||||
EMAIL_ENABLED=true
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -5,3 +5,4 @@ bin/
|
||||
i18n/*.json
|
||||
*_templ.go
|
||||
tmp/main
|
||||
test.go
|
||||
2995
ADD_THIS_TO_SQL.sql
Normal file
2995
ADD_THIS_TO_SQL.sql
Normal file
File diff suppressed because it is too large
Load Diff
12
Taskfile.yml
12
Taskfile.yml
@@ -73,10 +73,22 @@ vars:
|
||||
MP_SMTP_AUTH_ALLOW_INSECURE: true
|
||||
MP_ENABLE_SPAMASSASSIN: postmark
|
||||
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:
|
||||
db_data:
|
||||
mailpit_data:
|
||||
meilisearch:
|
||||
|
||||
|
||||
includes:
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
package restricted
|
||||
|
||||
import (
|
||||
"git.ma-al.com/goc_daniel/b2b/app/service/langsAndCountriesService"
|
||||
"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"
|
||||
)
|
||||
|
||||
// LangsAndCountriesHandler for getting languages and countries data
|
||||
type LangsAndCountriesHandler struct {
|
||||
langsAndCountriesService *langsAndCountriesService.LangsAndCountriesService
|
||||
}
|
||||
|
||||
// NewLangsAndCountriesHandler creates a new LangsAndCountriesHandler instance
|
||||
func NewLangsAndCountriesHandler() *LangsAndCountriesHandler {
|
||||
langsAndCountriesService := langsAndCountriesService.New()
|
||||
return &LangsAndCountriesHandler{
|
||||
langsAndCountriesService: langsAndCountriesService,
|
||||
}
|
||||
}
|
||||
|
||||
func LangsAndCountriesHandlerRoutes(r fiber.Router) fiber.Router {
|
||||
handler := NewLangsAndCountriesHandler()
|
||||
|
||||
r.Get("/get-languages", handler.GetLanguages)
|
||||
r.Get("/get-countries", handler.GetCountries)
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func (h *LangsAndCountriesHandler) GetLanguages(c fiber.Ctx) error {
|
||||
languages, err := h.langsAndCountriesService.GetLanguages()
|
||||
if err != nil {
|
||||
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||
}
|
||||
|
||||
return c.JSON(response.Make(&languages, 0, i18n.T_(c, response.Message_OK)))
|
||||
}
|
||||
|
||||
func (h *LangsAndCountriesHandler) GetCountries(c fiber.Ctx) error {
|
||||
countries, err := h.langsAndCountriesService.GetCountriesAndCurrencies()
|
||||
if err != nil {
|
||||
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||
}
|
||||
|
||||
return c.JSON(response.Make(&countries, 0, i18n.T_(c, response.Message_OK)))
|
||||
}
|
||||
@@ -50,7 +50,13 @@ func (h *ListProductsHandler) GetListing(c fiber.Ctx) error {
|
||||
// "override_currency": c.Query("override_currency", ""),
|
||||
// }
|
||||
|
||||
listing, err := h.listProductsService.GetListing(paging, filters)
|
||||
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)))
|
||||
}
|
||||
|
||||
listing, err := h.listProductsService.GetListing(id_lang, paging, filters)
|
||||
if err != nil {
|
||||
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||
|
||||
52
app/delivery/web/api/restricted/localeSelector.go
Normal file
52
app/delivery/web/api/restricted/localeSelector.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package restricted
|
||||
|
||||
import (
|
||||
"git.ma-al.com/goc_daniel/b2b/app/service/localeSelectorService"
|
||||
"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"
|
||||
)
|
||||
|
||||
// LocaleSelectorHandler for getting languages and countries data
|
||||
type LocaleSelectorHandler struct {
|
||||
localeSelectorService *localeSelectorService.LocaleSelectorService
|
||||
}
|
||||
|
||||
// NewLocaleSelectorHandler creates a new LocaleSelectorHandler instance
|
||||
func NewLocaleSelectorHandler() *LocaleSelectorHandler {
|
||||
localeSelectorService := localeSelectorService.New()
|
||||
return &LocaleSelectorHandler{
|
||||
localeSelectorService: localeSelectorService,
|
||||
}
|
||||
}
|
||||
|
||||
func LocaleSelectorHandlerRoutes(r fiber.Router) fiber.Router {
|
||||
handler := NewLocaleSelectorHandler()
|
||||
|
||||
r.Get("/get-languages", handler.GetLanguages)
|
||||
r.Get("/get-countries", handler.GetCountries)
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func (h *LocaleSelectorHandler) GetLanguages(c fiber.Ctx) error {
|
||||
languages, err := h.localeSelectorService.GetLanguages()
|
||||
if err != nil {
|
||||
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||
}
|
||||
|
||||
return c.JSON(response.Make(&languages, 0, i18n.T_(c, response.Message_OK)))
|
||||
}
|
||||
|
||||
func (h *LocaleSelectorHandler) GetCountries(c fiber.Ctx) error {
|
||||
countries, err := h.localeSelectorService.GetCountriesAndCurrencies()
|
||||
if err != nil {
|
||||
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||
}
|
||||
|
||||
return c.JSON(response.Make(&countries, 0, i18n.T_(c, response.Message_OK)))
|
||||
}
|
||||
63
app/delivery/web/api/restricted/meiliSearch.go
Normal file
63
app/delivery/web/api/restricted/meiliSearch.go
Normal file
@@ -0,0 +1,63 @@
|
||||
package restricted
|
||||
|
||||
import (
|
||||
"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/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 MeiliSearchHandler struct {
|
||||
meiliService *meiliService.MeiliService
|
||||
}
|
||||
|
||||
func NewMeiliSearchHandler() *MeiliSearchHandler {
|
||||
meiliService := meiliService.New()
|
||||
return &MeiliSearchHandler{
|
||||
meiliService: meiliService,
|
||||
}
|
||||
}
|
||||
|
||||
func MeiliSearchHandlerRoutes(r fiber.Router) fiber.Router {
|
||||
handler := NewMeiliSearchHandler()
|
||||
|
||||
r.Get("/test", handler.Test)
|
||||
r.Get("/create-index", handler.CreateIndex)
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func (h *MeiliSearchHandler) CreateIndex(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)))
|
||||
}
|
||||
|
||||
err := h.meiliService.CreateIndex(id_lang)
|
||||
if err != nil {
|
||||
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||
}
|
||||
|
||||
nothing := ""
|
||||
return c.JSON(response.Make(¬hing, 0, i18n.T_(c, response.Message_OK)))
|
||||
}
|
||||
|
||||
func (h *MeiliSearchHandler) Test(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)))
|
||||
}
|
||||
|
||||
test, err := h.meiliService.Test(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(&test, 0, i18n.T_(c, response.Message_OK)))
|
||||
}
|
||||
45
app/delivery/web/api/restricted/menu.go
Normal file
45
app/delivery/web/api/restricted/menu.go
Normal file
@@ -0,0 +1,45 @@
|
||||
package restricted
|
||||
|
||||
import (
|
||||
"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_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)))
|
||||
}
|
||||
|
||||
menu, err := h.menuService.GetMenu(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)))
|
||||
}
|
||||
@@ -54,13 +54,6 @@ func (h *ProductDescriptionHandler) GetProductDescription(c fiber.Ctx) error {
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute)))
|
||||
}
|
||||
|
||||
productShopID_attribute := c.Query("productShopID")
|
||||
productShopID, err := strconv.Atoi(productShopID_attribute)
|
||||
if err != nil {
|
||||
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute)))
|
||||
}
|
||||
|
||||
productLangID_attribute := c.Query("productLangID")
|
||||
productLangID, err := strconv.Atoi(productLangID_attribute)
|
||||
if err != nil {
|
||||
@@ -68,7 +61,7 @@ func (h *ProductDescriptionHandler) GetProductDescription(c fiber.Ctx) error {
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute)))
|
||||
}
|
||||
|
||||
description, err := h.productDescriptionService.GetProductDescription(userID, uint(productID), uint(productShopID), uint(productLangID))
|
||||
description, err := h.productDescriptionService.GetProductDescription(userID, uint(productID), uint(productLangID))
|
||||
if err != nil {
|
||||
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||
@@ -77,7 +70,7 @@ func (h *ProductDescriptionHandler) GetProductDescription(c fiber.Ctx) error {
|
||||
return c.JSON(response.Make(description, 1, i18n.T_(c, response.Message_OK)))
|
||||
}
|
||||
|
||||
// SaveProductDescription saves the description for a given product ID, in given shop and language
|
||||
// SaveProductDescription saves the description for a given product ID, in given language
|
||||
func (h *ProductDescriptionHandler) SaveProductDescription(c fiber.Ctx) error {
|
||||
userID, ok := c.Locals("userID").(uint)
|
||||
if !ok {
|
||||
@@ -92,13 +85,6 @@ func (h *ProductDescriptionHandler) SaveProductDescription(c fiber.Ctx) error {
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute)))
|
||||
}
|
||||
|
||||
productShopID_attribute := c.Query("productShopID")
|
||||
productShopID, err := strconv.Atoi(productShopID_attribute)
|
||||
if err != nil {
|
||||
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute)))
|
||||
}
|
||||
|
||||
productLangID_attribute := c.Query("productLangID")
|
||||
productLangID, err := strconv.Atoi(productLangID_attribute)
|
||||
if err != nil {
|
||||
@@ -112,7 +98,7 @@ func (h *ProductDescriptionHandler) SaveProductDescription(c fiber.Ctx) error {
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrInvalidBody)))
|
||||
}
|
||||
|
||||
err = h.productDescriptionService.SaveProductDescription(userID, uint(productID), uint(productShopID), uint(productLangID), updates)
|
||||
err = h.productDescriptionService.SaveProductDescription(userID, uint(productID), uint(productLangID), updates)
|
||||
if err != nil {
|
||||
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||
@@ -136,13 +122,6 @@ func (h *ProductDescriptionHandler) TranslateProductDescription(c fiber.Ctx) err
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute)))
|
||||
}
|
||||
|
||||
productShopID_attribute := c.Query("productShopID")
|
||||
productShopID, err := strconv.Atoi(productShopID_attribute)
|
||||
if err != nil {
|
||||
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute)))
|
||||
}
|
||||
|
||||
productFromLangID_attribute := c.Query("productFromLangID")
|
||||
productFromLangID, err := strconv.Atoi(productFromLangID_attribute)
|
||||
if err != nil {
|
||||
@@ -163,7 +142,7 @@ func (h *ProductDescriptionHandler) TranslateProductDescription(c fiber.Ctx) err
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute)))
|
||||
}
|
||||
|
||||
description, err := h.productDescriptionService.TranslateProductDescription(userID, uint(productID), uint(productShopID), uint(productFromLangID), uint(productToLangID), aiModel)
|
||||
description, err := h.productDescriptionService.TranslateProductDescription(userID, uint(productID), uint(productFromLangID), uint(productToLangID), aiModel)
|
||||
if err != nil {
|
||||
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||
|
||||
@@ -97,10 +97,18 @@ func (s *Server) Setup() error {
|
||||
listProducts := s.restricted.Group("/list-products")
|
||||
restricted.ListProductsHandlerRoutes(listProducts)
|
||||
|
||||
// changing the JWT cookies routes (restricted)
|
||||
// in reality it just handles changing user's country and language
|
||||
langsAndCountries := s.restricted.Group("/langs-and-countries")
|
||||
restricted.LangsAndCountriesHandlerRoutes(langsAndCountries)
|
||||
// locale selector (restricted)
|
||||
// this is basically for changing user's selected language and country
|
||||
localeSelector := s.restricted.Group("/langs-and-countries")
|
||||
restricted.LocaleSelectorHandlerRoutes(localeSelector)
|
||||
|
||||
// menu (restricted)
|
||||
menu := s.restricted.Group("/menu")
|
||||
restricted.MenuHandlerRoutes(menu)
|
||||
|
||||
// meili search (restricted)
|
||||
meiliSearch := s.restricted.Group("/meili-search")
|
||||
restricted.MeiliSearchHandlerRoutes(meiliSearch)
|
||||
|
||||
// // Restricted routes example
|
||||
// restricted := s.api.Group("/restricted")
|
||||
|
||||
@@ -61,6 +61,13 @@ type Product struct {
|
||||
State uint `gorm:"column:state" json:"state" form:"state"`
|
||||
DeliveryDays uint `gorm:"column:delivery_days" json:"delivery_days" form:"delivery_days"`
|
||||
}
|
||||
type ProductInList struct {
|
||||
ProductID uint `gorm:"column:ID;primaryKey" json:"product_id" form:"product_id"`
|
||||
Name string `gorm:"column:name" json:"name" form:"name"`
|
||||
ImageID uint `gorm:"column:id_image"`
|
||||
LinkRewrite string `gorm:"column:link_rewrite"`
|
||||
Active uint `gorm:"column:active" json:"active" form:"active"`
|
||||
}
|
||||
|
||||
type ProductFilters struct {
|
||||
Sort string `json:"sort,omitempty" query:"sort,omitempty" example:"price,asc;name,desc"` // sort rule
|
||||
@@ -74,4 +81,28 @@ type ProductFilters struct {
|
||||
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"`
|
||||
LinkRewrite string `gorm:"column:link_rewrite"`
|
||||
IsoCode string `gorm:"column:iso_code"`
|
||||
}
|
||||
type Category struct {
|
||||
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
|
||||
|
||||
@@ -18,3 +18,27 @@ type ProductDescription struct {
|
||||
DeliveryOutStock string `gorm:"column:delivery_out_stock;type:varchar(255)" json:"delivery_out_stock" form:"delivery_out_stock"`
|
||||
Usage string `gorm:"column:usage;type:text" json:"usage" form:"usage"`
|
||||
}
|
||||
|
||||
type ProductRow struct {
|
||||
IDProduct int `gorm:"column:id_product"`
|
||||
IDShop int `gorm:"column:id_shop"`
|
||||
Name string `gorm:"column:name"`
|
||||
Active uint8 `gorm:"column:active"`
|
||||
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"`
|
||||
}
|
||||
|
||||
41
app/repos/categoriesRepo/categoriesRepo.go
Normal file
41
app/repos/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"
|
||||
constdata "git.ma-al.com/goc_daniel/b2b/app/utils/const_data"
|
||||
)
|
||||
|
||||
type UICategoriesRepo interface {
|
||||
GetAllCategories(id_lang uint) ([]model.ScannedCategory, error)
|
||||
}
|
||||
|
||||
type CategoriesRepo struct{}
|
||||
|
||||
func New() UICategoriesRepo {
|
||||
return &CategoriesRepo{}
|
||||
}
|
||||
|
||||
func (repo *CategoriesRepo) GetAllCategories(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,
|
||||
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 = ?
|
||||
JOIN ps_lang ON ps_lang.id_lang = ps_category_lang.id_lang
|
||||
`,
|
||||
constdata.SHOP_ID, id_lang, constdata.SHOP_ID).
|
||||
Scan(&allCategories).Error
|
||||
|
||||
return allCategories, err
|
||||
}
|
||||
82
app/repos/listProductsRepo/listProductsRepo.go
Normal file
82
app/repos/listProductsRepo/listProductsRepo.go
Normal file
@@ -0,0 +1,82 @@
|
||||
package listProductsRepo
|
||||
|
||||
import (
|
||||
"git.ma-al.com/goc_daniel/b2b/app/db"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/model"
|
||||
constdata "git.ma-al.com/goc_daniel/b2b/app/utils/const_data"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/query/filters"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/query/find"
|
||||
)
|
||||
|
||||
type UIListProductsRepo interface {
|
||||
GetListing(id_lang uint, p find.Paging, filt *filters.FiltersList) (find.Found[model.ProductInList], error)
|
||||
}
|
||||
|
||||
type ListProductsRepo struct{}
|
||||
|
||||
func New() UIListProductsRepo {
|
||||
return &ListProductsRepo{}
|
||||
}
|
||||
|
||||
func (repo *ListProductsRepo) GetListing(id_lang uint, p find.Paging, filt *filters.FiltersList) (find.Found[model.ProductInList], error) {
|
||||
var listing []model.ProductInList
|
||||
var total int64
|
||||
|
||||
// var resultIDs []uint
|
||||
// q := db.DB.
|
||||
// // SQL_CALC_FOUND_ROWS is a neat trick which works on MariaDB and
|
||||
// // MySQL. It works when followed by `SELECT FOUND_ROWS();`. To learn
|
||||
// // more see: https://mariarawmodel.com/kb/en/found_rows/
|
||||
// // WARN: This might not work on different SQL databases
|
||||
// Select("DISTINCT SQL_CALC_FOUND_ROWS id").
|
||||
// // Debug().
|
||||
// Scopes(view.FromDBViewForDisplay(langID, countryIso)).
|
||||
// Scopes(scopesForFiltersOnDisplay(db.DB, langID, countryIso, filt)).
|
||||
// Scopes(filt.OfCategory(filters.ORDER_FILTER)...).
|
||||
// Limit(p.Limit()).
|
||||
// Offset(p.Offset())
|
||||
|
||||
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 ?`,
|
||||
constdata.SHOP_ID, id_lang, constdata.SHOP_ID, p.Limit(), p.Offset()).
|
||||
Scan(&listing).Error
|
||||
if err != nil {
|
||||
return find.Found[model.ProductInList]{}, err
|
||||
}
|
||||
|
||||
err = db.DB.Raw(`
|
||||
SELECT COUNT(*)
|
||||
FROM ps_product`).
|
||||
Scan(&total).Error
|
||||
if err != nil {
|
||||
return find.Found[model.ProductInList]{}, err
|
||||
}
|
||||
|
||||
return find.Found[model.ProductInList]{
|
||||
Items: listing,
|
||||
Count: uint(total),
|
||||
}, nil
|
||||
}
|
||||
@@ -1,22 +1,22 @@
|
||||
package langsAndCountriesRepo
|
||||
package localeSelectorRepo
|
||||
|
||||
import (
|
||||
"git.ma-al.com/goc_daniel/b2b/app/db"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/model"
|
||||
)
|
||||
|
||||
type UILangsAndCountriesRepo interface {
|
||||
type UILocaleSelectorRepo interface {
|
||||
GetLanguages() ([]model.Language, error)
|
||||
GetCountriesAndCurrencies() ([]model.Country, error)
|
||||
}
|
||||
|
||||
type LangsAndCountriesRepo struct{}
|
||||
type LocaleSelectorRepo struct{}
|
||||
|
||||
func New() UILangsAndCountriesRepo {
|
||||
return &LangsAndCountriesRepo{}
|
||||
func New() UILocaleSelectorRepo {
|
||||
return &LocaleSelectorRepo{}
|
||||
}
|
||||
|
||||
func (repo *LangsAndCountriesRepo) GetLanguages() ([]model.Language, error) {
|
||||
func (repo *LocaleSelectorRepo) GetLanguages() ([]model.Language, error) {
|
||||
var languages []model.Language
|
||||
|
||||
err := db.DB.Table("b2b_language").Scan(&languages).Error
|
||||
@@ -24,7 +24,7 @@ func (repo *LangsAndCountriesRepo) GetLanguages() ([]model.Language, error) {
|
||||
return languages, err
|
||||
}
|
||||
|
||||
func (repo *LangsAndCountriesRepo) GetCountriesAndCurrencies() ([]model.Country, error) {
|
||||
func (repo *LocaleSelectorRepo) GetCountriesAndCurrencies() ([]model.Country, error) {
|
||||
var countries []model.Country
|
||||
|
||||
err := db.DB.Table("b2b_countries").
|
||||
108
app/repos/productDescriptionRepo/productDescriptionRepo.go
Normal file
108
app/repos/productDescriptionRepo/productDescriptionRepo.go
Normal file
@@ -0,0 +1,108 @@
|
||||
package productDescriptionRepo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.ma-al.com/goc_daniel/b2b/app/db"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/model"
|
||||
constdata "git.ma-al.com/goc_daniel/b2b/app/utils/const_data"
|
||||
)
|
||||
|
||||
type UIProductDescriptionRepo interface {
|
||||
GetProductDescription(productID uint, productLangID uint) (*model.ProductDescription, error)
|
||||
CreateIfDoesNotExist(productID uint, productLangID uint) error
|
||||
UpdateFields(productID uint, productLangID uint, updates map[string]string) error
|
||||
GetMeiliProducts(id_lang uint) ([]model.MeiliSearchProduct, error)
|
||||
}
|
||||
|
||||
type ProductDescriptionRepo struct{}
|
||||
|
||||
func New() UIProductDescriptionRepo {
|
||||
return &ProductDescriptionRepo{}
|
||||
}
|
||||
|
||||
// We assume that any user has access to all product descriptions
|
||||
func (r *ProductDescriptionRepo) GetProductDescription(productID uint, productLangID uint) (*model.ProductDescription, error) {
|
||||
var ProductDescription model.ProductDescription
|
||||
|
||||
err := db.DB.
|
||||
Table("ps_product_lang").
|
||||
Where("id_product = ? AND id_shop = ? AND id_lang = ?", productID, constdata.SHOP_ID, productLangID).
|
||||
First(&ProductDescription).Error
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("database error: %w", err)
|
||||
}
|
||||
|
||||
return &ProductDescription, nil
|
||||
}
|
||||
|
||||
// If it doesn't exist, returns an error.
|
||||
func (r *ProductDescriptionRepo) CreateIfDoesNotExist(productID uint, productLangID uint) error {
|
||||
record := model.ProductDescription{
|
||||
ProductID: productID,
|
||||
ShopID: constdata.SHOP_ID,
|
||||
LangID: productLangID,
|
||||
}
|
||||
|
||||
err := db.DB.
|
||||
Table("ps_product_lang").
|
||||
Where("id_product = ? AND id_shop = ? AND id_lang = ?", productID, constdata.SHOP_ID, productLangID).
|
||||
FirstOrCreate(&record).Error
|
||||
if err != nil {
|
||||
return fmt.Errorf("database error: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *ProductDescriptionRepo) UpdateFields(productID uint, productLangID uint, updates map[string]string) error {
|
||||
if len(updates) == 0 {
|
||||
return nil
|
||||
}
|
||||
updatesIface := make(map[string]interface{}, len(updates))
|
||||
for k, v := range updates {
|
||||
updatesIface[k] = v
|
||||
}
|
||||
|
||||
err := db.DB.
|
||||
Table("ps_product_lang").
|
||||
Where("id_product = ? AND id_shop = ? AND id_lang = ?", productID, constdata.SHOP_ID, productLangID).
|
||||
Updates(updatesIface).Error
|
||||
if err != nil {
|
||||
return fmt.Errorf("database error: %w", err)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
@@ -610,10 +610,10 @@ func (s *AuthService) GetLangISOCode(langID uint) (string, error) {
|
||||
var lang string
|
||||
|
||||
if langID == 0 { // retrieve the default lang
|
||||
err := db.DB.Table("b2b_language").Where("is_default = ?", 1).First(lang).Error
|
||||
err := db.DB.Table("b2b_language").Where("is_default = ?", 1).Select("iso_code").Scan(&lang).Error
|
||||
return lang, err
|
||||
} else {
|
||||
err := db.DB.Table("b2b_language").Where("id = ?", langID).Where("active = ?", 1).First(lang).Error
|
||||
err := db.DB.Table("b2b_language").Where("id = ?", langID).Where("active = ?", 1).Select("iso_code").Scan(&lang).Error
|
||||
return lang, err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
package langsAndCountriesService
|
||||
|
||||
import (
|
||||
"git.ma-al.com/goc_daniel/b2b/app/model"
|
||||
"git.ma-al.com/goc_daniel/b2b/repository/langsAndCountriesRepo"
|
||||
)
|
||||
|
||||
// LangsAndCountriesService literally sends back language and countries information.
|
||||
type LangsAndCountriesService struct {
|
||||
repo langsAndCountriesRepo.UILangsAndCountriesRepo
|
||||
}
|
||||
|
||||
// NewLangsAndCountriesService creates a new LangsAndCountries service
|
||||
func New() *LangsAndCountriesService {
|
||||
return &LangsAndCountriesService{
|
||||
repo: langsAndCountriesRepo.New(),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *LangsAndCountriesService) GetLanguages() ([]model.Language, error) {
|
||||
return s.repo.GetLanguages()
|
||||
}
|
||||
|
||||
func (s *LangsAndCountriesService) GetCountriesAndCurrencies() ([]model.Country, error) {
|
||||
return s.repo.GetCountriesAndCurrencies()
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
package langsService
|
||||
|
||||
import (
|
||||
langs_repo "git.ma-al.com/goc_daniel/b2b/app/langs"
|
||||
langs_repo "git.ma-al.com/goc_daniel/b2b/app/repos/langsRepo"
|
||||
"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"
|
||||
|
||||
@@ -2,9 +2,9 @@ package listProductsService
|
||||
|
||||
import (
|
||||
"git.ma-al.com/goc_daniel/b2b/app/model"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/repos/listProductsRepo"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/query/filters"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/query/find"
|
||||
"git.ma-al.com/goc_daniel/b2b/repository/listProductsRepo"
|
||||
)
|
||||
|
||||
type ListProductsService struct {
|
||||
@@ -17,8 +17,8 @@ func New() *ListProductsService {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ListProductsService) GetListing(p find.Paging, filters *filters.FiltersList) (find.Found[model.Product], error) {
|
||||
var products find.Found[model.Product]
|
||||
func (s *ListProductsService) GetListing(id_lang uint, p find.Paging, filters *filters.FiltersList) (find.Found[model.ProductInList], error) {
|
||||
var products find.Found[model.ProductInList]
|
||||
|
||||
// currencyIso := c.Cookies("currency_iso", "")
|
||||
// countryIso := c.Cookies("country_iso", "")
|
||||
@@ -30,7 +30,7 @@ func (s *ListProductsService) GetListing(p find.Paging, filters *filters.Filters
|
||||
// countryIso = overrides["override_country"]
|
||||
// }
|
||||
|
||||
products, err := s.listProductsRepo.GetListing(p, filters)
|
||||
products, err := s.listProductsRepo.GetListing(id_lang, p, filters)
|
||||
if err != nil {
|
||||
return products, err
|
||||
}
|
||||
|
||||
26
app/service/localeSelectorService/localeSelectorService.go
Normal file
26
app/service/localeSelectorService/localeSelectorService.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package localeSelectorService
|
||||
|
||||
import (
|
||||
"git.ma-al.com/goc_daniel/b2b/app/model"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/repos/localeSelectorRepo"
|
||||
)
|
||||
|
||||
// LocaleSelectorService literally sends back language and countries information.
|
||||
type LocaleSelectorService struct {
|
||||
repo localeSelectorRepo.UILocaleSelectorRepo
|
||||
}
|
||||
|
||||
// NewLocaleSelectorService creates a new LocaleSelector service
|
||||
func New() *LocaleSelectorService {
|
||||
return &LocaleSelectorService{
|
||||
repo: localeSelectorRepo.New(),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *LocaleSelectorService) GetLanguages() ([]model.Language, error) {
|
||||
return s.repo.GetLanguages()
|
||||
}
|
||||
|
||||
func (s *LocaleSelectorService) GetCountriesAndCurrencies() ([]model.Country, error) {
|
||||
return s.repo.GetCountriesAndCurrencies()
|
||||
}
|
||||
8
app/service/meiliService/blank.json
Normal file
8
app/service/meiliService/blank.json
Normal 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}}"
|
||||
}
|
||||
}
|
||||
170
app/service/meiliService/meiliService.go
Normal file
170
app/service/meiliService/meiliService.go
Normal file
@@ -0,0 +1,170 @@
|
||||
package meiliService
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
type MeiliService struct {
|
||||
productDescriptionRepo productDescriptionRepo.UIProductDescriptionRepo
|
||||
meiliClient meilisearch.ServiceManager
|
||||
}
|
||||
|
||||
func New() *MeiliService {
|
||||
meiliURL := os.Getenv("MEILISEARCH_URL")
|
||||
meiliAPIKey := os.Getenv("MEILISEARCH_API_KEY")
|
||||
|
||||
client := meilisearch.New(
|
||||
meiliURL,
|
||||
meilisearch.WithAPIKey(meiliAPIKey),
|
||||
)
|
||||
|
||||
return &MeiliService{
|
||||
meiliClient: client,
|
||||
productDescriptionRepo: productDescriptionRepo.New(),
|
||||
}
|
||||
}
|
||||
|
||||
// ==================================== FOR SUPERADMIN ONLY ====================================
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
indexName := "meili_products_shop" + strconv.FormatInt(constdata.SHOP_ID, 10) + "_lang" + strconv.FormatInt(int64(id_lang), 10)
|
||||
primaryKey := "ProductID"
|
||||
docOptions := &meilisearch.DocumentOptions{
|
||||
PrimaryKey: &primaryKey,
|
||||
SkipCreation: false,
|
||||
}
|
||||
|
||||
task, err := s.meiliClient.Index(indexName).AddDocuments(products, docOptions)
|
||||
if err != nil {
|
||||
return fmt.Errorf("meili AddDocuments error: %w", err)
|
||||
}
|
||||
|
||||
finishedTask, err := s.meiliClient.WaitForTask(task.TaskUID, 500*time.Millisecond)
|
||||
fmt.Printf("Task status: %s\n", finishedTask.Status)
|
||||
fmt.Printf("Task error: %s\n", finishedTask.Error)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// ==================================== FOR DEBUG ONLY ====================================
|
||||
func (s *MeiliService) Test(id_lang uint) (meilisearch.SearchResponse, error) {
|
||||
indexName := "meili_products_shop" + strconv.FormatInt(constdata.SHOP_ID, 10) + "_lang" + strconv.FormatInt(int64(id_lang), 10)
|
||||
|
||||
searchReq := &meilisearch.SearchRequest{
|
||||
Limit: 3,
|
||||
}
|
||||
|
||||
// Perform search
|
||||
results, err := s.meiliClient.Index(indexName).Search("walek", searchReq)
|
||||
if err != nil {
|
||||
fmt.Printf("Meilisearch error: %v\n", err)
|
||||
return meilisearch.SearchResponse{}, err
|
||||
}
|
||||
|
||||
fmt.Printf("Search results for query 'walek' in %s: %d hits\n", indexName, len(results.Hits))
|
||||
|
||||
return *results, nil
|
||||
}
|
||||
|
||||
// Search performs a full-text search on the specified index
|
||||
func (s *MeiliService) Search(indexName string, query string, limit int) (meilisearch.SearchResponse, error) {
|
||||
searchReq := &meilisearch.SearchRequest{
|
||||
Limit: int64(limit),
|
||||
}
|
||||
|
||||
results, err := s.meiliClient.Index(indexName).Search(query, searchReq)
|
||||
if err != nil {
|
||||
fmt.Printf("Meilisearch search error: %v\n", err)
|
||||
return meilisearch.SearchResponse{}, err
|
||||
}
|
||||
|
||||
return *results, nil
|
||||
}
|
||||
|
||||
// HealthCheck checks if Meilisearch is healthy and accessible
|
||||
func (s *MeiliService) HealthCheck() (*meilisearch.Health, error) {
|
||||
health, err := s.meiliClient.Health()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("meilisearch health check failed: %w", err)
|
||||
}
|
||||
|
||||
return health, nil
|
||||
}
|
||||
|
||||
// remove all tags from HTML text
|
||||
func cleanHTML(s string) (string, error) {
|
||||
r := strings.NewReader(s)
|
||||
d := xml.NewDecoder(r)
|
||||
|
||||
text := ""
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
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:
|
||||
}
|
||||
}
|
||||
|
||||
return text, nil
|
||||
}
|
||||
92
app/service/menuService/menuService.go
Normal file
92
app/service/menuService/menuService.go
Normal file
@@ -0,0 +1,92 @@
|
||||
package menuService
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
"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/utils/responseErrors"
|
||||
)
|
||||
|
||||
type MenuService struct {
|
||||
categoriesRepo categoriesRepo.UICategoriesRepo
|
||||
}
|
||||
|
||||
func New() *MenuService {
|
||||
return &MenuService{
|
||||
categoriesRepo: categoriesRepo.New(),
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// 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.Children = append(node.Children, 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.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
|
||||
}
|
||||
|
||||
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 }
|
||||
@@ -17,9 +17,9 @@ import (
|
||||
"cloud.google.com/go/translate/apiv3/translatepb"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/config"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/model"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/repos/productDescriptionRepo"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/service/langsService"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/responseErrors"
|
||||
"git.ma-al.com/goc_daniel/b2b/repository/productDescriptionRepo"
|
||||
"github.com/openai/openai-go/v3"
|
||||
"github.com/openai/openai-go/v3/option"
|
||||
"github.com/openai/openai-go/v3/responses"
|
||||
@@ -70,7 +70,7 @@ func New() *ProductDescriptionService {
|
||||
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
|
||||
|
||||
return &ProductDescriptionService{
|
||||
@@ -82,12 +82,12 @@ func New() *ProductDescriptionService {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ProductDescriptionService) GetProductDescription(userID uint, productID uint, productShopID uint, productLangID uint) (*model.ProductDescription, error) {
|
||||
return s.productDescriptionRepo.GetProductDescription(productID, productShopID, productLangID)
|
||||
func (s *ProductDescriptionService) GetProductDescription(userID uint, productID uint, productLangID uint) (*model.ProductDescription, error) {
|
||||
return s.productDescriptionRepo.GetProductDescription(productID, productLangID)
|
||||
}
|
||||
|
||||
// Updates relevant fields with the "updates" map
|
||||
func (s *ProductDescriptionService) SaveProductDescription(userID uint, productID uint, productShopID uint, productLangID uint, updates map[string]string) error {
|
||||
func (s *ProductDescriptionService) SaveProductDescription(userID uint, productID uint, productLangID uint, updates map[string]string) error {
|
||||
// only some fields can be affected
|
||||
allowedFields := []string{"description", "description_short", "meta_description", "meta_title", "name", "available_now", "available_later", "usage"}
|
||||
for key := range updates {
|
||||
@@ -106,12 +106,12 @@ func (s *ProductDescriptionService) SaveProductDescription(userID uint, productI
|
||||
}
|
||||
}
|
||||
|
||||
err := s.productDescriptionRepo.CreateIfDoesNotExist(productID, productShopID, productLangID)
|
||||
err := s.productDescriptionRepo.CreateIfDoesNotExist(productID, productLangID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return s.productDescriptionRepo.UpdateFields(productID, productShopID, productLangID, updates)
|
||||
return s.productDescriptionRepo.UpdateFields(productID, productLangID, updates)
|
||||
}
|
||||
|
||||
// TranslateProductDescription fetches the product description for productFromLangID,
|
||||
@@ -120,9 +120,9 @@ func (s *ProductDescriptionService) SaveProductDescription(userID uint, productI
|
||||
//
|
||||
// The Google Cloud project must have the Cloud Translation API enabled and the
|
||||
// service account must hold the "Cloud Translation API User" role.
|
||||
func (s *ProductDescriptionService) TranslateProductDescription(userID uint, productID uint, productShopID uint, productFromLangID uint, productToLangID uint, aiModel string) (*model.ProductDescription, error) {
|
||||
func (s *ProductDescriptionService) TranslateProductDescription(userID uint, productID uint, productFromLangID uint, productToLangID uint, aiModel string) (*model.ProductDescription, error) {
|
||||
|
||||
productDescription, err := s.productDescriptionRepo.GetProductDescription(productID, productShopID, productFromLangID)
|
||||
productDescription, err := s.productDescriptionRepo.GetProductDescription(productID, productFromLangID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -2,3 +2,4 @@ package constdata
|
||||
|
||||
// PASSWORD_VALIDATION_REGEX is used by the frontend (JavaScript supports lookaheads).
|
||||
const PASSWORD_VALIDATION_REGEX = `^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{10,}$`
|
||||
const SHOP_ID = 1
|
||||
|
||||
@@ -48,6 +48,9 @@ var (
|
||||
|
||||
// Typed errors for product list handler
|
||||
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
|
||||
@@ -135,6 +138,9 @@ func GetErrorCode(c fiber.Ctx, err error) string {
|
||||
case errors.Is(err, ErrBadPaging):
|
||||
return i18n.T_(c, "error.err_bad_paging")
|
||||
|
||||
case errors.Is(err, ErrNoRootFound):
|
||||
return i18n.T_(c, "error.no_root_found")
|
||||
|
||||
default:
|
||||
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, ErrBadField),
|
||||
errors.Is(err, ErrInvalidXHTML),
|
||||
errors.Is(err, ErrBadPaging):
|
||||
errors.Is(err, ErrBadPaging),
|
||||
errors.Is(err, ErrNoRootFound):
|
||||
return fiber.StatusBadRequest
|
||||
case errors.Is(err, ErrEmailExists):
|
||||
return fiber.StatusConflict
|
||||
|
||||
10
bo/components.d.ts
vendored
10
bo/components.d.ts
vendored
@@ -16,8 +16,15 @@ declare module 'vue' {
|
||||
En_PrivacyPolicyView: typeof import('./src/components/terms/en_PrivacyPolicyView.vue')['default']
|
||||
En_TermsAndConditionsView: typeof import('./src/components/terms/en_TermsAndConditionsView.vue')['default']
|
||||
LangSwitch: typeof import('./src/components/inner/langSwitch.vue')['default']
|
||||
PageAddresses: typeof import('./src/components/customer/PageAddresses.vue')['default']
|
||||
PageCart: typeof import('./src/components/customer/PageCart.vue')['default']
|
||||
PageProductCardFull: typeof import('./src/components/customer/PageProductCardFull.vue')['default']
|
||||
Pl_PrivacyPolicyView: typeof import('./src/components/terms/pl_PrivacyPolicyView.vue')['default']
|
||||
Pl_TermsAndConditionsView: typeof import('./src/components/terms/pl_TermsAndConditionsView.vue')['default']
|
||||
ProductCustomization: typeof import('./src/components/customer/components/ProductCustomization.vue')['default']
|
||||
ProductDetailView: typeof import('./src/components/admin/ProductDetailView.vue')['default']
|
||||
ProductsView: typeof import('./src/components/admin/ProductsView.vue')['default']
|
||||
ProductVariants: typeof import('./src/components/customer/components/ProductVariants.vue')['default']
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
RouterView: typeof import('vue-router')['RouterView']
|
||||
ThemeSwitch: typeof import('./src/components/inner/themeSwitch.vue')['default']
|
||||
@@ -32,6 +39,9 @@ declare module 'vue' {
|
||||
UFormField: typeof import('./node_modules/@nuxt/ui/dist/runtime/components/FormField.vue')['default']
|
||||
UIcon: typeof import('./node_modules/@nuxt/ui/dist/runtime/vue/components/Icon.vue')['default']
|
||||
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']
|
||||
|
||||
@@ -2,10 +2,6 @@ import type { NuxtUIOptions } from '@nuxt/ui/unplugin'
|
||||
|
||||
export const uiOptions: NuxtUIOptions = {
|
||||
ui: {
|
||||
colors: {
|
||||
primary: 'blue',
|
||||
neutral: 'zink',
|
||||
},
|
||||
pagination: {
|
||||
slots: {
|
||||
root: '',
|
||||
@@ -22,6 +18,13 @@ export const uiOptions: NuxtUIOptions = {
|
||||
error: 'text-red-600!'
|
||||
},
|
||||
},
|
||||
inputNumber: {
|
||||
slots: {
|
||||
base: 'text-(--black) dark:text-white border! border-(--border-light)! dark:border-(--border-dark)! outline-0! ring-0! pt-2 px-1! w-auto!',
|
||||
increment: 'border-0! pe-0! ps-0!',
|
||||
decrement: 'border-0! pe-0! ps-0!'
|
||||
},
|
||||
},
|
||||
select: {
|
||||
slots: {
|
||||
base: 'w-full! cursor-pointer border! border-(--border-light)! dark:border-(--border-dark)! outline-0! ring-0!',
|
||||
@@ -49,6 +52,12 @@ export const uiOptions: NuxtUIOptions = {
|
||||
tr: 'border-b! border-(--border-light)! dark:border-(--border-dark)! outline-0! ring-0! text-(--black)! dark:text-white!',
|
||||
}
|
||||
|
||||
},
|
||||
modal: {
|
||||
slots: {
|
||||
content: 'border! border-(--border-light)! dark:border-(--border-dark)! outline-0! ring-0! bg-(--second-light) dark:bg-(--main-dark)',
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@ const authStore = useAuthStore()
|
||||
<template>
|
||||
<header
|
||||
class="fixed top-0 left-0 right-0 z-50 bg-white/80 dark:bg-(--black) backdrop-blur-md border-b border-(--border-light) dark:border-(--border-dark)">
|
||||
<div class="container px-4 sm:px-6 lg:px-8">
|
||||
<div class="container mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div class="flex items-center justify-between h-14">
|
||||
<!-- Logo -->
|
||||
<RouterLink :to="{ name: 'home' }" class="flex items-center gap-2">
|
||||
@@ -22,6 +22,15 @@ const authStore = useAuthStore()
|
||||
<RouterLink :to="{ name: 'product-detail' }">
|
||||
product detail
|
||||
</RouterLink>
|
||||
<RouterLink :to="{ name: 'product-card-full' }">
|
||||
ProductCardFull
|
||||
</RouterLink>
|
||||
<RouterLink :to="{ name: 'addresses' }">
|
||||
Addresses
|
||||
</RouterLink>
|
||||
<RouterLink :to="{ name: 'cart' }">
|
||||
Cart
|
||||
</RouterLink>
|
||||
<div class="flex items-center gap-2">
|
||||
<!-- Language Switcher -->
|
||||
<LangSwitch />
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<script setup lang="ts">
|
||||
import ProductsView from '@/views/customer/ProductsView.vue';
|
||||
import LangSwitch from './inner/langSwitch.vue'
|
||||
import ThemeSwitch from './inner/themeSwitch.vue'
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
|
||||
222
bo/src/components/admin/ProductDetailView.vue
Normal file
222
bo/src/components/admin/ProductDetailView.vue
Normal file
@@ -0,0 +1,222 @@
|
||||
<template>
|
||||
<div class="container my-10 mx-auto ">
|
||||
|
||||
<div
|
||||
class="flex items-end justify-between gap-4 mb-6 bg-(--second-light) dark:bg-(--main-dark) border border-(--border-light) dark:border-(--border-dark) p-4 rounded-md">
|
||||
<div class="flex items-end gap-3">
|
||||
<USelect v-model="selectedLanguage" :items="availableLangs" variant="outline" class="w-40!" valueKey="iso_code">
|
||||
<template #default="{ modelValue }">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-md">{{availableLangs.find(x => x.iso_code == modelValue)?.flag}}</span>
|
||||
<span class="font-medium dark:text-white text-black">{{availableLangs.find(x => x.iso_code ==
|
||||
modelValue)?.name}}</span>
|
||||
</div>
|
||||
</template>
|
||||
<template #item-leading="{ item }">
|
||||
<div class="flex items-center rounded-md cursor-pointer transition-colors">
|
||||
<span class="text-md">{{ item.flag }}</span>
|
||||
<span class="ml-2 dark:text-white text-black font-medium">{{ item.name }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</USelect>
|
||||
</div>
|
||||
<UButton @click="translateToSelectedLanguage" color="primary" :loading="translating"
|
||||
class="text-white bg-(--accent-blue-light) dark:bg-(--accent-blue-dark) px-12!">
|
||||
Translate
|
||||
</UButton>
|
||||
</div>
|
||||
|
||||
<!-- Loading Overlay -->
|
||||
<div v-if="translating" class="fixed inset-0 z-50 flex items-center justify-center bg-black/50">
|
||||
<div class="flex flex-col items-center gap-4 p-8 bg-(--main-light) dark:bg-(--main-dark) rounded-lg shadow-xl">
|
||||
<UIcon name="svg-spinners:ring-resize" class="text-4xl text-primary" />
|
||||
<p class="text-lg font-medium dark:text-white text-black">Translating...</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-start gap-30">
|
||||
<p class="p-80 bg-(--second-light)">img</p>
|
||||
<div class="flex flex-col gap-2">
|
||||
<p class="text-[25px] font-bold text-black dark:text-white">
|
||||
{{ productStore.productDescription.name }}
|
||||
</p>
|
||||
<p v-html="productStore.productDescription.description_short" class="text-black dark:text-white"></p>
|
||||
<div class="space-y-[10px]">
|
||||
<div class="flex items-center gap-1">
|
||||
<UIcon name="lets-icons:done-ring-round-fill" class="text-[20px] text-green-600" />
|
||||
<p class="text-[16px] font-bold text-(--accent-blue-light) dark:text-(--accent-blue-dark)">
|
||||
{{ productStore.productDescription.available_now }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex items-center gap-1">
|
||||
<UIcon name="marketeq:car-shipping" class="text-[25px] text-green-600" />
|
||||
<p class="text-[18px] font-bold text-black dark:text-white">
|
||||
{{ productStore.productDescription.delivery_in_stock }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-16">
|
||||
<div class="flex gap-4 my-6">
|
||||
<UButton @click="activeTab = 'description'"
|
||||
:class="['cursor-pointer', activeTab === 'description' ? 'bg-blue-500 text-white' : '']" color="neutral"
|
||||
variant="outline">
|
||||
<p class="dark:text-white">Description</p>
|
||||
</UButton>
|
||||
|
||||
<UButton @click="activeTab = 'usage'"
|
||||
:class="['cursor-pointer', activeTab === 'usage' ? 'bg-blue-500 text-white' : '']" color="neutral"
|
||||
variant="outline">
|
||||
<p class="dark:text-white">Usage</p>
|
||||
</UButton>
|
||||
</div>
|
||||
|
||||
<div v-if="activeTab === 'usage'"
|
||||
class="px-8 py-4 border border-(--border-light) dark:border-(--border-dark) rounded-md bg-(--second-light) dark:bg-(--main-dark)">
|
||||
|
||||
<div class="flex justify-end items-center gap-3 mb-4">
|
||||
<UButton v-if="!isEditing" @click="enableEdit"
|
||||
class="flex items-center gap-2 m-2 cursor-pointer bg-(--accent-blue-light)! dark:bg-(--accent-blue-dark)!">
|
||||
<p class="text-white">Change Text</p>
|
||||
<UIcon name="material-symbols-light:stylus-note-sharp" class="text-[30px] text-white!" />
|
||||
</UButton>
|
||||
<UButton v-if="isEditing" @click="saveText" color="neutral" variant="outline" class="p-2.5 cursor-pointer">
|
||||
<p class="dark:text-white text-black">Save the edited text</p>
|
||||
</UButton>
|
||||
<UButton v-if="isEditing" @click="cancelEdit" color="neutral" variant="outline" class="p-2.5 cursor-pointer">
|
||||
Cancel
|
||||
</UButton>
|
||||
</div>
|
||||
<p ref="usageRef" v-html="productStore.productDescription.usage"
|
||||
class="flex flex-col justify-center w-full text-start dark:text-white! text-black!"></p>
|
||||
|
||||
</div>
|
||||
|
||||
<div v-if="activeTab === 'description'"
|
||||
class="px-8 py-4 border border-(--border-light) dark:border-(--border-dark) rounded-md bg-(--second-light) dark:bg-(--main-dark)">
|
||||
<div class="flex items-center justify-end gap-3 mb-4">
|
||||
<UButton v-if="!descriptionEdit.isEditing.value" @click="enableDescriptionEdit"
|
||||
class="flex items-center gap-2 m-2 cursor-pointer bg-(--accent-blue-light)! dark:bg-(--accent-blue-dark)!">
|
||||
<p class="text-white">Change Text</p>
|
||||
<UIcon name="material-symbols-light:stylus-note-sharp" class="text-[30px] text-white!" />
|
||||
</UButton>
|
||||
<UButton v-if="descriptionEdit.isEditing.value" @click="saveDescription" color="neutral" variant="outline" class="p-2.5 cursor-pointer">
|
||||
<p class="dark:text-white text-black ">Save the edited text</p>
|
||||
</UButton>
|
||||
<UButton v-if="descriptionEdit.isEditing.value" @click="cancelDescriptionEdit" color="neutral" variant="outline" class="p-2.5 cursor-pointer">Cancel</UButton>
|
||||
</div>
|
||||
<div ref="descriptionRef" v-html="productStore.productDescription.description"
|
||||
class="flex flex-col justify-center dark:text-white text-black">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue'
|
||||
import { useProductStore } from '@/stores/product'
|
||||
import { useEditable } from '@/composable/useConteditable'
|
||||
import { langs } from '@/router/langs'
|
||||
import type { Language } from '@/types'
|
||||
|
||||
const activeTab = ref('description')
|
||||
const productStore = useProductStore()
|
||||
const translating = ref(false)
|
||||
|
||||
// const availableLangs = computed(() => {
|
||||
// return langs.filter((l: Language) => ['cs', 'pl', 'der'].includes(l.iso_code))
|
||||
// })
|
||||
|
||||
const isEditing = ref(false)
|
||||
|
||||
const availableLangs = computed(() => langs)
|
||||
|
||||
const selectedLanguage = ref('pl')
|
||||
|
||||
const currentLangId = ref(2)
|
||||
|
||||
const fetchForLanguage = async (langCode: string) => {
|
||||
const lang = langs.find((l: Language) => l.iso_code === langCode)
|
||||
if (lang) {
|
||||
await productStore.getProductDescription(lang.id)
|
||||
currentLangId.value = lang.id
|
||||
}
|
||||
}
|
||||
|
||||
const translateToSelectedLanguage = async () => {
|
||||
const targetLang = langs.find((l: Language) => l.iso_code === selectedLanguage.value)
|
||||
if (targetLang && currentLangId.value) {
|
||||
translating.value = true
|
||||
try {
|
||||
await productStore.translateProductDescription(currentLangId.value, targetLang.id)
|
||||
currentLangId.value = targetLang.id
|
||||
} finally {
|
||||
translating.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await fetchForLanguage(selectedLanguage.value)
|
||||
const descriptionRef = ref<HTMLElement | null>(null)
|
||||
const usageRef = ref<HTMLElement | null>(null)
|
||||
|
||||
const descriptionEdit = useEditable(descriptionRef)
|
||||
const usageEdit = useEditable(usageRef)
|
||||
|
||||
const originalDescription = ref('')
|
||||
const originalUsage = ref('')
|
||||
|
||||
const saveDescription = async () => {
|
||||
descriptionEdit.disableEdit()
|
||||
await productStore.saveProductDescription()
|
||||
}
|
||||
|
||||
const cancelDescriptionEdit = () => {
|
||||
if (descriptionRef.value) {
|
||||
descriptionRef.value.innerHTML = originalDescription.value
|
||||
}
|
||||
descriptionEdit.disableEdit()
|
||||
}
|
||||
|
||||
const enableDescriptionEdit = () => {
|
||||
if (descriptionRef.value) {
|
||||
originalDescription.value = descriptionRef.value.innerHTML
|
||||
}
|
||||
descriptionEdit.enableEdit()
|
||||
}
|
||||
|
||||
const enableEdit = () => {
|
||||
if (usageRef.value) {
|
||||
originalUsage.value = usageRef.value.innerHTML
|
||||
}
|
||||
isEditing.value = true
|
||||
usageEdit.enableEdit()
|
||||
}
|
||||
|
||||
const saveText = () => {
|
||||
usageEdit.disableEdit()
|
||||
isEditing.value = false
|
||||
productStore.saveProductDescription()
|
||||
}
|
||||
|
||||
const cancelEdit = () => {
|
||||
if (usageRef.value) {
|
||||
usageRef.value.innerHTML = originalUsage.value
|
||||
}
|
||||
usageEdit.disableEdit()
|
||||
isEditing.value = false
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.images {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 70px;
|
||||
margin: 20px 0 20px 0;
|
||||
}
|
||||
</style>
|
||||
@@ -155,7 +155,9 @@ function clearFilters() {
|
||||
<template>
|
||||
<div class="container">
|
||||
<div class="p-6 bg-white dark:bg-(--black) min-h-screen font-sans">
|
||||
<div v-html="productStore.products"></div>
|
||||
<div >
|
||||
<!-- v-html="productStore.products" -->
|
||||
</div>
|
||||
<h1 class="text-2xl font-bold mb-6 text-black dark:text-white">{{ t('products.title') }}</h1>
|
||||
|
||||
<div v-if="!authStore.isAuthenticated" class="mb-4 p-3 bg-yellow-100 text-yellow-700 rounded">
|
||||
189
bo/src/components/customer/PageAddresses.vue
Normal file
189
bo/src/components/customer/PageAddresses.vue
Normal file
@@ -0,0 +1,189 @@
|
||||
<template>
|
||||
<div class="container mx-auto mt-10">
|
||||
<div class="flex flex-col mb-6">
|
||||
<h1 class="text-2xl font-bold text-black dark:text-white">{{ t('Addresses') }}</h1>
|
||||
<div class="flex justify-between items-center">
|
||||
<div class="flex gap-2 items-center">
|
||||
<UInput v-model="searchQuery" type="text" :placeholder="t('Search address')"
|
||||
class="bg-white dark:bg-gray-800 text-black dark:text-white absolute" />
|
||||
<UIcon name="ic:baseline-search"
|
||||
class="text-[20px] text-(--accent-blue-light) dark:text-(--accent-blue-dark) relative left-40" />
|
||||
</div>
|
||||
<UButton color="primary" @click="openCreateModal"
|
||||
class="bg-(--accent-blue-light) dark:bg-(--accent-blue-dark) text-white hover:bg-(--accent-blue-dark) dark:hover:bg-(--accent-blue-light)">
|
||||
<UIcon name="mdi:add-bold" />
|
||||
{{ t('Add Address') }}
|
||||
</UButton>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="paginatedAddresses.length" class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||
<div v-for="address in paginatedAddresses" :key="address.id"
|
||||
class="border border-(--border-light) dark:border-(--border-dark) rounded-md p-4 bg-(--second-light) dark:bg-(--main-dark) hover:shadow-md transition-shadow">
|
||||
<p class="text-black dark:text-white">{{ address.street }}</p>
|
||||
<p class="text-black dark:text-white">{{ address.zipCode }}, {{ address.city }}</p>
|
||||
<p class="text-black dark:text-white">{{ address.country }}</p>
|
||||
<div class="flex gap-2 mt-2">
|
||||
<UButton size="sm" color="neutral" variant="outline" @click="openEditModal(address)"
|
||||
class="text-(--accent-blue-light) dark:text-(--accent-blue-dark)">{{ t('edit') }}
|
||||
</UButton>
|
||||
<button size="sm" color="destructive" variant="outline" @click="confirmDelete(address.id)"
|
||||
class="text-red-500 hover:bg-red-100 dark:hover:bg-red-900 dark:hover:text-red-100 rounded transition-colors p-2">
|
||||
<UIcon name="material-symbols:delete" class="text-[16px]" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="text-center py-8 text-gray-500 dark:text-gray-400">{{ t('No addresses found') }}</div>
|
||||
|
||||
<div class="mt-6 flex justify-center">
|
||||
<UPagination v-model:page="page" :total="totalItems" :page-size="pageSize" />
|
||||
</div>
|
||||
|
||||
<UModal v-model:open="showModal" :overlay="true" class="max-w-md mx-auto">
|
||||
<template #content>
|
||||
<div class="p-6 flex flex-col gap-6">
|
||||
<p class="text-[20px] text-black dark:text-white ">Address</p>
|
||||
<UForm @submit.prevent="saveAddress" class="space-y-4" :validate="validate">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-black dark:text-white mb-1">Street *</label>
|
||||
<UInput v-model="formData.street" placeholder="Enter street" name="street" class="w-full" />
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-black dark:text-white mb-1">Zip Code *</label>
|
||||
<UInput v-model="formData.zipCode" placeholder="Enter zip code" name="zipCode"
|
||||
class="w-full" />
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-black dark:text-white mb-1">City *</label>
|
||||
<UInput v-model="formData.city" placeholder="Enter city" name="city" class="w-full" />
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-black dark:text-white mb-1">Country *</label>
|
||||
<UInput v-model="formData.country" placeholder="Enter country" name="country"
|
||||
class="w-full" />
|
||||
</div>
|
||||
</UForm>
|
||||
<div class="flex justify-end gap-2">
|
||||
<UButton variant="outline" color="neutral" @click="closeModal"
|
||||
class="text-black dark:text-white">{{ t('Cancel') }}</UButton>
|
||||
<UButton variant="outline" color="neutral" @click="saveAddress"
|
||||
class="text-white bg-(--accent-blue-light) dark:bg-(--accent-blue-dark) hover:bg-(--accent-blue-dark) dark:hover:bg-(--accent-blue-light)">
|
||||
{{ t('Save') }}</UButton>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</UModal>
|
||||
|
||||
<UModal v-model:open="showDeleteConfirm" :overlay="true" class="max-w-md mx-auto">
|
||||
<template #content>
|
||||
<div class="p-6 flex flex-col gap-3">
|
||||
<div class="flex flex-col gap-2 justify-center items-center">
|
||||
<p class="flex items-end gap-2 dark:text-white text-black">
|
||||
<UIcon name='f7:exclamationmark-triangle' class="text-[35px] text-red-700" />
|
||||
Confirm Delete
|
||||
</p>
|
||||
<p class="text-gray-700 dark:text-gray-300">
|
||||
{{ t('Are you sure you want to delete this address?') }}</p>
|
||||
</div>
|
||||
<div class="flex justify-center gap-5">
|
||||
<UButton variant="outline" color="neutral" @click="showDeleteConfirm = false"
|
||||
class="dark:text-white text-black">{{ t('Cancel') }}
|
||||
</UButton>
|
||||
<UButton variant="outline" color="neutral" @click="deleteAddress" class="text-red-700">
|
||||
{{ t('Delete') }}</UButton>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</UModal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, watch } from 'vue'
|
||||
import { useAddressStore } from '@/stores/address'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
const addressStore = useAddressStore()
|
||||
const { t } = useI18n()
|
||||
|
||||
const searchQuery = ref('')
|
||||
const showModal = ref(false)
|
||||
const isEditing = ref(false)
|
||||
const editingAddressId = ref<number | null>(null)
|
||||
const formData = ref({ street: '', zipCode: '', city: '', country: '' })
|
||||
|
||||
const showDeleteConfirm = ref(false)
|
||||
const addressToDelete = ref<number | null>(null)
|
||||
|
||||
const page = ref(addressStore.currentPage)
|
||||
const paginatedAddresses = computed(() => addressStore.paginatedAddresses)
|
||||
const totalItems = computed(() => addressStore.totalItems)
|
||||
const pageSize = addressStore.pageSize
|
||||
|
||||
watch(page, (newPage) => addressStore.setPage(newPage))
|
||||
|
||||
watch(searchQuery, (val) => {
|
||||
addressStore.setSearchQuery(val)
|
||||
})
|
||||
|
||||
function openCreateModal() {
|
||||
resetForm()
|
||||
isEditing.value = false
|
||||
showModal.value = true
|
||||
}
|
||||
|
||||
function openEditModal(address: any) {
|
||||
formData.value = {
|
||||
street: address.street,
|
||||
zipCode: address.zipCode,
|
||||
city: address.city,
|
||||
country: address.country
|
||||
}
|
||||
isEditing.value = true
|
||||
editingAddressId.value = address.id
|
||||
showModal.value = true
|
||||
}
|
||||
|
||||
function resetForm() {
|
||||
formData.value = { street: '', zipCode: '', city: '', country: '' }
|
||||
editingAddressId.value = null
|
||||
}
|
||||
|
||||
function closeModal() {
|
||||
showModal.value = false
|
||||
resetForm()
|
||||
}
|
||||
|
||||
function validate() {
|
||||
const errors = []
|
||||
if (!formData.value.street) errors.push({ name: 'street', message: 'Street required' })
|
||||
if (!formData.value.zipCode) errors.push({ name: 'zipCode', message: 'Zip Code required' })
|
||||
if (!formData.value.city) errors.push({ name: 'city', message: 'City required' })
|
||||
if (!formData.value.country) errors.push({ name: 'country', message: 'Country required' })
|
||||
return errors.length ? errors : null
|
||||
}
|
||||
|
||||
function saveAddress() {
|
||||
if (validate()) return
|
||||
if (isEditing.value && editingAddressId.value) {
|
||||
addressStore.updateAddress(editingAddressId.value, formData.value)
|
||||
} else {
|
||||
addressStore.addAddress(formData.value)
|
||||
}
|
||||
closeModal()
|
||||
}
|
||||
|
||||
function confirmDelete(id: number) {
|
||||
addressToDelete.value = id
|
||||
showDeleteConfirm.value = true
|
||||
}
|
||||
|
||||
function deleteAddress() {
|
||||
if (addressToDelete.value) {
|
||||
addressStore.deleteAddress(addressToDelete.value)
|
||||
}
|
||||
showDeleteConfirm.value = false
|
||||
addressToDelete.value = null
|
||||
}
|
||||
</script>
|
||||
218
bo/src/components/customer/PageCart.vue
Normal file
218
bo/src/components/customer/PageCart.vue
Normal file
@@ -0,0 +1,218 @@
|
||||
<template>
|
||||
<div class="container mx-auto mt-20 px-4 py-8">
|
||||
<h1 class="text-2xl font-bold text-black dark:text-white mb-8">{{ t('Shopping Cart') }}</h1>
|
||||
<div class="flex flex-col lg:flex-row gap-8 mb-8">
|
||||
<div class="flex-1">
|
||||
<div class="bg-(--second-light) dark:bg-(--main-dark) rounded-lg border border-(--border-light) dark:border-(--border-dark) overflow-hidden">
|
||||
<h2 class="text-lg font-semibold text-black dark:text-white p-4 border-b border-(--border-light) dark:border-(--border-dark)">
|
||||
{{ t('Selected Products') }}
|
||||
</h2>
|
||||
<div class="hidden md:grid grid-cols-12 gap-4 p-4 bg-(--second-light) dark:bg-(--main-dark) text-sm font-medium text-gray-600 dark:text-gray-300 border-b border-(--border-light) dark:border-(--border-dark)">
|
||||
<div class="col-span-4">{{ t('Product') }}</div>
|
||||
<div class="col-span-2 text-right">{{ t('Price') }}</div>
|
||||
<div class="col-span-3 text-center">{{ t('Quantity') }}</div>
|
||||
<div class="col-span-2 text-right">{{ t('Total') }}</div>
|
||||
<div class="col-span-1 text-center">{{ t('Actions') }}</div>
|
||||
</div>
|
||||
<div v-if="cartStore.items.length > 0">
|
||||
<div v-for="item in cartStore.items" :key="item.id"
|
||||
class="grid grid-cols-1 md:grid-cols-12 gap-4 p-4 border-b border-(--border-light) dark:border-(--border-dark) items-center">
|
||||
<div class="col-span-4 flex items-center gap-4">
|
||||
<div class="w-16 h-16 bg-(--second-light) dark:bg-(--main-dark) rounded flex items-center justify-center overflow-hidden">
|
||||
<img v-if="item.image" :src="item.image" :alt="item.name" class="w-full h-full object-cover" />
|
||||
<UIcon v-else name="mdi:package-variant" class="text-2xl text-gray-400" />
|
||||
</div>
|
||||
<span class="text-black dark:text-white text-sm font-medium">{{ item.name }}</span>
|
||||
</div>
|
||||
<div class="col-span-2 text-right">
|
||||
<span class="md:hidden text-gray-500 dark:text-gray-400 text-sm">{{ t('Price') }}: </span>
|
||||
<span class="text-black dark:text-white">${{ item.price.toFixed(2) }}</span>
|
||||
</div>
|
||||
<div class="col-span-3 flex items-center justify-center">
|
||||
<div class="flex items-center border border-(--border-light) dark:border-(--border-dark) rounded">
|
||||
<button @click="decreaseQuantity(item)" class="px-3 py-1 text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors">
|
||||
<UIcon name="mdi:minus" />
|
||||
</button>
|
||||
<span class="px-3 py-1 text-black dark:text-white min-w-[40px] text-center">{{ item.quantity }}</span>
|
||||
<button @click="increaseQuantity(item)" class="px-3 py-1 text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors">
|
||||
<UIcon name="mdi:plus" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-span-2 text-right">
|
||||
<span class="md:hidden text-gray-500 dark:text-gray-400 text-sm">{{ t('Total') }}: </span>
|
||||
<span class="text-black dark:text-white font-medium">${{ (item.price * item.quantity).toFixed(2) }}</span>
|
||||
</div>
|
||||
<div class="col-span-1 flex justify-center">
|
||||
<button @click="removeItem(item.id)" class="p-2 text-red-500 hover:bg-red-50 dark:hover:bg-red-900/20 rounded transition-colors" :title="t('Remove')">
|
||||
<UIcon name="material-symbols:delete" class="text-[20px]" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="p-8 text-center">
|
||||
<UIcon name="mdi:cart-outline" class="text-6xl text-gray-300 dark:text-gray-600 mb-4" />
|
||||
<p class="text-gray-500 dark:text-gray-400">{{ t('Your cart is empty') }}</p>
|
||||
<RouterLink :to="{ name: 'product-card-full' }" class="inline-block mt-4 text-(--accent-blue-light) dark:text-(--accent-blue-dark) hover:underline">
|
||||
{{ t('Continue Shopping') }}
|
||||
</RouterLink>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="lg:w-80">
|
||||
<div class="bg-(--second-light) dark:bg-(--main-dark) rounded-lg border border-(--border-light) dark:border-(--border-dark) p-6 sticky top-24">
|
||||
<h2 class="text-lg font-semibold text-black dark:text-white mb-4">{{ t('Order Summary') }}</h2>
|
||||
<div class="space-y-3 border-b border-(--border-light) dark:border-(--border-dark) pb-4 mb-4">
|
||||
<div class="flex justify-between">
|
||||
<span class="text-gray-600 dark:text-gray-400">{{ t('Products total') }}</span>
|
||||
<span class="text-black dark:text-white">${{ cartStore.productsTotal.toFixed(2) }}</span>
|
||||
</div>
|
||||
<div class="flex justify-between">
|
||||
<span class="text-gray-600 dark:text-gray-400">{{ t('Shipping') }}</span>
|
||||
<span class="text-black dark:text-white">
|
||||
{{ cartStore.shippingCost > 0 ? `$${cartStore.shippingCost.toFixed(2)}` : t('Free') }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex justify-between">
|
||||
<span class="text-gray-600 dark:text-gray-400">{{ t('VAT') }} ({{ (cartStore.vatRate * 100).toFixed(0) }}%)</span>
|
||||
<span class="text-black dark:text-white">${{ cartStore.vatAmount.toFixed(2) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-between mb-6">
|
||||
<span class="text-black dark:text-white font-semibold text-lg">{{ t('Total') }}</span>
|
||||
<span class="text-(--accent-blue-light) dark:text-(--accent-blue-dark) font-bold text-lg">${{ cartStore.orderTotal.toFixed(2) }}</span>
|
||||
</div>
|
||||
<div class="flex flex-col gap-3">
|
||||
<UButton block color="primary" @click="placeOrder" :disabled="!canPlaceOrder"
|
||||
class="bg-(--accent-blue-light) dark:bg-(--accent-blue-dark) text-white hover:bg-(--accent-blue-dark) dark:hover:bg-(--accent-blue-light) disabled:opacity-50 disabled:cursor-not-allowed">
|
||||
{{ t('Place Order') }}
|
||||
</UButton>
|
||||
<UButton block variant="outline" color="neutral" @click="cancelOrder"
|
||||
class="text-black dark:text-white border-(--border-light) dark:border-(--border-dark) hover:bg-gray-100 dark:hover:bg-gray-700">
|
||||
{{ t('Cancel') }}
|
||||
</UButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col lg:flex-row gap-8">
|
||||
<div class="flex-1">
|
||||
<div class="bg-(--second-light) dark:bg-(--main-dark) rounded-lg border border-(--border-light) dark:border-(--border-dark) p-6">
|
||||
<h2 class="text-lg font-semibold text-black dark:text-white mb-4">{{ t('Select Delivery Address') }}</h2>
|
||||
<div class="mb-4">
|
||||
<UInput v-model="addressSearchQuery" type="text" :placeholder="t('Search address')"
|
||||
class="w-full bg-white dark:bg-gray-800 text-black dark:text-white" />
|
||||
</div>
|
||||
<div v-if="addressStore.filteredAddresses.length > 0" class="space-y-3">
|
||||
<label v-for="address in addressStore.filteredAddresses" :key="address.id"
|
||||
class="flex items-start gap-3 p-4 border rounded-lg cursor-pointer transition-colors"
|
||||
:class="cartStore.selectedAddressId === address.id
|
||||
? 'border-(--accent-blue-light) dark:border-(--accent-blue-dark) bg-blue-50 dark:bg-blue-900/20'
|
||||
: 'border-(--border-light) dark:border-(--border-dark) hover:border-gray-400'">
|
||||
<input type="radio" :value="address.id" v-model="selectedAddress"
|
||||
class="mt-1 w-4 h-4 text-(--accent-blue-light) dark:text-(--accent-blue-dark)" />
|
||||
<div class="flex-1">
|
||||
<p class="text-black dark:text-white font-medium">{{ address.street }}</p>
|
||||
<p class="text-gray-600 dark:text-gray-400 text-sm">{{ address.zipCode }}, {{ address.city }}</p>
|
||||
<p class="text-gray-600 dark:text-gray-400 text-sm">{{ address.country }}</p>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
<div v-else class="text-center py-6">
|
||||
<UIcon name="mdi:map-marker-outline" class="text-4xl text-gray-400 mb-2" />
|
||||
<p class="text-gray-500 dark:text-gray-400">{{ t('No addresses found') }}</p>
|
||||
<RouterLink :to="{ name: 'addresses' }" class="inline-block mt-2 text-(--accent-blue-light) dark:text-(--accent-blue-dark) hover:underline">
|
||||
{{ t('Add Address') }}
|
||||
</RouterLink>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<div class="bg-(--second-light) dark:bg-(--main-dark) rounded-lg border border-(--border-light) dark:border-(--border-dark) p-6">
|
||||
<h2 class="text-lg font-semibold text-black dark:text-white mb-4">{{ t('Delivery Method') }}</h2>
|
||||
<div class="space-y-3">
|
||||
<label v-for="method in cartStore.deliveryMethods" :key="method.id"
|
||||
class="flex items-center gap-3 p-4 border rounded-lg cursor-pointer transition-colors"
|
||||
:class="cartStore.selectedDeliveryMethodId === method.id
|
||||
? 'border-(--accent-blue-light) dark:border-(--accent-blue-dark) bg-blue-50 dark:bg-blue-900/20'
|
||||
: 'border-(--border-light) dark:border-(--border-dark) hover:border-gray-400'">
|
||||
<input type="radio" :value="method.id" v-model="selectedDeliveryMethod"
|
||||
class="w-4 h-4 text-(--accent-blue-light) dark:text-(--accent-blue-dark)" />
|
||||
<div class="flex-1">
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-black dark:text-white font-medium">{{ method.name }}</span>
|
||||
<span class="text-(--accent-blue-light) dark:text-(--accent-blue-dark) font-medium">
|
||||
{{ method.price > 0 ? `$${method.price.toFixed(2)}` : t('Free') }}
|
||||
</span>
|
||||
</div>
|
||||
<p class="text-gray-500 dark:text-gray-400 text-sm">{{ method.description }}</p>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, watch } from 'vue'
|
||||
import { useCartStore, type CartItem } from '@/stores/cart'
|
||||
import { useAddressStore } from '@/stores/address'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
const cartStore = useCartStore()
|
||||
const addressStore = useAddressStore()
|
||||
const { t } = useI18n()
|
||||
const router = useRouter()
|
||||
|
||||
const selectedAddress = ref<number | null>(cartStore.selectedAddressId)
|
||||
const selectedDeliveryMethod = ref<number | null>(cartStore.selectedDeliveryMethodId)
|
||||
const addressSearchQuery = ref('')
|
||||
|
||||
watch(addressSearchQuery, (val) => {
|
||||
addressStore.setSearchQuery(val)
|
||||
})
|
||||
|
||||
watch(selectedAddress, (newValue) => {
|
||||
cartStore.setSelectedAddress(newValue)
|
||||
})
|
||||
|
||||
watch(selectedDeliveryMethod, (newValue) => {
|
||||
if (newValue) {
|
||||
cartStore.setDeliveryMethod(newValue)
|
||||
}
|
||||
})
|
||||
|
||||
const canPlaceOrder = computed(() => {
|
||||
return cartStore.items.length > 0 &&
|
||||
cartStore.selectedAddressId !== null &&
|
||||
cartStore.selectedDeliveryMethodId !== null
|
||||
})
|
||||
|
||||
function increaseQuantity(item: CartItem) {
|
||||
cartStore.updateQuantity(item.id, item.quantity + 1)
|
||||
}
|
||||
|
||||
function decreaseQuantity(item: CartItem) {
|
||||
cartStore.updateQuantity(item.id, item.quantity - 1)
|
||||
}
|
||||
|
||||
function removeItem(itemId: number) {
|
||||
cartStore.removeItem(itemId)
|
||||
}
|
||||
|
||||
function placeOrder() {
|
||||
if (canPlaceOrder.value) {
|
||||
console.log('Placing order...')
|
||||
alert(t('Order placed successfully!'))
|
||||
cartStore.clearCart()
|
||||
router.push({ name: 'home' })
|
||||
}
|
||||
}
|
||||
|
||||
function cancelOrder() {
|
||||
router.back()
|
||||
}
|
||||
</script>
|
||||
164
bo/src/components/customer/PageProductCardFull.vue
Normal file
164
bo/src/components/customer/PageProductCardFull.vue
Normal file
@@ -0,0 +1,164 @@
|
||||
<template>
|
||||
<div class="container mt-14 mx-auto">
|
||||
<div class="flex justify-between gap-8 mb-6">
|
||||
<div class="flex-1">
|
||||
<div class="bg-gray-100 dark:bg-gray-800 rounded-lg p-8 flex items-center justify-center min-h-[300px]">
|
||||
<img :src="selectedColor?.image || productData.image" :alt="productData.name"
|
||||
class="max-w-full h-auto object-contain" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-1 flex flex-col gap-4">
|
||||
<h1 class="text-2xl font-bold text-gray-900 dark:text-white">
|
||||
{{ productData.name }}
|
||||
</h1>
|
||||
<p class="text-gray-600 dark:text-gray-300">
|
||||
{{ productData.description }}
|
||||
</p>
|
||||
<div class="text-3xl font-bold text-(--accent-blue-light) dark:text-(--accent-blue-dark)">
|
||||
{{ productData.price }}
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<div class="flex gap-2">
|
||||
<span class="text-gray-500 dark:text-gray-400">Dimensions:</span>
|
||||
<p class="font-medium dark:text-white">{{ productData.dimensions }}</p>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<span class="text-gray-500 dark:text-gray-400">Seat Height:</span>
|
||||
<p class="font-medium dark:text-white">{{ productData.seatHeight }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-between items-end mb-8">
|
||||
<div class="flex flex-col gap-3">
|
||||
<span class="text-sm text-(--accent-blue-light) dark:text-(--accent-blue-dark) ">Colors:</span>
|
||||
<div class="flex gap-2">
|
||||
<button v-for="color in productData.colors" :key="color.id" @click="selectedColor = color"
|
||||
class="w-10 h-10 border-2 transition-all" :class="selectedColor?.id === color.id
|
||||
? 'border-(--accent-blue-light) ring-2 ring-blue-600 ring-offset-2'
|
||||
: 'border-gray-300 dark:border-gray-600 hover:border-gray-400'"
|
||||
:style="{ backgroundColor: color.hex }" :title="color.name" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex gap-5 items-end">
|
||||
<UInputNumber v-model="value" />
|
||||
<UButton color="primary" class="px-14! bg-(--accent-blue-light) dark:bg-(--accent-blue-dark) text-white">
|
||||
Add to Cart
|
||||
</UButton>
|
||||
</div>
|
||||
</div>
|
||||
<ProductCustomization />
|
||||
<hr class="border-t border-(--border-light) dark:border-(--border-dark) mb-8" />
|
||||
<div class="mb-6 w-[55%]">
|
||||
<div class="flex justify-between items-center gap-10 mb-8">
|
||||
<UButton v-for="tab in tabs" :key="tab.id" @click="activeTab = tab.id" :class="[
|
||||
'px-15 py-2 cursor-pointer',
|
||||
activeTab === tab.id
|
||||
? 'bg-blue-600 hover:text-black hover:dark:text-white text-white'
|
||||
: 'text-gray-700 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-700'
|
||||
]" variant="ghost">
|
||||
{{ tab.label }}
|
||||
</UButton>
|
||||
</div>
|
||||
<div class="py-5 px-3 bg-(--second-light) dark:bg-(--main-dark) rounded-md">
|
||||
<p class="dark:text-white whitespace-pre-line">
|
||||
{{ activeTabContent }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<hr class="border-t border-(--border-light) dark:border-(--border-dark) mb-8" />
|
||||
<ProductVariants />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue'
|
||||
import ProductCustomization from './components/ProductCustomization.vue'
|
||||
import ProductVariants from './components/ProductVariants.vue'
|
||||
|
||||
interface Color {
|
||||
id: string
|
||||
name: string
|
||||
hex: string
|
||||
image: string
|
||||
}
|
||||
|
||||
interface ProductData {
|
||||
name: string
|
||||
description: string
|
||||
price: string
|
||||
dimensions: string
|
||||
seatHeight: string
|
||||
image: string
|
||||
colors: Color[]
|
||||
descriptionText: string
|
||||
howToUseText: string
|
||||
productDetailsText: string
|
||||
documentsText: string
|
||||
}
|
||||
|
||||
const activeTab = ref('description')
|
||||
const value = ref(5)
|
||||
const selectedColor = ref<Color | null>(null)
|
||||
|
||||
const productData: ProductData = {
|
||||
name: 'Larger Corner Sofa',
|
||||
description: 'The large corner sofa is a comfortable seating solution for young children. It is upholstered with phthalate-free PVC-coated material intended for medical products, making it very easy to clean and disinfect. The product is available in a wide range of colours (13 colours), allowing it to fit into any interior.',
|
||||
price: 'PLN 519.00 (VAT 23%)',
|
||||
dimensions: '65 x 65 x 120 cm',
|
||||
seatHeight: '45-55 cm',
|
||||
image: '/placeholder-chair.jpg',
|
||||
colors: [
|
||||
{ id: 'black', name: 'Black', hex: '#1a1a1a', image: '/chair-black.jpg' },
|
||||
{ id: 'gray', name: 'Gray', hex: '#6b7280', image: '/chair-gray.jpg' },
|
||||
{ id: 'blue', name: 'Blue', hex: '#3b82f6', image: '/chair-blue.jpg' },
|
||||
{ id: 'brown', name: 'Brown', hex: '#92400e', image: '/chair-brown.jpg' },
|
||||
],
|
||||
descriptionText: 'The large corner sofa is a comfortable seating solution for young children. It is upholstered with phthalate-free PVC-coated material intended for medical products, making it very easy to clean and disinfect. The product is available in a wide range of colours (13 colours), allowing it to fit into any interior',
|
||||
howToUseText: '1. Adjust the seat height using the lever under the seat.\n2. Set the lumbar support to your preferred position.\n3. Adjust the armrests for optimal arm support.\n4. Use the recline tension knob to adjust the backrest resistance.\n5. Lock the recline position when needed.',
|
||||
productDetailsText: '• Material: Mesh, Foam, Plastic\n• Max Load: 150 kg\n• Weight: 18 kg\n• Warranty: 2 years\n• Certifications: BIFMA, EN 1335',
|
||||
documentsText: '• Assembly Instructions (PDF)\n• User Manual (PDF)\n• Warranty Terms (PDF)\n• Safety Certificate (PDF)',
|
||||
}
|
||||
|
||||
const tabs = [
|
||||
{ id: 'description', label: 'Description' },
|
||||
{ id: 'howToUse', label: 'How to Use' },
|
||||
{ id: 'productDetails', label: 'Product Details' },
|
||||
{ id: 'documents', label: 'Documents' },
|
||||
]
|
||||
|
||||
const userActions = [
|
||||
'View detailed product information',
|
||||
'Browse product images and available colors',
|
||||
'Check product dimensions and specifications',
|
||||
'Select a product variant',
|
||||
'Select quantity',
|
||||
'Add the product to the cart',
|
||||
'Navigate between product description, usage instructions, and product details',
|
||||
]
|
||||
|
||||
const activeTabContent = computed(() => {
|
||||
switch (activeTab.value) {
|
||||
case 'description':
|
||||
return productData.descriptionText
|
||||
case 'howToUse':
|
||||
return productData.howToUseText
|
||||
case 'productDetails':
|
||||
return productData.productDetailsText
|
||||
case 'documents':
|
||||
return productData.documentsText
|
||||
default:
|
||||
return ''
|
||||
}
|
||||
})
|
||||
|
||||
if (productData.colors.length > 0) {
|
||||
selectedColor.value = productData.colors[0] as Color
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.product-card-full {
|
||||
font-family: inherit;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,16 @@
|
||||
<template>
|
||||
<div class="container flex flex-col gap-8">
|
||||
<div class="space-y-1 dark:text-white text-black">
|
||||
<p class="text-[24px] font-bold">Product customization</p>
|
||||
<p class="text-[15px]">Don't forget to save your customization to be able to add to cart</p>
|
||||
</div>
|
||||
<div class="grid grid-cols-3 gap-10">
|
||||
<UInput label="Podaj kolor kanapy narożnej" placeholder="Podaj kolor kanapy narożnej" class="dark:text-white text-black"/>
|
||||
<UInput label="Podaj kolor fotela" placeholder="Podaj kolor fotela" class="dark:text-white text-black"/>
|
||||
<UInput label="Podaj kolor kwadratu" placeholder="Podaj kolor kwadratu" class="dark:text-white text-black"/>
|
||||
</div>
|
||||
<div class="flex justify-end items-end mb-8">
|
||||
<UButton class="px-10! bg-(--accent-blue-light) dark:bg-(--accent-blue-dark) text-white">Save</UButton>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
57
bo/src/components/customer/components/ProductVariants.vue
Normal file
57
bo/src/components/customer/components/ProductVariants.vue
Normal file
@@ -0,0 +1,57 @@
|
||||
<template>
|
||||
<div class="container flex flex-col gap-8">
|
||||
<p class="text-[24px] font-bold dark:text-white text-black">Product Variants:</p>
|
||||
<div class="flex flex-col gap-4">
|
||||
<div v-for="(variant, index) in variants" :key="index" class="flex gap-10">
|
||||
<div
|
||||
class="flex items-center gap-15 border border-(--border-light) dark:border-(--border-dark) p-5 rounded-md hover:bg-gray-50 hover:dark:bg-gray-700 bg-(--second-light) dark:bg-(--second-dark) dark:text-white text-black">
|
||||
<img :src="variant.image" :alt="variant.image" class="w-16 h-16 object-cover" />
|
||||
<p class="">{{ variant.name }}</p>
|
||||
<p class="">{{ variant.productNumber }}</p>
|
||||
<p class="">{{ variant.value }}</p>
|
||||
<p class="">{{ variant.price }}</p>
|
||||
<p class="">{{ variant.quantity }}</p>
|
||||
<button class="bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded"
|
||||
@click="addToCart(variant)">
|
||||
Add to Cart
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
|
||||
const variants = ref([
|
||||
{
|
||||
image: 'img',
|
||||
name: 'Duży fotelik narożny ',
|
||||
productNumber: 'NC209/7000',
|
||||
value: '20,000',
|
||||
price: '519,00 zł',
|
||||
quantity: 10
|
||||
},
|
||||
{
|
||||
image: 'img',
|
||||
name: 'Duży fotelik narożny ',
|
||||
productNumber: 'NC209/7000',
|
||||
value: '20,000',
|
||||
price: '519,00 zł',
|
||||
quantity: 5
|
||||
},
|
||||
{
|
||||
image: 'img',
|
||||
name: 'Duży fotelik narożny ',
|
||||
productNumber: 'NC209/7000',
|
||||
value: '20,000',
|
||||
price: '519,00 zł',
|
||||
quantity: 8
|
||||
}
|
||||
])
|
||||
|
||||
const addToCart = (variant: any) => {
|
||||
console.log('Added to cart:', variant)
|
||||
}
|
||||
</script>
|
||||
@@ -1,14 +1,56 @@
|
||||
<script setup lang="ts">
|
||||
import TopBar from '@/components/TopBar.vue';
|
||||
import { getMenu } from '@/router/menu'
|
||||
import type { NavigationMenuItem } from '@nuxt/ui';
|
||||
import { on } from 'events';
|
||||
import { ref } from 'vue';
|
||||
let menu = await getMenu() as NavigationMenuItem[]
|
||||
|
||||
function adaptMenu(menu) {
|
||||
for (const item of menu) {
|
||||
if (item.children && item.children.length > 0) {
|
||||
adaptMenu(item.children);
|
||||
// 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 {
|
||||
// console.log(item.params);
|
||||
item.to = { name: 'category', params: item.params };
|
||||
item.icon = 'i-lucide-file-text'
|
||||
}
|
||||
}
|
||||
return menu;
|
||||
}
|
||||
|
||||
menu = adaptMenu(menu.children)
|
||||
|
||||
console.log(menu);
|
||||
|
||||
const items = ref<NavigationMenuItem[][]>([
|
||||
[
|
||||
|
||||
...menu
|
||||
],
|
||||
|
||||
])
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="h-screen grid grid-rows-[auto_1fr_auto]">
|
||||
<!-- <header class="w-full bg-gray-100 text-primary shadow border-b-gray-300 p-4 mb-8">Header</header> -->
|
||||
<main class="p-10">
|
||||
<div class="mt-24 bg-accented w-1/5 rounded-2xl">
|
||||
<UNavigationMenu orientation="vertical" type="single" :items="items" class="p-4">
|
||||
</UNavigationMenu>
|
||||
|
||||
</div>
|
||||
<!-- <template #item>{{ item }}</template> -->
|
||||
|
||||
|
||||
<TopBar/>
|
||||
<router-view />
|
||||
<slot></slot>
|
||||
</main>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<main :key="i18n.locale.value">
|
||||
<TopBarLogin />
|
||||
<router-view />
|
||||
<slot></slot>
|
||||
</main>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
import Empty from '@/layouts/empty.vue'
|
||||
import { currentLang, langs } from './langs'
|
||||
import { getSettings } from './settings'
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
import Default from '@/layouts/default.vue'
|
||||
import { getMenu } from './menu'
|
||||
|
||||
// Helper: read the non-HTTPOnly is_authenticated cookie set by the backend.
|
||||
// The backend sets it to "1" on login and removes it on logout.
|
||||
function isAuthenticated(): boolean {
|
||||
if (typeof document === 'undefined') return false
|
||||
return document.cookie.split('; ').some((c) => c === 'is_authenticated=1')
|
||||
@@ -14,6 +12,7 @@ function isAuthenticated(): boolean {
|
||||
|
||||
|
||||
await getSettings()
|
||||
// await getMenu()
|
||||
|
||||
|
||||
const router = createRouter({
|
||||
@@ -26,64 +25,101 @@ const router = createRouter({
|
||||
{
|
||||
path: '/:locale',
|
||||
children: [
|
||||
{ path: 'category/:category_id-:link_rewrite', component: () => import('../views/CategoryView.vue'), name: 'category' },
|
||||
{
|
||||
path: '',
|
||||
component: Default,
|
||||
children: [
|
||||
{ path: '', component: () => import('../views/RepoChartView.vue'), name: 'home' },
|
||||
{ path: 'products', component: () => import('../views/customer/ProductsView.vue'), name: 'products' },
|
||||
{ path: 'products-datail/', component: () => import('../views/customer/ProductDetailView.vue'), name: 'product-detail' },
|
||||
{ path: 'products', component: () => import('../components/admin/ProductsView.vue'), name: 'products' },
|
||||
{ path: 'products-datail/', component: () => import('../components/admin/ProductDetailView.vue'), name: 'product-detail' },
|
||||
{ path: 'product-card-full/', component: () => import('../components/customer/PageProductCardFull.vue'), name: 'product-card-full' },
|
||||
{ path: 'addresses', component: () => import('../components/customer/PageAddresses.vue'), name: 'addresses' },
|
||||
{ path: 'cart', component: () => import('../components/customer/PageCart.vue'), name: 'cart' },
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '',
|
||||
component: Empty,
|
||||
children: [
|
||||
{ path: 'login', component: () => import('@/views/LoginView.vue'), name: 'login', meta: { guest: true } },
|
||||
{ path: 'login', component: () => import('@/views/LoginView.vue'), name: 'login', meta: { guest: true, } },
|
||||
{ path: 'register', component: () => import('@/views/RegisterView.vue'), name: 'register', meta: { guest: true } },
|
||||
{ path: 'password-recovery', component: () => import('@/views/PasswordRecoveryView.vue'), name: 'password-recovery', meta: { guest: true } },
|
||||
{ path: 'reset-password', component: () => import('@/views/ResetPasswordForm.vue'), name: 'reset-password', meta: { guest: true } },
|
||||
{ path: 'verify-email', component: () => import('@/views/VerifyEmailView.vue'), name: 'verify-email', meta: { guest: true } },
|
||||
],
|
||||
// {
|
||||
// path: '',
|
||||
// component: Empty,
|
||||
// children: [
|
||||
// ],
|
||||
// },
|
||||
{
|
||||
path: '/:pathMatch(.*)*',
|
||||
component: () => import('@/views/NotFoundView.vue'),
|
||||
name: 'not-found',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/:pathMatch(.*)*',
|
||||
component: () => import('@/views/NotFoundView.vue'),
|
||||
name: 'not-found',
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
// Navigation guard: language handling + auth protection
|
||||
router.beforeEach((to, from, next) => {
|
||||
const locale = to.params.locale as string
|
||||
const localeLang = langs.find((x) => x.iso_code == locale)
|
||||
// router.beforeEach((to, from, next) => {
|
||||
// const locale = to.params.locale as string
|
||||
// const localeLang = langs.find((x) => x.iso_code == locale)
|
||||
|
||||
// if (locale && langs.length > 0) {
|
||||
// const authStore = useAuthStore()
|
||||
// // console.log(authStore.isAuthenticated, to, from)
|
||||
// // if()
|
||||
// const validLocale = langs.find((l) => l.lang_code === locale)
|
||||
|
||||
// if (validLocale) {
|
||||
// currentLang.value = localeLang
|
||||
// if (!to.meta?.guest && !isAuthenticated()) {
|
||||
// return next({ name: 'login', params: { locale } })
|
||||
// }
|
||||
|
||||
// // return next()
|
||||
// return next()
|
||||
// } else if (locale) {
|
||||
// return next(`/${currentLang.value?.iso_code}${to.path.replace(`/${locale}`, '') || '/'}`)
|
||||
// }
|
||||
// }
|
||||
|
||||
// if (!locale && to.path !== '/') {
|
||||
// return next(`/${currentLang.value?.iso_code}${to.path}`)
|
||||
// }
|
||||
|
||||
// next()
|
||||
// })
|
||||
|
||||
router.beforeEach((to, from) => {
|
||||
const locale = to.params.locale as string
|
||||
const localeLang = langs.find((x) => x.iso_code === locale)
|
||||
|
||||
// Check if the locale is valid
|
||||
if (locale && langs.length > 0) {
|
||||
const authStore = useAuthStore()
|
||||
console.log(authStore.isAuthenticated,to, from)
|
||||
// if()
|
||||
const validLocale = langs.find((l) => l.lang_code === locale)
|
||||
|
||||
if (validLocale) {
|
||||
currentLang.value = localeLang
|
||||
|
||||
// Auth guard: if the route does NOT have meta.guest = true, require authentication
|
||||
if (!to.meta?.guest && !isAuthenticated()) {
|
||||
return next({ name: 'login', params: { locale } })
|
||||
if (!to.meta?.guest && !authStore.isAuthenticated) {
|
||||
return { name: 'login', params: { locale } }
|
||||
}
|
||||
|
||||
return next()
|
||||
return true
|
||||
} else if (locale) {
|
||||
// Invalid locale - redirect to default language
|
||||
return next(`/${currentLang.value?.iso_code}${to.path.replace(`/${locale}`, '') || '/'}`)
|
||||
return `/${currentLang.value?.iso_code}${to.path.replace(`/${locale}`, '') || '/'}`
|
||||
}
|
||||
}
|
||||
|
||||
// No locale in URL - redirect to default language
|
||||
if (!locale && to.path !== '/') {
|
||||
return next(`/${currentLang.value?.iso_code}${to.path}`)
|
||||
return `/${currentLang.value?.iso_code}${to.path}`
|
||||
}
|
||||
|
||||
next()
|
||||
return true
|
||||
})
|
||||
|
||||
export default router
|
||||
|
||||
8
bo/src/router/menu.ts
Normal file
8
bo/src/router/menu.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { useFetchJson } from "@/composable/useFetchJson";
|
||||
|
||||
export const getMenu = async () => {
|
||||
const resp = await useFetchJson('/api/v1/restricted/menu/get-menu');
|
||||
|
||||
return resp.items
|
||||
|
||||
}
|
||||
145
bo/src/stores/address.ts
Normal file
145
bo/src/stores/address.ts
Normal file
@@ -0,0 +1,145 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref, computed } from 'vue'
|
||||
|
||||
export interface AddressFormData {
|
||||
street: string
|
||||
zipCode: string
|
||||
city: string
|
||||
country: string
|
||||
}
|
||||
|
||||
export interface Address {
|
||||
id: number
|
||||
street: string
|
||||
zipCode: string
|
||||
city: string
|
||||
country: string
|
||||
}
|
||||
|
||||
export const useAddressStore = defineStore('address', () => {
|
||||
const addresses = ref<Address[]>([])
|
||||
const loading = ref(false)
|
||||
const error = ref<string | null>(null)
|
||||
|
||||
const currentPage = ref(1)
|
||||
const pageSize = 20
|
||||
|
||||
const searchQuery = ref('')
|
||||
|
||||
function initMockData() {
|
||||
addresses.value = [
|
||||
{ id: 1, street: 'Main Street 123', zipCode: '10-001', city: 'New York', country: 'United States' },
|
||||
{ id: 2, street: 'Oak Avenue 123', zipCode: '90-001', city: 'Los Angeles', country: 'United States' },
|
||||
{ id: 3, street: 'Pine Road 123', zipCode: '60-601', city: 'Chicago', country: 'United States' }
|
||||
]
|
||||
}
|
||||
|
||||
const filteredAddresses = computed(() => {
|
||||
if (!searchQuery.value) return addresses.value
|
||||
|
||||
const query = searchQuery.value.toLowerCase()
|
||||
|
||||
return addresses.value.filter(addr =>
|
||||
addr.street.toLowerCase().includes(query) ||
|
||||
addr.city.toLowerCase().includes(query) ||
|
||||
addr.country.toLowerCase().includes(query) ||
|
||||
addr.zipCode.toLowerCase().includes(query)
|
||||
)
|
||||
})
|
||||
|
||||
const totalItems = computed(() => filteredAddresses.value.length)
|
||||
const totalPages = computed(() => Math.ceil(totalItems.value / pageSize))
|
||||
|
||||
const paginatedAddresses = computed(() => {
|
||||
const start = (currentPage.value - 1) * pageSize
|
||||
return filteredAddresses.value.slice(start, start + pageSize)
|
||||
})
|
||||
|
||||
function getAddressById(id: number) {
|
||||
return addresses.value.find(addr => addr.id === id)
|
||||
}
|
||||
|
||||
function normalize(data: AddressFormData): AddressFormData {
|
||||
return {
|
||||
street: data.street.trim(),
|
||||
zipCode: data.zipCode.trim(),
|
||||
city: data.city.trim(),
|
||||
country: data.country.trim()
|
||||
}
|
||||
}
|
||||
|
||||
function generateId(): number {
|
||||
return Math.max(0, ...addresses.value.map(a => a.id)) + 1
|
||||
}
|
||||
|
||||
function addAddress(formData: AddressFormData): Address {
|
||||
const newAddress: Address = {
|
||||
id: generateId(),
|
||||
...normalize(formData)
|
||||
}
|
||||
|
||||
addresses.value.unshift(newAddress)
|
||||
resetPagination()
|
||||
|
||||
return newAddress
|
||||
}
|
||||
|
||||
function updateAddress(id: number, formData: AddressFormData): boolean {
|
||||
const index = addresses.value.findIndex(a => a.id === id)
|
||||
if (index === -1) return false
|
||||
|
||||
const existing = addresses.value[index]
|
||||
if (!existing) return false
|
||||
|
||||
addresses.value[index] = {
|
||||
id: existing.id,
|
||||
...normalize(formData)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
function deleteAddress(id: number): boolean {
|
||||
const index = addresses.value.findIndex(a => a.id === id)
|
||||
if (index === -1) return false
|
||||
|
||||
addresses.value.splice(index, 1)
|
||||
resetPagination()
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
function setPage(page: number) {
|
||||
currentPage.value = page
|
||||
}
|
||||
|
||||
function setSearchQuery(query: string) {
|
||||
searchQuery.value = query
|
||||
currentPage.value = 1
|
||||
}
|
||||
|
||||
function resetPagination() {
|
||||
currentPage.value = 1
|
||||
}
|
||||
|
||||
initMockData()
|
||||
|
||||
return {
|
||||
addresses,
|
||||
loading,
|
||||
error,
|
||||
currentPage,
|
||||
pageSize,
|
||||
totalItems,
|
||||
totalPages,
|
||||
searchQuery,
|
||||
filteredAddresses,
|
||||
paginatedAddresses,
|
||||
getAddressById,
|
||||
addAddress,
|
||||
updateAddress,
|
||||
deleteAddress,
|
||||
setPage,
|
||||
setSearchQuery,
|
||||
resetPagination
|
||||
}
|
||||
})
|
||||
113
bo/src/stores/cart.ts
Normal file
113
bo/src/stores/cart.ts
Normal file
@@ -0,0 +1,113 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref, computed } from 'vue'
|
||||
|
||||
export interface CartItem {
|
||||
id: number
|
||||
productId: number
|
||||
name: string
|
||||
image: string
|
||||
price: number
|
||||
quantity: number
|
||||
}
|
||||
|
||||
export interface DeliveryMethod {
|
||||
id: number
|
||||
name: string
|
||||
price: number
|
||||
description: string
|
||||
}
|
||||
|
||||
export const useCartStore = defineStore('cart', () => {
|
||||
const items = ref<CartItem[]>([])
|
||||
const selectedAddressId = ref<number | null>(null)
|
||||
const selectedDeliveryMethodId = ref<number | null>(null)
|
||||
const shippingCost = ref(0)
|
||||
const vatRate = ref(0.23) // 23% VAT
|
||||
|
||||
const deliveryMethods = ref<DeliveryMethod[]>([
|
||||
{ id: 1, name: 'Standard Delivery', price: 0, description: '5-7 business days' },
|
||||
{ id: 2, name: 'Express Delivery', price: 15, description: '2-3 business days' },
|
||||
{ id: 3, name: 'Priority Delivery', price: 30, description: 'Next business day' }
|
||||
])
|
||||
|
||||
function initMockData() {
|
||||
items.value = [
|
||||
{ id: 1, productId: 101, name: 'Premium Widget Pro', image: '/img/product-1.jpg', price: 129.99, quantity: 2 },
|
||||
{ id: 2, productId: 102, name: 'Ultra Gadget X', image: '/img/product-2.jpg', price: 89.50, quantity: 1 },
|
||||
{ id: 3, productId: 103, name: 'Mega Tool Set', image: '/img/product-3.jpg', price: 249.00, quantity: 3 }
|
||||
]
|
||||
}
|
||||
|
||||
const productsTotal = computed(() => {
|
||||
return items.value.reduce((sum, item) => sum + (item.price * item.quantity), 0)
|
||||
})
|
||||
|
||||
const vatAmount = computed(() => {
|
||||
return productsTotal.value * vatRate.value
|
||||
})
|
||||
|
||||
const orderTotal = computed(() => {
|
||||
return productsTotal.value + shippingCost.value + vatAmount.value
|
||||
})
|
||||
|
||||
const itemCount = computed(() => {
|
||||
return items.value.reduce((sum, item) => sum + item.quantity, 0)
|
||||
})
|
||||
|
||||
function updateQuantity(itemId: number, quantity: number) {
|
||||
const item = items.value.find(i => i.id === itemId)
|
||||
if (item) {
|
||||
if (quantity <= 0) {
|
||||
removeItem(itemId)
|
||||
} else {
|
||||
item.quantity = quantity
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function removeItem(itemId: number) {
|
||||
const index = items.value.findIndex(i => i.id === itemId)
|
||||
if (index !== -1) {
|
||||
items.value.splice(index, 1)
|
||||
}
|
||||
}
|
||||
|
||||
function clearCart() {
|
||||
items.value = []
|
||||
selectedAddressId.value = null
|
||||
selectedDeliveryMethodId.value = null
|
||||
shippingCost.value = 0
|
||||
}
|
||||
|
||||
function setSelectedAddress(addressId: number | null) {
|
||||
selectedAddressId.value = addressId
|
||||
}
|
||||
|
||||
function setDeliveryMethod(methodId: number) {
|
||||
selectedDeliveryMethodId.value = methodId
|
||||
const method = deliveryMethods.value.find(m => m.id === methodId)
|
||||
if (method) {
|
||||
shippingCost.value = method.price
|
||||
}
|
||||
}
|
||||
|
||||
initMockData()
|
||||
|
||||
return {
|
||||
items,
|
||||
selectedAddressId,
|
||||
selectedDeliveryMethodId,
|
||||
shippingCost,
|
||||
vatRate,
|
||||
deliveryMethods,
|
||||
productsTotal,
|
||||
vatAmount,
|
||||
orderTotal,
|
||||
itemCount,
|
||||
updateQuantity,
|
||||
removeItem,
|
||||
clearCart,
|
||||
setSelectedAddress,
|
||||
setDeliveryMethod
|
||||
}
|
||||
})
|
||||
@@ -27,13 +27,12 @@ export const useProductStore = defineStore('product', () => {
|
||||
const loading = ref(false)
|
||||
const error = ref<string | null>(null)
|
||||
|
||||
// Fetch all products
|
||||
async function getProductDescription() {
|
||||
async function getProductDescription(langId: number = 1) {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
|
||||
try {
|
||||
const response = await useFetchJson('/api/v1/restricted/product-description/get-product-description?productID=51&productShopID=1&productLangID=1')
|
||||
const response = await useFetchJson(`/api/v1/restricted/product-description/get-product-description?productID=51&productShopID=1&productLangID=${langId}`)
|
||||
productDescription.value = response
|
||||
} catch (e: any) {
|
||||
error.value = e?.message || 'Failed to load product description'
|
||||
@@ -43,59 +42,19 @@ export const useProductStore = defineStore('product', () => {
|
||||
}
|
||||
}
|
||||
|
||||
async function getProductDescriptionTranslations() {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
|
||||
try {
|
||||
const response = await useFetchJson('api/v1/restricted/product-description/translate-product-description?productID=51&productShopID=1&productFromLangID=1&productToLangID=2')
|
||||
productDescription.value = response
|
||||
} catch (e: any) {
|
||||
error.value = e?.message || 'Failed to load product description'
|
||||
console.error('Failed to fetch product description:', e)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch single product by ID
|
||||
async function fetchProductById(id: number) {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
currentProduct.value = null
|
||||
|
||||
try {
|
||||
const data = await useFetchJson<{ items: Product }>(`/api/v1/restricted/product-description?id=${id}`)
|
||||
|
||||
const response = (data as any).items || data
|
||||
currentProduct.value = response.items?.[0] || response
|
||||
} catch (e: any) {
|
||||
error.value = e?.message || 'Failed to load product'
|
||||
console.error('Failed to fetch product:', e)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async function saveProductDescription(description: string) {
|
||||
async function saveProductDescription() {
|
||||
try {
|
||||
const data = await useFetchJson(
|
||||
`/api/v1/restricted/product-description/save-product-description?productID=1&productShopID=1&productLangID=1`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
description: description,
|
||||
description_short: "<p>Test short</p>",
|
||||
meta_description: "This is a test",
|
||||
meta_title: "...",
|
||||
name: "...",
|
||||
available_now: "Test",
|
||||
available_later: "...",
|
||||
usage: "<p>test</p>"
|
||||
body: JSON.stringify(
|
||||
{
|
||||
description: productDescription.value.description,
|
||||
description_short: productDescription.value.description_short,
|
||||
meta_description: productDescription.value.meta_description,
|
||||
available_now: productDescription.value.available_now,
|
||||
usage: productDescription.value.usage
|
||||
})
|
||||
}
|
||||
)
|
||||
@@ -105,7 +64,22 @@ export const useProductStore = defineStore('product', () => {
|
||||
}
|
||||
}
|
||||
|
||||
// Clear current product
|
||||
async function translateProductDescription(fromLangId: number, toLangId: number) {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
|
||||
try {
|
||||
const response = await useFetchJson(`/api/v1/restricted/product-description/translate-product-description?productID=51&productShopID=1&productFromLangID=${fromLangId}&productToLangID=${toLangId}&model=OpenAI`)
|
||||
productDescription.value = response
|
||||
return response
|
||||
} catch (e: any) {
|
||||
error.value = e?.message || 'Failed to translate product description'
|
||||
console.error('Failed to translate product description:', e)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function clearCurrentProduct() {
|
||||
currentProduct.value = null
|
||||
}
|
||||
@@ -116,9 +90,8 @@ export const useProductStore = defineStore('product', () => {
|
||||
loading,
|
||||
error,
|
||||
getProductDescription,
|
||||
fetchProductById,
|
||||
clearCurrentProduct,
|
||||
saveProductDescription,
|
||||
getProductDescriptionTranslations
|
||||
translateProductDescription
|
||||
}
|
||||
})
|
||||
|
||||
21
bo/src/views/CategoryView.vue
Normal file
21
bo/src/views/CategoryView.vue
Normal file
@@ -0,0 +1,21 @@
|
||||
<template>
|
||||
<component :is="Default || 'div'">
|
||||
<div class="container mt-24">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<h1>Category</h1>
|
||||
{{ $route.params }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</component>
|
||||
</template>
|
||||
|
||||
|
||||
<script setup lang="ts">
|
||||
// import { useRoute } from 'vue-router';
|
||||
import Default from '@/layouts/default.vue';
|
||||
// const route = useRoute()
|
||||
// console.log(route);
|
||||
|
||||
</script>
|
||||
@@ -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')),
|
||||
),
|
||||
)
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<component :is="Empty || 'div'">
|
||||
<UDrawer v-model:open="showTherms" :overlay="false">
|
||||
<template #body>
|
||||
<component :is="TermsComponent" />
|
||||
@@ -163,4 +165,5 @@ const PrivacyComponent = computed(() =>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</component>
|
||||
</template>
|
||||
|
||||
7
bo/src/views/NotFoundView.vue
Normal file
7
bo/src/views/NotFoundView.vue
Normal file
@@ -0,0 +1,7 @@
|
||||
<template>
|
||||
<div class="flex flex-col items-center justify-center h-screen">
|
||||
<h1 class="text-4xl font-bold text-gray-800">404</h1>
|
||||
<p class="mt-4 text-lg text-gray-600">Page not found</p>
|
||||
<router-link to="/" class="mt-6 text-blue-500 hover:underline">Go back home</router-link>
|
||||
</div>
|
||||
</template>
|
||||
@@ -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,6 +37,7 @@ function validate(): FormError[] {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<component :is="Empty || 'div'">
|
||||
<div class="h-[100vh] flex flex-col items-center justify-center px-4 sm:px-6 lg:px-8">
|
||||
<div class="text-center mb-15">
|
||||
<div
|
||||
@@ -84,11 +86,13 @@ function validate(): FormError[] {
|
||||
</UButton>
|
||||
</UForm>
|
||||
|
||||
<div class="text-center flex flex-col gap-3 border-t dark:border-(--border-dark) border-(--border-light) pt-4">
|
||||
<div
|
||||
class="text-center flex flex-col gap-3 border-t dark:border-(--border-dark) border-(--border-light) pt-4">
|
||||
<button color="neutral" variant="outline" :loading="authStore.loading"
|
||||
class="w-full flex items-center gap-2 justify-center text-[15px] dark:text-white text-black cursor-pointer"
|
||||
@click="goToLogin">
|
||||
<UIcon name="mingcute:arrow-left-line" class="text-(--accent-blue-light) dark:text-(--accent-blue-dark) text-[16px]" />
|
||||
<UIcon name="mingcute:arrow-left-line"
|
||||
class="text-(--accent-blue-light) dark:text-(--accent-blue-dark) text-[16px]" />
|
||||
{{ $t('general.back_to_sign_in') }}
|
||||
</button>
|
||||
<p class="text-sm text-gray-600 dark:text-gray-400">
|
||||
@@ -102,4 +106,5 @@ function validate(): FormError[] {
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</component>
|
||||
</template>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<template>
|
||||
<component :is="Empty || 'div'">
|
||||
<UDrawer v-model:open="showTherms" :overlay="false">
|
||||
<template #body>
|
||||
<component :is="TermsComponent" />
|
||||
@@ -110,6 +111,7 @@
|
||||
</UForm>
|
||||
</div>
|
||||
</div>
|
||||
</component>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
@@ -120,6 +122,8 @@ 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 { locale } = useI18n()
|
||||
const router = useRouter()
|
||||
|
||||
@@ -15,8 +15,6 @@ import { useAuthStore } from '@/stores/auth'
|
||||
import { i18n } from '@/plugins/02_i18n'
|
||||
import type { TableColumn } from '@nuxt/ui'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import ProductDetailView from './customer/ProductDetailView.vue'
|
||||
import ProductsView from './customer/ProductsView.vue'
|
||||
|
||||
ChartJS.register(Title, Tooltip, Legend, BarElement, CategoryScale, LinearScale)
|
||||
|
||||
|
||||
@@ -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 route = useRoute()
|
||||
@@ -49,6 +50,7 @@ function validate(): FormError[] {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<component :is="Empty || 'div'">
|
||||
<div class="h-[100vh] flex flex-col items-center justify-center px-4 sm:px-6 lg:px-8">
|
||||
<div class="text-center mb-15">
|
||||
<div
|
||||
@@ -85,13 +87,14 @@ function validate(): FormError[] {
|
||||
class="w-full dark:text-white text-black">
|
||||
<UInput v-model="new_password" :type="showNewPassword ? 'text' : 'password'"
|
||||
:placeholder="$t('general.enter_your_new_password')" :disabled="authStore.loading"
|
||||
class="w-full dark:text-white text-black placeholder:text-(--placeholder)" :ui="{ trailing: 'pe-1' }">
|
||||
class="w-full dark:text-white text-black placeholder:text-(--placeholder)"
|
||||
:ui="{ trailing: 'pe-1' }">
|
||||
<template #trailing>
|
||||
<UIcon color="neutral" variant="link" size="sm"
|
||||
:name="showNewPassword ? 'i-lucide-eye-off' : 'i-lucide-eye'"
|
||||
:aria-label="showNewPassword ? 'Hide password' : 'Show password'"
|
||||
:aria-pressed="showNewPassword" aria-controls="new_password"
|
||||
@click="showNewPassword = !showNewPassword" class="mr-2"/>
|
||||
@click="showNewPassword = !showNewPassword" class="mr-2" />
|
||||
</template>
|
||||
</UInput>
|
||||
</UFormField>
|
||||
@@ -100,11 +103,12 @@ function validate(): FormError[] {
|
||||
class="w-full dark:text-white text-black">
|
||||
<UInput v-model="confirm_new_password" :type="showConfirmPassword ? 'text' : 'password'"
|
||||
:placeholder="$t('general.confirm_your_new_password')" :disabled="authStore.loading"
|
||||
class="w-full dark:text-white text-black placeholder:text-(--placeholder)" :ui="{ trailing: 'pe-1' }">
|
||||
class="w-full dark:text-white text-black placeholder:text-(--placeholder)"
|
||||
:ui="{ trailing: 'pe-1' }">
|
||||
<template #trailing>
|
||||
<UIcon color="neutral" variant="ghost" size="sm"
|
||||
:name="showConfirmPassword ? 'i-lucide-eye-off' : 'i-lucide-eye'"
|
||||
@click="showConfirmPassword = !showConfirmPassword" class="mr-2"/>
|
||||
@click="showConfirmPassword = !showConfirmPassword" class="mr-2" />
|
||||
</template>
|
||||
</UInput>
|
||||
</UFormField>
|
||||
@@ -125,4 +129,5 @@ function validate(): FormError[] {
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</component>
|
||||
</template>
|
||||
|
||||
@@ -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,6 +67,7 @@ function goToLogin() {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<component :is="Empty || 'div'">
|
||||
<div
|
||||
class="min-h-screen bg-gradient-to-br from-primary-50 via-white to-primary-100 dark:from-gray-900 dark:via-gray-800 dark:to-gray-900">
|
||||
<div class="pt-20 pb-8 flex items-center justify-center px-4 sm:px-6 lg:px-8">
|
||||
@@ -133,7 +135,8 @@ function goToLogin() {
|
||||
<p class="text-sm text-gray-600 dark:text-gray-400">
|
||||
{{ $t('verify_email.already_registered') }}
|
||||
<button variant="link" size="sm" @click="goToLogin"
|
||||
class="cursor-pointer text-(--accent-blue-light) dark:text-(--accent-blue-dark)"> {{ $t('general.sign_in')
|
||||
class="cursor-pointer text-(--accent-blue-light) dark:text-(--accent-blue-dark)"> {{
|
||||
$t('general.sign_in')
|
||||
}}
|
||||
</button>
|
||||
</p>
|
||||
@@ -143,4 +146,5 @@ function goToLogin() {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</component>
|
||||
</template>
|
||||
|
||||
@@ -1,134 +0,0 @@
|
||||
<template>
|
||||
<div class="container my-10 ">
|
||||
|
||||
<UButton>Translations</UButton>
|
||||
<div class="flex items-start gap-30">
|
||||
<div class="flex flex-col gap-10">
|
||||
<p class="p-60 bg-yellow-300">img</p>
|
||||
</div>
|
||||
<div class="flex flex-col gap-2">
|
||||
<p class="text-[25px] font-bold">{{ productStore.productDescription.name }}</p>
|
||||
<p v-html="productStore.productDescription.description_short"></p>
|
||||
|
||||
<div>
|
||||
<p>Ilość:</p>
|
||||
<div
|
||||
class="flex items-center w-[15%] border border-(--border-light) dark:border-(--border-dark) rounded bg-gray-200">
|
||||
<button @click="decrement" class="px-3 py-1 cursor-pointer">-</button>
|
||||
<span class="px-6">{{ quantity }}</span>
|
||||
<button @click="increment" class="px-3 py-1 cursor-pointer">+</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="space-[10px]">
|
||||
<div class="flex gap-1 items-center">
|
||||
<UIcon name="lets-icons:done-ring-round-fill" class="text-[20px] text-green-600" />
|
||||
<p class=" gap-1text-[16px] font-bold text-(--accent-blue-light) dark:text-(--accent-blue-dark)">{{
|
||||
productStore.productDescription.available_now }}</p>
|
||||
</div>
|
||||
<div class="flex gap-1 items-center">
|
||||
<UIcon name="marketeq:car-shipping" class="text-[25px] text-green-600" />
|
||||
<p class="text-[18px] font-bold">{{ productStore.productDescription.delivery_in_stock }}</p>
|
||||
</div>
|
||||
<UButton class="cursor-pointer">
|
||||
<UIcon name="tdesign:cart-filled" class="text-[20px]" />
|
||||
<p> Dodaj do koszyka </p>
|
||||
</UButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-16">
|
||||
<div class="flex gap-4 m-2">
|
||||
<UButton @click="activeTab = 'description'"
|
||||
:class="['cursor-pointer', activeTab === 'description' ? 'bg-blue-500 text-white' : '']" color="neutral"
|
||||
variant="outline">
|
||||
<p>Description</p>
|
||||
</UButton>
|
||||
|
||||
<UButton @click="activeTab = 'usage'"
|
||||
:class="['cursor-pointer', activeTab === 'usage' ? 'bg-blue-500 text-white' : '']" color="neutral"
|
||||
variant="outline">
|
||||
<p>Usage</p>
|
||||
</UButton>
|
||||
</div>
|
||||
|
||||
<div v-if="activeTab === 'usage'">
|
||||
<div class="flex items-center gap-3 mb-3">
|
||||
<UButton @click="usageEdit.enableEdit()" class="flex items-center gap-2 m-2 cursor-pointer">
|
||||
<p>Create Text</p>
|
||||
<UIcon name="material-symbols-light:stylus-note-sharp" class="text-[30px]" />
|
||||
</UButton>
|
||||
<UButton @click="save" color="neutral" variant="outline" class="p-2.5 cursor-pointer">
|
||||
<p>Save the edited text</p>
|
||||
</UButton>
|
||||
</div>
|
||||
<p ref="usageRef" v-html="productStore.productDescription.usage"
|
||||
class="p-8 flex flex-col justify-center w-full text-start border border-(--border-light) dark:border-(--border-dark) rounded-md bg-(--second-light) dark:bg-(--main-dark) dark:text-white text-black">
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div v-if="activeTab === 'description'">
|
||||
<div class="flex items-center gap-3 mb-3">
|
||||
<UButton @click="descriptionEdit.enableEdit()" class="flex items-center gap-2 m-2 cursor-pointer">
|
||||
<p>Create Text</p>
|
||||
<UIcon name="material-symbols-light:stylus-note-sharp" class="text-[30px]" />
|
||||
</UButton>
|
||||
<UButton @click="save" color="neutral" variant="outline" class="p-2.5 cursor-pointer">
|
||||
<p>Save the edited text</p>
|
||||
</UButton>
|
||||
</div>
|
||||
<div ref="descriptionRef" v-html="productStore.productDescription.description"
|
||||
class="p-8 flex flex-col justify-center border border-(--border-light) dark:border-(--border-dark) rounded-md bg-(--second-light) dark:bg-(--main-dark) dark:text-white text-black">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { useProductStore } from '@/stores/product'
|
||||
import { useEditable } from '@/composable/useConteditable'
|
||||
|
||||
const activeTab = ref('description')
|
||||
const productStore = useProductStore()
|
||||
await productStore.getProductDescription()
|
||||
await productStore.getProductDescriptionTranslations()
|
||||
|
||||
const quantity = ref(1)
|
||||
|
||||
const increment = () => {
|
||||
quantity.value += 1
|
||||
}
|
||||
const decrement = () => {
|
||||
if (quantity.value > 1) quantity.value -= 1
|
||||
}
|
||||
const descriptionRef = ref<HTMLElement | null>(null)
|
||||
const usageRef = ref<HTMLElement | null>(null)
|
||||
|
||||
const descriptionEdit = useEditable(descriptionRef)
|
||||
const usageEdit = useEditable(usageRef)
|
||||
|
||||
const save = async () => {
|
||||
const description = descriptionEdit.getCleanHtml()
|
||||
const usage = usageEdit.getCleanHtml()
|
||||
|
||||
descriptionEdit.disableEdit()
|
||||
usageEdit.disableEdit()
|
||||
// await productStore.saveProductDescription({
|
||||
// description,
|
||||
// usage
|
||||
// })
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.images {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 70px;
|
||||
margin: 20px 0 20px 0;
|
||||
}
|
||||
</style>
|
||||
719
bun.lock
Normal file
719
bun.lock
Normal file
@@ -0,0 +1,719 @@
|
||||
{
|
||||
"lockfileVersion": 1,
|
||||
"configVersion": 0,
|
||||
"workspaces": {
|
||||
"": {
|
||||
"dependencies": {
|
||||
"@nuxt/ui": "^4.5.1",
|
||||
"chart.js": "^4.5.1",
|
||||
"dompurify": "^3.3.3",
|
||||
"vue-chartjs": "^5.3.3",
|
||||
},
|
||||
},
|
||||
},
|
||||
"packages": {
|
||||
"@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="],
|
||||
|
||||
"@antfu/install-pkg": ["@antfu/install-pkg@1.1.0", "", { "dependencies": { "package-manager-detector": "^1.3.0", "tinyexec": "^1.0.1" } }, "sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ=="],
|
||||
|
||||
"@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="],
|
||||
|
||||
"@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="],
|
||||
|
||||
"@babel/parser": ["@babel/parser@7.29.0", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": { "parser": "bin/babel-parser.js" } }, "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww=="],
|
||||
|
||||
"@babel/types": ["@babel/types@7.29.0", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A=="],
|
||||
|
||||
"@capsizecss/unpack": ["@capsizecss/unpack@4.0.0", "", { "dependencies": { "fontkitten": "^1.0.0" } }, "sha512-VERIM64vtTP1C4mxQ5thVT9fK0apjPFobqybMtA1UdUujWka24ERHbRHFGmpbbhp73MhV+KSsHQH9C6uOTdEQA=="],
|
||||
|
||||
"@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.3", "", { "os": "aix", "cpu": "ppc64" }, "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg=="],
|
||||
|
||||
"@esbuild/android-arm": ["@esbuild/android-arm@0.27.3", "", { "os": "android", "cpu": "arm" }, "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA=="],
|
||||
|
||||
"@esbuild/android-arm64": ["@esbuild/android-arm64@0.27.3", "", { "os": "android", "cpu": "arm64" }, "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg=="],
|
||||
|
||||
"@esbuild/android-x64": ["@esbuild/android-x64@0.27.3", "", { "os": "android", "cpu": "x64" }, "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ=="],
|
||||
|
||||
"@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.27.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg=="],
|
||||
|
||||
"@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.27.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg=="],
|
||||
|
||||
"@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.27.3", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w=="],
|
||||
|
||||
"@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.27.3", "", { "os": "freebsd", "cpu": "x64" }, "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA=="],
|
||||
|
||||
"@esbuild/linux-arm": ["@esbuild/linux-arm@0.27.3", "", { "os": "linux", "cpu": "arm" }, "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw=="],
|
||||
|
||||
"@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.27.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg=="],
|
||||
|
||||
"@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.27.3", "", { "os": "linux", "cpu": "ia32" }, "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg=="],
|
||||
|
||||
"@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.27.3", "", { "os": "linux", "cpu": "none" }, "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA=="],
|
||||
|
||||
"@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.27.3", "", { "os": "linux", "cpu": "none" }, "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw=="],
|
||||
|
||||
"@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.27.3", "", { "os": "linux", "cpu": "ppc64" }, "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA=="],
|
||||
|
||||
"@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.27.3", "", { "os": "linux", "cpu": "none" }, "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ=="],
|
||||
|
||||
"@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.27.3", "", { "os": "linux", "cpu": "s390x" }, "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw=="],
|
||||
|
||||
"@esbuild/linux-x64": ["@esbuild/linux-x64@0.27.3", "", { "os": "linux", "cpu": "x64" }, "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA=="],
|
||||
|
||||
"@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.27.3", "", { "os": "none", "cpu": "arm64" }, "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA=="],
|
||||
|
||||
"@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.27.3", "", { "os": "none", "cpu": "x64" }, "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA=="],
|
||||
|
||||
"@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.27.3", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw=="],
|
||||
|
||||
"@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.27.3", "", { "os": "openbsd", "cpu": "x64" }, "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ=="],
|
||||
|
||||
"@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.27.3", "", { "os": "none", "cpu": "arm64" }, "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g=="],
|
||||
|
||||
"@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.27.3", "", { "os": "sunos", "cpu": "x64" }, "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA=="],
|
||||
|
||||
"@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.27.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA=="],
|
||||
|
||||
"@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.27.3", "", { "os": "win32", "cpu": "ia32" }, "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q=="],
|
||||
|
||||
"@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.3", "", { "os": "win32", "cpu": "x64" }, "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA=="],
|
||||
|
||||
"@floating-ui/core": ["@floating-ui/core@1.7.5", "", { "dependencies": { "@floating-ui/utils": "^0.2.11" } }, "sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ=="],
|
||||
|
||||
"@floating-ui/dom": ["@floating-ui/dom@1.7.6", "", { "dependencies": { "@floating-ui/core": "^1.7.5", "@floating-ui/utils": "^0.2.11" } }, "sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ=="],
|
||||
|
||||
"@floating-ui/utils": ["@floating-ui/utils@0.2.11", "", {}, "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg=="],
|
||||
|
||||
"@floating-ui/vue": ["@floating-ui/vue@1.1.11", "", { "dependencies": { "@floating-ui/dom": "^1.7.6", "@floating-ui/utils": "^0.2.11", "vue-demi": ">=0.13.0" } }, "sha512-HzHKCNVxnGS35r9fCHBc3+uCnjw9IWIlCPL683cGgM9Kgj2BiAl8x1mS7vtvP6F9S/e/q4O6MApwSHj8hNLGfw=="],
|
||||
|
||||
"@iconify/collections": ["@iconify/collections@1.0.656", "", { "dependencies": { "@iconify/types": "*" } }, "sha512-X17vsrStihw4gEdgT2+46E+ioM506JJh/Jp/reJGZ2URcWhhS4aMnsi9onoGTQ8ppydZ0GJrPuvOyhySiteKsA=="],
|
||||
|
||||
"@iconify/types": ["@iconify/types@2.0.0", "", {}, "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg=="],
|
||||
|
||||
"@iconify/utils": ["@iconify/utils@3.1.0", "", { "dependencies": { "@antfu/install-pkg": "^1.1.0", "@iconify/types": "^2.0.0", "mlly": "^1.8.0" } }, "sha512-Zlzem1ZXhI1iHeeERabLNzBHdOa4VhQbqAcOQaMKuTuyZCpwKbC2R4Dd0Zo3g9EAc+Y4fiarO8HIHRAth7+skw=="],
|
||||
|
||||
"@iconify/vue": ["@iconify/vue@5.0.0", "", { "dependencies": { "@iconify/types": "^2.0.0" }, "peerDependencies": { "vue": ">=3" } }, "sha512-C+KuEWIF5nSBrobFJhT//JS87OZ++QDORB6f2q2Wm6fl2mueSTpFBeBsveK0KW9hWiZ4mNiPjsh6Zs4jjdROSg=="],
|
||||
|
||||
"@internationalized/date": ["@internationalized/date@3.11.0", "", { "dependencies": { "@swc/helpers": "^0.5.0" } }, "sha512-BOx5huLAWhicM9/ZFs84CzP+V3gBW6vlpM02yzsdYC7TGlZJX1OJiEEHcSayF00Z+3jLlm4w79amvSt6RqKN3Q=="],
|
||||
|
||||
"@internationalized/number": ["@internationalized/number@3.6.5", "", { "dependencies": { "@swc/helpers": "^0.5.0" } }, "sha512-6hY4Kl4HPBvtfS62asS/R22JzNNy8vi/Ssev7x6EobfCp+9QIB2hKvI2EtbdJ0VSQacxVNtqhE/NmF/NZ0gm6g=="],
|
||||
|
||||
"@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="],
|
||||
|
||||
"@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="],
|
||||
|
||||
"@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="],
|
||||
|
||||
"@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="],
|
||||
|
||||
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
|
||||
|
||||
"@kurkle/color": ["@kurkle/color@0.3.4", "", {}, "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w=="],
|
||||
|
||||
"@nuxt/devtools-kit": ["@nuxt/devtools-kit@3.2.2", "", { "dependencies": { "@nuxt/kit": "^4.3.1", "execa": "^8.0.1" }, "peerDependencies": { "vite": ">=6.0" } }, "sha512-07E1phqoVPNlexlkrYuOMPhTzLIRjcl9iEqyc/vZLH2zWeH/T1X3v+RLTVW5Oio40f/XBp9yQuyihmX34ddjgQ=="],
|
||||
|
||||
"@nuxt/fonts": ["@nuxt/fonts@0.14.0", "", { "dependencies": { "@nuxt/devtools-kit": "^3.2.1", "@nuxt/kit": "^4.2.2", "consola": "^3.4.2", "defu": "^6.1.4", "fontless": "^0.2.1", "h3": "^1.15.5", "magic-regexp": "^0.10.0", "ofetch": "^1.5.1", "pathe": "^2.0.3", "sirv": "^3.0.2", "tinyglobby": "^0.2.15", "ufo": "^1.6.3", "unifont": "^0.7.4", "unplugin": "^3.0.0", "unstorage": "^1.17.4" } }, "sha512-4uXQl9fa5F4ibdgU8zomoOcyMdnwgdem+Pi8JEqeDYI5yPR32Kam6HnuRr47dTb97CstaepAvXPWQUUHMtjsFQ=="],
|
||||
|
||||
"@nuxt/icon": ["@nuxt/icon@2.2.1", "", { "dependencies": { "@iconify/collections": "^1.0.641", "@iconify/types": "^2.0.0", "@iconify/utils": "^3.1.0", "@iconify/vue": "^5.0.0", "@nuxt/devtools-kit": "^3.1.1", "@nuxt/kit": "^4.2.2", "consola": "^3.4.2", "local-pkg": "^1.1.2", "mlly": "^1.8.0", "ohash": "^2.0.11", "pathe": "^2.0.3", "picomatch": "^4.0.3", "std-env": "^3.10.0", "tinyglobby": "^0.2.15" } }, "sha512-GI840yYGuvHI0BGDQ63d6rAxGzG96jQcWrnaWIQKlyQo/7sx9PjXkSHckXUXyX1MCr9zY6U25Td6OatfY6Hklw=="],
|
||||
|
||||
"@nuxt/kit": ["@nuxt/kit@4.3.1", "", { "dependencies": { "c12": "^3.3.3", "consola": "^3.4.2", "defu": "^6.1.4", "destr": "^2.0.5", "errx": "^0.1.0", "exsolve": "^1.0.8", "ignore": "^7.0.5", "jiti": "^2.6.1", "klona": "^2.0.6", "mlly": "^1.8.0", "ohash": "^2.0.11", "pathe": "^2.0.3", "pkg-types": "^2.3.0", "rc9": "^3.0.0", "scule": "^1.3.0", "semver": "^7.7.4", "tinyglobby": "^0.2.15", "ufo": "^1.6.3", "unctx": "^2.5.0", "untyped": "^2.0.0" } }, "sha512-UjBFt72dnpc+83BV3OIbCT0YHLevJtgJCHpxMX0YRKWLDhhbcDdUse87GtsQBrjvOzK7WUNUYLDS/hQLYev5rA=="],
|
||||
|
||||
"@nuxt/schema": ["@nuxt/schema@4.3.1", "", { "dependencies": { "@vue/shared": "^3.5.27", "defu": "^6.1.4", "pathe": "^2.0.3", "pkg-types": "^2.3.0", "std-env": "^3.10.0" } }, "sha512-S+wHJdYDuyk9I43Ej27y5BeWMZgi7R/UVql3b3qtT35d0fbpXW7fUenzhLRCCDC6O10sjguc6fcMcR9sMKvV8g=="],
|
||||
|
||||
"@nuxt/ui": ["@nuxt/ui@4.5.1", "", { "dependencies": { "@floating-ui/dom": "^1.7.5", "@iconify/vue": "^5.0.0", "@internationalized/date": "^3.11.0", "@internationalized/number": "^3.6.5", "@nuxt/fonts": "^0.14.0", "@nuxt/icon": "^2.2.1", "@nuxt/kit": "^4.3.1", "@nuxt/schema": "^4.3.1", "@nuxtjs/color-mode": "^3.5.2", "@standard-schema/spec": "^1.1.0", "@tailwindcss/postcss": "^4.2.1", "@tailwindcss/vite": "^4.2.1", "@tanstack/vue-table": "^8.21.3", "@tanstack/vue-virtual": "^3.13.19", "@tiptap/core": "^3.20.0", "@tiptap/extension-bubble-menu": "^3.20.0", "@tiptap/extension-code": "^3.20.0", "@tiptap/extension-collaboration": "^3.20.0", "@tiptap/extension-drag-handle": "^3.20.0", "@tiptap/extension-drag-handle-vue-3": "^3.20.0", "@tiptap/extension-floating-menu": "^3.20.0", "@tiptap/extension-horizontal-rule": "^3.20.0", "@tiptap/extension-image": "^3.20.0", "@tiptap/extension-mention": "^3.20.0", "@tiptap/extension-node-range": "^3.20.0", "@tiptap/extension-placeholder": "^3.20.0", "@tiptap/markdown": "^3.20.0", "@tiptap/pm": "^3.20.0", "@tiptap/starter-kit": "^3.20.0", "@tiptap/suggestion": "^3.20.0", "@tiptap/vue-3": "^3.20.0", "@unhead/vue": "^2.1.10", "@vueuse/core": "^14.2.1", "@vueuse/integrations": "^14.2.1", "@vueuse/shared": "^14.2.1", "colortranslator": "^5.0.0", "consola": "^3.4.2", "defu": "^6.1.4", "embla-carousel-auto-height": "^8.6.0", "embla-carousel-auto-scroll": "^8.6.0", "embla-carousel-autoplay": "^8.6.0", "embla-carousel-class-names": "^8.6.0", "embla-carousel-fade": "^8.6.0", "embla-carousel-vue": "^8.6.0", "embla-carousel-wheel-gestures": "^8.1.0", "fuse.js": "^7.1.0", "hookable": "^5.5.3", "knitwork": "^1.3.0", "magic-string": "^0.30.21", "mlly": "^1.8.0", "motion-v": "^1.10.3", "ohash": "^2.0.11", "pathe": "^2.0.3", "reka-ui": "2.8.2", "scule": "^1.3.0", "tailwind-merge": "^3.5.0", "tailwind-variants": "^3.2.2", "tailwindcss": "^4.2.1", "tinyglobby": "^0.2.15", "ufo": "^1.6.3", "unplugin": "^3.0.0", "unplugin-auto-import": "^21.0.0", "unplugin-vue-components": "^31.0.0", "vaul-vue": "0.4.1", "vue-component-type-helpers": "^3.2.5" }, "peerDependencies": { "@inertiajs/vue3": "^2.0.7", "@nuxt/content": "^3.0.0", "joi": "^18.0.0", "superstruct": "^2.0.0", "tailwindcss": "^4.0.0", "typescript": "^5.9.3", "valibot": "^1.0.0", "vue-router": "^4.5.0", "yup": "^1.7.0", "zod": "^3.24.0 || ^4.0.0" }, "optionalPeers": ["@inertiajs/vue3", "@nuxt/content", "joi", "superstruct", "valibot", "vue-router", "yup", "zod"], "bin": { "nuxt-ui": "cli/index.mjs" } }, "sha512-5hWgreVPX6EsNCZNoOd2o7m9fTA3fwUMDw+zeYTSAjhSKtAuvkZrBtmez4MUeTv+LO1gknesgvErdIvlUnElTg=="],
|
||||
|
||||
"@nuxtjs/color-mode": ["@nuxtjs/color-mode@3.5.2", "", { "dependencies": { "@nuxt/kit": "^3.13.2", "pathe": "^1.1.2", "pkg-types": "^1.2.1", "semver": "^7.6.3" } }, "sha512-cC6RfgZh3guHBMLLjrBB2Uti5eUoGM9KyauOaYS9ETmxNWBMTvpgjvSiSJp1OFljIXPIqVTJ3xtJpSNZiO3ZaA=="],
|
||||
|
||||
"@polka/url": ["@polka/url@1.0.0-next.29", "", {}, "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww=="],
|
||||
|
||||
"@remirror/core-constants": ["@remirror/core-constants@3.0.0", "", {}, "sha512-42aWfPrimMfDKDi4YegyS7x+/0tlzaqwPQCULLanv3DMIlu96KTJR0fM5isWX2UViOqlGnX6YFgqWepcX+XMNg=="],
|
||||
|
||||
"@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
|
||||
|
||||
"@swc/helpers": ["@swc/helpers@0.5.19", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-QamiFeIK3txNjgUTNppE6MiG3p7TdninpZu0E0PbqVh1a9FNLT2FRhisaa4NcaX52XVhA5l7Pk58Ft7Sqi/2sA=="],
|
||||
|
||||
"@tailwindcss/node": ["@tailwindcss/node@4.2.1", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "enhanced-resolve": "^5.19.0", "jiti": "^2.6.1", "lightningcss": "1.31.1", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.2.1" } }, "sha512-jlx6sLk4EOwO6hHe1oCGm1Q4AN/s0rSrTTPBGPM0/RQ6Uylwq17FuU8IeJJKEjtc6K6O07zsvP+gDO6MMWo7pg=="],
|
||||
|
||||
"@tailwindcss/oxide": ["@tailwindcss/oxide@4.2.1", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.2.1", "@tailwindcss/oxide-darwin-arm64": "4.2.1", "@tailwindcss/oxide-darwin-x64": "4.2.1", "@tailwindcss/oxide-freebsd-x64": "4.2.1", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.1", "@tailwindcss/oxide-linux-arm64-gnu": "4.2.1", "@tailwindcss/oxide-linux-arm64-musl": "4.2.1", "@tailwindcss/oxide-linux-x64-gnu": "4.2.1", "@tailwindcss/oxide-linux-x64-musl": "4.2.1", "@tailwindcss/oxide-wasm32-wasi": "4.2.1", "@tailwindcss/oxide-win32-arm64-msvc": "4.2.1", "@tailwindcss/oxide-win32-x64-msvc": "4.2.1" } }, "sha512-yv9jeEFWnjKCI6/T3Oq50yQEOqmpmpfzG1hcZsAOaXFQPfzWprWrlHSdGPEF3WQTi8zu8ohC9Mh9J470nT5pUw=="],
|
||||
|
||||
"@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.2.1", "", { "os": "android", "cpu": "arm64" }, "sha512-eZ7G1Zm5EC8OOKaesIKuw77jw++QJ2lL9N+dDpdQiAB/c/B2wDh0QPFHbkBVrXnwNugvrbJFk1gK2SsVjwWReg=="],
|
||||
|
||||
"@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.2.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-q/LHkOstoJ7pI1J0q6djesLzRvQSIfEto148ppAd+BVQK0JYjQIFSK3JgYZJa+Yzi0DDa52ZsQx2rqytBnf8Hw=="],
|
||||
|
||||
"@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.2.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-/f/ozlaXGY6QLbpvd/kFTro2l18f7dHKpB+ieXz+Cijl4Mt9AI2rTrpq7V+t04nK+j9XBQHnSMdeQRhbGyt6fw=="],
|
||||
|
||||
"@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.2.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-5e/AkgYJT/cpbkys/OU2Ei2jdETCLlifwm7ogMC7/hksI2fC3iiq6OcXwjibcIjPung0kRtR3TxEITkqgn0TcA=="],
|
||||
|
||||
"@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.2.1", "", { "os": "linux", "cpu": "arm" }, "sha512-Uny1EcVTTmerCKt/1ZuKTkb0x8ZaiuYucg2/kImO5A5Y/kBz41/+j0gxUZl+hTF3xkWpDmHX+TaWhOtba2Fyuw=="],
|
||||
|
||||
"@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.2.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-CTrwomI+c7n6aSSQlsPL0roRiNMDQ/YzMD9EjcR+H4f0I1SQ8QqIuPnsVp7QgMkC1Qi8rtkekLkOFjo7OlEFRQ=="],
|
||||
|
||||
"@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.2.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-WZA0CHRL/SP1TRbA5mp9htsppSEkWuQ4KsSUumYQnyl8ZdT39ntwqmz4IUHGN6p4XdSlYfJwM4rRzZLShHsGAQ=="],
|
||||
|
||||
"@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.2.1", "", { "os": "linux", "cpu": "x64" }, "sha512-qMFzxI2YlBOLW5PhblzuSWlWfwLHaneBE0xHzLrBgNtqN6mWfs+qYbhryGSXQjFYB1Dzf5w+LN5qbUTPhW7Y5g=="],
|
||||
|
||||
"@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.2.1", "", { "os": "linux", "cpu": "x64" }, "sha512-5r1X2FKnCMUPlXTWRYpHdPYUY6a1Ar/t7P24OuiEdEOmms5lyqjDRvVY1yy9Rmioh+AunQ0rWiOTPE8F9A3v5g=="],
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.2.1", "", { "cpu": "none" }, "sha512-MGFB5cVPvshR85MTJkEvqDUnuNoysrsRxd6vnk1Lf2tbiqNlXpHYZqkqOQalydienEWOHHFyyuTSYRsLfxFJ2Q=="],
|
||||
|
||||
"@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.2.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-YlUEHRHBGnCMh4Nj4GnqQyBtsshUPdiNroZj8VPkvTZSoHsilRCwXcVKnG9kyi0ZFAS/3u+qKHBdDc81SADTRA=="],
|
||||
|
||||
"@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.2.1", "", { "os": "win32", "cpu": "x64" }, "sha512-rbO34G5sMWWyrN/idLeVxAZgAKWrn5LiR3/I90Q9MkA67s6T1oB0xtTe+0heoBvHSpbU9Mk7i6uwJnpo4u21XQ=="],
|
||||
|
||||
"@tailwindcss/postcss": ["@tailwindcss/postcss@4.2.1", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "@tailwindcss/node": "4.2.1", "@tailwindcss/oxide": "4.2.1", "postcss": "^8.5.6", "tailwindcss": "4.2.1" } }, "sha512-OEwGIBnXnj7zJeonOh6ZG9woofIjGrd2BORfvE5p9USYKDCZoQmfqLcfNiRWoJlRWLdNPn2IgVZuWAOM4iTYMw=="],
|
||||
|
||||
"@tailwindcss/vite": ["@tailwindcss/vite@4.2.1", "", { "dependencies": { "@tailwindcss/node": "4.2.1", "@tailwindcss/oxide": "4.2.1", "tailwindcss": "4.2.1" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7" } }, "sha512-TBf2sJjYeb28jD2U/OhwdW0bbOsxkWPwQ7SrqGf9sVcoYwZj7rkXljroBO9wKBut9XnmQLXanuDUeqQK0lGg/w=="],
|
||||
|
||||
"@tanstack/table-core": ["@tanstack/table-core@8.21.3", "", {}, "sha512-ldZXEhOBb8Is7xLs01fR3YEc3DERiz5silj8tnGkFZytt1abEvl/GhUmCE0PMLaMPTa3Jk4HbKmRlHmu+gCftg=="],
|
||||
|
||||
"@tanstack/virtual-core": ["@tanstack/virtual-core@3.13.19", "", {}, "sha512-/BMP7kNhzKOd7wnDeB8NrIRNLwkf5AhCYCvtfZV2GXWbBieFm/el0n6LOAXlTi6ZwHICSNnQcIxRCWHrLzDY+g=="],
|
||||
|
||||
"@tanstack/vue-table": ["@tanstack/vue-table@8.21.3", "", { "dependencies": { "@tanstack/table-core": "8.21.3" }, "peerDependencies": { "vue": ">=3.2" } }, "sha512-rusRyd77c5tDPloPskctMyPLFEQUeBzxdQ+2Eow4F7gDPlPOB1UnnhzfpdvqZ8ZyX2rRNGmqNnQWm87OI2OQPw=="],
|
||||
|
||||
"@tanstack/vue-virtual": ["@tanstack/vue-virtual@3.13.19", "", { "dependencies": { "@tanstack/virtual-core": "3.13.19" }, "peerDependencies": { "vue": "^2.7.0 || ^3.0.0" } }, "sha512-07Fp1TYuIziB4zIDA/moeDKHODePy3K1fN4c4VIAGnkxo1+uOvBJP7m54CoxKiQX6Q9a1dZnznrwOg9C86yvvA=="],
|
||||
|
||||
"@tiptap/core": ["@tiptap/core@3.20.0", "", { "peerDependencies": { "@tiptap/pm": "^3.20.0" } }, "sha512-aC9aROgia/SpJqhsXFiX9TsligL8d+oeoI8W3u00WI45s0VfsqjgeKQLDLF7Tu7hC+7F02teC84SAHuup003VQ=="],
|
||||
|
||||
"@tiptap/extension-blockquote": ["@tiptap/extension-blockquote@3.20.0", "", { "peerDependencies": { "@tiptap/core": "^3.20.0" } }, "sha512-LQzn6aGtL4WXz2+rYshl/7/VnP2qJTpD7fWL96GXAzhqviPEY1bJES7poqJb3MU/gzl8VJUVzVzU1VoVfUKlbA=="],
|
||||
|
||||
"@tiptap/extension-bold": ["@tiptap/extension-bold@3.20.0", "", { "peerDependencies": { "@tiptap/core": "^3.20.0" } }, "sha512-sQklEWiyf58yDjiHtm5vmkVjfIc/cBuSusmCsQ0q9vGYnEF1iOHKhGpvnCeEXNeqF3fiJQRlquzt/6ymle3Iwg=="],
|
||||
|
||||
"@tiptap/extension-bubble-menu": ["@tiptap/extension-bubble-menu@3.20.0", "", { "dependencies": { "@floating-ui/dom": "^1.0.0" }, "peerDependencies": { "@tiptap/core": "^3.20.0", "@tiptap/pm": "^3.20.0" } }, "sha512-MDosUfs8Tj+nwg8RC+wTMWGkLJORXmbR6YZgbiX4hrc7G90Gopdd6kj6ht5/T8t7dLLaX7N0+DEHdUEPGED7dw=="],
|
||||
|
||||
"@tiptap/extension-bullet-list": ["@tiptap/extension-bullet-list@3.20.0", "", { "peerDependencies": { "@tiptap/extension-list": "^3.20.0" } }, "sha512-OcKMeopBbqWzhSi6o8nNz0aayogg1sfOAhto3NxJu3Ya32dwBFqmHXSYM6uW4jOphNvVPyjiq9aNRh3qTdd1dw=="],
|
||||
|
||||
"@tiptap/extension-code": ["@tiptap/extension-code@3.20.0", "", { "peerDependencies": { "@tiptap/core": "^3.20.0" } }, "sha512-TYDWFeSQ9umiyrqsT6VecbuhL8XIHkUhO+gEk0sVvH67ZLwjFDhAIIgWIr1/dbIGPcvMZM19E7xUUhAdIaXaOQ=="],
|
||||
|
||||
"@tiptap/extension-code-block": ["@tiptap/extension-code-block@3.20.0", "", { "peerDependencies": { "@tiptap/core": "^3.20.0", "@tiptap/pm": "^3.20.0" } }, "sha512-lBbmNek14aCjrHcBcq3PRqWfNLvC6bcRa2Osc6e/LtmXlcpype4f6n+Yx+WZ+f2uUh0UmDRCz7BEyUETEsDmlQ=="],
|
||||
|
||||
"@tiptap/extension-collaboration": ["@tiptap/extension-collaboration@3.20.0", "", { "peerDependencies": { "@tiptap/core": "^3.20.0", "@tiptap/pm": "^3.20.0", "@tiptap/y-tiptap": "^3.0.2", "yjs": "^13" } }, "sha512-JItmI4U0i4kqorO114u24hM9k945IdaQ6Uc2DEtPBFFuS8cepJf2zw+ulAT1kAx6ZRiNvNpT9M7w+J0mWRn+Sg=="],
|
||||
|
||||
"@tiptap/extension-document": ["@tiptap/extension-document@3.20.0", "", { "peerDependencies": { "@tiptap/core": "^3.20.0" } }, "sha512-oJfLIG3vAtZo/wg29WiBcyWt22KUgddpP8wqtCE+kY5Dw8znLR9ehNmVWlSWJA5OJUMO0ntAHx4bBT+I2MBd5w=="],
|
||||
|
||||
"@tiptap/extension-drag-handle": ["@tiptap/extension-drag-handle@3.20.0", "", { "dependencies": { "@floating-ui/dom": "^1.6.13" }, "peerDependencies": { "@tiptap/core": "^3.20.0", "@tiptap/extension-collaboration": "^3.20.0", "@tiptap/extension-node-range": "^3.20.0", "@tiptap/pm": "^3.20.0", "@tiptap/y-tiptap": "^3.0.2" } }, "sha512-CzLRyxZe5QddQey0RUWJUvICyhuRnU/jvzMIYlFvMxM7W97sZ2ggk0cRThlRt2pRUoSr8mmmUnobiorpISmksA=="],
|
||||
|
||||
"@tiptap/extension-drag-handle-vue-3": ["@tiptap/extension-drag-handle-vue-3@3.20.0", "", { "peerDependencies": { "@tiptap/extension-drag-handle": "^3.20.0", "@tiptap/pm": "^3.20.0", "@tiptap/vue-3": "^3.20.0", "vue": "^3.0.0" } }, "sha512-Jx6LHYRI5uRaJVNQGkQsTFQkAM84rYQh3Q+WBePhGF4yPBUJQFn7Nv+5fQhKKV3A5PVQ6kTAjvW6SSUcD6ON8A=="],
|
||||
|
||||
"@tiptap/extension-dropcursor": ["@tiptap/extension-dropcursor@3.20.0", "", { "peerDependencies": { "@tiptap/extensions": "^3.20.0" } }, "sha512-d+cxplRlktVgZPwatnc34IArlppM0IFKS1J5wLk+ba1jidizsbMVh45tP/BTK2flhyfRqcNoB5R0TArhUpbkNQ=="],
|
||||
|
||||
"@tiptap/extension-floating-menu": ["@tiptap/extension-floating-menu@3.20.0", "", { "peerDependencies": { "@floating-ui/dom": "^1.0.0", "@tiptap/core": "^3.20.0", "@tiptap/pm": "^3.20.0" } }, "sha512-rYs4Bv5pVjqZ/2vvR6oe7ammZapkAwN51As/WDbemvYDjfOGRqK58qGauUjYZiDzPOEIzI2mxGwsZ4eJhPW4Ig=="],
|
||||
|
||||
"@tiptap/extension-gapcursor": ["@tiptap/extension-gapcursor@3.20.0", "", { "peerDependencies": { "@tiptap/extensions": "^3.20.0" } }, "sha512-P/LasfvG9/qFq43ZAlNbAnPnXC+/RJf49buTrhtFvI9Zg0+Lbpjx1oh6oMHB19T88Y28KtrckfFZ8aTSUWDq6w=="],
|
||||
|
||||
"@tiptap/extension-hard-break": ["@tiptap/extension-hard-break@3.20.0", "", { "peerDependencies": { "@tiptap/core": "^3.20.0" } }, "sha512-rqvhMOw4f+XQmEthncbvDjgLH6fz8L9splnKZC7OeS0eX8b0qd7+xI1u5kyxF3KA2Z0BnigES++jjWuecqV6mA=="],
|
||||
|
||||
"@tiptap/extension-heading": ["@tiptap/extension-heading@3.20.0", "", { "peerDependencies": { "@tiptap/core": "^3.20.0" } }, "sha512-JgJhurnCe3eN6a0lEsNQM/46R1bcwzwWWZEFDSb1P9dR8+t1/5v7cMZWsSInpD7R4/74iJn0+M5hcXLwCmBmYA=="],
|
||||
|
||||
"@tiptap/extension-horizontal-rule": ["@tiptap/extension-horizontal-rule@3.20.0", "", { "peerDependencies": { "@tiptap/core": "^3.20.0", "@tiptap/pm": "^3.20.0" } }, "sha512-6uvcutFMv+9wPZgptDkbRDjAm3YVxlibmkhWD5GuaWwS9L/yUtobpI3GycujRSUZ8D3q6Q9J7LqpmQtQRTalWA=="],
|
||||
|
||||
"@tiptap/extension-image": ["@tiptap/extension-image@3.20.0", "", { "peerDependencies": { "@tiptap/core": "^3.20.0" } }, "sha512-0t7HYncV0kYEQS79NFczxdlZoZ8zu8X4VavDqt+mbSAUKRq3gCvgtZ5Zyd778sNmtmbz3arxkEYMIVou2swD0g=="],
|
||||
|
||||
"@tiptap/extension-italic": ["@tiptap/extension-italic@3.20.0", "", { "peerDependencies": { "@tiptap/core": "^3.20.0" } }, "sha512-/DhnKQF8yN8RxtuL8abZ28wd5281EaGoE2Oha35zXSOF1vNYnbyt8Ymkv/7u1BcWEWTvRPgaju0YCGXisPRLYw=="],
|
||||
|
||||
"@tiptap/extension-link": ["@tiptap/extension-link@3.20.0", "", { "dependencies": { "linkifyjs": "^4.3.2" }, "peerDependencies": { "@tiptap/core": "^3.20.0", "@tiptap/pm": "^3.20.0" } }, "sha512-qI/5A+R0ZWBxo/8HxSn1uOyr7odr3xHBZ/gzOR1GUJaZqjlJxkWFX0RtXMbLKEGEvT25o345cF7b0wFznEh8qA=="],
|
||||
|
||||
"@tiptap/extension-list": ["@tiptap/extension-list@3.20.0", "", { "peerDependencies": { "@tiptap/core": "^3.20.0", "@tiptap/pm": "^3.20.0" } }, "sha512-+V0/gsVWAv+7vcY0MAe6D52LYTIicMSHw00wz3ISZgprSb2yQhJ4+4gurOnUrQ4Du3AnRQvxPROaofwxIQ66WQ=="],
|
||||
|
||||
"@tiptap/extension-list-item": ["@tiptap/extension-list-item@3.20.0", "", { "peerDependencies": { "@tiptap/extension-list": "^3.20.0" } }, "sha512-qEtjaaGPuqaFB4VpLrGDoIe9RHnckxPfu6d3rc22ap6TAHCDyRv05CEyJogqccnFceG/v5WN4znUBER8RWnWHA=="],
|
||||
|
||||
"@tiptap/extension-list-keymap": ["@tiptap/extension-list-keymap@3.20.0", "", { "peerDependencies": { "@tiptap/extension-list": "^3.20.0" } }, "sha512-Z4GvKy04Ms4cLFN+CY6wXswd36xYsT2p/YL0V89LYFMZTerOeTjFYlndzn6svqL8NV1PRT5Diw4WTTxJSmcJPA=="],
|
||||
|
||||
"@tiptap/extension-mention": ["@tiptap/extension-mention@3.20.0", "", { "peerDependencies": { "@tiptap/core": "^3.20.0", "@tiptap/pm": "^3.20.0", "@tiptap/suggestion": "^3.20.0" } }, "sha512-wUjsq7Za0JJdJzrGNG+g8nrCpek/85GQ0Rm9bka3PynIVRwus+xQqW6IyWVPBdl1BSkrbgMAUqtrfoh1ymznbg=="],
|
||||
|
||||
"@tiptap/extension-node-range": ["@tiptap/extension-node-range@3.20.0", "", { "peerDependencies": { "@tiptap/core": "^3.20.0", "@tiptap/pm": "^3.20.0" } }, "sha512-XeKKTV88VuJ4Mh0Rxvc/PPzG76cb44sE+rB4u0J/ms63R/WFTm6yJQlCgUVGnGeHleSlrWuZY8gGSuoljmQzqg=="],
|
||||
|
||||
"@tiptap/extension-ordered-list": ["@tiptap/extension-ordered-list@3.20.0", "", { "peerDependencies": { "@tiptap/extension-list": "^3.20.0" } }, "sha512-jVKnJvrizLk7etwBMfyoj6H2GE4M+PD4k7Bwp6Bh1ohBWtfIA1TlngdS842Mx5i1VB2e3UWIwr8ZH46gl6cwMA=="],
|
||||
|
||||
"@tiptap/extension-paragraph": ["@tiptap/extension-paragraph@3.20.0", "", { "peerDependencies": { "@tiptap/core": "^3.20.0" } }, "sha512-mM99zK4+RnEXIMCv6akfNATAs0Iija6FgyFA9J9NZ6N4o8y9QiNLLa6HjLpAC+W+VoCgQIekyoF/Q9ftxmAYDQ=="],
|
||||
|
||||
"@tiptap/extension-placeholder": ["@tiptap/extension-placeholder@3.20.0", "", { "peerDependencies": { "@tiptap/extensions": "^3.20.0" } }, "sha512-ZhYD3L5m16ydSe2z8vqz+RdtAG/iOQaFHHedFct70tKRoLqi2ajF5kgpemu8DwpaRTcyiCN4G99J/+MqehKNjQ=="],
|
||||
|
||||
"@tiptap/extension-strike": ["@tiptap/extension-strike@3.20.0", "", { "peerDependencies": { "@tiptap/core": "^3.20.0" } }, "sha512-0vcTZRRAiDfon3VM1mHBr9EFmTkkUXMhm0Xtdtn0bGe+sIqufyi+hUYTEw93EQOD9XNsPkrud6jzQNYpX2H3AQ=="],
|
||||
|
||||
"@tiptap/extension-text": ["@tiptap/extension-text@3.20.0", "", { "peerDependencies": { "@tiptap/core": "^3.20.0" } }, "sha512-tf8bE8tSaOEWabCzPm71xwiUhyMFKqY9jkP5af3Kr1/F45jzZFIQAYZooHI/+zCHRrgJ99MQHKHe1ZNvODrKHQ=="],
|
||||
|
||||
"@tiptap/extension-underline": ["@tiptap/extension-underline@3.20.0", "", { "peerDependencies": { "@tiptap/core": "^3.20.0" } }, "sha512-LzNXuy2jwR/y+ymoUqC72TiGzbOCjioIjsDu0MNYpHuHqTWPK5aV9Mh0nbZcYFy/7fPlV1q0W139EbJeYBZEAQ=="],
|
||||
|
||||
"@tiptap/extensions": ["@tiptap/extensions@3.20.0", "", { "peerDependencies": { "@tiptap/core": "^3.20.0", "@tiptap/pm": "^3.20.0" } }, "sha512-HIsXX942w3nbxEQBlMAAR/aa6qiMBEP7CsSMxaxmTIVAmW35p6yUASw6GdV1u0o3lCZjXq2OSRMTskzIqi5uLg=="],
|
||||
|
||||
"@tiptap/markdown": ["@tiptap/markdown@3.20.0", "", { "dependencies": { "marked": "^17.0.1" }, "peerDependencies": { "@tiptap/core": "^3.20.0", "@tiptap/pm": "^3.20.0" } }, "sha512-3vUxs8tsVIf/KWKLWjFsTqrjuaTYJY9rawDL5sio9NwlqFWDuWpHEVJcqbQXJUrgQSh12AZoTKyfgiEqkAGI3Q=="],
|
||||
|
||||
"@tiptap/pm": ["@tiptap/pm@3.20.0", "", { "dependencies": { "prosemirror-changeset": "^2.3.0", "prosemirror-collab": "^1.3.1", "prosemirror-commands": "^1.6.2", "prosemirror-dropcursor": "^1.8.1", "prosemirror-gapcursor": "^1.3.2", "prosemirror-history": "^1.4.1", "prosemirror-inputrules": "^1.4.0", "prosemirror-keymap": "^1.2.2", "prosemirror-markdown": "^1.13.1", "prosemirror-menu": "^1.2.4", "prosemirror-model": "^1.24.1", "prosemirror-schema-basic": "^1.2.3", "prosemirror-schema-list": "^1.5.0", "prosemirror-state": "^1.4.3", "prosemirror-tables": "^1.6.4", "prosemirror-trailing-node": "^3.0.0", "prosemirror-transform": "^1.10.2", "prosemirror-view": "^1.38.1" } }, "sha512-jn+2KnQZn+b+VXr8EFOJKsnjVNaA4diAEr6FOazupMt8W8ro1hfpYtZ25JL87Kao/WbMze55sd8M8BDXLUKu1A=="],
|
||||
|
||||
"@tiptap/starter-kit": ["@tiptap/starter-kit@3.20.0", "", { "dependencies": { "@tiptap/core": "^3.20.0", "@tiptap/extension-blockquote": "^3.20.0", "@tiptap/extension-bold": "^3.20.0", "@tiptap/extension-bullet-list": "^3.20.0", "@tiptap/extension-code": "^3.20.0", "@tiptap/extension-code-block": "^3.20.0", "@tiptap/extension-document": "^3.20.0", "@tiptap/extension-dropcursor": "^3.20.0", "@tiptap/extension-gapcursor": "^3.20.0", "@tiptap/extension-hard-break": "^3.20.0", "@tiptap/extension-heading": "^3.20.0", "@tiptap/extension-horizontal-rule": "^3.20.0", "@tiptap/extension-italic": "^3.20.0", "@tiptap/extension-link": "^3.20.0", "@tiptap/extension-list": "^3.20.0", "@tiptap/extension-list-item": "^3.20.0", "@tiptap/extension-list-keymap": "^3.20.0", "@tiptap/extension-ordered-list": "^3.20.0", "@tiptap/extension-paragraph": "^3.20.0", "@tiptap/extension-strike": "^3.20.0", "@tiptap/extension-text": "^3.20.0", "@tiptap/extension-underline": "^3.20.0", "@tiptap/extensions": "^3.20.0", "@tiptap/pm": "^3.20.0" } }, "sha512-W4+1re35pDNY/7rpXVg+OKo/Fa4Gfrn08Bq3E3fzlJw6gjE3tYU8dY9x9vC2rK9pd9NOp7Af11qCFDaWpohXkw=="],
|
||||
|
||||
"@tiptap/suggestion": ["@tiptap/suggestion@3.20.0", "", { "peerDependencies": { "@tiptap/core": "^3.20.0", "@tiptap/pm": "^3.20.0" } }, "sha512-OA9Fe+1Q/Ex0ivTcpRcVFiLnNsVdIBmiEoctt/gu4H2ayCYmZ906veioXNdc1m/3MtVVUIuEnvwwsrOZXlfDEw=="],
|
||||
|
||||
"@tiptap/vue-3": ["@tiptap/vue-3@3.20.0", "", { "optionalDependencies": { "@tiptap/extension-bubble-menu": "^3.20.0", "@tiptap/extension-floating-menu": "^3.20.0" }, "peerDependencies": { "@floating-ui/dom": "^1.0.0", "@tiptap/core": "^3.20.0", "@tiptap/pm": "^3.20.0", "vue": "^3.0.0" } }, "sha512-u8UfDKsbIOF+mVsXwJ946p1jfrLGFUyqp9i/DAeGGg2I85DPOkhZgz67bUPVXkpossoEk+jKCkRN0eBHl9+eZQ=="],
|
||||
|
||||
"@tiptap/y-tiptap": ["@tiptap/y-tiptap@3.0.2", "", { "dependencies": { "lib0": "^0.2.100" }, "peerDependencies": { "prosemirror-model": "^1.7.1", "prosemirror-state": "^1.2.3", "prosemirror-view": "^1.9.10", "y-protocols": "^1.0.1", "yjs": "^13.5.38" } }, "sha512-flMn/YW6zTbc6cvDaUPh/NfLRTXDIqgpBUkYzM74KA1snqQwhOMjnRcnpu4hDFrTnPO6QGzr99vRyXEA7M44WA=="],
|
||||
|
||||
"@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
|
||||
|
||||
"@types/linkify-it": ["@types/linkify-it@5.0.0", "", {}, "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q=="],
|
||||
|
||||
"@types/markdown-it": ["@types/markdown-it@14.1.2", "", { "dependencies": { "@types/linkify-it": "^5", "@types/mdurl": "^2" } }, "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog=="],
|
||||
|
||||
"@types/mdurl": ["@types/mdurl@2.0.0", "", {}, "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg=="],
|
||||
|
||||
"@types/trusted-types": ["@types/trusted-types@2.0.7", "", {}, "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw=="],
|
||||
|
||||
"@types/web-bluetooth": ["@types/web-bluetooth@0.0.21", "", {}, "sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA=="],
|
||||
|
||||
"@unhead/vue": ["@unhead/vue@2.1.10", "", { "dependencies": { "hookable": "^6.0.1", "unhead": "2.1.10" }, "peerDependencies": { "vue": ">=3.5.18" } }, "sha512-VP78Onh2HNezLPfhYjfHqn4dxlcQsE6PJgTTs61NksO/thvilNswtgBq0N0MWCLtn43N5akEPGW2y2zxM3PWgQ=="],
|
||||
|
||||
"@vue/compiler-core": ["@vue/compiler-core@3.5.29", "", { "dependencies": { "@babel/parser": "^7.29.0", "@vue/shared": "3.5.29", "entities": "^7.0.1", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" } }, "sha512-cuzPhD8fwRHk8IGfmYaR4eEe4cAyJEL66Ove/WZL7yWNL134nqLddSLwNRIsFlnnW1kK+p8Ck3viFnC0chXCXw=="],
|
||||
|
||||
"@vue/compiler-dom": ["@vue/compiler-dom@3.5.29", "", { "dependencies": { "@vue/compiler-core": "3.5.29", "@vue/shared": "3.5.29" } }, "sha512-n0G5o7R3uBVmVxjTIYcz7ovr8sy7QObFG8OQJ3xGCDNhbG60biP/P5KnyY8NLd81OuT1WJflG7N4KWYHaeeaIg=="],
|
||||
|
||||
"@vue/compiler-sfc": ["@vue/compiler-sfc@3.5.29", "", { "dependencies": { "@babel/parser": "^7.29.0", "@vue/compiler-core": "3.5.29", "@vue/compiler-dom": "3.5.29", "@vue/compiler-ssr": "3.5.29", "@vue/shared": "3.5.29", "estree-walker": "^2.0.2", "magic-string": "^0.30.21", "postcss": "^8.5.6", "source-map-js": "^1.2.1" } }, "sha512-oJZhN5XJs35Gzr50E82jg2cYdZQ78wEwvRO6Y63TvLVTc+6xICzJHP1UIecdSPPYIbkautNBanDiWYa64QSFIA=="],
|
||||
|
||||
"@vue/compiler-ssr": ["@vue/compiler-ssr@3.5.29", "", { "dependencies": { "@vue/compiler-dom": "3.5.29", "@vue/shared": "3.5.29" } }, "sha512-Y/ARJZE6fpjzL5GH/phJmsFwx3g6t2KmHKHx5q+MLl2kencADKIrhH5MLF6HHpRMmlRAYBRSvv347Mepf1zVNw=="],
|
||||
|
||||
"@vue/reactivity": ["@vue/reactivity@3.5.29", "", { "dependencies": { "@vue/shared": "3.5.29" } }, "sha512-zcrANcrRdcLtmGZETBxWqIkoQei8HaFpZWx/GHKxx79JZsiZ8j1du0VUJtu4eJjgFvU/iKL5lRXFXksVmI+5DA=="],
|
||||
|
||||
"@vue/runtime-core": ["@vue/runtime-core@3.5.29", "", { "dependencies": { "@vue/reactivity": "3.5.29", "@vue/shared": "3.5.29" } }, "sha512-8DpW2QfdwIWOLqtsNcds4s+QgwSaHSJY/SUe04LptianUQ/0xi6KVsu/pYVh+HO3NTVvVJjIPL2t6GdeKbS4Lg=="],
|
||||
|
||||
"@vue/runtime-dom": ["@vue/runtime-dom@3.5.29", "", { "dependencies": { "@vue/reactivity": "3.5.29", "@vue/runtime-core": "3.5.29", "@vue/shared": "3.5.29", "csstype": "^3.2.3" } }, "sha512-AHvvJEtcY9tw/uk+s/YRLSlxxQnqnAkjqvK25ZiM4CllCZWzElRAoQnCM42m9AHRLNJ6oe2kC5DCgD4AUdlvXg=="],
|
||||
|
||||
"@vue/server-renderer": ["@vue/server-renderer@3.5.29", "", { "dependencies": { "@vue/compiler-ssr": "3.5.29", "@vue/shared": "3.5.29" }, "peerDependencies": { "vue": "3.5.29" } }, "sha512-G/1k6WK5MusLlbxSE2YTcqAAezS+VuwHhOvLx2KnQU7G2zCH6KIb+5Wyt6UjMq7a3qPzNEjJXs1hvAxDclQH+g=="],
|
||||
|
||||
"@vue/shared": ["@vue/shared@3.5.29", "", {}, "sha512-w7SR0A5zyRByL9XUkCfdLs7t9XOHUyJ67qPGQjOou3p6GvBeBW+AVjUUmlxtZ4PIYaRvE+1LmK44O4uajlZwcg=="],
|
||||
|
||||
"@vueuse/core": ["@vueuse/core@14.2.1", "", { "dependencies": { "@types/web-bluetooth": "^0.0.21", "@vueuse/metadata": "14.2.1", "@vueuse/shared": "14.2.1" }, "peerDependencies": { "vue": "^3.5.0" } }, "sha512-3vwDzV+GDUNpdegRY6kzpLm4Igptq+GA0QkJ3W61Iv27YWwW/ufSlOfgQIpN6FZRMG0mkaz4gglJRtq5SeJyIQ=="],
|
||||
|
||||
"@vueuse/integrations": ["@vueuse/integrations@14.2.1", "", { "dependencies": { "@vueuse/core": "14.2.1", "@vueuse/shared": "14.2.1" }, "peerDependencies": { "async-validator": "^4", "axios": "^1", "change-case": "^5", "drauu": "^0.4", "focus-trap": "^7 || ^8", "fuse.js": "^7", "idb-keyval": "^6", "jwt-decode": "^4", "nprogress": "^0.2", "qrcode": "^1.5", "sortablejs": "^1", "universal-cookie": "^7 || ^8", "vue": "^3.5.0" }, "optionalPeers": ["async-validator", "axios", "change-case", "drauu", "focus-trap", "idb-keyval", "jwt-decode", "nprogress", "qrcode", "sortablejs", "universal-cookie"] }, "sha512-2LIUpBi/67PoXJGqSDQUF0pgQWpNHh7beiA+KG2AbybcNm+pTGWT6oPGlBgUoDWmYwfeQqM/uzOHqcILpKL7nA=="],
|
||||
|
||||
"@vueuse/metadata": ["@vueuse/metadata@14.2.1", "", {}, "sha512-1ButlVtj5Sb/HDtIy1HFr1VqCP4G6Ypqt5MAo0lCgjokrk2mvQKsK2uuy0vqu/Ks+sHfuHo0B9Y9jn9xKdjZsw=="],
|
||||
|
||||
"@vueuse/shared": ["@vueuse/shared@14.2.1", "", { "peerDependencies": { "vue": "^3.5.0" } }, "sha512-shTJncjV9JTI4oVNyF1FQonetYAiTBd+Qj7cY89SWbXSkx7gyhrgtEdF2ZAVWS1S3SHlaROO6F2IesJxQEkZBw=="],
|
||||
|
||||
"acorn": ["acorn@8.16.0", "", { "bin": "bin/acorn" }, "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw=="],
|
||||
|
||||
"anymatch": ["anymatch@3.1.3", "", { "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" } }, "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw=="],
|
||||
|
||||
"argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
|
||||
|
||||
"aria-hidden": ["aria-hidden@1.2.6", "", { "dependencies": { "tslib": "^2.0.0" } }, "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA=="],
|
||||
|
||||
"c12": ["c12@3.3.3", "", { "dependencies": { "chokidar": "^5.0.0", "confbox": "^0.2.2", "defu": "^6.1.4", "dotenv": "^17.2.3", "exsolve": "^1.0.8", "giget": "^2.0.0", "jiti": "^2.6.1", "ohash": "^2.0.11", "pathe": "^2.0.3", "perfect-debounce": "^2.0.0", "pkg-types": "^2.3.0", "rc9": "^2.1.2" }, "peerDependencies": { "magicast": "*" }, "optionalPeers": ["magicast"] }, "sha512-750hTRvgBy5kcMNPdh95Qo+XUBeGo8C7nsKSmedDmaQI+E0r82DwHeM6vBewDe4rGFbnxoa4V9pw+sPh5+Iz8Q=="],
|
||||
|
||||
"chart.js": ["chart.js@4.5.1", "", { "dependencies": { "@kurkle/color": "^0.3.0" } }, "sha512-GIjfiT9dbmHRiYi6Nl2yFCq7kkwdkp1W/lp2J99rX0yo9tgJGn3lKQATztIjb5tVtevcBtIdICNWqlq5+E8/Pw=="],
|
||||
|
||||
"chokidar": ["chokidar@5.0.0", "", { "dependencies": { "readdirp": "^5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="],
|
||||
|
||||
"citty": ["citty@0.1.6", "", { "dependencies": { "consola": "^3.2.3" } }, "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ=="],
|
||||
|
||||
"colortranslator": ["colortranslator@5.0.0", "", {}, "sha512-Z3UPUKasUVDFCDYAjP2fmlVRf1jFHJv1izAmPjiOa0OCIw1W7iC8PZ2GsoDa8uZv+mKyWopxxStT9q05+27h7w=="],
|
||||
|
||||
"confbox": ["confbox@0.2.4", "", {}, "sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ=="],
|
||||
|
||||
"consola": ["consola@3.4.2", "", {}, "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA=="],
|
||||
|
||||
"cookie-es": ["cookie-es@1.2.2", "", {}, "sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg=="],
|
||||
|
||||
"crelt": ["crelt@1.0.6", "", {}, "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g=="],
|
||||
|
||||
"cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
|
||||
|
||||
"crossws": ["crossws@0.3.5", "", { "dependencies": { "uncrypto": "^0.1.3" } }, "sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA=="],
|
||||
|
||||
"css-tree": ["css-tree@3.1.0", "", { "dependencies": { "mdn-data": "2.12.2", "source-map-js": "^1.0.1" } }, "sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w=="],
|
||||
|
||||
"csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="],
|
||||
|
||||
"defu": ["defu@6.1.4", "", {}, "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg=="],
|
||||
|
||||
"destr": ["destr@2.0.5", "", {}, "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA=="],
|
||||
|
||||
"detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
|
||||
|
||||
"dompurify": ["dompurify@3.3.3", "", { "optionalDependencies": { "@types/trusted-types": "^2.0.7" } }, "sha512-Oj6pzI2+RqBfFG+qOaOLbFXLQ90ARpcGG6UePL82bJLtdsa6CYJD7nmiU8MW9nQNOtCHV3lZ/Bzq1X0QYbBZCA=="],
|
||||
|
||||
"dotenv": ["dotenv@17.3.1", "", {}, "sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA=="],
|
||||
|
||||
"embla-carousel": ["embla-carousel@8.6.0", "", {}, "sha512-SjWyZBHJPbqxHOzckOfo8lHisEaJWmwd23XppYFYVh10bU66/Pn5tkVkbkCMZVdbUE5eTCI2nD8OyIP4Z+uwkA=="],
|
||||
|
||||
"embla-carousel-auto-height": ["embla-carousel-auto-height@8.6.0", "", { "peerDependencies": { "embla-carousel": "8.6.0" } }, "sha512-/HrJQOEM6aol/oF33gd2QlINcXy3e19fJWvHDuHWp2bpyTa+2dm9tVVJak30m2Qy6QyQ6Fc8DkImtv7pxWOJUQ=="],
|
||||
|
||||
"embla-carousel-auto-scroll": ["embla-carousel-auto-scroll@8.6.0", "", { "peerDependencies": { "embla-carousel": "8.6.0" } }, "sha512-WT9fWhNXFpbQ6kP+aS07oF5IHYLZ1Dx4DkwgCY8Hv2ZyYd2KMCPfMV1q/cA3wFGuLO7GMgKiySLX90/pQkcOdQ=="],
|
||||
|
||||
"embla-carousel-autoplay": ["embla-carousel-autoplay@8.6.0", "", { "peerDependencies": { "embla-carousel": "8.6.0" } }, "sha512-OBu5G3nwaSXkZCo1A6LTaFMZ8EpkYbwIaH+bPqdBnDGQ2fh4+NbzjXjs2SktoPNKCtflfVMc75njaDHOYXcrsA=="],
|
||||
|
||||
"embla-carousel-class-names": ["embla-carousel-class-names@8.6.0", "", { "peerDependencies": { "embla-carousel": "8.6.0" } }, "sha512-l1hm1+7GxQ+zwdU2sea/LhD946on7XO2qk3Xq2XWSwBaWfdgchXdK567yzLtYSHn4sWYdiX+x4nnaj+saKnJkw=="],
|
||||
|
||||
"embla-carousel-fade": ["embla-carousel-fade@8.6.0", "", { "peerDependencies": { "embla-carousel": "8.6.0" } }, "sha512-qaYsx5mwCz72ZrjlsXgs1nKejSrW+UhkbOMwLgfRT7w2LtdEB03nPRI06GHuHv5ac2USvbEiX2/nAHctcDwvpg=="],
|
||||
|
||||
"embla-carousel-reactive-utils": ["embla-carousel-reactive-utils@8.6.0", "", { "peerDependencies": { "embla-carousel": "8.6.0" } }, "sha512-fMVUDUEx0/uIEDM0Mz3dHznDhfX+znCCDCeIophYb1QGVM7YThSWX+wz11zlYwWFOr74b4QLGg0hrGPJeG2s4A=="],
|
||||
|
||||
"embla-carousel-vue": ["embla-carousel-vue@8.6.0", "", { "dependencies": { "embla-carousel": "8.6.0", "embla-carousel-reactive-utils": "8.6.0" }, "peerDependencies": { "vue": "^3.2.37" } }, "sha512-v8UO5UsyLocZnu/LbfQA7Dn2QHuZKurJY93VUmZYP//QRWoCWOsionmvLLAlibkET3pGPs7++03VhJKbWD7vhQ=="],
|
||||
|
||||
"embla-carousel-wheel-gestures": ["embla-carousel-wheel-gestures@8.1.0", "", { "dependencies": { "wheel-gestures": "^2.2.5" }, "peerDependencies": { "embla-carousel": "^8.0.0 || ~8.0.0-rc03" } }, "sha512-J68jkYrxbWDmXOm2n2YHl+uMEXzkGSKjWmjaEgL9xVvPb3HqVmg6rJSKfI3sqIDVvm7mkeTy87wtG/5263XqHQ=="],
|
||||
|
||||
"enhanced-resolve": ["enhanced-resolve@5.20.0", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.0" } }, "sha512-/ce7+jQ1PQ6rVXwe+jKEg5hW5ciicHwIQUagZkp6IufBoY3YDgdTTY1azVs0qoRgVmvsNB+rbjLJxDAeHHtwsQ=="],
|
||||
|
||||
"entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="],
|
||||
|
||||
"errx": ["errx@0.1.0", "", {}, "sha512-fZmsRiDNv07K6s2KkKFTiD2aIvECa7++PKyD5NC32tpRw46qZA3sOz+aM+/V9V0GDHxVTKLziveV4JhzBHDp9Q=="],
|
||||
|
||||
"esbuild": ["esbuild@0.27.3", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.3", "@esbuild/android-arm": "0.27.3", "@esbuild/android-arm64": "0.27.3", "@esbuild/android-x64": "0.27.3", "@esbuild/darwin-arm64": "0.27.3", "@esbuild/darwin-x64": "0.27.3", "@esbuild/freebsd-arm64": "0.27.3", "@esbuild/freebsd-x64": "0.27.3", "@esbuild/linux-arm": "0.27.3", "@esbuild/linux-arm64": "0.27.3", "@esbuild/linux-ia32": "0.27.3", "@esbuild/linux-loong64": "0.27.3", "@esbuild/linux-mips64el": "0.27.3", "@esbuild/linux-ppc64": "0.27.3", "@esbuild/linux-riscv64": "0.27.3", "@esbuild/linux-s390x": "0.27.3", "@esbuild/linux-x64": "0.27.3", "@esbuild/netbsd-arm64": "0.27.3", "@esbuild/netbsd-x64": "0.27.3", "@esbuild/openbsd-arm64": "0.27.3", "@esbuild/openbsd-x64": "0.27.3", "@esbuild/openharmony-arm64": "0.27.3", "@esbuild/sunos-x64": "0.27.3", "@esbuild/win32-arm64": "0.27.3", "@esbuild/win32-ia32": "0.27.3", "@esbuild/win32-x64": "0.27.3" }, "bin": "bin/esbuild" }, "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg=="],
|
||||
|
||||
"escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="],
|
||||
|
||||
"estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="],
|
||||
|
||||
"execa": ["execa@8.0.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^8.0.1", "human-signals": "^5.0.0", "is-stream": "^3.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^5.1.0", "onetime": "^6.0.0", "signal-exit": "^4.1.0", "strip-final-newline": "^3.0.0" } }, "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg=="],
|
||||
|
||||
"exsolve": ["exsolve@1.0.8", "", {}, "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA=="],
|
||||
|
||||
"fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" } }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
|
||||
|
||||
"fontaine": ["fontaine@0.8.0", "", { "dependencies": { "@capsizecss/unpack": "^4.0.0", "css-tree": "^3.1.0", "magic-regexp": "^0.10.0", "magic-string": "^0.30.21", "pathe": "^2.0.3", "ufo": "^1.6.1", "unplugin": "^2.3.10" } }, "sha512-eek1GbzOdWIj9FyQH/emqW1aEdfC3lYRCHepzwlFCm5T77fBSRSyNRKE6/antF1/B1M+SfJXVRQTY9GAr7lnDg=="],
|
||||
|
||||
"fontkitten": ["fontkitten@1.0.2", "", { "dependencies": { "tiny-inflate": "^1.0.3" } }, "sha512-piJxbLnkD9Xcyi7dWJRnqszEURixe7CrF/efBfbffe2DPyabmuIuqraruY8cXTs19QoM8VJzx47BDRVNXETM7Q=="],
|
||||
|
||||
"fontless": ["fontless@0.2.1", "", { "dependencies": { "consola": "^3.4.2", "css-tree": "^3.1.0", "defu": "^6.1.4", "esbuild": "^0.27.0", "fontaine": "0.8.0", "jiti": "^2.6.1", "lightningcss": "^1.30.2", "magic-string": "^0.30.21", "ohash": "^2.0.11", "pathe": "^2.0.3", "ufo": "^1.6.1", "unifont": "^0.7.4", "unstorage": "^1.17.1" }, "peerDependencies": { "vite": "*" } }, "sha512-mUWZ8w91/mw2KEcZ6gHNoNNmsAq9Wiw2IypIux5lM03nhXm+WSloXGUNuRETNTLqZexMgpt7Aj/v63qqrsWraQ=="],
|
||||
|
||||
"framer-motion": ["framer-motion@12.34.4", "", { "dependencies": { "motion-dom": "^12.34.3", "motion-utils": "^12.29.2", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-q1PwNhc1XJ3qYG7nc9+pEU5P3tnjB6Eh9vv5gGzy61nedDLB4+xk5peMCWhKM0Zn6sfhgunf/q9n0UgCoyKOBA=="],
|
||||
|
||||
"fuse.js": ["fuse.js@7.1.0", "", {}, "sha512-trLf4SzuuUxfusZADLINj+dE8clK1frKdmqiJNb1Es75fmI5oY6X2mxLVUciLLjxqw/xr72Dhy+lER6dGd02FQ=="],
|
||||
|
||||
"get-stream": ["get-stream@8.0.1", "", {}, "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA=="],
|
||||
|
||||
"giget": ["giget@2.0.0", "", { "dependencies": { "citty": "^0.1.6", "consola": "^3.4.0", "defu": "^6.1.4", "node-fetch-native": "^1.6.6", "nypm": "^0.6.0", "pathe": "^2.0.3" }, "bin": "dist/cli.mjs" }, "sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA=="],
|
||||
|
||||
"graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
|
||||
|
||||
"h3": ["h3@1.15.5", "", { "dependencies": { "cookie-es": "^1.2.2", "crossws": "^0.3.5", "defu": "^6.1.4", "destr": "^2.0.5", "iron-webcrypto": "^1.2.1", "node-mock-http": "^1.0.4", "radix3": "^1.1.2", "ufo": "^1.6.3", "uncrypto": "^0.1.3" } }, "sha512-xEyq3rSl+dhGX2Lm0+eFQIAzlDN6Fs0EcC4f7BNUmzaRX/PTzeuM+Tr2lHB8FoXggsQIeXLj8EDVgs5ywxyxmg=="],
|
||||
|
||||
"hey-listen": ["hey-listen@1.0.8", "", {}, "sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q=="],
|
||||
|
||||
"hookable": ["hookable@5.5.3", "", {}, "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ=="],
|
||||
|
||||
"human-signals": ["human-signals@5.0.0", "", {}, "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ=="],
|
||||
|
||||
"ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="],
|
||||
|
||||
"iron-webcrypto": ["iron-webcrypto@1.2.1", "", {}, "sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg=="],
|
||||
|
||||
"is-stream": ["is-stream@3.0.0", "", {}, "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA=="],
|
||||
|
||||
"isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
|
||||
|
||||
"isomorphic.js": ["isomorphic.js@0.2.5", "", {}, "sha512-PIeMbHqMt4DnUP3MA/Flc0HElYjMXArsw1qwJZcm9sqR8mq3l8NYizFMty0pWwE/tzIGH3EKK5+jes5mAr85yw=="],
|
||||
|
||||
"jiti": ["jiti@2.6.1", "", { "bin": "lib/jiti-cli.mjs" }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="],
|
||||
|
||||
"js-tokens": ["js-tokens@9.0.1", "", {}, "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ=="],
|
||||
|
||||
"klona": ["klona@2.0.6", "", {}, "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA=="],
|
||||
|
||||
"knitwork": ["knitwork@1.3.0", "", {}, "sha512-4LqMNoONzR43B1W0ek0fhXMsDNW/zxa1NdFAVMY+k28pgZLovR4G3PB5MrpTxCy1QaZCqNoiaKPr5w5qZHfSNw=="],
|
||||
|
||||
"lib0": ["lib0@0.2.117", "", { "dependencies": { "isomorphic.js": "^0.2.4" }, "bin": { "0ecdsa-generate-keypair": "bin/0ecdsa-generate-keypair.js", "0gentesthtml": "bin/gentesthtml.js", "0serve": "bin/0serve.js" } }, "sha512-DeXj9X5xDCjgKLU/7RR+/HQEVzuuEUiwldwOGsHK/sfAfELGWEyTcf0x+uOvCvK3O2zPmZePXWL85vtia6GyZw=="],
|
||||
|
||||
"lightningcss": ["lightningcss@1.31.1", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.31.1", "lightningcss-darwin-arm64": "1.31.1", "lightningcss-darwin-x64": "1.31.1", "lightningcss-freebsd-x64": "1.31.1", "lightningcss-linux-arm-gnueabihf": "1.31.1", "lightningcss-linux-arm64-gnu": "1.31.1", "lightningcss-linux-arm64-musl": "1.31.1", "lightningcss-linux-x64-gnu": "1.31.1", "lightningcss-linux-x64-musl": "1.31.1", "lightningcss-win32-arm64-msvc": "1.31.1", "lightningcss-win32-x64-msvc": "1.31.1" } }, "sha512-l51N2r93WmGUye3WuFoN5k10zyvrVs0qfKBhyC5ogUQ6Ew6JUSswh78mbSO+IU3nTWsyOArqPCcShdQSadghBQ=="],
|
||||
|
||||
"lightningcss-android-arm64": ["lightningcss-android-arm64@1.31.1", "", { "os": "android", "cpu": "arm64" }, "sha512-HXJF3x8w9nQ4jbXRiNppBCqeZPIAfUo8zE/kOEGbW5NZvGc/K7nMxbhIr+YlFlHW5mpbg/YFPdbnCh1wAXCKFg=="],
|
||||
|
||||
"lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.31.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-02uTEqf3vIfNMq3h/z2cJfcOXnQ0GRwQrkmPafhueLb2h7mqEidiCzkE4gBMEH65abHRiQvhdcQ+aP0D0g67sg=="],
|
||||
|
||||
"lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.31.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-1ObhyoCY+tGxtsz1lSx5NXCj3nirk0Y0kB/g8B8DT+sSx4G9djitg9ejFnjb3gJNWo7qXH4DIy2SUHvpoFwfTA=="],
|
||||
|
||||
"lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.31.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-1RINmQKAItO6ISxYgPwszQE1BrsVU5aB45ho6O42mu96UiZBxEXsuQ7cJW4zs4CEodPUioj/QrXW1r9pLUM74A=="],
|
||||
|
||||
"lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.31.1", "", { "os": "linux", "cpu": "arm" }, "sha512-OOCm2//MZJ87CdDK62rZIu+aw9gBv4azMJuA8/KB74wmfS3lnC4yoPHm0uXZ/dvNNHmnZnB8XLAZzObeG0nS1g=="],
|
||||
|
||||
"lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.31.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-WKyLWztD71rTnou4xAD5kQT+982wvca7E6QoLpoawZ1gP9JM0GJj4Tp5jMUh9B3AitHbRZ2/H3W5xQmdEOUlLg=="],
|
||||
|
||||
"lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.31.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-mVZ7Pg2zIbe3XlNbZJdjs86YViQFoJSpc41CbVmKBPiGmC4YrfeOyz65ms2qpAobVd7WQsbW4PdsSJEMymyIMg=="],
|
||||
|
||||
"lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.31.1", "", { "os": "linux", "cpu": "x64" }, "sha512-xGlFWRMl+0KvUhgySdIaReQdB4FNudfUTARn7q0hh/V67PVGCs3ADFjw+6++kG1RNd0zdGRlEKa+T13/tQjPMA=="],
|
||||
|
||||
"lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.31.1", "", { "os": "linux", "cpu": "x64" }, "sha512-eowF8PrKHw9LpoZii5tdZwnBcYDxRw2rRCyvAXLi34iyeYfqCQNA9rmUM0ce62NlPhCvof1+9ivRaTY6pSKDaA=="],
|
||||
|
||||
"lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.31.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-aJReEbSEQzx1uBlQizAOBSjcmr9dCdL3XuC/6HLXAxmtErsj2ICo5yYggg1qOODQMtnjNQv2UHb9NpOuFtYe4w=="],
|
||||
|
||||
"lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.31.1", "", { "os": "win32", "cpu": "x64" }, "sha512-I9aiFrbd7oYHwlnQDqr1Roz+fTz61oDDJX7n9tYF9FJymH1cIN1DtKw3iYt6b8WZgEjoNwVSncwF4wx/ZedMhw=="],
|
||||
|
||||
"linkify-it": ["linkify-it@5.0.0", "", { "dependencies": { "uc.micro": "^2.0.0" } }, "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ=="],
|
||||
|
||||
"linkifyjs": ["linkifyjs@4.3.2", "", {}, "sha512-NT1CJtq3hHIreOianA8aSXn6Cw0JzYOuDQbOrSPe7gqFnCpKP++MQe3ODgO3oh2GJFORkAAdqredOa60z63GbA=="],
|
||||
|
||||
"local-pkg": ["local-pkg@1.1.2", "", { "dependencies": { "mlly": "^1.7.4", "pkg-types": "^2.3.0", "quansync": "^0.2.11" } }, "sha512-arhlxbFRmoQHl33a0Zkle/YWlmNwoyt6QNZEIJcqNbdrsix5Lvc4HyyI3EnwxTYlZYc32EbYrQ8SzEZ7dqgg9A=="],
|
||||
|
||||
"lru-cache": ["lru-cache@11.2.6", "", {}, "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ=="],
|
||||
|
||||
"magic-regexp": ["magic-regexp@0.10.0", "", { "dependencies": { "estree-walker": "^3.0.3", "magic-string": "^0.30.12", "mlly": "^1.7.2", "regexp-tree": "^0.1.27", "type-level-regexp": "~0.1.17", "ufo": "^1.5.4", "unplugin": "^2.0.0" } }, "sha512-Uly1Bu4lO1hwHUW0CQeSWuRtzCMNO00CmXtS8N6fyvB3B979GOEEeAkiTUDsmbYLAbvpUS/Kt5c4ibosAzVyVg=="],
|
||||
|
||||
"magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="],
|
||||
|
||||
"markdown-it": ["markdown-it@14.1.1", "", { "dependencies": { "argparse": "^2.0.1", "entities": "^4.4.0", "linkify-it": "^5.0.0", "mdurl": "^2.0.0", "punycode.js": "^2.3.1", "uc.micro": "^2.1.0" }, "bin": "bin/markdown-it.mjs" }, "sha512-BuU2qnTti9YKgK5N+IeMubp14ZUKUUw7yeJbkjtosvHiP0AZ5c8IAgEMk79D0eC8F23r4Ac/q8cAIFdm2FtyoA=="],
|
||||
|
||||
"marked": ["marked@17.0.3", "", { "bin": "bin/marked.js" }, "sha512-jt1v2ObpyOKR8p4XaUJVk3YWRJ5n+i4+rjQopxvV32rSndTJXvIzuUdWWIy/1pFQMkQmvTXawzDNqOH/CUmx6A=="],
|
||||
|
||||
"mdn-data": ["mdn-data@2.12.2", "", {}, "sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA=="],
|
||||
|
||||
"mdurl": ["mdurl@2.0.0", "", {}, "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w=="],
|
||||
|
||||
"merge-stream": ["merge-stream@2.0.0", "", {}, "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w=="],
|
||||
|
||||
"mimic-fn": ["mimic-fn@4.0.0", "", {}, "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw=="],
|
||||
|
||||
"mlly": ["mlly@1.8.0", "", { "dependencies": { "acorn": "^8.15.0", "pathe": "^2.0.3", "pkg-types": "^1.3.1", "ufo": "^1.6.1" } }, "sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g=="],
|
||||
|
||||
"motion-dom": ["motion-dom@12.34.3", "", { "dependencies": { "motion-utils": "^12.29.2" } }, "sha512-sYgFe+pR9aIM7o4fhs2aXtOI+oqlUd33N9Yoxcgo1Fv7M20sRkHtCmzE/VRNIcq7uNJ+qio+Xubt1FXH3pQ+eQ=="],
|
||||
|
||||
"motion-utils": ["motion-utils@12.29.2", "", {}, "sha512-G3kc34H2cX2gI63RqU+cZq+zWRRPSsNIOjpdl9TN4AQwC4sgwYPl/Q/Obf/d53nOm569T0fYK+tcoSV50BWx8A=="],
|
||||
|
||||
"motion-v": ["motion-v@1.10.3", "", { "dependencies": { "framer-motion": "^12.25.0", "hey-listen": "^1.0.8", "motion-dom": "^12.23.23" }, "peerDependencies": { "@vueuse/core": ">=10.0.0", "vue": ">=3.0.0" } }, "sha512-9Ewo/wwGv7FO3PqYJpllBF/Efc7tbeM1iinVrM73s0RUQrnXHwMZCaRX98u4lu0PQCrZghPPfCsQ14pWKIEbnQ=="],
|
||||
|
||||
"mrmime": ["mrmime@2.0.1", "", {}, "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ=="],
|
||||
|
||||
"nanoid": ["nanoid@3.3.11", "", { "bin": "bin/nanoid.cjs" }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
|
||||
|
||||
"node-fetch-native": ["node-fetch-native@1.6.7", "", {}, "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q=="],
|
||||
|
||||
"node-mock-http": ["node-mock-http@1.0.4", "", {}, "sha512-8DY+kFsDkNXy1sJglUfuODx1/opAGJGyrTuFqEoN90oRc2Vk0ZbD4K2qmKXBBEhZQzdKHIVfEJpDU8Ak2NJEvQ=="],
|
||||
|
||||
"normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="],
|
||||
|
||||
"npm-run-path": ["npm-run-path@5.3.0", "", { "dependencies": { "path-key": "^4.0.0" } }, "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ=="],
|
||||
|
||||
"nypm": ["nypm@0.6.5", "", { "dependencies": { "citty": "^0.2.0", "pathe": "^2.0.3", "tinyexec": "^1.0.2" }, "bin": "dist/cli.mjs" }, "sha512-K6AJy1GMVyfyMXRVB88700BJqNUkByijGJM8kEHpLdcAt+vSQAVfkWWHYzuRXHSY6xA2sNc5RjTj0p9rE2izVQ=="],
|
||||
|
||||
"obug": ["obug@2.1.1", "", {}, "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ=="],
|
||||
|
||||
"ofetch": ["ofetch@1.5.1", "", { "dependencies": { "destr": "^2.0.5", "node-fetch-native": "^1.6.7", "ufo": "^1.6.1" } }, "sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA=="],
|
||||
|
||||
"ohash": ["ohash@2.0.11", "", {}, "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ=="],
|
||||
|
||||
"onetime": ["onetime@6.0.0", "", { "dependencies": { "mimic-fn": "^4.0.0" } }, "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ=="],
|
||||
|
||||
"orderedmap": ["orderedmap@2.1.1", "", {}, "sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g=="],
|
||||
|
||||
"package-manager-detector": ["package-manager-detector@1.6.0", "", {}, "sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA=="],
|
||||
|
||||
"path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
|
||||
|
||||
"pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
|
||||
|
||||
"perfect-debounce": ["perfect-debounce@2.1.0", "", {}, "sha512-LjgdTytVFXeUgtHZr9WYViYSM/g8MkcTPYDlPa3cDqMirHjKiSZPYd6DoL7pK8AJQr+uWkQvCjHNdiMqsrJs+g=="],
|
||||
|
||||
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
|
||||
|
||||
"picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="],
|
||||
|
||||
"pkg-types": ["pkg-types@2.3.0", "", { "dependencies": { "confbox": "^0.2.2", "exsolve": "^1.0.7", "pathe": "^2.0.3" } }, "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig=="],
|
||||
|
||||
"postcss": ["postcss@8.5.8", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg=="],
|
||||
|
||||
"prosemirror-changeset": ["prosemirror-changeset@2.4.0", "", { "dependencies": { "prosemirror-transform": "^1.0.0" } }, "sha512-LvqH2v7Q2SF6yxatuPP2e8vSUKS/L+xAU7dPDC4RMyHMhZoGDfBC74mYuyYF4gLqOEG758wajtyhNnsTkuhvng=="],
|
||||
|
||||
"prosemirror-collab": ["prosemirror-collab@1.3.1", "", { "dependencies": { "prosemirror-state": "^1.0.0" } }, "sha512-4SnynYR9TTYaQVXd/ieUvsVV4PDMBzrq2xPUWutHivDuOshZXqQ5rGbZM84HEaXKbLdItse7weMGOUdDVcLKEQ=="],
|
||||
|
||||
"prosemirror-commands": ["prosemirror-commands@1.7.1", "", { "dependencies": { "prosemirror-model": "^1.0.0", "prosemirror-state": "^1.0.0", "prosemirror-transform": "^1.10.2" } }, "sha512-rT7qZnQtx5c0/y/KlYaGvtG411S97UaL6gdp6RIZ23DLHanMYLyfGBV5DtSnZdthQql7W+lEVbpSfwtO8T+L2w=="],
|
||||
|
||||
"prosemirror-dropcursor": ["prosemirror-dropcursor@1.8.2", "", { "dependencies": { "prosemirror-state": "^1.0.0", "prosemirror-transform": "^1.1.0", "prosemirror-view": "^1.1.0" } }, "sha512-CCk6Gyx9+Tt2sbYk5NK0nB1ukHi2ryaRgadV/LvyNuO3ena1payM2z6Cg0vO1ebK8cxbzo41ku2DE5Axj1Zuiw=="],
|
||||
|
||||
"prosemirror-gapcursor": ["prosemirror-gapcursor@1.4.0", "", { "dependencies": { "prosemirror-keymap": "^1.0.0", "prosemirror-model": "^1.0.0", "prosemirror-state": "^1.0.0", "prosemirror-view": "^1.0.0" } }, "sha512-z00qvurSdCEWUIulij/isHaqu4uLS8r/Fi61IbjdIPJEonQgggbJsLnstW7Lgdk4zQ68/yr6B6bf7sJXowIgdQ=="],
|
||||
|
||||
"prosemirror-history": ["prosemirror-history@1.5.0", "", { "dependencies": { "prosemirror-state": "^1.2.2", "prosemirror-transform": "^1.0.0", "prosemirror-view": "^1.31.0", "rope-sequence": "^1.3.0" } }, "sha512-zlzTiH01eKA55UAf1MEjtssJeHnGxO0j4K4Dpx+gnmX9n+SHNlDqI2oO1Kv1iPN5B1dm5fsljCfqKF9nFL6HRg=="],
|
||||
|
||||
"prosemirror-inputrules": ["prosemirror-inputrules@1.5.1", "", { "dependencies": { "prosemirror-state": "^1.0.0", "prosemirror-transform": "^1.0.0" } }, "sha512-7wj4uMjKaXWAQ1CDgxNzNtR9AlsuwzHfdFH1ygEHA2KHF2DOEaXl1CJfNPAKCg9qNEh4rum975QLaCiQPyY6Fw=="],
|
||||
|
||||
"prosemirror-keymap": ["prosemirror-keymap@1.2.3", "", { "dependencies": { "prosemirror-state": "^1.0.0", "w3c-keyname": "^2.2.0" } }, "sha512-4HucRlpiLd1IPQQXNqeo81BGtkY8Ai5smHhKW9jjPKRc2wQIxksg7Hl1tTI2IfT2B/LgX6bfYvXxEpJl7aKYKw=="],
|
||||
|
||||
"prosemirror-markdown": ["prosemirror-markdown@1.13.4", "", { "dependencies": { "@types/markdown-it": "^14.0.0", "markdown-it": "^14.0.0", "prosemirror-model": "^1.25.0" } }, "sha512-D98dm4cQ3Hs6EmjK500TdAOew4Z03EV71ajEFiWra3Upr7diytJsjF4mPV2dW+eK5uNectiRj0xFxYI9NLXDbw=="],
|
||||
|
||||
"prosemirror-menu": ["prosemirror-menu@1.3.0", "", { "dependencies": { "crelt": "^1.0.0", "prosemirror-commands": "^1.0.0", "prosemirror-history": "^1.0.0", "prosemirror-state": "^1.0.0" } }, "sha512-TImyPXCHPcDsSka2/lwJ6WjTASr4re/qWq1yoTTuLOqfXucwF6VcRa2LWCkM/EyTD1UO3CUwiH8qURJoWJRxwg=="],
|
||||
|
||||
"prosemirror-model": ["prosemirror-model@1.25.4", "", { "dependencies": { "orderedmap": "^2.0.0" } }, "sha512-PIM7E43PBxKce8OQeezAs9j4TP+5yDpZVbuurd1h5phUxEKIu+G2a+EUZzIC5nS1mJktDJWzbqS23n1tsAf5QA=="],
|
||||
|
||||
"prosemirror-schema-basic": ["prosemirror-schema-basic@1.2.4", "", { "dependencies": { "prosemirror-model": "^1.25.0" } }, "sha512-ELxP4TlX3yr2v5rM7Sb70SqStq5NvI15c0j9j/gjsrO5vaw+fnnpovCLEGIcpeGfifkuqJwl4fon6b+KdrODYQ=="],
|
||||
|
||||
"prosemirror-schema-list": ["prosemirror-schema-list@1.5.1", "", { "dependencies": { "prosemirror-model": "^1.0.0", "prosemirror-state": "^1.0.0", "prosemirror-transform": "^1.7.3" } }, "sha512-927lFx/uwyQaGwJxLWCZRkjXG0p48KpMj6ueoYiu4JX05GGuGcgzAy62dfiV8eFZftgyBUvLx76RsMe20fJl+Q=="],
|
||||
|
||||
"prosemirror-state": ["prosemirror-state@1.4.4", "", { "dependencies": { "prosemirror-model": "^1.0.0", "prosemirror-transform": "^1.0.0", "prosemirror-view": "^1.27.0" } }, "sha512-6jiYHH2CIGbCfnxdHbXZ12gySFY/fz/ulZE333G6bPqIZ4F+TXo9ifiR86nAHpWnfoNjOb3o5ESi7J8Uz1jXHw=="],
|
||||
|
||||
"prosemirror-tables": ["prosemirror-tables@1.8.5", "", { "dependencies": { "prosemirror-keymap": "^1.2.3", "prosemirror-model": "^1.25.4", "prosemirror-state": "^1.4.4", "prosemirror-transform": "^1.10.5", "prosemirror-view": "^1.41.4" } }, "sha512-V/0cDCsHKHe/tfWkeCmthNUcEp1IVO3p6vwN8XtwE9PZQLAZJigbw3QoraAdfJPir4NKJtNvOB8oYGKRl+t0Dw=="],
|
||||
|
||||
"prosemirror-trailing-node": ["prosemirror-trailing-node@3.0.0", "", { "dependencies": { "@remirror/core-constants": "3.0.0", "escape-string-regexp": "^4.0.0" }, "peerDependencies": { "prosemirror-model": "^1.22.1", "prosemirror-state": "^1.4.2", "prosemirror-view": "^1.33.8" } }, "sha512-xiun5/3q0w5eRnGYfNlW1uU9W6x5MoFKWwq/0TIRgt09lv7Hcser2QYV8t4muXbEr+Fwo0geYn79Xs4GKywrRQ=="],
|
||||
|
||||
"prosemirror-transform": ["prosemirror-transform@1.11.0", "", { "dependencies": { "prosemirror-model": "^1.21.0" } }, "sha512-4I7Ce4KpygXb9bkiPS3hTEk4dSHorfRw8uI0pE8IhxlK2GXsqv5tIA7JUSxtSu7u8APVOTtbUBxTmnHIxVkIJw=="],
|
||||
|
||||
"prosemirror-view": ["prosemirror-view@1.41.6", "", { "dependencies": { "prosemirror-model": "^1.20.0", "prosemirror-state": "^1.0.0", "prosemirror-transform": "^1.1.0" } }, "sha512-mxpcDG4hNQa/CPtzxjdlir5bJFDlm0/x5nGBbStB2BWX+XOQ9M8ekEG+ojqB5BcVu2Rc80/jssCMZzSstJuSYg=="],
|
||||
|
||||
"punycode.js": ["punycode.js@2.3.1", "", {}, "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA=="],
|
||||
|
||||
"quansync": ["quansync@0.2.11", "", {}, "sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA=="],
|
||||
|
||||
"radix3": ["radix3@1.1.2", "", {}, "sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA=="],
|
||||
|
||||
"rc9": ["rc9@3.0.0", "", { "dependencies": { "defu": "^6.1.4", "destr": "^2.0.5" } }, "sha512-MGOue0VqscKWQ104udASX/3GYDcKyPI4j4F8gu/jHHzglpmy9a/anZK3PNe8ug6aZFl+9GxLtdhe3kVZuMaQbA=="],
|
||||
|
||||
"readdirp": ["readdirp@5.0.0", "", {}, "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ=="],
|
||||
|
||||
"regexp-tree": ["regexp-tree@0.1.27", "", { "bin": "bin/regexp-tree" }, "sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA=="],
|
||||
|
||||
"reka-ui": ["reka-ui@2.8.2", "", { "dependencies": { "@floating-ui/dom": "^1.6.13", "@floating-ui/vue": "^1.1.6", "@internationalized/date": "^3.5.0", "@internationalized/number": "^3.5.0", "@tanstack/vue-virtual": "^3.12.0", "@vueuse/core": "^14.1.0", "@vueuse/shared": "^14.1.0", "aria-hidden": "^1.2.4", "defu": "^6.1.4", "ohash": "^2.0.11" }, "peerDependencies": { "vue": ">= 3.2.0" } }, "sha512-8lTKcJhmG+D3UyJxhBnNnW/720sLzm0pbA9AC1MWazmJ5YchJAyTSl+O00xP/kxBmEN0fw5JqWVHguiFmsGjzA=="],
|
||||
|
||||
"rollup": ["rollup@4.59.0", "", { "dependencies": { "@types/estree": "1.0.8" }, "bin": "dist/bin/rollup" }, "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg=="],
|
||||
|
||||
"rope-sequence": ["rope-sequence@1.3.4", "", {}, "sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ=="],
|
||||
|
||||
"scule": ["scule@1.3.0", "", {}, "sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g=="],
|
||||
|
||||
"semver": ["semver@7.7.4", "", { "bin": "bin/semver.js" }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="],
|
||||
|
||||
"shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="],
|
||||
|
||||
"shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
|
||||
|
||||
"signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
|
||||
|
||||
"sirv": ["sirv@3.0.2", "", { "dependencies": { "@polka/url": "^1.0.0-next.24", "mrmime": "^2.0.0", "totalist": "^3.0.0" } }, "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g=="],
|
||||
|
||||
"source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
|
||||
|
||||
"std-env": ["std-env@3.10.0", "", {}, "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg=="],
|
||||
|
||||
"strip-final-newline": ["strip-final-newline@3.0.0", "", {}, "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw=="],
|
||||
|
||||
"strip-literal": ["strip-literal@3.1.0", "", { "dependencies": { "js-tokens": "^9.0.1" } }, "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg=="],
|
||||
|
||||
"tailwind-merge": ["tailwind-merge@3.5.0", "", {}, "sha512-I8K9wewnVDkL1NTGoqWmVEIlUcB9gFriAEkXkfCjX5ib8ezGxtR3xD7iZIxrfArjEsH7F1CHD4RFUtxefdqV/A=="],
|
||||
|
||||
"tailwind-variants": ["tailwind-variants@3.2.2", "", { "peerDependencies": { "tailwind-merge": ">=3.0.0", "tailwindcss": "*" } }, "sha512-Mi4kHeMTLvKlM98XPnK+7HoBPmf4gygdFmqQPaDivc3DpYS6aIY6KiG/PgThrGvii5YZJqRsPz0aPyhoFzmZgg=="],
|
||||
|
||||
"tailwindcss": ["tailwindcss@4.2.1", "", {}, "sha512-/tBrSQ36vCleJkAOsy9kbNTgaxvGbyOamC30PRePTQe/o1MFwEKHQk4Cn7BNGaPtjp+PuUrByJehM1hgxfq4sw=="],
|
||||
|
||||
"tapable": ["tapable@2.3.0", "", {}, "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg=="],
|
||||
|
||||
"tiny-inflate": ["tiny-inflate@1.0.3", "", {}, "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw=="],
|
||||
|
||||
"tinyexec": ["tinyexec@1.0.2", "", {}, "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg=="],
|
||||
|
||||
"tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="],
|
||||
|
||||
"totalist": ["totalist@3.0.1", "", {}, "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ=="],
|
||||
|
||||
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||
|
||||
"type-level-regexp": ["type-level-regexp@0.1.17", "", {}, "sha512-wTk4DH3cxwk196uGLK/E9pE45aLfeKJacKmcEgEOA/q5dnPGNxXt0cfYdFxb57L+sEpf1oJH4Dnx/pnRcku9jg=="],
|
||||
|
||||
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
||||
|
||||
"uc.micro": ["uc.micro@2.1.0", "", {}, "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A=="],
|
||||
|
||||
"ufo": ["ufo@1.6.3", "", {}, "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q=="],
|
||||
|
||||
"uncrypto": ["uncrypto@0.1.3", "", {}, "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q=="],
|
||||
|
||||
"unctx": ["unctx@2.5.0", "", { "dependencies": { "acorn": "^8.15.0", "estree-walker": "^3.0.3", "magic-string": "^0.30.21", "unplugin": "^2.3.11" } }, "sha512-p+Rz9x0R7X+CYDkT+Xg8/GhpcShTlU8n+cf9OtOEf7zEQsNcCZO1dPKNRDqvUTaq+P32PMMkxWHwfrxkqfqAYg=="],
|
||||
|
||||
"unhead": ["unhead@2.1.10", "", { "dependencies": { "hookable": "^6.0.1" } }, "sha512-We8l9uNF8zz6U8lfQaVG70+R/QBfQx1oPIgXin4BtZnK2IQpz6yazQ0qjMNVBDw2ADgF2ea58BtvSK+XX5AS7g=="],
|
||||
|
||||
"unifont": ["unifont@0.7.4", "", { "dependencies": { "css-tree": "^3.1.0", "ofetch": "^1.5.1", "ohash": "^2.0.11" } }, "sha512-oHeis4/xl42HUIeHuNZRGEvxj5AaIKR+bHPNegRq5LV1gdc3jundpONbjglKpihmJf+dswygdMJn3eftGIMemg=="],
|
||||
|
||||
"unimport": ["unimport@5.7.0", "", { "dependencies": { "acorn": "^8.16.0", "escape-string-regexp": "^5.0.0", "estree-walker": "^3.0.3", "local-pkg": "^1.1.2", "magic-string": "^0.30.21", "mlly": "^1.8.0", "pathe": "^2.0.3", "picomatch": "^4.0.3", "pkg-types": "^2.3.0", "scule": "^1.3.0", "strip-literal": "^3.1.0", "tinyglobby": "^0.2.15", "unplugin": "^2.3.11", "unplugin-utils": "^0.3.1" } }, "sha512-njnL6sp8lEA8QQbZrt+52p/g4X0rw3bnGGmUcJnt1jeG8+iiqO779aGz0PirCtydAIVcuTBRlJ52F0u46z309Q=="],
|
||||
|
||||
"unplugin": ["unplugin@3.0.0", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "picomatch": "^4.0.3", "webpack-virtual-modules": "^0.6.2" } }, "sha512-0Mqk3AT2TZCXWKdcoaufeXNukv2mTrEZExeXlHIOZXdqYoHHr4n51pymnwV8x2BOVxwXbK2HLlI7usrqMpycdg=="],
|
||||
|
||||
"unplugin-auto-import": ["unplugin-auto-import@21.0.0", "", { "dependencies": { "local-pkg": "^1.1.2", "magic-string": "^0.30.21", "picomatch": "^4.0.3", "unimport": "^5.6.0", "unplugin": "^2.3.11", "unplugin-utils": "^0.3.1" }, "peerDependencies": { "@nuxt/kit": "^4.0.0", "@vueuse/core": "*" } }, "sha512-vWuC8SwqJmxZFYwPojhOhOXDb5xFhNNcEVb9K/RFkyk/3VnfaOjzitWN7v+8DEKpMjSsY2AEGXNgt6I0yQrhRQ=="],
|
||||
|
||||
"unplugin-utils": ["unplugin-utils@0.3.1", "", { "dependencies": { "pathe": "^2.0.3", "picomatch": "^4.0.3" } }, "sha512-5lWVjgi6vuHhJ526bI4nlCOmkCIF3nnfXkCMDeMJrtdvxTs6ZFCM8oNufGTsDbKv/tJ/xj8RpvXjRuPBZJuJog=="],
|
||||
|
||||
"unplugin-vue-components": ["unplugin-vue-components@31.0.0", "", { "dependencies": { "chokidar": "^5.0.0", "local-pkg": "^1.1.2", "magic-string": "^0.30.21", "mlly": "^1.8.0", "obug": "^2.1.1", "picomatch": "^4.0.3", "tinyglobby": "^0.2.15", "unplugin": "^2.3.11", "unplugin-utils": "^0.3.1" }, "peerDependencies": { "@nuxt/kit": "^3.2.2 || ^4.0.0", "vue": "^3.0.0" } }, "sha512-4ULwfTZTLuWJ7+S9P7TrcStYLsSRkk6vy2jt/WTfgUEUb0nW9//xxmrfhyHUEVpZ2UKRRwfRb8Yy15PDbVZf+Q=="],
|
||||
|
||||
"unstorage": ["unstorage@1.17.4", "", { "dependencies": { "anymatch": "^3.1.3", "chokidar": "^5.0.0", "destr": "^2.0.5", "h3": "^1.15.5", "lru-cache": "^11.2.0", "node-fetch-native": "^1.6.7", "ofetch": "^1.5.1", "ufo": "^1.6.3" }, "peerDependencies": { "@azure/app-configuration": "^1.8.0", "@azure/cosmos": "^4.2.0", "@azure/data-tables": "^13.3.0", "@azure/identity": "^4.6.0", "@azure/keyvault-secrets": "^4.9.0", "@azure/storage-blob": "^12.26.0", "@capacitor/preferences": "^6 || ^7 || ^8", "@deno/kv": ">=0.9.0", "@netlify/blobs": "^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0", "@planetscale/database": "^1.19.0", "@upstash/redis": "^1.34.3", "@vercel/blob": ">=0.27.1", "@vercel/functions": "^2.2.12 || ^3.0.0", "@vercel/kv": "^1 || ^2 || ^3", "aws4fetch": "^1.0.20", "db0": ">=0.2.1", "idb-keyval": "^6.2.1", "ioredis": "^5.4.2", "uploadthing": "^7.4.4" }, "optionalPeers": ["@azure/app-configuration", "@azure/cosmos", "@azure/data-tables", "@azure/identity", "@azure/keyvault-secrets", "@azure/storage-blob", "@capacitor/preferences", "@deno/kv", "@netlify/blobs", "@planetscale/database", "@upstash/redis", "@vercel/blob", "@vercel/functions", "@vercel/kv", "aws4fetch", "db0", "idb-keyval", "ioredis", "uploadthing"] }, "sha512-fHK0yNg38tBiJKp/Vgsq4j0JEsCmgqH58HAn707S7zGkArbZsVr/CwINoi+nh3h98BRCwKvx1K3Xg9u3VV83sw=="],
|
||||
|
||||
"untyped": ["untyped@2.0.0", "", { "dependencies": { "citty": "^0.1.6", "defu": "^6.1.4", "jiti": "^2.4.2", "knitwork": "^1.2.0", "scule": "^1.3.0" }, "bin": "dist/cli.mjs" }, "sha512-nwNCjxJTjNuLCgFr42fEak5OcLuB3ecca+9ksPFNvtfYSLpjf+iJqSIaSnIile6ZPbKYxI5k2AfXqeopGudK/g=="],
|
||||
|
||||
"vaul-vue": ["vaul-vue@0.4.1", "", { "dependencies": { "@vueuse/core": "^10.8.0", "reka-ui": "^2.0.0", "vue": "^3.4.5" }, "peerDependencies": { "reka-ui": "^2.0.0", "vue": "^3.3.0" } }, "sha512-A6jOWOZX5yvyo1qMn7IveoWN91mJI5L3BUKsIwkg6qrTGgHs1Sb1JF/vyLJgnbN1rH4OOOxFbtqL9A46bOyGUQ=="],
|
||||
|
||||
"vite": ["vite@7.3.1", "", { "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": "bin/vite.js" }, "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA=="],
|
||||
|
||||
"vue": ["vue@3.5.29", "", { "dependencies": { "@vue/compiler-dom": "3.5.29", "@vue/compiler-sfc": "3.5.29", "@vue/runtime-dom": "3.5.29", "@vue/server-renderer": "3.5.29", "@vue/shared": "3.5.29" }, "peerDependencies": { "typescript": "*" } }, "sha512-BZqN4Ze6mDQVNAni0IHeMJ5mwr8VAJ3MQC9FmprRhcBYENw+wOAAjRj8jfmN6FLl0j96OXbR+CjWhmAmM+QGnA=="],
|
||||
|
||||
"vue-chartjs": ["vue-chartjs@5.3.3", "", { "peerDependencies": { "chart.js": "^4.1.1", "vue": "^3.0.0-0 || ^2.7.0" } }, "sha512-jqxtL8KZ6YJ5NTv6XzrzLS7osyegOi28UGNZW0h9OkDL7Sh1396ht4Dorh04aKrl2LiSalQ84WtqiG0RIJb0tA=="],
|
||||
|
||||
"vue-component-type-helpers": ["vue-component-type-helpers@3.2.5", "", {}, "sha512-tkvNr+bU8+xD/onAThIe7CHFvOJ/BO6XCOrxMzeytJq40nTfpGDJuVjyCM8ccGZKfAbGk2YfuZyDMXM56qheZQ=="],
|
||||
|
||||
"vue-demi": ["vue-demi@0.14.10", "", { "peerDependencies": { "@vue/composition-api": "^1.0.0-rc.1", "vue": "^3.0.0-0 || ^2.6.0" }, "optionalPeers": ["@vue/composition-api"], "bin": { "vue-demi-fix": "bin/vue-demi-fix.js", "vue-demi-switch": "bin/vue-demi-switch.js" } }, "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg=="],
|
||||
|
||||
"w3c-keyname": ["w3c-keyname@2.2.8", "", {}, "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ=="],
|
||||
|
||||
"webpack-virtual-modules": ["webpack-virtual-modules@0.6.2", "", {}, "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ=="],
|
||||
|
||||
"wheel-gestures": ["wheel-gestures@2.2.48", "", {}, "sha512-f+Gy33Oa5Z14XY9679Zze+7VFhbsQfBFXodnU2x589l4kxGM9L5Y8zETTmcMR5pWOPQyRv4Z0lNax6xCO0NSlA=="],
|
||||
|
||||
"which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
|
||||
|
||||
"y-protocols": ["y-protocols@1.0.7", "", { "dependencies": { "lib0": "^0.2.85" }, "peerDependencies": { "yjs": "^13.0.0" } }, "sha512-YSVsLoXxO67J6eE/nV4AtFtT3QEotZf5sK5BHxFBXso7VDUT3Tx07IfA6hsu5Q5OmBdMkQVmFZ9QOA7fikWvnw=="],
|
||||
|
||||
"yjs": ["yjs@13.6.29", "", { "dependencies": { "lib0": "^0.2.99" } }, "sha512-kHqDPdltoXH+X4w1lVmMtddE3Oeqq48nM40FD5ojTd8xYhQpzIDcfE2keMSU5bAgRPJBe225WTUdyUgj1DtbiQ=="],
|
||||
|
||||
"@nuxtjs/color-mode/@nuxt/kit": ["@nuxt/kit@3.21.1", "", { "dependencies": { "c12": "^3.3.3", "consola": "^3.4.2", "defu": "^6.1.4", "destr": "^2.0.5", "errx": "^0.1.0", "exsolve": "^1.0.8", "ignore": "^7.0.5", "jiti": "^2.6.1", "klona": "^2.0.6", "knitwork": "^1.3.0", "mlly": "^1.8.0", "ohash": "^2.0.11", "pathe": "^2.0.3", "pkg-types": "^2.3.0", "rc9": "^3.0.0", "scule": "^1.3.0", "semver": "^7.7.4", "tinyglobby": "^0.2.15", "ufo": "^1.6.3", "unctx": "^2.5.0", "untyped": "^2.0.0" } }, "sha512-QORZRjcuTKgo++XP1Pc2c2gqwRydkaExrIRfRI9vFsPA3AzuHVn5Gfmbv1ic8y34e78mr5DMBvJlelUaeOuajg=="],
|
||||
|
||||
"@nuxtjs/color-mode/pathe": ["pathe@1.1.2", "", {}, "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ=="],
|
||||
|
||||
"@nuxtjs/color-mode/pkg-types": ["pkg-types@1.3.1", "", { "dependencies": { "confbox": "^0.1.8", "mlly": "^1.7.4", "pathe": "^2.0.1" } }, "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ=="],
|
||||
|
||||
"@unhead/vue/hookable": ["hookable@6.0.1", "", {}, "sha512-uKGyY8BuzN/a5gvzvA+3FVWo0+wUjgtfSdnmjtrOVwQCZPHpHDH2WRO3VZSOeluYrHoDCiXFffZXs8Dj1ULWtw=="],
|
||||
|
||||
"@vue/compiler-core/entities": ["entities@7.0.1", "", {}, "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA=="],
|
||||
|
||||
"@vue/compiler-core/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="],
|
||||
|
||||
"@vue/compiler-sfc/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="],
|
||||
|
||||
"anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
||||
|
||||
"c12/rc9": ["rc9@2.1.2", "", { "dependencies": { "defu": "^6.1.4", "destr": "^2.0.3" } }, "sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg=="],
|
||||
|
||||
"fontaine/unplugin": ["unplugin@2.3.11", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "acorn": "^8.15.0", "picomatch": "^4.0.3", "webpack-virtual-modules": "^0.6.2" } }, "sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww=="],
|
||||
|
||||
"magic-regexp/unplugin": ["unplugin@2.3.11", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "acorn": "^8.15.0", "picomatch": "^4.0.3", "webpack-virtual-modules": "^0.6.2" } }, "sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww=="],
|
||||
|
||||
"mlly/pkg-types": ["pkg-types@1.3.1", "", { "dependencies": { "confbox": "^0.1.8", "mlly": "^1.7.4", "pathe": "^2.0.1" } }, "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ=="],
|
||||
|
||||
"npm-run-path/path-key": ["path-key@4.0.0", "", {}, "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="],
|
||||
|
||||
"nypm/citty": ["citty@0.2.1", "", {}, "sha512-kEV95lFBhQgtogAPlQfJJ0WGVSokvLr/UEoFPiKKOXF7pl98HfUVUD0ejsuTCld/9xH9vogSywZ5KqHzXrZpqg=="],
|
||||
|
||||
"unctx/unplugin": ["unplugin@2.3.11", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "acorn": "^8.15.0", "picomatch": "^4.0.3", "webpack-virtual-modules": "^0.6.2" } }, "sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww=="],
|
||||
|
||||
"unhead/hookable": ["hookable@6.0.1", "", {}, "sha512-uKGyY8BuzN/a5gvzvA+3FVWo0+wUjgtfSdnmjtrOVwQCZPHpHDH2WRO3VZSOeluYrHoDCiXFffZXs8Dj1ULWtw=="],
|
||||
|
||||
"unimport/escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="],
|
||||
|
||||
"unimport/unplugin": ["unplugin@2.3.11", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "acorn": "^8.15.0", "picomatch": "^4.0.3", "webpack-virtual-modules": "^0.6.2" } }, "sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww=="],
|
||||
|
||||
"unplugin-auto-import/unplugin": ["unplugin@2.3.11", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "acorn": "^8.15.0", "picomatch": "^4.0.3", "webpack-virtual-modules": "^0.6.2" } }, "sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww=="],
|
||||
|
||||
"unplugin-vue-components/unplugin": ["unplugin@2.3.11", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "acorn": "^8.15.0", "picomatch": "^4.0.3", "webpack-virtual-modules": "^0.6.2" } }, "sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww=="],
|
||||
|
||||
"vaul-vue/@vueuse/core": ["@vueuse/core@10.11.1", "", { "dependencies": { "@types/web-bluetooth": "^0.0.20", "@vueuse/metadata": "10.11.1", "@vueuse/shared": "10.11.1", "vue-demi": ">=0.14.8" } }, "sha512-guoy26JQktXPcz+0n3GukWIy/JDNKti9v6VEMu6kV2sYBsWuGiTU8OWdg+ADfUbHg3/3DlqySDe7JmdHrktiww=="],
|
||||
|
||||
"@nuxtjs/color-mode/@nuxt/kit/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
|
||||
|
||||
"@nuxtjs/color-mode/@nuxt/kit/pkg-types": ["pkg-types@2.3.0", "", { "dependencies": { "confbox": "^0.2.2", "exsolve": "^1.0.7", "pathe": "^2.0.3" } }, "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig=="],
|
||||
|
||||
"@nuxtjs/color-mode/pkg-types/confbox": ["confbox@0.1.8", "", {}, "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w=="],
|
||||
|
||||
"@nuxtjs/color-mode/pkg-types/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
|
||||
|
||||
"mlly/pkg-types/confbox": ["confbox@0.1.8", "", {}, "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w=="],
|
||||
|
||||
"vaul-vue/@vueuse/core/@types/web-bluetooth": ["@types/web-bluetooth@0.0.20", "", {}, "sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow=="],
|
||||
|
||||
"vaul-vue/@vueuse/core/@vueuse/metadata": ["@vueuse/metadata@10.11.1", "", {}, "sha512-IGa5FXd003Ug1qAZmyE8wF3sJ81xGLSqTqtQ6jaVfkeZ4i5kS2mwQF61yhVqojRnenVew5PldLyRgvdl4YYuSw=="],
|
||||
|
||||
"vaul-vue/@vueuse/core/@vueuse/shared": ["@vueuse/shared@10.11.1", "", { "dependencies": { "vue-demi": ">=0.14.8" } }, "sha512-LHpC8711VFZlDaYUXEBbFBCQ7GS3dVU9mjOhhMhXP6txTV4EhYQg/KGnQuvt/sPAtoUKq7VVUnL6mVtFoL42sA=="],
|
||||
|
||||
"vaul-vue/@vueuse/core/vue-demi": ["vue-demi@0.14.10", "", { "peerDependencies": { "@vue/composition-api": "^1.0.0-rc.1", "vue": "^3.0.0-0 || ^2.6.0" }, "optionalPeers": ["@vue/composition-api"], "bin": { "vue-demi-fix": "bin/vue-demi-fix.js", "vue-demi-switch": "bin/vue-demi-switch.js" } }, "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg=="],
|
||||
|
||||
"vaul-vue/@vueuse/core/@vueuse/shared/vue-demi": ["vue-demi@0.14.10", "", { "peerDependencies": { "@vue/composition-api": "^1.0.0-rc.1", "vue": "^3.0.0-0 || ^2.6.0" }, "optionalPeers": ["@vue/composition-api"], "bin": { "vue-demi-fix": "bin/vue-demi-fix.js", "vue-demi-switch": "bin/vue-demi-switch.js" } }, "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg=="],
|
||||
}
|
||||
}
|
||||
1
go.mod
1
go.mod
@@ -11,6 +11,7 @@ require (
|
||||
github.com/gofiber/fiber/v3 v3.1.0
|
||||
github.com/golang-jwt/jwt/v5 v5.3.1
|
||||
github.com/joho/godotenv v1.5.1
|
||||
github.com/meilisearch/meilisearch-go v0.36.1
|
||||
github.com/openai/openai-go/v3 v3.28.0
|
||||
github.com/samber/lo v1.53.0
|
||||
golang.org/x/crypto v0.48.0
|
||||
|
||||
2
go.sum
2
go.sum
@@ -109,6 +109,8 @@ github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHP
|
||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/meilisearch/meilisearch-go v0.36.1 h1:mJTCJE5g7tRvaqKco6DfqOuJEjX+rRltDEnkEC02Y0M=
|
||||
github.com/meilisearch/meilisearch-go v0.36.1/go.mod h1:hWcR0MuWLSzHfbz9GGzIr3s9rnXLm1jqkmHkJPbUSvM=
|
||||
github.com/onsi/gomega v1.39.1 h1:1IJLAad4zjPn2PsnhH70V4DKRFlrCzGBNrNaru+Vf28=
|
||||
github.com/onsi/gomega v1.39.1/go.mod h1:hL6yVALoTOxeWudERyfppUcZXjMwIMLnuSfruD2lcfg=
|
||||
github.com/openai/openai-go/v3 v3.28.0 h1:2+FfrCVMdGXSQrBv1tLWtokm+BU7+3hJ/8rAHPQ63KM=
|
||||
|
||||
@@ -6,16 +6,16 @@ CREATE TABLE IF NOT EXISTS b2b_tracker_routes (
|
||||
path VARCHAR(255) NULL,
|
||||
component VARCHAR(255) NOT NULL COMMENT 'path to component file',
|
||||
layout VARCHAR(50) DEFAULT 'default' COMMENT "'default' | 'empty'",
|
||||
meta JSON DEFAULT '{}' ,
|
||||
meta JSON DEFAULT '{}',
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
sort_order INT DEFAULT 0,
|
||||
parent_id INT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
parent_id INT NULL,
|
||||
|
||||
ALTER TABLE b2b_tracker_routes
|
||||
ADD CONSTRAINT fk_parent
|
||||
FOREIGN KEY (parent_id) REFERENCES b2b_tracker_routes(id)
|
||||
ON DELETE SET NULL;
|
||||
CONSTRAINT fk_parent
|
||||
FOREIGN KEY (parent_id)
|
||||
REFERENCES b2b_tracker_routes(id)
|
||||
ON DELETE SET NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
INSERT IGNORE INTO b2b_tracker_routes
|
||||
(name, path, component, layout, meta, is_active, sort_order, parent_id)
|
||||
|
||||
@@ -72,7 +72,8 @@ CREATE TABLE IF NOT EXISTS b2b_customers (
|
||||
password_reset_expires DATETIME(6) NULL,
|
||||
last_password_reset_request DATETIME(6) NULL,
|
||||
last_login_at DATETIME(6) NULL,
|
||||
lang VARCHAR(10) NULL DEFAULT 'en',
|
||||
lang_id BIGINT NULL DEFAULT 2,
|
||||
country_id BIGINT NULL DEFAULT 2,
|
||||
created_at DATETIME(6) NULL,
|
||||
updated_at DATETIME(6) NULL,
|
||||
deleted_at DATETIME(6) NULL
|
||||
@@ -112,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);
|
||||
|
||||
|
||||
-- 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, 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, 'pl', '2026-03-02 16:55:10.252740', '2026-03-02 16:55:10.252740', NULL);
|
||||
ALTER TABLE b2b_customers AUTO_INCREMENT = 1;
|
||||
|
||||
-- countries
|
||||
CREATE TABLE IF NOT EXISTS b2b_countries (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
|
||||
431
package-lock.json
generated
431
package-lock.json
generated
@@ -4,6 +4,7 @@
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "b2b",
|
||||
"dependencies": {
|
||||
"@nuxt/ui": "^4.5.1",
|
||||
"chart.js": "^4.5.1",
|
||||
@@ -1006,6 +1007,395 @@
|
||||
"integrity": "sha512-42aWfPrimMfDKDi4YegyS7x+/0tlzaqwPQCULLanv3DMIlu96KTJR0fM5isWX2UViOqlGnX6YFgqWepcX+XMNg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz",
|
||||
"integrity": "sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm64": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.0.tgz",
|
||||
"integrity": "sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-darwin-arm64": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.0.tgz",
|
||||
"integrity": "sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-darwin-x64": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.0.tgz",
|
||||
"integrity": "sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-freebsd-arm64": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.0.tgz",
|
||||
"integrity": "sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-freebsd-x64": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.0.tgz",
|
||||
"integrity": "sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.0.tgz",
|
||||
"integrity": "sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.0.tgz",
|
||||
"integrity": "sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm64-gnu": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.59.0.tgz",
|
||||
"integrity": "sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm64-musl": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.0.tgz",
|
||||
"integrity": "sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-loong64-gnu": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.59.0.tgz",
|
||||
"integrity": "sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==",
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-loong64-musl": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.0.tgz",
|
||||
"integrity": "sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==",
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-ppc64-gnu": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.0.tgz",
|
||||
"integrity": "sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-ppc64-musl": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.59.0.tgz",
|
||||
"integrity": "sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.59.0.tgz",
|
||||
"integrity": "sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-riscv64-musl": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.59.0.tgz",
|
||||
"integrity": "sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-s390x-gnu": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.59.0.tgz",
|
||||
"integrity": "sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-x64-gnu": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.0.tgz",
|
||||
"integrity": "sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-x64-musl": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.0.tgz",
|
||||
"integrity": "sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-openbsd-x64": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.0.tgz",
|
||||
"integrity": "sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openbsd"
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-openharmony-arm64": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.0.tgz",
|
||||
"integrity": "sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openharmony"
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-arm64-msvc": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.0.tgz",
|
||||
"integrity": "sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-ia32-msvc": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.0.tgz",
|
||||
"integrity": "sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-x64-gnu": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.0.tgz",
|
||||
"integrity": "sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-x64-msvc": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.0.tgz",
|
||||
"integrity": "sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@standard-schema/spec": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz",
|
||||
@@ -1975,13 +2365,13 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@unhead/vue": {
|
||||
"version": "2.1.10",
|
||||
"resolved": "https://registry.npmjs.org/@unhead/vue/-/vue-2.1.10.tgz",
|
||||
"integrity": "sha512-VP78Onh2HNezLPfhYjfHqn4dxlcQsE6PJgTTs61NksO/thvilNswtgBq0N0MWCLtn43N5akEPGW2y2zxM3PWgQ==",
|
||||
"version": "2.1.12",
|
||||
"resolved": "https://registry.npmjs.org/@unhead/vue/-/vue-2.1.12.tgz",
|
||||
"integrity": "sha512-zEWqg0nZM8acpuTZE40wkeUl8AhIe0tU0OkilVi1D4fmVjACrwoh5HP6aNqJ8kUnKsoy6D+R3Vi/O+fmdNGO7g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"hookable": "^6.0.1",
|
||||
"unhead": "2.1.10"
|
||||
"unhead": "2.1.12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/harlan-zw"
|
||||
@@ -2801,6 +3191,21 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/fsevents": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/fuse.js": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-7.1.0.tgz",
|
||||
@@ -2846,9 +3251,9 @@
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/h3": {
|
||||
"version": "1.15.5",
|
||||
"resolved": "https://registry.npmjs.org/h3/-/h3-1.15.5.tgz",
|
||||
"integrity": "sha512-xEyq3rSl+dhGX2Lm0+eFQIAzlDN6Fs0EcC4f7BNUmzaRX/PTzeuM+Tr2lHB8FoXggsQIeXLj8EDVgs5ywxyxmg==",
|
||||
"version": "1.15.8",
|
||||
"resolved": "https://registry.npmjs.org/h3/-/h3-1.15.8.tgz",
|
||||
"integrity": "sha512-iOH6Vl8mGd9nNfu9C0IZ+GuOAfJHcyf3VriQxWaSWIB76Fg4BnFuk4cxBxjmQSSxJS664+pgjP6e7VBnUzFfcg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cookie-es": "^1.2.2",
|
||||
@@ -4257,9 +4662,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/unhead": {
|
||||
"version": "2.1.10",
|
||||
"resolved": "https://registry.npmjs.org/unhead/-/unhead-2.1.10.tgz",
|
||||
"integrity": "sha512-We8l9uNF8zz6U8lfQaVG70+R/QBfQx1oPIgXin4BtZnK2IQpz6yazQ0qjMNVBDw2ADgF2ea58BtvSK+XX5AS7g==",
|
||||
"version": "2.1.12",
|
||||
"resolved": "https://registry.npmjs.org/unhead/-/unhead-2.1.12.tgz",
|
||||
"integrity": "sha512-iTHdWD9ztTunOErtfUFk6Wr11BxvzumcYJ0CzaSCBUOEtg+DUZ9+gnE99i8QkLFT2q1rZD48BYYGXpOZVDLYkA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"hookable": "^6.0.1"
|
||||
@@ -4269,9 +4674,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/unhead/node_modules/hookable": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/hookable/-/hookable-6.0.1.tgz",
|
||||
"integrity": "sha512-uKGyY8BuzN/a5gvzvA+3FVWo0+wUjgtfSdnmjtrOVwQCZPHpHDH2WRO3VZSOeluYrHoDCiXFffZXs8Dj1ULWtw==",
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/hookable/-/hookable-6.1.0.tgz",
|
||||
"integrity": "sha512-ZoKZSJgu8voGK2geJS+6YtYjvIzu9AOM/KZXsBxr83uhLL++e9pEv/dlgwgy3dvHg06kTz6JOh1hk3C8Ceiymw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/unifont": {
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
package listProductsRepo
|
||||
|
||||
import (
|
||||
"git.ma-al.com/goc_daniel/b2b/app/db"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/model"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/query/filters"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/query/find"
|
||||
)
|
||||
|
||||
type UIListProductsRepo interface {
|
||||
GetListing(p find.Paging, filt *filters.FiltersList) (find.Found[model.Product], error)
|
||||
}
|
||||
|
||||
type ListProductsRepo struct{}
|
||||
|
||||
func New() UIListProductsRepo {
|
||||
return &ListProductsRepo{}
|
||||
}
|
||||
|
||||
func (repo *ListProductsRepo) GetListing(p find.Paging, filt *filters.FiltersList) (find.Found[model.Product], error) {
|
||||
var listing []model.Product
|
||||
var total int64
|
||||
|
||||
// Apply filters here
|
||||
q := db.DB.Table("ps_product")
|
||||
|
||||
// var resultIDs []uint
|
||||
// q := db.DB.
|
||||
// // SQL_CALC_FOUND_ROWS is a neat trick which works on MariaDB and
|
||||
// // MySQL. It works when followed by `SELECT FOUND_ROWS();`. To learn
|
||||
// // more see: https://mariarawmodel.com/kb/en/found_rows/
|
||||
// // WARN: This might not work on different SQL databases
|
||||
// Select("DISTINCT SQL_CALC_FOUND_ROWS id").
|
||||
// // Debug().
|
||||
// Scopes(view.FromDBViewForDisplay(langID, countryIso)).
|
||||
// Scopes(scopesForFiltersOnDisplay(db.DB, langID, countryIso, filt)).
|
||||
// Scopes(filt.OfCategory(filters.ORDER_FILTER)...).
|
||||
// Limit(p.Limit()).
|
||||
// Offset(p.Offset())
|
||||
|
||||
err := q.Count(&total).Error
|
||||
if err != nil {
|
||||
return find.Found[model.Product]{}, err
|
||||
}
|
||||
|
||||
err = q.
|
||||
Limit(p.Limit()).
|
||||
Offset(p.Offset()).
|
||||
Scan(&listing).Error
|
||||
if err != nil {
|
||||
return find.Found[model.Product]{}, err
|
||||
}
|
||||
|
||||
return find.Found[model.Product]{
|
||||
Items: listing,
|
||||
Count: uint(total),
|
||||
}, nil
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
package productDescriptionRepo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.ma-al.com/goc_daniel/b2b/app/db"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/model"
|
||||
)
|
||||
|
||||
type UIProductDescriptionRepo interface {
|
||||
GetProductDescription(productID uint, productShopID uint, productLangID uint) (*model.ProductDescription, error)
|
||||
CreateIfDoesNotExist(productID uint, productShopID uint, productLangID uint) error
|
||||
UpdateFields(productID uint, productShopID uint, productLangID uint, updates map[string]string) error
|
||||
}
|
||||
|
||||
type ProductDescriptionRepo struct{}
|
||||
|
||||
func New() UIProductDescriptionRepo {
|
||||
return &ProductDescriptionRepo{}
|
||||
}
|
||||
|
||||
// We assume that any user has access to all product descriptions
|
||||
func (r *ProductDescriptionRepo) GetProductDescription(productID uint, productShopID uint, productLangID uint) (*model.ProductDescription, error) {
|
||||
var ProductDescription model.ProductDescription
|
||||
|
||||
err := db.DB.
|
||||
Table("ps_product_lang").
|
||||
Where("id_product = ? AND id_shop = ? AND id_lang = ?", productID, productShopID, productLangID).
|
||||
First(&ProductDescription).Error
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("database error: %w", err)
|
||||
}
|
||||
|
||||
return &ProductDescription, nil
|
||||
}
|
||||
|
||||
// If it doesn't exist, returns an error.
|
||||
func (r *ProductDescriptionRepo) CreateIfDoesNotExist(productID uint, productShopID uint, productLangID uint) error {
|
||||
record := model.ProductDescription{
|
||||
ProductID: productID,
|
||||
ShopID: productShopID,
|
||||
LangID: productLangID,
|
||||
}
|
||||
|
||||
err := db.DB.
|
||||
Table("ps_product_lang").
|
||||
Where("id_product = ? AND id_shop = ? AND id_lang = ?", productID, productShopID, productLangID).
|
||||
FirstOrCreate(&record).Error
|
||||
if err != nil {
|
||||
return fmt.Errorf("database error: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *ProductDescriptionRepo) UpdateFields(productID uint, productShopID uint, productLangID uint, updates map[string]string) error {
|
||||
if len(updates) == 0 {
|
||||
return nil
|
||||
}
|
||||
updatesIface := make(map[string]interface{}, len(updates))
|
||||
for k, v := range updates {
|
||||
updatesIface[k] = v
|
||||
}
|
||||
|
||||
err := db.DB.
|
||||
Table("ps_product_lang").
|
||||
Where("id_product = ? AND id_shop = ? AND id_lang = ?", productID, productShopID, productLangID).
|
||||
Updates(updatesIface).Error
|
||||
if err != nil {
|
||||
return fmt.Errorf("database error: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user