70 Commits

Author SHA1 Message Date
059808665f Merge remote-tracking branch 'origin/translate' into front-styles 2026-04-14 08:58:32 +02:00
b54645830f fix: store customer-product 2026-04-14 08:55:53 +02:00
1973189525 Merge remote-tracking branch 'origin/main' into front-styles 2026-04-14 08:18:19 +02:00
26cbdeec0a Merge pull request 'product-procedures' (#59) from product-procedures into main
Reviewed-on: #59
Reviewed-by: goc_daniel <goc_daniel@ma-al.com>
2026-04-13 13:32:28 +00:00
38cb07f3d4 chore: address pull request review issues 2026-04-13 14:22:14 +02:00
5d97d537cd Merge remote-tracking branch 'origin/main' into front-styles 2026-04-13 08:22:35 +02:00
2e61cde742 Merge branch 'main' of ssh://git.ma-al.com:8822/goc_daniel/b2b into product-procedures 2026-04-10 15:26:36 +02:00
54608410ea feat: create specific price system and adapt product queries 2026-04-10 15:04:34 +02:00
f1a2f4c0b2 fix: fix storage file 2026-04-10 11:38:36 +02:00
bfd20aaa7b Merge branch 'main' of ssh://git.ma-al.com:8822/goc_daniel/b2b into translate 2026-04-10 10:44:47 +02:00
8024d9f739 Merge pull request 'favorites' (#57) from favorites into main
Reviewed-on: #57
Reviewed-by: Wiktor Dudzic <dudzic_wiktor@ma-al.com>
2026-04-10 08:09:13 +00:00
1fb2a33cfd fix: create component StorageFileBrowser 2026-04-09 16:00:36 +02:00
75af44b0df feat: product_attribute list with prices 2026-04-09 09:51:06 +02:00
0d2bf3a27f Merge branch 'main' of ssh://git.ma-al.com:8822/goc_daniel/b2b into translate 2026-04-09 08:28:47 +02:00
c7692bc817 fix: page serchUsers 2026-04-08 13:51:10 +02:00
8824796846 Merge branch 'main' of ssh://git.ma-al.com:8822/goc_daniel/b2b into translate 2026-04-08 10:09:40 +02:00
9eb8fc6625 fix: page usersList 2026-04-07 16:06:36 +02:00
a290a72d1d fix: style 2026-04-07 13:54:08 +02:00
b0d0338d23 Merge branch 'main' of ssh://git.ma-al.com:8822/goc_daniel/b2b into translate 2026-04-07 13:40:51 +02:00
849b18c4db Merge branch 'main' of ssh://git.ma-al.com:8822/goc_daniel/b2b into translate 2026-04-07 12:55:50 +02:00
a874a063d8 fix: page usersList 2026-04-07 10:06:36 +02:00
11dab263fa Merge branch 'main' of ssh://git.ma-al.com:8822/goc_daniel/b2b into translate 2026-04-07 08:03:23 +02:00
5bda48b94a Merge branch 'front-styles' of ssh://git.ma-al.com:8822/goc_daniel/b2b into translate 2026-04-07 08:02:32 +02:00
2f4ccbaf92 fix: import 2026-04-07 08:01:21 +02:00
fb910b9ac6 Merge remote-tracking branch 'origin/main' into front-styles 2026-04-03 16:33:32 +02:00
4c505c0520 Merge branch 'front-styles' of ssh://git.ma-al.com:8822/goc_daniel/b2b into translate 2026-04-03 15:54:23 +02:00
2f7e313c95 fix: user info 2026-04-03 15:53:31 +02:00
110946a924 Merge remote-tracking branch 'origin/main' into front-styles 2026-04-03 14:35:29 +02:00
3083330fcd Merge branch 'front-styles' of ssh://git.ma-al.com:8822/goc_daniel/b2b into translate 2026-04-03 11:32:10 +02:00
b7c4b6e3fd fix: store 2026-04-03 11:32:04 +02:00
e9016baf5b Merge remote-tracking branch 'origin/product-procedures' into front-styles 2026-04-03 11:31:35 +02:00
68f31952da Merge branch 'front-styles' of ssh://git.ma-al.com:8822/goc_daniel/b2b into translate 2026-04-03 10:56:21 +02:00
c1efcf1b86 fix: style 2026-04-03 10:55:12 +02:00
5cab7573a8 Merge remote-tracking branch 'origin/product-procedures' into front-styles 2026-04-03 10:54:35 +02:00
5f5c3641f9 Merge remote-tracking branch 'origin/product-procedures' into front-styles 2026-04-03 09:23:51 +02:00
d655e22173 chore: add bruno endpoints 2026-04-03 09:22:58 +02:00
5255ece1b4 Merge remote-tracking branch 'origin/translate' into front-styles 2026-04-03 08:57:10 +02:00
e82279926f erditor on product 2026-04-03 08:56:13 +02:00
855721b4b1 fix: tabs 2026-04-03 08:51:45 +02:00
9fcecfb209 Merge branch 'front-styles' of ssh://git.ma-al.com:8822/goc_daniel/b2b into translate 2026-04-03 08:24:31 +02:00
ee375b7cbd Merge remote-tracking branch 'origin/translate' into front-styles 2026-04-02 16:15:00 +02:00
f58abda5cd fix: style 2026-04-02 16:07:15 +02:00
c20cdf2b63 fix: editor 2026-04-02 15:55:00 +02:00
2ce2556685 fix: tabs 2026-04-02 15:53:27 +02:00
e961967a49 Merge remote-tracking branch 'refs/remotes/origin/front-styles' into front-styles 2026-04-02 15:28:00 +02:00
Daniel Goc
a4eb3b52cf change incoming HTML to HTML4 2026-04-02 15:24:34 +02:00
7c69d56a48 fix: style 2026-04-02 14:24:00 +02:00
1494af985a Merge branch 'front-styles' of ssh://git.ma-al.com:8822/goc_daniel/b2b into translate 2026-04-02 11:42:43 +02:00
9513feba37 fix: style 2026-04-02 11:42:12 +02:00
551b5ef77d fix: translations 2026-04-02 11:41:44 +02:00
a7f69c854a Merge remote-tracking branch 'refs/remotes/origin/front-styles' into front-styles 2026-04-02 11:32:26 +02:00
Daniel Goc
2bfe97f6fe bugfix 2026-04-02 10:38:19 +02:00
7e8e9897e1 fix: button showProduct 2026-04-02 09:01:26 +02:00
9c8ce43998 Merge branch 'front-styles' of ssh://git.ma-al.com:8822/goc_daniel/b2b into translate 2026-04-02 08:56:55 +02:00
3652a78783 fix: button showProduct 2026-04-02 08:56:31 +02:00
ecf51e92e8 fix: button showProduct 2026-04-02 08:56:05 +02:00
d0c055a819 fix: select for languages 2026-04-02 08:52:29 +02:00
272c6066fb Merge branch 'front-styles' of ssh://git.ma-al.com:8822/goc_daniel/b2b into translate 2026-04-01 16:19:36 +02:00
79730eb826 fix: save product descriptions 2026-04-01 16:19:02 +02:00
6846ffc0dc fix: sidebar 2026-04-01 16:16:03 +02:00
d8f71bd8ff Merge remote-tracking branch 'origin/translate' into front-styles 2026-04-01 13:42:00 +02:00
aa57d38bd6 fix: selects 2026-04-01 13:41:21 +02:00
f9ae1e491e fix: translate 2026-04-01 13:40:56 +02:00
8bf5a1cf8b Merge branch 'front-styles' of ssh://git.ma-al.com:8822/goc_daniel/b2b into translate 2026-04-01 10:53:49 +02:00
b48a143b40 fix: default lang 2026-04-01 10:52:34 +02:00
729b54ca1a fix: currency/county 2026-04-01 10:52:08 +02:00
980fb1543b Merge branch 'main' of ssh://git.ma-al.com:8822/goc_daniel/b2b into translate 2026-04-01 09:36:56 +02:00
b829bf2185 fix: translate 2026-04-01 09:36:01 +02:00
d6066e39ce Merge branch 'front-styles' of ssh://git.ma-al.com:8822/goc_daniel/b2b into translate 2026-03-31 12:49:25 +02:00
12f6249721 fix: requests 2026-03-31 12:22:21 +02:00
109 changed files with 4922 additions and 7379 deletions

View File

@@ -2,27 +2,27 @@ package middleware
import ( import (
"git.ma-al.com/goc_daniel/b2b/app/delivery/middleware/perms" "git.ma-al.com/goc_daniel/b2b/app/delivery/middleware/perms"
"git.ma-al.com/goc_daniel/b2b/app/model" "git.ma-al.com/goc_daniel/b2b/app/utils/localeExtractor"
"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" "github.com/gofiber/fiber/v3"
) )
func Require(p perms.Permission) fiber.Handler { func Require(p perms.Permission) fiber.Handler {
return func(c fiber.Ctx) error { return func(c fiber.Ctx) error {
u := c.Locals("user") user, ok := localeExtractor.GetCustomer(c)
if u == nil {
return c.SendStatus(fiber.StatusUnauthorized)
}
user, ok := u.(*model.UserSession)
if !ok { if !ok {
return c.SendStatus(fiber.StatusInternalServerError) return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrInvalidBody)).
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrInvalidBody)))
} }
for _, perm := range user.Permissions { for _, perm := range user.Role.Permissions {
if perm == p { if perm.Name == p {
return c.Next() return c.Next()
} }
} }
return c.SendStatus(fiber.StatusForbidden) return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrForbidden)).
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrForbidden)))
} }
} }

View File

@@ -3,8 +3,9 @@ package perms
type Permission string type Permission string
const ( const (
UserReadAny Permission = "user.read.any" UserReadAny Permission = "user.read.any"
UserWriteAny Permission = "user.write.any" UserWriteAny Permission = "user.write.any"
UserDeleteAny Permission = "user.delete.any" UserDeleteAny Permission = "user.delete.any"
CurrencyWrite Permission = "currency.write" CurrencyWrite Permission = "currency.write"
SpecificPriceManage Permission = "specific_price.manage"
) )

View File

@@ -4,8 +4,9 @@ import (
"strconv" "strconv"
"git.ma-al.com/goc_daniel/b2b/app/config" "git.ma-al.com/goc_daniel/b2b/app/config"
"git.ma-al.com/goc_daniel/b2b/app/model" "git.ma-al.com/goc_daniel/b2b/app/model/dbmodel"
"git.ma-al.com/goc_daniel/b2b/app/service/productService" "git.ma-al.com/goc_daniel/b2b/app/service/productService"
constdata "git.ma-al.com/goc_daniel/b2b/app/utils/const_data"
"git.ma-al.com/goc_daniel/b2b/app/utils/i18n" "git.ma-al.com/goc_daniel/b2b/app/utils/i18n"
"git.ma-al.com/goc_daniel/b2b/app/utils/localeExtractor" "git.ma-al.com/goc_daniel/b2b/app/utils/localeExtractor"
"git.ma-al.com/goc_daniel/b2b/app/utils/nullable" "git.ma-al.com/goc_daniel/b2b/app/utils/nullable"
@@ -34,6 +35,7 @@ func ProductsHandlerRoutes(r fiber.Router) fiber.Router {
r.Get("/:id/:country_id/:quantity", handler.GetProductJson) r.Get("/:id/:country_id/:quantity", handler.GetProductJson)
r.Get("/list", handler.ListProducts) r.Get("/list", handler.ListProducts)
r.Get("/list-variants/:product_id", handler.ListProductVariants)
r.Post("/favorite/:product_id", handler.AddToFavorites) r.Post("/favorite/:product_id", handler.AddToFavorites)
r.Delete("/favorite/:product_id", handler.RemoveFromFavorites) r.Delete("/favorite/:product_id", handler.RemoveFromFavorites)
@@ -70,7 +72,7 @@ func (h *ProductsHandler) GetProductJson(c fiber.Ctx) error {
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)). return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)).
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute))) JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute)))
} }
productJson, err := h.productService.GetJSON(p_id_product, int(customer.LangID), int(customer.ID), b2b_id_country, p_quantity) productJson, err := h.productService.Get(uint(p_id_product), customer.LangID, customer.ID, uint(b2b_id_country), uint(p_quantity))
if err != nil { if err != nil {
return c.Status(responseErrors.GetErrorStatus(err)). return c.Status(responseErrors.GetErrorStatus(err)).
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
@@ -80,25 +82,19 @@ func (h *ProductsHandler) GetProductJson(c fiber.Ctx) error {
} }
func (h *ProductsHandler) ListProducts(c fiber.Ctx) error { func (h *ProductsHandler) ListProducts(c fiber.Ctx) error {
paging, filters, err := query_params.ParseFilters[model.Product](c, columnMappingListProducts) paging, filters, err := query_params.ParseFilters[dbmodel.PsProduct](c, columnMappingListProducts)
if err != nil { if err != nil {
return c.Status(responseErrors.GetErrorStatus(err)). return c.Status(responseErrors.GetErrorStatus(err)).
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
} }
id_lang, ok := localeExtractor.GetLangID(c) customer, ok := localeExtractor.GetCustomer(c)
if !ok { if !ok || customer == nil {
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)). return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)).
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute))) JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute)))
} }
userID, ok := localeExtractor.GetUserID(c) list, err := h.productService.Find(customer.LangID, customer.ID, paging, filters, customer, constdata.DEFAULT_PRODUCT_QUANTITY, constdata.SHOP_ID)
if !ok {
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrInvalidBody)).
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrInvalidBody)))
}
list, err := h.productService.Find(id_lang, userID, paging, filters)
if err != nil { if err != nil {
return c.Status(responseErrors.GetErrorStatus(err)). return c.Status(responseErrors.GetErrorStatus(err)).
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
@@ -164,3 +160,27 @@ func (h *ProductsHandler) RemoveFromFavorites(c fiber.Ctx) error {
return c.JSON(response.Make(nullable.GetNil(""), 0, i18n.T_(c, response.Message_OK))) return c.JSON(response.Make(nullable.GetNil(""), 0, i18n.T_(c, response.Message_OK)))
} }
func (h *ProductsHandler) ListProductVariants(c fiber.Ctx) error {
productIDStr := c.Params("product_id")
productID, err := strconv.Atoi(productIDStr)
if err != nil {
return c.Status(responseErrors.GetErrorStatus(err)).
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
}
customer, ok := localeExtractor.GetCustomer(c)
if !ok || customer == nil {
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)).
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute)))
}
list, err := h.productService.GetProductAttributes(customer.LangID, uint(productID), constdata.SHOP_ID, customer.ID, customer.CountryID, constdata.DEFAULT_PRODUCT_QUANTITY)
if err != nil {
return c.Status(responseErrors.GetErrorStatus(err)).
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
}
return c.JSON(response.Make(&list, len(list), i18n.T_(c, response.Message_OK)))
}

View File

@@ -0,0 +1,159 @@
package restricted
import (
"strconv"
"git.ma-al.com/goc_daniel/b2b/app/config"
"git.ma-al.com/goc_daniel/b2b/app/delivery/middleware"
"git.ma-al.com/goc_daniel/b2b/app/model"
"git.ma-al.com/goc_daniel/b2b/app/service/specificPriceService"
"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 SpecificPriceHandler struct {
SpecificPriceService *specificPriceService.SpecificPriceService
config *config.Config
}
func NewSpecificPriceHandler() *SpecificPriceHandler {
SpecificPriceService := specificPriceService.New()
return &SpecificPriceHandler{
SpecificPriceService: SpecificPriceService,
config: config.Get(),
}
}
func SpecificPriceHandlerRoutes(r fiber.Router) fiber.Router {
handler := NewSpecificPriceHandler()
r.Post("/", middleware.Require("specific_price.manage"), handler.Create)
r.Put("/:id", middleware.Require("specific_price.manage"), handler.Update)
r.Delete("/:id", middleware.Require("specific_price.manage"), handler.Delete)
r.Get("/", middleware.Require("specific_price.manage"), handler.List)
r.Get("/:id", middleware.Require("specific_price.manage"), handler.GetByID)
r.Patch("/:id/activate", middleware.Require("specific_price.manage"), handler.Activate)
r.Patch("/:id/deactivate", middleware.Require("specific_price.manage"), handler.Deactivate)
return r
}
func (h *SpecificPriceHandler) Create(c fiber.Ctx) error {
var pr model.SpecificPrice
if err := c.Bind().Body(&pr); err != nil {
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrInvalidBody)).
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrInvalidBody)))
}
result, err := h.SpecificPriceService.Create(c.Context(), &pr)
if err != nil {
return c.Status(responseErrors.GetErrorStatus(err)).
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
}
return c.JSON(response.Make(&result, 1, i18n.T_(c, response.Message_OK)))
}
func (h *SpecificPriceHandler) Update(c fiber.Ctx) error {
idStr := c.Params("id")
id, err := strconv.ParseUint(idStr, 10, 64)
if err != nil {
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)).
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute)))
}
var pr model.SpecificPrice
if err := c.Bind().Body(&pr); err != nil {
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrInvalidBody)).
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrInvalidBody)))
}
result, err := h.SpecificPriceService.Update(c.Context(), id, &pr)
if err != nil {
return c.Status(responseErrors.GetErrorStatus(err)).
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
}
return c.JSON(response.Make(&result, 1, i18n.T_(c, response.Message_OK)))
}
func (h *SpecificPriceHandler) List(c fiber.Ctx) error {
result, err := h.SpecificPriceService.List(c.Context())
if err != nil {
return c.Status(responseErrors.GetErrorStatus(err)).
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
}
return c.JSON(response.Make(&result, 1, i18n.T_(c, response.Message_OK)))
}
func (h *SpecificPriceHandler) GetByID(c fiber.Ctx) error {
idStr := c.Params("id")
id, err := strconv.ParseUint(idStr, 10, 64)
if err != nil {
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)).
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute)))
}
result, err := h.SpecificPriceService.GetByID(c.Context(), id)
if err != nil {
return c.Status(responseErrors.GetErrorStatus(err)).
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
}
return c.JSON(response.Make(&result, 1, i18n.T_(c, response.Message_OK)))
}
func (h *SpecificPriceHandler) Activate(c fiber.Ctx) error {
idStr := c.Params("id")
id, err := strconv.ParseUint(idStr, 10, 64)
if err != nil {
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)).
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute)))
}
err = h.SpecificPriceService.SetActive(c.Context(), id, true)
if err != nil {
return c.Status(responseErrors.GetErrorStatus(err)).
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
}
return c.JSON(response.Make(nullable.GetNil(""), 0, i18n.T_(c, response.Message_OK)))
}
func (h *SpecificPriceHandler) Deactivate(c fiber.Ctx) error {
idStr := c.Params("id")
id, err := strconv.ParseUint(idStr, 10, 64)
if err != nil {
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)).
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute)))
}
err = h.SpecificPriceService.SetActive(c.Context(), id, false)
if err != nil {
return c.Status(responseErrors.GetErrorStatus(err)).
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
}
return c.JSON(response.Make(nullable.GetNil(""), 0, i18n.T_(c, response.Message_OK)))
}
func (h *SpecificPriceHandler) Delete(c fiber.Ctx) error {
idStr := c.Params("id")
id, err := strconv.ParseUint(idStr, 10, 64)
if err != nil {
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)).
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute)))
}
err = h.SpecificPriceService.Delete(c.Context(), id)
if err != nil {
return c.Status(responseErrors.GetErrorStatus(err)).
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
}
return c.JSON(response.Make(nullable.GetNil(""), 0, i18n.T_(c, response.Message_OK)))
}

View File

@@ -132,6 +132,8 @@ func (s *Server) Setup() error {
carts := s.restricted.Group("/carts") carts := s.restricted.Group("/carts")
restricted.CartsHandlerRoutes(carts) restricted.CartsHandlerRoutes(carts)
specificPrice := s.restricted.Group("/specific-price")
restricted.SpecificPriceHandlerRoutes(specificPrice)
// addresses (restricted) // addresses (restricted)
addresses := s.restricted.Group("/addresses") addresses := s.restricted.Group("/addresses")
restricted.AddressesHandlerRoutes(addresses) restricted.AddressesHandlerRoutes(addresses)

View File

@@ -1,15 +1,13 @@
package model package model
import "git.ma-al.com/goc_daniel/b2b/app/model/dbmodel"
// Represents a country together with its associated currency // Represents a country together with its associated currency
type Country struct { type Country struct {
ID uint `gorm:"primaryKey;column:id" json:"id"` ID uint `gorm:"primaryKey;column:id" json:"id"`
Name string `gorm:"column:name" json:"name"` Name string `gorm:"column:name" json:"name"`
Flag string `gorm:"size:16;not null;column:flag" json:"flag"` Flag string `gorm:"size:16;not null;column:flag" json:"flag"`
PSCurrencyID uint `gorm:"column:currency_id" json:"currency_id"` CurrencyID uint `gorm:"column:b2b_id_currency" json:"currency_id"`
PSCurrency *dbmodel.PsCurrency `gorm:"foreignKey:PSCurrencyID;references:IDCurrency" json:"ps_currency"` Currency *Currency `gorm:"foreignKey:CurrencyID" json:"currency,omitempty"`
} }
func (Country) TableName() string { func (Country) TableName() string {

View File

@@ -31,6 +31,7 @@ type Customer struct {
LastLoginAt *time.Time `json:"last_login_at,omitempty"` LastLoginAt *time.Time `json:"last_login_at,omitempty"`
LangID uint `gorm:"default:2" json:"lang_id"` // User's preferred language LangID uint `gorm:"default:2" json:"lang_id"` // User's preferred language
CountryID uint `gorm:"default:2" json:"country_id"` // User's selected country CountryID uint `gorm:"default:2" json:"country_id"` // User's selected country
Country *Country `gorm:"foreignKey:CountryID" json:"country,omitempty"`
CreatedAt time.Time `json:"created_at"` CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"` UpdatedAt time.Time `json:"updated_at"`
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"` DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`

View File

@@ -1,76 +1,17 @@
package model package model
// Product contains each and every column from the table ps_product.
type Product struct {
ProductID uint `gorm:"column:id_product;primaryKey" json:"product_id" form:"product_id"`
SupplierID uint `gorm:"column:id_supplier" json:"supplier_id" form:"supplier_id"`
ManufacturerID uint `gorm:"column:id_manufacturer" json:"manufacturer_id" form:"manufacturer_id"`
CategoryDefaultID uint `gorm:"column:id_category_default" json:"category_default_id" form:"category_default_id"`
ShopDefaultID uint `gorm:"column:id_shop_default" json:"shop_default_id" form:"shop_default_id"`
TaxRulesGroupID uint `gorm:"column:id_tax_rules_group" json:"tax_rules_group_id" form:"tax_rules_group_id"`
OnSale uint `gorm:"column:on_sale" json:"on_sale" form:"on_sale"`
OnlineOnly uint `gorm:"column:online_only" json:"online_only" form:"online_only"`
EAN13 string `gorm:"column:ean13;type:varchar(13)" json:"ean13" form:"ean13"`
ISBN string `gorm:"column:isbn;type:varchar(32)" json:"isbn" form:"isbn"`
UPC string `gorm:"column:upc;type:varchar(12)" json:"upc" form:"upc"`
EkoTax float32 `gorm:"column:eko_tax;type:decimal(20,6)" json:"eko_tax" form:"eko_tax"`
Quantity uint `gorm:"column:quantity" json:"quantity" form:"quantity"`
MinimalQuantity uint `gorm:"column:minimal_quantity" json:"minimal_quantity" form:"minimal_quantity"`
LowStockThreshold uint `gorm:"column:low_stock_threshold" json:"low_stock_threshold" form:"low_stock_threshold"`
LowStockAlert uint `gorm:"column:low_stock_alert" json:"low_stock_alert" form:"low_stock_alert"`
Price float32 `gorm:"column:price;type:decimal(20,6)" json:"price" form:"price"`
WholesalePrice float32 `gorm:"column:wholesale_price;type:decimal(20,6)" json:"wholesale_price" form:"wholesale_price"`
Unity string `gorm:"column:unity;type:varchar(255)" json:"unity" form:"unity"`
UnitPriceRatio float32 `gorm:"column:unit_price_ratio;type:decimal(20,6)" json:"unit_price_ratio" form:"unit_price_ratio"`
UnitID uint `gorm:"column:id_unit;primaryKey" json:"unit_id" form:"unit_id"`
AdditionalShippingCost float32 `gorm:"column:additional_shipping_cost;type:decimal(20,2)" json:"additional_shipping_cost" form:"additional_shipping_cost"`
Reference string `gorm:"column:reference;type:varchar(64)" json:"reference" form:"reference"`
SupplierReference string `gorm:"column:supplier_reference;type:varchar(64)" json:"supplier_reference" form:"supplier_reference"`
Location string `gorm:"column:location;type:varchar(64)" json:"location" form:"location"`
Width float32 `gorm:"column:width;type:decimal(20,6)" json:"width" form:"width"`
Height float32 `gorm:"column:height;type:decimal(20,6)" json:"height" form:"height"`
Depth float32 `gorm:"column:depth;type:decimal(20,6)" json:"depth" form:"depth"`
Weight float32 `gorm:"column:weight;type:decimal(20,6)" json:"weight" form:"weight"`
OutOfStock uint `gorm:"column:out_of_stock" json:"out_of_stock" form:"out_of_stock"`
AdditionalDeliveryTimes uint `gorm:"column:additional_delivery_times" json:"additional_delivery_times" form:"additional_delivery_times"`
QuantityDiscount uint `gorm:"column:quantity_discount" json:"quantity_discount" form:"quantity_discount"`
Customizable uint `gorm:"column:customizable" json:"customizable" form:"customizable"`
UploadableFiles uint `gorm:"column:uploadable_files" json:"uploadable_files" form:"uploadable_files"`
TextFields uint `gorm:"column:text_fields" json:"text_fields" form:"text_fields"`
Active uint `gorm:"column:active" json:"active" form:"active"`
RedirectType string `gorm:"column:redirect_type;type:enum('','404','301-product','302-product','301-category','302-category')" json:"redirect_type" form:"redirect_type"`
TypeRedirectedID int `gorm:"column:id_type_redirected" json:"type_redirected_id" form:"type_redirected_id"`
AvailableForOrder uint `gorm:"column:available_for_order" json:"available_for_order" form:"available_for_order"`
AvailableDate string `gorm:"column:available_date;type:date" json:"available_date" form:"available_date"`
ShowCondition uint `gorm:"column:show_condition" json:"show_condition" form:"show_condition"`
Condition string `gorm:"column:condition;type:enum('new','used','refurbished')" json:"condition" form:"condition"`
ShowPrice uint `gorm:"column:show_price" json:"show_price" form:"show_price"`
Indexed uint `gorm:"column:indexed" json:"indexed" form:"indexed"`
Visibility string `gorm:"column:visibility;type:enum('both','catalog','search','none')" json:"visibility" form:"visibility"`
CacheIsPack uint `gorm:"column:cache_is_pack" json:"cache_is_pack" form:"cache_is_pack"`
CacheHasAttachments uint `gorm:"column:cache_has_attachments" json:"cache_has_attachments" form:"cache_has_attachments"`
IsVirtual uint `gorm:"column:is_virtual" json:"is_virtual" form:"is_virtual"`
CacheDefaultAttribute uint `gorm:"column:cache_default_attribute" json:"cache_default_attribute" form:"cache_default_attribute"`
DateAdd string `gorm:"column:date_add;type:datetime" json:"date_add" form:"date_add"`
DateUpd string `gorm:"column:date_upd;type:datetime" json:"date_upd" form:"date_upd"`
AdvancedStockManagement uint `gorm:"column:advanced_stock_management" json:"advanced_stock_management" form:"advanced_stock_management"`
PackStockType uint `gorm:"column:pack_stock_type" json:"pack_stock_type" form:"pack_stock_type"`
State uint `gorm:"column:state" json:"state" form:"state"`
DeliveryDays uint `gorm:"column:delivery_days" json:"delivery_days" form:"delivery_days"`
}
type ProductInList struct { type ProductInList struct {
ProductID uint `gorm:"column:product_id" json:"product_id" form:"product_id"` ProductID uint `gorm:"column:product_id" json:"product_id" form:"product_id"`
Name string `gorm:"column:name" json:"name" form:"name"` Name string `gorm:"column:name" json:"name" form:"name"`
LinkRewrite string `gorm:"column:link_rewrite" json:"link_rewrite"` LinkRewrite string `gorm:"column:link_rewrite" json:"link_rewrite"`
ImageLink string `gorm:"column:image_link" json:"image_link"` ImageLink string `gorm:"column:image_link" json:"image_link"`
CategoryName string `gorm:"column:category_name" json:"category_name" form:"category_name"` CategoryName string `gorm:"column:category_name" json:"category_name" form:"category_name"`
Reference string `gorm:"column:reference" json:"reference"` Reference string `gorm:"column:reference" json:"reference"`
VariantsNumber uint `gorm:"column:variants_number" json:"variants_number"` VariantsNumber uint `gorm:"column:variants_number" json:"variants_number"`
Quantity int64 `gorm:"column:quantity" json:"quantity"` Quantity int64 `gorm:"column:quantity" json:"quantity"`
IsFavorite bool `gorm:"column:is_favorite" json:"is_favorite"` PriceTaxExcl float64 `gorm:"column:price_tax_excl" json:"price_tax_excl"`
PriceTaxIncl float64 `gorm:"column:price_tax_incl" json:"price_tax_incl"`
IsFavorite bool `gorm:"column:is_favorite" json:"is_favorite"`
} }
type ProductFilters struct { type ProductFilters struct {

View File

@@ -0,0 +1,29 @@
package model
import "time"
type SpecificPrice struct {
ID uint64 `gorm:"primaryKey;autoIncrement" json:"id"`
Name string `gorm:"type:varchar(255);not null" json:"name"`
ValidFrom *time.Time `gorm:"null" json:"valid_from"`
ValidTill *time.Time `gorm:"null" json:"valid_till"`
HasExpirationDate bool `gorm:"default:false" json:"has_expiration_date"`
ReductionType string `gorm:"type:enum('amount','percentage');not null" json:"reduction_type"`
Price *float64 `gorm:"type:decimal(10,2);null" json:"price"`
CurrencyID *uint64 `gorm:"column:b2b_id_currency;null" json:"currency_id"`
PercentageReduction *float64 `gorm:"type:decimal(5,2);null" json:"percentage_reduction"`
FromQuantity uint32 `gorm:"default:1" json:"from_quantity"`
IsActive bool `gorm:"default:true" json:"is_active"`
CreatedAt *time.Time `gorm:"null" json:"created_at"`
UpdatedAt *time.Time `gorm:"null" json:"updated_at"`
ProductIDs []uint64 `gorm:"-" json:"product_ids"`
CategoryIDs []uint64 `gorm:"-" json:"category_ids"`
ProductAttributeIDs []uint64 `gorm:"-" json:"product_attribute_ids"`
CountryIDs []uint64 `gorm:"-" json:"country_ids"`
CustomerIDs []uint64 `gorm:"-" json:"customer_ids"`
}
func (SpecificPrice) TableName() string {
return "b2b_specific_price"
}

View File

@@ -2,7 +2,6 @@ package productsRepo
import ( import (
"encoding/json" "encoding/json"
"fmt"
"git.ma-al.com/goc_daniel/b2b/app/config" "git.ma-al.com/goc_daniel/b2b/app/config"
"git.ma-al.com/goc_daniel/b2b/app/db" "git.ma-al.com/goc_daniel/b2b/app/db"
@@ -10,13 +9,18 @@ import (
"git.ma-al.com/goc_daniel/b2b/app/model/dbmodel" "git.ma-al.com/goc_daniel/b2b/app/model/dbmodel"
"git.ma-al.com/goc_daniel/b2b/app/utils/query/filters" "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/app/utils/query/find"
"git.ma-al.com/goc_daniel/b2b/app/view"
"git.ma-al.com/goc_marek/gormcol" "git.ma-al.com/goc_marek/gormcol"
"github.com/WinterYukky/gorm-extra-clause-plugin/exclause" "github.com/WinterYukky/gorm-extra-clause-plugin/exclause"
) )
type UIProductsRepo interface { type UIProductsRepo interface {
GetJSON(p_id_product, p_id_shop, p_id_lang, p_id_customer, b2b_id_country, p_quantity int) (*json.RawMessage, error) // GetJSON(p_id_product, p_id_shop, p_id_lang, p_id_customer, b2b_id_country, p_quantity int) (*json.RawMessage, error)
Find(id_lang, userID uint, p find.Paging, filt *filters.FiltersList) (find.Found[model.ProductInList], error) Find(id_lang uint, userID uint, p find.Paging, filt *filters.FiltersList) (*find.Found[model.ProductInList], error)
GetProductVariants(langID uint, productID uint, shopID uint, customerID uint, countryID uint, quantity uint) ([]view.ProductAttribute, error)
GetBase(p_id_product, p_id_shop, p_id_lang uint) (view.Product, error)
GetPrice(p_id_product uint, productAttributeID *uint, p_id_shop uint, p_id_customer uint, p_id_country uint, p_quantity uint) (view.Price, error)
GetVariants(p_id_product, p_id_shop, p_id_lang, p_id_customer, p_id_country, p_quantity uint) ([]view.ProductAttribute, error)
AddToFavorites(userID uint, productID uint) error AddToFavorites(userID uint, productID uint) error
RemoveFromFavorites(userID uint, productID uint) error RemoveFromFavorites(userID uint, productID uint) error
ExistsInFavorites(userID uint, productID uint) (bool, error) ExistsInFavorites(userID uint, productID uint) (bool, error)
@@ -29,30 +33,78 @@ func New() UIProductsRepo {
return &ProductsRepo{} return &ProductsRepo{}
} }
func (repo *ProductsRepo) GetJSON(p_id_product, p_id_shop, p_id_lang, p_id_customer, b2b_id_country, p_quantity int) (*json.RawMessage, error) { func (repo *ProductsRepo) GetBase(p_id_product, p_id_shop, p_id_lang uint) (view.Product, error) {
var productStr string // ← Scan as string first var result view.Product
err := db.DB.Raw(`CALL get_full_product(?,?,?,?,?,?)`, err := db.DB.Raw(`CALL get_product_base(?,?,?)`,
p_id_product, p_id_shop, p_id_lang, p_id_customer, b2b_id_country, p_quantity). p_id_product, p_id_shop, p_id_lang).
Scan(&productStr). Scan(&result).Error
Error
if err != nil { return result, err
return nil, err
}
if !json.Valid([]byte(productStr)) {
return nil, fmt.Errorf("invalid json returned from stored procedure")
}
raw := json.RawMessage(productStr)
return &raw, nil
} }
func (repo *ProductsRepo) Find(id_lang, userID uint, p find.Paging, filt *filters.FiltersList) (find.Found[model.ProductInList], error) { func (repo *ProductsRepo) GetPrice(
var list []model.ProductInList p_id_product uint, productAttributeID *uint, p_id_shop uint, p_id_customer uint, p_id_country uint, p_quantity uint,
var total int64 ) (view.Price, error) {
type row struct {
Price json.RawMessage `gorm:"column:price"`
}
var r row
err := db.DB.Raw(`
SELECT fn_product_price(?,?,?,?,?,?) AS price`,
p_id_product, p_id_shop, p_id_customer, p_id_country, p_quantity, productAttributeID).
Scan(&r).Error
if err != nil {
return view.Price{}, err
}
var temp struct {
Base json.Number `json:"base"`
FinalTaxExcl json.Number `json:"final_tax_excl"`
FinalTaxIncl json.Number `json:"final_tax_incl"`
TaxRate json.Number `json:"tax_rate"`
Priority json.Number `json:"priority"`
}
if err := json.Unmarshal(r.Price, &temp); err != nil {
return view.Price{}, err
}
price := view.Price{
Base: mustParseFloat(temp.Base),
FinalTaxExcl: mustParseFloat(temp.FinalTaxExcl),
FinalTaxIncl: mustParseFloat(temp.FinalTaxIncl),
TaxRate: mustParseFloat(temp.TaxRate),
Priority: mustParseInt(temp.Priority),
}
return price, nil
}
func mustParseFloat(n json.Number) float64 {
f, _ := n.Float64()
return f
}
func mustParseInt(n json.Number) int {
i, _ := n.Int64()
return int(i)
}
func (repo *ProductsRepo) GetVariants(p_id_product, p_id_shop, p_id_lang, p_id_customer, p_id_country, p_quantity uint) ([]view.ProductAttribute, error) {
var results []view.ProductAttribute
err := db.DB.Raw(`
CALL get_product_variants(?,?,?,?,?,?)`,
p_id_product, p_id_shop, p_id_lang, p_id_customer, p_id_country, p_quantity).
Scan(&results).Error
return results, err
}
func (repo *ProductsRepo) Find(langID uint, userID uint, p find.Paging, filt *filters.FiltersList) (*find.Found[model.ProductInList], error) {
query := db.Get(). query := db.Get().
Table(gormcol.Field.Tab(dbmodel.PsProductShopCols.Active)+" AS ps"). Table(gormcol.Field.Tab(dbmodel.PsProductShopCols.Active)+" AS ps").
Select(` Select(`
@@ -67,9 +119,9 @@ func (repo *ProductsRepo) Find(id_lang, userID uint, p find.Paging, filt *filter
COALESCE(f.is_favorite, 0) AS is_favorite COALESCE(f.is_favorite, 0) AS is_favorite
`, config.Get().Image.ImagePrefix). `, config.Get().Image.ImagePrefix).
Joins("JOIN "+dbmodel.PsProductCols.IDProduct.Tab()+" p ON p.id_product = ps.id_product"). Joins("JOIN "+dbmodel.PsProductCols.IDProduct.Tab()+" p ON p.id_product = ps.id_product").
Joins("JOIN ps_product_lang pl ON pl.id_product = ps.id_product AND pl.id_lang = ?", id_lang). Joins("JOIN ps_product_lang pl ON pl.id_product = ps.id_product AND pl.id_lang = ?", langID).
Joins("JOIN ps_image_shop ims ON ims.id_product = ps.id_product AND ims.cover = 1"). Joins("JOIN ps_image_shop ims ON ims.id_product = ps.id_product AND ims.cover = 1").
Joins("JOIN ps_category_lang cl ON cl.id_category = ps.id_category_default AND cl.id_lang = ?", id_lang). Joins("JOIN ps_category_lang cl ON cl.id_category = ps.id_category_default AND cl.id_lang = ?", langID).
Joins("JOIN ps_category_product cp ON cp.id_product = ps.id_product"). Joins("JOIN ps_category_product cp ON cp.id_product = ps.id_product").
Joins("LEFT JOIN variants v ON v.id_product = ps.id_product"). Joins("LEFT JOIN variants v ON v.id_product = ps.id_product").
Joins("LEFT JOIN favorites f ON f.id_product = ps.id_product"). Joins("LEFT JOIN favorites f ON f.id_product = ps.id_product").
@@ -105,29 +157,64 @@ func (repo *ProductsRepo) Find(id_lang, userID uint, p find.Paging, filt *filter
}). }).
Order("ps.id_product DESC") Order("ps.id_product DESC")
// Apply all filters query = query.Scopes(filt.All()...)
if filt != nil {
filt.ApplyAll(query)
}
// run counter first as query is without limit and offset list, err := find.Paginate[model.ProductInList](langID, p, query)
err := query.Count(&total).Error
if err != nil { if err != nil {
return find.Found[model.ProductInList]{}, err return nil, err
} }
return &list, nil
}
func (repo *ProductsRepo) GetProductVariants(langID uint, productID uint, shopID uint, customerID uint, countryID uint, quantity uint) ([]view.ProductAttribute, error) {
var result []view.ProductAttribute
err := db.DB.
Raw(`
CALL get_product_attributes_with_price(?, ?, ?, ?, ?, ?)
`,
langID,
productID,
shopID,
customerID,
countryID,
quantity,
).
Scan(&result).Error
err = query.
Limit(p.Limit()).
Offset(p.Offset()).
Find(&list).Error
if err != nil { if err != nil {
return find.Found[model.ProductInList]{}, err return nil, err
} }
return find.Found[model.ProductInList]{ return result, nil
Items: list, }
Count: uint(total),
}, nil func (repo *ProductsRepo) PopulateProductPrice(product *model.ProductInList, targetCustomer *model.Customer, quantity int, shopID uint) error {
row := db.Get().Raw(
"CALL get_product_price(?, ?, ?, ?, ?)",
product.ProductID,
shopID,
targetCustomer.ID,
targetCustomer.CountryID,
quantity,
).Row()
var (
id uint
base float64
excl float64
incl float64
tax float64
)
err := row.Scan(&id, &base, &excl, &incl, &tax)
if err != nil {
return err
}
product.PriceTaxExcl = excl
product.PriceTaxIncl = incl
return nil
} }
func (repo *ProductsRepo) AddToFavorites(userID uint, productID uint) error { func (repo *ProductsRepo) AddToFavorites(userID uint, productID uint) error {

View File

@@ -0,0 +1,247 @@
package specificPriceRepo
import (
"context"
"errors"
"time"
"git.ma-al.com/goc_daniel/b2b/app/db"
"git.ma-al.com/goc_daniel/b2b/app/model"
"gorm.io/gorm"
)
type UISpecificPriceRepo interface {
Create(ctx context.Context, pr *model.SpecificPrice) error
Update(ctx context.Context, pr *model.SpecificPrice) error
Delete(ctx context.Context, id uint64) error
GetByID(ctx context.Context, id uint64) (*model.SpecificPrice, error)
List(ctx context.Context) ([]*model.SpecificPrice, error)
SetActive(ctx context.Context, id uint64, active bool) error
}
type SpecificPriceRepo struct{}
func New() UISpecificPriceRepo {
return &SpecificPriceRepo{}
}
func (repo *SpecificPriceRepo) Create(ctx context.Context, pr *model.SpecificPrice) error {
return db.DB.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
now := time.Now()
pr.CreatedAt = &now
if err := tx.Create(pr).Error; err != nil {
return err
}
return repo.insertRelations(tx, pr)
})
}
func (repo *SpecificPriceRepo) Update(ctx context.Context, pr *model.SpecificPrice) error {
return db.DB.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
now := time.Now()
pr.UpdatedAt = &now
if err := tx.Save(pr).Error; err != nil {
return err
}
if err := repo.clearRelations(tx, pr.ID); err != nil {
return err
}
return repo.insertRelations(tx, pr)
})
}
func (repo *SpecificPriceRepo) GetByID(ctx context.Context, id uint64) (*model.SpecificPrice, error) {
var pr model.SpecificPrice
err := db.DB.WithContext(ctx).Where("id = ?", id).First(&pr).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, nil
}
return nil, err
}
if err := repo.loadRelations(ctx, &pr); err != nil {
return nil, err
}
return &pr, nil
}
func (repo *SpecificPriceRepo) List(ctx context.Context) ([]*model.SpecificPrice, error) {
var specificPrices []*model.SpecificPrice
err := db.DB.WithContext(ctx).Find(&specificPrices).Error
if err != nil {
return nil, err
}
for i := range specificPrices {
if err := repo.loadRelations(ctx, specificPrices[i]); err != nil {
return nil, err
}
}
return specificPrices, nil
}
func (repo *SpecificPriceRepo) SetActive(ctx context.Context, id uint64, active bool) error {
return db.DB.WithContext(ctx).Model(&model.SpecificPrice{}).Where("id = ?", id).Update("is_active", active).Error
}
func (repo *SpecificPriceRepo) Delete(ctx context.Context, id uint64) error {
return db.DB.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
if err := tx.Exec("DELETE FROM b2b_specific_price_product WHERE b2b_specific_price_id = ?", id).Error; err != nil {
return err
}
if err := tx.Exec("DELETE FROM b2b_specific_price_category WHERE b2b_specific_price_id = ?", id).Error; err != nil {
return err
}
if err := tx.Exec("DELETE FROM b2b_specific_price_product_attribute WHERE b2b_specific_price_id = ?", id).Error; err != nil {
return err
}
if err := tx.Exec("DELETE FROM b2b_specific_price_country WHERE b2b_specific_price_id = ?", id).Error; err != nil {
return err
}
if err := tx.Exec("DELETE FROM b2b_specific_price_customer WHERE b2b_specific_price_id = ?", id).Error; err != nil {
return err
}
if err := tx.Delete(&model.SpecificPrice{}, "id = ?", id).Error; err != nil {
return err
}
return nil
})
}
func (repo *SpecificPriceRepo) insertRelations(tx *gorm.DB, pr *model.SpecificPrice) error {
if len(pr.ProductIDs) > 0 {
for _, productID := range pr.ProductIDs {
if err := tx.Exec(`
INSERT INTO b2b_specific_price_product (b2b_specific_price_id, id_product) VALUES (?, ?)
`, pr.ID, productID).Error; err != nil {
return err
}
}
}
if len(pr.CategoryIDs) > 0 {
for _, categoryID := range pr.CategoryIDs {
if err := tx.Exec(`
INSERT INTO b2b_specific_price_category (b2b_specific_price_id, id_category) VALUES (?, ?)
`, pr.ID, categoryID).Error; err != nil {
return err
}
}
}
if len(pr.ProductAttributeIDs) > 0 {
for _, attrID := range pr.ProductAttributeIDs {
if err := tx.Exec(`
INSERT INTO b2b_specific_price_product_attribute (b2b_specific_price_id, id_product_attribute) VALUES (?, ?)
`, pr.ID, attrID).Error; err != nil {
return err
}
}
}
if len(pr.CountryIDs) > 0 {
for _, countryID := range pr.CountryIDs {
if err := tx.Exec(`
INSERT INTO b2b_specific_price_country (b2b_specific_price_id, b2b_id_country) VALUES (?, ?)
`, pr.ID, countryID).Error; err != nil {
return err
}
}
}
if len(pr.CustomerIDs) > 0 {
for _, customerID := range pr.CustomerIDs {
if err := tx.Exec(`
INSERT INTO b2b_specific_price_customer (b2b_specific_price_id, b2b_id_customer) VALUES (?, ?)
`, pr.ID, customerID).Error; err != nil {
return err
}
}
}
return nil
}
func (repo *SpecificPriceRepo) clearRelations(tx *gorm.DB, id uint64) error {
if err := tx.Exec("DELETE FROM b2b_specific_price_product WHERE b2b_specific_price_id = ?", id).Error; err != nil {
return err
}
if err := tx.Exec("DELETE FROM b2b_specific_price_category WHERE b2b_specific_price_id = ?", id).Error; err != nil {
return err
}
if err := tx.Exec("DELETE FROM b2b_specific_price_product_attribute WHERE b2b_specific_price_id = ?", id).Error; err != nil {
return err
}
if err := tx.Exec("DELETE FROM b2b_specific_price_country WHERE b2b_specific_price_id = ?", id).Error; err != nil {
return err
}
if err := tx.Exec("DELETE FROM b2b_specific_price_customer WHERE b2b_specific_price_id = ?", id).Error; err != nil {
return err
}
return nil
}
func (repo *SpecificPriceRepo) loadRelations(ctx context.Context, pr *model.SpecificPrice) error {
var err error
var productIDs []struct {
IDProduct uint64 `gorm:"column:id_product"`
}
if err = db.DB.WithContext(ctx).Table("b2b_specific_price_product").Where("b2b_specific_price_id = ?", pr.ID).Select("id_product").Scan(&productIDs).Error; err != nil {
return err
}
for _, p := range productIDs {
pr.ProductIDs = append(pr.ProductIDs, p.IDProduct)
}
var categoryIDs []struct {
IDCategory uint64 `gorm:"column:id_category"`
}
if err = db.DB.WithContext(ctx).Table("b2b_specific_price_category").Where("b2b_specific_price_id = ?", pr.ID).Select("id_category").Scan(&categoryIDs).Error; err != nil {
return err
}
for _, c := range categoryIDs {
pr.CategoryIDs = append(pr.CategoryIDs, c.IDCategory)
}
var attrIDs []struct {
IDAttr uint64 `gorm:"column:id_product_attribute"`
}
if err = db.DB.WithContext(ctx).Table("b2b_specific_price_product_attribute").Where("b2b_specific_price_id = ?", pr.ID).Select("id_product_attribute").Scan(&attrIDs).Error; err != nil {
return err
}
for _, a := range attrIDs {
pr.ProductAttributeIDs = append(pr.ProductAttributeIDs, a.IDAttr)
}
var countryIDs []struct {
IDCountry uint64 `gorm:"column:b2b_id_country"`
}
if err = db.DB.WithContext(ctx).Table("b2b_specific_price_country").Where("b2b_specific_price_id = ?", pr.ID).Select("b2b_id_country").Scan(&countryIDs).Error; err != nil {
return err
}
for _, c := range countryIDs {
pr.CountryIDs = append(pr.CountryIDs, c.IDCountry)
}
var customerIDs []struct {
IDCustomer uint64 `gorm:"column:b2b_id_customer"`
}
if err = db.DB.WithContext(ctx).Table("b2b_specific_price_customer").Where("b2b_specific_price_id = ?", pr.ID).Select("b2b_id_customer").Scan(&customerIDs).Error; err != nil {
return err
}
for _, c := range customerIDs {
pr.CustomerIDs = append(pr.CustomerIDs, c.IDCustomer)
}
return nil
}

View File

@@ -21,6 +21,7 @@ import (
"github.com/golang-jwt/jwt/v5" "github.com/golang-jwt/jwt/v5"
"golang.org/x/crypto/bcrypt" "golang.org/x/crypto/bcrypt"
"gorm.io/gorm" "gorm.io/gorm"
"gorm.io/gorm/clause"
) )
// JWTClaims represents the JWT claims // JWTClaims represents the JWT claims
@@ -436,7 +437,7 @@ func (s *AuthService) RevokeAllRefreshTokens(userID uint) {
// GetUserByID retrieves a user by ID // GetUserByID retrieves a user by ID
func (s *AuthService) GetUserByID(userID uint) (*model.Customer, error) { func (s *AuthService) GetUserByID(userID uint) (*model.Customer, error) {
var user model.Customer var user model.Customer
if err := s.db.Preload("Role.Permissions").First(&user, userID).Error; err != nil { if err := s.db.Preload("Role.Permissions").Preload(clause.Associations).First(&user, userID).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) { if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, responseErrors.ErrUserNotFound return nil, responseErrors.ErrUserNotFound
} }

View File

@@ -2,6 +2,7 @@ package productService
import ( import (
"encoding/json" "encoding/json"
"errors"
"git.ma-al.com/goc_daniel/b2b/app/model" "git.ma-al.com/goc_daniel/b2b/app/model"
"git.ma-al.com/goc_daniel/b2b/app/repos/productsRepo" "git.ma-al.com/goc_daniel/b2b/app/repos/productsRepo"
@@ -9,6 +10,7 @@ import (
"git.ma-al.com/goc_daniel/b2b/app/utils/query/filters" "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/app/utils/query/find"
"git.ma-al.com/goc_daniel/b2b/app/utils/responseErrors" "git.ma-al.com/goc_daniel/b2b/app/utils/responseErrors"
"git.ma-al.com/goc_daniel/b2b/app/view"
) )
type ProductService struct { type ProductService struct {
@@ -21,17 +23,108 @@ func New() *ProductService {
} }
} }
func (s *ProductService) GetJSON(p_id_product, p_id_lang, p_id_customer, b2b_id_country, p_quantity int) (*json.RawMessage, error) { func (s *ProductService) Get(
products, err := s.productsRepo.GetJSON(p_id_product, constdata.SHOP_ID, p_id_lang, p_id_customer, b2b_id_country, p_quantity) p_id_product, p_id_lang, p_id_customer, b2b_id_country, p_quantity uint,
) (*json.RawMessage, error) {
product, err := s.productsRepo.GetBase(p_id_product, constdata.SHOP_ID, p_id_lang)
if err != nil { if err != nil {
return products, err return nil, err
} }
return products, nil price, err := s.productsRepo.GetPrice(p_id_product, nil, constdata.SHOP_ID, p_id_customer, b2b_id_country, p_quantity)
if err != nil {
return nil, err
}
variants, err := s.productsRepo.GetVariants(p_id_product, constdata.SHOP_ID, p_id_lang, p_id_customer, b2b_id_country, p_quantity)
if err != nil {
return nil, err
}
result := view.ProductFull{
Product: product,
Price: price,
Variants: variants,
}
if len(variants) > 0 {
result.Variants = variants
}
jsonBytes, err := json.Marshal(result)
if err != nil {
return nil, err
}
raw := json.RawMessage(jsonBytes)
return &raw, nil
} }
func (s *ProductService) Find(id_lang, userID uint, p find.Paging, filters *filters.FiltersList) (find.Found[model.ProductInList], error) { func (s *ProductService) Find(
return s.productsRepo.Find(id_lang, userID, p, filters) idLang uint,
userID uint,
p find.Paging,
filters *filters.FiltersList,
customer *model.Customer,
quantity uint,
shopID uint,
) (*find.Found[model.ProductInList], error) {
if customer == nil || customer.Country == nil {
return nil, errors.New("customer is nil or missing fields")
}
found, err := s.productsRepo.Find(idLang, userID, p, filters)
if err != nil {
return nil, err
}
// 1. collect simple products (no variants)
simpleProductIndexes := make([]int, 0, len(found.Items))
for i := range found.Items {
if found.Items[i].VariantsNumber <= 0 {
simpleProductIndexes = append(simpleProductIndexes, i)
}
}
// 2. resolve prices ONLY for simple products
for _, i := range simpleProductIndexes {
price, err := s.productsRepo.GetPrice(
found.Items[i].ProductID,
nil,
shopID,
customer.ID,
customer.CountryID,
quantity,
)
if err != nil {
return nil, err
}
found.Items[i].PriceTaxExcl = price.FinalTaxExcl
found.Items[i].PriceTaxIncl = price.FinalTaxIncl
}
return found, nil
}
func (s *ProductService) GetProductAttributes(
langID uint,
productID uint,
shopID uint,
customerID uint,
countryID uint,
quantity uint,
) ([]view.ProductAttribute, error) {
variants, err := s.productsRepo.GetVariants(productID, constdata.SHOP_ID, langID, customerID, countryID, quantity)
if err != nil {
return nil, err
}
return variants, nil
} }
func (s *ProductService) AddToFavorites(userID uint, productID uint) error { func (s *ProductService) AddToFavorites(userID uint, productID uint) error {

View File

@@ -8,6 +8,7 @@ import (
"log" "log"
"net/http" "net/http"
"os" "os"
"regexp"
"slices" "slices"
"strings" "strings"
"time" "time"
@@ -110,8 +111,9 @@ func (s *ProductTranslationService) SaveProductDescription(userID uint, productI
// check that fields description, description_short and usage, if they exist, have a valid html format // check that fields description, description_short and usage, if they exist, have a valid html format
mustBeHTML := []string{"description", "description_short", "usage"} mustBeHTML := []string{"description", "description_short", "usage"}
for i := 0; i < len(mustBeHTML); i++ { for i := 0; i < len(mustBeHTML); i++ {
if text, exists := updates[mustBeHTML[i]]; exists { if _, exists := updates[mustBeHTML[i]]; exists {
if !isValidXHTML(text) { updates[mustBeHTML[i]] = parseAutoCloseTags(updates[mustBeHTML[i]])
if !isValidXHTML(updates[mustBeHTML[i]]) {
return responseErrors.ErrInvalidXHTML return responseErrors.ErrInvalidXHTML
} }
} }
@@ -264,9 +266,17 @@ func cleanForPrompt(s string) string {
} }
} }
prompt += ">" if slices.Contains(xml.HTMLAutoClose, v.Name.Local) {
prompt += "/>"
} else {
prompt += ">"
}
case xml.EndElement: case xml.EndElement:
prompt += "</" + attrName(v.Name) + ">" if !slices.Contains(xml.HTMLAutoClose, v.Name.Local) {
prompt += "</" + attrName(v.Name) + ">"
}
case xml.CharData: case xml.CharData:
prompt += string(v) prompt += string(v)
case xml.Comment: case xml.Comment:
@@ -307,6 +317,43 @@ func getStringInBetween(str string, start string, end string) (success bool, res
return true, str[s : s+e] return true, str[s : s+e]
} }
// this converts input into HTML4 format.
// this really is ad-hoc solution, but it works.
func parseAutoCloseTags(s string) string {
alts := ""
for i, name := range xml.HTMLAutoClose {
if i > 0 {
alts += "|"
}
alts += name
}
// remove closing </img> tags
reClose := regexp.MustCompile(`(?i)<\s*\/\s*(?:` + alts + `)\s*>`)
s = reClose.ReplaceAllString(s, "")
// convert <img ...> → <img ... />
// matches <img ...> that do NOT already end with />
reOpen := regexp.MustCompile(`(?i)<\s*(` + alts + `)\b([^>]*?)>`)
s = reOpen.ReplaceAllStringFunc(s, func(tag string) string {
trimmed := strings.TrimSpace(tag)
// Already self-closed: <img ... />, <br/>
if strings.HasSuffix(trimmed, "/>") {
return tag
}
// Replace final > with />
i := strings.LastIndex(tag, ">")
if i < 0 {
return tag
}
return tag[:i] + " />"
})
return s
}
// isValidXHTML checks if the string obeys the XHTML format // isValidXHTML checks if the string obeys the XHTML format
func isValidXHTML(s string) bool { func isValidXHTML(s string) bool {
r := strings.NewReader(s) r := strings.NewReader(s)
@@ -382,7 +429,12 @@ func rebuildFromResponse(s_original string, s_response string) (bool, string) {
result += fmt.Sprintf(` %s="%s"`, attrName(attr.Name), attr.Value) result += fmt.Sprintf(` %s="%s"`, attrName(attr.Name), attr.Value)
} }
} }
result += ">"
if slices.Contains(xml.HTMLAutoClose, v_original.Name.Local) {
result += "/>"
} else {
result += ">"
}
case xml.CharData: case xml.CharData:
result += string(v_response) result += string(v_response)
@@ -400,7 +452,7 @@ func rebuildFromResponse(s_original string, s_response string) (bool, string) {
return false, "" return false, ""
} }
if v_original.Name.Local != "img" { if !slices.Contains(xml.HTMLAutoClose, v_original.Name.Local) {
result += "</" + attrName(v_original.Name) + ">" result += "</" + attrName(v_original.Name) + ">"
} }

View File

@@ -0,0 +1,124 @@
package specificPriceService
import (
"context"
"git.ma-al.com/goc_daniel/b2b/app/model"
"git.ma-al.com/goc_daniel/b2b/app/repos/specificPriceRepo"
"git.ma-al.com/goc_daniel/b2b/app/utils/responseErrors"
)
type SpecificPriceService struct {
specificPriceRepo specificPriceRepo.UISpecificPriceRepo
}
func New() *SpecificPriceService {
return &SpecificPriceService{
specificPriceRepo: specificPriceRepo.New(),
}
}
func (s *SpecificPriceService) Create(ctx context.Context, pr *model.SpecificPrice) (*model.SpecificPrice, error) {
if err := s.validateRequest(pr); err != nil {
return nil, err
}
if err := s.specificPriceRepo.Create(ctx, pr); err != nil {
return nil, err
}
return pr, nil
}
func (s *SpecificPriceService) Update(ctx context.Context, id uint64, pr *model.SpecificPrice) (*model.SpecificPrice, error) {
existing, err := s.specificPriceRepo.GetByID(ctx, id)
if err != nil {
return nil, err
}
if existing == nil {
return nil, responseErrors.ErrSpecificPriceNotFound
}
if err := s.validateUpdateRequest(pr); err != nil {
return nil, err
}
pr.ID = id
if err := s.specificPriceRepo.Update(ctx, pr); err != nil {
return nil, err
}
return pr, nil
}
func (s *SpecificPriceService) GetByID(ctx context.Context, id uint64) (*model.SpecificPrice, error) {
pr, err := s.specificPriceRepo.GetByID(ctx, id)
if err != nil {
return nil, err
}
if pr == nil {
return nil, responseErrors.ErrSpecificPriceNotFound
}
return pr, nil
}
func (s *SpecificPriceService) List(ctx context.Context) ([]*model.SpecificPrice, error) {
return s.specificPriceRepo.List(ctx)
}
func (s *SpecificPriceService) SetActive(ctx context.Context, id uint64, active bool) error {
pr, err := s.specificPriceRepo.GetByID(ctx, id)
if err != nil {
return err
}
if pr == nil {
return responseErrors.ErrSpecificPriceNotFound
}
return s.specificPriceRepo.SetActive(ctx, id, active)
}
func (s *SpecificPriceService) Delete(ctx context.Context, id uint64) error {
pr, err := s.specificPriceRepo.GetByID(ctx, id)
if err != nil {
return err
}
if pr == nil {
return responseErrors.ErrSpecificPriceNotFound
}
return s.specificPriceRepo.Delete(ctx, id)
}
func (s *SpecificPriceService) validateRequest(pr *model.SpecificPrice) error {
if pr.ReductionType != "amount" && pr.ReductionType != "percentage" {
return responseErrors.ErrInvalidReductionType
}
if pr.ReductionType == "percentage" && pr.PercentageReduction == nil {
return responseErrors.ErrPercentageRequired
}
if pr.ReductionType == "amount" && pr.Price == nil {
return responseErrors.ErrPriceRequired
}
return nil
}
func (s *SpecificPriceService) validateUpdateRequest(pr *model.SpecificPrice) error {
if pr.ReductionType != "" && pr.ReductionType != "amount" && pr.ReductionType != "percentage" {
return responseErrors.ErrInvalidReductionType
}
if pr.ReductionType == "percentage" && pr.PercentageReduction == nil {
return responseErrors.ErrPercentageRequired
}
if pr.ReductionType == "amount" && pr.Price == nil {
return responseErrors.ErrPriceRequired
}
return nil
}

View File

@@ -3,6 +3,7 @@ package constdata
// PASSWORD_VALIDATION_REGEX is used by the frontend (JavaScript supports lookaheads). // PASSWORD_VALIDATION_REGEX is used by the frontend (JavaScript supports lookaheads).
const PASSWORD_VALIDATION_REGEX = `^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{10,}$` const PASSWORD_VALIDATION_REGEX = `^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{10,}$`
const SHOP_ID = 1 const SHOP_ID = 1
const DEFAULT_PRODUCT_QUANTITY = 1
const SHOP_DEFAULT_LANGUAGE = 1 const SHOP_DEFAULT_LANGUAGE = 1
const ADMIN_NOTIFICATION_LANGUAGE = 2 const ADMIN_NOTIFICATION_LANGUAGE = 2

View File

@@ -66,6 +66,11 @@ var (
ErrUserHasNoSuchCart = errors.New("user does not have cart with given id") ErrUserHasNoSuchCart = errors.New("user does not have cart with given id")
ErrProductOrItsVariationDoesNotExist = errors.New("product or its variation with given ids does not exist") ErrProductOrItsVariationDoesNotExist = errors.New("product or its variation with given ids does not exist")
// Typed errors for price reduction handler
ErrInvalidReductionType = errors.New("invalid reduction type: must be 'amount' or 'percentage'")
ErrPercentageRequired = errors.New("percentage_reduction required when reduction_type is percentage")
ErrPriceRequired = errors.New("price required when reduction_type is amount")
ErrSpecificPriceNotFound = errors.New("price reduction not found")
// Typed errors for storage // Typed errors for storage
ErrAccessDenied = errors.New("access denied!") ErrAccessDenied = errors.New("access denied!")
ErrFolderDoesNotExist = errors.New("folder does not exist") ErrFolderDoesNotExist = errors.New("folder does not exist")
@@ -207,6 +212,15 @@ func GetErrorCode(c fiber.Ctx, err error) string {
case errors.Is(err, ErrMissingFileFieldDocument): case errors.Is(err, ErrMissingFileFieldDocument):
return i18n.T_(c, "error.err_missing_file_field_document") return i18n.T_(c, "error.err_missing_file_field_document")
case errors.Is(err, ErrInvalidReductionType):
return i18n.T_(c, "error.invalid_reduction_type")
case errors.Is(err, ErrPercentageRequired):
return i18n.T_(c, "error.percentage_required")
case errors.Is(err, ErrPriceRequired):
return i18n.T_(c, "error.price_required")
case errors.Is(err, ErrSpecificPriceNotFound):
return i18n.T_(c, "error.price_reduction_not_found")
case errors.Is(err, ErrJSONBody): case errors.Is(err, ErrJSONBody):
return i18n.T_(c, "error.err_json_body") return i18n.T_(c, "error.err_json_body")
@@ -268,6 +282,9 @@ func GetErrorStatus(err error) int {
errors.Is(err, ErrMaxAmtOfCartsReached), errors.Is(err, ErrMaxAmtOfCartsReached),
errors.Is(err, ErrUserHasNoSuchCart), errors.Is(err, ErrUserHasNoSuchCart),
errors.Is(err, ErrProductOrItsVariationDoesNotExist), errors.Is(err, ErrProductOrItsVariationDoesNotExist),
errors.Is(err, ErrInvalidReductionType),
errors.Is(err, ErrPercentageRequired),
errors.Is(err, ErrPriceRequired),
errors.Is(err, ErrAccessDenied), errors.Is(err, ErrAccessDenied),
errors.Is(err, ErrFolderDoesNotExist), errors.Is(err, ErrFolderDoesNotExist),
errors.Is(err, ErrFileDoesNotExist), errors.Is(err, ErrFileDoesNotExist),
@@ -279,6 +296,8 @@ func GetErrorStatus(err error) int {
errors.Is(err, ErrInvalidCountryID), errors.Is(err, ErrInvalidCountryID),
errors.Is(err, ErrInvalidAddressJSON): errors.Is(err, ErrInvalidAddressJSON):
return fiber.StatusBadRequest return fiber.StatusBadRequest
case errors.Is(err, ErrSpecificPriceNotFound):
return fiber.StatusNotFound
case errors.Is(err, ErrEmailExists): case errors.Is(err, ErrEmailExists):
return fiber.StatusConflict return fiber.StatusConflict
case errors.Is(err, ErrAIResponseFail), case errors.Is(err, ErrAIResponseFail),

98
app/view/product.go Normal file
View File

@@ -0,0 +1,98 @@
package view
import (
"encoding/json"
"time"
)
type ProductAttribute struct {
IDProductAttribute uint `gorm:"column:id_product_attribute" json:"id_product_attribute"`
Reference string `gorm:"column:reference" json:"reference"`
BasePrice float64 `gorm:"column:base_price" json:"base_price"`
PriceTaxExcl float64 `gorm:"column:price_tax_excl" json:"price_tax_excl"`
PriceTaxIncl float64 `gorm:"column:price_tax_incl" json:"price_tax_incl"`
Quantity int64 `gorm:"column:quantity" json:"quantity"`
Attributes json.RawMessage `gorm:"column:attributes" json:"attributes"`
}
type Price struct {
Base float64 `json:"base"`
FinalTaxExcl float64 `json:"final_tax_excl"`
FinalTaxIncl float64 `json:"final_tax_incl"`
TaxRate float64 `json:"tax_rate"`
Priority int `json:"priority"` // or string
}
type Variant struct {
ID uint `json:"id_product_attribute"`
Reference string `json:"reference"`
BasePrice float64 `json:"base_price"`
FinalExcl float64 `json:"final_tax_excl"`
FinalIncl float64 `json:"final_tax_incl"`
Stock int `json:"stock"`
}
type ProductFull struct {
Product Product `json:"product"`
Price Price `json:"price"`
Variants []ProductAttribute `json:"variants,omitempty"`
}
type Product struct {
ID uint `gorm:"column:id" json:"id"`
Reference string `gorm:"column:reference" json:"reference"`
SupplierReference string `gorm:"column:supplier_reference" json:"supplier_reference,omitempty"`
EAN13 string `gorm:"column:ean13" json:"ean13,omitempty"`
UPC string `gorm:"column:upc" json:"upc,omitempty"`
ISBN string `gorm:"column:isbn" json:"isbn,omitempty"`
// Basic Price (from product table)
BasePrice float64 `gorm:"column:base_price" json:"base_price"`
WholesalePrice float64 `gorm:"column:wholesale_price" json:"wholesale_price,omitempty"`
Unity string `gorm:"column:unity" json:"unity,omitempty"`
UnitPriceRatio float64 `gorm:"column:unit_price_ratio" json:"unit_price_ratio,omitempty"`
// Stock & Availability
Quantity int `gorm:"column:quantity" json:"quantity"`
MinimalQuantity int `gorm:"column:minimal_quantity" json:"minimal_quantity"`
AvailableForOrder bool `gorm:"column:available_for_order" json:"available_for_order"`
AvailableDate string `gorm:"column:available_date" json:"available_date,omitempty"`
OutOfStockBehavior int `gorm:"column:out_of_stock_behavior" json:"out_of_stock_behavior"`
// Flags
OnSale bool `gorm:"column:on_sale" json:"on_sale"`
ShowPrice bool `gorm:"column:show_price" json:"show_price"`
Condition string `gorm:"column:condition" json:"condition"`
IsVirtual bool `gorm:"column:is_virtual" json:"is_virtual"`
// Physical
Weight float64 `gorm:"column:weight" json:"weight"`
Width float64 `gorm:"column:width" json:"width"`
Height float64 `gorm:"column:height" json:"height"`
Depth float64 `gorm:"column:depth" json:"depth"`
AdditionalShippingCost float64 `gorm:"column:additional_shipping_cost" json:"additional_shipping_cost,omitempty"`
// Delivery
DeliveryDays int `gorm:"column:delivery_days" json:"delivery_days,omitempty"`
// Status
Active bool `gorm:"column:active" json:"active"`
Visibility string `gorm:"column:visibility" json:"visibility"`
Indexed bool `gorm:"column:indexed" json:"indexed"`
// Timestamps
DateAdd time.Time `gorm:"column:date_add" json:"date_add"`
DateUpd time.Time `gorm:"column:date_upd" json:"date_upd"`
// Language fields
Name string `gorm:"column:name" json:"name"`
Description string `gorm:"column:description" json:"description"`
DescriptionShort string `gorm:"column:description_short" json:"description_short"`
// Relations
Manufacturer string `gorm:"column:manufacturer" json:"manufacturer"`
Category string `gorm:"column:category" json:"category"`
IsFavorite bool `gorm:"column:is_favorite" json:"is_favorite"`
}

55
bo/auto-imports.d.ts vendored
View File

@@ -7,10 +7,10 @@
export {} export {}
declare global { declare global {
const avatarGroupInjectionKey: typeof import('./node_modules/@nuxt/ui/dist/runtime/composables/useAvatarGroup.js').avatarGroupInjectionKey const avatarGroupInjectionKey: typeof import('./node_modules/@nuxt/ui/dist/runtime/composables/useAvatarGroup.js').avatarGroupInjectionKey
const defineLocale: typeof import('./node_modules/@nuxt/ui/dist/runtime/composables/defineLocale.js').defineLocale const defineLocale: typeof import('./node_modules/@nuxt/ui/dist/runtime/composables/defineLocale').defineLocale
const defineShortcuts: typeof import('./node_modules/@nuxt/ui/dist/runtime/composables/defineShortcuts.js').defineShortcuts const defineShortcuts: typeof import('./node_modules/@nuxt/ui/dist/runtime/composables/defineShortcuts').defineShortcuts
const extendLocale: typeof import('./node_modules/@nuxt/ui/dist/runtime/composables/defineLocale.js').extendLocale const extendLocale: typeof import('./node_modules/@nuxt/ui/dist/runtime/composables/defineLocale').extendLocale
const extractShortcuts: typeof import('./node_modules/@nuxt/ui/dist/runtime/composables/defineShortcuts.js').extractShortcuts const extractShortcuts: typeof import('./node_modules/@nuxt/ui/dist/runtime/composables/defineShortcuts').extractShortcuts
const fieldGroupInjectionKey: typeof import('./node_modules/@nuxt/ui/dist/runtime/composables/useFieldGroup.js').fieldGroupInjectionKey const fieldGroupInjectionKey: typeof import('./node_modules/@nuxt/ui/dist/runtime/composables/useFieldGroup.js').fieldGroupInjectionKey
const formBusInjectionKey: typeof import('./node_modules/@nuxt/ui/dist/runtime/composables/useFormField.js').formBusInjectionKey const formBusInjectionKey: typeof import('./node_modules/@nuxt/ui/dist/runtime/composables/useFormField.js').formBusInjectionKey
const formErrorsInjectionKey: typeof import('./node_modules/@nuxt/ui/dist/runtime/composables/useFormField.js').formErrorsInjectionKey const formErrorsInjectionKey: typeof import('./node_modules/@nuxt/ui/dist/runtime/composables/useFormField.js').formErrorsInjectionKey
@@ -29,46 +29,17 @@ declare global {
const useAvatarGroup: typeof import('./node_modules/@nuxt/ui/dist/runtime/composables/useAvatarGroup.js').useAvatarGroup const useAvatarGroup: typeof import('./node_modules/@nuxt/ui/dist/runtime/composables/useAvatarGroup.js').useAvatarGroup
const useComponentIcons: typeof import('./node_modules/@nuxt/ui/dist/runtime/composables/useComponentIcons.js').useComponentIcons const useComponentIcons: typeof import('./node_modules/@nuxt/ui/dist/runtime/composables/useComponentIcons.js').useComponentIcons
const useComponentUI: typeof import('./node_modules/@nuxt/ui/dist/runtime/composables/useComponentUI.js').useComponentUI const useComponentUI: typeof import('./node_modules/@nuxt/ui/dist/runtime/composables/useComponentUI.js').useComponentUI
const useContentSearch: typeof import('./node_modules/@nuxt/ui/dist/runtime/composables/useContentSearch.js').useContentSearch const useContentSearch: typeof import('./node_modules/@nuxt/ui/dist/runtime/composables/useContentSearch').useContentSearch
const useEditorMenu: typeof import('./node_modules/@nuxt/ui/dist/runtime/composables/useEditorMenu.js').useEditorMenu const useEditorMenu: typeof import('./node_modules/@nuxt/ui/dist/runtime/composables/useEditorMenu.js').useEditorMenu
const useFieldGroup: typeof import('./node_modules/@nuxt/ui/dist/runtime/composables/useFieldGroup.js').useFieldGroup const useFieldGroup: typeof import('./node_modules/@nuxt/ui/dist/runtime/composables/useFieldGroup.js').useFieldGroup
const useFileUpload: typeof import('./node_modules/@nuxt/ui/dist/runtime/composables/useFileUpload.js').useFileUpload const useFileUpload: typeof import('./node_modules/@nuxt/ui/dist/runtime/composables/useFileUpload').useFileUpload
const useFormField: typeof import('./node_modules/@nuxt/ui/dist/runtime/composables/useFormField.js').useFormField const useFormField: typeof import('./node_modules/@nuxt/ui/dist/runtime/composables/useFormField').useFormField
const useKbd: typeof import('./node_modules/@nuxt/ui/dist/runtime/composables/useKbd.js').useKbd const useKbd: typeof import('./node_modules/@nuxt/ui/dist/runtime/composables/useKbd').useKbd
const useLocale: typeof import('./node_modules/@nuxt/ui/dist/runtime/composables/useLocale.js').useLocale const useLocale: typeof import('./node_modules/@nuxt/ui/dist/runtime/composables/useLocale.js').useLocale
const useOverlay: typeof import('./node_modules/@nuxt/ui/dist/runtime/composables/useOverlay.js').useOverlay const useOverlay: typeof import('./node_modules/@nuxt/ui/dist/runtime/composables/useOverlay').useOverlay
const usePortal: typeof import('./node_modules/@nuxt/ui/dist/runtime/composables/usePortal.js').usePortal const usePortal: typeof import('./node_modules/@nuxt/ui/dist/runtime/composables/usePortal.js').usePortal
const useResizable: typeof import('./node_modules/@nuxt/ui/dist/runtime/composables/useResizable.js').useResizable const useResizable: typeof import('./node_modules/@nuxt/ui/dist/runtime/composables/useResizable').useResizable
const useScrollspy: typeof import('./node_modules/@nuxt/ui/dist/runtime/composables/useScrollspy.js').useScrollspy const useScrollShadow: typeof import('./node_modules/@nuxt/ui/dist/runtime/composables/useScrollShadow').useScrollShadow
const useToast: typeof import('./node_modules/@nuxt/ui/dist/runtime/composables/useToast.js').useToast const useScrollspy: typeof import('./node_modules/@nuxt/ui/dist/runtime/composables/useScrollspy').useScrollspy
} const useToast: typeof import('./node_modules/@nuxt/ui/dist/runtime/composables/useToast').useToast
// for type re-export
declare global {
// @ts-ignore
export type { ShortcutConfig, ShortcutsConfig, ShortcutsOptions } from './node_modules/@nuxt/ui/dist/runtime/composables/defineShortcuts.d'
import('./node_modules/@nuxt/ui/dist/runtime/composables/defineShortcuts.d')
// @ts-ignore
export type { UseComponentIconsProps } from './node_modules/@nuxt/ui/dist/runtime/composables/useComponentIcons.d'
import('./node_modules/@nuxt/ui/dist/runtime/composables/useComponentIcons.d')
// @ts-ignore
export type { ThemeUI, ThemeRootContext } from './node_modules/@nuxt/ui/dist/runtime/composables/useComponentUI.d'
import('./node_modules/@nuxt/ui/dist/runtime/composables/useComponentUI.d')
// @ts-ignore
export type { EditorMenuOptions } from './node_modules/@nuxt/ui/dist/runtime/composables/useEditorMenu.d'
import('./node_modules/@nuxt/ui/dist/runtime/composables/useEditorMenu.d')
// @ts-ignore
export type { UseFileUploadOptions } from './node_modules/@nuxt/ui/dist/runtime/composables/useFileUpload.d'
import('./node_modules/@nuxt/ui/dist/runtime/composables/useFileUpload.d')
// @ts-ignore
export type { KbdKey, KbdKeySpecific } from './node_modules/@nuxt/ui/dist/runtime/composables/useKbd.d'
import('./node_modules/@nuxt/ui/dist/runtime/composables/useKbd.d')
// @ts-ignore
export type { OverlayOptions, Overlay } from './node_modules/@nuxt/ui/dist/runtime/composables/useOverlay.d'
import('./node_modules/@nuxt/ui/dist/runtime/composables/useOverlay.d')
// @ts-ignore
export type { UseResizableProps, UseResizableReturn } from './node_modules/@nuxt/ui/dist/runtime/composables/useResizable.d'
import('./node_modules/@nuxt/ui/dist/runtime/composables/useResizable.d')
// @ts-ignore
export type { Toast } from './node_modules/@nuxt/ui/dist/runtime/composables/useToast.d'
import('./node_modules/@nuxt/ui/dist/runtime/composables/useToast.d')
} }

View File

@@ -1,25 +1,25 @@
{ {
"lockfileVersion": 1, "lockfileVersion": 1,
"configVersion": 1,
"workspaces": { "workspaces": {
"": { "": {
"name": "bo", "name": "bo",
"dependencies": { "dependencies": {
"@nuxt/ui": "^4.5.0", "@nuxt/ui": "^4.6.0",
"@tailwindcss/vite": "^4.2.0", "@tailwindcss/vite": "^4.2.2",
"chart.js": "^4.5.1", "chart.js": "^4.5.1",
"pinia": "^3.0.4", "pinia": "^3.0.4",
"tailwindcss": "^4.2.0", "reka-ui": "^2.9.3",
"tailwindcss": "^4.2.2",
"vue": "beta", "vue": "beta",
"vue-chartjs": "^5.3.3", "vue-chartjs": "^5.3.3",
"vue-i18n": "11", "vue-i18n": "^11.3.0",
"vue-router": "^5.0.2", "vue-router": "^5.0.4",
}, },
"devDependencies": { "devDependencies": {
"@tsconfig/node24": "^24.0.4", "@tsconfig/node24": "^24.0.4",
"@types/node": "^24.10.13", "@types/node": "^24.12.0",
"@vitejs/plugin-vue": "^6.0.4", "@vitejs/plugin-vue": "^6.0.5",
"@vue/eslint-config-typescript": "^14.6.0", "@vue/eslint-config-typescript": "^14.7.0",
"@vue/tsconfig": "^0.8.1", "@vue/tsconfig": "^0.8.1",
"jiti": "^2.6.1", "jiti": "^2.6.1",
"npm-run-all2": "^8.0.4", "npm-run-all2": "^8.0.4",
@@ -27,8 +27,8 @@
"prettier": "3.8.1", "prettier": "3.8.1",
"typescript": "~5.9.3", "typescript": "~5.9.3",
"vite": "beta", "vite": "beta",
"vite-plugin-vue-devtools": "^8.0.6", "vite-plugin-vue-devtools": "^8.1.1",
"vue-tsc": "^3.2.4", "vue-tsc": "^3.2.6",
}, },
}, },
}, },
@@ -88,9 +88,9 @@
"@babel/helper-validator-option": ["@babel/helper-validator-option@7.27.1", "", {}, "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg=="], "@babel/helper-validator-option": ["@babel/helper-validator-option@7.27.1", "", {}, "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg=="],
"@babel/helpers": ["@babel/helpers@7.28.6", "", { "dependencies": { "@babel/template": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw=="], "@babel/helpers": ["@babel/helpers@7.29.2", "", { "dependencies": { "@babel/template": "^7.28.6", "@babel/types": "^7.29.0" } }, "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw=="],
"@babel/parser": ["@babel/parser@7.29.0", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww=="], "@babel/parser": ["@babel/parser@7.29.2", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA=="],
"@babel/plugin-proposal-decorators": ["@babel/plugin-proposal-decorators@7.29.0", "", { "dependencies": { "@babel/helper-create-class-features-plugin": "^7.28.6", "@babel/helper-plugin-utils": "^7.28.6", "@babel/plugin-syntax-decorators": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-CVBVv3VY/XRMxRYq5dwr2DS7/MvqPm23cOCjbwNnVrfOqcWlnefua1uUs0sjdKOGjvPUG633o07uWzJq4oI6dA=="], "@babel/plugin-proposal-decorators": ["@babel/plugin-proposal-decorators@7.29.0", "", { "dependencies": { "@babel/helper-create-class-features-plugin": "^7.28.6", "@babel/helper-plugin-utils": "^7.28.6", "@babel/plugin-syntax-decorators": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-CVBVv3VY/XRMxRYq5dwr2DS7/MvqPm23cOCjbwNnVrfOqcWlnefua1uUs0sjdKOGjvPUG633o07uWzJq4oI6dA=="],
@@ -114,63 +114,63 @@
"@capsizecss/unpack": ["@capsizecss/unpack@4.0.0", "", { "dependencies": { "fontkitten": "^1.0.0" } }, "sha512-VERIM64vtTP1C4mxQ5thVT9fK0apjPFobqybMtA1UdUujWka24ERHbRHFGmpbbhp73MhV+KSsHQH9C6uOTdEQA=="], "@capsizecss/unpack": ["@capsizecss/unpack@4.0.0", "", { "dependencies": { "fontkitten": "^1.0.0" } }, "sha512-VERIM64vtTP1C4mxQ5thVT9fK0apjPFobqybMtA1UdUujWka24ERHbRHFGmpbbhp73MhV+KSsHQH9C6uOTdEQA=="],
"@emnapi/core": ["@emnapi/core@1.8.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" } }, "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg=="], "@emnapi/core": ["@emnapi/core@1.9.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.0", "tslib": "^2.4.0" } }, "sha512-mukuNALVsoix/w1BJwFzwXBN/dHeejQtuVzcDsfOEsdpCumXb/E9j8w11h5S54tT1xhifGfbbSm/ICrObRb3KA=="],
"@emnapi/runtime": ["@emnapi/runtime@1.8.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg=="], "@emnapi/runtime": ["@emnapi/runtime@1.9.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA=="],
"@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.1.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ=="], "@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg=="],
"@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.3", "", { "os": "aix", "cpu": "ppc64" }, "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg=="], "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.4", "", { "os": "aix", "cpu": "ppc64" }, "sha512-cQPwL2mp2nSmHHJlCyoXgHGhbEPMrEEU5xhkcy3Hs/O7nGZqEpZ2sUtLaL9MORLtDfRvVl2/3PAuEkYZH0Ty8Q=="],
"@esbuild/android-arm": ["@esbuild/android-arm@0.27.3", "", { "os": "android", "cpu": "arm" }, "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA=="], "@esbuild/android-arm": ["@esbuild/android-arm@0.27.4", "", { "os": "android", "cpu": "arm" }, "sha512-X9bUgvxiC8CHAGKYufLIHGXPJWnr0OCdR0anD2e21vdvgCI8lIfqFbnoeOz7lBjdrAGUhqLZLcQo6MLhTO2DKQ=="],
"@esbuild/android-arm64": ["@esbuild/android-arm64@0.27.3", "", { "os": "android", "cpu": "arm64" }, "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg=="], "@esbuild/android-arm64": ["@esbuild/android-arm64@0.27.4", "", { "os": "android", "cpu": "arm64" }, "sha512-gdLscB7v75wRfu7QSm/zg6Rx29VLdy9eTr2t44sfTW7CxwAtQghZ4ZnqHk3/ogz7xao0QAgrkradbBzcqFPasw=="],
"@esbuild/android-x64": ["@esbuild/android-x64@0.27.3", "", { "os": "android", "cpu": "x64" }, "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ=="], "@esbuild/android-x64": ["@esbuild/android-x64@0.27.4", "", { "os": "android", "cpu": "x64" }, "sha512-PzPFnBNVF292sfpfhiyiXCGSn9HZg5BcAz+ivBuSsl6Rk4ga1oEXAamhOXRFyMcjwr2DVtm40G65N3GLeH1Lvw=="],
"@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.27.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg=="], "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.27.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-b7xaGIwdJlht8ZFCvMkpDN6uiSmnxxK56N2GDTMYPr2/gzvfdQN8rTfBsvVKmIVY/X7EM+/hJKEIbbHs9oA4tQ=="],
"@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.27.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg=="], "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.27.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-sR+OiKLwd15nmCdqpXMnuJ9W2kpy0KigzqScqHI3Hqwr7IXxBp3Yva+yJwoqh7rE8V77tdoheRYataNKL4QrPw=="],
"@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.27.3", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w=="], "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.27.4", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-jnfpKe+p79tCnm4GVav68A7tUFeKQwQyLgESwEAUzyxk/TJr4QdGog9sqWNcUbr/bZt/O/HXouspuQDd9JxFSw=="],
"@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.27.3", "", { "os": "freebsd", "cpu": "x64" }, "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA=="], "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.27.4", "", { "os": "freebsd", "cpu": "x64" }, "sha512-2kb4ceA/CpfUrIcTUl1wrP/9ad9Atrp5J94Lq69w7UwOMolPIGrfLSvAKJp0RTvkPPyn6CIWrNy13kyLikZRZQ=="],
"@esbuild/linux-arm": ["@esbuild/linux-arm@0.27.3", "", { "os": "linux", "cpu": "arm" }, "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw=="], "@esbuild/linux-arm": ["@esbuild/linux-arm@0.27.4", "", { "os": "linux", "cpu": "arm" }, "sha512-aBYgcIxX/wd5n2ys0yESGeYMGF+pv6g0DhZr3G1ZG4jMfruU9Tl1i2Z+Wnj9/KjGz1lTLCcorqE2viePZqj4Eg=="],
"@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.27.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg=="], "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.27.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-7nQOttdzVGth1iz57kxg9uCz57dxQLHWxopL6mYuYthohPKEK0vU0C3O21CcBK6KDlkYVcnDXY099HcCDXd9dA=="],
"@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.27.3", "", { "os": "linux", "cpu": "ia32" }, "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg=="], "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.27.4", "", { "os": "linux", "cpu": "ia32" }, "sha512-oPtixtAIzgvzYcKBQM/qZ3R+9TEUd1aNJQu0HhGyqtx6oS7qTpvjheIWBbes4+qu1bNlo2V4cbkISr8q6gRBFA=="],
"@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.27.3", "", { "os": "linux", "cpu": "none" }, "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA=="], "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.27.4", "", { "os": "linux", "cpu": "none" }, "sha512-8mL/vh8qeCoRcFH2nM8wm5uJP+ZcVYGGayMavi8GmRJjuI3g1v6Z7Ni0JJKAJW+m0EtUuARb6Lmp4hMjzCBWzA=="],
"@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.27.3", "", { "os": "linux", "cpu": "none" }, "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw=="], "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.27.4", "", { "os": "linux", "cpu": "none" }, "sha512-1RdrWFFiiLIW7LQq9Q2NES+HiD4NyT8Itj9AUeCl0IVCA459WnPhREKgwrpaIfTOe+/2rdntisegiPWn/r/aAw=="],
"@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.27.3", "", { "os": "linux", "cpu": "ppc64" }, "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA=="], "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.27.4", "", { "os": "linux", "cpu": "ppc64" }, "sha512-tLCwNG47l3sd9lpfyx9LAGEGItCUeRCWeAx6x2Jmbav65nAwoPXfewtAdtbtit/pJFLUWOhpv0FpS6GQAmPrHA=="],
"@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.27.3", "", { "os": "linux", "cpu": "none" }, "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ=="], "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.27.4", "", { "os": "linux", "cpu": "none" }, "sha512-BnASypppbUWyqjd1KIpU4AUBiIhVr6YlHx/cnPgqEkNoVOhHg+YiSVxM1RLfiy4t9cAulbRGTNCKOcqHrEQLIw=="],
"@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.27.3", "", { "os": "linux", "cpu": "s390x" }, "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw=="], "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.27.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-+eUqgb/Z7vxVLezG8bVB9SfBie89gMueS+I0xYh2tJdw3vqA/0ImZJ2ROeWwVJN59ihBeZ7Tu92dF/5dy5FttA=="],
"@esbuild/linux-x64": ["@esbuild/linux-x64@0.27.3", "", { "os": "linux", "cpu": "x64" }, "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA=="], "@esbuild/linux-x64": ["@esbuild/linux-x64@0.27.4", "", { "os": "linux", "cpu": "x64" }, "sha512-S5qOXrKV8BQEzJPVxAwnryi2+Iq5pB40gTEIT69BQONqR7JH1EPIcQ/Uiv9mCnn05jff9umq/5nqzxlqTOg9NA=="],
"@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.27.3", "", { "os": "none", "cpu": "arm64" }, "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA=="], "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.27.4", "", { "os": "none", "cpu": "arm64" }, "sha512-xHT8X4sb0GS8qTqiwzHqpY00C95DPAq7nAwX35Ie/s+LO9830hrMd3oX0ZMKLvy7vsonee73x0lmcdOVXFzd6Q=="],
"@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.27.3", "", { "os": "none", "cpu": "x64" }, "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA=="], "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.27.4", "", { "os": "none", "cpu": "x64" }, "sha512-RugOvOdXfdyi5Tyv40kgQnI0byv66BFgAqjdgtAKqHoZTbTF2QqfQrFwa7cHEORJf6X2ht+l9ABLMP0dnKYsgg=="],
"@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.27.3", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw=="], "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.27.4", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-2MyL3IAaTX+1/qP0O1SwskwcwCoOI4kV2IBX1xYnDDqthmq5ArrW94qSIKCAuRraMgPOmG0RDTA74mzYNQA9ow=="],
"@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.27.3", "", { "os": "openbsd", "cpu": "x64" }, "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ=="], "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.27.4", "", { "os": "openbsd", "cpu": "x64" }, "sha512-u8fg/jQ5aQDfsnIV6+KwLOf1CmJnfu1ShpwqdwC0uA7ZPwFws55Ngc12vBdeUdnuWoQYx/SOQLGDcdlfXhYmXQ=="],
"@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.27.3", "", { "os": "none", "cpu": "arm64" }, "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g=="], "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.27.4", "", { "os": "none", "cpu": "arm64" }, "sha512-JkTZrl6VbyO8lDQO3yv26nNr2RM2yZzNrNHEsj9bm6dOwwu9OYN28CjzZkH57bh4w0I2F7IodpQvUAEd1mbWXg=="],
"@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.27.3", "", { "os": "sunos", "cpu": "x64" }, "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA=="], "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.27.4", "", { "os": "sunos", "cpu": "x64" }, "sha512-/gOzgaewZJfeJTlsWhvUEmUG4tWEY2Spp5M20INYRg2ZKl9QPO3QEEgPeRtLjEWSW8FilRNacPOg8R1uaYkA6g=="],
"@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.27.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA=="], "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.27.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-Z9SExBg2y32smoDQdf1HRwHRt6vAHLXcxD2uGgO/v2jK7Y718Ix4ndsbNMU/+1Qiem9OiOdaqitioZwxivhXYg=="],
"@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.27.3", "", { "os": "win32", "cpu": "ia32" }, "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q=="], "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.27.4", "", { "os": "win32", "cpu": "ia32" }, "sha512-DAyGLS0Jz5G5iixEbMHi5KdiApqHBWMGzTtMiJ72ZOLhbu/bzxgAe8Ue8CTS3n3HbIUHQz/L51yMdGMeoxXNJw=="],
"@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.3", "", { "os": "win32", "cpu": "x64" }, "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA=="], "@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.4", "", { "os": "win32", "cpu": "x64" }, "sha512-+knoa0BDoeXgkNvvV1vvbZX4+hizelrkwmGJBdT17t8FNPwG2lKemmuMZlmaNQ3ws3DKKCxpb4zRZEIp3UxFCg=="],
"@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.9.1", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ=="], "@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.9.1", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ=="],
@@ -202,7 +202,7 @@
"@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.3", "", {}, "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ=="], "@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.3", "", {}, "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ=="],
"@iconify/collections": ["@iconify/collections@1.0.659", "", { "dependencies": { "@iconify/types": "*" } }, "sha512-gD9i7zfQpr3ZAv1BywaEri8o008KQT9iYaMw5EY4Y/y0qeEZB12FJMMyJGS3NvSeG80udu4kx8aZZLRsAdcANw=="], "@iconify/collections": ["@iconify/collections@1.0.668", "", { "dependencies": { "@iconify/types": "*" } }, "sha512-d4ygrDoTqb02p7YNHdNzy64y4VQG0bd3w26EH+mJ0eKAj+DSWxDJ1Qw/WdCAqhKfjb+mRp1n9+YwcELs2YWVlg=="],
"@iconify/types": ["@iconify/types@2.0.0", "", {}, "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg=="], "@iconify/types": ["@iconify/types@2.0.0", "", {}, "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg=="],
@@ -234,7 +234,7 @@
"@kurkle/color": ["@kurkle/color@0.3.4", "", {}, "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w=="], "@kurkle/color": ["@kurkle/color@0.3.4", "", {}, "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w=="],
"@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.1", "", { "dependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1", "@tybys/wasm-util": "^0.10.1" } }, "sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A=="], "@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.2", "", { "dependencies": { "@tybys/wasm-util": "^0.10.1" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" } }, "sha512-sNXv5oLJ7ob93xkZ1XnxisYhGYXfaG9f65/ZgYuAu3qt7b3NadcOEhLvx28hv31PgX8SZJRYrAIPQilQmFpLVw=="],
"@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="], "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="],
@@ -242,17 +242,17 @@
"@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="], "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="],
"@nuxt/devtools-kit": ["@nuxt/devtools-kit@3.2.3", "", { "dependencies": { "@nuxt/kit": "^4.3.1", "execa": "^8.0.1" }, "peerDependencies": { "vite": ">=6.0" } }, "sha512-5zj7Xx5CDI6P84kMalXoxGLd470buF6ncsRhiEPq8UlwdpVeR7bwi8QnparZNFBdG79bZ5KUkfi5YDXpLYPoIA=="], "@nuxt/devtools-kit": ["@nuxt/devtools-kit@3.2.4", "", { "dependencies": { "@nuxt/kit": "^4.4.2", "execa": "^8.0.1" }, "peerDependencies": { "vite": ">=6.0" } }, "sha512-Yxy2Xgmq5hf3dQy983V0xh0OJV2mYwRZz9eVIGc3EaribdFGPDNGMMbYqX9qCty3Pbxn/bCF3J0UyPaNlHVayQ=="],
"@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/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/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/kit": ["@nuxt/kit@4.4.2", "", { "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.1", "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-5+IPRNX2CjkBhuWUwz0hBuLqiaJPRoKzQ+SvcdrQDbAyE+VDeFt74VpSFr5/R0ujrK4b+XnSHUJWdS72w6hsog=="],
"@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/schema": ["@nuxt/schema@4.4.2", "", { "dependencies": { "@vue/shared": "^3.5.30", "defu": "^6.1.4", "pathe": "^2.0.3", "pkg-types": "^2.3.0", "std-env": "^4.0.0" } }, "sha512-/q6C7Qhiricgi+PKR7ovBnJlKTL0memCbA1CzRT+itCW/oeYzUfeMdQ35mGntlBoyRPNrMXbzuSUhfDbSCU57w=="],
"@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", "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=="], "@nuxt/ui": ["@nuxt/ui@4.6.0", "", { "dependencies": { "@floating-ui/dom": "^1.7.6", "@iconify/vue": "^5.0.0", "@internationalized/date": "^3.12.0", "@internationalized/number": "^3.6.5", "@nuxt/fonts": "^0.14.0", "@nuxt/icon": "^2.2.1", "@nuxt/kit": "^4.4.2", "@nuxt/schema": "^4.4.2", "@nuxtjs/color-mode": "^3.5.2", "@standard-schema/spec": "^1.1.0", "@tailwindcss/postcss": "^4.2.2", "@tailwindcss/vite": "^4.2.2", "@tanstack/vue-table": "^8.21.3", "@tanstack/vue-virtual": "^3.13.23", "@tiptap/core": "^3.20.4", "@tiptap/extension-bubble-menu": "^3.20.4", "@tiptap/extension-code": "^3.20.4", "@tiptap/extension-collaboration": "^3.20.4", "@tiptap/extension-drag-handle": "^3.20.4", "@tiptap/extension-drag-handle-vue-3": "^3.20.4", "@tiptap/extension-floating-menu": "^3.20.4", "@tiptap/extension-horizontal-rule": "^3.20.4", "@tiptap/extension-image": "^3.20.4", "@tiptap/extension-mention": "^3.20.4", "@tiptap/extension-node-range": "^3.20.4", "@tiptap/extension-placeholder": "^3.20.4", "@tiptap/markdown": "^3.20.4", "@tiptap/pm": "^3.20.4", "@tiptap/starter-kit": "^3.20.4", "@tiptap/suggestion": "^3.20.4", "@tiptap/vue-3": "^3.20.4", "@unhead/vue": "^2.1.12", "@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": "^6.1.0", "knitwork": "^1.3.0", "magic-string": "^0.30.21", "mlly": "^1.8.2", "motion-v": "^2.2.0", "ohash": "^2.0.11", "pathe": "^2.0.3", "reka-ui": "2.9.2", "scule": "^1.3.0", "tailwind-merge": "^3.5.0", "tailwind-variants": "^3.2.2", "tailwindcss": "^4.2.2", "tinyglobby": "^0.2.15", "ufo": "^1.6.3", "unplugin": "^3.0.0", "unplugin-auto-import": "^21.0.0", "unplugin-vue-components": "^31.1.0", "vaul-vue": "0.4.1", "vue-component-type-helpers": "^3.2.6" }, "peerDependencies": { "@inertiajs/vue3": "^2.0.7", "@nuxt/content": "^3.0.0", "joi": "^18.0.0", "superstruct": "^2.0.0", "typescript": "^5.6.3", "valibot": "^1.0.0", "vue-router": "^4.5.0 || ^5.0.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-AcoM3bljDvxd4PGfzDbY4zyyy9Zsajfagr3il1iki7G8Ji3SG6Xsgy+weug+Hyp76YM5919oc6ciJmram5bf6A=="],
"@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=="], "@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=="],
@@ -336,121 +336,121 @@
"@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], "@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=="], "@swc/helpers": ["@swc/helpers@0.5.20", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-2egEBHUMasdypIzrprsu8g+OEVd7Vp2MM3a2eVlM/cyFYto0nGz5BX5BTgh/ShZZI9ed+ozEq+Ngt+rgmUs8tw=="],
"@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/node": ["@tailwindcss/node@4.2.2", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "enhanced-resolve": "^5.19.0", "jiti": "^2.6.1", "lightningcss": "1.32.0", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.2.2" } }, "sha512-pXS+wJ2gZpVXqFaUEjojq7jzMpTGf8rU6ipJz5ovJV6PUGmlJ+jvIwGrzdHdQ80Sg+wmQxUFuoW1UAAwHNEdFA=="],
"@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": ["@tailwindcss/oxide@4.2.2", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.2.2", "@tailwindcss/oxide-darwin-arm64": "4.2.2", "@tailwindcss/oxide-darwin-x64": "4.2.2", "@tailwindcss/oxide-freebsd-x64": "4.2.2", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.2", "@tailwindcss/oxide-linux-arm64-gnu": "4.2.2", "@tailwindcss/oxide-linux-arm64-musl": "4.2.2", "@tailwindcss/oxide-linux-x64-gnu": "4.2.2", "@tailwindcss/oxide-linux-x64-musl": "4.2.2", "@tailwindcss/oxide-wasm32-wasi": "4.2.2", "@tailwindcss/oxide-win32-arm64-msvc": "4.2.2", "@tailwindcss/oxide-win32-x64-msvc": "4.2.2" } }, "sha512-qEUA07+E5kehxYp9BVMpq9E8vnJuBHfJEC0vPC5e7iL/hw7HR61aDKoVoKzrG+QKp56vhNZe4qwkRmMC0zDLvg=="],
"@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.2.1", "", { "os": "android", "cpu": "arm64" }, "sha512-eZ7G1Zm5EC8OOKaesIKuw77jw++QJ2lL9N+dDpdQiAB/c/B2wDh0QPFHbkBVrXnwNugvrbJFk1gK2SsVjwWReg=="], "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.2.2", "", { "os": "android", "cpu": "arm64" }, "sha512-dXGR1n+P3B6748jZO/SvHZq7qBOqqzQ+yFrXpoOWWALWndF9MoSKAT3Q0fYgAzYzGhxNYOoysRvYlpixRBBoDg=="],
"@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.2.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-q/LHkOstoJ7pI1J0q6djesLzRvQSIfEto148ppAd+BVQK0JYjQIFSK3JgYZJa+Yzi0DDa52ZsQx2rqytBnf8Hw=="], "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.2.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-iq9Qjr6knfMpZHj55/37ouZeykwbDqF21gPFtfnhCCKGDcPI/21FKC9XdMO/XyBM7qKORx6UIhGgg6jLl7BZlg=="],
"@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-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.2.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-BlR+2c3nzc8f2G639LpL89YY4bdcIdUmiOOkv2GQv4/4M0vJlpXEa0JXNHhCHU7VWOKWT/CjqHdTP8aUuDJkuw=="],
"@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.2.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-5e/AkgYJT/cpbkys/OU2Ei2jdETCLlifwm7ogMC7/hksI2fC3iiq6OcXwjibcIjPung0kRtR3TxEITkqgn0TcA=="], "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.2.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-YUqUgrGMSu2CDO82hzlQ5qSb5xmx3RUrke/QgnoEx7KvmRJHQuZHZmZTLSuuHwFf0DJPybFMXMYf+WJdxHy/nQ=="],
"@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-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.2.2", "", { "os": "linux", "cpu": "arm" }, "sha512-FPdhvsW6g06T9BWT0qTwiVZYE2WIFo2dY5aCSpjG/S/u1tby+wXoslXS0kl3/KXnULlLr1E3NPRRw0g7t2kgaQ=="],
"@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-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.2.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-4og1V+ftEPXGttOO7eCmW7VICmzzJWgMx+QXAJRAhjrSjumCwWqMfkDrNu1LXEQzNAwz28NCUpucgQPrR4S2yw=="],
"@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.2.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-WZA0CHRL/SP1TRbA5mp9htsppSEkWuQ4KsSUumYQnyl8ZdT39ntwqmz4IUHGN6p4XdSlYfJwM4rRzZLShHsGAQ=="], "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.2.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-oCfG/mS+/+XRlwNjnsNLVwnMWYH7tn/kYPsNPh+JSOMlnt93mYNCKHYzylRhI51X+TbR+ufNhhKKzm6QkqX8ag=="],
"@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-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.2.2", "", { "os": "linux", "cpu": "x64" }, "sha512-rTAGAkDgqbXHNp/xW0iugLVmX62wOp2PoE39BTCGKjv3Iocf6AFbRP/wZT/kuCxC9QBh9Pu8XPkv/zCZB2mcMg=="],
"@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.2.1", "", { "os": "linux", "cpu": "x64" }, "sha512-5r1X2FKnCMUPlXTWRYpHdPYUY6a1Ar/t7P24OuiEdEOmms5lyqjDRvVY1yy9Rmioh+AunQ0rWiOTPE8F9A3v5g=="], "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.2.2", "", { "os": "linux", "cpu": "x64" }, "sha512-XW3t3qwbIwiSyRCggeO2zxe3KWaEbM0/kW9e8+0XpBgyKU4ATYzcVSMKteZJ1iukJ3HgHBjbg9P5YPRCVUxlnQ=="],
"@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.2.1", "", { "dependencies": { "@emnapi/core": "^1.8.1", "@emnapi/runtime": "^1.8.1", "@emnapi/wasi-threads": "^1.1.0", "@napi-rs/wasm-runtime": "^1.1.1", "@tybys/wasm-util": "^0.10.1", "tslib": "^2.8.1" }, "cpu": "none" }, "sha512-MGFB5cVPvshR85MTJkEvqDUnuNoysrsRxd6vnk1Lf2tbiqNlXpHYZqkqOQalydienEWOHHFyyuTSYRsLfxFJ2Q=="], "@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.2.2", "", { "dependencies": { "@emnapi/core": "^1.8.1", "@emnapi/runtime": "^1.8.1", "@emnapi/wasi-threads": "^1.1.0", "@napi-rs/wasm-runtime": "^1.1.1", "@tybys/wasm-util": "^0.10.1", "tslib": "^2.8.1" }, "cpu": "none" }, "sha512-eKSztKsmEsn1O5lJ4ZAfyn41NfG7vzCg496YiGtMDV86jz1q/irhms5O0VrY6ZwTUkFy/EKG3RfWgxSI3VbZ8Q=="],
"@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.2.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-YlUEHRHBGnCMh4Nj4GnqQyBtsshUPdiNroZj8VPkvTZSoHsilRCwXcVKnG9kyi0ZFAS/3u+qKHBdDc81SADTRA=="], "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.2.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-qPmaQM4iKu5mxpsrWZMOZRgZv1tOZpUm+zdhhQP0VhJfyGGO3aUKdbh3gDZc/dPLQwW4eSqWGrrcWNBZWUWaXQ=="],
"@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.2.1", "", { "os": "win32", "cpu": "x64" }, "sha512-rbO34G5sMWWyrN/idLeVxAZgAKWrn5LiR3/I90Q9MkA67s6T1oB0xtTe+0heoBvHSpbU9Mk7i6uwJnpo4u21XQ=="], "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.2.2", "", { "os": "win32", "cpu": "x64" }, "sha512-1T/37VvI7WyH66b+vqHj/cLwnCxt7Qt3WFu5Q8hk65aOvlwAhs7rAp1VkulBJw/N4tMirXjVnylTR72uI0HGcA=="],
"@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/postcss": ["@tailwindcss/postcss@4.2.2", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "@tailwindcss/node": "4.2.2", "@tailwindcss/oxide": "4.2.2", "postcss": "^8.5.6", "tailwindcss": "4.2.2" } }, "sha512-n4goKQbW8RVXIbNKRB/45LzyUqN451deQK0nzIeauVEqjlI49slUlgKYJM2QyUzap/PcpnS7kzSUmPb1sCRvYQ=="],
"@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=="], "@tailwindcss/vite": ["@tailwindcss/vite@4.2.2", "", { "dependencies": { "@tailwindcss/node": "4.2.2", "@tailwindcss/oxide": "4.2.2", "tailwindcss": "4.2.2" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7 || ^8" } }, "sha512-mEiF5HO1QqCLXoNEfXVA1Tzo+cYsrqV7w9Juj2wdUFyW07JRenqMG225MvPwr3ZD9N1bFQj46X7r33iHxLUW0w=="],
"@tanstack/table-core": ["@tanstack/table-core@8.21.3", "", {}, "sha512-ldZXEhOBb8Is7xLs01fR3YEc3DERiz5silj8tnGkFZytt1abEvl/GhUmCE0PMLaMPTa3Jk4HbKmRlHmu+gCftg=="], "@tanstack/table-core": ["@tanstack/table-core@8.21.3", "", {}, "sha512-ldZXEhOBb8Is7xLs01fR3YEc3DERiz5silj8tnGkFZytt1abEvl/GhUmCE0PMLaMPTa3Jk4HbKmRlHmu+gCftg=="],
"@tanstack/virtual-core": ["@tanstack/virtual-core@3.13.21", "", {}, "sha512-ww+fmLHyCbPSf7JNbWZP3g7wl6SdNo3ah5Aiw+0e9FDErkVHLKprYUrwTm7dF646FtEkN/KkAKPYezxpmvOjxw=="], "@tanstack/virtual-core": ["@tanstack/virtual-core@3.13.23", "", {}, "sha512-zSz2Z2HNyLjCplANTDyl3BcdQJc2k1+yyFoKhNRmCr7V7dY8o8q5m8uFTI1/Pg1kL+Hgrz6u3Xo6eFUB7l66cg=="],
"@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-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.21", "", { "dependencies": { "@tanstack/virtual-core": "3.13.21" }, "peerDependencies": { "vue": "^2.7.0 || ^3.0.0" } }, "sha512-zneUNdQTcUhoDl6+ek+/O4S9gSZRAc2q7VLscZ4WZnFfZcHc3M7OyVCfSDC3hGuwFqzfL8Cx5bZF6zbGCYwXmw=="], "@tanstack/vue-virtual": ["@tanstack/vue-virtual@3.13.23", "", { "dependencies": { "@tanstack/virtual-core": "3.13.23" }, "peerDependencies": { "vue": "^2.7.0 || ^3.0.0" } }, "sha512-b5jPluAR6U3eOq6GWAYSpj3ugnAIZgGR0e6aGAgyRse0Yu6MVQQ0ZWm9SArSXWtageogn6bkVD8D//c4IjW3xQ=="],
"@tiptap/core": ["@tiptap/core@3.20.1", "", { "peerDependencies": { "@tiptap/pm": "^3.20.1" } }, "sha512-SwkPEWIfaDEZjC8SEIi4kZjqIYUbRgLUHUuQezo5GbphUNC8kM1pi3C3EtoOPtxXrEbY6e4pWEzW54Pcrd+rVA=="], "@tiptap/core": ["@tiptap/core@3.22.0", "", { "peerDependencies": { "@tiptap/pm": "^3.22.0" } }, "sha512-EA/XFbvvz0yRyccqrgOwB9RQe6+uJ8NszjLKH9+3xPE2/+Sa2imax0IqWl7YOXkWihdQVrlpP+EpQF9APKx3jg=="],
"@tiptap/extension-blockquote": ["@tiptap/extension-blockquote@3.20.1", "", { "peerDependencies": { "@tiptap/core": "^3.20.1" } }, "sha512-WzNXk/63PQI2fav4Ta6P0GmYRyu8Gap1pV3VUqaVK829iJ6Zt1T21xayATHEHWMK27VT1GLPJkx9Ycr2jfDyQw=="], "@tiptap/extension-blockquote": ["@tiptap/extension-blockquote@3.22.0", "", { "peerDependencies": { "@tiptap/core": "^3.22.0" } }, "sha512-WF7K1jtEhkhCZFOoei3QrUHMsM6i9eqXw1IuL6cAX3+CBpqVg89KbP/cJp05dYKU0SO0LJkn87biKVqcnAcN7A=="],
"@tiptap/extension-bold": ["@tiptap/extension-bold@3.20.1", "", { "peerDependencies": { "@tiptap/core": "^3.20.1" } }, "sha512-fz++Qv6Rk/Hov0IYG/r7TJ1Y4zWkuGONe0UN5g0KY32NIMg3HeOHicbi4xsNWTm9uAOl3eawWDkezEMrleObMw=="], "@tiptap/extension-bold": ["@tiptap/extension-bold@3.22.0", "", { "peerDependencies": { "@tiptap/core": "^3.22.0" } }, "sha512-mPG1FzOy2DVaJHHuX/eQPIuYie0kqG07M04nElBY8QlV0oYB4/kd0Aubz+m9czqHx/F9u/L98kmMFhCh2DWk2w=="],
"@tiptap/extension-bubble-menu": ["@tiptap/extension-bubble-menu@3.20.1", "", { "dependencies": { "@floating-ui/dom": "^1.0.0" }, "peerDependencies": { "@tiptap/core": "^3.20.1", "@tiptap/pm": "^3.20.1" } }, "sha512-XaPvO6aCoWdFnCBus0s88lnj17NR/OopV79i8Qhgz3WMR0vrsL5zsd45l0lZuu9pSvm5VW47SoxakkJiZC1suw=="], "@tiptap/extension-bubble-menu": ["@tiptap/extension-bubble-menu@3.22.0", "", { "dependencies": { "@floating-ui/dom": "^1.0.0" }, "peerDependencies": { "@tiptap/core": "^3.22.0", "@tiptap/pm": "^3.22.0" } }, "sha512-792CUdP0roO17jQJ+fflSJEWfw2cAric61nV2291a2iL7L/6mNJGP4QFim1FqZzfx3/FwHSXEI3NYT6wdUlh8w=="],
"@tiptap/extension-bullet-list": ["@tiptap/extension-bullet-list@3.20.1", "", { "peerDependencies": { "@tiptap/extension-list": "^3.20.1" } }, "sha512-mbrlvOZo5OF3vLhp+3fk9KuL/6J/wsN0QxF6ZFRAHzQ9NkJdtdfARcBeBnkWXGN8inB6YxbTGY1/E4lmBkOpOw=="], "@tiptap/extension-bullet-list": ["@tiptap/extension-bullet-list@3.22.0", "", { "peerDependencies": { "@tiptap/extension-list": "^3.22.0" } }, "sha512-GUBYUXlNMxfJVpbDQnwNU54rqIBHEIQ7KyPptghSzvmnwMlr6n10OmYSGSVvNpOAIYkHkuhzkwgUDeU+YzWbjg=="],
"@tiptap/extension-code": ["@tiptap/extension-code@3.20.1", "", { "peerDependencies": { "@tiptap/core": "^3.20.1" } }, "sha512-509DHINIA/Gg+eTG7TEkfsS8RUiPLH5xZNyLRT0A1oaoaJmECKfrV6aAm05IdfTyqDqz6LW5pbnX6DdUC4keug=="], "@tiptap/extension-code": ["@tiptap/extension-code@3.22.0", "", { "peerDependencies": { "@tiptap/core": "^3.22.0" } }, "sha512-JGxByyyUdR0yRt1mOxnA2dp6PmI9pr6C846UkZtuOCwhBOBLkoBGkZqW4FytLPOfWGVJpm7w5tx7h1n5uNwfag=="],
"@tiptap/extension-code-block": ["@tiptap/extension-code-block@3.20.1", "", { "peerDependencies": { "@tiptap/core": "^3.20.1", "@tiptap/pm": "^3.20.1" } }, "sha512-vKejwBq+Nlj4Ybd3qOyDxIQKzYymdNH+8eXkKwGShk2nfLJIxq69DCyGvmuHgipIO1qcYPJ149UNpGN+YGcdmA=="], "@tiptap/extension-code-block": ["@tiptap/extension-code-block@3.22.0", "", { "peerDependencies": { "@tiptap/core": "^3.22.0", "@tiptap/pm": "^3.22.0" } }, "sha512-HtnYHj6yHVy2dKs02j5dyEehWQMGOGRMiZBkefY3TwSSNzGESVcFfDV+Xr87j7zDGYvY16vOtVmyRTOTqPn49A=="],
"@tiptap/extension-collaboration": ["@tiptap/extension-collaboration@3.20.1", "", { "peerDependencies": { "@tiptap/core": "^3.20.1", "@tiptap/pm": "^3.20.1", "@tiptap/y-tiptap": "^3.0.2", "yjs": "^13" } }, "sha512-JnwLvyzrutBffHp6YPnf0XyTnhAgqZ9D8JSUKFp0edvai+dxsb+UMlawesBrgAuoQXw3B8YZUo2VFDVdKas1xQ=="], "@tiptap/extension-collaboration": ["@tiptap/extension-collaboration@3.22.0", "", { "peerDependencies": { "@tiptap/core": "^3.22.0", "@tiptap/pm": "^3.22.0", "@tiptap/y-tiptap": "^3.0.2", "yjs": "^13" } }, "sha512-S/nPIth4/Dr5wmxROk4ELWRYhBXxWt7O6cIi8Fca1rYDBdXofjgt4VDO2iCjnOF0LkIekjQudSOFLWadbM2Z+w=="],
"@tiptap/extension-document": ["@tiptap/extension-document@3.20.1", "", { "peerDependencies": { "@tiptap/core": "^3.20.1" } }, "sha512-9vrqdGmRV7bQCSY3NLgu7UhIwgOCDp4sKqMNsoNRX0aZ021QQMTvBQDPkiRkCf7MNsnWrNNnr52PVnULEn3vFQ=="], "@tiptap/extension-document": ["@tiptap/extension-document@3.22.0", "", { "peerDependencies": { "@tiptap/core": "^3.22.0" } }, "sha512-ZdtuBt2KnxIYBtp/VrKiWQ5cLPw1qDKb+sieipBaDWuvhgDNi1kfr//ByEP8xPhcjJfH/C3PCdYYVwIUJwzdqQ=="],
"@tiptap/extension-drag-handle": ["@tiptap/extension-drag-handle@3.20.1", "", { "dependencies": { "@floating-ui/dom": "^1.6.13" }, "peerDependencies": { "@tiptap/core": "^3.20.1", "@tiptap/extension-collaboration": "^3.20.1", "@tiptap/extension-node-range": "^3.20.1", "@tiptap/pm": "^3.20.1", "@tiptap/y-tiptap": "^3.0.2" } }, "sha512-sIfu5Gt1aZVAagvzr+3Qx+8GE294eU3G6/pYjqNHvf71sDCpdN2gFpeI7WF05foVsCGsH6ieu8x/kTXf5GbNZA=="], "@tiptap/extension-drag-handle": ["@tiptap/extension-drag-handle@3.22.0", "", { "dependencies": { "@floating-ui/dom": "^1.6.13" }, "peerDependencies": { "@tiptap/core": "^3.22.0", "@tiptap/extension-collaboration": "^3.22.0", "@tiptap/extension-node-range": "^3.22.0", "@tiptap/pm": "^3.22.0", "@tiptap/y-tiptap": "^3.0.2" } }, "sha512-OdOCqdgprFrvVhbLCCGX1D0hXo/2SL8o1o50Gy0l1lgiH9vqi/B2LjUJZwYYneQa1K0ME1J4dL4Vp89V4jej0A=="],
"@tiptap/extension-drag-handle-vue-3": ["@tiptap/extension-drag-handle-vue-3@3.20.1", "", { "peerDependencies": { "@tiptap/extension-drag-handle": "^3.20.1", "@tiptap/pm": "^3.20.1", "@tiptap/vue-3": "^3.20.1", "vue": "^3.0.0" } }, "sha512-FJonOxcj/I7uBpJBSqfEfU+Fqem5aanEj+EsqG5ZQnxhp7cxbUYZkGH0tBBR7jeIIkt2Jk+1LoCAlYzCWbDLcQ=="], "@tiptap/extension-drag-handle-vue-3": ["@tiptap/extension-drag-handle-vue-3@3.22.0", "", { "peerDependencies": { "@tiptap/extension-drag-handle": "^3.22.0", "@tiptap/pm": "^3.22.0", "@tiptap/vue-3": "^3.22.0", "vue": "^3.0.0" } }, "sha512-QuA3Fxe/jxJAW1LzUazN5FEA0i8UyUQCRQ46IzpA878RI2yzeaOfxTiY26s0oA7uUaoAyPg/pLgVjIdnc1epfA=="],
"@tiptap/extension-dropcursor": ["@tiptap/extension-dropcursor@3.20.1", "", { "peerDependencies": { "@tiptap/extensions": "^3.20.1" } }, "sha512-K18L9FX4znn+ViPSIbTLOGcIaXMx/gLNwAPE8wPLwswbHhQqdiY1zzdBw6drgOc1Hicvebo2dIoUlSXOZsOEcw=="], "@tiptap/extension-dropcursor": ["@tiptap/extension-dropcursor@3.22.0", "", { "peerDependencies": { "@tiptap/extensions": "^3.22.0" } }, "sha512-yI0aMD4szbNdy/dlglPbZ9Ddc2UucRatJSifmtenCLg7YWyIIYton0T6Uym+FXAEUZ6KsaoNqEKiUbK5cRzZMQ=="],
"@tiptap/extension-floating-menu": ["@tiptap/extension-floating-menu@3.20.1", "", { "peerDependencies": { "@floating-ui/dom": "^1.0.0", "@tiptap/core": "^3.20.1", "@tiptap/pm": "^3.20.1" } }, "sha512-BeDC6nfOesIMn5pFuUnkEjOxGv80sOJ8uk1mdt9/3Fkvra8cB9NIYYCVtd6PU8oQFmJ8vFqPrRkUWrG5tbqnOg=="], "@tiptap/extension-floating-menu": ["@tiptap/extension-floating-menu@3.22.0", "", { "peerDependencies": { "@floating-ui/dom": "^1.0.0", "@tiptap/core": "^3.22.0", "@tiptap/pm": "^3.22.0" } }, "sha512-6Gg3I6n+YaCJyvpcKheWiOtU9Oy0M3lbwUGdLK7jTxgAG2YOJxEcx2CzDv3PtNcoyAVUVk9Eio21awVMOECLAQ=="],
"@tiptap/extension-gapcursor": ["@tiptap/extension-gapcursor@3.20.1", "", { "peerDependencies": { "@tiptap/extensions": "^3.20.1" } }, "sha512-kZOtttV6Ai8VUAgEng3h4WKFbtdSNJ6ps7r0cRPY+FctWhVmgNb/JJwwyC+vSilR7nRENAhrA/Cv/RxVlvLw+g=="], "@tiptap/extension-gapcursor": ["@tiptap/extension-gapcursor@3.22.0", "", { "peerDependencies": { "@tiptap/extensions": "^3.22.0" } }, "sha512-VsnaTU88PlA/eG9DtUvuB90z5gVZIaH6T/JVTxGasxR4CFsv0L4Zq5awwr0+SsYH9dKepRMgbanVU03c6k1SuA=="],
"@tiptap/extension-hard-break": ["@tiptap/extension-hard-break@3.20.1", "", { "peerDependencies": { "@tiptap/core": "^3.20.1" } }, "sha512-9sKpmg/IIdlLXimYWUZ3PplIRcehv4Oc7V1miTqlnAthMzjMqigDkjjgte4JZV67RdnDJTQkRw8TklCAU28Emg=="], "@tiptap/extension-hard-break": ["@tiptap/extension-hard-break@3.22.0", "", { "peerDependencies": { "@tiptap/core": "^3.22.0" } }, "sha512-F51pt3fgjbtWrY0Uud+5HoJW4f7w/aBZvmoCk19nrEY955vvuQQ2PD/DZtecl4A8fF50PpRjgilrYnnh99l0ew=="],
"@tiptap/extension-heading": ["@tiptap/extension-heading@3.20.1", "", { "peerDependencies": { "@tiptap/core": "^3.20.1" } }, "sha512-unudyfQP6FxnyWinxvPqe/51DG91J6AaJm666RnAubgYMCgym+33kBftx4j4A6qf+ddWYbD00thMNKOnVLjAEQ=="], "@tiptap/extension-heading": ["@tiptap/extension-heading@3.22.0", "", { "peerDependencies": { "@tiptap/core": "^3.22.0" } }, "sha512-SnOUBXzh9Dft7HY0rqaSL/kZKg4W9wlHfpnFPW8aIuewXvFDLKa6PisqxPpHsXSbG21kfs5E0MLdwdXtNP89XA=="],
"@tiptap/extension-horizontal-rule": ["@tiptap/extension-horizontal-rule@3.20.1", "", { "peerDependencies": { "@tiptap/core": "^3.20.1", "@tiptap/pm": "^3.20.1" } }, "sha512-rjFKFXNntdl0jay8oIGFvvykHlpyQTLmrH3Ag2fj3i8yh6MVvqhtaDomYQbw5sxECd5hBkL+T4n2d2DRuVw/QQ=="], "@tiptap/extension-horizontal-rule": ["@tiptap/extension-horizontal-rule@3.22.0", "", { "peerDependencies": { "@tiptap/core": "^3.22.0", "@tiptap/pm": "^3.22.0" } }, "sha512-9v08PcmJOumVmgGgcuFPZpAk+tf+m7+vaCNsNyf8Ce1i0m3GPSle1ZmxzjDU2FlpaCFrcgoUKlEjKYaFYFCJIg=="],
"@tiptap/extension-image": ["@tiptap/extension-image@3.20.1", "", { "peerDependencies": { "@tiptap/core": "^3.20.1" } }, "sha512-/GPFSLNdYSZQ0E6VBXSAk0UFtDdn98HT1Aa2tO+STELqc5jAdFB42dfFnTC6KQzTvcUOUYkE2S1Q22YC5H2XNQ=="], "@tiptap/extension-image": ["@tiptap/extension-image@3.22.0", "", { "peerDependencies": { "@tiptap/core": "^3.22.0" } }, "sha512-b3n86lWF3thywUrWmZv27EzTGhCBTjifXY/NzPfri3D22cKkxardrJi2WZIc0J65je91oTzqY7SkdQnyIrZksw=="],
"@tiptap/extension-italic": ["@tiptap/extension-italic@3.20.1", "", { "peerDependencies": { "@tiptap/core": "^3.20.1" } }, "sha512-ZYRX13Kt8tR8JOzSXirH3pRpi8x30o7LHxZY58uXBdUvr3tFzOkh03qbN523+diidSVeHP/aMd/+IrplHRkQug=="], "@tiptap/extension-italic": ["@tiptap/extension-italic@3.22.0", "", { "peerDependencies": { "@tiptap/core": "^3.22.0" } }, "sha512-+qq9QZF44O1MRqk6w1AMDZ8oDBs5AtdDdNEcdXpzVU54cJAtWyEPEfXtD0B68hOUp/RdZjMdL27fp+4Id7C1YA=="],
"@tiptap/extension-link": ["@tiptap/extension-link@3.20.1", "", { "dependencies": { "linkifyjs": "^4.3.2" }, "peerDependencies": { "@tiptap/core": "^3.20.1", "@tiptap/pm": "^3.20.1" } }, "sha512-oYTTIgsQMqpkSnJAuAc+UtIKMuI4lv9e1y4LfI1iYm6NkEUHhONppU59smhxHLzb3Ww7YpDffbp5IgDTAiJztA=="], "@tiptap/extension-link": ["@tiptap/extension-link@3.22.0", "", { "dependencies": { "linkifyjs": "^4.3.2" }, "peerDependencies": { "@tiptap/core": "^3.22.0", "@tiptap/pm": "^3.22.0" } }, "sha512-tGMBUAmni532G6R5gnaRvTb6c7+ST1qCHBV0p5kGGzdHaQTDd1R7S8fnuA3M7+6Sruc82iIY+Ur+6Tusvo/vLA=="],
"@tiptap/extension-list": ["@tiptap/extension-list@3.20.1", "", { "peerDependencies": { "@tiptap/core": "^3.20.1", "@tiptap/pm": "^3.20.1" } }, "sha512-euBRAn0mkV7R2VEE+AuOt3R0j9RHEMFXamPFmtvTo8IInxDClusrm6mJoDjS8gCGAXsQCRiAe1SCQBPgGbOOwg=="], "@tiptap/extension-list": ["@tiptap/extension-list@3.22.0", "", { "peerDependencies": { "@tiptap/core": "^3.22.0", "@tiptap/pm": "^3.22.0" } }, "sha512-NfSCAgX44NVLib6aN4HmsP1wi6fFfK3dt6TBb9EgcR82nzq6n7dq7VEBw9V1aKqeXQEtNpqMnQFd0SDayweyfQ=="],
"@tiptap/extension-list-item": ["@tiptap/extension-list-item@3.20.1", "", { "peerDependencies": { "@tiptap/extension-list": "^3.20.1" } }, "sha512-tzgnyTW82lYJkUnadYbatwkI9dLz/OWRSWuFpQPRje/ItmFMWuQ9c9NDD8qLbXPdEYnvrgSAA+ipCD/1G0qA0Q=="], "@tiptap/extension-list-item": ["@tiptap/extension-list-item@3.22.0", "", { "peerDependencies": { "@tiptap/extension-list": "^3.22.0" } }, "sha512-9cFvFLEtf0bnOc/LaGeX2D+c9wOxeqhzgabUl2Ztz8Xzoby4JtamXnrIpb7DG7hZMf3luJMF8bz0HSvAnNiISQ=="],
"@tiptap/extension-list-keymap": ["@tiptap/extension-list-keymap@3.20.1", "", { "peerDependencies": { "@tiptap/extension-list": "^3.20.1" } }, "sha512-Dr0xsQKx0XPOgDg7xqoWwfv7FFwZ3WeF3eOjqh3rDXlNHMj1v+UW5cj1HLphrsAZHTrVTn2C+VWPJkMZrSbpvQ=="], "@tiptap/extension-list-keymap": ["@tiptap/extension-list-keymap@3.22.0", "", { "peerDependencies": { "@tiptap/extension-list": "^3.22.0" } }, "sha512-uCtr5/g+Cwkmsb/VLctgo4VjKm0jv52moAmDyr/TLRjW94gnSLhwXFKzyd7BNIXBQHDyS44UEIJFD3ul4dUKdw=="],
"@tiptap/extension-mention": ["@tiptap/extension-mention@3.20.1", "", { "peerDependencies": { "@tiptap/core": "^3.20.1", "@tiptap/pm": "^3.20.1", "@tiptap/suggestion": "^3.20.1" } }, "sha512-KOGokj7oH1QpcM8P02V+o6wHsVE0g7XEtdIy2vtq2vlFE3npNNNFkMa8F8VWX6qyC+VeVrNU6SIzS5MFY2TORA=="], "@tiptap/extension-mention": ["@tiptap/extension-mention@3.22.0", "", { "peerDependencies": { "@tiptap/core": "^3.22.0", "@tiptap/pm": "^3.22.0", "@tiptap/suggestion": "^3.22.0" } }, "sha512-Ndjn8YTcZMyyEV5cm+3TupRsQ4b8+4gRrZXfsqbEbXN/IGXgl+ObBx+bxBU+aAe2ZRgqEeuVIBIVWBzYaxLxrg=="],
"@tiptap/extension-node-range": ["@tiptap/extension-node-range@3.20.1", "", { "peerDependencies": { "@tiptap/core": "^3.20.1", "@tiptap/pm": "^3.20.1" } }, "sha512-+W/mQJxlkXMcwldWUqwdoR0eniJ1S9cVJoAy2Lkis0NhILZDWVNGKl9J4WFoCOXn8Myr17IllIxRYvAXJJ4FHQ=="], "@tiptap/extension-node-range": ["@tiptap/extension-node-range@3.22.0", "", { "peerDependencies": { "@tiptap/core": "^3.22.0", "@tiptap/pm": "^3.22.0" } }, "sha512-tize9T79ABbOmkw3+7k8cFjSU/JMpzv1JyvFXuGD7lmiHSrFH9Ll8uOGJC7qhCGJWMsjVvrKmBHSOGbzfJI5hQ=="],
"@tiptap/extension-ordered-list": ["@tiptap/extension-ordered-list@3.20.1", "", { "peerDependencies": { "@tiptap/extension-list": "^3.20.1" } }, "sha512-Y+3Ad7OwAdagqdYwCnbqf7/to5ypD4NnUNHA0TXRCs7cAHRA8AdgPoIcGFpaaSpV86oosNU3yfeJouYeroffog=="], "@tiptap/extension-ordered-list": ["@tiptap/extension-ordered-list@3.22.0", "", { "peerDependencies": { "@tiptap/extension-list": "^3.22.0" } }, "sha512-B5JSJ2Xe2KPIYYG7jpZHVeAku/VJB+CCgPYl+qIHjZ4JGTnW23qkIA+6dWk6WljGmhQL1qusxZZn4UAnZtBLeQ=="],
"@tiptap/extension-paragraph": ["@tiptap/extension-paragraph@3.20.1", "", { "peerDependencies": { "@tiptap/core": "^3.20.1" } }, "sha512-QFrAtXNyv7JSnomMQc1nx5AnG9mMznfbYJAbdOQYVdbLtAzTfiTuNPNbQrufy5ZGtGaHxDCoaygu2QEfzaKG+Q=="], "@tiptap/extension-paragraph": ["@tiptap/extension-paragraph@3.22.0", "", { "peerDependencies": { "@tiptap/core": "^3.22.0" } }, "sha512-fwkPvbGI3xvzWrTJVGZVocgA99Pgqd5kW7iv7MEWlI9uOUa6Ifu31/seHV7j+QDW3y3mADcx+zyhxcMVELtLjA=="],
"@tiptap/extension-placeholder": ["@tiptap/extension-placeholder@3.20.1", "", { "peerDependencies": { "@tiptap/extensions": "^3.20.1" } }, "sha512-k+jfbCugYGuIFBdojukgEopGazIMOgHrw46FnyN2X/6ICOIjQP2rh2ObslrsUOsJYoEevxCsNF9hZl1HvWX66g=="], "@tiptap/extension-placeholder": ["@tiptap/extension-placeholder@3.22.0", "", { "peerDependencies": { "@tiptap/extensions": "^3.22.0" } }, "sha512-1GCc87DIPG3jgLH1Xg3ZW11LEzEWnpinLBIZ8RQqrEH0UwtRUW3QsbvwnKKgbbAEtA91GAdnc1eUQ0dpn+tSeQ=="],
"@tiptap/extension-strike": ["@tiptap/extension-strike@3.20.1", "", { "peerDependencies": { "@tiptap/core": "^3.20.1" } }, "sha512-EYgyma10lpsY+rwbVQL9u+gA7hBlKLSMFH7Zgd37FSxukOjr+HE8iKPQQ+SwbGejyDsPlLT8Z5Jnuxo5Ng90Pg=="], "@tiptap/extension-strike": ["@tiptap/extension-strike@3.22.0", "", { "peerDependencies": { "@tiptap/core": "^3.22.0" } }, "sha512-gCgFr1sIcqrJeV5Gdrh8KVZHA+0B1FpFBuOi6FzMyVfBB2sBBqKnjoInYTkPXXdP49Qu8L8hi4luFQtoj4zGzA=="],
"@tiptap/extension-text": ["@tiptap/extension-text@3.20.1", "", { "peerDependencies": { "@tiptap/core": "^3.20.1" } }, "sha512-7PlIbYW8UenV6NPOXHmv8IcmPGlGx6HFq66RmkJAOJRPXPkTLAiX0N8rQtzUJ6jDEHqoJpaHFEHJw0xzW1yF+A=="], "@tiptap/extension-text": ["@tiptap/extension-text@3.22.0", "", { "peerDependencies": { "@tiptap/core": "^3.22.0" } }, "sha512-FQ3lBRswZbSEbtxOnDF4T7pdsZRmKh/8q+M29zXaDHGfBc6nuGNPlNKSIy0Iryjhf/YmMVaWDpHvzk56KD7QtA=="],
"@tiptap/extension-underline": ["@tiptap/extension-underline@3.20.1", "", { "peerDependencies": { "@tiptap/core": "^3.20.1" } }, "sha512-fmHvDKzwCgnZUwRreq8tYkb1YyEwgzZ6QQkAQ0CsCRtvRMqzerr3Duz0Als4i8voZTuGDEL3VR6nAJbLAb/wPg=="], "@tiptap/extension-underline": ["@tiptap/extension-underline@3.22.0", "", { "peerDependencies": { "@tiptap/core": "^3.22.0" } }, "sha512-AxQOnXQwYmZNjagkEoCZZqbpJbLVmBcu1ivJ9dE0SAQsr1wRUp7mAg+g1SqhbMAvrXvv7yhhNevSdQKmXsnFyA=="],
"@tiptap/extensions": ["@tiptap/extensions@3.20.1", "", { "peerDependencies": { "@tiptap/core": "^3.20.1", "@tiptap/pm": "^3.20.1" } }, "sha512-JRc/v+OBH0qLTdvQ7HvHWTxGJH73QOf1MC0R8NhOX2QnAbg2mPFv1h+FjGa2gfLGuCXBdWQomjekWkUKbC4e5A=="], "@tiptap/extensions": ["@tiptap/extensions@3.22.0", "", { "peerDependencies": { "@tiptap/core": "^3.22.0", "@tiptap/pm": "^3.22.0" } }, "sha512-En8p1FiFBT3V9CduErCyLPFxDRsYLISb2cCtLKTeYVeCRn2vQZK4B8WuOgHI4IBipz3I3XidmDhra4yt8mmi/w=="],
"@tiptap/markdown": ["@tiptap/markdown@3.20.1", "", { "dependencies": { "marked": "^17.0.1" }, "peerDependencies": { "@tiptap/core": "^3.20.1", "@tiptap/pm": "^3.20.1" } }, "sha512-dNrtP7kmabDomgjv9G/6+JSFL6WraPaFbmKh1eHSYKdDGvIwBfJnVPTV2VS3bP1OuYJEDJN/2ydtiCHyOTrQsQ=="], "@tiptap/markdown": ["@tiptap/markdown@3.22.0", "", { "dependencies": { "marked": "^17.0.1" }, "peerDependencies": { "@tiptap/core": "^3.22.0", "@tiptap/pm": "^3.22.0" } }, "sha512-lvYqID2ui4+HEgh/zULQJoKfb7ILe6ahB9IeaJ5R48mrVyeudciqoJV6VmNOaucm/UpKExlDC3I+QlA9sLpYPw=="],
"@tiptap/pm": ["@tiptap/pm@3.20.1", "", { "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-6kCiGLvpES4AxcEuOhb7HR7/xIeJWMjZlb6J7e8zpiIh5BoQc7NoRdctsnmFEjZvC19bIasccshHQ7H2zchWqw=="], "@tiptap/pm": ["@tiptap/pm@3.22.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-O9kpzNnFX5837kFevwAM8yr7ImLHu8noIwIpoci0AwfJjiBMzfZBejhbzxnKEfTpFWnkvZ8rWohlb6CQdJ6Crg=="],
"@tiptap/starter-kit": ["@tiptap/starter-kit@3.20.1", "", { "dependencies": { "@tiptap/core": "^3.20.1", "@tiptap/extension-blockquote": "^3.20.1", "@tiptap/extension-bold": "^3.20.1", "@tiptap/extension-bullet-list": "^3.20.1", "@tiptap/extension-code": "^3.20.1", "@tiptap/extension-code-block": "^3.20.1", "@tiptap/extension-document": "^3.20.1", "@tiptap/extension-dropcursor": "^3.20.1", "@tiptap/extension-gapcursor": "^3.20.1", "@tiptap/extension-hard-break": "^3.20.1", "@tiptap/extension-heading": "^3.20.1", "@tiptap/extension-horizontal-rule": "^3.20.1", "@tiptap/extension-italic": "^3.20.1", "@tiptap/extension-link": "^3.20.1", "@tiptap/extension-list": "^3.20.1", "@tiptap/extension-list-item": "^3.20.1", "@tiptap/extension-list-keymap": "^3.20.1", "@tiptap/extension-ordered-list": "^3.20.1", "@tiptap/extension-paragraph": "^3.20.1", "@tiptap/extension-strike": "^3.20.1", "@tiptap/extension-text": "^3.20.1", "@tiptap/extension-underline": "^3.20.1", "@tiptap/extensions": "^3.20.1", "@tiptap/pm": "^3.20.1" } }, "sha512-opqWxL/4OTEiqmVC0wsU4o3JhAf6LycJ2G/gRIZVAIFLljI9uHfpPMTFGxZ5w9IVVJaP5PJysfwW/635kKqkrw=="], "@tiptap/starter-kit": ["@tiptap/starter-kit@3.22.0", "", { "dependencies": { "@tiptap/core": "^3.22.0", "@tiptap/extension-blockquote": "^3.22.0", "@tiptap/extension-bold": "^3.22.0", "@tiptap/extension-bullet-list": "^3.22.0", "@tiptap/extension-code": "^3.22.0", "@tiptap/extension-code-block": "^3.22.0", "@tiptap/extension-document": "^3.22.0", "@tiptap/extension-dropcursor": "^3.22.0", "@tiptap/extension-gapcursor": "^3.22.0", "@tiptap/extension-hard-break": "^3.22.0", "@tiptap/extension-heading": "^3.22.0", "@tiptap/extension-horizontal-rule": "^3.22.0", "@tiptap/extension-italic": "^3.22.0", "@tiptap/extension-link": "^3.22.0", "@tiptap/extension-list": "^3.22.0", "@tiptap/extension-list-item": "^3.22.0", "@tiptap/extension-list-keymap": "^3.22.0", "@tiptap/extension-ordered-list": "^3.22.0", "@tiptap/extension-paragraph": "^3.22.0", "@tiptap/extension-strike": "^3.22.0", "@tiptap/extension-text": "^3.22.0", "@tiptap/extension-underline": "^3.22.0", "@tiptap/extensions": "^3.22.0", "@tiptap/pm": "^3.22.0" } }, "sha512-3V0RysviBKbsvzHuupM30ftb/WLogSgINtIbmGQHZK4Ta1YzwzW63nAWPGKOoFw8r1HRFFO6LjtqrT6iJsPnzQ=="],
"@tiptap/suggestion": ["@tiptap/suggestion@3.20.1", "", { "peerDependencies": { "@tiptap/core": "^3.20.1", "@tiptap/pm": "^3.20.1" } }, "sha512-ng7olbzgZhWvPJVJygNQK5153CjquR2eJXpkLq7bRjHlahvt4TH4tGFYvGdYZcXuzbe2g9RoqT7NaPGL9CUq9w=="], "@tiptap/suggestion": ["@tiptap/suggestion@3.22.0", "", { "peerDependencies": { "@tiptap/core": "^3.22.0", "@tiptap/pm": "^3.22.0" } }, "sha512-qdZ4v7sXkM4p2+M7OWCGVoNmHmiaHhiGQN3lUzaNG7mG1gZ2SDirSaeOfL8lHxpnPlGzmE4tUe5eCWMpNKHypA=="],
"@tiptap/vue-3": ["@tiptap/vue-3@3.20.1", "", { "optionalDependencies": { "@tiptap/extension-bubble-menu": "^3.20.1", "@tiptap/extension-floating-menu": "^3.20.1" }, "peerDependencies": { "@floating-ui/dom": "^1.0.0", "@tiptap/core": "^3.20.1", "@tiptap/pm": "^3.20.1", "vue": "^3.0.0" } }, "sha512-06IsNzIkAC0HPHNSdXf4AgtExnr02BHBLXmUiNrjjhgHdxXDKIB0Yq3hEWEfbWNPr3lr7jK6EaFu2IKFMhoUtQ=="], "@tiptap/vue-3": ["@tiptap/vue-3@3.22.0", "", { "optionalDependencies": { "@tiptap/extension-bubble-menu": "^3.22.0", "@tiptap/extension-floating-menu": "^3.22.0" }, "peerDependencies": { "@floating-ui/dom": "^1.0.0", "@tiptap/core": "^3.22.0", "@tiptap/pm": "^3.22.0", "vue": "^3.0.0" } }, "sha512-CVThMRtCAYmz+N5RtnxRAjfPZwavdqi0RqjznF3M0Tc5UVrwfJG5YYtE7uzySmzuxW7VrkjTUC+Zx9YM2hQEXQ=="],
"@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=="], "@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=="],
@@ -474,29 +474,29 @@
"@types/web-bluetooth": ["@types/web-bluetooth@0.0.21", "", {}, "sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA=="], "@types/web-bluetooth": ["@types/web-bluetooth@0.0.21", "", {}, "sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA=="],
"@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.57.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.12.2", "@typescript-eslint/scope-manager": "8.57.0", "@typescript-eslint/type-utils": "8.57.0", "@typescript-eslint/utils": "8.57.0", "@typescript-eslint/visitor-keys": "8.57.0", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.57.0", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-qeu4rTHR3/IaFORbD16gmjq9+rEs9fGKdX0kF6BKSfi+gCuG3RCKLlSBYzn/bGsY9Tj7KE/DAQStbp8AHJGHEQ=="], "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.58.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.12.2", "@typescript-eslint/scope-manager": "8.58.0", "@typescript-eslint/type-utils": "8.58.0", "@typescript-eslint/utils": "8.58.0", "@typescript-eslint/visitor-keys": "8.58.0", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.58.0", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-RLkVSiNuUP1C2ROIWfqX+YcUfLaSnxGE/8M+Y57lopVwg9VTYYfhuz15Yf1IzCKgZj6/rIbYTmJCUSqr76r0Wg=="],
"@typescript-eslint/parser": ["@typescript-eslint/parser@8.57.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.57.0", "@typescript-eslint/types": "8.57.0", "@typescript-eslint/typescript-estree": "8.57.0", "@typescript-eslint/visitor-keys": "8.57.0", "debug": "^4.4.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-XZzOmihLIr8AD1b9hL9ccNMzEMWt/dE2u7NyTY9jJG6YNiNthaD5XtUHVF2uCXZ15ng+z2hT3MVuxnUYhq6k1g=="], "@typescript-eslint/parser": ["@typescript-eslint/parser@8.58.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.58.0", "@typescript-eslint/types": "8.58.0", "@typescript-eslint/typescript-estree": "8.58.0", "@typescript-eslint/visitor-keys": "8.58.0", "debug": "^4.4.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-rLoGZIf9afaRBYsPUMtvkDWykwXwUPL60HebR4JgTI8mxfFe2cQTu3AGitANp4b9B2QlVru6WzjgB2IzJKiCSA=="],
"@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.57.0", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.57.0", "@typescript-eslint/types": "^8.57.0", "debug": "^4.4.3" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-pR+dK0BlxCLxtWfaKQWtYr7MhKmzqZxuii+ZjuFlZlIGRZm22HnXFqa2eY+90MUz8/i80YJmzFGDUsi8dMOV5w=="], "@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.58.0", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.58.0", "@typescript-eslint/types": "^8.58.0", "debug": "^4.4.3" }, "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-8Q/wBPWLQP1j16NxoPNIKpDZFMaxl7yWIoqXWYeWO+Bbd2mjgvoF0dxP2jKZg5+x49rgKdf7Ck473M8PC3V9lg=="],
"@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.57.0", "", { "dependencies": { "@typescript-eslint/types": "8.57.0", "@typescript-eslint/visitor-keys": "8.57.0" } }, "sha512-nvExQqAHF01lUM66MskSaZulpPL5pgy5hI5RfrxviLgzZVffB5yYzw27uK/ft8QnKXI2X0LBrHJFr1TaZtAibw=="], "@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.58.0", "", { "dependencies": { "@typescript-eslint/types": "8.58.0", "@typescript-eslint/visitor-keys": "8.58.0" } }, "sha512-W1Lur1oF50FxSnNdGp3Vs6P+yBRSmZiw4IIjEeYxd8UQJwhUF0gDgDD/W/Tgmh73mxgEU3qX0Bzdl/NGuSPEpQ=="],
"@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.57.0", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-LtXRihc5ytjJIQEH+xqjB0+YgsV4/tW35XKX3GTZHpWtcC8SPkT/d4tqdf1cKtesryHm2bgp6l555NYcT2NLvA=="], "@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.58.0", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-doNSZEVJsWEu4htiVC+PR6NpM+pa+a4ClH9INRWOWCUzMst/VA9c4gXq92F8GUD1rwhNvRLkgjfYtFXegXQF7A=="],
"@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.57.0", "", { "dependencies": { "@typescript-eslint/types": "8.57.0", "@typescript-eslint/typescript-estree": "8.57.0", "@typescript-eslint/utils": "8.57.0", "debug": "^4.4.3", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-yjgh7gmDcJ1+TcEg8x3uWQmn8ifvSupnPfjP21twPKrDP/pTHlEQgmKcitzF/rzPSmv7QjJ90vRpN4U+zoUjwQ=="], "@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.58.0", "", { "dependencies": { "@typescript-eslint/types": "8.58.0", "@typescript-eslint/typescript-estree": "8.58.0", "@typescript-eslint/utils": "8.58.0", "debug": "^4.4.3", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-aGsCQImkDIqMyx1u4PrVlbi/krmDsQUs4zAcCV6M7yPcPev+RqVlndsJy9kJ8TLihW9TZ0kbDAzctpLn5o+lOg=="],
"@typescript-eslint/types": ["@typescript-eslint/types@8.57.0", "", {}, "sha512-dTLI8PEXhjUC7B9Kre+u0XznO696BhXcTlOn0/6kf1fHaQW8+VjJAVHJ3eTI14ZapTxdkOmc80HblPQLaEeJdg=="], "@typescript-eslint/types": ["@typescript-eslint/types@8.58.0", "", {}, "sha512-O9CjxypDT89fbHxRfETNoAnHj/i6IpRK0CvbVN3qibxlLdo5p5hcLmUuCCrHMpxiWSwKyI8mCP7qRNYuOJ0Uww=="],
"@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.57.0", "", { "dependencies": { "@typescript-eslint/project-service": "8.57.0", "@typescript-eslint/tsconfig-utils": "8.57.0", "@typescript-eslint/types": "8.57.0", "@typescript-eslint/visitor-keys": "8.57.0", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-m7faHcyVg0BT3VdYTlX8GdJEM7COexXxS6KqGopxdtkQRvBanK377QDHr4W/vIPAR+ah9+B/RclSW5ldVniO1Q=="], "@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.58.0", "", { "dependencies": { "@typescript-eslint/project-service": "8.58.0", "@typescript-eslint/tsconfig-utils": "8.58.0", "@typescript-eslint/types": "8.58.0", "@typescript-eslint/visitor-keys": "8.58.0", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-7vv5UWbHqew/dvs+D3e1RvLv1v2eeZ9txRHPnEEBUgSNLx5ghdzjHa0sgLWYVKssH+lYmV0JaWdoubo0ncGYLA=="],
"@typescript-eslint/utils": ["@typescript-eslint/utils@8.57.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/scope-manager": "8.57.0", "@typescript-eslint/types": "8.57.0", "@typescript-eslint/typescript-estree": "8.57.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-5iIHvpD3CZe06riAsbNxxreP+MuYgVUsV0n4bwLH//VJmgtt54sQeY2GszntJ4BjYCpMzrfVh2SBnUQTtys2lQ=="], "@typescript-eslint/utils": ["@typescript-eslint/utils@8.58.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/scope-manager": "8.58.0", "@typescript-eslint/types": "8.58.0", "@typescript-eslint/typescript-estree": "8.58.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-RfeSqcFeHMHlAWzt4TBjWOAtoW9lnsAGiP3GbaX9uVgTYYrMbVnGONEfUCiSss+xMHFl+eHZiipmA8WkQ7FuNA=="],
"@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.57.0", "", { "dependencies": { "@typescript-eslint/types": "8.57.0", "eslint-visitor-keys": "^5.0.0" } }, "sha512-zm6xx8UT/Xy2oSr2ZXD0pZo7Jx2XsCoID2IUh9YSTFRu7z+WdwYTRk6LhUftm1crwqbuoF6I8zAFeCMw0YjwDg=="], "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.58.0", "", { "dependencies": { "@typescript-eslint/types": "8.58.0", "eslint-visitor-keys": "^5.0.0" } }, "sha512-XJ9UD9+bbDo4a4epraTwG3TsNPeiB9aShrUneAVXy8q4LuwowN+qu89/6ByLMINqvIMeI9H9hOHQtg/ijrYXzQ=="],
"@unhead/vue": ["@unhead/vue@2.1.12", "", { "dependencies": { "hookable": "^6.0.1", "unhead": "2.1.12" }, "peerDependencies": { "vue": ">=3.5.18" } }, "sha512-zEWqg0nZM8acpuTZE40wkeUl8AhIe0tU0OkilVi1D4fmVjACrwoh5HP6aNqJ8kUnKsoy6D+R3Vi/O+fmdNGO7g=="], "@unhead/vue": ["@unhead/vue@2.1.12", "", { "dependencies": { "hookable": "^6.0.1", "unhead": "2.1.12" }, "peerDependencies": { "vue": ">=3.5.18" } }, "sha512-zEWqg0nZM8acpuTZE40wkeUl8AhIe0tU0OkilVi1D4fmVjACrwoh5HP6aNqJ8kUnKsoy6D+R3Vi/O+fmdNGO7g=="],
"@vitejs/plugin-vue": ["@vitejs/plugin-vue@6.0.4", "", { "dependencies": { "@rolldown/pluginutils": "1.0.0-rc.2" }, "peerDependencies": { "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0", "vue": "^3.2.25" } }, "sha512-uM5iXipgYIn13UUQCZNdWkYk+sysBeA97d5mHsAoAt1u/wpN3+zxOmsVJWosuzX+IMGRzeYUNytztrYznboIkQ=="], "@vitejs/plugin-vue": ["@vitejs/plugin-vue@6.0.5", "", { "dependencies": { "@rolldown/pluginutils": "1.0.0-rc.2" }, "peerDependencies": { "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0", "vue": "^3.2.25" } }, "sha512-bL3AxKuQySfk1iGcBsQnoRVexTPJq0Z/ixFVM8OhVJAP6ZXXXLtM7NFKWhLl30Kg7uTBqIaPXbh+nuQCuBDedg=="],
"@volar/language-core": ["@volar/language-core@2.4.28", "", { "dependencies": { "@volar/source-map": "2.4.28" } }, "sha512-w4qhIJ8ZSitgLAkVay6AbcnC7gP3glYM3fYwKV3srj8m494E3xtrCv6E+bWviiK/8hs6e6t1ij1s2Endql7vzQ=="], "@volar/language-core": ["@volar/language-core@2.4.28", "", { "dependencies": { "@volar/source-map": "2.4.28" } }, "sha512-w4qhIJ8ZSitgLAkVay6AbcnC7gP3glYM3fYwKV3srj8m494E3xtrCv6E+bWviiK/8hs6e6t1ij1s2Endql7vzQ=="],
@@ -512,39 +512,39 @@
"@vue/babel-plugin-resolve-type": ["@vue/babel-plugin-resolve-type@1.5.0", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/helper-module-imports": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1", "@babel/parser": "^7.28.0", "@vue/compiler-sfc": "^3.5.18" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-Wm/60o+53JwJODm4Knz47dxJnLDJ9FnKnGZJbUUf8nQRAtt6P+undLUAVU3Ha33LxOJe6IPoifRQ6F/0RrU31w=="], "@vue/babel-plugin-resolve-type": ["@vue/babel-plugin-resolve-type@1.5.0", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/helper-module-imports": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1", "@babel/parser": "^7.28.0", "@vue/compiler-sfc": "^3.5.18" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-Wm/60o+53JwJODm4Knz47dxJnLDJ9FnKnGZJbUUf8nQRAtt6P+undLUAVU3Ha33LxOJe6IPoifRQ6F/0RrU31w=="],
"@vue/compiler-core": ["@vue/compiler-core@3.6.0-beta.7", "", { "dependencies": { "@babel/parser": "^7.29.0", "@vue/shared": "3.6.0-beta.7", "entities": "^7.0.1", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" } }, "sha512-fYOlA9PCFuf5l8AtKJSxAM+K+MLoM8dyN9Xsa/GDE0Uf6wxFe33AEU6xyVqYMPy6uxp4RtgE4KuQ7JdYvE0Jnw=="], "@vue/compiler-core": ["@vue/compiler-core@3.6.0-beta.9", "", { "dependencies": { "@babel/parser": "^7.29.2", "@vue/shared": "3.6.0-beta.9", "entities": "^7.0.1", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" } }, "sha512-/qlfk4cbU//tgdwWeAbl4kBvH5ZocjJ6CMGigzRKk1So/0jl6kRpFxVbc6oNsYYH6Xj28ByA+msAes1FjExejg=="],
"@vue/compiler-dom": ["@vue/compiler-dom@3.6.0-beta.7", "", { "dependencies": { "@vue/compiler-core": "3.6.0-beta.7", "@vue/shared": "3.6.0-beta.7" } }, "sha512-4myAKKPogd6MAA/oKKn1+uDOPGJ6gB+pivaUxaBqfvXJch1QHSpcUgdHCJqDb9O/99EjcwppkTLGe7cRWMSqHQ=="], "@vue/compiler-dom": ["@vue/compiler-dom@3.6.0-beta.9", "", { "dependencies": { "@vue/compiler-core": "3.6.0-beta.9", "@vue/shared": "3.6.0-beta.9" } }, "sha512-Bh2h+Co2Vl85jhNjR+yBU1oIujqQ3XIhF7FzODBStvp8yn2WvsXmm5QlvnE7q+smJkfglTbfFcaou8fjR0Y4ag=="],
"@vue/compiler-sfc": ["@vue/compiler-sfc@3.6.0-beta.7", "", { "dependencies": { "@babel/parser": "^7.29.0", "@vue/compiler-core": "3.6.0-beta.7", "@vue/compiler-dom": "3.6.0-beta.7", "@vue/compiler-ssr": "3.6.0-beta.7", "@vue/compiler-vapor": "3.6.0-beta.7", "@vue/shared": "3.6.0-beta.7", "estree-walker": "^2.0.2", "magic-string": "^0.30.21", "postcss": "^8.5.6", "source-map-js": "^1.2.1" } }, "sha512-bf0NkJb0Hxoj1K8cZKYgduEY0FPP28WnoUGqHvYA/a1cjJfrhaypbiS5u6pEFVXug/JZJF9sI+c4JTHkgqRKDw=="], "@vue/compiler-sfc": ["@vue/compiler-sfc@3.6.0-beta.9", "", { "dependencies": { "@babel/parser": "^7.29.2", "@vue/compiler-core": "3.6.0-beta.9", "@vue/compiler-dom": "3.6.0-beta.9", "@vue/compiler-ssr": "3.6.0-beta.9", "@vue/compiler-vapor": "3.6.0-beta.9", "@vue/shared": "3.6.0-beta.9", "estree-walker": "^2.0.2", "magic-string": "^0.30.21", "postcss": "^8.5.8", "source-map-js": "^1.2.1" } }, "sha512-13MwFqrIN+AFxR5uT88WYaMTxXycpNNI75sIllz5blXbFaBsDfiegQZj10Pj5G3cgH7WFy2Trs1rTlXrmSgG/w=="],
"@vue/compiler-ssr": ["@vue/compiler-ssr@3.6.0-beta.7", "", { "dependencies": { "@vue/compiler-dom": "3.6.0-beta.7", "@vue/shared": "3.6.0-beta.7" } }, "sha512-fwO4qe6iXZ+SZedurh/vLwjBgjAX/wL5aI/eIECv0C79AgTAlJNjD6dwMafukhnZamQgNKYBxXUOkGl8sqn1PA=="], "@vue/compiler-ssr": ["@vue/compiler-ssr@3.6.0-beta.9", "", { "dependencies": { "@vue/compiler-dom": "3.6.0-beta.9", "@vue/shared": "3.6.0-beta.9" } }, "sha512-ufzHVMJoMz7QzMFop03a8KpWAWUh4hiof/tJXvxiDClIFUY7P5OrKk644fV9qlWgTP7V+eQvA7kVSEVRUgYjvA=="],
"@vue/compiler-vapor": ["@vue/compiler-vapor@3.6.0-beta.7", "", { "dependencies": { "@babel/parser": "^7.29.0", "@vue/compiler-dom": "3.6.0-beta.7", "@vue/shared": "3.6.0-beta.7", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" } }, "sha512-fc6ZAFv9yRSoQQVsgN5gGDaUWdTPctDb6lVUtUGgnrCB7n+hQIBwuId7HIeic3PK6HPVZMoEbluNU/ge3DIveA=="], "@vue/compiler-vapor": ["@vue/compiler-vapor@3.6.0-beta.9", "", { "dependencies": { "@babel/parser": "^7.29.2", "@vue/compiler-dom": "3.6.0-beta.9", "@vue/shared": "3.6.0-beta.9", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" } }, "sha512-1WWCcRjkH/scYJ8dsjNp8+NDf4f9SwQgOcd4Hbl5p0srNPflqrQYvAaJh4N2Szxj5FdkXw2t5uiK35TpFM5uKA=="],
"@vue/devtools-api": ["@vue/devtools-api@7.7.9", "", { "dependencies": { "@vue/devtools-kit": "^7.7.9" } }, "sha512-kIE8wvwlcZ6TJTbNeU2HQNtaxLx3a84aotTITUuL/4bzfPxzajGBOoqjMhwZJ8L9qFYDU/lAYMEEm11dnZOD6g=="], "@vue/devtools-api": ["@vue/devtools-api@7.7.9", "", { "dependencies": { "@vue/devtools-kit": "^7.7.9" } }, "sha512-kIE8wvwlcZ6TJTbNeU2HQNtaxLx3a84aotTITUuL/4bzfPxzajGBOoqjMhwZJ8L9qFYDU/lAYMEEm11dnZOD6g=="],
"@vue/devtools-core": ["@vue/devtools-core@8.0.7", "", { "dependencies": { "@vue/devtools-kit": "^8.0.7", "@vue/devtools-shared": "^8.0.7" }, "peerDependencies": { "vue": "^3.0.0" } }, "sha512-PmpiPxvg3Of80ODHVvyckxwEW1Z02VIAvARIZS1xegINn3VuNQLm9iHUmKD+o6cLkMNWV8OG8x7zo0kgydZgdg=="], "@vue/devtools-core": ["@vue/devtools-core@8.1.1", "", { "dependencies": { "@vue/devtools-kit": "^8.1.1", "@vue/devtools-shared": "^8.1.1" }, "peerDependencies": { "vue": "^3.0.0" } }, "sha512-bCCsSABp1/ot4j8xJEycM6Mtt2wbuucfByr6hMgjbYhrtlscOJypZKvy8f1FyWLYrLTchB5Qz216Lm92wfbq0A=="],
"@vue/devtools-kit": ["@vue/devtools-kit@8.0.7", "", { "dependencies": { "@vue/devtools-shared": "^8.0.7", "birpc": "^2.6.1", "hookable": "^5.5.3", "perfect-debounce": "^2.0.0" } }, "sha512-H6esJGHGl5q0E9iV3m2EoBQHJ+V83WMW83A0/+Fn95eZ2iIvdsq4+UCS6yT/Fdd4cGZSchx/MdWDreM3WqMsDw=="], "@vue/devtools-kit": ["@vue/devtools-kit@8.1.1", "", { "dependencies": { "@vue/devtools-shared": "^8.1.1", "birpc": "^2.6.1", "hookable": "^5.5.3", "perfect-debounce": "^2.0.0" } }, "sha512-gVBaBv++i+adg4JpH71k9ppl4soyR7Y2McEqO5YNgv0BI1kMZ7BDX5gnwkZ5COYgiCyhejZG+yGNrBAjj6Coqg=="],
"@vue/devtools-shared": ["@vue/devtools-shared@8.0.7", "", {}, "sha512-CgAb9oJH5NUmbQRdYDj/1zMiaICYSLtm+B1kxcP72LBrifGAjUmt8bx52dDH1gWRPlQgxGPqpAMKavzVirAEhA=="], "@vue/devtools-shared": ["@vue/devtools-shared@8.1.1", "", {}, "sha512-+h4ttmJYl/txpxHKaoZcaKpC+pvckgLzIDiSQlaQ7kKthKh8KuwoLW2D8hPJEnqKzXOvu15UHEoGyngAXCz0EQ=="],
"@vue/eslint-config-typescript": ["@vue/eslint-config-typescript@14.7.0", "", { "dependencies": { "@typescript-eslint/utils": "^8.56.0", "fast-glob": "^3.3.3", "typescript-eslint": "^8.56.0", "vue-eslint-parser": "^10.4.0" }, "peerDependencies": { "eslint": "^9.10.0 || ^10.0.0", "eslint-plugin-vue": "^9.28.0 || ^10.0.0", "typescript": ">=4.8.4" }, "optionalPeers": ["typescript"] }, "sha512-iegbMINVc+seZ/QxtzWiOBozctrHiF2WvGedruu2EbLujg9VuU0FQiNcN2z1ycuaoKKpF4m2qzB5HDEMKbxtIg=="], "@vue/eslint-config-typescript": ["@vue/eslint-config-typescript@14.7.0", "", { "dependencies": { "@typescript-eslint/utils": "^8.56.0", "fast-glob": "^3.3.3", "typescript-eslint": "^8.56.0", "vue-eslint-parser": "^10.4.0" }, "peerDependencies": { "eslint": "^9.10.0 || ^10.0.0", "eslint-plugin-vue": "^9.28.0 || ^10.0.0", "typescript": ">=4.8.4" }, "optionalPeers": ["typescript"] }, "sha512-iegbMINVc+seZ/QxtzWiOBozctrHiF2WvGedruu2EbLujg9VuU0FQiNcN2z1ycuaoKKpF4m2qzB5HDEMKbxtIg=="],
"@vue/language-core": ["@vue/language-core@3.2.5", "", { "dependencies": { "@volar/language-core": "2.4.28", "@vue/compiler-dom": "^3.5.0", "@vue/shared": "^3.5.0", "alien-signals": "^3.0.0", "muggle-string": "^0.4.1", "path-browserify": "^1.0.1", "picomatch": "^4.0.2" } }, "sha512-d3OIxN/+KRedeM5wQ6H6NIpwS3P5gC9nmyaHgBk+rO6dIsjY+tOh4UlPpiZbAh3YtLdCGEX4M16RmsBqPmJV+g=="], "@vue/language-core": ["@vue/language-core@3.2.6", "", { "dependencies": { "@volar/language-core": "2.4.28", "@vue/compiler-dom": "^3.5.0", "@vue/shared": "^3.5.0", "alien-signals": "^3.0.0", "muggle-string": "^0.4.1", "path-browserify": "^1.0.1", "picomatch": "^4.0.2" } }, "sha512-xYYYX3/aVup576tP/23sEUpgiEnujrENaoNRbaozC1/MA9I6EGFQRJb4xrt/MmUCAGlxTKL2RmT8JLTPqagCkg=="],
"@vue/reactivity": ["@vue/reactivity@3.6.0-beta.7", "", { "dependencies": { "@vue/shared": "3.6.0-beta.7" } }, "sha512-d1Z7U3QqfrpnT1/m8glRDBGlzQVj4J3u7LvCxieyxvrH03ZcR9qZ8hMw9x/Q3K/VcT2BSHp8A8662cqigCiCEA=="], "@vue/reactivity": ["@vue/reactivity@3.6.0-beta.9", "", { "dependencies": { "@vue/shared": "3.6.0-beta.9" } }, "sha512-Mo0GjZB/q7LUJplPjBdKqAAk4mawkzvnk2nNf9CEetp4RQJP5/e/DdmgjYBsr/+UVX5ShasCalwdyknuuIepdw=="],
"@vue/runtime-core": ["@vue/runtime-core@3.6.0-beta.7", "", { "dependencies": { "@vue/reactivity": "3.6.0-beta.7", "@vue/shared": "3.6.0-beta.7" } }, "sha512-z6wXT1w0lFYS2uHAYuNZRutxNmg5i4S6yKYVf58/DVu+j/L1eq2ntPNVpPJpof71izXbv14ImvvPUCTTqHwAUA=="], "@vue/runtime-core": ["@vue/runtime-core@3.6.0-beta.9", "", { "dependencies": { "@vue/reactivity": "3.6.0-beta.9", "@vue/shared": "3.6.0-beta.9" } }, "sha512-IPbO/cmw0BSHec17fSE8Mbtv0UFBiiiGhvlUGiJWTa/eOlbiO+mZ9/PRRUQ+v/tkKuwFSzKye7KOKj5aFO1dgQ=="],
"@vue/runtime-dom": ["@vue/runtime-dom@3.6.0-beta.7", "", { "dependencies": { "@vue/reactivity": "3.6.0-beta.7", "@vue/runtime-core": "3.6.0-beta.7", "@vue/shared": "3.6.0-beta.7", "csstype": "^3.2.3" } }, "sha512-purk/iT3R5y2CizeV+YdxHhqPE1yAk3MSptggsCdO0zkShl2NA1/xec1WgVg1OL6IV0xNrknsMxl9Fit4g44hA=="], "@vue/runtime-dom": ["@vue/runtime-dom@3.6.0-beta.9", "", { "dependencies": { "@vue/reactivity": "3.6.0-beta.9", "@vue/runtime-core": "3.6.0-beta.9", "@vue/shared": "3.6.0-beta.9", "csstype": "^3.2.3" } }, "sha512-+WV0V1s/uf9iFwZt41lOtJHbIqKyYdEO+nmW0UOnQLjG9ELz6rKuZ92fEim6NPcKCsucv2BoB1mK9T82khYqgg=="],
"@vue/runtime-vapor": ["@vue/runtime-vapor@3.6.0-beta.7", "", { "dependencies": { "@vue/reactivity": "3.6.0-beta.7", "@vue/shared": "3.6.0-beta.7" }, "peerDependencies": { "@vue/runtime-dom": "3.6.0-beta.7" } }, "sha512-H+XdbkqtBI+9gsS957Om94qpTx+keklzvO6rllls4TcKlRo/xcXsDnMhWjghX0Irg6iM/MgBBK8bQkfUkF+xSg=="], "@vue/runtime-vapor": ["@vue/runtime-vapor@3.6.0-beta.9", "", { "dependencies": { "@vue/reactivity": "3.6.0-beta.9", "@vue/shared": "3.6.0-beta.9" }, "peerDependencies": { "@vue/runtime-dom": "3.6.0-beta.9" } }, "sha512-UlAziGF77byUa84RgkIdCT4bleZbB4UvQvmxdGHFfscGylSABBZHAMR8WJIwBsy1MsZnlpRSSeDAYB20RicrhQ=="],
"@vue/server-renderer": ["@vue/server-renderer@3.6.0-beta.7", "", { "dependencies": { "@vue/compiler-ssr": "3.6.0-beta.7", "@vue/shared": "3.6.0-beta.7" }, "peerDependencies": { "vue": "3.6.0-beta.7" } }, "sha512-VqX6+8+NyCC/nqkOvj/vKHeMtBr3Mxo7rccDLNyuSb6o5Mp+itlxdpaD0LD28SqHEaDjqfIUBTnFsfDJLN/cww=="], "@vue/server-renderer": ["@vue/server-renderer@3.6.0-beta.9", "", { "dependencies": { "@vue/compiler-ssr": "3.6.0-beta.9", "@vue/shared": "3.6.0-beta.9" }, "peerDependencies": { "vue": "3.6.0-beta.9" } }, "sha512-qyz37g6pmwHvtXbe7CWKm5+VTmCs4mHrC1uDw4OVZlvwDDZb9pWMzW0kOrzhj1iI6sZ4D9k5fYQIdzBqB7aOqw=="],
"@vue/shared": ["@vue/shared@3.6.0-beta.7", "", {}, "sha512-BNC0ohwv5hYybiUvZ/7gBjSzWFRxpZ2MzRQ/lH2zYZds085ylmAqMGEwbGlWFD+/WPRBsXQd7+e1hNGFksNuCg=="], "@vue/shared": ["@vue/shared@3.6.0-beta.9", "", {}, "sha512-nY6DXeelJsmveeNk5YZYGd8q0ulWXACL8Qs/qvkPC4HQkyhPgf+Ja00AkG2hYsEI8Uixia2UpQl4CbObzw0hag=="],
"@vue/tsconfig": ["@vue/tsconfig@0.8.1", "", { "peerDependencies": { "typescript": "5.x", "vue": "^3.4.0" }, "optionalPeers": ["typescript", "vue"] }, "sha512-aK7feIWPXFSUhsCP9PFqPyFOcz4ENkb8hZ2pneL6m2UjCkccvaOhC/5KCKluuBufvp2KzkbdA2W2pk20vLzu3g=="], "@vue/tsconfig": ["@vue/tsconfig@0.8.1", "", { "peerDependencies": { "typescript": "5.x", "vue": "^3.4.0" }, "optionalPeers": ["typescript", "vue"] }, "sha512-aK7feIWPXFSUhsCP9PFqPyFOcz4ENkb8hZ2pneL6m2UjCkccvaOhC/5KCKluuBufvp2KzkbdA2W2pk20vLzu3g=="],
@@ -580,23 +580,23 @@
"balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="], "balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="],
"baseline-browser-mapping": ["baseline-browser-mapping@2.10.0", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA=="], "baseline-browser-mapping": ["baseline-browser-mapping@2.10.13", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-BL2sTuHOdy0YT1lYieUxTw/QMtPBC3pmlJC6xk8BBYVv6vcw3SGdKemQ+Xsx9ik2F/lYDO9tqsFQH1r9PFuHKw=="],
"birpc": ["birpc@2.9.0", "", {}, "sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw=="], "birpc": ["birpc@2.9.0", "", {}, "sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw=="],
"boolbase": ["boolbase@1.0.0", "", {}, "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="], "boolbase": ["boolbase@1.0.0", "", {}, "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="],
"brace-expansion": ["brace-expansion@5.0.4", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg=="], "brace-expansion": ["brace-expansion@5.0.5", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ=="],
"braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="],
"browserslist": ["browserslist@4.28.1", "", { "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", "electron-to-chromium": "^1.5.263", "node-releases": "^2.0.27", "update-browserslist-db": "^1.2.0" }, "bin": { "browserslist": "cli.js" } }, "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA=="], "browserslist": ["browserslist@4.28.2", "", { "dependencies": { "baseline-browser-mapping": "^2.10.12", "caniuse-lite": "^1.0.30001782", "electron-to-chromium": "^1.5.328", "node-releases": "^2.0.36", "update-browserslist-db": "^1.2.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg=="],
"bundle-name": ["bundle-name@4.1.0", "", { "dependencies": { "run-applescript": "^7.0.0" } }, "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q=="], "bundle-name": ["bundle-name@4.1.0", "", { "dependencies": { "run-applescript": "^7.0.0" } }, "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q=="],
"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=="], "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=="],
"caniuse-lite": ["caniuse-lite@1.0.30001777", "", {}, "sha512-tmN+fJxroPndC74efCdp12j+0rk0RHwV5Jwa1zWaFVyw2ZxAuPeG8ZgWC3Wz7uSjT3qMRQ5XHZ4COgQmsCMJAQ=="], "caniuse-lite": ["caniuse-lite@1.0.30001784", "", {}, "sha512-WU346nBTklUV9YfUl60fqRbU5ZqyXlqvo1SgigE1OAXK5bFL8LL9q1K7aap3N739l4BvNqnkm3YrGHiY9sfUQw=="],
"chart.js": ["chart.js@4.5.1", "", { "dependencies": { "@kurkle/color": "^0.3.0" } }, "sha512-GIjfiT9dbmHRiYi6Nl2yFCq7kkwdkp1W/lp2J99rX0yo9tgJGn3lKQATztIjb5tVtevcBtIdICNWqlq5+E8/Pw=="], "chart.js": ["chart.js@4.5.1", "", { "dependencies": { "@kurkle/color": "^0.3.0" } }, "sha512-GIjfiT9dbmHRiYi6Nl2yFCq7kkwdkp1W/lp2J99rX0yo9tgJGn3lKQATztIjb5tVtevcBtIdICNWqlq5+E8/Pw=="],
@@ -646,7 +646,7 @@
"dotenv": ["dotenv@17.3.1", "", {}, "sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA=="], "dotenv": ["dotenv@17.3.1", "", {}, "sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA=="],
"electron-to-chromium": ["electron-to-chromium@1.5.307", "", {}, "sha512-5z3uFKBWjiNR44nFcYdkcXjKMbg5KXNdciu7mhTPo9tB7NbqSNP2sSnGR+fqknZSCwKkBN+oxiiajWs4dT6ORg=="], "electron-to-chromium": ["electron-to-chromium@1.5.330", "", {}, "sha512-jFNydB5kFtYUobh4IkWUnXeyDbjf/r9gcUEXe1xcrcUxIGfTdzPXA+ld6zBRbwvgIGVzDll/LTIiDztEtckSnA=="],
"embla-carousel": ["embla-carousel@8.6.0", "", {}, "sha512-SjWyZBHJPbqxHOzckOfo8lHisEaJWmwd23XppYFYVh10bU66/Pn5tkVkbkCMZVdbUE5eTCI2nD8OyIP4Z+uwkA=="], "embla-carousel": ["embla-carousel@8.6.0", "", {}, "sha512-SjWyZBHJPbqxHOzckOfo8lHisEaJWmwd23XppYFYVh10bU66/Pn5tkVkbkCMZVdbUE5eTCI2nD8OyIP4Z+uwkA=="],
@@ -666,7 +666,7 @@
"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=="], "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=="], "enhanced-resolve": ["enhanced-resolve@5.20.1", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.0" } }, "sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA=="],
"entities": ["entities@7.0.1", "", {}, "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA=="], "entities": ["entities@7.0.1", "", {}, "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA=="],
@@ -674,13 +674,13 @@
"errx": ["errx@0.1.0", "", {}, "sha512-fZmsRiDNv07K6s2KkKFTiD2aIvECa7++PKyD5NC32tpRw46qZA3sOz+aM+/V9V0GDHxVTKLziveV4JhzBHDp9Q=="], "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": { "esbuild": "bin/esbuild" } }, "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg=="], "esbuild": ["esbuild@0.27.4", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.4", "@esbuild/android-arm": "0.27.4", "@esbuild/android-arm64": "0.27.4", "@esbuild/android-x64": "0.27.4", "@esbuild/darwin-arm64": "0.27.4", "@esbuild/darwin-x64": "0.27.4", "@esbuild/freebsd-arm64": "0.27.4", "@esbuild/freebsd-x64": "0.27.4", "@esbuild/linux-arm": "0.27.4", "@esbuild/linux-arm64": "0.27.4", "@esbuild/linux-ia32": "0.27.4", "@esbuild/linux-loong64": "0.27.4", "@esbuild/linux-mips64el": "0.27.4", "@esbuild/linux-ppc64": "0.27.4", "@esbuild/linux-riscv64": "0.27.4", "@esbuild/linux-s390x": "0.27.4", "@esbuild/linux-x64": "0.27.4", "@esbuild/netbsd-arm64": "0.27.4", "@esbuild/netbsd-x64": "0.27.4", "@esbuild/openbsd-arm64": "0.27.4", "@esbuild/openbsd-x64": "0.27.4", "@esbuild/openharmony-arm64": "0.27.4", "@esbuild/sunos-x64": "0.27.4", "@esbuild/win32-arm64": "0.27.4", "@esbuild/win32-ia32": "0.27.4", "@esbuild/win32-x64": "0.27.4" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-Rq4vbHnYkK5fws5NF7MYTU68FPRE1ajX7heQ/8QXXWqNgqqJ/GkmmyxIzUnf2Sr/bakf8l54716CcMGHYhMrrQ=="],
"escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="],
"escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="],
"eslint": ["eslint@10.0.3", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.2", "@eslint/config-array": "^0.23.3", "@eslint/config-helpers": "^0.5.2", "@eslint/core": "^1.1.1", "@eslint/plugin-kit": "^0.6.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "ajv": "^6.14.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^9.1.2", "eslint-visitor-keys": "^5.0.1", "espree": "^11.1.1", "esquery": "^1.7.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "minimatch": "^10.2.4", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-COV33RzXZkqhG9P2rZCFl9ZmJ7WL+gQSCRzE7RhkbclbQPtLAWReL7ysA0Sh4c8Im2U9ynybdR56PV0XcKvqaQ=="], "eslint": ["eslint@10.1.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.2", "@eslint/config-array": "^0.23.3", "@eslint/config-helpers": "^0.5.3", "@eslint/core": "^1.1.1", "@eslint/plugin-kit": "^0.6.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "ajv": "^6.14.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^9.1.2", "eslint-visitor-keys": "^5.0.1", "espree": "^11.2.0", "esquery": "^1.7.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "minimatch": "^10.2.4", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-S9jlY/ELKEUwwQnqWDO+f+m6sercqOPSqXM5Go94l7DOmxHVDgmSFGWEzeE/gwgTAr0W103BWt0QLe/7mabIvA=="],
"eslint-plugin-vue": ["eslint-plugin-vue@10.8.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "natural-compare": "^1.4.0", "nth-check": "^2.1.1", "postcss-selector-parser": "^7.1.0", "semver": "^7.6.3", "xml-name-validator": "^4.0.0" }, "peerDependencies": { "@stylistic/eslint-plugin": "^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0", "@typescript-eslint/parser": "^7.0.0 || ^8.0.0", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "vue-eslint-parser": "^10.0.0" }, "optionalPeers": ["@stylistic/eslint-plugin", "@typescript-eslint/parser"] }, "sha512-f1J/tcbnrpgC8suPN5AtdJ5MQjuXbSU9pGRSSYAuF3SHoiYCOdEX6O22pLaRyLHXvDcOe+O5ENgc1owQ587agA=="], "eslint-plugin-vue": ["eslint-plugin-vue@10.8.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "natural-compare": "^1.4.0", "nth-check": "^2.1.1", "postcss-selector-parser": "^7.1.0", "semver": "^7.6.3", "xml-name-validator": "^4.0.0" }, "peerDependencies": { "@stylistic/eslint-plugin": "^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0", "@typescript-eslint/parser": "^7.0.0 || ^8.0.0", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "vue-eslint-parser": "^10.0.0" }, "optionalPeers": ["@stylistic/eslint-plugin", "@typescript-eslint/parser"] }, "sha512-f1J/tcbnrpgC8suPN5AtdJ5MQjuXbSU9pGRSSYAuF3SHoiYCOdEX6O22pLaRyLHXvDcOe+O5ENgc1owQ587agA=="],
@@ -724,7 +724,7 @@
"flat-cache": ["flat-cache@4.0.1", "", { "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" } }, "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw=="], "flat-cache": ["flat-cache@4.0.1", "", { "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" } }, "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw=="],
"flatted": ["flatted@3.4.1", "", {}, "sha512-IxfVbRFVlV8V/yRaGzk0UVIcsKKHMSfYw66T/u4nTwlWteQePsxe//LjudR1AMX4tZW3WFCh3Zqa/sjlqpbURQ=="], "flatted": ["flatted@3.4.2", "", {}, "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA=="],
"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=="], "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=="],
@@ -732,7 +732,7 @@
"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": "*" }, "optionalPeers": ["vite"] }, "sha512-mUWZ8w91/mw2KEcZ6gHNoNNmsAq9Wiw2IypIux5lM03nhXm+WSloXGUNuRETNTLqZexMgpt7Aj/v63qqrsWraQ=="], "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": "*" }, "optionalPeers": ["vite"] }, "sha512-mUWZ8w91/mw2KEcZ6gHNoNNmsAq9Wiw2IypIux5lM03nhXm+WSloXGUNuRETNTLqZexMgpt7Aj/v63qqrsWraQ=="],
"framer-motion": ["framer-motion@12.35.2", "", { "dependencies": { "motion-dom": "^12.35.2", "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-dhfuEMaNo0hc+AEqyHiIfiJRNb9U9UQutE9FoKm5pjf7CMitp9xPEF1iWZihR1q86LBmo6EJ7S8cN8QXEy49AA=="], "framer-motion": ["framer-motion@12.38.0", "", { "dependencies": { "motion-dom": "^12.38.0", "motion-utils": "^12.36.0", "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-rFYkY/pigbcswl1XQSb7q424kSTQ8q6eAC+YUsSKooHQYuLdzdHjrt6uxUC+PRAO++q5IS7+TamgIw1AphxR+g=="],
"fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
@@ -748,11 +748,11 @@
"graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
"h3": ["h3@1.15.6", "", { "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-oi15ESLW5LRthZ+qPCi5GNasY/gvynSKUQxgiovrY63bPAtG59wtM+LSrlcwvOHAXzGrXVLnI97brbkdPF9WoQ=="], "h3": ["h3@1.15.10", "", { "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-YzJeWSkDZxAhvmp8dexjRK5hxziRO7I9m0N53WhvYL5NiWfkUkzssVzY9jvGu0HBoLFW6+duYmNSn6MaZBCCtg=="],
"hey-listen": ["hey-listen@1.0.8", "", {}, "sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q=="], "hey-listen": ["hey-listen@1.0.8", "", {}, "sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q=="],
"hookable": ["hookable@5.5.3", "", {}, "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ=="], "hookable": ["hookable@6.1.0", "", {}, "sha512-ZoKZSJgu8voGK2geJS+6YtYjvIzu9AOM/KZXsBxr83uhLL++e9pEv/dlgwgy3dvHg06kTz6JOh1hk3C8Ceiymw=="],
"human-signals": ["human-signals@5.0.0", "", {}, "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ=="], "human-signals": ["human-signals@5.0.0", "", {}, "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ=="],
@@ -842,7 +842,7 @@
"locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="], "locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="],
"lru-cache": ["lru-cache@11.2.6", "", {}, "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ=="], "lru-cache": ["lru-cache@11.2.7", "", {}, "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA=="],
"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-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=="],
@@ -852,7 +852,7 @@
"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": { "markdown-it": "bin/markdown-it.mjs" } }, "sha512-BuU2qnTti9YKgK5N+IeMubp14ZUKUUw7yeJbkjtosvHiP0AZ5c8IAgEMk79D0eC8F23r4Ac/q8cAIFdm2FtyoA=="], "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": { "markdown-it": "bin/markdown-it.mjs" } }, "sha512-BuU2qnTti9YKgK5N+IeMubp14ZUKUUw7yeJbkjtosvHiP0AZ5c8IAgEMk79D0eC8F23r4Ac/q8cAIFdm2FtyoA=="],
"marked": ["marked@17.0.4", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-NOmVMM+KAokHMvjWmC5N/ZOvgmSWuqJB8FoYI019j4ogb/PeRMKoKIjReZ2w3376kkA8dSJIP8uD993Kxc0iRQ=="], "marked": ["marked@17.0.5", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-6hLvc0/JEbRjRgzI6wnT2P1XuM1/RrrDEX0kPt0N7jGm1133g6X7DlxFasUIx+72aKAr904GTxhSLDrd5DIlZg=="],
"mdn-data": ["mdn-data@2.27.1", "", {}, "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ=="], "mdn-data": ["mdn-data@2.27.1", "", {}, "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ=="],
@@ -868,17 +868,17 @@
"mimic-fn": ["mimic-fn@4.0.0", "", {}, "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw=="], "mimic-fn": ["mimic-fn@4.0.0", "", {}, "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw=="],
"minimatch": ["minimatch@10.2.4", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg=="], "minimatch": ["minimatch@10.2.5", "", { "dependencies": { "brace-expansion": "^5.0.5" } }, "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg=="],
"mitt": ["mitt@3.0.1", "", {}, "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw=="], "mitt": ["mitt@3.0.1", "", {}, "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw=="],
"mlly": ["mlly@1.8.1", "", { "dependencies": { "acorn": "^8.16.0", "pathe": "^2.0.3", "pkg-types": "^1.3.1", "ufo": "^1.6.3" } }, "sha512-SnL6sNutTwRWWR/vcmCYHSADjiEesp5TGQQ0pXyLhW5IoeibRlF/CbSLailbB3CNqJUk9cVJ9dUDnbD7GrcHBQ=="], "mlly": ["mlly@1.8.2", "", { "dependencies": { "acorn": "^8.16.0", "pathe": "^2.0.3", "pkg-types": "^1.3.1", "ufo": "^1.6.3" } }, "sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA=="],
"motion-dom": ["motion-dom@12.35.2", "", { "dependencies": { "motion-utils": "^12.29.2" } }, "sha512-pWXFMTwvGDbx1Fe9YL5HZebv2NhvGBzRtiNUv58aoK7+XrsuaydQ0JGRKK2r+bTKlwgSWwWxHbP5249Qr/BNpg=="], "motion-dom": ["motion-dom@12.38.0", "", { "dependencies": { "motion-utils": "^12.36.0" } }, "sha512-pdkHLD8QYRp8VfiNLb8xIBJis1byQ9gPT3Jnh2jqfFtAsWUA3dEepDlsWe/xMpO8McV+VdpKVcp+E+TGJEtOoA=="],
"motion-utils": ["motion-utils@12.29.2", "", {}, "sha512-G3kc34H2cX2gI63RqU+cZq+zWRRPSsNIOjpdl9TN4AQwC4sgwYPl/Q/Obf/d53nOm569T0fYK+tcoSV50BWx8A=="], "motion-utils": ["motion-utils@12.36.0", "", {}, "sha512-eHWisygbiwVvf6PZ1vhaHCLamvkSbPIeAYxWUuL3a2PD/TROgE7FvfHWTIH4vMl798QLfMw15nRqIaRDXTlYRg=="],
"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=="], "motion-v": ["motion-v@2.2.0", "", { "dependencies": { "framer-motion": "^12.38.0", "hey-listen": "^1.0.8", "motion-dom": "^12.38.0", "motion-utils": "^12.36.0" }, "peerDependencies": { "@vueuse/core": ">=10.0.0", "vue": ">=3.0.0" } }, "sha512-8rrUBt9UMwy45MfLMwHer9J7xBY/rL31S+0U3ZUAouqjH3AOVHvS0NGuXwF4mfWfvb22rfZEv59ZsBc2f1epxQ=="],
"mrmime": ["mrmime@2.0.1", "", {}, "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ=="], "mrmime": ["mrmime@2.0.1", "", {}, "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ=="],
@@ -942,7 +942,7 @@
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
"picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], "picomatch": ["picomatch@4.0.4", "", {}, "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A=="],
"pidtree": ["pidtree@0.6.0", "", { "bin": { "pidtree": "bin/pidtree.js" } }, "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g=="], "pidtree": ["pidtree@0.6.0", "", { "bin": { "pidtree": "bin/pidtree.js" } }, "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g=="],
@@ -990,9 +990,9 @@
"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-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-transform": ["prosemirror-transform@1.12.0", "", { "dependencies": { "prosemirror-model": "^1.21.0" } }, "sha512-GxboyN4AMIsoHNtz5uf2r2Ru551i5hWeCMD6E2Ib4Eogqoub0NflniaBPVQ4MrGE5yZ8JV9tUHg9qcZTTrcN4w=="],
"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=="], "prosemirror-view": ["prosemirror-view@1.41.7", "", { "dependencies": { "prosemirror-model": "^1.20.0", "prosemirror-state": "^1.0.0", "prosemirror-transform": "^1.1.0" } }, "sha512-jUwKNCEIGiqdvhlS91/2QAg21e4dfU5bH2iwmSDQeosXJgKF7smG0YSplOWK0cjSNgIqXe7VXqo7EIfUFJdt3w=="],
"punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
@@ -1012,7 +1012,7 @@
"regexp-tree": ["regexp-tree@0.1.27", "", { "bin": { "regexp-tree": "bin/regexp-tree" } }, "sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA=="], "regexp-tree": ["regexp-tree@0.1.27", "", { "bin": { "regexp-tree": "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=="], "reka-ui": ["reka-ui@2.9.3", "", { "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.4.0" } }, "sha512-C9lCVxsSC7uYD0Nbgik1+14FNndHNprZmf0zGQt0ZDYIt5KxXV3zD0hEqNcfRUsEEJvVmoRsUkJnASBVBeaaUw=="],
"reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="], "reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="],
@@ -1056,13 +1056,13 @@
"tailwind-variants": ["tailwind-variants@3.2.2", "", { "peerDependencies": { "tailwind-merge": ">=3.0.0", "tailwindcss": "*" }, "optionalPeers": ["tailwind-merge"] }, "sha512-Mi4kHeMTLvKlM98XPnK+7HoBPmf4gygdFmqQPaDivc3DpYS6aIY6KiG/PgThrGvii5YZJqRsPz0aPyhoFzmZgg=="], "tailwind-variants": ["tailwind-variants@3.2.2", "", { "peerDependencies": { "tailwind-merge": ">=3.0.0", "tailwindcss": "*" }, "optionalPeers": ["tailwind-merge"] }, "sha512-Mi4kHeMTLvKlM98XPnK+7HoBPmf4gygdFmqQPaDivc3DpYS6aIY6KiG/PgThrGvii5YZJqRsPz0aPyhoFzmZgg=="],
"tailwindcss": ["tailwindcss@4.2.1", "", {}, "sha512-/tBrSQ36vCleJkAOsy9kbNTgaxvGbyOamC30PRePTQe/o1MFwEKHQk4Cn7BNGaPtjp+PuUrByJehM1hgxfq4sw=="], "tailwindcss": ["tailwindcss@4.2.2", "", {}, "sha512-KWBIxs1Xb6NoLdMVqhbhgwZf2PGBpPEiwOqgI4pFIYbNTfBXiKYyWoTsXgBQ9WFg/OlhnvHaY+AEpW7wSmFo2Q=="],
"tapable": ["tapable@2.3.0", "", {}, "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg=="], "tapable": ["tapable@2.3.2", "", {}, "sha512-1MOpMXuhGzGL5TTCZFItxCc0AARf1EZFQkGqMm7ERKj8+Hgr5oLvJOVFcC+lRmR8hCe2S3jC4T5D7Vg/d7/fhA=="],
"tiny-inflate": ["tiny-inflate@1.0.3", "", {}, "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw=="], "tiny-inflate": ["tiny-inflate@1.0.3", "", {}, "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw=="],
"tinyexec": ["tinyexec@1.0.2", "", {}, "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg=="], "tinyexec": ["tinyexec@1.0.4", "", {}, "sha512-u9r3uZC0bdpGOXtlxUIdwf9pkmvhqJdrVCH9fapQtgy/OeTTMZ1nqH7agtvEfmGui6e1XxjcdrlxvxJvc3sMqw=="],
"tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="],
@@ -1070,7 +1070,7 @@
"totalist": ["totalist@3.0.1", "", {}, "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ=="], "totalist": ["totalist@3.0.1", "", {}, "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ=="],
"ts-api-utils": ["ts-api-utils@2.4.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA=="], "ts-api-utils": ["ts-api-utils@2.5.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA=="],
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
@@ -1080,7 +1080,7 @@
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
"typescript-eslint": ["typescript-eslint@8.57.0", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.57.0", "@typescript-eslint/parser": "8.57.0", "@typescript-eslint/typescript-estree": "8.57.0", "@typescript-eslint/utils": "8.57.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-W8GcigEMEeB07xEZol8oJ26rigm3+bfPHxHvwbYUlu1fUDsGuQ7Hiskx5xGW/xM4USc9Ephe3jtv7ZYPQntHeA=="], "typescript-eslint": ["typescript-eslint@8.58.0", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.58.0", "@typescript-eslint/parser": "8.58.0", "@typescript-eslint/typescript-estree": "8.58.0", "@typescript-eslint/utils": "8.58.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-e2TQzKfaI85fO+F3QywtX+tCTsu/D3WW5LVU6nz8hTFKFZ8yBJ6mSYRpXqdR3mFjPWmO0eWsTa5f+UpAOe/FMA=="],
"uc.micro": ["uc.micro@2.1.0", "", {}, "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A=="], "uc.micro": ["uc.micro@2.1.0", "", {}, "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A=="],
@@ -1104,9 +1104,9 @@
"unplugin-utils": ["unplugin-utils@0.3.1", "", { "dependencies": { "pathe": "^2.0.3", "picomatch": "^4.0.3" } }, "sha512-5lWVjgi6vuHhJ526bI4nlCOmkCIF3nnfXkCMDeMJrtdvxTs6ZFCM8oNufGTsDbKv/tJ/xj8RpvXjRuPBZJuJog=="], "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" }, "optionalPeers": ["@nuxt/kit"] }, "sha512-4ULwfTZTLuWJ7+S9P7TrcStYLsSRkk6vy2jt/WTfgUEUb0nW9//xxmrfhyHUEVpZ2UKRRwfRb8Yy15PDbVZf+Q=="], "unplugin-vue-components": ["unplugin-vue-components@31.1.0", "", { "dependencies": { "chokidar": "^5.0.0", "local-pkg": "^1.1.2", "magic-string": "^0.30.21", "mlly": "^1.8.2", "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" }, "optionalPeers": ["@nuxt/kit"] }, "sha512-9EbV5ark21A4BOBt6RJGJXCVD2I1eoxTZL1TAvNgYTokcrFIiuxpufb8owyWn7n+z2x8daz/ltZq6IRRKL3ydQ=="],
"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=="], "unstorage": ["unstorage@1.17.5", "", { "dependencies": { "anymatch": "^3.1.3", "chokidar": "^5.0.0", "destr": "^2.0.5", "h3": "^1.15.10", "lru-cache": "^11.2.7", "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-0i3iqvRfx29hkNntHyQvJTpf5W9dQ9ZadSoRU8+xVlhVtT7jAX57fazYO9EHvcRCfBCyi5YRya7XCDOsbTgkPg=="],
"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": { "untyped": "dist/cli.mjs" } }, "sha512-nwNCjxJTjNuLCgFr42fEak5OcLuB3ecca+9ksPFNvtfYSLpjf+iJqSIaSnIile6ZPbKYxI5k2AfXqeopGudK/g=="], "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": { "untyped": "dist/cli.mjs" } }, "sha512-nwNCjxJTjNuLCgFr42fEak5OcLuB3ecca+9ksPFNvtfYSLpjf+iJqSIaSnIile6ZPbKYxI5k2AfXqeopGudK/g=="],
@@ -1126,17 +1126,17 @@
"vite-plugin-inspect": ["vite-plugin-inspect@11.3.3", "", { "dependencies": { "ansis": "^4.1.0", "debug": "^4.4.1", "error-stack-parser-es": "^1.0.5", "ohash": "^2.0.11", "open": "^10.2.0", "perfect-debounce": "^2.0.0", "sirv": "^3.0.1", "unplugin-utils": "^0.3.0", "vite-dev-rpc": "^1.1.0" }, "peerDependencies": { "vite": "^6.0.0 || ^7.0.0-0" } }, "sha512-u2eV5La99oHoYPHE6UvbwgEqKKOQGz86wMg40CCosP6q8BkB6e5xPneZfYagK4ojPJSj5anHCrnvC20DpwVdRA=="], "vite-plugin-inspect": ["vite-plugin-inspect@11.3.3", "", { "dependencies": { "ansis": "^4.1.0", "debug": "^4.4.1", "error-stack-parser-es": "^1.0.5", "ohash": "^2.0.11", "open": "^10.2.0", "perfect-debounce": "^2.0.0", "sirv": "^3.0.1", "unplugin-utils": "^0.3.0", "vite-dev-rpc": "^1.1.0" }, "peerDependencies": { "vite": "^6.0.0 || ^7.0.0-0" } }, "sha512-u2eV5La99oHoYPHE6UvbwgEqKKOQGz86wMg40CCosP6q8BkB6e5xPneZfYagK4ojPJSj5anHCrnvC20DpwVdRA=="],
"vite-plugin-vue-devtools": ["vite-plugin-vue-devtools@8.0.7", "", { "dependencies": { "@vue/devtools-core": "^8.0.7", "@vue/devtools-kit": "^8.0.7", "@vue/devtools-shared": "^8.0.7", "sirv": "^3.0.2", "vite-plugin-inspect": "^11.3.3", "vite-plugin-vue-inspector": "^5.3.2" }, "peerDependencies": { "vite": "^6.0.0 || ^7.0.0-0 || ^8.0.0-0" } }, "sha512-BWj/ykGpqVAJVdPyHmSTUm44buz3jPv+6jnvuFdQSRH0kAgP1cEIE4doHiFyqHXOmuB5EQVR/nh2g9YRiRNs9g=="], "vite-plugin-vue-devtools": ["vite-plugin-vue-devtools@8.1.1", "", { "dependencies": { "@vue/devtools-core": "^8.1.1", "@vue/devtools-kit": "^8.1.1", "@vue/devtools-shared": "^8.1.1", "sirv": "^3.0.2", "vite-plugin-inspect": "^11.3.3", "vite-plugin-vue-inspector": "^5.3.2" }, "peerDependencies": { "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-9qTpOmZ2vHpvlI9hdVXAQ1Ry4I8GcBArU7aPi0qfIaV7fQIXy0L1nb6X4mFY2Gw0dYshHuLbIl0Ulb572SCjsQ=="],
"vite-plugin-vue-inspector": ["vite-plugin-vue-inspector@5.3.2", "", { "dependencies": { "@babel/core": "^7.23.0", "@babel/plugin-proposal-decorators": "^7.23.0", "@babel/plugin-syntax-import-attributes": "^7.22.5", "@babel/plugin-syntax-import-meta": "^7.10.4", "@babel/plugin-transform-typescript": "^7.22.15", "@vue/babel-plugin-jsx": "^1.1.5", "@vue/compiler-dom": "^3.3.4", "kolorist": "^1.8.0", "magic-string": "^0.30.4" }, "peerDependencies": { "vite": "^3.0.0-0 || ^4.0.0-0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0" } }, "sha512-YvEKooQcSiBTAs0DoYLfefNja9bLgkFM7NI2b07bE2SruuvX0MEa9cMaxjKVMkeCp5Nz9FRIdcN1rOdFVBeL6Q=="], "vite-plugin-vue-inspector": ["vite-plugin-vue-inspector@5.4.0", "", { "dependencies": { "@babel/core": "^7.23.0", "@babel/plugin-proposal-decorators": "^7.23.0", "@babel/plugin-syntax-import-attributes": "^7.22.5", "@babel/plugin-syntax-import-meta": "^7.10.4", "@babel/plugin-transform-typescript": "^7.22.15", "@vue/babel-plugin-jsx": "^1.1.5", "@vue/compiler-dom": "^3.3.4", "kolorist": "^1.8.0", "magic-string": "^0.30.4" }, "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-Iq/024CydcE46FZqWPU4t4lw4uYOdLnFSO1RNxJVt2qY9zxIjmnkBqhHnYaReWM82kmNnaXs7OkfgRrV2GEjyw=="],
"vscode-uri": ["vscode-uri@3.1.0", "", {}, "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ=="], "vscode-uri": ["vscode-uri@3.1.0", "", {}, "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ=="],
"vue": ["vue@3.6.0-beta.7", "", { "dependencies": { "@vue/compiler-dom": "3.6.0-beta.7", "@vue/compiler-sfc": "3.6.0-beta.7", "@vue/runtime-dom": "3.6.0-beta.7", "@vue/runtime-vapor": "3.6.0-beta.7", "@vue/server-renderer": "3.6.0-beta.7", "@vue/shared": "3.6.0-beta.7" }, "peerDependencies": { "typescript": "*" }, "optionalPeers": ["typescript"] }, "sha512-SlNdNG+c37xycRFIJ/KB9CmpZmWwKovbDTNl00ck0+ZSNZSgb+n6DKLuXtVvGZImNPu6jj+zzaM1gqNB5NeC6Q=="], "vue": ["vue@3.6.0-beta.9", "", { "dependencies": { "@vue/compiler-dom": "3.6.0-beta.9", "@vue/compiler-sfc": "3.6.0-beta.9", "@vue/runtime-dom": "3.6.0-beta.9", "@vue/runtime-vapor": "3.6.0-beta.9", "@vue/server-renderer": "3.6.0-beta.9", "@vue/shared": "3.6.0-beta.9" }, "peerDependencies": { "typescript": "*" }, "optionalPeers": ["typescript"] }, "sha512-K/+HT52OAZvaNDDMvT+Lfk36e/97PsyJyDWXM0FDfLQKGbEDXsd4pSQz+BJViGssLcltQLSTlSqQlZ6fkGo5CA=="],
"vue-chartjs": ["vue-chartjs@5.3.3", "", { "peerDependencies": { "chart.js": "^4.1.1", "vue": "^3.0.0-0 || ^2.7.0" } }, "sha512-jqxtL8KZ6YJ5NTv6XzrzLS7osyegOi28UGNZW0h9OkDL7Sh1396ht4Dorh04aKrl2LiSalQ84WtqiG0RIJb0tA=="], "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-component-type-helpers": ["vue-component-type-helpers@3.2.6", "", {}, "sha512-O02tnvIfOQVmnvoWwuSydwRoHjZVt8UEBR+2p4rT35p8GAy5VTlWP8o5qXfJR/GWCN0nVZoYWsVUvx2jwgdBmQ=="],
"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=="], "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=="],
@@ -1144,9 +1144,9 @@
"vue-i18n": ["vue-i18n@11.3.0", "", { "dependencies": { "@intlify/core-base": "11.3.0", "@intlify/devtools-types": "11.3.0", "@intlify/shared": "11.3.0", "@vue/devtools-api": "^6.5.0" }, "peerDependencies": { "vue": "^3.0.0" } }, "sha512-1J+xDfDJTLhDxElkd3+XUhT7FYSZd2b8pa7IRKGxhWH/8yt6PTvi3xmWhGwhYT5EaXdatui11pF2R6tL73/zPA=="], "vue-i18n": ["vue-i18n@11.3.0", "", { "dependencies": { "@intlify/core-base": "11.3.0", "@intlify/devtools-types": "11.3.0", "@intlify/shared": "11.3.0", "@vue/devtools-api": "^6.5.0" }, "peerDependencies": { "vue": "^3.0.0" } }, "sha512-1J+xDfDJTLhDxElkd3+XUhT7FYSZd2b8pa7IRKGxhWH/8yt6PTvi3xmWhGwhYT5EaXdatui11pF2R6tL73/zPA=="],
"vue-router": ["vue-router@5.0.3", "", { "dependencies": { "@babel/generator": "^7.28.6", "@vue-macros/common": "^3.1.1", "@vue/devtools-api": "^8.0.6", "ast-walker-scope": "^0.8.3", "chokidar": "^5.0.0", "json5": "^2.2.3", "local-pkg": "^1.1.2", "magic-string": "^0.30.21", "mlly": "^1.8.0", "muggle-string": "^0.4.1", "pathe": "^2.0.3", "picomatch": "^4.0.3", "scule": "^1.3.0", "tinyglobby": "^0.2.15", "unplugin": "^3.0.0", "unplugin-utils": "^0.3.1", "yaml": "^2.8.2" }, "peerDependencies": { "@pinia/colada": ">=0.21.2", "@vue/compiler-sfc": "^3.5.17", "pinia": "^3.0.4", "vue": "^3.5.0" }, "optionalPeers": ["@pinia/colada", "@vue/compiler-sfc", "pinia"] }, "sha512-nG1c7aAFac7NYj8Hluo68WyWfc41xkEjaR0ViLHCa3oDvTQ/nIuLJlXJX1NUPw/DXzx/8+OKMng045HHQKQKWw=="], "vue-router": ["vue-router@5.0.4", "", { "dependencies": { "@babel/generator": "^7.28.6", "@vue-macros/common": "^3.1.1", "@vue/devtools-api": "^8.0.6", "ast-walker-scope": "^0.8.3", "chokidar": "^5.0.0", "json5": "^2.2.3", "local-pkg": "^1.1.2", "magic-string": "^0.30.21", "mlly": "^1.8.0", "muggle-string": "^0.4.1", "pathe": "^2.0.3", "picomatch": "^4.0.3", "scule": "^1.3.0", "tinyglobby": "^0.2.15", "unplugin": "^3.0.0", "unplugin-utils": "^0.3.1", "yaml": "^2.8.2" }, "peerDependencies": { "@pinia/colada": ">=0.21.2", "@vue/compiler-sfc": "^3.5.17", "pinia": "^3.0.4", "vue": "^3.5.0" }, "optionalPeers": ["@pinia/colada", "@vue/compiler-sfc", "pinia"] }, "sha512-lCqDLCI2+fKVRl2OzXuzdSWmxXFLQRxQbmHugnRpTMyYiT+hNaycV0faqG5FBHDXoYrZ6MQcX87BvbY8mQ20Bg=="],
"vue-tsc": ["vue-tsc@3.2.5", "", { "dependencies": { "@volar/typescript": "2.4.28", "@vue/language-core": "3.2.5" }, "peerDependencies": { "typescript": ">=5.0.0" }, "bin": { "vue-tsc": "bin/vue-tsc.js" } }, "sha512-/htfTCMluQ+P2FISGAooul8kO4JMheOTCbCy4M6dYnYYjqLe3BExZudAua6MSIKSFYQtFOYAll7XobYwcpokGA=="], "vue-tsc": ["vue-tsc@3.2.6", "", { "dependencies": { "@volar/typescript": "2.4.28", "@vue/language-core": "3.2.6" }, "peerDependencies": { "typescript": ">=5.0.0" }, "bin": { "vue-tsc": "bin/vue-tsc.js" } }, "sha512-gYW/kWI0XrwGzd0PKc7tVB/qpdeAkIZLNZb10/InizkQjHjnT8weZ/vBarZoj4kHKbUTZT/bAVgoOr8x4NsQ/Q=="],
"w3c-keyname": ["w3c-keyname@2.2.8", "", {}, "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ=="], "w3c-keyname": ["w3c-keyname@2.2.8", "", {}, "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ=="],
@@ -1166,9 +1166,9 @@
"yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="], "yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="],
"yaml": ["yaml@2.8.2", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A=="], "yaml": ["yaml@2.8.3", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg=="],
"yjs": ["yjs@13.6.29", "", { "dependencies": { "lib0": "^0.2.99" } }, "sha512-kHqDPdltoXH+X4w1lVmMtddE3Oeqq48nM40FD5ojTd8xYhQpzIDcfE2keMSU5bAgRPJBe225WTUdyUgj1DtbiQ=="], "yjs": ["yjs@13.6.30", "", { "dependencies": { "lib0": "^0.2.99" } }, "sha512-vv/9h42eCMC81ZHDFswuu/MKzkl/vyq1BhaNGfHyOonwlG4CJbQF4oiBBJPvfdeCt/PlVDWh7Nov9D34YY09uQ=="],
"yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="],
@@ -1184,21 +1184,23 @@
"@nuxt/kit/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="], "@nuxt/kit/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="],
"@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=="], "@nuxt/schema/std-env": ["std-env@4.0.0", "", {}, "sha512-zUMPtQ/HBY3/50VbpkupYHbRroTRZJPRLvreamgErJVys0ceuzMkD44J/QjqhHjOzK42GQ3QZIeFG1OYfOtKqQ=="],
"@nuxt/ui/reka-ui": ["reka-ui@2.9.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.4.0" } }, "sha512-/t4e6y1hcG+uDuRfpg6tbMz3uUEvRzNco6NeYTufoJeUghy5Iosxos5YL/p+ieAsid84sdMX9OrgDqpEuCJhBw=="],
"@nuxtjs/color-mode/@nuxt/kit": ["@nuxt/kit@3.21.2", "", { "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.1", "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-Bd6m6mrDrqpBEbX+g0rc66/ALd1sxlgdx5nfK9MAYO0yKLTOSK7McSYz1KcOYn3LQFCXOWfvXwaqih/b+REI1g=="],
"@nuxtjs/color-mode/pathe": ["pathe@1.1.2", "", {}, "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ=="], "@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=="], "@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=="],
"@tailwindcss/node/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=="], "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.9.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-mukuNALVsoix/w1BJwFzwXBN/dHeejQtuVzcDsfOEsdpCumXb/E9j8w11h5S54tT1xhifGfbbSm/ICrObRb3KA=="],
"@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.8.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg=="], "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.9.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA=="],
"@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.8.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg=="], "@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg=="],
"@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.1.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ=="], "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.2", "", { "dependencies": { "@tybys/wasm-util": "^0.10.1" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" }, "bundled": true }, "sha512-sNXv5oLJ7ob93xkZ1XnxisYhGYXfaG9f65/ZgYuAu3qt7b3NadcOEhLvx28hv31PgX8SZJRYrAIPQilQmFpLVw=="],
"@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.1", "", { "dependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1", "@tybys/wasm-util": "^0.10.1" }, "bundled": true }, "sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A=="],
"@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="], "@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="],
@@ -1206,11 +1208,11 @@
"@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="], "@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="],
"@unhead/vue/hookable": ["hookable@6.0.1", "", {}, "sha512-uKGyY8BuzN/a5gvzvA+3FVWo0+wUjgtfSdnmjtrOVwQCZPHpHDH2WRO3VZSOeluYrHoDCiXFffZXs8Dj1ULWtw=="],
"@vue/devtools-api/@vue/devtools-kit": ["@vue/devtools-kit@7.7.9", "", { "dependencies": { "@vue/devtools-shared": "^7.7.9", "birpc": "^2.3.0", "hookable": "^5.5.3", "mitt": "^3.0.1", "perfect-debounce": "^1.0.0", "speakingurl": "^14.0.1", "superjson": "^2.2.2" } }, "sha512-PyQ6odHSgiDVd4hnTP+aDk2X4gl2HmLDfiyEnn3/oV+ckFDuswRs4IbBT7vacMuGdwY/XemxBoh302ctbsptuA=="], "@vue/devtools-api/@vue/devtools-kit": ["@vue/devtools-kit@7.7.9", "", { "dependencies": { "@vue/devtools-shared": "^7.7.9", "birpc": "^2.3.0", "hookable": "^5.5.3", "mitt": "^3.0.1", "perfect-debounce": "^1.0.0", "speakingurl": "^14.0.1", "superjson": "^2.2.2" } }, "sha512-PyQ6odHSgiDVd4hnTP+aDk2X4gl2HmLDfiyEnn3/oV+ckFDuswRs4IbBT7vacMuGdwY/XemxBoh302ctbsptuA=="],
"anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], "@vue/devtools-kit/hookable": ["hookable@5.5.3", "", {}, "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ=="],
"anymatch/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="],
"c12/rc9": ["rc9@2.1.2", "", { "dependencies": { "defu": "^6.1.4", "destr": "^2.0.3" } }, "sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg=="], "c12/rc9": ["rc9@2.1.2", "", { "dependencies": { "defu": "^6.1.4", "destr": "^2.0.3" } }, "sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg=="],
@@ -1226,7 +1228,7 @@
"markdown-it/entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], "markdown-it/entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="],
"micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], "micromatch/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="],
"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=="], "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=="],
@@ -1242,8 +1244,6 @@
"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=="], "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/escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="],
"unimport/estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="], "unimport/estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="],
@@ -1256,9 +1256,11 @@
"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=="], "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=="],
"vaul-vue/reka-ui": ["reka-ui@2.9.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.4.0" } }, "sha512-/t4e6y1hcG+uDuRfpg6tbMz3uUEvRzNco6NeYTufoJeUghy5Iosxos5YL/p+ieAsid84sdMX9OrgDqpEuCJhBw=="],
"vue-i18n/@vue/devtools-api": ["@vue/devtools-api@6.6.4", "", {}, "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g=="], "vue-i18n/@vue/devtools-api": ["@vue/devtools-api@6.6.4", "", {}, "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g=="],
"vue-router/@vue/devtools-api": ["@vue/devtools-api@8.0.7", "", { "dependencies": { "@vue/devtools-kit": "^8.0.7" } }, "sha512-tc1TXAxclsn55JblLkFVcIRG7MeSJC4fWsPjfM7qu/IcmPUYnQ5Q8vzWwBpyDY24ZjmZTUCCwjRSNbx58IhlAA=="], "vue-router/@vue/devtools-api": ["@vue/devtools-api@8.1.1", "", { "dependencies": { "@vue/devtools-kit": "^8.1.1" } }, "sha512-bsDMJ07b3GN1puVwJb/fyFnj/U2imyswK5UQVLZwVl7O05jDrt6BHxeG5XffmOOdasOj/bOmIjxJvGPxU7pcqw=="],
"@nuxtjs/color-mode/@nuxt/kit/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="], "@nuxtjs/color-mode/@nuxt/kit/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="],
@@ -1270,30 +1272,10 @@
"@nuxtjs/color-mode/pkg-types/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], "@nuxtjs/color-mode/pkg-types/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
"@tailwindcss/node/lightningcss/lightningcss-android-arm64": ["lightningcss-android-arm64@1.31.1", "", { "os": "android", "cpu": "arm64" }, "sha512-HXJF3x8w9nQ4jbXRiNppBCqeZPIAfUo8zE/kOEGbW5NZvGc/K7nMxbhIr+YlFlHW5mpbg/YFPdbnCh1wAXCKFg=="],
"@tailwindcss/node/lightningcss/lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.31.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-02uTEqf3vIfNMq3h/z2cJfcOXnQ0GRwQrkmPafhueLb2h7mqEidiCzkE4gBMEH65abHRiQvhdcQ+aP0D0g67sg=="],
"@tailwindcss/node/lightningcss/lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.31.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-1ObhyoCY+tGxtsz1lSx5NXCj3nirk0Y0kB/g8B8DT+sSx4G9djitg9ejFnjb3gJNWo7qXH4DIy2SUHvpoFwfTA=="],
"@tailwindcss/node/lightningcss/lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.31.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-1RINmQKAItO6ISxYgPwszQE1BrsVU5aB45ho6O42mu96UiZBxEXsuQ7cJW4zs4CEodPUioj/QrXW1r9pLUM74A=="],
"@tailwindcss/node/lightningcss/lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.31.1", "", { "os": "linux", "cpu": "arm" }, "sha512-OOCm2//MZJ87CdDK62rZIu+aw9gBv4azMJuA8/KB74wmfS3lnC4yoPHm0uXZ/dvNNHmnZnB8XLAZzObeG0nS1g=="],
"@tailwindcss/node/lightningcss/lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.31.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-WKyLWztD71rTnou4xAD5kQT+982wvca7E6QoLpoawZ1gP9JM0GJj4Tp5jMUh9B3AitHbRZ2/H3W5xQmdEOUlLg=="],
"@tailwindcss/node/lightningcss/lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.31.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-mVZ7Pg2zIbe3XlNbZJdjs86YViQFoJSpc41CbVmKBPiGmC4YrfeOyz65ms2qpAobVd7WQsbW4PdsSJEMymyIMg=="],
"@tailwindcss/node/lightningcss/lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.31.1", "", { "os": "linux", "cpu": "x64" }, "sha512-xGlFWRMl+0KvUhgySdIaReQdB4FNudfUTARn7q0hh/V67PVGCs3ADFjw+6++kG1RNd0zdGRlEKa+T13/tQjPMA=="],
"@tailwindcss/node/lightningcss/lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.31.1", "", { "os": "linux", "cpu": "x64" }, "sha512-eowF8PrKHw9LpoZii5tdZwnBcYDxRw2rRCyvAXLi34iyeYfqCQNA9rmUM0ce62NlPhCvof1+9ivRaTY6pSKDaA=="],
"@tailwindcss/node/lightningcss/lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.31.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-aJReEbSEQzx1uBlQizAOBSjcmr9dCdL3XuC/6HLXAxmtErsj2ICo5yYggg1qOODQMtnjNQv2UHb9NpOuFtYe4w=="],
"@tailwindcss/node/lightningcss/lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.31.1", "", { "os": "win32", "cpu": "x64" }, "sha512-I9aiFrbd7oYHwlnQDqr1Roz+fTz61oDDJX7n9tYF9FJymH1cIN1DtKw3iYt6b8WZgEjoNwVSncwF4wx/ZedMhw=="],
"@vue/devtools-api/@vue/devtools-kit/@vue/devtools-shared": ["@vue/devtools-shared@7.7.9", "", { "dependencies": { "rfdc": "^1.4.1" } }, "sha512-iWAb0v2WYf0QWmxCGy0seZNDPdO3Sp5+u78ORnyeonS6MT4PC7VPrryX2BpMJrwlDeaZ6BD4vP4XKjK0SZqaeA=="], "@vue/devtools-api/@vue/devtools-kit/@vue/devtools-shared": ["@vue/devtools-shared@7.7.9", "", { "dependencies": { "rfdc": "^1.4.1" } }, "sha512-iWAb0v2WYf0QWmxCGy0seZNDPdO3Sp5+u78ORnyeonS6MT4PC7VPrryX2BpMJrwlDeaZ6BD4vP4XKjK0SZqaeA=="],
"@vue/devtools-api/@vue/devtools-kit/hookable": ["hookable@5.5.3", "", {}, "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ=="],
"@vue/devtools-api/@vue/devtools-kit/perfect-debounce": ["perfect-debounce@1.0.0", "", {}, "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA=="], "@vue/devtools-api/@vue/devtools-kit/perfect-debounce": ["perfect-debounce@1.0.0", "", {}, "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA=="],
"cross-spawn/which/isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], "cross-spawn/which/isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
@@ -1305,5 +1287,7 @@
"vaul-vue/@vueuse/core/@vueuse/metadata": ["@vueuse/metadata@10.11.1", "", {}, "sha512-IGa5FXd003Ug1qAZmyE8wF3sJ81xGLSqTqtQ6jaVfkeZ4i5kS2mwQF61yhVqojRnenVew5PldLyRgvdl4YYuSw=="], "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/@vueuse/shared": ["@vueuse/shared@10.11.1", "", { "dependencies": { "vue-demi": ">=0.14.8" } }, "sha512-LHpC8711VFZlDaYUXEBbFBCQ7GS3dVU9mjOhhMhXP6txTV4EhYQg/KGnQuvt/sPAtoUKq7VVUnL6mVtFoL42sA=="],
"vaul-vue/reka-ui/@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=="],
} }
} }

24
bo/components.d.ts vendored
View File

@@ -11,13 +11,17 @@ export {}
/* prettier-ignore */ /* prettier-ignore */
declare module 'vue' { declare module 'vue' {
export interface GlobalComponents { export interface GlobalComponents {
ButtonGoToProfile: typeof import('./src/components/customer-management/ButtonGoToProfile.vue')['default']
CartDetails: typeof import('./src/components/customer/CartDetails.vue')['default'] CartDetails: typeof import('./src/components/customer/CartDetails.vue')['default']
CategoryMenu: typeof import('./src/components/inner/categoryMenu.vue')['default'] CategoryMenu: typeof import('./src/components/inner/CategoryMenu.vue')['default']
copy: typeof import('./src/components/admin/ProductDetailView copy.vue')['default']
CountryCurrencySwitch: typeof import('./src/components/inner/CountryCurrencySwitch.vue')['default']
Cs_PrivacyPolicyView: typeof import('./src/components/terms/cs_PrivacyPolicyView.vue')['default'] Cs_PrivacyPolicyView: typeof import('./src/components/terms/cs_PrivacyPolicyView.vue')['default']
Cs_TermsAndConditionsView: typeof import('./src/components/terms/cs_TermsAndConditionsView.vue')['default'] Cs_TermsAndConditionsView: typeof import('./src/components/terms/cs_TermsAndConditionsView.vue')['default']
En_PrivacyPolicyView: typeof import('./src/components/terms/en_PrivacyPolicyView.vue')['default'] En_PrivacyPolicyView: typeof import('./src/components/terms/en_PrivacyPolicyView.vue')['default']
En_TermsAndConditionsView: typeof import('./src/components/terms/en_TermsAndConditionsView.vue')['default'] En_TermsAndConditionsView: typeof import('./src/components/terms/en_TermsAndConditionsView.vue')['default']
LangSwitch: typeof import('./src/components/inner/langSwitch.vue')['default'] FavoriteProducts: typeof import('./src/components/admin/FavoriteProducts.vue')['default']
LangSwitch: typeof import('./src/components/inner/LangSwitch.vue')['default']
PageAddresses: typeof import('./src/components/customer/PageAddresses.vue')['default'] PageAddresses: typeof import('./src/components/customer/PageAddresses.vue')['default']
PageCarts: typeof import('./src/components/customer/PageCarts.vue')['default'] PageCarts: typeof import('./src/components/customer/PageCarts.vue')['default']
PageOrders: typeof import('./src/components/customer/PageOrders.vue')['default'] PageOrders: typeof import('./src/components/customer/PageOrders.vue')['default']
@@ -30,17 +34,25 @@ declare module 'vue' {
Pl_TermsAndConditionsView: typeof import('./src/components/terms/pl_TermsAndConditionsView.vue')['default'] Pl_TermsAndConditionsView: typeof import('./src/components/terms/pl_TermsAndConditionsView.vue')['default']
ProductCustomization: typeof import('./src/components/customer/components/ProductCustomization.vue')['default'] ProductCustomization: typeof import('./src/components/customer/components/ProductCustomization.vue')['default']
ProductDetailView: typeof import('./src/components/admin/ProductDetailView.vue')['default'] ProductDetailView: typeof import('./src/components/admin/ProductDetailView.vue')['default']
'ProductDetailView copy': typeof import('./src/components/admin/ProductDetailView copy.vue')['default']
ProductEditor: typeof import('./src/components/inner/ProductEditor.vue')['default']
ProductVariants: typeof import('./src/components/customer/components/ProductVariants.vue')['default'] ProductVariants: typeof import('./src/components/customer/components/ProductVariants.vue')['default']
Profile: typeof import('./src/components/customer-management/Profile.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink'] RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView'] RouterView: typeof import('vue-router')['RouterView']
ThemeSwitch: typeof import('./src/components/inner/themeSwitch.vue')['default'] StorageFileBrowser: typeof import('./src/components/customer/StorageFileBrowser.vue')['default']
ThemeSwitch: typeof import('./src/components/inner/ThemeSwitch.vue')['default']
TopBar: typeof import('./src/components/TopBar.vue')['default'] TopBar: typeof import('./src/components/TopBar.vue')['default']
TopBarLogin: typeof import('./src/components/TopBarLogin.vue')['default'] TopBarLogin: typeof import('./src/components/TopBarLogin.vue')['default']
UAlert: typeof import('./node_modules/@nuxt/ui/dist/runtime/components/Alert.vue')['default'] UAlert: typeof import('./node_modules/@nuxt/ui/dist/runtime/components/Alert.vue')['default']
UAvatar: typeof import('./node_modules/@nuxt/ui/dist/runtime/components/Avatar.vue')['default']
UButton: typeof import('./node_modules/@nuxt/ui/dist/runtime/components/Button.vue')['default'] UButton: typeof import('./node_modules/@nuxt/ui/dist/runtime/components/Button.vue')['default']
UCard: typeof import('./node_modules/@nuxt/ui/dist/runtime/components/Card.vue')['default'] UCard: typeof import('./node_modules/@nuxt/ui/dist/runtime/components/Card.vue')['default']
UCheckbox: typeof import('./node_modules/@nuxt/ui/dist/runtime/components/Checkbox.vue')['default'] UCheckbox: typeof import('./node_modules/@nuxt/ui/dist/runtime/components/Checkbox.vue')['default']
UDrawer: typeof import('./node_modules/@nuxt/ui/dist/runtime/components/Drawer.vue')['default'] UDrawer: typeof import('./node_modules/@nuxt/ui/dist/runtime/components/Drawer.vue')['default']
UDropdownMenu: typeof import('./node_modules/@nuxt/ui/dist/runtime/components/DropdownMenu.vue')['default']
UEditor: typeof import('./node_modules/@nuxt/ui/dist/runtime/components/Editor.vue')['default']
UEditorToolbar: typeof import('./node_modules/@nuxt/ui/dist/runtime/components/EditorToolbar.vue')['default']
UForm: typeof import('./node_modules/@nuxt/ui/dist/runtime/components/Form.vue')['default'] UForm: typeof import('./node_modules/@nuxt/ui/dist/runtime/components/Form.vue')['default']
UFormField: typeof import('./node_modules/@nuxt/ui/dist/runtime/components/FormField.vue')['default'] 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'] UIcon: typeof import('./node_modules/@nuxt/ui/dist/runtime/vue/components/Icon.vue')['default']
@@ -51,6 +63,12 @@ declare module 'vue' {
UPagination: typeof import('./node_modules/@nuxt/ui/dist/runtime/components/Pagination.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'] 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'] USelectMenu: typeof import('./node_modules/@nuxt/ui/dist/runtime/components/SelectMenu.vue')['default']
UsersList: typeof import('./src/components/admin/UsersList.vue')['default']
UsersSearch: typeof import('./src/components/admin/UsersSearch.vue')['default']
USidebar: typeof import('./node_modules/@nuxt/ui/dist/runtime/components/Sidebar.vue')['default']
UTable: typeof import('./node_modules/@nuxt/ui/dist/runtime/components/Table.vue')['default'] UTable: typeof import('./node_modules/@nuxt/ui/dist/runtime/components/Table.vue')['default']
UTabs: typeof import('./node_modules/@nuxt/ui/dist/runtime/components/Tabs.vue')['default']
UTextarea: typeof import('./node_modules/@nuxt/ui/dist/runtime/components/Textarea.vue')['default']
UTree: typeof import('./node_modules/@nuxt/ui/dist/runtime/components/Tree.vue')['default']
} }
} }

View File

@@ -13,21 +13,22 @@
"format": "prettier --write --experimental-cli src/" "format": "prettier --write --experimental-cli src/"
}, },
"dependencies": { "dependencies": {
"@nuxt/ui": "^4.5.0", "@nuxt/ui": "^4.6.0",
"@tailwindcss/vite": "^4.2.0", "@tailwindcss/vite": "^4.2.2",
"chart.js": "^4.5.1", "chart.js": "^4.5.1",
"pinia": "^3.0.4", "pinia": "^3.0.4",
"tailwindcss": "^4.2.0", "reka-ui": "^2.9.3",
"tailwindcss": "^4.2.2",
"vue": "beta", "vue": "beta",
"vue-chartjs": "^5.3.3", "vue-chartjs": "^5.3.3",
"vue-i18n": "11", "vue-i18n": "^11.3.0",
"vue-router": "^5.0.2" "vue-router": "^5.0.4"
}, },
"devDependencies": { "devDependencies": {
"@tsconfig/node24": "^24.0.4", "@tsconfig/node24": "^24.0.4",
"@types/node": "^24.10.13", "@types/node": "^24.12.0",
"@vitejs/plugin-vue": "^6.0.4", "@vitejs/plugin-vue": "^6.0.5",
"@vue/eslint-config-typescript": "^14.6.0", "@vue/eslint-config-typescript": "^14.7.0",
"@vue/tsconfig": "^0.8.1", "@vue/tsconfig": "^0.8.1",
"jiti": "^2.6.1", "jiti": "^2.6.1",
"npm-run-all2": "^8.0.4", "npm-run-all2": "^8.0.4",
@@ -35,8 +36,8 @@
"prettier": "3.8.1", "prettier": "3.8.1",
"typescript": "~5.9.3", "typescript": "~5.9.3",
"vite": "beta", "vite": "beta",
"vite-plugin-vue-devtools": "^8.0.6", "vite-plugin-vue-devtools": "^8.1.1",
"vue-tsc": "^3.2.4" "vue-tsc": "^3.2.6"
}, },
"engines": { "engines": {
"node": "^20.19.0 || >=22.12.0" "node": "^20.19.0 || >=22.12.0"

View File

@@ -1,9 +1,17 @@
<script setup lang="ts"> <script setup lang="ts">
import { TooltipProvider } from 'reka-ui'
import { RouterView } from 'vue-router' import { RouterView } from 'vue-router'
import { useAuthStore } from './stores/customer/auth'
const authStore = useAuthStore()
</script> </script>
<template> <template>
<Suspense> <Suspense>
<RouterView /> <TooltipProvider>
<RouterView />
</TooltipProvider>
</Suspense> </Suspense>
</template> </template>

View File

@@ -8,9 +8,6 @@ export const uiOptions: NuxtUIOptions = {
} }
}, },
button: { button: {
slots: {
base: 'border! border-(--border-light)! dark:border-(--border-dark)! outline-0! ring-0!',
},
}, },
input: { input: {
slots: { slots: {
@@ -40,16 +37,17 @@ export const uiOptions: NuxtUIOptions = {
}, },
selectMenu: { selectMenu: {
slots: { slots: {
base: 'border! border-(--border-light)! dark:border-(--border-dark)! outline-0! ring-0!', // base: 'border! border-(--border-light)! dark:border-(--border-dark)! outline-0! ring-0!',
content: 'border! border-(--border-light)! dark:border-(--border-dark)! outline-0! ring-0! z-80 text-(--black)! dark:text-white!', // content: 'border! border-(--border-light)! dark:border-(--border-dark)! outline-0! ring-0! z-80',
itemLeadingIcon: 'text-(--black)! dark:text-white!' // content: 'border! border-(--border-light)! dark:border-(--border-dark)! outline-0! ring-0! z-80 text-(--black)! dark:text-white!',
// itemLeadingIcon: 'text-(--black)! dark:text-white!'
} }
}, },
table: { table: {
slots: { slots: {
base: 'border! border-(--border-light)! dark:border-(--border-dark)! outline-0! ring-0! bg-(--second-light) dark:bg-(--main-dark)', base: 'border! border-(--border-light)! dark:border-(--border-dark)! outline-0! ring-0! bg-(--second-light) dark:bg-(--main-dark)',
tr: 'border-b! border-(--border-light)! dark:border-(--border-dark)! outline-0! ring-0! text-(--black)! dark:text-white!', // tr: 'border-b! border-(--border-light)! dark:border-(--border-dark)! outline-0! ring-0! text-(--black)! dark:text-white!',
} }
}, },
@@ -58,6 +56,11 @@ export const uiOptions: NuxtUIOptions = {
content: 'border! border-(--border-light)! dark:border-(--border-dark)! outline-0! ring-0! bg-(--second-light) dark:bg-(--main-dark)', content: 'border! border-(--border-light)! dark:border-(--border-dark)! outline-0! ring-0! bg-(--second-light) dark:bg-(--main-dark)',
} }
},
textarea: {
slots: {
base: 'ring-0! outline-0! rounded-md border border-(--border-light)! dark:border-(--border-dark)! text-base!'
}
} }
} }
} }

1
bo/src/assets/error.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g fill="none"><path stroke="#5b5b5b" stroke-width="2" d="M3 11c0-3.771 0-5.657 1.172-6.828S7.229 3 11 3h2c3.771 0 5.657 0 6.828 1.172S21 7.229 21 11v2c0 3.771 0 5.657-1.172 6.828S16.771 21 13 21h-2c-3.771 0-5.657 0-6.828-1.172S3 16.771 3 13z"/><path fill="#5b5b5b" fill-rule="evenodd" d="m19 13.585l-.02-.02c-.39-.39-.726-.726-1.026-.979c-.317-.267-.662-.502-1.088-.63a3 3 0 0 0-1.732 0c-.426.129-.77.363-1.088.63c-.3.253-.636.59-1.025.98l-.029.028c-.307.306-.487.486-.628.601l-.02.017l-.012-.023c-.087-.16-.189-.393-.36-.792l-.053-.124l-.022-.052c-.356-.83-.655-1.528-.95-2.054c-.305-.541-.685-1.05-1.277-1.346a3 3 0 0 0-1.597-.307c-.66.055-1.201.386-1.685.775c-.406.327-.86.77-1.388 1.297V13q0 .774.003 1.411l1.114-1.114c.69-.69 1.15-1.147 1.525-1.45c.37-.298.53-.335.6-.34a1 1 0 0 1 .532.102c.061.031.196.124.43.54c.236.42.493 1.016.877 1.912l.053.124l.017.038c.149.348.287.67.425.924c.145.265.355.583.709.802a2 2 0 0 0 1.398.27c.41-.073.723-.29.956-.482c.222-.184.47-.432.738-.7l.03-.03c.425-.425.701-.7.928-.891c.218-.184.32-.228.376-.245a1 1 0 0 1 .578 0c.056.017.158.061.376.245c.227.191.503.466.929.892l1.35 1.35c.046-.718.054-1.61.056-2.773" clip-rule="evenodd"/><circle cx="16.5" cy="7.5" r="1.5" fill="#5b5b5b"/></g></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -5,19 +5,22 @@ body {
font-family: "Inter", sans-serif; font-family: "Inter", sans-serif;
} }
li {
margin-left: 20px
}
.inter { .inter {
font-family: "Inter", sans-serif; font-family: "Inter", sans-serif;
} }
.container{ .container {
max-width: 2100px; max-width: 2100px;
} }
@theme { @theme {
--main-light: #FFFEFB; --main-light: #FFFEFB;
--second-light: #F5F6FA; --second-light: #F8FAFC;
--main-dark: #1A1A1A;
--main-dark: #212121;
--black: #1A1A1A; --black: #1A1A1A;
/* gray */ /* gray */
@@ -25,13 +28,15 @@ body {
--gray-dark: #6B7280; --gray-dark: #6B7280;
/* borders */ /* borders */
--border-light: #E8E7E0; --border-light: #E2E8F0;
--border-dark: #3F3E3D; --border-dark: #334155;
/* text */ /* text */
--accent-blue-dark: #3B82F6; --accent-blue-dark: #0EA5E91A;
--accent-blue-light:#2563EB; --accent-blue-light: #E0F2FE;
--text-dark: #FFFEFB; --text-dark: #FFFEFB;
--text-sky-light: #0284C7;
--text-sky-dark: #38BDF8;
/* placeholder */ /* placeholder */
--placeholder: #8C8C8A; --placeholder: #8C8C8A;
@@ -40,11 +45,11 @@ body {
--ui-border-accented: var(--border-light); --ui-border-accented: var(--border-light);
--ui-border: var(--border-light); --ui-border: var(--border-light);
--ui-color-neutral-700: var(--black); --ui-color-neutral-700: var(--black);
--ui-primary: var(--accent-blue-light)
} }
.dark { .dark {
--ui-bg: var(--black); --ui-bg: var(--black);
--ui-primary: var(--color-gray-500);
--ui-secondary: var(--accent-green-dark); --ui-secondary: var(--accent-green-dark);
--ui-border-accented: var(--border-dark); --ui-border-accented: var(--border-dark);
--ui-text-dimmed: var(--gray-dark); --ui-text-dimmed: var(--gray-dark);
@@ -52,22 +57,27 @@ body {
--ui-bg-elevated: var(--color-gray-500); --ui-bg-elevated: var(--color-gray-500);
--ui-error: var(--dark-red); --ui-error: var(--dark-red);
--border: var(--border-dark); --border: var(--border-dark);
--tw-border-style: var(--border-dark); --tw-border-style: var(--border-dark);
--ui-text-inverted: #fff;
--ui-primary: var(--accent-blue-dark)
} }
.label-form { .label-form {
@apply text-(--gray) dark:text-(--gray-dark) pl-0 md:pl-6 leading-none; @apply text-(--gray) dark:text-(--gray-dark) pl-0 md:pl-6 leading-none;
} }
.title { .title {
@apply font-medium text-[19px] sm:text-xl md:text-[22px] leading-none text-(--black) dark:text-(--main-light); @apply font-medium text-[19px] sm:text-xl md:text-[22px] leading-none text-(--black) dark:text-(--main-light);
} }
.column-title { .column-title {
@apply md:ml-[25px] mb-[25px] sm:mb-[30px]; @apply md:ml-[25px] mb-[25px] sm:mb-[30px];
} }
.form-title { .form-title {
@apply text-(--accent-green) dark:text-(--accent-green-dark) font-medium; @apply text-(--accent-green) dark:text-(--accent-green-dark) font-medium;
} }
.blue-button {
@apply bg-info! text-white!
}

View File

@@ -1,13 +1,51 @@
<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="px-4">
<div class="flex items-center justify-between h-16">
<RouterLink :to="{ name: 'home' }" class="flex items-center gap-2">
<div class="w-8 h-8 rounded-lg bg-primary text-white flex items-center justify-center">
<UIcon name="i-heroicons-clock" class="w-5 h-5" />
</div>
<span class="font-semibold text-gray-900 dark:text-white">{{ settings.app.name }}</span>
</RouterLink>
<UNavigationMenu :type="'trigger'" :ui="{
root: 'justify-center',
list: 'gap-4 text-(--black)',
linkLabel: 'text-(--black) text-sm!'
}" :items="menuItems" class="" />
<!-- orientation="vertical" -->
<div class="flex items-center gap-12">
<div class="flex items-center gap-2">
<CountryCurrencySwitch />
<LangSwitch />
</div>
<div class="flex items-center gap-2">
<ThemeSwitch />
<button v-if="authStore.isAuthenticated" @click="authStore.logout()"
class="px-3 py-1.5 text-sm font-medium text-black dark:text-white hover:text-black dark:hover:text-white hover:bg-gray-100 dark:hover:bg-gray-600 rounded-lg transition-colors border border-(--border-light) dark:border-(--border-dark) whitespace-nowrap">
{{ $t('general.logout') }}
</button>
</div>
</div>
</div>
</div>
</header>
</template>
<script setup lang="ts"> <script setup lang="ts">
import { useFetchJson } from '@/composable/useFetchJson' import { useFetchJson } from '@/composable/useFetchJson'
import LangSwitch from './inner/langSwitch.vue' import LangSwitch from './inner/LangSwitch.vue'
import ThemeSwitch from './inner/themeSwitch.vue' import ThemeSwitch from './inner/ThemeSwitch.vue'
import { useAuthStore } from '@/stores/auth' import { useAuthStore } from '@/stores/customer/auth'
import { computed, onMounted, ref } from 'vue' import { computed, ref } from 'vue'
import { currentLang } from '@/router/langs' import { currentLang } from '@/router/langs'
import type { LabelTrans, TopMenuItem } from '@/types' import type { LabelTrans, TopMenuItem } from '@/types'
import type { NavigationMenuItem } from '@nuxt/ui' import type { NavigationMenuItem } from '@nuxt/ui'
import { useRoute, useRouter } from 'vue-router' import { useRoute, useRouter } from 'vue-router'
import CountryCurrencySwitch from './inner/CountryCurrencySwitch.vue'
import { settings } from '@/router/settings'
const authStore = useAuthStore() const authStore = useAuthStore()
let menu = ref() let menu = ref()
@@ -51,37 +89,3 @@ function transformMenu(items: TopMenuItem[], locale: string | undefined): Naviga
} }
await getTopMenu() await getTopMenu()
</script> </script>
<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)">
<!-- px-4 sm:px-6 lg:px-8 -->
<div class="container mx-auto px-4">
<div class="flex items-center justify-between h-14">
<!-- Logo -->
<RouterLink :to="{ name: 'home' }" class="flex items-center gap-2">
<div class="w-8 h-8 rounded-lg bg-primary text-white flex items-center justify-center">
<UIcon name="i-heroicons-clock" class="w-5 h-5" />
</div>
<span class="font-semibold text-gray-900 dark:text-white">TimeTracker</span>
</RouterLink>
<UNavigationMenu :type="'trigger'" :ui="{
root: 'justify-center',
list: 'gap-4'
}" :items="menuItems" class="w-full"></UNavigationMenu>
<div class="flex items-center gap-2">
<!-- Language Switcher -->
<LangSwitch />
<!-- Theme Switcher -->
<ThemeSwitch />
<!-- Logout Button (only when authenticated) -->
<button v-if="authStore.isAuthenticated" @click="authStore.logout()"
class="px-3 py-1.5 text-sm font-medium text-black dark:text-white hover:text-black dark:hover:text-white hover:bg-gray-100 dark:hover:bg-gray-600 rounded-lg transition-colors border border-(--border-light) dark:border-(--border-dark)">
{{ $t('general.logout') }}
</button>
</div>
</div>
</div>
</header>
</template>

View File

@@ -1,7 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import LangSwitch from './inner/langSwitch.vue' import LangSwitch from './inner/LangSwitch.vue'
import ThemeSwitch from './inner/themeSwitch.vue' import ThemeSwitch from './inner/ThemeSwitch.vue'
import { useAuthStore } from '@/stores/auth' import { useAuthStore } from '@/stores/customer/auth'
const authStore = useAuthStore() const authStore = useAuthStore()
</script> </script>

View File

@@ -0,0 +1,12 @@
<template>
<component :is="Default || 'div'">
<div>
</div>
</component>
</template>
<script setup lang="ts">
import Default from '@/layouts/default.vue';
</script>

View File

@@ -1,15 +1,15 @@
<template> <template>
<suspense> <component :is="Default || 'div'">
<component :is="Default || 'div'"> <div class="flex flex-col md:flex-row gap-10">
<div class="flex gap-10"> <CategoryMenu />
<CategoryMenu /> <div class="w-full flex flex-col items-center gap-4">
<div class="w-full flex flex-col items-center gap-4"> <UTable :data="productsList" :columns="columns" class="flex-1 w-full" :ui="{
<UTable :data="productsList" :columns="columns" class="flex-1 w-full" /> root: 'max-w-100wv overflow-auto!'
<UPagination v-model:page="page" :total="total" :items-per-page="perPage" /> }" />
</div> <UPagination v-model:page="page" :total="total" :items-per-page="perPage" />
</div> </div>
</component> </div>
</suspense> </component>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
@@ -18,7 +18,7 @@ import { useFetchJson } from '@/composable/useFetchJson'
import Default from '@/layouts/default.vue' import Default from '@/layouts/default.vue'
import { useRoute, useRouter } from 'vue-router' import { useRoute, useRouter } from 'vue-router'
import type { TableColumn } from '@nuxt/ui' import type { TableColumn } from '@nuxt/ui'
import CategoryMenu from '../inner/categoryMenu.vue' import CategoryMenu from '../inner/CategoryMenu.vue'
import type { Product } from '@/types/product' import type { Product } from '@/types/product'
const router = useRouter() const router = useRouter()
@@ -141,7 +141,7 @@ async function fetchProductList() {
if (route.params.category_id) if (route.params.category_id)
params.append('category_id', String(route.params.category_id)) params.append('category_id', String(route.params.category_id))
const url = `/api/v1/restricted/list/list-products?elems=${perPage.value}&${params.toString()}` const url = `/api/v1/restricted/product/list?elems=${perPage.value}&${params.toString()}`
try { try {
const response = await useFetchJson<ApiResponse>(url) const response = await useFetchJson<ApiResponse>(url)
productsList.value = response.items || [] productsList.value = response.items || []
@@ -153,10 +153,16 @@ async function fetchProductList() {
} }
} }
function goToProduct(productId: number) { function goToProduct(productId: number, linkRewrite: string) {
let path = {
name: route.name,
params: route.params,
query: route.query
}
localStorage.setItem('back_from_product', JSON.stringify(path))
router.push({ router.push({
name: 'customer-product-details', name: 'admin-product-details',
params: { product_id: productId } params: { product_id: productId, link_rewrite: linkRewrite }
}) })
} }
@@ -173,6 +179,7 @@ const UInput = resolveComponent('UInput')
const UButton = resolveComponent('UButton') const UButton = resolveComponent('UButton')
const UIcon = resolveComponent('UIcon') const UIcon = resolveComponent('UIcon')
import errorImg from '@/assets/error.svg'
const columns: TableColumn<Product>[] = [ const columns: TableColumn<Product>[] = [
{ {
accessorKey: 'product_id', accessorKey: 'product_id',
@@ -209,9 +216,13 @@ const columns: TableColumn<Product>[] = [
cell: ({ row }) => { cell: ({ row }) => {
return h('img', { return h('img', {
src: row.getValue('image_link') as string, src: row.getValue('image_link') as string,
style: 'width:40px;height:40px;object-fit:cover;' style: 'width:40px;height:40px;object-fit:cover;',
onError: (e: Event) => {
const target = e.target as HTMLImageElement
target.src = errorImg
}
}) })
} },
}, },
{ {
accessorKey: 'name', accessorKey: 'name',
@@ -270,10 +281,11 @@ const columns: TableColumn<Product>[] = [
cell: ({ row }) => { cell: ({ row }) => {
return h(UButton, { return h(UButton, {
onClick: () => { onClick: () => {
goToProduct(row.original.product_id) goToProduct(row.original.product_id, row.original.link_rewrite)
}, },
color: 'primary', class: 'cursor-pointer',
variant: 'solid' color: 'info',
variant: 'soft'
}, () => 'Show product') }, () => 'Show product')
}, },
} }

View File

@@ -0,0 +1,526 @@
<template>
<component :is="Default || 'div'">
<div class="flex items-center gap-2 mb-4">
<UIcon name="line-md:arrow-left" class="text-(--text-sky-light) dark:text-(--text-sky-dark)" />
<p class="cursor-pointer text-(--text-sky-light) dark:text-(--text-sky-dark)" @click="backFromProduct()">
Back to products</p>
</div>
<div class="container mx-auto ">
<div
class="gap-4 mb-6 bg-slate-50 dark:bg-(--main-dark) border border-(--border-light) dark:border-(--border-dark) p-4 rounded-md">
<div class="flex items-center justify-between">
<div class="flex flex-col items-center gap-3" v-if="!isTranslations">
<USelectMenu v-model="toLangId" :items="langs" value-key="id"
class="w-48 bg-(--main-light) dark:bg-(--black) rounded-md shadow-sm" :searchInput="false">
<template #default>
<div class="flex flex-col items-start leading-tight">
<span class="text-xs text-gray-400">
Selected language
</span>
<span class="font-medium dark:text-white text-black">
{{langs.find(l => l.id === toLangId)?.name || 'Select language'}}
</span>
</div>
</template>
<template #item-leading="{ item }">
<div class="flex items-center gap-2 cursor-pointer">
<span class="text-lg">{{ item.flag }}</span>
<span class="font-medium dark:text-white text-black">
{{ item.name }}
</span>
</div>
</template>
</USelectMenu>
</div>
<div v-if="toLangId !== settingStore.shopDefaultLanguage && !isTranslations" class="flex gap-7">
<UButton @click="translateToSelectedLanguage" color="info" :loading="translating">
Translate from Polish to {{langs.find(l => l.id === toLangId)?.name}}
</UButton>
</div>
<div v-if="isTranslations" class="flex gap-3 w-full justify-end">
<UButton @click="() => {
toLangId = settingStore.shopDefaultLanguage
isTranslations = false
}" color="info" variant="outline">
Cancel and back to Polish
</UButton>
<UButton color="info" @click="productStore.saveProductDescription(productID, toLangId)">
Save translations
</UButton>
</div>
<!-- <div class="flex gap-7">
<div v-if="isTranslations === false">
<UButton @click="() => {
fetchForLanguage(toLangId)
isShowProductView = true
}" class="text-(--accent-blue-light) dark:text-(--accent-blue-dark)"
color="neutral" variant="outline">
<p class="dark:text-white"> Show product</p>
</UButton>
<UButton @click="translateToSelectedLanguage" color="primary" :loading="translating"
class="text-white bg-(--accent-blue-light) dark:bg-(--accent-blue-dark) flex items-center! justify-center! px-12">
Translate
</UButton>
</div>
<div v-else class="flex gap-3">
<UButton @click="() => {
toLangId = settingStore.shopDefaultLanguage
isTranslations = false
}" color="primary" class="text-white bg-(--accent-blue-light) dark:bg-(--accent-blue-dark)">
Cancel
</UButton>
<UButton color="primary" @click="productStore.saveProductDescription(productID, toLangId)"
class="text-white bg-(--accent-blue-light) dark:bg-(--accent-blue-dark)">
Save
</UButton>
</div>
</div> -->
</div>
</div>
<!-- <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 v-if="productStore.loading" class="flex items-center justify-center py-20">
<UIcon name="svg-spinners:ring-resize" class="text-4xl text-primary" />
</div>
<div v-else-if="productStore.error" class="flex items-center justify-center py-20">
<p class="text-red-500">{{ productStore.error }}</p>
</div>
<div v-else-if="productStore.productDescription" class="">
<div class="flex items-start gap-30">
<div class="w-80 h-80 bg-(--second-light) dark:bg-gray-700 rounded-lg flex items-center justify-center">
<span class="text-gray-500 dark:text-gray-400">Product Image</span>
</div>
<div class="flex flex-col gap-2">
<p class="text-[25px] font-bold text-black dark:text-white">
{{ productStore.productDescription.name || 'Product 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] light:text-(--accent-green-light) dark:text-(--accent-green-dark)" />
<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] light:text-(--accent-green-light) dark:text-(--accent-green-dark)" />
<p class="text-[18px] font-bold text-black dark:text-white">
{{ productStore.productDescription.delivery_in_stock || 'Delivery information' }}
</p>
</div>
</div>
</div>
</div>
<UTabs :items="items" v-model="activeTab" color="info" :content="true" :ui="{
list : 'w-auto!'
}">
<template #description>
<div v-if="!isTranslations" class="flex justify-end items-center gap-3 mb-4">
<UButton v-if="!isEditing" @click="activeTab === 'usage' ? enableEdit() : 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="isEditing" @click="activeTab === 'usage' ? saveText : 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="isEditing" @click="activeTab === 'usage' ? cancelEdit : cancelDescriptionEdit"
color="neutral" variant="outline" class="p-2.5 cursor-pointer">
Cancel
</UButton>
</div>
<UEditor v-if="isTranslations" v-slot="{ editor }" v-model="productStore.productDescription.description"
content-type="html" :ui="{ base: 'p-8 sm:px-16' }" class="w-full min-h-74" placeholder="Write there ...">
<UEditorToolbar :editor="editor" :items="toolbarItems" class="sm:px-8">
<template #link>
<EditorLinkPopover :editor="editor" auto-open />
</template>
</UEditorToolbar>
</UEditor>
<p v-else v-html="productStore.productDescription.description" class="text-black dark:text-white"></p>
</template>
<template #usage>
<div class="px-7">
<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>
<UEditor v-if="isTranslations" v-slot="{ editor }" v-model="productStore.productDescription.usage"
content-type="html" :ui="{ base: 'p-8 sm:px-16' }" class="w-full min-h-74" placeholder="Write there ...">
<UEditorToolbar :editor="editor" :items="toolbarItems" class="sm:px-8">
<template #link>
<EditorLinkPopover :editor="editor" auto-open />
</template>
</UEditorToolbar>
</UEditor>
<p v-else v-html="productStore.productDescription.usage" class="text-black dark:text-white"></p>
</div>
</template>
</UTabs>
</div>
</div>
</component>
</template>
<script setup lang="ts">
import { useEditable } from '@/composable/useConteditable';
import Default from '@/layouts/default.vue';
import { langs } from '@/router/langs';
import { useProductStore } from '@/stores/product';
import { useSettingsStore } from '@/stores/admin/settings';
import type { EditorToolbarItem } from '@nuxt/ui';
import { onMounted, ref, watch } from 'vue';
import { useRoute, useRouter } from 'vue-router';
const toolbarItems: EditorToolbarItem[][] = [
// History controls
[{
kind: 'undo',
icon: 'i-lucide-undo',
tooltip: { text: 'Undo' }
}, {
kind: 'redo',
icon: 'i-lucide-redo',
tooltip: { text: 'Redo' }
}],
// Block types
[{
icon: 'i-lucide-heading',
tooltip: { text: 'Headings' },
content: {
align: 'start'
},
items: [{
kind: 'heading',
level: 1,
icon: 'i-lucide-heading-1',
label: 'Heading 1'
}, {
kind: 'heading',
level: 2,
icon: 'i-lucide-heading-2',
label: 'Heading 2'
}, {
kind: 'heading',
level: 3,
icon: 'i-lucide-heading-3',
label: 'Heading 3'
}, {
kind: 'heading',
level: 4,
icon: 'i-lucide-heading-4',
label: 'Heading 4'
}]
}, {
icon: 'i-lucide-list',
tooltip: { text: 'Lists' },
content: {
align: 'start'
},
items: [{
kind: 'bulletList',
icon: 'i-lucide-list',
label: 'Bullet List'
}, {
kind: 'orderedList',
icon: 'i-lucide-list-ordered',
label: 'Ordered List'
}]
}, {
kind: 'blockquote',
icon: 'i-lucide-text-quote',
tooltip: { text: 'Blockquote' }
}, {
kind: 'codeBlock',
icon: 'i-lucide-square-code',
tooltip: { text: 'Code Block' }
}, {
kind: 'horizontalRule',
icon: 'i-lucide-separator-horizontal',
tooltip: { text: 'Horizontal Rule' }
}],
// Text formatting
[{
kind: 'mark',
mark: 'bold',
icon: 'i-lucide-bold',
tooltip: { text: 'Bold' }
}, {
kind: 'mark',
mark: 'italic',
icon: 'i-lucide-italic',
tooltip: { text: 'Italic' }
}, {
kind: 'mark',
mark: 'underline',
icon: 'i-lucide-underline',
tooltip: { text: 'Underline' }
}, {
kind: 'mark',
mark: 'strike',
icon: 'i-lucide-strikethrough',
tooltip: { text: 'Strikethrough' }
}, {
kind: 'mark',
mark: 'code',
icon: 'i-lucide-code',
tooltip: { text: 'Code' }
}],
// Link
[{
kind: 'link',
icon: 'i-lucide-link',
tooltip: { text: 'Link' }
}],
// Text alignment
[{
icon: 'i-lucide-align-justify',
tooltip: { text: 'Text Align' },
content: {
align: 'end'
},
items: [{
kind: 'textAlign',
align: 'left',
icon: 'i-lucide-align-left',
label: 'Align Left'
}, {
kind: 'textAlign',
align: 'center',
icon: 'i-lucide-align-center',
label: 'Align Center'
}, {
kind: 'textAlign',
align: 'right',
icon: 'i-lucide-align-right',
label: 'Align Right'
}, {
kind: 'textAlign',
align: 'justify',
icon: 'i-lucide-align-justify',
label: 'Align Justify'
}]
}]
]
const router = useRouter()
function backFromProduct() {
let path = localStorage.getItem('back_from_product')
if (path) {
let res = JSON.parse(path)
router.push({
name: res.name,
params: res.params,
query: res.query
})
localStorage.removeItem('back_from_product')
} else {
router.push({
name: 'customer-products',
})
}
}
const route = useRoute()
const settingStore = useSettingsStore()
const productStore = useProductStore()
const isTranslations = ref(false)
const toLangId = ref<number>(settingStore.shopDefaultLanguage)
const productID = ref<number>(0)
const translating = ref(false)
const activeTab = ref('description')
const usageRef = ref<HTMLElement | null>(null)
const originalUsage = ref('')
const isEditing = ref(false)
const usageEdit = useEditable(usageRef)
const descriptionRef = ref<HTMLElement | null>(null)
const descriptionEdit = useEditable(descriptionRef)
const originalDescription = ref('')
const translateToSelectedLanguage = async () => {
if (toLangId.value && productID.value) {
translating.value = true
try {
await productStore.translateProductDescription(productID.value, toLangId.value)
} finally {
translating.value = false
enableEdit()
enableDescriptionEdit()
}
}
if (productStore.productDescription) {
isTranslations.value = true
}
}
const fetchForLanguage = async (langId: number | null) => {
if (productID.value) {
await productStore.getProductDescription(langId, productID.value)
removeInlineStylesFromAll(productStore.productDescription)
}
}
watch(toLangId, () => {
fetchForLanguage(toLangId.value)
})
onMounted(async () => {
const id = route.params.product_id
if (id) {
productID.value = Number(id)
await fetchForLanguage(toLangId.value)
}
})
const items = ref([
{ label: 'Description', slot: 'description', name: 'description' },
{ label: 'Usage', slot: 'usage', name: 'usage' }
])
// text edit
const enableEdit = () => {
console.log(usageRef.value, 'usageRef');
if (usageRef.value) {
originalUsage.value = usageRef.value.innerHTML
}
isEditing.value = true
usageEdit.enableEdit()
}
const enableDescriptionEdit = () => {
console.log(usageRef.value, 'usageRef');
if (descriptionRef.value) {
originalDescription.value = descriptionRef.value.innerHTML
}
descriptionEdit.enableEdit()
}
const saveText = () => {
if (usageRef.value) {
productStore.productDescription.usage = usageRef.value.innerHTML
}
usageEdit.disableEdit()
isEditing.value = false
productStore.saveProductDescription(productID.value, toLangId.value)
}
const cancelEdit = () => {
if (usageRef.value) {
usageRef.value.innerHTML = originalUsage.value
}
usageEdit.disableEdit()
isEditing.value = false
}
const saveDescription = async () => {
if (descriptionRef.value) {
productStore.productDescription.description = descriptionRef.value.innerHTML
}
descriptionEdit.disableEdit()
await productStore.saveProductDescription(productID.value, toLangId.value)
toLangId.value = settingStore.shopDefaultLanguage
isTranslations.value = false
}
const cancelDescriptionEdit = () => {
if (descriptionRef.value) {
descriptionRef.value.innerHTML = originalDescription.value
}
descriptionEdit.disableEdit()
}
function removeInlineStylesFromAll(product) {
if (!product) return
const removeStyles = (html: string) => {
if (!html) return ''
const div = document.createElement('div')
div.innerHTML = html
div.querySelectorAll('*').forEach(el => {
const bg = el.style.background || el.style.backgroundColor
if (bg) {
el.style.background = ''
el.style.backgroundColor = ''
}
})
div.querySelectorAll('p').forEach(p => {
if (p.querySelector('img')) {
p.style.display = 'flex'
p.style.gap = '20px'
p.style.padding = '30px 0'
}
})
div.querySelectorAll('table').forEach(table => {
table.querySelectorAll('tbody').forEach(tbody => {
tbody.style.setProperty('background-color', '#F8FAFC', 'important')
tbody.querySelectorAll('tr').forEach(tr => {
tr.querySelectorAll('td').forEach(td => {
td.style.setProperty('padding', '10px', 'important')
td.style.setProperty('border-top', '1pt solid black', 'important')
td.style.setProperty('border-left', '1pt solid black', 'important')
td.style.setProperty('border-bottom', '1pt solid black', 'important')
td.style.setProperty('border-right', '1pt solid black', 'important')
td.querySelectorAll('p').forEach(p => {
p.style.setProperty('display', 'flex', 'important')
p.style.setProperty('padding', '0px', 'important')
p.style.setProperty('align-items', 'start', 'important')
p.style.setProperty('color', 'black', 'important')
p.style.setProperty('font-size', '14px', 'important')
p.querySelectorAll('span').forEach(span => {
span.style.setProperty('color', 'black', 'important')
span.style.setProperty('font-size', '14px', 'important')
})
})
})
})
})
})
return div.innerHTML
}
product.description = removeStyles(product.description)
product.description_short = removeStyles(product.description_short)
product.usage = removeStyles(product.usage)
}
</script>

View File

@@ -1,37 +1,62 @@
<template> <template>
<component :is="Default || 'div'"> <component :is="Default || 'div'">
<div class="container my-10 mx-auto "> <div class="flex items-center gap-2 mb-4">
<UIcon name="line-md:arrow-left" class="text-(--text-sky-light) dark:text-(--text-sky-dark)" />
<p class="cursor-pointer text-(--text-sky-light) dark:text-(--text-sky-dark)" @click="backFromProduct()">
Back to products</p>
</div>
<div>
<div class="gap-4 mb-6 border-b border-(--border-light) dark:border-(--border-dark) py-4">
<div class="flex flex-wrap gap-4 items-center justify-between">
<div class="flex flex-col items-center gap-3" v-if="!isTranslations">
<USelectMenu v-model="toLangId" :items="langs" value-key="id"
class="w-48 bg-(--main-light) dark:bg-(--black) rounded-md shadow-sm" :searchInput="false">
<template #default>
<div class="flex flex-col items-start leading-tight">
<span class="text-xs text-gray-400">
Selected language
</span>
<span class="font-medium dark:text-white text-black">
{{ selectedLangName }}
</span>
</div>
</template>
<div <template #item-leading="{ item }">
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-center gap-2 cursor-pointer">
<div class="flex items-end gap-3"> <span class="text-lg">{{ item.flag }}</span>
<USelect v-model="selectedLanguage" :items="availableLangs" variant="outline" class="w-40!" <span class="font-medium dark:text-white text-black">
valueKey="iso_code"> {{ item.name }}
<template #default="{ modelValue }"> </span>
<div class="flex items-center gap-2"> </div>
<span class="text-md">{{availableLangs.find(x => x.iso_code == modelValue)?.flag}}</span> </template>
<span class="font-medium dark:text-white text-black">{{availableLangs.find(x => x.iso_code == </USelectMenu>
modelValue)?.name}}</span> </div>
</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>
<div v-if="translating" class="fixed inset-0 z-50 flex items-center justify-center bg-black/50"> <div v-if="toLangId !== settingStore.shopDefaultLanguage && !isTranslations"
<div class="flex flex-col items-center gap-4 p-8 bg-(--main-light) dark:bg-(--main-dark) rounded-lg shadow-xl"> class="flex flex-wrap-reverse justify-between gap-2">
<UIcon name="svg-spinners:ring-resize" class="text-4xl text-primary" /> <UButton color="info" variant="outline" v-if="!isEditing" @click="isTranslations = true">
<p class="text-lg font-medium dark:text-white text-black">Translating...</p> <p>Change Text</p>
<UIcon name="material-symbols-light:stylus-note-sharp" class="text-7.5" />
</UButton>
<UButton @click="translateToSelectedLanguage" color="info" :loading="translating">
Translate from Polish to {{langs.find(l => l.id === toLangId)?.name}}
</UButton>
</div>
<div v-if="isTranslations" class="flex gap-3 w-full justify-end">
<UButton @click="() => {
handleCancel()
isTranslations = false
}" color="info" variant="outline">
Cancel
</UButton>
<UButton color="info" @click="async () => {
await productStore.saveProductDescription(productID, toLangId)
isTranslations = false
}">
Save
</UButton>
</div>
</div> </div>
</div> </div>
@@ -41,209 +66,181 @@
<div v-else-if="productStore.error" class="flex items-center justify-center py-20"> <div v-else-if="productStore.error" class="flex items-center justify-center py-20">
<p class="text-red-500">{{ productStore.error }}</p> <p class="text-red-500">{{ productStore.error }}</p>
</div> </div>
<div v-else-if="productStore.productDescription" class="flex items-start gap-30">
<div class="w-80 h-80 bg-(--second-light) dark:bg-gray-700 rounded-lg flex items-center justify-center"> <div v-else-if="productStore.productDescription" class="space-y-7.5">
<span class="text-gray-500 dark:text-gray-400">Product Image</span> <div class="grid grid-cols-1 md:grid-cols-6 h-full w-full gap-6">
</div> <img
<div class="flex flex-col gap-2"> class="md:col-span-2 rounded-md bg-white rounded-md border border-(--border-light) dark:border-(--border-dark)"
<p class="text-[25px] font-bold text-black dark:text-white"> :src="productStore.productDescription.image_link" :alt="productStore.productDescription.name" @error="(e: Event) => {
{{ productStore.productDescription.name || 'Product Name' }} const target = e.target as HTMLImageElement
</p> target.src = errorImg
<p v-html="productStore.productDescription.description_short" class="text-black dark:text-white"></p> }">
<div class="space-y-[10px]"> <div class="md:col-span-4 flex flex-col justify-between min-h-full w-full space-y-7.5">
<div class="flex items-center gap-1"> <div class="space-y-7.5">
<UIcon name="lets-icons:done-ring-round-fill" class="text-[20px] text-green-600" /> <div>
<p class="text-[16px] font-bold text-(--accent-blue-light) dark:text-(--accent-blue-dark)"> <p v-if="!isTranslations" class="text-[25px] font-bold text-black dark:text-white">
{{ productStore.productDescription.available_now }} {{ productStore.productDescription.name || 'Product name not provided' }}
</p> </p>
<div class="" v-if="isTranslations">
<p>Title:</p>
<UTextarea :rows="1" v-model="productStore.productDescription.name" autoresize :ui="{
root: 'w-full bg-white!',
base: 'text-2xl!',
}" />
</div>
</div>
<div class="" v-if="isTranslations">
<p>Link rewrite:</p>
<UTextarea :rows="1" v-model="productStore.productDescription.link_rewrite" autoresize :ui="{
root: 'w-full',
base: 'bg-inherit!',
}" />
</div>
<div class="" v-if="isTranslations">
<p>Link rewrite:</p>
<UTextarea :rows="1" v-model="productStore.productDescription.link_rewrite" autoresize :ui="{
root: 'w-full bg-white!'
}" />
</div>
<div>
<p v-if="!isTranslations"
v-html="productStore.productDescription.description_short || 'No short description available'"
class="text-black dark:text-white" />
<div class="" v-if="isTranslations">
<p>Short description:</p>
<ProductEditor v-model="productStore.productDescription.description_short" />
</div>
</div>
</div> </div>
<div class="flex items-center gap-1">
<UIcon name="marketeq:car-shipping" class="text-[25px] text-green-600" /> <div class="space-y-2.5">
<p class="text-[18px] font-bold text-black dark:text-white"> <div v-if="productStore.productDescription.available_now" class="flex items-center gap-1">
{{ productStore.productDescription.delivery_in_stock || 'Delivery information' }} <UIcon name="lets-icons:done-ring-round-fill"
</p> class="text-[20px] light:text-(--accent-green-light) dark:text-(--accent-green-dark)" />
<p>
{{ productStore.productDescription.available_now }}
</p>
</div>
<div v-if="productStore.productDescription.delivery_in_stock" class="flex items-center gap-1">
<UIcon name="marketeq:car-shipping"
class="text-[25px] light:text-(--accent-green-light) dark:text-(--accent-green-dark)" />
<p>
{{ productStore.productDescription.delivery_in_stock }}
</p>
</div>
</div> </div>
</div> </div>
</div> </div>
<UTabs :items="items" color="info" default-value="0" :ui="{
list: 'w-auto!',
root: 'items-start!'
}">
<template #description>
<ProductEditor v-if="isTranslations" v-model="productStore.productDescription.description" />
<p v-else v-html="productStore.productDescription.description || 'No description available'"
class="text-black dark:text-white" />
</template>
<template #usage>
<ProductEditor v-if="isTranslations" v-model="productStore.productDescription.usage" />
<p v-else v-html="productStore.productDescription.usage || 'No usage information available'"
class="text-black dark:text-white" />
</template>
</UTabs>
</div> </div>
<div class=""></div>
<div v-if="productStore.productDescription" 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> </div>
</component> </component>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, computed, onMounted, watch } from 'vue' import Default from '@/layouts/default.vue';
import { useRoute } from 'vue-router' import { langs } from '@/router/langs';
import { useProductStore } from '@/stores/product' import { useProductStore } from '@/stores/admin/product';
import { useEditable } from '@/composable/useConteditable' import { useSettingsStore } from '@/stores/admin/settings';
import { langs } from '@/router/langs' import { computed, onMounted, ref, watch } from 'vue';
import type { Language } from '@/types' import { useRoute, useRouter } from 'vue-router';
import Default from '@/layouts/default.vue' import ProductEditor from '../inner/ProductEditor.vue';
import errorImg from '@/assets/error.svg'
const router = useRouter()
const route = useRoute() const route = useRoute()
const activeTab = ref('description') const settingStore = useSettingsStore()
const productStore = useProductStore() const productStore = useProductStore()
const translating = ref(false)
const isEditing = ref(false) const toLangId = ref<number>(settingStore.shopDefaultLanguage)
const availableLangs = computed(() => langs)
const selectedLanguage = ref('en')
const currentLangId = ref(2)
const productID = ref<number>(0) const productID = ref<number>(0)
const translating = ref(false)
const isEditing = ref(false)
const isTranslations = ref(false)
// Watch for language changes and refetch product description const selectedLangName = computed(() => {
watch(selectedLanguage, async (newLang: string) => { return langs.find(l => l.id === toLangId.value)?.name || 'Select language'
if (productID.value) {
await fetchForLanguage(newLang)
}
}) })
const fetchForLanguage = async (langCode: string) => {
const lang = langs.find((l: Language) => l.iso_code === langCode) function backFromProduct() {
if (lang && productID.value) { let path = localStorage.getItem('back_from_product')
await productStore.getProductDescription(lang.id, productID.value)
currentLangId.value = lang.id if (path) {
let res = JSON.parse(path)
router.push({
name: res.name,
params: res.params,
query: res.query
})
localStorage.removeItem('back_from_product')
} else {
router.push({
name: 'admin-products',
})
} }
} }
const translateToSelectedLanguage = async () => { const translateToSelectedLanguage = async () => {
const targetLang = langs.find((l: Language) => l.iso_code === selectedLanguage.value) if (toLangId.value && productID.value) {
if (targetLang && currentLangId.value && productID.value) {
translating.value = true translating.value = true
try { try {
await productStore.translateProductDescription(productID.value, currentLangId.value, targetLang.id) await productStore.translateProductDescription(productID.value, toLangId.value)
currentLangId.value = targetLang.id
} finally { } finally {
translating.value = false translating.value = false
} }
} }
if (productStore.productDescription) {
isTranslations.value = true
}
} }
const fetchForLanguage = async (langId: number | null) => {
if (productID.value) {
await productStore.getProductDescription(langId, productID.value)
}
}
const handleCancel = async () => {
await productStore.getProductDescription(toLangId.value, productID.value)
isTranslations.value = false
}
watch(toLangId, (lang) => {
fetchForLanguage(lang)
})
onMounted(async () => { onMounted(async () => {
const id = route.params.product_id const id = route.params.product_id
if (id) { if (id) {
productID.value = Number(id) productID.value = Number(id)
await fetchForLanguage(selectedLanguage.value) await fetchForLanguage(toLangId.value)
} }
}) })
const descriptionRef = ref<HTMLElement | null>(null) const items = ref([
const usageRef = ref<HTMLElement | null>(null) { label: 'Description', slot: 'description', name: 'description' },
{ label: 'Usage', slot: 'usage', name: 'usage' }
const descriptionEdit = useEditable(descriptionRef) ])
const usageEdit = useEditable(usageRef)
const originalDescription = ref('')
const originalUsage = ref('')
const saveDescription = async () => {
descriptionEdit.disableEdit()
await productStore.saveProductDescription(productID.value)
}
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(productID.value)
}
const cancelEdit = () => {
if (usageRef.value) {
usageRef.value.innerHTML = originalUsage.value
}
usageEdit.disableEdit()
isEditing.value = false
}
</script> </script>
<style>
.images {
display: flex;
align-items: center;
gap: 70px;
margin: 20px 0 20px 0;
}
</style>

View File

@@ -0,0 +1,211 @@
<template>
<component :is="Default || 'div'">
<div class="flex flex-col md:flex-row gap-10">
<div class="w-full flex flex-col items-center gap-4">
<UTable :data="usersList" :columns="columns" class="flex-1 w-full"
:ui="{ root: 'max-w-100wv overflow-auto!' }" />
<UPagination v-model:page="page" :total="total" :items-per-page="perPage" />
</div>
</div>
</component>
</template>
<script setup lang="ts">
import Default from '@/layouts/default.vue'
import { ref, computed, watch, resolveComponent, h } from 'vue'
import { useFetchJson } from '@/composable/useFetchJson'
import { useRoute, useRouter } from 'vue-router'
import CategoryMenu from '../inner/CategoryMenu.vue'
import type { TableColumn } from '@nuxt/ui'
import type { Customer } from '@/types/user'
const router = useRouter()
const route = useRoute()
const perPage = ref(15)
const page = computed({
get: () => Number(route.query.p) || 1,
set: (val: number) => {
router.push({ query: { ...route.query, p: val } })
}
})
const sortField = computed({
get: () => [
route.query.sort as string | undefined,
route.query.direction as 'asc' | 'desc' | undefined
],
set: ([sort]: [string, 'asc' | 'desc']) => {
const currentSort = route.query.sort as string | undefined
const currentDirection = route.query.direction as 'asc' | 'desc' | undefined
const query = { ...route.query }
if (currentSort === sort) {
if (currentDirection === 'asc') query.direction = 'desc'
else if (currentDirection === 'desc') {
delete query.sort
delete query.direction
} else {
query.direction = 'asc'
query.sort = sort
}
} else {
query.sort = sort
query.direction = 'asc'
}
router.push({ query })
}
})
const filters = computed<Record<string, string>>({
get: () => {
const q = { ...route.query }
delete q.page
delete q.sort
delete q.direction
return q as Record<string, string>
},
set: (val) => {
const baseQuery = { ...route.query }
Object.keys(baseQuery).forEach(k => {
if (!['page', 'sort', 'direction'].includes(k)) delete baseQuery[k]
})
router.push({ query: { ...baseQuery, ...val, page: 1 } })
}
})
function debounce(fn: Function, delay = 400) {
let t: any
return (...args: any[]) => {
clearTimeout(t)
t = setTimeout(() => fn(...args), delay)
}
}
const updateFilter = debounce((columnId: string, val: string) => {
const newFilters = { ...filters.value }
if (val) newFilters[columnId] = val
else delete newFilters[columnId]
filters.value = newFilters
}, 400)
const usersList = ref<Customer[]>([])
const total = ref(0)
const loading = ref(true)
const error = ref<string | null>(null)
async function fetchUsersList() {
loading.value = true
error.value = null
const params = new URLSearchParams(route.query as any).toString()
const url = `/api/v1/restricted/customer/list?elems=${perPage.value}&${params}`
try {
const res = await useFetchJson<{ items: { items: Customer[] }; count: number }>(url)
usersList.value = res.items?.items || []
total.value = res.count || 0
} catch (e: unknown) {
error.value = e instanceof Error ? e.message : 'Failed to load users'
} finally {
loading.value = false
}
}
function getIcon(name: string) {
if (sortField.value[0] === name) {
if (sortField.value[1] === 'asc') return 'i-lucide-arrow-up-narrow-wide'
if (sortField.value[1] === 'desc') return 'i-lucide-arrow-down-wide-narrow'
}
return 'i-lucide-arrow-up-down'
}
const UInput = resolveComponent('UInput')
const UIcon = resolveComponent('UIcon')
const UButton = resolveComponent('UButton')
const columns: TableColumn<Customer>[] = [
{
accessorKey: 'user_id',
header: ({ column }) => h('div', { class: 'flex flex-col gap-1' }, [
h('div', {
class: 'flex items-center gap-2 cursor-pointer',
onClick: () => (sortField.value = ['user_id', 'asc'])
}, [h('span', 'Client ID'), h(UIcon, { name: getIcon('user_id') })]),
h(UInput, {
placeholder: 'Search...',
modelValue: filters.value[column.id] ?? '',
'onUpdate:modelValue': (val: string) => updateFilter(column.id, val),
size: 'xs'
})
]),
cell: ({ row }) => `#${row.getValue('user_id')}`
},
{
accessorKey: 'name',
header: ({ column }) => h('div', { class: 'flex flex-col gap-1' }, [
h('div', {
class: 'flex items-center gap-2 cursor-pointer',
onClick: () => (sortField.value = ['last_name, first_name', 'asc'])
}, [h('span', 'Name/Surname'), h(UIcon, { name: getIcon('name') })]),
h(UInput, {
placeholder: 'Search...',
modelValue: filters.value[column.id] ?? '',
'onUpdate:modelValue': (val: string) => updateFilter(column.id, val),
size: 'xs'
})
]),
cell: ({ row }) => `${row.original.first_name} ${row.original.last_name}`
},
{
accessorKey: 'email',
header: ({ column }) => h('div', { class: 'flex flex-col gap-1' }, [
h('div', {
class: 'flex items-center gap-2 cursor-pointer',
onClick: () => (sortField.value = ['email', 'asc'])
}, [h('span', 'Email'), h(UIcon, { name: getIcon('email') })]),
h(UInput, {
placeholder: 'Search...',
modelValue: filters.value[column.id] ?? '',
'onUpdate:modelValue': (val: string) => updateFilter(column.id, val),
size: 'xs'
})
]),
cell: ({ row }) => row.getValue('email')
},
{
accessorKey: 'count',
header: '',
cell: ({ row }) => {
return h(UButton, {
// onClick: () => {
// goToProduct(row.original.product_id, row.original.link_rewrite)
// },
class: 'cursor-pointer',
color: 'info',
variant: 'soft'
}, () => 'Manage')
},
},
{
accessorKey: 'profile',
header: '',
cell: ({ row }) => {
const userId = row.original.user_id
return h(UButton, {
color: 'info',
size: 'sm',
variant: 'soft',
onClick: () => {
router.push({
name: 'customer-management-profile',
params: { user_id: userId }
})
}
}, () => 'Go to profile')
}
}
]
watch(() => route.query, fetchUsersList, { immediate: true })
</script>

View File

@@ -0,0 +1,245 @@
<template>
<component :is="Default">
<div class="pt-70! flex flex-col items-center justify-center bg-gray-50 dark:bg-(--main-dark)">
<h1 class="text-6xl font-bold text-black dark:text-white mb-14">Search Users</h1>
<div class="w-full max-w-4xl">
<UInput icon="i-lucide-search" type="text" placeholder="Type user name or ID..." v-model="searchQuery"
class="w-full!" :ui="{ base: 'py-4! rounded-full!' }" />
</div>
<p v-if="loading">Loading...</p>
<p v-else-if="error" class="text-red-600">{{ error }}</p>
<div v-else-if="clients.length" class="w-full max-w-4xl mt-7">
<UTable :columns="columns" :data="clients" :ui="{ root: 'w-full!' }" />
</div>
<p v-else-if="searchQuery.length" class="pt-4 text-gray-700">
No users found with that name or ID
</p>
</div>
</component>
</template>
<script setup lang="ts">
import { ref, computed, watch, resolveComponent, h } from 'vue'
import Default from '@/layouts/default.vue';
import type { TableColumn } from '@nuxt/ui';
import { useRoute, useRouter } from 'vue-router';
import { useFetchJson } from '@/composable/useFetchJson';
interface Customer {
user_id: number;
email: string;
first_name: string;
last_name: string;
}
const searchQuery = ref('');
const clients = ref<Customer[]>([]);
const loading = ref(false);
const error = ref<string | null>(null);
const limit = ref(10);
const page = ref(1);
function debounce(fn: Function, delay = 400) {
let t: any;
return (...args: any[]) => {
clearTimeout(t);
t = setTimeout(() => fn(...args), delay);
};
}
const searchClients = async () => {
if (!searchQuery.value) {
clients.value = [];
return;
}
loading.value = true;
error.value = null;
try {
const result = await useFetchJson(
`/api/v1/restricted/customer/list?search=${encodeURIComponent(searchQuery.value)}&elems=${limit.value}&page=${page.value}`
);
clients.value = result.items?.items || [];
} catch (err: unknown) {
error.value = err instanceof Error ? err.message : 'Failed to fetch clients';
clients.value = [];
} finally {
loading.value = false;
}
};
watch(searchQuery, debounce(searchClients, 300));
const router = useRouter()
const route = useRoute()
const UInput = resolveComponent('UInput')
const UIcon = resolveComponent('UIcon')
const UButton = resolveComponent('UButton')
const sortField = computed({
get: () => [
route.query.sort as string | undefined,
route.query.direction as 'asc' | 'desc' | undefined
],
set: ([sort]: [string, 'asc' | 'desc']) => {
const currentSort = route.query.sort as string | undefined
const currentDirection = route.query.direction as 'asc' | 'desc' | undefined
const query = { ...route.query }
if (currentSort === sort) {
if (currentDirection === 'asc') query.direction = 'desc'
else if (currentDirection === 'desc') {
delete query.sort
delete query.direction
} else {
query.direction = 'asc'
query.sort = sort
}
} else {
query.sort = sort
query.direction = 'asc'
}
router.push({ query })
}
})
function getIcon(name: string) {
if (sortField.value[0] === name) {
if (sortField.value[1] === 'asc') return 'i-lucide-arrow-up-narrow-wide'
if (sortField.value[1] === 'desc') return 'i-lucide-arrow-down-wide-narrow'
}
return 'i-lucide-arrow-up-down'
}
const filters = computed<Record<string, string>>({
get: () => {
const q = { ...route.query }
delete q.page
delete q.sort
delete q.direction
return q as Record<string, string>
},
set: (val) => {
const baseQuery = { ...route.query }
Object.keys(baseQuery).forEach(k => {
if (!['page', 'sort', 'direction'].includes(k)) delete baseQuery[k]
})
router.push({ query: { ...baseQuery, ...val, page: 1 } })
}
})
const updateFilter = debounce((columnId: string, val: string) => {
const newFilters = { ...filters.value }
if (val) newFilters[columnId] = val
else delete newFilters[columnId]
filters.value = newFilters
}, 400)
const columns: TableColumn<Customer>[] = [
{
accessorKey: 'user_id',
header: ({ column }) => h('div', { class: 'flex flex-col gap-1' }, [
h('div', {
class: 'flex items-center gap-2 cursor-pointer',
onClick: () => (sortField.value = ['user_id', 'asc'])
}, [h('span', 'Client ID'), h(UIcon, { name: getIcon('user_id') })]),
h(UInput, {
placeholder: 'Search...',
modelValue: filters.value[column.id] ?? '',
'onUpdate:modelValue': (val: string) => updateFilter(column.id, val),
size: 'xs'
})
]),
cell: ({ row }) => `#${row.getValue('user_id')}`
},
{
accessorKey: 'name',
header: ({ column }) => h('div', { class: 'flex flex-col gap-1' }, [
h('div', {
class: 'flex items-center gap-2 cursor-pointer',
onClick: () => (sortField.value = ['last_name, first_name', 'asc'])
}, [h('span', 'Name/Surname'), h(UIcon, { name: getIcon('name') })]),
h(UInput, {
placeholder: 'Search...',
modelValue: filters.value[column.id] ?? '',
'onUpdate:modelValue': (val: string) => updateFilter(column.id, val),
size: 'xs'
})
]),
cell: ({ row }) => `${row.original.first_name} ${row.original.last_name}`
},
{
accessorKey: 'email',
header: ({ column }) => h('div', { class: 'flex flex-col gap-1' }, [
h('div', {
class: 'flex items-center gap-2 cursor-pointer',
onClick: () => (sortField.value = ['email', 'asc'])
}, [h('span', 'Email'), h(UIcon, { name: getIcon('email') })]),
h(UInput, {
placeholder: 'Search...',
modelValue: filters.value[column.id] ?? '',
'onUpdate:modelValue': (val: string) => updateFilter(column.id, val),
size: 'xs'
})
]),
cell: ({ row }) => row.getValue('email')
},
{
accessorKey: 'count',
header: '',
cell: ({ row }) => {
return h(UButton, {
// onClick: () => {
// goToProduct(row.original.product_id, row.original.link_rewrite)
// },
class: 'cursor-pointer',
color: 'info',
variant: 'soft'
}, () => 'Manage')
},
},
{
accessorKey: 'profile',
header: '',
cell: ({ row }) => {
const userId = row.original.user_id
return h(UButton, {
color: 'info',
size: 'sm',
variant: 'soft',
onClick: () => {
router.push({
name: 'customer-management-profile',
params: { user_id: userId }
})
}
}, () => 'Go to profile')
}
}
]
</script>
<style scoped>
input::placeholder {
color: #9ca3af;
}
</style>

View File

@@ -0,0 +1,11 @@
<template>
<component :is="Management || 'div'">
<div>customer-management</div>
</component>
</template>
<script setup lang="ts">
import Management from '@/layouts/management.vue';
</script>

View File

@@ -1,6 +1,6 @@
<template> <template>
<component :is="Default || 'div'"> <component :is="Default || 'div'">
<div class="container mx-auto"> <div class="">
<h2 <h2
class="font-semibold text-black dark:text-white pb-6 text-2xl"> class="font-semibold text-black dark:text-white pb-6 text-2xl">
{{ t('Cart Items') }} {{ t('Cart Items') }}
@@ -52,7 +52,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { useCartStore } from '@/stores/cart' import { useCartStore } from '@/stores/customer/cart'
import { ref } from 'vue' import { ref } from 'vue'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'

View File

@@ -1,6 +1,6 @@
<template> <template>
<component :is="Default || 'div'"> <component :is="Default || 'div'">
<div class="container mx-auto"> <div class="">
<div class="flex flex-col gap-5 mb-6"> <div class="flex flex-col gap-5 mb-6">
<h1 class="text-2xl font-bold text-black dark:text-white">{{ t('Addresses') }}</h1> <h1 class="text-2xl font-bold text-black dark:text-white">{{ t('Addresses') }}</h1>
<div class="flex md:flex-row flex-col justify-between items-start md:items-center gap-5 md:gap-0"> <div class="flex md:flex-row flex-col justify-between items-start md:items-center gap-5 md:gap-0">
@@ -8,7 +8,7 @@
<UInput v-model="searchQuery" type="text" :placeholder="t('Search address')" <UInput v-model="searchQuery" type="text" :placeholder="t('Search address')"
class="bg-white dark:bg-gray-800 text-black dark:text-white absolute" /> class="bg-white dark:bg-gray-800 text-black dark:text-white absolute" />
<UIcon name="ic:baseline-search" <UIcon name="ic:baseline-search"
class="text-[20px] text-(--accent-blue-light) dark:text-(--accent-blue-dark) relative left-40" /> class="text-[20px] text-(--text-sky-light) dark:text-(--text-sky-dark) relative left-40" />
</div> </div>
<UButton color="primary" @click="openCreateModal" <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)"> class="bg-(--accent-blue-light) dark:bg-(--accent-blue-dark) text-white hover:bg-(--accent-blue-dark) dark:hover:bg-(--accent-blue-light)">
@@ -32,7 +32,7 @@
<UIcon name="material-symbols:delete" class="text-[18px]" /> <UIcon name="material-symbols:delete" class="text-[18px]" />
</button> </button>
<UButton size="sm" color="neutral" variant="outline" @click="openEditModal(address)" <UButton size="sm" color="neutral" variant="outline" @click="openEditModal(address)"
class="text-(--accent-blue-light) dark:text-(--accent-blue-dark) text-[13px]"> class="text-(--text-sky-light) dark:text-(--text-sky-dark) text-[13px]">
{{ t('edit') }} {{ t('edit') }}
<UIcon name="ic:sharp-edit" class="text-[15px]" /> <UIcon name="ic:sharp-edit" class="text-[15px]" />
</UButton> </UButton>
@@ -104,7 +104,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, computed, watch } from 'vue' import { ref, computed, watch } from 'vue'
import { useAddressStore } from '@/stores/address' import { useAddressStore } from '@/stores/customer/address'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import Default from '@/layouts/default.vue' import Default from '@/layouts/default.vue'
const addressStore = useAddressStore() const addressStore = useAddressStore()

View File

@@ -1,6 +1,6 @@
<template> <template>
<component :is="Default || 'div'"> <component :is="Default || 'div'">
<div class="container mx-auto flex flex-col gap-5 md:gap-10"> <div class="flex flex-col gap-5 md:gap-10">
<h1 class="text-2xl font-bold text-black dark:text-white">{{ t('Shopping Cart') }}</h1> <h1 class="text-2xl font-bold text-black dark:text-white">{{ t('Shopping Cart') }}</h1>
<div class="flex flex-col lg:flex-row gap-5 md:gap-10"> <div class="flex flex-col lg:flex-row gap-5 md:gap-10">
<div class="flex-1"> <div class="flex-1">
@@ -43,7 +43,7 @@
name: 'customer-product', params: { name: 'customer-product', params: {
product_id: '51' product_id: '51'
} }
}" class="inline-block mt-4 text-(--accent-blue-light) dark:text-(--accent-blue-dark) hover:underline"> }" class="inline-block mt-4 text-(--text-sky-light) dark:text-(--text-sky-dark) hover:underline">
{{ t('Continue Shopping') }} {{ t('Continue Shopping') }}
</RouterLink> </RouterLink>
</div> </div>
@@ -72,7 +72,7 @@
</div> </div>
<div class="flex justify-between mb-6"> <div class="flex justify-between mb-6">
<span class="text-black dark:text-white font-semibold text-lg">{{ t('Total') }}</span> <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">${{ <span class="text-(--text-sky-light) dark:text-(--text-sky-dark) font-bold text-lg">${{
cartStore.orderTotal.toFixed(2) }}</span> cartStore.orderTotal.toFixed(2) }}</span>
</div> </div>
<div class="flex flex-col gap-3"> <div class="flex flex-col gap-3">
@@ -103,7 +103,7 @@
? 'border-(--accent-blue-light) dark:border-(--accent-blue-dark) bg-blue-50 dark:bg-blue-900/20' ? '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'"> : 'border-(--border-light) dark:border-(--border-dark) hover:border-gray-400'">
<input type="radio" :value="address.id" v-model="selectedAddress" <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)" /> class="mt-1 w-4 h-4 text-(--text-sky-light) dark:text-(--text-sky-dark)" />
<div class="flex-1"> <div class="flex-1">
<p class="text-black dark:text-white font-medium">{{ address.street }}</p> <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.zipCode }}, {{ address.city }}</p>
@@ -115,7 +115,7 @@
<UIcon name="mdi:map-marker-outline" class="text-4xl text-gray-400 mb-2" /> <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> <p class="text-gray-500 dark:text-gray-400">{{ t('No addresses found') }}</p>
<RouterLink :to="{ name: 'addresses' }" <RouterLink :to="{ name: 'addresses' }"
class="inline-block mt-2 text-(--accent-blue-light) dark:text-(--accent-blue-dark) hover:underline"> class="inline-block mt-2 text-(--text-sky-light) dark:text-(--text-sky-dark) hover:underline">
{{ t('Add Address') }} {{ t('Add Address') }}
</RouterLink> </RouterLink>
</div> </div>
@@ -131,11 +131,11 @@
? 'border-(--accent-blue-light) dark:border-(--accent-blue-dark) bg-blue-50 dark:bg-blue-900/20' ? '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'"> : 'border-(--border-light) dark:border-(--border-dark) hover:border-gray-400'">
<input type="radio" :value="method.id" v-model="selectedDeliveryMethod" <input type="radio" :value="method.id" v-model="selectedDeliveryMethod"
class="w-4 h-4 text-(--accent-blue-light) dark:text-(--accent-blue-dark)" /> class="w-4 h-4 text-(--text-sky-light) dark:text-(--text-sky-dark)" />
<div class="flex-1"> <div class="flex-1">
<div class="flex justify-between items-center"> <div class="flex justify-between items-center">
<span class="text-black dark:text-white font-medium">{{ method.name }}</span> <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"> <span class="text-(--text-sky-light) dark:text-(--text-sky-dark) font-medium">
{{ method.price > 0 ? `$${method.price.toFixed(2)}` : t('Free') }} {{ method.price > 0 ? `$${method.price.toFixed(2)}` : t('Free') }}
</span> </span>
</div> </div>
@@ -152,8 +152,8 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, computed, watch } from 'vue' import { ref, computed, watch } from 'vue'
import { useCartStore } from '@/stores/cart' import { useCartStore } from '@/stores/customer/cart'
import { useAddressStore } from '@/stores/address' import { useAddressStore } from '@/stores/customer/address'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import Default from '@/layouts/default.vue' import Default from '@/layouts/default.vue'

View File

@@ -1,75 +1,84 @@
<template> <template>
<component :is="Default || 'div'"> <component :is="Default || 'div'">
<div class="container mx-auto"> <div class="">
<div class="flex md:flex-row flex-col justify-between gap-8 my-6"> <div class="flex md:flex-row flex-col justify-between gap-8 my-6">
<div class="flex-1"> <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] "> <div
<img :src="selectedColor?.image || productData.image" :alt="productData.name" class="bg-gray-100 dark:bg-gray-800 rounded-lg p-8 flex items-center justify-center min-h-[300px] ">
class="max-w-full h-auto object-contain" /> <img :src="selectedColor?.image || productData.image" :alt="productData.name"
</div> class="max-w-full h-auto object-contain" />
</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>
<div class="flex gap-2"> </div>
<span class="text-gray-500 dark:text-gray-400">Seat Height:</span> <div class="flex-1 flex flex-col gap-4">
<p class="font-medium dark:text-white">{{ productData.seatHeight }}</p> <div class="flex justify-between items-center">
<h1 class="text-2xl font-bold text-gray-900 dark:text-white">
{{ productData.name }}
</h1>
<UIcon name="material-symbols:favorite"
class="cursor-pointer text-2xl transition hover:scale-110"
:class="productData.is_favorite ? 'text-red-500' : 'text-gray-400'"
@click="toggleFavorite" />
</div>
<p class="text-gray-600 dark:text-gray-300">
{{ productData.description }}
</p>
<div class="text-3xl font-bold text-(--text-sky-light) dark:text-(--text-sky-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>
</div> </div>
</div> <div class="flex md:flex-row flex-col justify-between md:items-end items-start gap-5 md:gap-0 md:mb-8 mb-4">
<div class="flex md:flex-row flex-col justify-between md:items-end items-start gap-5 md:gap-0 md:mb-8 mb-4"> <div class="flex flex-col gap-3">
<div class="flex flex-col gap-3"> <span class="text-sm text-(--text-sky-light) dark:text-(--text-sky-dark) ">Colors:</span>
<span class="text-sm text-(--accent-blue-light) dark:text-(--accent-blue-dark) ">Colors:</span> <div class="flex gap-2">
<div class="flex gap-2"> <button v-for="color in productData.colors" :key="color.id" @click="selectedColor = color"
<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
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-(--accent-blue-light) ring-2 ring-blue-600 ring-offset-2' : 'border-gray-300 dark:border-gray-600 hover:border-gray-400'"
: 'border-gray-300 dark:border-gray-600 hover:border-gray-400'" :style="{ backgroundColor: color.hex }" :title="color.name" />
: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>
</div> </div>
<div class="flex gap-5 items-end"> <ProductCustomization />
<UInputNumber v-model="value" /> <hr class="border-t border-(--border-light) dark:border-(--border-dark) mb-8" />
<UButton color="primary" class="px-14! bg-(--accent-blue-light) dark:bg-(--accent-blue-dark) text-white"> <div class="mb-6 w-[100%] xl:w-[60%]">
Add to Cart <div class="grid grid-cols-2 lg:grid-cols-4 gap-10 mb-8">
</UButton> <UButton v-for="tab in tabs" :key="tab.id" @click="activeTab = tab.id" :class="[
'px-15 py-2 cursor-pointer sm:text-nowrap flex items-center! justify-center!',
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> </div>
<hr class="border-t border-(--border-light) dark:border-(--border-dark) mb-8" />
<ProductVariants />
</div> </div>
<ProductCustomization />
<hr class="border-t border-(--border-light) dark:border-(--border-dark) mb-8" />
<div class="mb-6 w-[100%] xl:w-[60%]">
<div class="grid grid-cols-2 lg:grid-cols-4 gap-10 mb-8">
<UButton v-for="tab in tabs" :key="tab.id" @click="activeTab = tab.id" :class="[
'px-15 py-2 cursor-pointer sm:text-nowrap flex items-center! justify-center!',
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>
</component> </component>
</template> </template>
@@ -78,6 +87,8 @@ import { ref, computed } from 'vue'
import ProductCustomization from './components/ProductCustomization.vue' import ProductCustomization from './components/ProductCustomization.vue'
import ProductVariants from './components/ProductVariants.vue' import ProductVariants from './components/ProductVariants.vue'
import Default from '@/layouts/default.vue' import Default from '@/layouts/default.vue'
import { useFetchJson } from '@/composable/useFetchJson'
import { useRoute } from 'vue-router'
interface Color { interface Color {
id: string id: string
name: string name: string
@@ -97,6 +108,7 @@ interface ProductData {
howToUseText: string howToUseText: string
productDetailsText: string productDetailsText: string
documentsText: string documentsText: string
is_favorite: boolean
} }
const activeTab = ref('description') const activeTab = ref('description')
@@ -157,6 +169,24 @@ const activeTabContent = computed(() => {
if (productData.colors.length > 0) { if (productData.colors.length > 0) {
selectedColor.value = productData.colors[0] as Color selectedColor.value = productData.colors[0] as Color
} }
const route = useRoute()
async function toggleFavorite() {
const url = `/api/v1/restricted/product/favorite/${route.params.product_id}`
try {
if (!productData.is_favorite) {
await useFetchJson(url, { method: 'POST' })
} else {
await useFetchJson(url, { method: 'DELETE' })
}
productData.is_favorite = !productData.is_favorite
} catch (e: unknown) {
console.error(e)
}
}
</script> </script>
<style scoped> <style scoped>

View File

@@ -1,7 +1,7 @@
<template> <template>
<suspense> <suspense>
<component :is="Default || 'div'"> <component :is="Default || 'div'">
<div class="container mx-auto"> <div class="">
<!-- <UNavigationMenu orientation="vertical" :items="listing" class="data-[orientation=vertical]:w-48"> <!-- <UNavigationMenu orientation="vertical" :items="listing" class="data-[orientation=vertical]:w-48">
<template #item="{ item, active }"> <template #item="{ item, active }">
<div class="flex items-center gap-2 px-3 py-2"> <div class="flex items-center gap-2 px-3 py-2">
@@ -11,27 +11,30 @@
</template> </template>
</UNavigationMenu> --> </UNavigationMenu> -->
<h1 class="text-2xl font-bold mb-6 text-gray-900 dark:text-white">Products</h1> <h1 class="text-2xl font-bold mb-6 text-gray-900 dark:text-white">Products</h1>
<div v-if="loading" class="text-center py-8"> <div v-if="customerProductStore.loading" class="text-center py-8">
<span class="text-gray-600 dark:text-gray-400">Loading products...</span> <span class="text-gray-600 dark:text-gray-400">Loading products...</span>
</div> </div>
<div v-else-if="error" class="mb-4 p-3 bg-red-100 text-red-700 rounded"> <div v-else-if="customerProductStore.error" class="mb-4 p-3 bg-red-100 text-red-700 rounded">
{{ error }} {{ customerProductStore.error }}
</div> </div>
<div v-else class="overflow-x-auto"> <div v-else class="overflow-x-auto">
<div class="flex gap-2"> <div class="flex gap-2">
<CategoryMenu /> <CategoryMenu />
<UTable :data="productsList" :columns="columns" class="flex-1"> <UTable :data="customerProductStore.productsList" :columns="columns" class="flex-1">
<template #expanded="{ row }"> <template #expanded="{ row }">
<UTable :data="productsList.slice(0, 3)" :columns="columnsChild" :ui="{ <UTable :data="customerProductStore.productsList.slice(0, 3)" :columns="columnsChild"
thead: 'hidden' :ui="{
}" /> thead: 'hidden'
}" />
</template> </template>
</UTable> </UTable>
</div> </div>
<div class="flex justify-center items-center py-8"> <div class="flex justify-center items-center py-8">
<UPagination v-model:page="page" :total="total" :page-size="perPage" /> <UPagination v-model:page="page" :total="customerProductStore.total"
:page-size="customerProductStore.perPage" />
</div> </div>
<div v-if="productsList.length === 0" class="text-center py-8 text-gray-500 dark:text-gray-400"> <div v-if="customerProductStore.productsList.length === 0"
class="text-center py-8 text-gray-500 dark:text-gray-400">
No products found No products found
</div> </div>
</div> </div>
@@ -42,20 +45,15 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, watch, h, resolveComponent, computed } from 'vue' import { ref, watch, h, resolveComponent, computed } from 'vue'
import { useFetchJson } from '@/composable/useFetchJson'
import Default from '@/layouts/default.vue' import Default from '@/layouts/default.vue'
import { useRoute, useRouter } from 'vue-router' import { useRoute, useRouter } from 'vue-router'
import type { TableColumn } from '@nuxt/ui' import type { TableColumn } from '@nuxt/ui'
import CategoryMenu from '../inner/categoryMenu.vue' import CategoryMenu from '../inner/CategoryMenu.vue'
import { useCustomerProductStore } from '@/stores/customer/customer-product'
import type { Product } from '@/stores/customer/customer-product'
interface Product {
reference: number
product_id: number
name: string
image_link: string
link_rewrite: string
}
const customerProductStore = useCustomerProductStore()
const router = useRouter() const router = useRouter()
const route = useRoute() const route = useRoute()
@@ -101,18 +99,6 @@ const sortField = computed({
} }
}) })
const perPage = ref(15)
const total = ref(0)
interface ApiResponse {
message: string
items: Product[]
count: number
}
const productsList = ref<Product[]>([])
const loading = ref(true)
const error = ref<string | null>(null)
const filters = computed<Record<string, string>>({ const filters = computed<Record<string, string>>({
get: () => { get: () => {
const q = { ...route.query } const q = { ...route.query }
@@ -157,36 +143,16 @@ const updateFilter = debounce((columnId: string, val: string) => {
filters.value = newFilters filters.value = newFilters
}, 400) }, 400)
async function fetchProductList() {
loading.value = true
error.value = null
const params = new URLSearchParams()
Object.entries(route.query).forEach(([key, value]) => {
if (value) params.append(key, String(value))
})
const url = `/api/v1/restricted/list/list-products?${params}`
try {
const response = await useFetchJson<ApiResponse>(url)
productsList.value = response.items || []
total.value = response.count || 0
} catch (e: unknown) {
error.value = e instanceof Error ? e.message : 'Failed to load products'
} finally {
loading.value = false
}
}
function goToProduct(productId: number) { function goToProduct(productId: number) {
router.push({ router.push({
name: 'product-detail', name: 'customer-product-details',
params: { id: productId } params: { product_id: productId }
}) })
} }
const selectedCount = ref({ const selectedCount = ref({
product_id: null, product_id: null,
count: 0 count: 0
@@ -205,7 +171,7 @@ const UInput = resolveComponent('UInput')
const UButton = resolveComponent('UButton') const UButton = resolveComponent('UButton')
const UIcon = resolveComponent('UIcon') const UIcon = resolveComponent('UIcon')
const columns: TableColumn<Payment>[] = [ const columns: TableColumn<Product>[] = [
{ {
id: 'expand', id: 'expand',
cell: ({ row }) => cell: ({ row }) =>
@@ -351,6 +317,35 @@ const columns: TableColumn<Payment>[] = [
variant: 'solid' variant: 'solid'
}, 'Add to cart') }, 'Add to cart')
}, },
},
{
accessorKey: 'count',
header: '',
cell: ({ row }) => {
return h(UButton, {
onClick: () => {
goToProduct(row.original.product_id)
},
class: 'cursor-pointer',
color: 'info',
variant: 'soft'
}, () => 'Show product')
},
},
{
accessorKey: 'counta',
header: '',
cell: ({ row }) => {
return h(UIcon, {
onClick: () => customerProductStore.toggleFavorite(row.original),
class: [
'cursor-pointer text-[20px] transition-transform duration-200 hover:scale-125',
row.original.is_favorite ? 'text-red-500' : 'text-blue-500'
],
name: 'material-symbols:favorite',
variant: 'soft',
})
}
} }
] ]
@@ -417,13 +412,27 @@ const columnsChild: TableColumn<Payment>[] = [
variant: 'solid' variant: 'solid'
}, 'Add to cart') }, 'Add to cart')
}, },
},
{
accessorKey: 'count',
header: '',
cell: ({ row }) => {
return h(UButton, {
onClick: () => {
goToProduct(row.original.product_id)
},
class: 'cursor-pointer',
color: 'info',
variant: 'soft'
}, () => 'Show product')
},
} }
] ]
watch( watch(
() => route.query, () => route.query,
() => { () => {
fetchProductList() customerProductStore.fetchProductList()
}, },
{ immediate: true } { immediate: true }
) )

View File

@@ -1,6 +1,6 @@
<template> <template>
<component :is="Default || 'div'"> <component :is="Default || 'div'">
<div class="container mx-auto"> <div class="">
<div class="flex flex-col gap-5 mb-6"> <div class="flex flex-col gap-5 mb-6">
<h1 class="text-2xl font-bold text-black dark:text-white">{{ t('Customer Data') }}</h1> <h1 class="text-2xl font-bold text-black dark:text-white">{{ t('Customer Data') }}</h1>
@@ -24,7 +24,7 @@
class="bg-(--second-light) dark:bg-(--main-dark) rounded-lg border border-(--border-light) dark:border-(--border-dark) p-4"> class="bg-(--second-light) dark:bg-(--main-dark) rounded-lg border border-(--border-light) dark:border-(--border-dark) p-4">
<h2 class="text-xl font-semibold text-black dark:text-white mb-4 flex items-center gap-2"> <h2 class="text-xl font-semibold text-black dark:text-white mb-4 flex items-center gap-2">
<UIcon name="mdi:domain" <UIcon name="mdi:domain"
class="text-[24px] text-(--accent-blue-light) dark:text-(--accent-blue-dark)" /> class="text-[24px] text-(--text-sky-light) dark:text-(--text-sky-dark)" />
{{ t('Company Information') }} {{ t('Company Information') }}
</h2> </h2>
<div class="grid grid-cols-1 gap-10"> <div class="grid grid-cols-1 gap-10">
@@ -65,7 +65,7 @@
<h2 <h2
class="text-xl font-semibold text-black dark:text-white mb-2 flex items-center gap-2"> class="text-xl font-semibold text-black dark:text-white mb-2 flex items-center gap-2">
<UIcon name="mdi:map-marker" <UIcon name="mdi:map-marker"
class="text-[24px] text-(--accent-blue-light) dark:text-(--accent-blue-dark)" /> class="text-[24px] text-(--text-sky-light) dark:text-(--text-sky-dark)" />
{{ t('Addresses') }} {{ t('Addresses') }}
</h2> </h2>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6"> <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
@@ -88,7 +88,7 @@
</div> </div>
<div class="flex justify-end"> <div class="flex justify-end">
<UButton color="primary" variant="outline" <UButton color="primary" variant="outline"
class="text-(--accent-blue-light) dark:text-(--accent-blue-dark) border-(--accent-blue-light) dark:border-(--accent-blue-dark)" class="text-(--text-sky-light) dark:text-(--text-sky-dark) border-(--accent-blue-light) dark:border-(--accent-blue-dark)"
@click="goToCreateAccount"> @click="goToCreateAccount">
<UIcon name="ic:sharp-edit" /> <UIcon name="ic:sharp-edit" />
{{ t('Edit Account') }} {{ t('Edit Account') }}
@@ -104,7 +104,7 @@
import { computed } from 'vue' import { computed } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { useCustomerStore } from '@/stores/customer' import { useCustomerStore } from '@/stores/customer'
import { useAddressStore } from '@/stores/address' import { useAddressStore } from '@/stores/customer/address'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import Default from '@/layouts/default.vue' import Default from '@/layouts/default.vue'
const router = useRouter() const router = useRouter()

View File

@@ -1,6 +1,6 @@
<template> <template>
<component :is="Default || 'div'"> <component :is="Default || 'div'">
<div class="container mx-auto"> <div class="">
<div class="max-w-2xl mx-auto"> <div class="max-w-2xl mx-auto">
<div class="flex flex-col gap-5 mb-6"> <div class="flex flex-col gap-5 mb-6">
<h1 class="text-2xl font-bold text-black dark:text-white">{{ t('Create Account') }}</h1> <h1 class="text-2xl font-bold text-black dark:text-white">{{ t('Create Account') }}</h1>
@@ -11,7 +11,7 @@
<h2 <h2
class="text-lg font-semibold text-black dark:text-white mb-4 flex items-center gap-2"> class="text-lg font-semibold text-black dark:text-white mb-4 flex items-center gap-2">
<UIcon name="mdi:domain" <UIcon name="mdi:domain"
class="text-[20px] text-(--accent-blue-light) dark:text-(--accent-blue-dark)" /> class="text-[20px] text-(--text-sky-light) dark:text-(--text-sky-dark)" />
{{ t('Company Information') }} {{ t('Company Information') }}
</h2> </h2>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4"> <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
@@ -54,7 +54,7 @@
<h2 <h2
class="text-lg font-semibold text-black dark:text-white mb-4 flex items-center gap-2"> class="text-lg font-semibold text-black dark:text-white mb-4 flex items-center gap-2">
<UIcon name="mdi:map-marker" <UIcon name="mdi:map-marker"
class="text-[20px] text-(--accent-blue-light) dark:text-(--accent-blue-dark)" /> class="text-[20px] text-(--text-sky-light) dark:text-(--text-sky-dark)" />
{{ t('Select Addresses') }} {{ t('Select Addresses') }}
</h2> </h2>
<div class="bg-(--second-light) dark:bg-(--main-dark)"> <div class="bg-(--second-light) dark:bg-(--main-dark)">
@@ -70,7 +70,7 @@
? 'border-(--accent-blue-light) dark:border-(--accent-blue-dark) bg-blue-50 dark:bg-blue-900/20' ? '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'"> : 'border-(--border-light) dark:border-(--border-dark) hover:border-gray-400'">
<input type="radio" :value="address.id" v-model="selectedAddress" <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)" /> class="mt-1 w-4 h-4 text-(--text-sky-light) dark:text-(--text-sky-dark)" />
<div class="flex-1"> <div class="flex-1">
<p class="text-black dark:text-white font-medium">{{ address.street }} <p class="text-black dark:text-white font-medium">{{ address.street }}
</p> </p>
@@ -87,7 +87,7 @@
<UIcon name="mdi:map-marker-outline" class="text-4xl text-gray-400 mb-2" /> <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> <p class="text-gray-500 dark:text-gray-400">{{ t('No addresses found') }}</p>
<RouterLink :to="{ name: 'addresses' }" <RouterLink :to="{ name: 'addresses' }"
class="inline-block mt-2 text-(--accent-blue-light) dark:text-(--accent-blue-dark) hover:underline"> class="inline-block mt-2 text-(--text-sky-light) dark:text-(--text-sky-dark) hover:underline">
{{ t('Add Address') }} {{ t('Add Address') }}
</RouterLink> </RouterLink>
</div> </div>
@@ -116,9 +116,9 @@
import { ref, watch } from 'vue' import { ref, watch } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { useCustomerStore } from '@/stores/customer' import { useCustomerStore } from '@/stores/customer'
import { useAddressStore } from '@/stores/address' import { useAddressStore } from '@/stores/customer/address'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { useCartStore } from '@/stores/cart' import { useCartStore } from '@/stores/customer/cart'
import Default from '@/layouts/default.vue' import Default from '@/layouts/default.vue'
const router = useRouter() const router = useRouter()
const customerStore = useCustomerStore() const customerStore = useCustomerStore()

View File

@@ -0,0 +1,161 @@
<template>
<component :is="Default || 'div'">
<div class="p-4">
<div v-if="loading" class="flex justify-center py-8">
<ULoader />
</div>
<div v-else-if="error" class="text-red-500">
{{ error }}
</div>
<UTree v-if="showTree" :items="treeItems" v-model:expanded="expandedFolders" :key="treeKey" @toggle="onToggle" :get-key="item => item.value">
<template #item-wrapper="{ item }">
<div class="flex items-start cursor-pointer" @click.stop="!item.isFolder">
<div class="flex items-center gap-1">
<UIcon :name="item.icon" :size="30" />
<div class="flex gap-1 items-center">
<span class="text-[15px] font-medium">
{{ item.label }}
</span>
<UButton v-if="!item.isFolder && item.fileName" size="xxs" color="neutral"
variant="outline" icon="i-lucide-download"
@click.stop="downloadFile(item.path, item.fileName)" :ui="{ base: 'ring-0!' }" />
</div>
</div>
</div>
</template>
</UTree>
</div>
</component>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue'
import { useFetchJson } from '@/composable/useFetchJson'
import Default from '@/layouts/default.vue'
interface FileItemRaw {
Name: string
IsFolder: boolean
}
interface FileItem {
name: string
type: 'file' | 'folder'
}
interface TreeItem {
label: string
icon: string
children?: TreeItem[]
isFolder: boolean
path: string
value: string
fileName?: string
}
const props = defineProps<{ initialPath?: string }>()
const currentPath = ref(props.initialPath || '')
const allData = ref<Record<string, FileItem[]>>({})
const expandedFolders = ref<string[]>([])
const treeKey = ref(0)
const loading = ref(false)
const error = ref<string | null>(null)
const showTree = computed(() => !error.value)
async function fetchFolderContents(path: string): Promise<FileItem[]> {
const url = `/api/v1/restricted/storage/list-content/${path}`
const data = await useFetchJson<FileItemRaw[]>(url)
return (data.items || []).map(i => ({
name: i.Name,
type: i.IsFolder ? 'folder' : 'file'
}))
}
async function loadFolder(path: string) {
if (allData.value[path]) return
try {
const items = await fetchFolderContents(path)
allData.value[path] = items
} catch (e) {
error.value = e instanceof Error ? e.message : 'Failed to load folder contents'
}
}
function buildTree(path: string): TreeItem[] {
const items = allData.value[path] || []
return items.map(item => {
const itemPath = path ? `${path}/${item.name}` : item.name
const isFolder = item.type === 'folder'
const isExpanded = expandedFolders.value.includes(itemPath)
const isLoaded = !!allData.value[itemPath]
return {
label: item.name,
icon: isFolder ? 'fxemoji:folder' : 'flat-color-icons:file',
isFolder,
path: isFolder ? itemPath : path,
value: itemPath,
fileName: isFolder ? undefined : item.name,
children: isFolder && isExpanded && isLoaded
? buildTree(itemPath)
: []
}
})
}
const treeItems = computed(() => buildTree(currentPath.value))
async function toggleFolder(item: TreeItem) {
if (!item.isFolder) return
const isOpen = expandedFolders.value.includes(item.value)
if (!isOpen) {
await loadFolder(item.value)
treeKey.value++
} else {
treeKey.value++
}
}
function onToggle(_event: unknown, item: TreeItem) {
console.log('Toggle:', item)
if (item.isFolder) {
toggleFolder(item)
}
}
async function downloadFile(path: string, fileName: string) {
try {
const response = await fetch(`/api/v1/restricted/storage/download-file/${path}/${fileName}`)
if (!response.ok) throw new Error('Download failed')
const blob = await response.blob()
const url = window.URL.createObjectURL(blob)
const a = document.createElement('a')
a.href = url
a.download = fileName
document.body.appendChild(a)
a.click()
a.remove()
window.URL.revokeObjectURL(url)
} catch (e) {
console.error(e)
alert('Download failed')
}
}
loadFolder(currentPath.value)
</script>

View File

@@ -1,5 +1,7 @@
<template> <template>
<UNavigationMenu orientation="vertical" type="single" :items="items" class="data-[orientation=vertical]:w-72" /> <UNavigationMenu orientation="vertical" type="single" :items="items" class="data-[orientation=vertical]:w-72" :ui="{
root:''
}"/>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
@@ -39,7 +41,7 @@ function adaptMenu(menu: NavigationMenuItem[]) {
adaptMenu(item.children); adaptMenu(item.children);
item.children.unshift({ item.children.unshift({
label: item.label, icon: 'i-lucide-book-open', popover: item.label, to: { label: item.label, icon: 'i-lucide-book-open', popover: item.label, to: {
name: 'customer-products-category', params: { name: 'admin-products-category', params: {
category_id: item.params.category_id, category_id: item.params.category_id,
link_rewrite: item.params.link_rewrite link_rewrite: item.params.link_rewrite
} }
@@ -47,7 +49,7 @@ function adaptMenu(menu: NavigationMenuItem[]) {
}) })
} else { } else {
item.to = { item.to = {
name: 'customer-products-category', params: { name: 'admin-products-category', params: {
category_id: item.params.category_id, category_id: item.params.category_id,
link_rewrite: item.params.link_rewrite link_rewrite: item.params.link_rewrite
} }

View File

@@ -0,0 +1,53 @@
<template>
<USelectMenu v-model="country" :items="countries" value-key="id"
class="w-48 bg-(--main-light) dark:bg-(--black) rounded-md shadow-sm" :searchInput="false">
<template #default>
<div class="flex flex-col items-start leading-tight">
<span class="text-xs text-gray-400">
Country/Currency
</span>
<span v-if="country" class="font-medium dark:text-white text-black">
{{ country?.name || 'Country' }} / {{ country?.ps_currency || 'Currency' }}
</span>
</div>
</template>
<template #item-leading="{ item }">
<div class="flex items-center gap-2 cursor-pointer">
<span class="text-lg">{{ item.flag }}</span>
<span class="font-medium dark:text-white text-black">
{{ item.name }}
</span>
</div>
</template>
</USelectMenu>
</template>
<script setup lang="ts">
import { countries, currentCountry, switchLocalization } from '@/router/langs'
import { useCookie } from '@/composable/useCookie'
import { computed, watch } from 'vue'
const cookie = useCookie()
const country = computed({
get() {
return currentCountry.value
},
set(value: string) {
currentCountry.value = countries.find((x) => x.id == Number(value))
cookie.setCookie('country_id', `${countries.find((x) => x.id == Number(value))?.id}`, { days: 60, secure: true, sameSite: 'Lax' })
switchLocalization()
},
})
watch(
() => country,
(newCountry) => {
if (newCountry) {
currentCountry.value = countries.find((x) => x.id == Number(newCountry.value?.id))
}
},
{ immediate: true },
)
</script>

View File

@@ -1,34 +1,47 @@
<template> <template>
<USelectMenu v-model="locale" :items="langs" <USelectMenu v-model="locale" :items="langs" value-key="iso_code"
class="w-40 bg-(--main-light) dark:bg-(--black) rounded-md shadow-sm hover:none!" valueKey="iso_code" class="w-48 bg-(--main-light) dark:bg-(--black) rounded-md shadow-sm" :searchInput="false">
:searchInput="false"> <template #default>
<template #default="{ modelValue }"> <div class="flex items-center gap-2">
<div class="flex items-center gap-1"> <!-- <span class="text-lg">{{ selectedLang?.flag }}</span> -->
<!-- <span class="text-md dark:text-white text-black">{{langs.find(x => x.iso_code == modelValue)?.flag}}</span> -->
<span class="font-medium dark:text-white text-black">{{langs.find(x => x.iso_code == modelValue)?.name}}</span> <div class="flex flex-col leading-tight items-start">
<span class="text-xs text-gray-400">
Language
</span>
<span class="font-medium dark:text-white text-black">
{{ selectedLang?.name || 'Select language' }}
</span>
</div>
</div> </div>
</template> </template>
<template #item-leading="{ item }"> <template #item-leading="{ item }">
<div class="flex items-center rounded-md cursor-pointer transition-colors"> <div class="flex items-center gap-2">
<!-- <span class="text-md ">{{ item.flag }}</span> --> <span class="text-lg">{{ item.flag }}</span>
<span class="ml-2 dark:text-white text-black font-medium">{{ item.name }}</span> <span class="font-medium dark:text-white text-black">
{{ item.name }}
</span>
</div> </div>
</template> </template>
</USelectMenu> </USelectMenu>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { langs, currentLang } from '@/router/langs' import { langs, currentLang, switchLocalization } from '@/router/langs'
import { useRouter, useRoute } from 'vue-router' import { useRouter, useRoute } from 'vue-router'
import { useCookie } from '@/composable/useCookie' import { useCookie } from '@/composable/useCookie'
import { computed, watch } from 'vue' import { computed, watch } from 'vue'
import { i18n } from '@/plugins/02_i18n' import { i18n } from '@/plugins/02_i18n'
import { useFetchJson } from '@/composable/useFetchJson'
const router = useRouter() const router = useRouter()
const route = useRoute() const route = useRoute()
const cookie = useCookie() const selectedLang = computed(() =>
langs.find(item => item.iso_code === locale.value)
)
const cookie = useCookie()
const locale = computed({ const locale = computed({
get() { get() {
return currentLang.value?.iso_code || i18n.locale.value return currentLang.value?.iso_code || i18n.locale.value
@@ -52,21 +65,10 @@ const locale = computed({
} }
} }
changeLang() switchLocalization()
}, },
}) })
async function changeLang() {
try {
const { items } = await useFetchJson('/api/v1/public/auth/update-choice', {
method: 'POST'
})
} catch (error) {
console.log(error)
}
}
watch( watch(
() => route.params.locale, () => route.params.locale,
(newLocale) => { (newLocale) => {

View File

@@ -0,0 +1,160 @@
<template>
<UEditor v-slot="{ editor }" v-model="localValue" content-type="html"
:ui="{ base: 'p-8 sm:px-16', root: 'p-2' }"
class="min-w-full border rounded-md bg-white! border-(--border-light)" placeholder="Write there ...">
<UEditorToolbar :editor="editor" :items="toolbarItems" class="sm:px-8 flex-wrap!">
<template #link>
<EditorLinkPopover :editor="editor" auto-open />
</template>
</UEditorToolbar>
</UEditor>
</template>
<script lang="ts" setup>
import type { EditorToolbarItem } from '@nuxt/ui';
import { computed } from 'vue';
const props = defineProps<{
modelValue: string
}>()
const emit = defineEmits<{
(e: 'update:modelValue', value: string): void
}>()
const localValue = computed({
get: () => props.modelValue,
set: (value: string) => emit('update:modelValue', value)
})
const toolbarItems: EditorToolbarItem[][] = [
// History controls
[{
kind: 'undo',
icon: 'i-lucide-undo',
tooltip: { text: 'Undo' }
}, {
kind: 'redo',
icon: 'i-lucide-redo',
tooltip: { text: 'Redo' }
}],
// Block types
[{
icon: 'i-lucide-heading',
tooltip: { text: 'Headings' },
content: {
align: 'start'
},
items: [{
kind: 'heading',
level: 1,
icon: 'i-lucide-heading-1',
label: 'Heading 1'
}, {
kind: 'heading',
level: 2,
icon: 'i-lucide-heading-2',
label: 'Heading 2'
}, {
kind: 'heading',
level: 3,
icon: 'i-lucide-heading-3',
label: 'Heading 3'
}, {
kind: 'heading',
level: 4,
icon: 'i-lucide-heading-4',
label: 'Heading 4'
}]
}, {
icon: 'i-lucide-list',
tooltip: { text: 'Lists' },
content: {
align: 'start'
},
items: [{
kind: 'bulletList',
icon: 'i-lucide-list',
label: 'Bullet List'
}, {
kind: 'orderedList',
icon: 'i-lucide-list-ordered',
label: 'Ordered List'
}]
}, {
kind: 'blockquote',
icon: 'i-lucide-text-quote',
tooltip: { text: 'Blockquote' }
}, {
kind: 'codeBlock',
icon: 'i-lucide-square-code',
tooltip: { text: 'Code Block' }
}, {
kind: 'horizontalRule',
icon: 'i-lucide-separator-horizontal',
tooltip: { text: 'Horizontal Rule' }
}],
// Text formatting
[{
kind: 'mark',
mark: 'bold',
icon: 'i-lucide-bold',
tooltip: { text: 'Bold' }
}, {
kind: 'mark',
mark: 'italic',
icon: 'i-lucide-italic',
tooltip: { text: 'Italic' }
}, {
kind: 'mark',
mark: 'underline',
icon: 'i-lucide-underline',
tooltip: { text: 'Underline' }
}, {
kind: 'mark',
mark: 'strike',
icon: 'i-lucide-strikethrough',
tooltip: { text: 'Strikethrough' }
}, {
kind: 'mark',
mark: 'code',
icon: 'i-lucide-code',
tooltip: { text: 'Code' }
}],
// Link
[{
kind: 'link',
icon: 'i-lucide-link',
tooltip: { text: 'Link' }
}],
// Text alignment
[{
icon: 'i-lucide-align-justify',
tooltip: { text: 'Text Align' },
content: {
align: 'end'
},
items: [{
kind: 'textAlign',
align: 'left',
icon: 'i-lucide-align-left',
label: 'Align Left'
}, {
kind: 'textAlign',
align: 'center',
icon: 'i-lucide-align-center',
label: 'Align Center'
}, {
kind: 'textAlign',
align: 'right',
icon: 'i-lucide-align-right',
label: 'Align Right'
}, {
kind: 'textAlign',
align: 'justify',
icon: 'i-lucide-align-justify',
label: 'Align Justify'
}]
}]
]
</script>

View File

@@ -0,0 +1,37 @@
<template>
<UButton variant="outline" size="sm" color="info" @click="themeStorage.setTheme()">
<span class="hidden sm:inline">
<UIcon class="size-5" :name="themeStorage.themeIcon" />
</span>
</UButton>
<!-- <UButton variant="solid" size="sm" color="info" @click="themeStorage.setTheme()">
<span class="hidden sm:inline">
<UIcon class="size-5" :name="themeStorage.themeIcon" />
</span>
</UButton>
<UButton variant="soft" size="sm" color="info" @click="themeStorage.setTheme()">
<span class="hidden sm:inline">
<UIcon class="size-5" :name="themeStorage.themeIcon" />
</span>
</UButton>
<UButton variant="subtle" size="sm" color="info" @click="themeStorage.setTheme()">
<span class="hidden sm:inline">
<UIcon class="size-5" :name="themeStorage.themeIcon" />
</span>
</UButton>
<UButton variant="ghost" size="sm" color="info" @click="themeStorage.setTheme()">
<span class="hidden sm:inline">
<UIcon class="size-5" :name="themeStorage.themeIcon" />
</span>
</UButton>
<UButton variant="link" size="sm" color="info" @click="themeStorage.setTheme()">
<span class="hidden sm:inline">
<UIcon class="size-5" :name="themeStorage.themeIcon" />
</span>
</UButton> -->
</template>
<script setup lang="ts">
import { useThemeStore } from '@/stores/admin/theme'
const themeStorage = useThemeStore()
</script>

View File

@@ -1,12 +0,0 @@
<template>
<UButton variant="ghost" size="sm" @click="themeStorage.setTheme()">
<span class="hidden sm:inline">
<UIcon class="size-5" :name="themeStorage.themeIcon" />
</span>
</UButton>
</template>
<script setup lang="ts">
import { useThemeStore } from '@/stores/theme'
const themeStorage = useThemeStore()
</script>

View File

@@ -29,7 +29,7 @@ export async function useFetchJson<T = unknown>(url: string, opt?: RequestInit):
// Handle 401 — access token expired; try to refresh via the HTTPOnly refresh_token cookie // Handle 401 — access token expired; try to refresh via the HTTPOnly refresh_token cookie
if (res.status === 401) { if (res.status === 401) {
const { useAuthStore } = await import('@/stores/auth') const { useAuthStore } = await import('../stores/customer/auth')
const authStore = useAuthStore() const authStore = useAuthStore()
const refreshed = await authStore.refreshAccessToken() const refreshed = await authStore.refreshAccessToken()

View File

@@ -1,14 +1,301 @@
<script setup lang="ts">
import TopBar from '@/components/TopBar.vue';
</script>
<template> <template>
<div class="h-screen grid grid-rows-[auto_1fr_auto]"> <div class="flex flex-1 overflow-x-hidden h-svh">
<main class="pt-20 pb-10"> <USidebar v-model:open="open" collapsible="icon" rail :ui="{
<TopBar /> container: 'h-full z-80',
<div class="container mx-auto px-4"> inner: 'bg-elevated/25 divide-transparent',
body: 'py-0'
}">
<template #header>
<UDropdownMenu :items="teamsItems" :content="{ align: 'start', collisionPadding: 12 }"
:ui="{ content: 'w-(--reka-dropdown-menu-trigger-width) min-w-48' }">
<UButton v-bind="selectedTeam" trailing-icon="i-lucide-chevrons-up-down" color="neutral" variant="ghost"
square class="w-full data-[state=open]:bg-elevated overflow-hidden" :ui="{
trailingIcon: 'text-dimmed ms-auto'
}" />
</UDropdownMenu>
</template>
<template #default="{ state }">
<UNavigationMenu :key="state" :items="menuItems" orientation="vertical"
:ui="{ link: 'p-1.5 overflow-hidden' }" />
</template>
<template #footer>
<UDropdownMenu :items="userItems" :content="{ align: 'center', collisionPadding: 12 }"
:ui="{ content: 'w-(--reka-dropdown-menu-trigger-width) min-w-48' }">
<UButton v-bind="userStore.user" :label="userStore.user?.email" trailing-icon="i-lucide-chevrons-up-down"
color="neutral" variant="ghost" square class="w-full data-[state=open]:bg-elevated overflow-hidden" :ui="{
trailingIcon: 'text-dimmed ms-auto'
}" />
</UDropdownMenu>
<!-- first_name: '', last_name: '' -->
</template>
</USidebar>
<div class="flex-1 flex flex-col">
<div class="flex h-(--ui-header-height) shrink-0 items-center justify-between px-4 border-b border-default">
<div class="flex items-center gap-2">
<UButton icon="i-lucide-panel-left" color="neutral" variant="ghost" aria-label="Toggle sidebar"
@click="open = !open" />
<span class="text-[20px] font-medium">{{ pageTitle }}</span>
</div>
<div class="hidden md:flex items-center gap-12">
<div class="flex items-center gap-2">
<CountryCurrencySwitch />
<LangSwitch />
</div>
<div class="flex items-center gap-2">
<ThemeSwitch />
<button v-if="authStore.isAuthenticated" @click="authStore.logout()"
class="flex gap-2 items-center px-3 py-1.5 text-sm font-medium text-black dark:text-white hover:text-black dark:hover:text-white hover:bg-gray-100 dark:hover:bg-gray-600 rounded-lg transition-colors border border-(--border-light) dark:border-(--border-dark) whitespace-nowrap">
{{ $t('general.logout') }}
<UIcon name="ic:baseline-logout" class="text-red-700 dark:text-red-500"/>
</button>
</div>
</div>
</div>
<div class="flex-1 p-4 bg-slate-50 dark:bg-(--main-dark)">
<slot /> <slot />
</div> </div>
</main> </div>
</div> </div>
</template> </template>
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'
import { useColorMode } from '@vueuse/core'
import type { DropdownMenuItem, NavigationMenuItem } from '@nuxt/ui'
import { defineShortcuts, extractShortcuts } from '@nuxt/ui/runtime/composables/defineShortcuts.js'
import { useAuthStore } from '../stores/customer/auth'
const authStore = useAuthStore()
const userStore = useUserStore()
const route = useRoute()
const pageTitle = computed(() => route.meta.name ?? 'Default Page')
await userStore.getUser()
const open = ref(true)
const colorMode = useColorMode()
const teams = ref([
{
label: 'Nuxt',
avatar: {
src: 'https://github.com/nuxt.png',
alt: 'Nuxt'
}
},
{
label: 'Vue',
avatar: {
src: 'https://github.com/vuejs.png',
alt: 'Vue'
}
},
{
label: 'UnJS',
avatar: {
src: 'https://github.com/unjs.png',
alt: 'UnJS'
}
}
])
const selectedTeam = ref(teams.value[0])
const teamsItems = computed<DropdownMenuItem[][]>(() => {
return [
teams.value.map((team, index) => ({
...team,
kbds: ['meta', String(index + 1)],
onSelect() {
selectedTeam.value = team
}
})),
[
{
label: 'Create team',
icon: 'i-lucide-circle-plus'
}
]
]
})
function getItems(state: 'collapsed' | 'expanded') {
return [
{
label: 'Inbox',
icon: 'i-lucide-inbox',
badge: '4'
},
{
label: 'Issues',
icon: 'i-lucide-square-dot'
},
{
label: 'Activity',
icon: 'i-lucide-square-activity'
},
{
label: 'Settings',
icon: 'i-lucide-settings',
defaultOpen: true,
children:
state === 'expanded'
? [
{
label: 'General',
icon: 'i-lucide-house'
},
{
label: 'Team',
icon: 'i-lucide-users'
},
{
label: 'Billing',
icon: 'i-lucide-credit-card'
}
]
: []
}
] satisfies NavigationMenuItem[]
}
//
import { useRoute, useRouter } from 'vue-router'
import { currentLang } from '@/router/langs'
import { useFetchJson } from '@/composable/useFetchJson'
import CountryCurrencySwitch from '@/components/inner/CountryCurrencySwitch.vue'
import LangSwitch from '@/components/inner/LangSwitch.vue'
import ThemeSwitch from '@/components/inner/ThemeSwitch.vue'
import type { LabelTrans, TopMenuItem } from '@/types'
import { useUserStore } from '@/stores/user'
const router = useRouter()
const menu = ref<TopMenuItem[] | null>(null)
async function getTopMenu() {
try {
const { items } = await useFetchJson<TopMenuItem[]>('/api/v1/restricted/menu/get-top-menu')
menu.value = items
} catch (err) {
console.log(err)
}
}
onMounted(() => {
getTopMenu()
})
const menuItems = computed(() => {
if (!menu.value?.length) return []
return transformMenu(
menu.value || [],
currentLang.value?.iso_code
)
})
function transformMenu(
items: TopMenuItem[],
locale: string | undefined
): NavigationMenuItem[] {
return items.map((item) => {
const route: NavigationMenuItem = {
icon: item.label.icon || 'i-lucide-house',
label:
item.label.trans?.[locale as keyof LabelTrans]?.label ||
item.label.trans?.en?.label ||
'—',
children: item.children
? transformMenu(item.children, locale)
: undefined,
onSelect: () => {
router.push({
name: item.params.route.name,
params: {
...(item.params.route.params || {}),
locale: currentLang.value?.iso_code
}
})
}
}
return route
})
}
const userItems = computed<DropdownMenuItem[][]>(() => [
[
{
label: 'Profile',
icon: 'i-lucide-user'
},
{
label: 'Billing',
icon: 'i-lucide-credit-card'
},
{
label: 'Settings',
icon: 'i-lucide-settings',
to: '/settings'
}
],
[
{
label: 'Appearance',
icon: 'i-lucide-sun-moon',
children: [
{
label: 'Light',
icon: 'i-lucide-sun',
type: 'checkbox',
checked: colorMode.value === 'light',
onUpdateChecked(checked: boolean) {
if (checked) {
colorMode.preference = 'light'
}
},
onSelect(e: Event) {
e.preventDefault()
}
},
{
label: 'Dark',
icon: 'i-lucide-moon',
type: 'checkbox',
checked: colorMode.value === 'dark',
onUpdateChecked(checked: boolean) {
if (checked) {
colorMode.preference = 'dark'
}
},
onSelect(e: Event) {
e.preventDefault()
}
}
]
}
],
[
{
label: 'GitHub',
icon: 'i-simple-icons-github',
to: 'https://github.com/nuxt/ui',
target: '_blank'
},
{
label: 'Log out',
icon: 'i-lucide-log-out'
}
]
])
defineShortcuts(extractShortcuts(teamsItems.value))
</script>

View File

@@ -0,0 +1,311 @@
<template>
<div class="flex flex-1 overflow-x-hidden h-svh">
<USidebar v-model:open="open" collapsible="icon" rail :ui="{
container: 'h-full z-80',
inner: 'bg-elevated/25 divide-transparent',
body: 'py-0'
}">
<template #header>
<UDropdownMenu :items="teamsItems" :content="{ align: 'start', collisionPadding: 12 }"
:ui="{ content: 'w-(--reka-dropdown-menu-trigger-width) min-w-48' }">
<UButton v-bind="selectedTeam" trailing-icon="i-lucide-chevrons-up-down" color="neutral"
variant="ghost" square class="w-full data-[state=open]:bg-elevated overflow-hidden" :ui="{
trailingIcon: 'text-dimmed ms-auto'
}" />
</UDropdownMenu>
</template>
<template #default="{ state }">
<UNavigationMenu :key="state" :items="menuItems" orientation="vertical"
:ui="{ link: 'p-1.5 overflow-hidden' }" />
</template>
<template #footer>
<UDropdownMenu :items="userItems" :content="{ align: 'center', collisionPadding: 12 }"
:ui="{ content: 'w-(--reka-dropdown-menu-trigger-width) min-w-48' }">
<UButton v-bind="userStore.user" :label="userStore.user?.email"
trailing-icon="i-lucide-chevrons-up-down" color="neutral" variant="ghost" square
class="w-full data-[state=open]:bg-elevated overflow-hidden" :ui="{
trailingIcon: 'text-dimmed ms-auto'
}" />
</UDropdownMenu>
<!-- first_name: '', last_name: '' -->
</template>
</USidebar>
<div class="flex-1 flex flex-col">
<div class="flex h-(--ui-header-height) shrink-0 items-center justify-between px-4 border-b border-default">
<div class="flex items-center gap-2">
<UButton icon="i-lucide-panel-left" color="neutral" variant="ghost" aria-label="Toggle sidebar"
@click="open = !open" />
<p class="font-bold text-xl">Customer-Management: <span class="text-[20px] font-medium">{{ pageTitle
}}</span></p>
</div>
<div class="hidden md:flex items-center gap-12">
<div class="flex items-center gap-2">
<CountryCurrencySwitch />
<LangSwitch />
</div>
<div class="flex items-center gap-2">
<ThemeSwitch />
<button v-if="authStore.isAuthenticated" @click="authStore.logout()"
class="flex gap-2 items-center px-3 py-1.5 text-sm font-medium text-black dark:text-white hover:text-black dark:hover:text-white hover:bg-gray-100 dark:hover:bg-gray-600 rounded-lg transition-colors border border-(--border-light) dark:border-(--border-dark) whitespace-nowrap">
{{ $t('general.logout') }}
<UIcon name="ic:baseline-logout" class="text-red-700 dark:text-red-500" />
</button>
</div>
</div>
</div>
<div class="flex-1 p-4 bg-slate-50 dark:bg-(--main-dark)">
<slot />
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'
import { useColorMode } from '@vueuse/core'
import type { DropdownMenuItem, NavigationMenuItem } from '@nuxt/ui'
import { defineShortcuts, extractShortcuts } from '@nuxt/ui/runtime/composables/defineShortcuts.js'
import { useAuthStore } from '../stores/customer/auth'
import { useRoute } from 'vue-router'
const route = useRoute()
const pageTitle = computed(() => route.meta.name ?? 'Default Page')
const authStore = useAuthStore()
const userStore = useUserStore()
const open = ref(true)
const colorMode = useColorMode()
const teams = ref([
{
label: 'Nuxt',
avatar: {
src: 'https://github.com/nuxt.png',
alt: 'Nuxt'
}
},
{
label: 'Vue',
avatar: {
src: 'https://github.com/vuejs.png',
alt: 'Vue'
}
},
{
label: 'UnJS',
avatar: {
src: 'https://github.com/unjs.png',
alt: 'UnJS'
}
}
])
const selectedTeam = ref(teams.value[0])
const teamsItems = computed<DropdownMenuItem[][]>(() => {
return [
teams.value.map((team, index) => ({
...team,
kbds: ['meta', String(index + 1)],
onSelect() {
selectedTeam.value = team
}
})),
[
{
label: 'Create team',
icon: 'i-lucide-circle-plus'
}
]
]
})
function getItems(state: 'collapsed' | 'expanded') {
return [
{
label: 'Inbox',
icon: 'i-lucide-inbox',
badge: '4'
},
{
label: 'Issues',
icon: 'i-lucide-square-dot'
},
{
label: 'Activity',
icon: 'i-lucide-square-activity'
},
{
label: 'Settings',
icon: 'i-lucide-settings',
defaultOpen: true,
children:
state === 'expanded'
? [
{
label: 'General',
icon: 'i-lucide-house'
},
{
label: 'Team',
icon: 'i-lucide-users'
},
{
label: 'Billing',
icon: 'i-lucide-credit-card'
}
]
: []
}
] satisfies NavigationMenuItem[]
}
//
import { useRouter } from 'vue-router'
import { currentLang } from '@/router/langs'
import { useFetchJson } from '@/composable/useFetchJson'
import CountryCurrencySwitch from '@/components/inner/CountryCurrencySwitch.vue'
import LangSwitch from '@/components/inner/LangSwitch.vue'
import ThemeSwitch from '@/components/inner/ThemeSwitch.vue'
import type { LabelTrans, TopMenuItem } from '@/types'
import { useUserStore } from '@/stores/user'
import { watch } from 'vue'
const router = useRouter()
const menu = ref<TopMenuItem[] | null>(null)
const Id =Number(route.params.user_id)
async function cmGetTopMenu() {
try {
const { items } = await useFetchJson<TopMenuItem[]>(`/api/v1/restricted/menu/get-top-menu?target_user_id=${Id}`)
menu.value = items
} catch (err) {
console.log(err)
}
}
console.log(route)
watch(
() => route.params.user_id,
() => {
if (route.params.user_id) {
cmGetTopMenu()
}
},
{ immediate: true }
)
const menuItems = computed(() => {
if (!menu.value?.length) return []
return transformMenu(
menu.value || [],
currentLang.value?.iso_code
)
})
function transformMenu(
items: TopMenuItem[],
locale: string | undefined
): NavigationMenuItem[] {
return items.map((item) => {
const route: NavigationMenuItem = {
icon: item.label.icon || 'i-lucide-house',
label:
item.label.trans?.[locale as keyof LabelTrans]?.label ||
item.label.trans?.en?.label ||
'—',
children: item.children
? transformMenu(item.children, locale)
: undefined,
onSelect: () => {
router.push({
name: item.params.route.name,
params: {
...(item.params.route.params || {}),
locale: currentLang.value?.iso_code
}
})
}
}
return route
})
}
const userItems = computed<DropdownMenuItem[][]>(() => [
[
{
label: 'Profile',
icon: 'i-lucide-user'
},
{
label: 'Billing',
icon: 'i-lucide-credit-card'
},
{
label: 'Settings',
icon: 'i-lucide-settings',
to: '/settings'
}
],
[
{
label: 'Appearance',
icon: 'i-lucide-sun-moon',
children: [
{
label: 'Light',
icon: 'i-lucide-sun',
type: 'checkbox',
checked: colorMode.value === 'light',
onUpdateChecked(checked: boolean) {
if (checked) {
colorMode.preference = 'light'
}
},
onSelect(e: Event) {
e.preventDefault()
}
},
{
label: 'Dark',
icon: 'i-lucide-moon',
type: 'checkbox',
checked: colorMode.value === 'dark',
onUpdateChecked(checked: boolean) {
if (checked) {
colorMode.preference = 'dark'
}
},
onSelect(e: Event) {
e.preventDefault()
}
}
]
}
],
[
{
label: 'GitHub',
icon: 'i-simple-icons-github',
to: 'https://github.com/nuxt/ui',
target: '_blank'
},
{
label: 'Log out',
icon: 'i-lucide-log-out'
}
]
])
defineShortcuts(extractShortcuts(teamsItems.value))
</script>

View File

@@ -1,11 +1,12 @@
import { useFetchJson } from '@/composable/useFetchJson' import { useFetchJson } from '@/composable/useFetchJson'
import { initLangs, langs } from '@/router/langs' import { initCountryCurrency, initLangs, langs } from '@/router/langs'
import { watch } from 'vue' import { watch } from 'vue'
import { createI18n, type PathValue } from 'vue-i18n' import { createI18n, type PathValue } from 'vue-i18n'
// const x = // const x =
await initLangs() await initLangs()
await initCountryCurrency()
export const i18ninstall = createI18n({ export const i18ninstall = createI18n({
legacy: false, // you must set `false`, to use Composition API legacy: false, // you must set `false`, to use Composition API
locale: 'en', locale: 'en',

View File

@@ -1,13 +1,14 @@
import { createRouter, createWebHistory } from 'vue-router' import { createRouter, createWebHistory } from 'vue-router'
import { currentLang, langs } from './langs' import { currentLang, langs, switchLocalization } from './langs'
import { getSettings } from './settings' import { getSettings } from './settings'
import { useAuthStore } from '@/stores/auth' import { useAuthStore } from '@/stores/customer/auth'
import { getRoutes } from './menu' import { getRoutes } from './menu'
function isAuthenticated(): boolean { function isAuthenticated(): boolean {
if (typeof document === 'undefined') return false if (typeof document === 'undefined') return false
return document.cookie.split('; ').some((c) => c === 'is_authenticated=1') return document.cookie.split('; ').some((c) => c === 'is_authenticated=1')
} }
await switchLocalization()
await getSettings() await getSettings()
const routes = await getRoutes() const routes = await getRoutes()
@@ -80,6 +81,8 @@ async function setRoutes() {
meta: item.meta ? JSON.parse(item.meta) : {} meta: item.meta ? JSON.parse(item.meta) : {}
}) })
} }
// await router.replace(router.currentRoute.value.fullPath)
} }
await setRoutes() await setRoutes()

View File

@@ -1,30 +1,67 @@
import { useCookie } from "@/composable/useCookie" import { useCookie } from "@/composable/useCookie"
import { useFetchJson } from "@/composable/useFetchJson" import { useFetchJson } from "@/composable/useFetchJson"
import type { Language } from "@/types" import type { Country, Language } from "@/types"
import { reactive, ref } from "vue" import { reactive, ref } from "vue"
export const langs = reactive([] as Language[]) export const langs = reactive([] as Language[])
export const currentLang = ref<Language>() export const currentLang = ref<Language>()
const deflang = ref<Language>() export const countries = reactive([] as Country[])
export const currentCountry = ref<Country>()
const defLang = ref<Language>()
const defCountry = ref<Country>()
const cookie = useCookie() const cookie = useCookie()
// Get available language codes for route matching // Get available language codes for route matching
// export const availableLocales = computed(() => langs.map((l) => l.lang_code)) // export const availableLocales = computed(() => langs.map((l) => l.lang_code))
// Initialize languages from API // Initialize languages from API
export async function initLangs() { export async function initLangs() {
try { try {
const { items } = await useFetchJson<Language[]>('/api/v1/langs') const { items } = await useFetchJson<Language[]>('/api/v1/langs')
langs.push(...items) langs.push(...items)
let idfromcookie = null let idfromcookie = null
const cc = cookie.getCookie('lang_id') const cc = cookie.getCookie('lang_id')
if (cc) { if (cc) {
idfromcookie = langs.find((x) => x.id == parseInt(cc)) idfromcookie = langs.find((x) => x.id == parseInt(cc))
}
deflang.value = items.find((x) => x.is_default == true)
currentLang.value = idfromcookie ?? deflang.value
} catch (error) {
console.error('Failed to fetch languages:', error)
} }
defLang.value = items.find((x) => x.is_default == true)
currentLang.value = idfromcookie ?? defLang.value
} catch (error) {
console.error('Failed to fetch languages:', error)
}
}
// Initialize country/currency from API
export async function initCountryCurrency() {
try {
const { items } = await useFetchJson<Country[]>('/api/v1/restricted/langs-and-countries/get-countries')
countries.push(...items)
let idfromcookie = null
const cc = cookie.getCookie('country_id')
if (cc) {
idfromcookie = langs.find((x) => x.id == parseInt(cc))
}
defCountry.value = items.find((x) => x.id === defLang.value?.id)
currentCountry.value = idfromcookie ?? defCountry.value
console.log(defCountry.value);
console.log(currentCountry.value);
} catch (error) {
console.error('Failed to fetch languages:', error)
}
}
export async function switchLocalization() {
try {
await useFetchJson('/api/v1/public/auth/update-choice', {
method: 'POST'
})
} catch (error) {
console.log(error)
}
} }

View File

@@ -1,6 +1,5 @@
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
import { ref, computed } from 'vue' import { ref, computed } from 'vue'
import type { Address } from './address'
export interface CustomerData { export interface CustomerData {
companyName: string companyName: string

View File

@@ -2,6 +2,7 @@ import { defineStore } from 'pinia'
import { ref } from 'vue' import { ref } from 'vue'
import { useFetchJson } from '@/composable/useFetchJson' import { useFetchJson } from '@/composable/useFetchJson'
import type { ProductDescription } from '@/types/product' import type { ProductDescription } from '@/types/product'
import { useSettingsStore } from './settings'
export interface Product { export interface Product {
id: number id: number
@@ -23,21 +24,21 @@ export interface ProductResponse {
} }
export const useProductStore = defineStore('product', () => { export const useProductStore = defineStore('product', () => {
const productDescription = ref()
const currentProduct = ref<Product | null>(null)
const loading = ref(false) const loading = ref(false)
const error = ref<string | null>(null) const error = ref<string | null>(null)
const productDescription = ref()
const copyProductDescription = ref()
async function getProductDescription(langId = 1, productID: number) { async function getProductDescription(langId: number | null, productID: number) {
loading.value = true loading.value = true
error.value = null error.value = null
try { try {
const response = await useFetchJson<ProductDescription>( const response = await useFetchJson<ProductDescription>(
`/api/v1/restricted/product-translation/get-product-description?productID=${productID}&productLangID=${langId}` `/api/v1/restricted/product-translation/get-product-description?productID=${productID}&productLangID=${langId ? langId : settingStore.shopDefaultLanguage}`
) )
productDescription.value = response.items productDescription.value = structuredClone(response.items)
copyProductDescription.value = structuredClone(response.items)
} catch (e: unknown) { } catch (e: unknown) {
error.value = e instanceof Error ? e.message : 'Failed to load product description' error.value = e instanceof Error ? e.message : 'Failed to load product description'
} finally { } finally {
@@ -45,35 +46,13 @@ export const useProductStore = defineStore('product', () => {
} }
} }
async function saveProductDescription(productID?: number) { const settingStore = useSettingsStore()
const id = productID || 1 async function translateProductDescription(productID: number, toLangId: number, model: string = 'Google') {
try {
const data = await useFetchJson(
`/api/v1/restricted/product-description/save-product-description?productID=${id}&productShopID=1&productLangID=1`,
{
method: 'POST',
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
})
}
)
return data
} catch (e) {
console.error(e)
}
}
async function translateProductDescription(productID: number, fromLangId: number, toLangId: number) {
loading.value = true loading.value = true
error.value = null error.value = null
try { try {
const response = await useFetchJson<ProductDescription>(`/api/v1/restricted/product-description/translate-product-description?productID=${productID}&productShopID=1&productFromLangID=${fromLangId}&productToLangID=${toLangId}&model=OpenAI`) const response = await useFetchJson<ProductDescription>(`/api/v1/restricted/product-translation/translate-product-description?productID=${productID}&productFromLangID=${settingStore.shopDefaultLanguage}&productToLangID=${toLangId}&model=${model}`)
productDescription.value = response.items productDescription.value = response.items
return response.items return response.items
} catch (e: any) { } catch (e: any) {
@@ -84,18 +63,46 @@ export const useProductStore = defineStore('product', () => {
} }
} }
function clearCurrentProduct() { async function saveProductDescription(productID?: number, langId?: number | null) {
currentProduct.value = null const id = productID || 1
const lang = langId || 1
try {
const data = await useFetchJson(
`/api/v1/restricted/product-translation/save-product-description?productID=${id}&productLangID=${lang}`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: productDescription.value?.name || '',
description: productDescription.value?.description || '',
description_short: productDescription.value?.description_short || '',
meta_title: productDescription.value?.meta_title || '',
meta_description: productDescription.value?.meta_description || '',
available_now: productDescription.value?.available_now || '',
available_later: productDescription.value?.available_later || '',
usage: productDescription.value?.usage || '',
})
}
)
await getProductDescription(langId, productID)
return data
} catch (e) {
console.error(e)
}
} }
return { return {
productDescription, productDescription,
currentProduct,
loading, loading,
error, error,
getProductDescription, copyProductDescription,
clearCurrentProduct,
saveProductDescription,
translateProductDescription, translateProductDescription,
getProductDescription,
saveProductDescription
} }
}) })

View File

@@ -0,0 +1,21 @@
import { useFetchJson } from '@/composable/useFetchJson'
import type { Resp } from '@/types'
import type { Settings } from '@/types/settings'
import { defineStore } from 'pinia'
import { computed, ref } from 'vue'
export const useSettingsStore = defineStore('settings', () => {
const settings = ref<Settings | null>(null)
const loaded = ref(false)
const shopDefaultLanguage = computed(() => settings.value?.app?.shop_default_language ?? 1)
async function getSettings(): Promise<Settings | null> {
if (loaded.value && settings.value) return settings.value
const resp = await useFetchJson<Settings>('/api/v1/settings')
settings.value = resp.items
loaded.value = true
return resp.items
}
return { settings, loaded, shopDefaultLanguage, getSettings }
})

View File

@@ -1,6 +1,7 @@
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
import { ref, computed } from 'vue' import { ref, computed } from 'vue'
import { useFetchJson } from '@/composable/useFetchJson' import { useFetchJson } from '@/composable/useFetchJson'
import { useUserStore } from '../user'
export interface User { export interface User {
id: string id: string
@@ -41,6 +42,8 @@ export const useAuthStore = defineStore('auth', () => {
_isAuthenticated.value = readIsAuthenticatedCookie() _isAuthenticated.value = readIsAuthenticatedCookie()
} }
// const auth = useAuthStore()
// const userStore = useUserStore()
async function login(email: string, password: string) { async function login(email: string, password: string) {
loading.value = true loading.value = true
error.value = null error.value = null
@@ -60,6 +63,7 @@ export const useAuthStore = defineStore('auth', () => {
user.value = response.user user.value = response.user
_syncAuthState() _syncAuthState()
// await userStore.getUser()
return true return true
} catch (e: any) { } catch (e: any) {

View File

@@ -0,0 +1,86 @@
import { useFetchJson } from '@/composable/useFetchJson'
import { defineStore } from 'pinia'
import { ref } from 'vue'
import { useRoute } from 'vue-router'
export interface Product {
id: number
image: string
name: string
productDetails?: string
product_id: number
is_favorite?: boolean
}
export interface ProductResponse {
items: Product[]
items_count: number
}
export interface ApiResponse {
message: string
items: Product[]
count: number
}
export const useCustomerProductStore = defineStore('customer-product', () => {
const loading = ref(true)
const error = ref<string | null>(null)
const route = useRoute()
const productsList = ref<Product[]>([])
const total = ref(0)
const perPage = ref(15)
async function fetchProductList() {
loading.value = true
error.value = null
const params = new URLSearchParams()
Object.entries(route.query).forEach(([key, value]) => {
if (value) params.append(key, String(value))
})
const url = `/api/v1/restricted/product/list?${params}`
try {
const response = await useFetchJson<ApiResponse>(url)
productsList.value = response.items || []
total.value = response.count || 0
} catch (e: unknown) {
error.value = e instanceof Error ? e.message : 'Failed to load products'
} finally {
loading.value = false
}
}
async function toggleFavorite(product: Product) {
const productId = product.product_id
const isFavorite = product.is_favorite
const url = `/api/v1/restricted/product/favorite/${productId}`
try {
if (!isFavorite) {
await useFetchJson(url, { method: 'POST' })
} else {
await useFetchJson(url, { method: 'DELETE' })
}
product.is_favorite = !isFavorite
} catch (e: unknown) {
error.value = e instanceof Error ? e.message : 'Failed to update favorite'
}
}
return {
fetchProductList,
toggleFavorite,
productsList,
total,
loading,
error,
perPage
}
})

View File

@@ -1,14 +0,0 @@
import { useFetchJson } from '@/composable/useFetchJson'
import type { Resp } from '@/types'
import type { Settings } from '@/types/settings'
import { defineStore } from 'pinia'
export const useSettingsStore = defineStore('settings', () => {
async function getSettings() {
const { items } = await useFetchJson<Resp<Settings>>('/api/v1/settings',)
console.log(items);
}
getSettings()
return {}
})

32
bo/src/stores/user.ts Normal file
View File

@@ -0,0 +1,32 @@
import type { User } from '@/types/user'
import { ref } from 'vue'
import { defineStore } from 'pinia'
import { useFetchJson } from '@/composable/useFetchJson'
export const useUserStore = defineStore('user', () => {
const error = ref<string | null>(null)
const user = ref<User | null>(null)
async function getUser() {
error.value = null
try {
const data = await useFetchJson<User>(`/api/v1/restricted/customer`)
console.log('getUser API response:', data)
const response: User = (data as any).items ?? data
console.log('User response:', response)
user.value = response
return response
} catch (err: any) {
error.value = err?.message ?? 'Unknown error'
return null
}
}
return {
error,
user,
getUser
}
})

View File

@@ -12,3 +12,20 @@ export interface Language {
active: boolean active: boolean
flag: string flag: string
} }
export interface Country {
id: number
name: string
flag: string
currency_id: string
ps_currency: {
id_currency: string
name: string
iso_code: string
numeric_iso_code: string
precision: number
conversion_rate: number
deleted: boolean
active: boolean
}
}

View File

@@ -5,7 +5,10 @@ export interface ProductDescription {
description_short: string description_short: string
meta_description: string meta_description: string
available_now: string available_now: string
delivery_in_stock?: string
usage: string usage: string
image_link: string
link_rewrite: string
} }
export interface Product { export interface Product {

View File

@@ -12,6 +12,7 @@ export interface App {
environment: string environment: string
base_url: string base_url: string
password_regex: string password_regex: string
shop_default_language: number
} }
export interface Server { export interface Server {

23
bo/src/types/user.d.ts vendored Normal file
View File

@@ -0,0 +1,23 @@
export interface User {
id: string
email: string
name: string
first_name?: string
last_name?: string
company_name?: string
company_email?: string
company_address?: Address
billing_address?: Address
regon?: string
nip?: string
vat?: string
}
interface Customer {
user_id: number
email: string
first_name: string
last_name: string
}

View File

@@ -20,7 +20,7 @@
<script setup lang="ts"> <script setup lang="ts">
// import { useRoute } from 'vue-router'; // import { useRoute } from 'vue-router';
import Default from '@/layouts/default.vue'; import Default from '@/layouts/default.vue';
import { useCategoryStore } from '@/stores/category'; import { useCategoryStore } from '@/stores/admin/category';
import { ref, watch } from 'vue'; import { ref, watch } from 'vue';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
// const route = useRoute() // const route = useRoute()

View File

@@ -1,7 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed, defineAsyncComponent, ref } from 'vue' import { computed, defineAsyncComponent, ref } from 'vue'
import { useRouter, useRoute } from 'vue-router' import { useRouter, useRoute } from 'vue-router'
import { useAuthStore } from '@/stores/auth' import { useAuthStore } from '@/stores/customer/auth'
import { useValidation } from '@/composable/useValidation' import { useValidation } from '@/composable/useValidation'
import type { FormError } from '@nuxt/ui' import type { FormError } from '@nuxt/ui'
import { i18n } from '@/plugins/02_i18n' import { i18n } from '@/plugins/02_i18n'
@@ -107,7 +107,7 @@ const PrivacyComponent = computed(() =>
<div class="flex items-center justify-between w-full dark:text-white text-black"> <div class="flex items-center justify-between w-full dark:text-white text-black">
<button variant="link" size="sm" @click="goToPasswordRecovery" <button variant="link" size="sm" @click="goToPasswordRecovery"
class="text-[15px] w-full flex justify-end text-(--accent-blue-light) dark:text-(--accent-blue-dark) cursor-pointer"> class="text-[15px] w-full flex justify-end text-(--text-sky-light) dark:text-(--text-sky-dark) cursor-pointer">
{{ $t('general.forgot_password') }}? {{ $t('general.forgot_password') }}?
</button> </button>
</div> </div>
@@ -145,18 +145,18 @@ const PrivacyComponent = computed(() =>
<p class="dark:text-white text-black"> <p class="dark:text-white text-black">
{{ $t('general.dont_have_an_account') }}? {{ $t('general.dont_have_an_account') }}?
<button variant="link" size="sm" <button variant="link" size="sm"
class="text-[15px] text-(--accent-blue-light) dark:text-(--accent-blue-dark) cursor-pointer" class="text-[15px] text-(--text-sky-light) dark:text-(--text-sky-dark) cursor-pointer"
@click="goToRegister">{{ $t('general.create_account_now') }}</button> @click="goToRegister">{{ $t('general.create_account_now') }}</button>
</p> </p>
</div> </div>
<p class="mt-8 text-center text-xs dark:text-white text-black"> <p class="mt-8 text-center text-xs dark:text-white text-black">
{{ $t('general.by_signing_in_you_agree_to_our') }} {{ $t('general.by_signing_in_you_agree_to_our') }}
<span @click="showTherms = !showTherms" <span @click="showTherms = !showTherms"
class="cursor-pointer underline text-(--accent-blue-light) dark:text-(--accent-blue-dark) cursor-pointer">{{ class="cursor-pointer underline text-(--text-sky-light) dark:text-(--text-sky-dark) cursor-pointer">{{
$t('general.terms_of_service') }}</span> $t('general.terms_of_service') }}</span>
{{ $t('general.and') }} {{ $t('general.and') }}
<span @click="showPrivacy = !showPrivacy" <span @click="showPrivacy = !showPrivacy"
class="cursor-pointer underline text-(--accent-blue-light) dark:text-(--accent-blue-dark) cursor-pointer">{{ class="cursor-pointer underline text-(--text-sky-light) dark:text-(--text-sky-dark) cursor-pointer">{{
$t('general.privacy_policy') }}</span> $t('general.privacy_policy') }}</span>
</p> </p>
</div> </div>

View File

@@ -1,7 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue' import { ref } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { useAuthStore } from '@/stores/auth' import { useAuthStore } from '@/stores/customer/auth'
import { useValidation } from '@/composable/useValidation' import { useValidation } from '@/composable/useValidation'
import type { FormError } from '@nuxt/ui' import type { FormError } from '@nuxt/ui'
import { i18n } from '@/plugins/02_i18n' import { i18n } from '@/plugins/02_i18n'
@@ -92,13 +92,13 @@ function validate(): FormError[] {
class="w-full flex items-center gap-2 justify-center text-[15px] dark:text-white text-black cursor-pointer" class="w-full flex items-center gap-2 justify-center text-[15px] dark:text-white text-black cursor-pointer"
@click="goToLogin"> @click="goToLogin">
<UIcon name="mingcute:arrow-left-line" <UIcon name="mingcute:arrow-left-line"
class="text-(--accent-blue-light) dark:text-(--accent-blue-dark) text-[16px]" /> class="text-(--text-sky-light) dark:text-(--text-sky-dark) text-[16px]" />
{{ $t('general.back_to_sign_in') }} {{ $t('general.back_to_sign_in') }}
</button> </button>
<p class="text-sm text-gray-600 dark:text-gray-400"> <p class="text-sm text-gray-600 dark:text-gray-400">
{{ $t('general.dont_have_an_account') }} {{ $t('general.dont_have_an_account') }}
<button variant="link" size="sm" @click="goToRegister" <button variant="link" size="sm" @click="goToRegister"
class=" text-[15px] text-(--accent-blue-light) dark:text-(--accent-blue-dark) cursor-pointer">{{ class=" text-[15px] text-(--text-sky-light) dark:text-(--text-sky-dark) cursor-pointer">{{
$t('general.create_account_now') }} $t('general.create_account_now') }}
</button> </button>
</p> </p>

View File

@@ -83,12 +83,12 @@
<span class="dark:text-white text-black"> <span class="dark:text-white text-black">
{{ $t('general.i_agree_to_the') }} {{ $t('general.i_agree_to_the') }}
<span @click="showTherms = !showTherms" <span @click="showTherms = !showTherms"
class="cursor-pointer underline text-(--accent-blue-light) dark:text-(--accent-blue-dark)">{{ class="cursor-pointer underline text-(--text-sky-light) dark:text-(--text-sky-dark)">{{
$t('general.terms_of_service') $t('general.terms_of_service')
}}</span> }}</span>
{{ $t('general.and') }} {{ $t('general.and') }}
<span @click="showPrivacy = !showPrivacy" <span @click="showPrivacy = !showPrivacy"
class="cursor-pointer underline text-(--accent-blue-light) dark:text-(--accent-blue-dark)">{{ class="cursor-pointer underline text-(--text-sky-light) dark:text-(--text-sky-dark)">{{
$t('general.privacy_policy') $t('general.privacy_policy')
}}</span> }}</span>
</span> </span>
@@ -117,7 +117,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, computed, defineAsyncComponent } from 'vue' import { ref, computed, defineAsyncComponent } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { useAuthStore } from '@/stores/auth' import { useAuthStore } from '@/stores/customer/auth'
import { useValidation } from '@/composable/useValidation' import { useValidation } from '@/composable/useValidation'
import type { FormError } from '@nuxt/ui' import type { FormError } from '@nuxt/ui'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'

View File

@@ -11,7 +11,7 @@ import {
LinearScale, LinearScale,
} from 'chart.js' } from 'chart.js'
import { getYears, getQuarters, getIssues, type QuarterData, type IssueTimeSummary } from '@/composable/useRepoApi' import { getYears, getQuarters, getIssues, type QuarterData, type IssueTimeSummary } from '@/composable/useRepoApi'
import { useAuthStore } from '@/stores/auth' import { useAuthStore } from '@/stores/customer/auth'
import { i18n } from '@/plugins/02_i18n' import { i18n } from '@/plugins/02_i18n'
import type { TableColumn } from '@nuxt/ui' import type { TableColumn } from '@nuxt/ui'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
@@ -183,7 +183,7 @@ const columns = computed<TableColumn<IssueTimeSummary>[]>(() => [
<template> <template>
<component :is="Default || 'div'"> <component :is="Default || 'div'">
<div class="container mx-auto"> <div class="">
<div class="p-6 bg-(--main-light) dark:bg-(--black) font-sans"> <div class="p-6 bg-(--main-light) dark:bg-(--black) font-sans">
<h1 class="text-2xl font-bold mb-6 text-black dark:text-white">{{ $t('repo_chart.repository_work_chart') }} <h1 class="text-2xl font-bold mb-6 text-black dark:text-white">{{ $t('repo_chart.repository_work_chart') }}
</h1> </h1>

View File

@@ -1,7 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted } from 'vue' import { ref, onMounted } from 'vue'
import { useRouter, useRoute } from 'vue-router' import { useRouter, useRoute } from 'vue-router'
import { useAuthStore } from '@/stores/auth' import { useAuthStore } from '@/stores/customer/auth'
import { useValidation } from '@/composable/useValidation' import { useValidation } from '@/composable/useValidation'
import type { FormError } from '@nuxt/ui' import type { FormError } from '@nuxt/ui'
import { i18n } from '@/plugins/02_i18n' import { i18n } from '@/plugins/02_i18n'
@@ -120,7 +120,7 @@ function validate(): FormError[] {
<div class="text-center border-t dark:border-(--border-dark) border-(--border-light) pt-4"> <div class="text-center border-t dark:border-(--border-dark) border-(--border-light) pt-4">
<button color="neutral" variant="ghost" @click="goToLogin" <button color="neutral" variant="ghost" @click="goToLogin"
class="text-[15px] flex items-center gap-2 text-(--accent-blue-light) dark:text-(--accent-blue-dark) cursor-pointer"> class="text-[15px] flex items-center gap-2 text-(--text-sky-light) dark:text-(--text-sky-dark) cursor-pointer">
<UIcon name="mingcute:arrow-left-line" /> <UIcon name="mingcute:arrow-left-line" />
{{ $t('general.back_to_sign_in') }} {{ $t('general.back_to_sign_in') }}
</button> </button>

View File

@@ -0,0 +1,12 @@
<template>
<Default>
<div class="h-full">
<StorageFileBrowser initial-path="dest/src" />
</div>
</Default>
</template>
<script setup lang="ts">
import Default from '@/layouts/default.vue'
import StorageFileBrowser from '@/components/customer/StorageFileBrowser.vue'
</script>

View File

@@ -135,7 +135,7 @@ function goToLogin() {
<p class="text-sm text-gray-600 dark:text-gray-400"> <p class="text-sm text-gray-600 dark:text-gray-400">
{{ $t('verify_email.already_registered') }} {{ $t('verify_email.already_registered') }}
<button variant="link" size="sm" @click="goToLogin" <button variant="link" size="sm" @click="goToLogin"
class="cursor-pointer text-(--accent-blue-light) dark:text-(--accent-blue-dark)"> {{ class="cursor-pointer text-(--text-sky-light) dark:text-(--text-sky-dark)"> {{
$t('general.sign_in') $t('general.sign_in')
}} }}
</button> </button>

View File

@@ -1,7 +1,7 @@
info: info:
name: Change Locales name: Change Locales
type: http type: http
seq: 3 seq: 5
http: http:
method: POST method: POST

View File

@@ -1,7 +1,7 @@
info: info:
name: Delete Index - MeiliSearch name: Delete Index - MeiliSearch
type: http type: http
seq: 5 seq: 7
http: http:
method: DELETE method: DELETE

View File

@@ -1,7 +1,7 @@
info: info:
name: Search Index Settings name: Search Index Settings
type: http type: http
seq: 4 seq: 6
http: http:
method: POST method: POST

View File

@@ -1,7 +1,7 @@
info: info:
name: Search Items name: Search Items
type: http type: http
seq: 2 seq: 4
http: http:
method: POST method: POST

View File

@@ -1,7 +1,7 @@
info: info:
name: auth name: auth
type: folder type: folder
seq: 6 seq: 2
request: request:
auth: inherit auth: inherit

View File

@@ -11,7 +11,7 @@ http:
data: |- data: |-
{ {
"b2b_id_currency" : 1, "b2b_id_currency" : 1,
"conversion_rate": 4.2 "conversion_rate": 3
} }
auth: inherit auth: inherit

View File

@@ -11,7 +11,7 @@ http:
runtime: runtime:
variables: variables:
- name: id - name: id
value: "1" value: "2"
settings: settings:
encodeUrl: true encodeUrl: true

View File

@@ -1,7 +1,7 @@
info: info:
name: currency name: currency
type: folder type: folder
seq: 8 seq: 9
request: request:
auth: inherit auth: inherit

View File

@@ -5,10 +5,10 @@ info:
http: http:
method: GET method: GET
url: "{{bas_url}}/restricted/customer?id=1" url: "{{bas_url}}/restricted/customer?id=2"
params: params:
- name: id - name: id
value: "1" value: "2"
type: query type: query
auth: inherit auth: inherit

View File

@@ -5,10 +5,10 @@ info:
http: http:
method: GET method: GET
url: "{{bas_url}}/restricted/customer/list?search=" url: "{{bas_url}}/restricted/customer/list?search=marek"
params: params:
- name: search - name: search
value: "" value: marek
type: query type: query
auth: inherit auth: inherit

View File

@@ -1,7 +1,7 @@
info: info:
name: customer name: customer
type: folder type: folder
seq: 9 seq: 10
request: request:
auth: inherit auth: inherit

View File

@@ -5,7 +5,7 @@ info:
http: http:
method: GET method: GET
url: "{{bas_url}}/restricted/product/200/1/5" url: "{{bas_url}}/restricted/product/51/1/7"
auth: inherit auth: inherit
settings: settings:

View File

@@ -0,0 +1,22 @@
info:
name: Product Variants List
type: http
seq: 3
http:
method: GET
url: "{{bas_url}}/restricted/product/list-variants/{{product_id}}"
body:
type: json
data: ""
runtime:
variables:
- name: product_id
value: "51"
settings:
encodeUrl: true
timeout: 0
followRedirects: true
maxRedirects: 5

View File

@@ -5,7 +5,7 @@ info:
http: http:
method: GET method: GET
url: "{{bas_url}}/restricted/product/list?p=1&elems=30&sort=product_id,asc&category_id_in=243&reference=~62" url: "{{bas_url}}/restricted/product/list?p=1&elems=30&sort=product_id,asc&reference=~NC100"
params: params:
- name: p - name: p
value: "1" value: "1"
@@ -19,8 +19,9 @@ http:
- name: category_id_in - name: category_id_in
value: "243" value: "243"
type: query type: query
disabled: true
- name: reference - name: reference
value: ~62 value: ~NC100
type: query type: query
body: body:
type: json type: json

View File

@@ -1,7 +1,7 @@
info: info:
name: product name: product
type: folder type: folder
seq: 7 seq: 8
request: request:
auth: inherit auth: inherit

View File

@@ -0,0 +1,20 @@
info:
name: Activate
type: http
seq: 5
http:
method: PATCH
url: "{{bas_url}}/restricted/specific-price/{{id}}/activate"
auth: inherit
runtime:
variables:
- name: id
value: "1"
settings:
encodeUrl: true
timeout: 0
followRedirects: true
maxRedirects: 5

View File

@@ -0,0 +1,27 @@
info:
name: Create
type: http
seq: 3
http:
method: POST
url: "{{bas_url}}/restricted/specific-price"
body:
type: json
data: |-
{
"name": "Summer Sale 3",
"scope": "shop",
"reduction_type": "amount",
"price": 69,
"from_quantity": 1,
"is_active": true,
"currency_id": 2
}
auth: inherit
settings:
encodeUrl: true
timeout: 0
followRedirects: true
maxRedirects: 5

View File

@@ -0,0 +1,20 @@
info:
name: Deactivate
type: http
seq: 6
http:
method: PATCH
url: "{{bas_url}}/restricted/specific-price/{{id}}/deactivate"
auth: inherit
runtime:
variables:
- name: id
value: "1"
settings:
encodeUrl: true
timeout: 0
followRedirects: true
maxRedirects: 5

View File

@@ -0,0 +1,20 @@
info:
name: Delete
type: http
seq: 7
http:
method: DELETE
url: "{{bas_url}}/restricted/price-reductions/{{id}}"
auth: inherit
runtime:
variables:
- name: id
value: "1"
settings:
encodeUrl: true
timeout: 0
followRedirects: true
maxRedirects: 5

View File

@@ -0,0 +1,20 @@
info:
name: Get
type: http
seq: 2
http:
method: GET
url: "{{bas_url}}/restricted/specific-price/{{id}}"
auth: inherit
runtime:
variables:
- name: id
value: "1"
settings:
encodeUrl: true
timeout: 0
followRedirects: true
maxRedirects: 5

View File

@@ -0,0 +1,15 @@
info:
name: List
type: http
seq: 1
http:
method: GET
url: "{{bas_url}}/restricted/specific-price"
auth: inherit
settings:
encodeUrl: true
timeout: 0
followRedirects: true
maxRedirects: 5

View File

@@ -0,0 +1,38 @@
info:
name: Update
type: http
seq: 4
http:
method: PUT
url: "{{bas_url}}/restricted/specific-price/{{id}}"
body:
type: json
data: |-
{
"name": "Summer Sale Updated",
"reduction_type": "amount",
"percentage_reduction": 50.0,
"price": 69,
"currency_id": 1,
"scope": "shop",
"is_active": true,
"from_quantity":1
// "product_ids": [51,53],
// "category_ids": [1],
// "product_attribute_ids": [1114],
// "country_ids": [1],
// "customer_ids": [2,1]
}
auth: inherit
runtime:
variables:
- name: id
value: "3"
settings:
encodeUrl: true
timeout: 0
followRedirects: true
maxRedirects: 5

Some files were not shown because too many files have changed in this diff Show More