diff --git a/UIs/Customers/.editorconfig b/UIs/Customers/.editorconfig
new file mode 100644
index 0000000..a3ad0dc
--- /dev/null
+++ b/UIs/Customers/.editorconfig
@@ -0,0 +1,9 @@
+root = true
+
+[*]
+indent_style = space
+indent_size = 2
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = false
\ No newline at end of file
diff --git a/UIs/Customers/.env.example b/UIs/Customers/.env.example
new file mode 100644
index 0000000..3999104
--- /dev/null
+++ b/UIs/Customers/.env.example
@@ -0,0 +1,2 @@
+VITE_AUTH_API
+VITE_GATEWAY_API
\ No newline at end of file
diff --git a/UIs/Customers/.gitignore b/UIs/Customers/.gitignore
new file mode 100644
index 0000000..cd568f3
--- /dev/null
+++ b/UIs/Customers/.gitignore
@@ -0,0 +1,29 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+dist
+dist-ssr
+*.local
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+.DS_Store
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
+
+.env
+
+coverage/
+scripts/
\ No newline at end of file
diff --git a/UIs/Customers/.prettierrc b/UIs/Customers/.prettierrc
new file mode 100644
index 0000000..098ec71
--- /dev/null
+++ b/UIs/Customers/.prettierrc
@@ -0,0 +1,6 @@
+{
+ "trailingComma": "none",
+ "semi": true,
+ "singleQuote": true,
+ "bracketSpacing": true
+}
\ No newline at end of file
diff --git a/UIs/Customers/.vscode/extensions.json b/UIs/Customers/.vscode/extensions.json
new file mode 100644
index 0000000..74baffc
--- /dev/null
+++ b/UIs/Customers/.vscode/extensions.json
@@ -0,0 +1,3 @@
+{
+ "recommendations": ["denoland.vscode-deno"]
+}
diff --git a/UIs/Customers/README.md b/UIs/Customers/README.md
new file mode 100644
index 0000000..37fa08e
--- /dev/null
+++ b/UIs/Customers/README.md
@@ -0,0 +1,495 @@
+# Nome da aplicação UI
+
+**Propósito da aplicação**
+
+O projeto segue princípios de **Feature-Sliced Design** e **Clean Architecture**, com forte tipagem e isolamento entre UI, domínio e infraestrutura.
+
+---
+
+# 📌 Visão Geral da Arquitetura
+
+A aplicação separa responsabilidades em camadas bem definidas.
+
+```
+React Components
+ ↓
+Feature Hooks
+ ↓
+Feature Services
+ ↓
+Infra Layer (API / SSE / WebSocket)
+ ↓
+Backend
+```
+
+### Responsabilidades
+
+| Camada | Responsabilidade |
+| ---------- | ----------------------------------- |
+| Components | UI e interação com o utilizador |
+| Hooks | Orquestração de dados e lógica |
+| Services | Comunicação com backend |
+| Mappers | Conversão DTO ⇄ Model |
+| Infra | Clientes HTTP e comunicação externa |
+
+Essa separação garante:
+
+* isolamento de regras de negócio
+* proteção da UI contra mudanças da API
+* código previsível e escalável
+
+---
+
+# 🚀 Stack Tecnológica
+
+### Core
+
+* React 19
+* React Router 7
+* Vite (SWC)
+
+### Estado
+
+| Tipo de estado | Tecnologia |
+| --------------- | -------------- |
+| Server State | TanStack Query |
+| Global UI State | Zustand |
+| Local State | React Hooks |
+
+### UI
+
+* Tailwind CSS
+* Radix UI
+* Lucide Icons
+
+### Formulários
+
+* React Hook Form
+* Zod
+
+### Tempo real
+
+* SignalR
+* Server Sent Events (SSE)
+
+### Testes
+
+* Vitest
+* React Testing Library
+
+---
+
+# 📂 Estrutura do Projeto
+
+```
+src
+├── app
+│ ├── layouts
+│ ├── routers
+│ └── theme
+│
+├── assets
+│ ├── images
+│ ├── icons
+│ └── styles
+│
+├── features
+│ ├── operators
+│ ├── partners
+│ └── ...
+│
+├── shared
+│ ├── components
+│ │ ├── ui
+│ │ └── common
+│ ├── hooks
+│ ├── services
+│ └── utils
+│
+├── infra
+│ ├── api
+│ └── sse
+```
+
+### Descrição
+
+| Diretório | Função |
+| ------------------------ | ---------------------------------------------- |
+| app/layouts | estruturas de página |
+| app/routers | rotas e guards |
+| app/theme | configuração de tema |
+| assets | recursos estáticos (imagens, ícones e estilos) |
+| features | módulos de domínio da aplicação |
+| shared/components/ui | componentes visuais primitivos |
+| shared/components/common | componentes reutilizáveis com lógica de UI |
+| shared/hooks | hooks reutilizáveis |
+| shared/services | serviços compartilhados |
+| shared/utils | utilitários globais |
+| infra/api | clientes HTTP |
+| infra/sse | comunicação em tempo real |
+
+---
+
+# 🧩 Estrutura de uma Feature
+
+Cada feature encapsula completamente o seu domínio.
+
+Exemplo:
+
+```
+features/operators
+│
+├── components
+│
+├── hooks
+│ ├── use-operators.ts
+│ └── use-create-operator.ts
+│
+├── services
+│ ├── index.ts
+│ ├── mapper.ts
+│ └── error-handler.ts
+│
+├── types
+│ ├── operator.dto.ts
+│ └── operator.model.ts
+```
+
+### Responsabilidades
+
+| Arquivo | Função |
+| ---------------------- | -------------------------- |
+| services/index | chamadas HTTP |
+| services/mapper | conversão DTO ⇄ Model |
+| services/error-handler | tratamento de erros |
+| hooks | integração com React Query |
+| components | UI da feature |
+
+---
+
+# 🔄 DTO vs Model
+
+A UI **nunca consome diretamente os DTOs da API**.
+
+### DTO
+
+Representa o formato da API.
+
+```
+OperatorDTO
+```
+
+### Model
+
+Representa o modelo usado pela UI.
+
+```
+Operator
+```
+
+### Conversão
+
+```
+DTO → mapper → Model
+Model → mapper → DTO
+```
+
+Isso protege a aplicação contra mudanças no backend.
+
+---
+
+# 🔁 Fluxo de Dados
+
+## Leitura (Queries)
+
+```
+API
+ ↓
+Service
+ ↓
+Mapper
+ ↓
+React Query
+ ↓
+Hook
+ ↓
+Component
+```
+
+## Escrita (Mutations)
+
+```
+Component
+ ↓
+Hook
+ ↓
+Service
+ ↓
+Mapper
+ ↓
+API
+```
+
+---
+
+# 🧠 Gestão de Estado
+
+A aplicação segue regras claras:
+
+### 1️⃣ Server State
+
+TanStack Query
+
+Usado para:
+
+* listas
+* detalhes
+* dados vindos da API
+
+### 2️⃣ Estado Global
+
+Zustand
+
+Usado para:
+
+* token de autenticação
+* tema
+* estado do menu
+
+### 3️⃣ Estado Local
+
+React Hooks
+
+Usado para:
+
+* UI
+* dropdowns
+* inputs
+* interações locais
+
+---
+
+# 📡 Comunicação em Tempo Real
+
+Algumas áreas da aplicação utilizam atualizações passivas.
+
+Tecnologias usadas:
+
+* SignalR
+* Server Sent Events (SSE)
+
+Esses clientes atualizam diretamente o **cache do React Query**, evitando polling.
+
+---
+
+# 🧩 Componentes
+
+Os componentes são divididos em três níveis.
+
+## UI Components
+
+```
+shared/components/ui
+```
+
+Componentes visuais baseados em Radix.
+
+Regras:
+
+* sem lógica de negócio
+* apenas props e eventos
+
+---
+
+## Shared Components
+
+```
+shared/components/common
+```
+
+Componentes reutilizáveis com lógica interna de UI.
+
+Exemplos:
+
+* Datatable
+* File Picker
+* Wizard
+
+---
+
+## Feature Components
+
+```
+features/.../components
+```
+
+Componentes específicos do domínio.
+
+Regras:
+
+* não acessam API diretamente
+* recebem dados via hooks
+
+---
+
+# 🔐 Roteamento e Guards
+
+O projeto possui dois tipos de proteção.
+
+### Auth Guard
+
+Protege rotas privadas.
+
+Se o utilizador não estiver autenticado:
+
+```
+redirect → /login
+```
+
+---
+
+### Guest Guard
+
+Impede utilizadores autenticados de acessar páginas públicas.
+
+```
+/login → redirect → /dashboard
+```
+
+---
+
+# 🚨 Tratamento de Erros
+
+Erros são tratados em três níveis.
+
+### Validação
+
+Zod
+
+### Domínio
+
+`error-handler.ts` da feature.
+
+### Infraestrutura
+
+Interceptores HTTP.
+
+O feedback ao utilizador é exibido via **toasts**.
+
+---
+
+# 🧪 Testes
+
+Ferramentas usadas:
+
+* Vitest
+* React Testing Library
+
+Testamos principalmente:
+
+* utilitários
+* hooks
+* interações de UI
+
+---
+
+# 🚀 Começando
+
+### Pré-requisitos
+
+* Node 20+
+* pnpm
+
+### Instalação
+
+```
+pnpm install
+```
+
+### Configuração
+
+```
+cp .env .env.local
+```
+
+Preencher:
+
+```
+VITE_API_GATEWAY_URL
+VITE_AUTH_API_URL
+```
+
+---
+
+# ▶ Executar o projeto
+
+```
+pnpm dev
+```
+
+Servidor de desenvolvimento:
+
+```
+http://localhost:5173
+```
+
+---
+
+# 🛠 Scripts
+
+| Script | Função |
+| ------------------ | --------------------------- |
+| pnpm dev | ambiente de desenvolvimento |
+| pnpm build | build de produção |
+| pnpm preview | preview da build |
+| pnpm lint | lint do projeto |
+| pnpm test | executar testes |
+| pnpm test:coverage | relatório de coverage |
+
+---
+
+# 📏 Convenções do Projeto
+
+### Arquivos
+
+```
+kebab-case
+```
+
+### Componentes
+
+```
+PascalCase
+```
+
+### Sufixos obrigatórios
+
+```
+*.dto.ts
+*.model.ts
+*.test.ts
+*.mapper.ts
+```
+
+---
+
+# 📚 Regras Arquiteturais
+
+1️⃣ UI nunca consome DTO diretamente
+2️⃣ chamadas HTTP apenas em services
+3️⃣ hooks são responsáveis por React Query
+4️⃣ componentes não fazem side-effects
+5️⃣ lógica de domínio nunca fica em componentes
+
+---
+
+# 🤝 Contribuindo
+
+Ao criar uma nova feature:
+
+1. criar pasta em `features/`
+2. definir DTOs e Models
+3. criar `services`
+4. criar `hooks`
+5. criar `components`
+6. registrar rotas
diff --git a/UIs/Customers/components.json b/UIs/Customers/components.json
new file mode 100644
index 0000000..4cc9fbd
--- /dev/null
+++ b/UIs/Customers/components.json
@@ -0,0 +1,21 @@
+{
+ "$schema": "https://ui.shadcn.com/schema.json",
+ "style": "new-york",
+ "rsc": false,
+ "tsx": true,
+ "tailwind": {
+ "config": "",
+ "css": "src/index.css",
+ "baseColor": "zinc",
+ "cssVariables": true,
+ "prefix": ""
+ },
+ "iconLibrary": "lucide",
+ "aliases": {
+ "components": "@/shared/components",
+ "utils": "@/shared/utils",
+ "ui": "@/shared/components/ui",
+ "hooks": "@/shared/hooks"
+ },
+ "registries": {}
+}
diff --git a/UIs/Customers/eslint.config.js b/UIs/Customers/eslint.config.js
new file mode 100644
index 0000000..f3664f8
--- /dev/null
+++ b/UIs/Customers/eslint.config.js
@@ -0,0 +1,37 @@
+import js from '@eslint/js'
+import globals from 'globals'
+import reactHooks from 'eslint-plugin-react-hooks'
+import reactRefresh from 'eslint-plugin-react-refresh'
+import tseslint from 'typescript-eslint'
+import eslintPluginPrettier from 'eslint-plugin-prettier/recommended'
+import { defineConfig, globalIgnores } from 'eslint/config'
+import eslintConfigPrettier from 'eslint-config-prettier/prettier'
+
+export default defineConfig([
+ globalIgnores(['dist']),
+ {
+ files: ['**/*.{ts,tsx}'],
+ extends: [
+ js.configs.recommended,
+ tseslint.configs.recommended,
+ reactHooks.configs['recommended-latest'],
+ reactRefresh.configs.vite,
+ eslintPluginPrettier
+ ],
+ languageOptions: {
+ ecmaVersion: 2020,
+ globals: globals.browser,
+ },
+ rules:{
+ "react-refresh/only-export-components":"off",
+ "@typescript-eslint/no-explicit-any": "off",
+ "prettier/prettier": [
+ 'error',
+ {
+ 'endOfLine': 'auto',
+ }
+ ]
+ }
+ },
+ eslintConfigPrettier
+])
diff --git a/UIs/Customers/index.html b/UIs/Customers/index.html
new file mode 100644
index 0000000..8f5e756
--- /dev/null
+++ b/UIs/Customers/index.html
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+ Administração | SmartTechInnovation
+
+
+
+
+
+
+
+
+
+
diff --git a/UIs/Customers/package.json b/UIs/Customers/package.json
new file mode 100644
index 0000000..cb43141
--- /dev/null
+++ b/UIs/Customers/package.json
@@ -0,0 +1,98 @@
+{
+ "name": "react-apps-template",
+ "private": true,
+ "version": "0.0.0",
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "tsc -b && vite build",
+ "test": "vitest --coverage",
+ "lint": "eslint .",
+ "preview": "vite preview",
+ "prepare": "husky"
+ },
+ "lint-staged": {
+ "src/**/*": [
+ "pnpm lint --fix",
+ "pnpm vitest related --run --passWithNoTests"
+ ]
+ },
+ "dependencies": {
+ "@dnd-kit/core": "^6.3.1",
+ "@dnd-kit/modifiers": "^9.0.0",
+ "@dnd-kit/sortable": "^10.0.0",
+ "@dnd-kit/utilities": "^3.2.2",
+ "@hookform/resolvers": "^5.2.2",
+ "@microsoft/signalr": "^10.0.0",
+ "@radix-ui/react-avatar": "^1.1.10",
+ "@radix-ui/react-checkbox": "^1.3.3",
+ "@radix-ui/react-dialog": "^1.1.15",
+ "@radix-ui/react-dropdown-menu": "^2.1.16",
+ "@radix-ui/react-label": "^2.1.7",
+ "@radix-ui/react-popover": "^1.1.15",
+ "@radix-ui/react-progress": "^1.1.7",
+ "@radix-ui/react-radio-group": "^1.3.8",
+ "@radix-ui/react-scroll-area": "^1.2.10",
+ "@radix-ui/react-select": "^2.2.6",
+ "@radix-ui/react-separator": "^1.1.7",
+ "@radix-ui/react-slider": "^1.3.6",
+ "@radix-ui/react-slot": "^1.2.3",
+ "@radix-ui/react-switch": "^1.2.6",
+ "@radix-ui/react-tabs": "^1.1.13",
+ "@radix-ui/react-tooltip": "^1.2.8",
+ "@tailwindcss/vite": "^4.1.14",
+ "@tanstack/react-query": "^5.90.6",
+ "@tanstack/react-query-devtools": "^5.90.2",
+ "@tanstack/react-table": "^8.21.3",
+ "axios": "^1.13.2",
+ "class-variance-authority": "^0.7.1",
+ "clsx": "^2.1.1",
+ "cmdk": "^1.1.1",
+ "date-fns": "^4.1.0",
+ "embla-carousel-react": "^8.6.0",
+ "jwt-decode": "^4.0.0",
+ "lucide-react": "^0.546.0",
+ "next-themes": "^0.4.6",
+ "radix-ui": "^1.4.3",
+ "react": "^19.1.1",
+ "react-day-picker": "^9.11.1",
+ "react-dom": "^19.1.1",
+ "react-easy-crop": "^5.5.6",
+ "react-hook-form": "^7.65.0",
+ "react-router": "^7.9.4",
+ "recharts": "2.15.4",
+ "sonner": "^2.0.7",
+ "tailwind-merge": "^3.3.1",
+ "tailwindcss": "^4.1.14",
+ "vaul": "^1.1.2",
+ "zod": "^4.1.12",
+ "zustand": "^5.0.11"
+ },
+ "devDependencies": {
+ "@eslint/js": "^9.36.0",
+ "@testing-library/jest-dom": "^6.9.1",
+ "@testing-library/react": "^16.3.0",
+ "@testing-library/user-event": "^14.6.1",
+ "@types/node": "^24.6.0",
+ "@types/react": "^19.1.16",
+ "@types/react-dom": "^19.1.9",
+ "@vitejs/plugin-react-swc": "^4.1.0",
+ "@vitest/coverage-v8": "4.0.6",
+ "eslint": "^9.36.0",
+ "eslint-config-prettier": "^10.1.8",
+ "eslint-plugin-prettier": "^5.5.4",
+ "eslint-plugin-react-hooks": "^5.2.0",
+ "eslint-plugin-react-refresh": "^0.4.22",
+ "globals": "^16.4.0",
+ "husky": "^9.1.7",
+ "jsdom": "^27.2.0",
+ "lint-staged": "^16.2.6",
+ "prettier": "3.6.2",
+ "tw-animate-css": "^1.4.0",
+ "typescript": "~5.9.3",
+ "typescript-eslint": "^8.45.0",
+ "vite": "^7.1.7",
+ "vitest": "^4.0.6",
+ "vitest-localstorage-mock": "^0.1.2"
+ }
+}
diff --git a/UIs/Customers/pnpm-lock.yaml b/UIs/Customers/pnpm-lock.yaml
new file mode 100644
index 0000000..0d195b5
--- /dev/null
+++ b/UIs/Customers/pnpm-lock.yaml
@@ -0,0 +1,6288 @@
+lockfileVersion: '9.0'
+
+settings:
+ autoInstallPeers: true
+ excludeLinksFromLockfile: false
+
+importers:
+
+ .:
+ dependencies:
+ '@dnd-kit/core':
+ specifier: ^6.3.1
+ version: 6.3.1(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@dnd-kit/modifiers':
+ specifier: ^9.0.0
+ version: 9.0.0(@dnd-kit/core@6.3.1(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react@19.2.0)
+ '@dnd-kit/sortable':
+ specifier: ^10.0.0
+ version: 10.0.0(@dnd-kit/core@6.3.1(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react@19.2.0)
+ '@dnd-kit/utilities':
+ specifier: ^3.2.2
+ version: 3.2.2(react@19.2.0)
+ '@hookform/resolvers':
+ specifier: ^5.2.2
+ version: 5.2.2(react-hook-form@7.65.0(react@19.2.0))
+ '@microsoft/signalr':
+ specifier: ^10.0.0
+ version: 10.0.0
+ '@radix-ui/react-avatar':
+ specifier: ^1.1.10
+ version: 1.1.10(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-checkbox':
+ specifier: ^1.3.3
+ version: 1.3.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-dialog':
+ specifier: ^1.1.15
+ version: 1.1.15(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-dropdown-menu':
+ specifier: ^2.1.16
+ version: 2.1.16(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-label':
+ specifier: ^2.1.7
+ version: 2.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-popover':
+ specifier: ^1.1.15
+ version: 1.1.15(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-progress':
+ specifier: ^1.1.7
+ version: 1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-radio-group':
+ specifier: ^1.3.8
+ version: 1.3.8(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-scroll-area':
+ specifier: ^1.2.10
+ version: 1.2.10(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-select':
+ specifier: ^2.2.6
+ version: 2.2.6(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-separator':
+ specifier: ^1.1.7
+ version: 1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-slider':
+ specifier: ^1.3.6
+ version: 1.3.6(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-slot':
+ specifier: ^1.2.3
+ version: 1.2.3(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-switch':
+ specifier: ^1.2.6
+ version: 1.2.6(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-tabs':
+ specifier: ^1.1.13
+ version: 1.1.13(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-tooltip':
+ specifier: ^1.2.8
+ version: 1.2.8(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@tailwindcss/vite':
+ specifier: ^4.1.14
+ version: 4.1.14(vite@7.1.10(@types/node@24.8.1)(jiti@2.6.1)(lightningcss@1.30.1)(yaml@2.8.1))
+ '@tanstack/react-query':
+ specifier: ^5.90.6
+ version: 5.90.6(react@19.2.0)
+ '@tanstack/react-query-devtools':
+ specifier: ^5.90.2
+ version: 5.90.2(@tanstack/react-query@5.90.6(react@19.2.0))(react@19.2.0)
+ '@tanstack/react-table':
+ specifier: ^8.21.3
+ version: 8.21.3(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ axios:
+ specifier: ^1.13.2
+ version: 1.13.2
+ class-variance-authority:
+ specifier: ^0.7.1
+ version: 0.7.1
+ clsx:
+ specifier: ^2.1.1
+ version: 2.1.1
+ cmdk:
+ specifier: ^1.1.1
+ version: 1.1.1(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ date-fns:
+ specifier: ^4.1.0
+ version: 4.1.0
+ embla-carousel-react:
+ specifier: ^8.6.0
+ version: 8.6.0(react@19.2.0)
+ jwt-decode:
+ specifier: ^4.0.0
+ version: 4.0.0
+ lucide-react:
+ specifier: ^0.546.0
+ version: 0.546.0(react@19.2.0)
+ next-themes:
+ specifier: ^0.4.6
+ version: 0.4.6(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ radix-ui:
+ specifier: ^1.4.3
+ version: 1.4.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ react:
+ specifier: ^19.1.1
+ version: 19.2.0
+ react-day-picker:
+ specifier: ^9.11.1
+ version: 9.11.1(react@19.2.0)
+ react-dom:
+ specifier: ^19.1.1
+ version: 19.2.0(react@19.2.0)
+ react-easy-crop:
+ specifier: ^5.5.6
+ version: 5.5.6(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ react-hook-form:
+ specifier: ^7.65.0
+ version: 7.65.0(react@19.2.0)
+ react-router:
+ specifier: ^7.9.4
+ version: 7.9.4(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ recharts:
+ specifier: 2.15.4
+ version: 2.15.4(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ sonner:
+ specifier: ^2.0.7
+ version: 2.0.7(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ tailwind-merge:
+ specifier: ^3.3.1
+ version: 3.3.1
+ tailwindcss:
+ specifier: ^4.1.14
+ version: 4.1.14
+ vaul:
+ specifier: ^1.1.2
+ version: 1.1.2(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ zod:
+ specifier: ^4.1.12
+ version: 4.1.12
+ zustand:
+ specifier: ^5.0.11
+ version: 5.0.11(@types/react@19.2.2)(react@19.2.0)(use-sync-external-store@1.6.0(react@19.2.0))
+ devDependencies:
+ '@eslint/js':
+ specifier: ^9.36.0
+ version: 9.37.0
+ '@testing-library/jest-dom':
+ specifier: ^6.9.1
+ version: 6.9.1
+ '@testing-library/react':
+ specifier: ^16.3.0
+ version: 16.3.0(@testing-library/dom@10.4.1)(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@testing-library/user-event':
+ specifier: ^14.6.1
+ version: 14.6.1(@testing-library/dom@10.4.1)
+ '@types/node':
+ specifier: ^24.6.0
+ version: 24.8.1
+ '@types/react':
+ specifier: ^19.1.16
+ version: 19.2.2
+ '@types/react-dom':
+ specifier: ^19.1.9
+ version: 19.2.2(@types/react@19.2.2)
+ '@vitejs/plugin-react-swc':
+ specifier: ^4.1.0
+ version: 4.1.0(vite@7.1.10(@types/node@24.8.1)(jiti@2.6.1)(lightningcss@1.30.1)(yaml@2.8.1))
+ '@vitest/coverage-v8':
+ specifier: 4.0.6
+ version: 4.0.6(vitest@4.0.6(@types/node@24.8.1)(jiti@2.6.1)(jsdom@27.2.0)(lightningcss@1.30.1)(yaml@2.8.1))
+ eslint:
+ specifier: ^9.36.0
+ version: 9.37.0(jiti@2.6.1)
+ eslint-config-prettier:
+ specifier: ^10.1.8
+ version: 10.1.8(eslint@9.37.0(jiti@2.6.1))
+ eslint-plugin-prettier:
+ specifier: ^5.5.4
+ version: 5.5.4(eslint-config-prettier@10.1.8(eslint@9.37.0(jiti@2.6.1)))(eslint@9.37.0(jiti@2.6.1))(prettier@3.6.2)
+ eslint-plugin-react-hooks:
+ specifier: ^5.2.0
+ version: 5.2.0(eslint@9.37.0(jiti@2.6.1))
+ eslint-plugin-react-refresh:
+ specifier: ^0.4.22
+ version: 0.4.24(eslint@9.37.0(jiti@2.6.1))
+ globals:
+ specifier: ^16.4.0
+ version: 16.4.0
+ husky:
+ specifier: ^9.1.7
+ version: 9.1.7
+ jsdom:
+ specifier: ^27.2.0
+ version: 27.2.0
+ lint-staged:
+ specifier: ^16.2.6
+ version: 16.2.6
+ prettier:
+ specifier: 3.6.2
+ version: 3.6.2
+ tw-animate-css:
+ specifier: ^1.4.0
+ version: 1.4.0
+ typescript:
+ specifier: ~5.9.3
+ version: 5.9.3
+ typescript-eslint:
+ specifier: ^8.45.0
+ version: 8.46.1(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3)
+ vite:
+ specifier: ^7.1.7
+ version: 7.1.10(@types/node@24.8.1)(jiti@2.6.1)(lightningcss@1.30.1)(yaml@2.8.1)
+ vitest:
+ specifier: ^4.0.6
+ version: 4.0.6(@types/node@24.8.1)(jiti@2.6.1)(jsdom@27.2.0)(lightningcss@1.30.1)(yaml@2.8.1)
+ vitest-localstorage-mock:
+ specifier: ^0.1.2
+ version: 0.1.2(vitest@4.0.6(@types/node@24.8.1)(jiti@2.6.1)(jsdom@27.2.0)(lightningcss@1.30.1)(yaml@2.8.1))
+
+packages:
+
+ '@acemir/cssom@0.9.23':
+ resolution: {integrity: sha512-2kJ1HxBKzPLbmhZpxBiTZggjtgCwKg1ma5RHShxvd6zgqhDEdEkzpiwe7jLkI2p2BrZvFCXIihdoMkl1H39VnA==}
+
+ '@adobe/css-tools@4.4.4':
+ resolution: {integrity: sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==}
+
+ '@asamuzakjp/css-color@4.0.5':
+ resolution: {integrity: sha512-lMrXidNhPGsDjytDy11Vwlb6OIGrT3CmLg3VWNFyWkLWtijKl7xjvForlh8vuj0SHGjgl4qZEQzUmYTeQA2JFQ==}
+
+ '@asamuzakjp/dom-selector@6.7.4':
+ resolution: {integrity: sha512-buQDjkm+wDPXd6c13534URWZqbz0RP5PAhXZ+LIoa5LgwInT9HVJvGIJivg75vi8I13CxDGdTnz+aY5YUJlIAA==}
+
+ '@asamuzakjp/nwsapi@2.3.9':
+ resolution: {integrity: sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==}
+
+ '@babel/code-frame@7.27.1':
+ resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-string-parser@7.27.1':
+ resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-validator-identifier@7.28.5':
+ resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/parser@7.28.5':
+ resolution: {integrity: sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==}
+ engines: {node: '>=6.0.0'}
+ hasBin: true
+
+ '@babel/runtime@7.28.4':
+ resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/types@7.28.5':
+ resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==}
+ engines: {node: '>=6.9.0'}
+
+ '@bcoe/v8-coverage@1.0.2':
+ resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==}
+ engines: {node: '>=18'}
+
+ '@csstools/color-helpers@5.1.0':
+ resolution: {integrity: sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==}
+ engines: {node: '>=18'}
+
+ '@csstools/css-calc@2.1.4':
+ resolution: {integrity: sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ '@csstools/css-parser-algorithms': ^3.0.5
+ '@csstools/css-tokenizer': ^3.0.4
+
+ '@csstools/css-color-parser@3.1.0':
+ resolution: {integrity: sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ '@csstools/css-parser-algorithms': ^3.0.5
+ '@csstools/css-tokenizer': ^3.0.4
+
+ '@csstools/css-parser-algorithms@3.0.5':
+ resolution: {integrity: sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ '@csstools/css-tokenizer': ^3.0.4
+
+ '@csstools/css-syntax-patches-for-csstree@1.0.16':
+ resolution: {integrity: sha512-2SpS4/UaWQaGpBINyG5ZuCHnUDeVByOhvbkARwfmnfxDvTaj80yOI1cD8Tw93ICV5Fx4fnyDKWQZI1CDtcWyUg==}
+ engines: {node: '>=18'}
+
+ '@csstools/css-tokenizer@3.0.4':
+ resolution: {integrity: sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==}
+ engines: {node: '>=18'}
+
+ '@date-fns/tz@1.4.1':
+ resolution: {integrity: sha512-P5LUNhtbj6YfI3iJjw5EL9eUAG6OitD0W3fWQcpQjDRc/QIsL0tRNuO1PcDvPccWL1fSTXXdE1ds+l95DV/OFA==}
+
+ '@dnd-kit/accessibility@3.1.1':
+ resolution: {integrity: sha512-2P+YgaXF+gRsIihwwY1gCsQSYnu9Zyj2py8kY5fFvUM1qm2WA2u639R6YNVfU4GWr+ZM5mqEsfHZZLoRONbemw==}
+ peerDependencies:
+ react: '>=16.8.0'
+
+ '@dnd-kit/core@6.3.1':
+ resolution: {integrity: sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==}
+ peerDependencies:
+ react: '>=16.8.0'
+ react-dom: '>=16.8.0'
+
+ '@dnd-kit/modifiers@9.0.0':
+ resolution: {integrity: sha512-ybiLc66qRGuZoC20wdSSG6pDXFikui/dCNGthxv4Ndy8ylErY0N3KVxY2bgo7AWwIbxDmXDg3ylAFmnrjcbVvw==}
+ peerDependencies:
+ '@dnd-kit/core': ^6.3.0
+ react: '>=16.8.0'
+
+ '@dnd-kit/sortable@10.0.0':
+ resolution: {integrity: sha512-+xqhmIIzvAYMGfBYYnbKuNicfSsk4RksY2XdmJhT+HAC01nix6fHCztU68jooFiMUB01Ky3F0FyOvhG/BZrWkg==}
+ peerDependencies:
+ '@dnd-kit/core': ^6.3.0
+ react: '>=16.8.0'
+
+ '@dnd-kit/utilities@3.2.2':
+ resolution: {integrity: sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg==}
+ peerDependencies:
+ react: '>=16.8.0'
+
+ '@esbuild/aix-ppc64@0.25.11':
+ resolution: {integrity: sha512-Xt1dOL13m8u0WE8iplx9Ibbm+hFAO0GsU2P34UNoDGvZYkY8ifSiy6Zuc1lYxfG7svWE2fzqCUmFp5HCn51gJg==}
+ engines: {node: '>=18'}
+ cpu: [ppc64]
+ os: [aix]
+
+ '@esbuild/android-arm64@0.25.11':
+ resolution: {integrity: sha512-9slpyFBc4FPPz48+f6jyiXOx/Y4v34TUeDDXJpZqAWQn/08lKGeD8aDp9TMn9jDz2CiEuHwfhRmGBvpnd/PWIQ==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [android]
+
+ '@esbuild/android-arm@0.25.11':
+ resolution: {integrity: sha512-uoa7dU+Dt3HYsethkJ1k6Z9YdcHjTrSb5NUy66ZfZaSV8hEYGD5ZHbEMXnqLFlbBflLsl89Zke7CAdDJ4JI+Gg==}
+ engines: {node: '>=18'}
+ cpu: [arm]
+ os: [android]
+
+ '@esbuild/android-x64@0.25.11':
+ resolution: {integrity: sha512-Sgiab4xBjPU1QoPEIqS3Xx+R2lezu0LKIEcYe6pftr56PqPygbB7+szVnzoShbx64MUupqoE0KyRlN7gezbl8g==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [android]
+
+ '@esbuild/darwin-arm64@0.25.11':
+ resolution: {integrity: sha512-VekY0PBCukppoQrycFxUqkCojnTQhdec0vevUL/EDOCnXd9LKWqD/bHwMPzigIJXPhC59Vd1WFIL57SKs2mg4w==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@esbuild/darwin-x64@0.25.11':
+ resolution: {integrity: sha512-+hfp3yfBalNEpTGp9loYgbknjR695HkqtY3d3/JjSRUyPg/xd6q+mQqIb5qdywnDxRZykIHs3axEqU6l1+oWEQ==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@esbuild/freebsd-arm64@0.25.11':
+ resolution: {integrity: sha512-CmKjrnayyTJF2eVuO//uSjl/K3KsMIeYeyN7FyDBjsR3lnSJHaXlVoAK8DZa7lXWChbuOk7NjAc7ygAwrnPBhA==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [freebsd]
+
+ '@esbuild/freebsd-x64@0.25.11':
+ resolution: {integrity: sha512-Dyq+5oscTJvMaYPvW3x3FLpi2+gSZTCE/1ffdwuM6G1ARang/mb3jvjxs0mw6n3Lsw84ocfo9CrNMqc5lTfGOw==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@esbuild/linux-arm64@0.25.11':
+ resolution: {integrity: sha512-Qr8AzcplUhGvdyUF08A1kHU3Vr2O88xxP0Tm8GcdVOUm25XYcMPp2YqSVHbLuXzYQMf9Bh/iKx7YPqECs6ffLA==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@esbuild/linux-arm@0.25.11':
+ resolution: {integrity: sha512-TBMv6B4kCfrGJ8cUPo7vd6NECZH/8hPpBHHlYI3qzoYFvWu2AdTvZNuU/7hsbKWqu/COU7NIK12dHAAqBLLXgw==}
+ engines: {node: '>=18'}
+ cpu: [arm]
+ os: [linux]
+
+ '@esbuild/linux-ia32@0.25.11':
+ resolution: {integrity: sha512-TmnJg8BMGPehs5JKrCLqyWTVAvielc615jbkOirATQvWWB1NMXY77oLMzsUjRLa0+ngecEmDGqt5jiDC6bfvOw==}
+ engines: {node: '>=18'}
+ cpu: [ia32]
+ os: [linux]
+
+ '@esbuild/linux-loong64@0.25.11':
+ resolution: {integrity: sha512-DIGXL2+gvDaXlaq8xruNXUJdT5tF+SBbJQKbWy/0J7OhU8gOHOzKmGIlfTTl6nHaCOoipxQbuJi7O++ldrxgMw==}
+ engines: {node: '>=18'}
+ cpu: [loong64]
+ os: [linux]
+
+ '@esbuild/linux-mips64el@0.25.11':
+ resolution: {integrity: sha512-Osx1nALUJu4pU43o9OyjSCXokFkFbyzjXb6VhGIJZQ5JZi8ylCQ9/LFagolPsHtgw6himDSyb5ETSfmp4rpiKQ==}
+ engines: {node: '>=18'}
+ cpu: [mips64el]
+ os: [linux]
+
+ '@esbuild/linux-ppc64@0.25.11':
+ resolution: {integrity: sha512-nbLFgsQQEsBa8XSgSTSlrnBSrpoWh7ioFDUmwo158gIm5NNP+17IYmNWzaIzWmgCxq56vfr34xGkOcZ7jX6CPw==}
+ engines: {node: '>=18'}
+ cpu: [ppc64]
+ os: [linux]
+
+ '@esbuild/linux-riscv64@0.25.11':
+ resolution: {integrity: sha512-HfyAmqZi9uBAbgKYP1yGuI7tSREXwIb438q0nqvlpxAOs3XnZ8RsisRfmVsgV486NdjD7Mw2UrFSw51lzUk1ww==}
+ engines: {node: '>=18'}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@esbuild/linux-s390x@0.25.11':
+ resolution: {integrity: sha512-HjLqVgSSYnVXRisyfmzsH6mXqyvj0SA7pG5g+9W7ESgwA70AXYNpfKBqh1KbTxmQVaYxpzA/SvlB9oclGPbApw==}
+ engines: {node: '>=18'}
+ cpu: [s390x]
+ os: [linux]
+
+ '@esbuild/linux-x64@0.25.11':
+ resolution: {integrity: sha512-HSFAT4+WYjIhrHxKBwGmOOSpphjYkcswF449j6EjsjbinTZbp8PJtjsVK1XFJStdzXdy/jaddAep2FGY+wyFAQ==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [linux]
+
+ '@esbuild/netbsd-arm64@0.25.11':
+ resolution: {integrity: sha512-hr9Oxj1Fa4r04dNpWr3P8QKVVsjQhqrMSUzZzf+LZcYjZNqhA3IAfPQdEh1FLVUJSiu6sgAwp3OmwBfbFgG2Xg==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [netbsd]
+
+ '@esbuild/netbsd-x64@0.25.11':
+ resolution: {integrity: sha512-u7tKA+qbzBydyj0vgpu+5h5AeudxOAGncb8N6C9Kh1N4n7wU1Xw1JDApsRjpShRpXRQlJLb9wY28ELpwdPcZ7A==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [netbsd]
+
+ '@esbuild/openbsd-arm64@0.25.11':
+ resolution: {integrity: sha512-Qq6YHhayieor3DxFOoYM1q0q1uMFYb7cSpLD2qzDSvK1NAvqFi8Xgivv0cFC6J+hWVw2teCYltyy9/m/14ryHg==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [openbsd]
+
+ '@esbuild/openbsd-x64@0.25.11':
+ resolution: {integrity: sha512-CN+7c++kkbrckTOz5hrehxWN7uIhFFlmS/hqziSFVWpAzpWrQoAG4chH+nN3Be+Kzv/uuo7zhX716x3Sn2Jduw==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [openbsd]
+
+ '@esbuild/openharmony-arm64@0.25.11':
+ resolution: {integrity: sha512-rOREuNIQgaiR+9QuNkbkxubbp8MSO9rONmwP5nKncnWJ9v5jQ4JxFnLu4zDSRPf3x4u+2VN4pM4RdyIzDty/wQ==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [openharmony]
+
+ '@esbuild/sunos-x64@0.25.11':
+ resolution: {integrity: sha512-nq2xdYaWxyg9DcIyXkZhcYulC6pQ2FuCgem3LI92IwMgIZ69KHeY8T4Y88pcwoLIjbed8n36CyKoYRDygNSGhA==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [sunos]
+
+ '@esbuild/win32-arm64@0.25.11':
+ resolution: {integrity: sha512-3XxECOWJq1qMZ3MN8srCJ/QfoLpL+VaxD/WfNRm1O3B4+AZ/BnLVgFbUV3eiRYDMXetciH16dwPbbHqwe1uU0Q==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@esbuild/win32-ia32@0.25.11':
+ resolution: {integrity: sha512-3ukss6gb9XZ8TlRyJlgLn17ecsK4NSQTmdIXRASVsiS2sQ6zPPZklNJT5GR5tE/MUarymmy8kCEf5xPCNCqVOA==}
+ engines: {node: '>=18'}
+ cpu: [ia32]
+ os: [win32]
+
+ '@esbuild/win32-x64@0.25.11':
+ resolution: {integrity: sha512-D7Hpz6A2L4hzsRpPaCYkQnGOotdUpDzSGRIv9I+1ITdHROSFUWW95ZPZWQmGka1Fg7W3zFJowyn9WGwMJ0+KPA==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [win32]
+
+ '@eslint-community/eslint-utils@4.9.0':
+ resolution: {integrity: sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+ peerDependencies:
+ eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
+
+ '@eslint-community/regexpp@4.12.1':
+ resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==}
+ engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
+
+ '@eslint/config-array@0.21.0':
+ resolution: {integrity: sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/config-helpers@0.4.0':
+ resolution: {integrity: sha512-WUFvV4WoIwW8Bv0KeKCIIEgdSiFOsulyN0xrMu+7z43q/hkOLXjvb5u7UC9jDxvRzcrbEmuZBX5yJZz1741jog==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/core@0.16.0':
+ resolution: {integrity: sha512-nmC8/totwobIiFcGkDza3GIKfAw1+hLiYVrh3I1nIomQ8PEr5cxg34jnkmGawul/ep52wGRAcyeDCNtWKSOj4Q==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/eslintrc@3.3.1':
+ resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/js@9.37.0':
+ resolution: {integrity: sha512-jaS+NJ+hximswBG6pjNX0uEJZkrT0zwpVi3BA3vX22aFGjJjmgSTSmPpZCRKmoBL5VY/M6p0xsSJx7rk7sy5gg==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/object-schema@2.1.6':
+ resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/plugin-kit@0.4.0':
+ resolution: {integrity: sha512-sB5uyeq+dwCWyPi31B2gQlVlo+j5brPlWx4yZBrEaRo/nhdDE8Xke1gsGgtiBdaBTxuTkceLVuVt/pclrasb0A==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@floating-ui/core@1.7.3':
+ resolution: {integrity: sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==}
+
+ '@floating-ui/dom@1.7.4':
+ resolution: {integrity: sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==}
+
+ '@floating-ui/react-dom@2.1.6':
+ resolution: {integrity: sha512-4JX6rEatQEvlmgU80wZyq9RT96HZJa88q8hp0pBd+LrczeDI4o6uA2M+uvxngVHo4Ihr8uibXxH6+70zhAFrVw==}
+ peerDependencies:
+ react: '>=16.8.0'
+ react-dom: '>=16.8.0'
+
+ '@floating-ui/utils@0.2.10':
+ resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==}
+
+ '@hookform/resolvers@5.2.2':
+ resolution: {integrity: sha512-A/IxlMLShx3KjV/HeTcTfaMxdwy690+L/ZADoeaTltLx+CVuzkeVIPuybK3jrRfw7YZnmdKsVVHAlEPIAEUNlA==}
+ peerDependencies:
+ react-hook-form: ^7.55.0
+
+ '@humanfs/core@0.19.1':
+ resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==}
+ engines: {node: '>=18.18.0'}
+
+ '@humanfs/node@0.16.7':
+ resolution: {integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==}
+ engines: {node: '>=18.18.0'}
+
+ '@humanwhocodes/module-importer@1.0.1':
+ resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==}
+ engines: {node: '>=12.22'}
+
+ '@humanwhocodes/retry@0.4.3':
+ resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==}
+ engines: {node: '>=18.18'}
+
+ '@isaacs/fs-minipass@4.0.1':
+ resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==}
+ engines: {node: '>=18.0.0'}
+
+ '@jridgewell/gen-mapping@0.3.13':
+ resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==}
+
+ '@jridgewell/remapping@2.3.5':
+ resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==}
+
+ '@jridgewell/resolve-uri@3.1.2':
+ resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
+ engines: {node: '>=6.0.0'}
+
+ '@jridgewell/sourcemap-codec@1.5.5':
+ resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
+
+ '@jridgewell/trace-mapping@0.3.31':
+ resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
+
+ '@microsoft/signalr@10.0.0':
+ resolution: {integrity: sha512-0BRqz/uCx3JdrOqiqgFhih/+hfTERaUfCZXFB52uMaZJrKaPRzHzMuqVsJC/V3pt7NozcNXGspjKiQEK+X7P2w==}
+
+ '@nodelib/fs.scandir@2.1.5':
+ resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
+ engines: {node: '>= 8'}
+
+ '@nodelib/fs.stat@2.0.5':
+ resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==}
+ engines: {node: '>= 8'}
+
+ '@nodelib/fs.walk@1.2.8':
+ resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
+ engines: {node: '>= 8'}
+
+ '@pkgr/core@0.2.9':
+ resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==}
+ engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
+
+ '@radix-ui/number@1.1.1':
+ resolution: {integrity: sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==}
+
+ '@radix-ui/primitive@1.1.3':
+ resolution: {integrity: sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==}
+
+ '@radix-ui/react-accessible-icon@1.1.7':
+ resolution: {integrity: sha512-XM+E4WXl0OqUJFovy6GjmxxFyx9opfCAIUku4dlKRd5YEPqt4kALOkQOp0Of6reHuUkJuiPBEc5k0o4z4lTC8A==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-accordion@1.2.12':
+ resolution: {integrity: sha512-T4nygeh9YE9dLRPhAHSeOZi7HBXo+0kYIPJXayZfvWOWA0+n3dESrZbjfDPUABkUNym6Hd+f2IR113To8D2GPA==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-alert-dialog@1.1.15':
+ resolution: {integrity: sha512-oTVLkEw5GpdRe29BqJ0LSDFWI3qu0vR1M0mUkOQWDIUnY/QIkLpgDMWuKxP94c2NAC2LGcgVhG1ImF3jkZ5wXw==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-arrow@1.1.7':
+ resolution: {integrity: sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-aspect-ratio@1.1.7':
+ resolution: {integrity: sha512-Yq6lvO9HQyPwev1onK1daHCHqXVLzPhSVjmsNjCa2Zcxy2f7uJD2itDtxknv6FzAKCwD1qQkeVDmX/cev13n/g==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-avatar@1.1.10':
+ resolution: {integrity: sha512-V8piFfWapM5OmNCXTzVQY+E1rDa53zY+MQ4Y7356v4fFz6vqCyUtIz2rUD44ZEdwg78/jKmMJHj07+C/Z/rcog==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-checkbox@1.3.3':
+ resolution: {integrity: sha512-wBbpv+NQftHDdG86Qc0pIyXk5IR3tM8Vd0nWLKDcX8nNn4nXFOFwsKuqw2okA/1D/mpaAkmuyndrPJTYDNZtFw==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-collapsible@1.1.12':
+ resolution: {integrity: sha512-Uu+mSh4agx2ib1uIGPP4/CKNULyajb3p92LsVXmH2EHVMTfZWpll88XJ0j4W0z3f8NK1eYl1+Mf/szHPmcHzyA==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-collection@1.1.7':
+ resolution: {integrity: sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-compose-refs@1.1.2':
+ resolution: {integrity: sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-context-menu@2.2.16':
+ resolution: {integrity: sha512-O8morBEW+HsVG28gYDZPTrT9UUovQUlJue5YO836tiTJhuIWBm/zQHc7j388sHWtdH/xUZurK9olD2+pcqx5ww==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-context@1.1.2':
+ resolution: {integrity: sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-dialog@1.1.15':
+ resolution: {integrity: sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-direction@1.1.1':
+ resolution: {integrity: sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-dismissable-layer@1.1.11':
+ resolution: {integrity: sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-dropdown-menu@2.1.16':
+ resolution: {integrity: sha512-1PLGQEynI/3OX/ftV54COn+3Sud/Mn8vALg2rWnBLnRaGtJDduNW/22XjlGgPdpcIbiQxjKtb7BkcjP00nqfJw==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-focus-guards@1.1.3':
+ resolution: {integrity: sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-focus-scope@1.1.7':
+ resolution: {integrity: sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-form@0.1.8':
+ resolution: {integrity: sha512-QM70k4Zwjttifr5a4sZFts9fn8FzHYvQ5PiB19O2HsYibaHSVt9fH9rzB0XZo/YcM+b7t/p7lYCT/F5eOeF5yQ==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-hover-card@1.1.15':
+ resolution: {integrity: sha512-qgTkjNT1CfKMoP0rcasmlH2r1DAiYicWsDsufxl940sT2wHNEWWv6FMWIQXWhVdmC1d/HYfbhQx60KYyAtKxjg==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-id@1.1.1':
+ resolution: {integrity: sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-label@2.1.7':
+ resolution: {integrity: sha512-YT1GqPSL8kJn20djelMX7/cTRp/Y9w5IZHvfxQTVHrOqa2yMl7i/UfMqKRU5V7mEyKTrUVgJXhNQPVCG8PBLoQ==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-menu@2.1.16':
+ resolution: {integrity: sha512-72F2T+PLlphrqLcAotYPp0uJMr5SjP5SL01wfEspJbru5Zs5vQaSHb4VB3ZMJPimgHHCHG7gMOeOB9H3Hdmtxg==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-menubar@1.1.16':
+ resolution: {integrity: sha512-EB1FktTz5xRRi2Er974AUQZWg2yVBb1yjip38/lgwtCVRd3a+maUoGHN/xs9Yv8SY8QwbSEb+YrxGadVWbEutA==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-navigation-menu@1.2.14':
+ resolution: {integrity: sha512-YB9mTFQvCOAQMHU+C/jVl96WmuWeltyUEpRJJky51huhds5W2FQr1J8D/16sQlf0ozxkPK8uF3niQMdUwZPv5w==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-one-time-password-field@0.1.8':
+ resolution: {integrity: sha512-ycS4rbwURavDPVjCb5iS3aG4lURFDILi6sKI/WITUMZ13gMmn/xGjpLoqBAalhJaDk8I3UbCM5GzKHrnzwHbvg==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-password-toggle-field@0.1.3':
+ resolution: {integrity: sha512-/UuCrDBWravcaMix4TdT+qlNdVwOM1Nck9kWx/vafXsdfj1ChfhOdfi3cy9SGBpWgTXwYCuboT/oYpJy3clqfw==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-popover@1.1.15':
+ resolution: {integrity: sha512-kr0X2+6Yy/vJzLYJUPCZEc8SfQcf+1COFoAqauJm74umQhta9M7lNJHP7QQS3vkvcGLQUbWpMzwrXYwrYztHKA==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-popper@1.2.8':
+ resolution: {integrity: sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-portal@1.1.9':
+ resolution: {integrity: sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-presence@1.1.5':
+ resolution: {integrity: sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-primitive@2.1.3':
+ resolution: {integrity: sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-progress@1.1.7':
+ resolution: {integrity: sha512-vPdg/tF6YC/ynuBIJlk1mm7Le0VgW6ub6J2UWnTQ7/D23KXcPI1qy+0vBkgKgd38RCMJavBXpB83HPNFMTb0Fg==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-radio-group@1.3.8':
+ resolution: {integrity: sha512-VBKYIYImA5zsxACdisNQ3BjCBfmbGH3kQlnFVqlWU4tXwjy7cGX8ta80BcrO+WJXIn5iBylEH3K6ZTlee//lgQ==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-roving-focus@1.1.11':
+ resolution: {integrity: sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-scroll-area@1.2.10':
+ resolution: {integrity: sha512-tAXIa1g3sM5CGpVT0uIbUx/U3Gs5N8T52IICuCtObaos1S8fzsrPXG5WObkQN3S6NVl6wKgPhAIiBGbWnvc97A==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-select@2.2.6':
+ resolution: {integrity: sha512-I30RydO+bnn2PQztvo25tswPH+wFBjehVGtmagkU78yMdwTwVf12wnAOF+AeP8S2N8xD+5UPbGhkUfPyvT+mwQ==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-separator@1.1.7':
+ resolution: {integrity: sha512-0HEb8R9E8A+jZjvmFCy/J4xhbXy3TV+9XSnGJ3KvTtjlIUy/YQ/p6UYZvi7YbeoeXdyU9+Y3scizK6hkY37baA==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-slider@1.3.6':
+ resolution: {integrity: sha512-JPYb1GuM1bxfjMRlNLE+BcmBC8onfCi60Blk7OBqi2MLTFdS+8401U4uFjnwkOr49BLmXxLC6JHkvAsx5OJvHw==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-slot@1.2.3':
+ resolution: {integrity: sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-switch@1.2.6':
+ resolution: {integrity: sha512-bByzr1+ep1zk4VubeEVViV592vu2lHE2BZY5OnzehZqOOgogN80+mNtCqPkhn2gklJqOpxWgPoYTSnhBCqpOXQ==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-tabs@1.1.13':
+ resolution: {integrity: sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-toast@1.2.15':
+ resolution: {integrity: sha512-3OSz3TacUWy4WtOXV38DggwxoqJK4+eDkNMl5Z/MJZaoUPaP4/9lf81xXMe1I2ReTAptverZUpbPY4wWwWyL5g==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-toggle-group@1.1.11':
+ resolution: {integrity: sha512-5umnS0T8JQzQT6HbPyO7Hh9dgd82NmS36DQr+X/YJ9ctFNCiiQd6IJAYYZ33LUwm8M+taCz5t2ui29fHZc4Y6Q==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-toggle@1.1.10':
+ resolution: {integrity: sha512-lS1odchhFTeZv3xwHH31YPObmJn8gOg7Lq12inrr0+BH/l3Tsq32VfjqH1oh80ARM3mlkfMic15n0kg4sD1poQ==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-toolbar@1.1.11':
+ resolution: {integrity: sha512-4ol06/1bLoFu1nwUqzdD4Y5RZ9oDdKeiHIsntug54Hcr1pgaHiPqHFEaXI1IFP/EsOfROQZ8Mig9VTIRza6Tjg==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-tooltip@1.2.8':
+ resolution: {integrity: sha512-tY7sVt1yL9ozIxvmbtN5qtmH2krXcBCfjEiCgKGLqunJHvgvZG2Pcl2oQ3kbcZARb1BGEHdkLzcYGO8ynVlieg==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-use-callback-ref@1.1.1':
+ resolution: {integrity: sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-use-controllable-state@1.2.2':
+ resolution: {integrity: sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-use-effect-event@0.0.2':
+ resolution: {integrity: sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-use-escape-keydown@1.1.1':
+ resolution: {integrity: sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-use-is-hydrated@0.1.0':
+ resolution: {integrity: sha512-U+UORVEq+cTnRIaostJv9AGdV3G6Y+zbVd+12e18jQ5A3c0xL03IhnHuiU4UV69wolOQp5GfR58NW/EgdQhwOA==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-use-layout-effect@1.1.1':
+ resolution: {integrity: sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-use-previous@1.1.1':
+ resolution: {integrity: sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-use-rect@1.1.1':
+ resolution: {integrity: sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-use-size@1.1.1':
+ resolution: {integrity: sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-visually-hidden@1.2.3':
+ resolution: {integrity: sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/rect@1.1.1':
+ resolution: {integrity: sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==}
+
+ '@rolldown/pluginutils@1.0.0-beta.35':
+ resolution: {integrity: sha512-slYrCpoxJUqzFDDNlvrOYRazQUNRvWPjXA17dAOISY3rDMxX6k8K4cj2H+hEYMHF81HO3uNd5rHVigAWRM5dSg==}
+
+ '@rollup/rollup-android-arm-eabi@4.52.4':
+ resolution: {integrity: sha512-BTm2qKNnWIQ5auf4deoetINJm2JzvihvGb9R6K/ETwKLql/Bb3Eg2H1FBp1gUb4YGbydMA3jcmQTR73q7J+GAA==}
+ cpu: [arm]
+ os: [android]
+
+ '@rollup/rollup-android-arm64@4.52.4':
+ resolution: {integrity: sha512-P9LDQiC5vpgGFgz7GSM6dKPCiqR3XYN1WwJKA4/BUVDjHpYsf3iBEmVz62uyq20NGYbiGPR5cNHI7T1HqxNs2w==}
+ cpu: [arm64]
+ os: [android]
+
+ '@rollup/rollup-darwin-arm64@4.52.4':
+ resolution: {integrity: sha512-QRWSW+bVccAvZF6cbNZBJwAehmvG9NwfWHwMy4GbWi/BQIA/laTIktebT2ipVjNncqE6GLPxOok5hsECgAxGZg==}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@rollup/rollup-darwin-x64@4.52.4':
+ resolution: {integrity: sha512-hZgP05pResAkRJxL1b+7yxCnXPGsXU0fG9Yfd6dUaoGk+FhdPKCJ5L1Sumyxn8kvw8Qi5PvQ8ulenUbRjzeCTw==}
+ cpu: [x64]
+ os: [darwin]
+
+ '@rollup/rollup-freebsd-arm64@4.52.4':
+ resolution: {integrity: sha512-xmc30VshuBNUd58Xk4TKAEcRZHaXlV+tCxIXELiE9sQuK3kG8ZFgSPi57UBJt8/ogfhAF5Oz4ZSUBN77weM+mQ==}
+ cpu: [arm64]
+ os: [freebsd]
+
+ '@rollup/rollup-freebsd-x64@4.52.4':
+ resolution: {integrity: sha512-WdSLpZFjOEqNZGmHflxyifolwAiZmDQzuOzIq9L27ButpCVpD7KzTRtEG1I0wMPFyiyUdOO+4t8GvrnBLQSwpw==}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@rollup/rollup-linux-arm-gnueabihf@4.52.4':
+ resolution: {integrity: sha512-xRiOu9Of1FZ4SxVbB0iEDXc4ddIcjCv2aj03dmW8UrZIW7aIQ9jVJdLBIhxBI+MaTnGAKyvMwPwQnoOEvP7FgQ==}
+ cpu: [arm]
+ os: [linux]
+
+ '@rollup/rollup-linux-arm-musleabihf@4.52.4':
+ resolution: {integrity: sha512-FbhM2p9TJAmEIEhIgzR4soUcsW49e9veAQCziwbR+XWB2zqJ12b4i/+hel9yLiD8pLncDH4fKIPIbt5238341Q==}
+ cpu: [arm]
+ os: [linux]
+
+ '@rollup/rollup-linux-arm64-gnu@4.52.4':
+ resolution: {integrity: sha512-4n4gVwhPHR9q/g8lKCyz0yuaD0MvDf7dV4f9tHt0C73Mp8h38UCtSCSE6R9iBlTbXlmA8CjpsZoujhszefqueg==}
+ cpu: [arm64]
+ os: [linux]
+
+ '@rollup/rollup-linux-arm64-musl@4.52.4':
+ resolution: {integrity: sha512-u0n17nGA0nvi/11gcZKsjkLj1QIpAuPFQbR48Subo7SmZJnGxDpspyw2kbpuoQnyK+9pwf3pAoEXerJs/8Mi9g==}
+ cpu: [arm64]
+ os: [linux]
+
+ '@rollup/rollup-linux-loong64-gnu@4.52.4':
+ resolution: {integrity: sha512-0G2c2lpYtbTuXo8KEJkDkClE/+/2AFPdPAbmaHoE870foRFs4pBrDehilMcrSScrN/fB/1HTaWO4bqw+ewBzMQ==}
+ cpu: [loong64]
+ os: [linux]
+
+ '@rollup/rollup-linux-ppc64-gnu@4.52.4':
+ resolution: {integrity: sha512-teSACug1GyZHmPDv14VNbvZFX779UqWTsd7KtTM9JIZRDI5NUwYSIS30kzI8m06gOPB//jtpqlhmraQ68b5X2g==}
+ cpu: [ppc64]
+ os: [linux]
+
+ '@rollup/rollup-linux-riscv64-gnu@4.52.4':
+ resolution: {integrity: sha512-/MOEW3aHjjs1p4Pw1Xk4+3egRevx8Ji9N6HUIA1Ifh8Q+cg9dremvFCUbOX2Zebz80BwJIgCBUemjqhU5XI5Eg==}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@rollup/rollup-linux-riscv64-musl@4.52.4':
+ resolution: {integrity: sha512-1HHmsRyh845QDpEWzOFtMCph5Ts+9+yllCrREuBR/vg2RogAQGGBRC8lDPrPOMnrdOJ+mt1WLMOC2Kao/UwcvA==}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@rollup/rollup-linux-s390x-gnu@4.52.4':
+ resolution: {integrity: sha512-seoeZp4L/6D1MUyjWkOMRU6/iLmCU2EjbMTyAG4oIOs1/I82Y5lTeaxW0KBfkUdHAWN7j25bpkt0rjnOgAcQcA==}
+ cpu: [s390x]
+ os: [linux]
+
+ '@rollup/rollup-linux-x64-gnu@4.52.4':
+ resolution: {integrity: sha512-Wi6AXf0k0L7E2gteNsNHUs7UMwCIhsCTs6+tqQ5GPwVRWMaflqGec4Sd8n6+FNFDw9vGcReqk2KzBDhCa1DLYg==}
+ cpu: [x64]
+ os: [linux]
+
+ '@rollup/rollup-linux-x64-musl@4.52.4':
+ resolution: {integrity: sha512-dtBZYjDmCQ9hW+WgEkaffvRRCKm767wWhxsFW3Lw86VXz/uJRuD438/XvbZT//B96Vs8oTA8Q4A0AfHbrxP9zw==}
+ cpu: [x64]
+ os: [linux]
+
+ '@rollup/rollup-openharmony-arm64@4.52.4':
+ resolution: {integrity: sha512-1ox+GqgRWqaB1RnyZXL8PD6E5f7YyRUJYnCqKpNzxzP0TkaUh112NDrR9Tt+C8rJ4x5G9Mk8PQR3o7Ku2RKqKA==}
+ cpu: [arm64]
+ os: [openharmony]
+
+ '@rollup/rollup-win32-arm64-msvc@4.52.4':
+ resolution: {integrity: sha512-8GKr640PdFNXwzIE0IrkMWUNUomILLkfeHjXBi/nUvFlpZP+FA8BKGKpacjW6OUUHaNI6sUURxR2U2g78FOHWQ==}
+ cpu: [arm64]
+ os: [win32]
+
+ '@rollup/rollup-win32-ia32-msvc@4.52.4':
+ resolution: {integrity: sha512-AIy/jdJ7WtJ/F6EcfOb2GjR9UweO0n43jNObQMb6oGxkYTfLcnN7vYYpG+CN3lLxrQkzWnMOoNSHTW54pgbVxw==}
+ cpu: [ia32]
+ os: [win32]
+
+ '@rollup/rollup-win32-x64-gnu@4.52.4':
+ resolution: {integrity: sha512-UF9KfsH9yEam0UjTwAgdK0anlQ7c8/pWPU2yVjyWcF1I1thABt6WXE47cI71pGiZ8wGvxohBoLnxM04L/wj8mQ==}
+ cpu: [x64]
+ os: [win32]
+
+ '@rollup/rollup-win32-x64-msvc@4.52.4':
+ resolution: {integrity: sha512-bf9PtUa0u8IXDVxzRToFQKsNCRz9qLYfR/MpECxl4mRoWYjAeFjgxj1XdZr2M/GNVpT05p+LgQOHopYDlUu6/w==}
+ cpu: [x64]
+ os: [win32]
+
+ '@standard-schema/spec@1.0.0':
+ resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==}
+
+ '@standard-schema/utils@0.3.0':
+ resolution: {integrity: sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==}
+
+ '@swc/core-darwin-arm64@1.13.5':
+ resolution: {integrity: sha512-lKNv7SujeXvKn16gvQqUQI5DdyY8v7xcoO3k06/FJbHJS90zEwZdQiMNRiqpYw/orU543tPaWgz7cIYWhbopiQ==}
+ engines: {node: '>=10'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@swc/core-darwin-x64@1.13.5':
+ resolution: {integrity: sha512-ILd38Fg/w23vHb0yVjlWvQBoE37ZJTdlLHa8LRCFDdX4WKfnVBiblsCU9ar4QTMNdeTBEX9iUF4IrbNWhaF1Ng==}
+ engines: {node: '>=10'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@swc/core-linux-arm-gnueabihf@1.13.5':
+ resolution: {integrity: sha512-Q6eS3Pt8GLkXxqz9TAw+AUk9HpVJt8Uzm54MvPsqp2yuGmY0/sNaPPNVqctCX9fu/Nu8eaWUen0si6iEiCsazQ==}
+ engines: {node: '>=10'}
+ cpu: [arm]
+ os: [linux]
+
+ '@swc/core-linux-arm64-gnu@1.13.5':
+ resolution: {integrity: sha512-aNDfeN+9af+y+M2MYfxCzCy/VDq7Z5YIbMqRI739o8Ganz6ST+27kjQFd8Y/57JN/hcnUEa9xqdS3XY7WaVtSw==}
+ engines: {node: '>=10'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@swc/core-linux-arm64-musl@1.13.5':
+ resolution: {integrity: sha512-9+ZxFN5GJag4CnYnq6apKTnnezpfJhCumyz0504/JbHLo+Ue+ZtJnf3RhyA9W9TINtLE0bC4hKpWi8ZKoETyOQ==}
+ engines: {node: '>=10'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@swc/core-linux-x64-gnu@1.13.5':
+ resolution: {integrity: sha512-WD530qvHrki8Ywt/PloKUjaRKgstQqNGvmZl54g06kA+hqtSE2FTG9gngXr3UJxYu/cNAjJYiBifm7+w4nbHbA==}
+ engines: {node: '>=10'}
+ cpu: [x64]
+ os: [linux]
+
+ '@swc/core-linux-x64-musl@1.13.5':
+ resolution: {integrity: sha512-Luj8y4OFYx4DHNQTWjdIuKTq2f5k6uSXICqx+FSabnXptaOBAbJHNbHT/06JZh6NRUouaf0mYXN0mcsqvkhd7Q==}
+ engines: {node: '>=10'}
+ cpu: [x64]
+ os: [linux]
+
+ '@swc/core-win32-arm64-msvc@1.13.5':
+ resolution: {integrity: sha512-cZ6UpumhF9SDJvv4DA2fo9WIzlNFuKSkZpZmPG1c+4PFSEMy5DFOjBSllCvnqihCabzXzpn6ykCwBmHpy31vQw==}
+ engines: {node: '>=10'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@swc/core-win32-ia32-msvc@1.13.5':
+ resolution: {integrity: sha512-C5Yi/xIikrFUzZcyGj9L3RpKljFvKiDMtyDzPKzlsDrKIw2EYY+bF88gB6oGY5RGmv4DAX8dbnpRAqgFD0FMEw==}
+ engines: {node: '>=10'}
+ cpu: [ia32]
+ os: [win32]
+
+ '@swc/core-win32-x64-msvc@1.13.5':
+ resolution: {integrity: sha512-YrKdMVxbYmlfybCSbRtrilc6UA8GF5aPmGKBdPvjrarvsmf4i7ZHGCEnLtfOMd3Lwbs2WUZq3WdMbozYeLU93Q==}
+ engines: {node: '>=10'}
+ cpu: [x64]
+ os: [win32]
+
+ '@swc/core@1.13.5':
+ resolution: {integrity: sha512-WezcBo8a0Dg2rnR82zhwoR6aRNxeTGfK5QCD6TQ+kg3xx/zNT02s/0o+81h/3zhvFSB24NtqEr8FTw88O5W/JQ==}
+ engines: {node: '>=10'}
+ peerDependencies:
+ '@swc/helpers': '>=0.5.17'
+ peerDependenciesMeta:
+ '@swc/helpers':
+ optional: true
+
+ '@swc/counter@0.1.3':
+ resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==}
+
+ '@swc/types@0.1.25':
+ resolution: {integrity: sha512-iAoY/qRhNH8a/hBvm3zKj9qQ4oc2+3w1unPJa2XvTK3XjeLXtzcCingVPw/9e5mn1+0yPqxcBGp9Jf0pkfMb1g==}
+
+ '@tailwindcss/node@4.1.14':
+ resolution: {integrity: sha512-hpz+8vFk3Ic2xssIA3e01R6jkmsAhvkQdXlEbRTk6S10xDAtiQiM3FyvZVGsucefq764euO/b8WUW9ysLdThHw==}
+
+ '@tailwindcss/oxide-android-arm64@4.1.14':
+ resolution: {integrity: sha512-a94ifZrGwMvbdeAxWoSuGcIl6/DOP5cdxagid7xJv6bwFp3oebp7y2ImYsnZBMTwjn5Ev5xESvS3FFYUGgPODQ==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [android]
+
+ '@tailwindcss/oxide-darwin-arm64@4.1.14':
+ resolution: {integrity: sha512-HkFP/CqfSh09xCnrPJA7jud7hij5ahKyWomrC3oiO2U9i0UjP17o9pJbxUN0IJ471GTQQmzwhp0DEcpbp4MZTA==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@tailwindcss/oxide-darwin-x64@4.1.14':
+ resolution: {integrity: sha512-eVNaWmCgdLf5iv6Qd3s7JI5SEFBFRtfm6W0mphJYXgvnDEAZ5sZzqmI06bK6xo0IErDHdTA5/t7d4eTfWbWOFw==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@tailwindcss/oxide-freebsd-x64@4.1.14':
+ resolution: {integrity: sha512-QWLoRXNikEuqtNb0dhQN6wsSVVjX6dmUFzuuiL09ZeXju25dsei2uIPl71y2Ic6QbNBsB4scwBoFnlBfabHkEw==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.14':
+ resolution: {integrity: sha512-VB4gjQni9+F0VCASU+L8zSIyjrLLsy03sjcR3bM0V2g4SNamo0FakZFKyUQ96ZVwGK4CaJsc9zd/obQy74o0Fw==}
+ engines: {node: '>= 10'}
+ cpu: [arm]
+ os: [linux]
+
+ '@tailwindcss/oxide-linux-arm64-gnu@4.1.14':
+ resolution: {integrity: sha512-qaEy0dIZ6d9vyLnmeg24yzA8XuEAD9WjpM5nIM1sUgQ/Zv7cVkharPDQcmm/t/TvXoKo/0knI3me3AGfdx6w1w==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@tailwindcss/oxide-linux-arm64-musl@4.1.14':
+ resolution: {integrity: sha512-ISZjT44s59O8xKsPEIesiIydMG/sCXoMBCqsphDm/WcbnuWLxxb+GcvSIIA5NjUw6F8Tex7s5/LM2yDy8RqYBQ==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@tailwindcss/oxide-linux-x64-gnu@4.1.14':
+ resolution: {integrity: sha512-02c6JhLPJj10L2caH4U0zF8Hji4dOeahmuMl23stk0MU1wfd1OraE7rOloidSF8W5JTHkFdVo/O7uRUJJnUAJg==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [linux]
+
+ '@tailwindcss/oxide-linux-x64-musl@4.1.14':
+ resolution: {integrity: sha512-TNGeLiN1XS66kQhxHG/7wMeQDOoL0S33x9BgmydbrWAb9Qw0KYdd8o1ifx4HOGDWhVmJ+Ul+JQ7lyknQFilO3Q==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [linux]
+
+ '@tailwindcss/oxide-wasm32-wasi@4.1.14':
+ resolution: {integrity: sha512-uZYAsaW/jS/IYkd6EWPJKW/NlPNSkWkBlaeVBi/WsFQNP05/bzkebUL8FH1pdsqx4f2fH/bWFcUABOM9nfiJkQ==}
+ engines: {node: '>=14.0.0'}
+ cpu: [wasm32]
+ bundledDependencies:
+ - '@napi-rs/wasm-runtime'
+ - '@emnapi/core'
+ - '@emnapi/runtime'
+ - '@tybys/wasm-util'
+ - '@emnapi/wasi-threads'
+ - tslib
+
+ '@tailwindcss/oxide-win32-arm64-msvc@4.1.14':
+ resolution: {integrity: sha512-Az0RnnkcvRqsuoLH2Z4n3JfAef0wElgzHD5Aky/e+0tBUxUhIeIqFBTMNQvmMRSP15fWwmvjBxZ3Q8RhsDnxAA==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@tailwindcss/oxide-win32-x64-msvc@4.1.14':
+ resolution: {integrity: sha512-ttblVGHgf68kEE4om1n/n44I0yGPkCPbLsqzjvybhpwa6mKKtgFfAzy6btc3HRmuW7nHe0OOrSeNP9sQmmH9XA==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [win32]
+
+ '@tailwindcss/oxide@4.1.14':
+ resolution: {integrity: sha512-23yx+VUbBwCg2x5XWdB8+1lkPajzLmALEfMb51zZUBYaYVPDQvBSD/WYDqiVyBIo2BZFa3yw1Rpy3G2Jp+K0dw==}
+ engines: {node: '>= 10'}
+
+ '@tailwindcss/vite@4.1.14':
+ resolution: {integrity: sha512-BoFUoU0XqgCUS1UXWhmDJroKKhNXeDzD7/XwabjkDIAbMnc4ULn5e2FuEuBbhZ6ENZoSYzKlzvZ44Yr6EUDUSA==}
+ peerDependencies:
+ vite: ^5.2.0 || ^6 || ^7
+
+ '@tanstack/query-core@5.90.6':
+ resolution: {integrity: sha512-AnZSLF26R8uX+tqb/ivdrwbVdGemdEDm1Q19qM6pry6eOZ6bEYiY7mWhzXT1YDIPTNEVcZ5kYP9nWjoxDLiIVw==}
+
+ '@tanstack/query-devtools@5.90.1':
+ resolution: {integrity: sha512-GtINOPjPUH0OegJExZ70UahT9ykmAhmtNVcmtdnOZbxLwT7R5OmRztR5Ahe3/Cu7LArEmR6/588tAycuaWb1xQ==}
+
+ '@tanstack/react-query-devtools@5.90.2':
+ resolution: {integrity: sha512-vAXJzZuBXtCQtrY3F/yUNJCV4obT/A/n81kb3+YqLbro5Z2+phdAbceO+deU3ywPw8B42oyJlp4FhO0SoivDFQ==}
+ peerDependencies:
+ '@tanstack/react-query': ^5.90.2
+ react: ^18 || ^19
+
+ '@tanstack/react-query@5.90.6':
+ resolution: {integrity: sha512-gB1sljYjcobZKxjPbKSa31FUTyr+ROaBdoH+wSSs9Dk+yDCmMs+TkTV3PybRRVLC7ax7q0erJ9LvRWnMktnRAw==}
+ peerDependencies:
+ react: ^18 || ^19
+
+ '@tanstack/react-table@8.21.3':
+ resolution: {integrity: sha512-5nNMTSETP4ykGegmVkhjcS8tTLW6Vl4axfEGQN3v0zdHYbK4UfoqfPChclTrJ4EoK9QynqAu9oUf8VEmrpZ5Ww==}
+ engines: {node: '>=12'}
+ peerDependencies:
+ react: '>=16.8'
+ react-dom: '>=16.8'
+
+ '@tanstack/table-core@8.21.3':
+ resolution: {integrity: sha512-ldZXEhOBb8Is7xLs01fR3YEc3DERiz5silj8tnGkFZytt1abEvl/GhUmCE0PMLaMPTa3Jk4HbKmRlHmu+gCftg==}
+ engines: {node: '>=12'}
+
+ '@testing-library/dom@10.4.1':
+ resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==}
+ engines: {node: '>=18'}
+
+ '@testing-library/jest-dom@6.9.1':
+ resolution: {integrity: sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==}
+ engines: {node: '>=14', npm: '>=6', yarn: '>=1'}
+
+ '@testing-library/react@16.3.0':
+ resolution: {integrity: sha512-kFSyxiEDwv1WLl2fgsq6pPBbw5aWKrsY2/noi1Id0TK0UParSF62oFQFGHXIyaG4pp2tEub/Zlel+fjjZILDsw==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ '@testing-library/dom': ^10.0.0
+ '@types/react': ^18.0.0 || ^19.0.0
+ '@types/react-dom': ^18.0.0 || ^19.0.0
+ react: ^18.0.0 || ^19.0.0
+ react-dom: ^18.0.0 || ^19.0.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@testing-library/user-event@14.6.1':
+ resolution: {integrity: sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==}
+ engines: {node: '>=12', npm: '>=6'}
+ peerDependencies:
+ '@testing-library/dom': '>=7.21.4'
+
+ '@types/aria-query@5.0.4':
+ resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==}
+
+ '@types/chai@5.2.3':
+ resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==}
+
+ '@types/d3-array@3.2.2':
+ resolution: {integrity: sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==}
+
+ '@types/d3-color@3.1.3':
+ resolution: {integrity: sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==}
+
+ '@types/d3-ease@3.0.2':
+ resolution: {integrity: sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==}
+
+ '@types/d3-interpolate@3.0.4':
+ resolution: {integrity: sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==}
+
+ '@types/d3-path@3.1.1':
+ resolution: {integrity: sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==}
+
+ '@types/d3-scale@4.0.9':
+ resolution: {integrity: sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==}
+
+ '@types/d3-shape@3.1.8':
+ resolution: {integrity: sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==}
+
+ '@types/d3-time@3.0.4':
+ resolution: {integrity: sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==}
+
+ '@types/d3-timer@3.0.2':
+ resolution: {integrity: sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==}
+
+ '@types/deep-eql@4.0.2':
+ resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==}
+
+ '@types/estree@1.0.8':
+ resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
+
+ '@types/json-schema@7.0.15':
+ resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
+
+ '@types/node@24.8.1':
+ resolution: {integrity: sha512-alv65KGRadQVfVcG69MuB4IzdYVpRwMG/mq8KWOaoOdyY617P5ivaDiMCGOFDWD2sAn5Q0mR3mRtUOgm99hL9Q==}
+
+ '@types/react-dom@19.2.2':
+ resolution: {integrity: sha512-9KQPoO6mZCi7jcIStSnlOWn2nEF3mNmyr3rIAsGnAbQKYbRLyqmeSc39EVgtxXVia+LMT8j3knZLAZAh+xLmrw==}
+ peerDependencies:
+ '@types/react': ^19.2.0
+
+ '@types/react@19.2.2':
+ resolution: {integrity: sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==}
+
+ '@typescript-eslint/eslint-plugin@8.46.1':
+ resolution: {integrity: sha512-rUsLh8PXmBjdiPY+Emjz9NX2yHvhS11v0SR6xNJkm5GM1MO9ea/1GoDKlHHZGrOJclL/cZ2i/vRUYVtjRhrHVQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ '@typescript-eslint/parser': ^8.46.1
+ eslint: ^8.57.0 || ^9.0.0
+ typescript: '>=4.8.4 <6.0.0'
+
+ '@typescript-eslint/parser@8.46.1':
+ resolution: {integrity: sha512-6JSSaBZmsKvEkbRUkf7Zj7dru/8ZCrJxAqArcLaVMee5907JdtEbKGsZ7zNiIm/UAkpGUkaSMZEXShnN2D1HZA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ eslint: ^8.57.0 || ^9.0.0
+ typescript: '>=4.8.4 <6.0.0'
+
+ '@typescript-eslint/project-service@8.46.1':
+ resolution: {integrity: sha512-FOIaFVMHzRskXr5J4Jp8lFVV0gz5ngv3RHmn+E4HYxSJ3DgDzU7fVI1/M7Ijh1zf6S7HIoaIOtln1H5y8V+9Zg==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ typescript: '>=4.8.4 <6.0.0'
+
+ '@typescript-eslint/scope-manager@8.46.1':
+ resolution: {integrity: sha512-weL9Gg3/5F0pVQKiF8eOXFZp8emqWzZsOJuWRUNtHT+UNV2xSJegmpCNQHy37aEQIbToTq7RHKhWvOsmbM680A==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@typescript-eslint/tsconfig-utils@8.46.1':
+ resolution: {integrity: sha512-X88+J/CwFvlJB+mK09VFqx5FE4H5cXD+H/Bdza2aEWkSb8hnWIQorNcscRl4IEo1Cz9VI/+/r/jnGWkbWPx54g==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ typescript: '>=4.8.4 <6.0.0'
+
+ '@typescript-eslint/type-utils@8.46.1':
+ resolution: {integrity: sha512-+BlmiHIiqufBxkVnOtFwjah/vrkF4MtKKvpXrKSPLCkCtAp8H01/VV43sfqA98Od7nJpDcFnkwgyfQbOG0AMvw==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ eslint: ^8.57.0 || ^9.0.0
+ typescript: '>=4.8.4 <6.0.0'
+
+ '@typescript-eslint/types@8.46.1':
+ resolution: {integrity: sha512-C+soprGBHwWBdkDpbaRC4paGBrkIXxVlNohadL5o0kfhsXqOC6GYH2S/Obmig+I0HTDl8wMaRySwrfrXVP8/pQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@typescript-eslint/typescript-estree@8.46.1':
+ resolution: {integrity: sha512-uIifjT4s8cQKFQ8ZBXXyoUODtRoAd7F7+G8MKmtzj17+1UbdzFl52AzRyZRyKqPHhgzvXunnSckVu36flGy8cg==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ typescript: '>=4.8.4 <6.0.0'
+
+ '@typescript-eslint/utils@8.46.1':
+ resolution: {integrity: sha512-vkYUy6LdZS7q1v/Gxb2Zs7zziuXN0wxqsetJdeZdRe/f5dwJFglmuvZBfTUivCtjH725C1jWCDfpadadD95EDQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ eslint: ^8.57.0 || ^9.0.0
+ typescript: '>=4.8.4 <6.0.0'
+
+ '@typescript-eslint/visitor-keys@8.46.1':
+ resolution: {integrity: sha512-ptkmIf2iDkNUjdeu2bQqhFPV1m6qTnFFjg7PPDjxKWaMaP0Z6I9l30Jr3g5QqbZGdw8YdYvLp+XnqnWWZOg/NA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@vitejs/plugin-react-swc@4.1.0':
+ resolution: {integrity: sha512-Ff690TUck0Anlh7wdIcnsVMhofeEVgm44Y4OYdeeEEPSKyZHzDI9gfVBvySEhDfXtBp8tLCbfsVKPWEMEjq8/g==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ peerDependencies:
+ vite: ^4 || ^5 || ^6 || ^7
+
+ '@vitest/coverage-v8@4.0.6':
+ resolution: {integrity: sha512-cv6pFXj9/Otk7q1Ocoj8k3BUVVwnFr3jqcqpwYrU5LkKClU9DpaMEdX+zptx/RyIJS+/VpoxMWmfISXchmVDPQ==}
+ peerDependencies:
+ '@vitest/browser': 4.0.6
+ vitest: 4.0.6
+ peerDependenciesMeta:
+ '@vitest/browser':
+ optional: true
+
+ '@vitest/expect@4.0.6':
+ resolution: {integrity: sha512-5j8UUlBVhOjhj4lR2Nt9sEV8b4WtbcYh8vnfhTNA2Kn5+smtevzjNq+xlBuVhnFGXiyPPNzGrOVvmyHWkS5QGg==}
+
+ '@vitest/mocker@4.0.6':
+ resolution: {integrity: sha512-3COEIew5HqdzBFEYN9+u0dT3i/NCwppLnO1HkjGfAP1Vs3vti1Hxm/MvcbC4DAn3Szo1M7M3otiAaT83jvqIjA==}
+ peerDependencies:
+ msw: ^2.4.9
+ vite: ^6.0.0 || ^7.0.0-0
+ peerDependenciesMeta:
+ msw:
+ optional: true
+ vite:
+ optional: true
+
+ '@vitest/pretty-format@4.0.6':
+ resolution: {integrity: sha512-4vptgNkLIA1W1Nn5X4x8rLJBzPiJwnPc+awKtfBE5hNMVsoAl/JCCPPzNrbf+L4NKgklsis5Yp2gYa+XAS442g==}
+
+ '@vitest/runner@4.0.6':
+ resolution: {integrity: sha512-trPk5qpd7Jj+AiLZbV/e+KiiaGXZ8ECsRxtnPnCrJr9OW2mLB72Cb824IXgxVz/mVU3Aj4VebY+tDTPn++j1Og==}
+
+ '@vitest/snapshot@4.0.6':
+ resolution: {integrity: sha512-PaYLt7n2YzuvxhulDDu6c9EosiRuIE+FI2ECKs6yvHyhoga+2TBWI8dwBjs+IeuQaMtZTfioa9tj3uZb7nev1g==}
+
+ '@vitest/spy@4.0.6':
+ resolution: {integrity: sha512-g9jTUYPV1LtRPRCQfhbMintW7BTQz1n6WXYQYRQ25qkyffA4bjVXjkROokZnv7t07OqfaFKw1lPzqKGk1hmNuQ==}
+
+ '@vitest/utils@4.0.6':
+ resolution: {integrity: sha512-bG43VS3iYKrMIZXBo+y8Pti0O7uNju3KvNn6DrQWhQQKcLavMB+0NZfO1/QBAEbq0MaQ3QjNsnnXlGQvsh0Z6A==}
+
+ abort-controller@3.0.0:
+ resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==}
+ engines: {node: '>=6.5'}
+
+ acorn-jsx@5.3.2:
+ resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
+ peerDependencies:
+ acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
+
+ acorn@8.15.0:
+ resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==}
+ engines: {node: '>=0.4.0'}
+ hasBin: true
+
+ agent-base@7.1.4:
+ resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==}
+ engines: {node: '>= 14'}
+
+ ajv@6.12.6:
+ resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
+
+ ansi-escapes@7.2.0:
+ resolution: {integrity: sha512-g6LhBsl+GBPRWGWsBtutpzBYuIIdBkLEvad5C/va/74Db018+5TZiyA26cZJAr3Rft5lprVqOIPxf5Vid6tqAw==}
+ engines: {node: '>=18'}
+
+ ansi-regex@5.0.1:
+ resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
+ engines: {node: '>=8'}
+
+ ansi-regex@6.2.2:
+ resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==}
+ engines: {node: '>=12'}
+
+ ansi-styles@4.3.0:
+ resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
+ engines: {node: '>=8'}
+
+ ansi-styles@5.2.0:
+ resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==}
+ engines: {node: '>=10'}
+
+ ansi-styles@6.2.3:
+ resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==}
+ engines: {node: '>=12'}
+
+ argparse@2.0.1:
+ resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
+
+ aria-hidden@1.2.6:
+ resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==}
+ engines: {node: '>=10'}
+
+ aria-query@5.3.0:
+ resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==}
+
+ assertion-error@2.0.1:
+ resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==}
+ engines: {node: '>=12'}
+
+ ast-v8-to-istanbul@0.3.8:
+ resolution: {integrity: sha512-szgSZqUxI5T8mLKvS7WTjF9is+MVbOeLADU73IseOcrqhxr/VAvy6wfoVE39KnKzA7JRhjF5eUagNlHwvZPlKQ==}
+
+ asynckit@0.4.0:
+ resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
+
+ axios@1.13.2:
+ resolution: {integrity: sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==}
+
+ balanced-match@1.0.2:
+ resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
+
+ bidi-js@1.0.3:
+ resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==}
+
+ brace-expansion@1.1.12:
+ resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==}
+
+ brace-expansion@2.0.2:
+ resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==}
+
+ braces@3.0.3:
+ resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
+ engines: {node: '>=8'}
+
+ call-bind-apply-helpers@1.0.2:
+ resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
+ engines: {node: '>= 0.4'}
+
+ callsites@3.1.0:
+ resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
+ engines: {node: '>=6'}
+
+ chai@6.2.0:
+ resolution: {integrity: sha512-aUTnJc/JipRzJrNADXVvpVqi6CO0dn3nx4EVPxijri+fj3LUUDyZQOgVeW54Ob3Y1Xh9Iz8f+CgaCl8v0mn9bA==}
+ engines: {node: '>=18'}
+
+ chalk@4.1.2:
+ resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
+ engines: {node: '>=10'}
+
+ chownr@3.0.0:
+ resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==}
+ engines: {node: '>=18'}
+
+ class-variance-authority@0.7.1:
+ resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==}
+
+ cli-cursor@5.0.0:
+ resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==}
+ engines: {node: '>=18'}
+
+ cli-truncate@5.1.1:
+ resolution: {integrity: sha512-SroPvNHxUnk+vIW/dOSfNqdy1sPEFkrTk6TUtqLCnBlo3N7TNYYkzzN7uSD6+jVjrdO4+p8nH7JzH6cIvUem6A==}
+ engines: {node: '>=20'}
+
+ clsx@2.1.1:
+ resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
+ engines: {node: '>=6'}
+
+ cmdk@1.1.1:
+ resolution: {integrity: sha512-Vsv7kFaXm+ptHDMZ7izaRsP70GgrW9NBNGswt9OZaVBLlE0SNpDq8eu/VGXyF9r7M0azK3Wy7OlYXsuyYLFzHg==}
+ peerDependencies:
+ react: ^18 || ^19 || ^19.0.0-rc
+ react-dom: ^18 || ^19 || ^19.0.0-rc
+
+ color-convert@2.0.1:
+ resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
+ engines: {node: '>=7.0.0'}
+
+ color-name@1.1.4:
+ resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
+
+ colorette@2.0.20:
+ resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==}
+
+ combined-stream@1.0.8:
+ resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
+ engines: {node: '>= 0.8'}
+
+ commander@14.0.2:
+ resolution: {integrity: sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==}
+ engines: {node: '>=20'}
+
+ concat-map@0.0.1:
+ resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
+
+ cookie@1.0.2:
+ resolution: {integrity: sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==}
+ engines: {node: '>=18'}
+
+ cross-spawn@7.0.6:
+ resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
+ engines: {node: '>= 8'}
+
+ css-tree@3.1.0:
+ resolution: {integrity: sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==}
+ engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0}
+
+ css.escape@1.5.1:
+ resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==}
+
+ cssstyle@5.3.3:
+ resolution: {integrity: sha512-OytmFH+13/QXONJcC75QNdMtKpceNk3u8ThBjyyYjkEcy/ekBwR1mMAuNvi3gdBPW3N5TlCzQ0WZw8H0lN/bDw==}
+ engines: {node: '>=20'}
+
+ csstype@3.1.3:
+ resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
+
+ d3-array@3.2.4:
+ resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==}
+ engines: {node: '>=12'}
+
+ d3-color@3.1.0:
+ resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==}
+ engines: {node: '>=12'}
+
+ d3-ease@3.0.1:
+ resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==}
+ engines: {node: '>=12'}
+
+ d3-format@3.1.2:
+ resolution: {integrity: sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==}
+ engines: {node: '>=12'}
+
+ d3-interpolate@3.0.1:
+ resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==}
+ engines: {node: '>=12'}
+
+ d3-path@3.1.0:
+ resolution: {integrity: sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==}
+ engines: {node: '>=12'}
+
+ d3-scale@4.0.2:
+ resolution: {integrity: sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==}
+ engines: {node: '>=12'}
+
+ d3-shape@3.2.0:
+ resolution: {integrity: sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==}
+ engines: {node: '>=12'}
+
+ d3-time-format@4.1.0:
+ resolution: {integrity: sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==}
+ engines: {node: '>=12'}
+
+ d3-time@3.1.0:
+ resolution: {integrity: sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==}
+ engines: {node: '>=12'}
+
+ d3-timer@3.0.1:
+ resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==}
+ engines: {node: '>=12'}
+
+ data-urls@6.0.0:
+ resolution: {integrity: sha512-BnBS08aLUM+DKamupXs3w2tJJoqU+AkaE/+6vQxi/G/DPmIZFJJp9Dkb1kM03AZx8ADehDUZgsNxju3mPXZYIA==}
+ engines: {node: '>=20'}
+
+ date-fns-jalali@4.1.0-0:
+ resolution: {integrity: sha512-hTIP/z+t+qKwBDcmmsnmjWTduxCg+5KfdqWQvb2X/8C9+knYY6epN/pfxdDuyVlSVeFz0sM5eEfwIUQ70U4ckg==}
+
+ date-fns@4.1.0:
+ resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==}
+
+ debug@4.4.3:
+ resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
+ engines: {node: '>=6.0'}
+ peerDependencies:
+ supports-color: '*'
+ peerDependenciesMeta:
+ supports-color:
+ optional: true
+
+ decimal.js-light@2.5.1:
+ resolution: {integrity: sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==}
+
+ decimal.js@10.6.0:
+ resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==}
+
+ deep-is@0.1.4:
+ resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
+
+ delayed-stream@1.0.0:
+ resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
+ engines: {node: '>=0.4.0'}
+
+ dequal@2.0.3:
+ resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
+ engines: {node: '>=6'}
+
+ detect-libc@2.1.2:
+ resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
+ engines: {node: '>=8'}
+
+ detect-node-es@1.1.0:
+ resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==}
+
+ dom-accessibility-api@0.5.16:
+ resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==}
+
+ dom-accessibility-api@0.6.3:
+ resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==}
+
+ dom-helpers@5.2.1:
+ resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==}
+
+ dunder-proto@1.0.1:
+ resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
+ engines: {node: '>= 0.4'}
+
+ embla-carousel-react@8.6.0:
+ resolution: {integrity: sha512-0/PjqU7geVmo6F734pmPqpyHqiM99olvyecY7zdweCw+6tKEXnrE90pBiBbMMU8s5tICemzpQ3hi5EpxzGW+JA==}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.1 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
+
+ embla-carousel-reactive-utils@8.6.0:
+ resolution: {integrity: sha512-fMVUDUEx0/uIEDM0Mz3dHznDhfX+znCCDCeIophYb1QGVM7YThSWX+wz11zlYwWFOr74b4QLGg0hrGPJeG2s4A==}
+ peerDependencies:
+ embla-carousel: 8.6.0
+
+ embla-carousel@8.6.0:
+ resolution: {integrity: sha512-SjWyZBHJPbqxHOzckOfo8lHisEaJWmwd23XppYFYVh10bU66/Pn5tkVkbkCMZVdbUE5eTCI2nD8OyIP4Z+uwkA==}
+
+ emoji-regex@10.6.0:
+ resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==}
+
+ enhanced-resolve@5.18.3:
+ resolution: {integrity: sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==}
+ engines: {node: '>=10.13.0'}
+
+ entities@6.0.1:
+ resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==}
+ engines: {node: '>=0.12'}
+
+ environment@1.1.0:
+ resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==}
+ engines: {node: '>=18'}
+
+ es-define-property@1.0.1:
+ resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==}
+ engines: {node: '>= 0.4'}
+
+ es-errors@1.3.0:
+ resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
+ engines: {node: '>= 0.4'}
+
+ es-module-lexer@1.7.0:
+ resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==}
+
+ es-object-atoms@1.1.1:
+ resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==}
+ engines: {node: '>= 0.4'}
+
+ es-set-tostringtag@2.1.0:
+ resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==}
+ engines: {node: '>= 0.4'}
+
+ esbuild@0.25.11:
+ resolution: {integrity: sha512-KohQwyzrKTQmhXDW1PjCv3Tyspn9n5GcY2RTDqeORIdIJY8yKIF7sTSopFmn/wpMPW4rdPXI0UE5LJLuq3bx0Q==}
+ engines: {node: '>=18'}
+ hasBin: true
+
+ escape-string-regexp@4.0.0:
+ resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
+ engines: {node: '>=10'}
+
+ eslint-config-prettier@10.1.8:
+ resolution: {integrity: sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==}
+ hasBin: true
+ peerDependencies:
+ eslint: '>=7.0.0'
+
+ eslint-plugin-prettier@5.5.4:
+ resolution: {integrity: sha512-swNtI95SToIz05YINMA6Ox5R057IMAmWZ26GqPxusAp1TZzj+IdY9tXNWWD3vkF/wEqydCONcwjTFpxybBqZsg==}
+ engines: {node: ^14.18.0 || >=16.0.0}
+ peerDependencies:
+ '@types/eslint': '>=8.0.0'
+ eslint: '>=8.0.0'
+ eslint-config-prettier: '>= 7.0.0 <10.0.0 || >=10.1.0'
+ prettier: '>=3.0.0'
+ peerDependenciesMeta:
+ '@types/eslint':
+ optional: true
+ eslint-config-prettier:
+ optional: true
+
+ eslint-plugin-react-hooks@5.2.0:
+ resolution: {integrity: sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==}
+ engines: {node: '>=10'}
+ peerDependencies:
+ eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0
+
+ eslint-plugin-react-refresh@0.4.24:
+ resolution: {integrity: sha512-nLHIW7TEq3aLrEYWpVaJ1dRgFR+wLDPN8e8FpYAql/bMV2oBEfC37K0gLEGgv9fy66juNShSMV8OkTqzltcG/w==}
+ peerDependencies:
+ eslint: '>=8.40'
+
+ eslint-scope@8.4.0:
+ resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ eslint-visitor-keys@3.4.3:
+ resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+
+ eslint-visitor-keys@4.2.1:
+ resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ eslint@9.37.0:
+ resolution: {integrity: sha512-XyLmROnACWqSxiGYArdef1fItQd47weqB7iwtfr9JHwRrqIXZdcFMvvEcL9xHCmL0SNsOvF0c42lWyM1U5dgig==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ hasBin: true
+ peerDependencies:
+ jiti: '*'
+ peerDependenciesMeta:
+ jiti:
+ optional: true
+
+ espree@10.4.0:
+ resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ esquery@1.6.0:
+ resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==}
+ engines: {node: '>=0.10'}
+
+ esrecurse@4.3.0:
+ resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==}
+ engines: {node: '>=4.0'}
+
+ estraverse@5.3.0:
+ resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==}
+ engines: {node: '>=4.0'}
+
+ estree-walker@3.0.3:
+ resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==}
+
+ esutils@2.0.3:
+ resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
+ engines: {node: '>=0.10.0'}
+
+ event-target-shim@5.0.1:
+ resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==}
+ engines: {node: '>=6'}
+
+ eventemitter3@4.0.7:
+ resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==}
+
+ eventemitter3@5.0.1:
+ resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==}
+
+ eventsource@2.0.2:
+ resolution: {integrity: sha512-IzUmBGPR3+oUG9dUeXynyNmf91/3zUSJg1lCktzKw47OXuhco54U3r9B7O4XX+Rb1Itm9OZ2b0RkTs10bICOxA==}
+ engines: {node: '>=12.0.0'}
+
+ expect-type@1.2.2:
+ resolution: {integrity: sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==}
+ engines: {node: '>=12.0.0'}
+
+ fast-deep-equal@3.1.3:
+ resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
+
+ fast-diff@1.3.0:
+ resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==}
+
+ fast-equals@5.4.0:
+ resolution: {integrity: sha512-jt2DW/aNFNwke7AUd+Z+e6pz39KO5rzdbbFCg2sGafS4mk13MI7Z8O5z9cADNn5lhGODIgLwug6TZO2ctf7kcw==}
+ engines: {node: '>=6.0.0'}
+
+ fast-glob@3.3.3:
+ resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==}
+ engines: {node: '>=8.6.0'}
+
+ fast-json-stable-stringify@2.1.0:
+ resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==}
+
+ fast-levenshtein@2.0.6:
+ resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
+
+ fastq@1.19.1:
+ resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==}
+
+ fdir@6.5.0:
+ resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==}
+ engines: {node: '>=12.0.0'}
+ peerDependencies:
+ picomatch: ^3 || ^4
+ peerDependenciesMeta:
+ picomatch:
+ optional: true
+
+ fetch-cookie@2.2.0:
+ resolution: {integrity: sha512-h9AgfjURuCgA2+2ISl8GbavpUdR+WGAM2McW/ovn4tVccegp8ZqCKWSBR8uRdM8dDNlx5WdKRWxBYUwteLDCNQ==}
+
+ file-entry-cache@8.0.0:
+ resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==}
+ engines: {node: '>=16.0.0'}
+
+ fill-range@7.1.1:
+ resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
+ engines: {node: '>=8'}
+
+ find-up@5.0.0:
+ resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
+ engines: {node: '>=10'}
+
+ flat-cache@4.0.1:
+ resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==}
+ engines: {node: '>=16'}
+
+ flatted@3.3.3:
+ resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==}
+
+ follow-redirects@1.15.11:
+ resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==}
+ engines: {node: '>=4.0'}
+ peerDependencies:
+ debug: '*'
+ peerDependenciesMeta:
+ debug:
+ optional: true
+
+ form-data@4.0.5:
+ resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==}
+ engines: {node: '>= 6'}
+
+ fsevents@2.3.3:
+ resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
+ engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
+ os: [darwin]
+
+ function-bind@1.1.2:
+ resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
+
+ get-east-asian-width@1.4.0:
+ resolution: {integrity: sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==}
+ engines: {node: '>=18'}
+
+ get-intrinsic@1.3.0:
+ resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==}
+ engines: {node: '>= 0.4'}
+
+ get-nonce@1.0.1:
+ resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==}
+ engines: {node: '>=6'}
+
+ get-proto@1.0.1:
+ resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==}
+ engines: {node: '>= 0.4'}
+
+ glob-parent@5.1.2:
+ resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
+ engines: {node: '>= 6'}
+
+ glob-parent@6.0.2:
+ resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
+ engines: {node: '>=10.13.0'}
+
+ globals@14.0.0:
+ resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==}
+ engines: {node: '>=18'}
+
+ globals@16.4.0:
+ resolution: {integrity: sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==}
+ engines: {node: '>=18'}
+
+ gopd@1.2.0:
+ resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==}
+ engines: {node: '>= 0.4'}
+
+ graceful-fs@4.2.11:
+ resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
+
+ graphemer@1.4.0:
+ resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==}
+
+ has-flag@4.0.0:
+ resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
+ engines: {node: '>=8'}
+
+ has-symbols@1.1.0:
+ resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==}
+ engines: {node: '>= 0.4'}
+
+ has-tostringtag@1.0.2:
+ resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==}
+ engines: {node: '>= 0.4'}
+
+ hasown@2.0.2:
+ resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
+ engines: {node: '>= 0.4'}
+
+ html-encoding-sniffer@4.0.0:
+ resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==}
+ engines: {node: '>=18'}
+
+ html-escaper@2.0.2:
+ resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==}
+
+ http-proxy-agent@7.0.2:
+ resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==}
+ engines: {node: '>= 14'}
+
+ https-proxy-agent@7.0.6:
+ resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==}
+ engines: {node: '>= 14'}
+
+ husky@9.1.7:
+ resolution: {integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==}
+ engines: {node: '>=18'}
+ hasBin: true
+
+ iconv-lite@0.6.3:
+ resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
+ engines: {node: '>=0.10.0'}
+
+ ignore@5.3.2:
+ resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
+ engines: {node: '>= 4'}
+
+ ignore@7.0.5:
+ resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==}
+ engines: {node: '>= 4'}
+
+ import-fresh@3.3.1:
+ resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==}
+ engines: {node: '>=6'}
+
+ imurmurhash@0.1.4:
+ resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
+ engines: {node: '>=0.8.19'}
+
+ indent-string@4.0.0:
+ resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==}
+ engines: {node: '>=8'}
+
+ internmap@2.0.3:
+ resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==}
+ engines: {node: '>=12'}
+
+ is-extglob@2.1.1:
+ resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
+ engines: {node: '>=0.10.0'}
+
+ is-fullwidth-code-point@5.1.0:
+ resolution: {integrity: sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==}
+ engines: {node: '>=18'}
+
+ is-glob@4.0.3:
+ resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
+ engines: {node: '>=0.10.0'}
+
+ is-number@7.0.0:
+ resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
+ engines: {node: '>=0.12.0'}
+
+ is-potential-custom-element-name@1.0.1:
+ resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==}
+
+ isexe@2.0.0:
+ resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
+
+ istanbul-lib-coverage@3.2.2:
+ resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==}
+ engines: {node: '>=8'}
+
+ istanbul-lib-report@3.0.1:
+ resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==}
+ engines: {node: '>=10'}
+
+ istanbul-lib-source-maps@5.0.6:
+ resolution: {integrity: sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==}
+ engines: {node: '>=10'}
+
+ istanbul-reports@3.2.0:
+ resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==}
+ engines: {node: '>=8'}
+
+ jiti@2.6.1:
+ resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==}
+ hasBin: true
+
+ js-tokens@4.0.0:
+ resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
+
+ js-tokens@9.0.1:
+ resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==}
+
+ js-yaml@4.1.0:
+ resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
+ hasBin: true
+
+ jsdom@27.2.0:
+ resolution: {integrity: sha512-454TI39PeRDW1LgpyLPyURtB4Zx1tklSr6+OFOipsxGUH1WMTvk6C65JQdrj455+DP2uJ1+veBEHTGFKWVLFoA==}
+ engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
+ peerDependencies:
+ canvas: ^3.0.0
+ peerDependenciesMeta:
+ canvas:
+ optional: true
+
+ json-buffer@3.0.1:
+ resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==}
+
+ json-schema-traverse@0.4.1:
+ resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
+
+ json-stable-stringify-without-jsonify@1.0.1:
+ resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
+
+ jwt-decode@4.0.0:
+ resolution: {integrity: sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==}
+ engines: {node: '>=18'}
+
+ keyv@4.5.4:
+ resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
+
+ levn@0.4.1:
+ resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
+ engines: {node: '>= 0.8.0'}
+
+ lightningcss-darwin-arm64@1.30.1:
+ resolution: {integrity: sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [darwin]
+
+ lightningcss-darwin-x64@1.30.1:
+ resolution: {integrity: sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [darwin]
+
+ lightningcss-freebsd-x64@1.30.1:
+ resolution: {integrity: sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [freebsd]
+
+ lightningcss-linux-arm-gnueabihf@1.30.1:
+ resolution: {integrity: sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm]
+ os: [linux]
+
+ lightningcss-linux-arm64-gnu@1.30.1:
+ resolution: {integrity: sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [linux]
+
+ lightningcss-linux-arm64-musl@1.30.1:
+ resolution: {integrity: sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [linux]
+
+ lightningcss-linux-x64-gnu@1.30.1:
+ resolution: {integrity: sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [linux]
+
+ lightningcss-linux-x64-musl@1.30.1:
+ resolution: {integrity: sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [linux]
+
+ lightningcss-win32-arm64-msvc@1.30.1:
+ resolution: {integrity: sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [win32]
+
+ lightningcss-win32-x64-msvc@1.30.1:
+ resolution: {integrity: sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [win32]
+
+ lightningcss@1.30.1:
+ resolution: {integrity: sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==}
+ engines: {node: '>= 12.0.0'}
+
+ lint-staged@16.2.6:
+ resolution: {integrity: sha512-s1gphtDbV4bmW1eylXpVMk2u7is7YsrLl8hzrtvC70h4ByhcMLZFY01Fx05ZUDNuv1H8HO4E+e2zgejV1jVwNw==}
+ engines: {node: '>=20.17'}
+ hasBin: true
+
+ listr2@9.0.5:
+ resolution: {integrity: sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g==}
+ engines: {node: '>=20.0.0'}
+
+ locate-path@6.0.0:
+ resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
+ engines: {node: '>=10'}
+
+ lodash.merge@4.6.2:
+ resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
+
+ lodash@4.17.21:
+ resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
+
+ log-update@6.1.0:
+ resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==}
+ engines: {node: '>=18'}
+
+ loose-envify@1.4.0:
+ resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
+ hasBin: true
+
+ lru-cache@11.2.2:
+ resolution: {integrity: sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==}
+ engines: {node: 20 || >=22}
+
+ lucide-react@0.546.0:
+ resolution: {integrity: sha512-Z94u6fKT43lKeYHiVyvyR8fT7pwCzDu7RyMPpTvh054+xahSgj4HFQ+NmflvzdXsoAjYGdCguGaFKYuvq0ThCQ==}
+ peerDependencies:
+ react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0
+
+ lz-string@1.5.0:
+ resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==}
+ hasBin: true
+
+ magic-string@0.30.19:
+ resolution: {integrity: sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==}
+
+ magicast@0.3.5:
+ resolution: {integrity: sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==}
+
+ make-dir@4.0.0:
+ resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==}
+ engines: {node: '>=10'}
+
+ math-intrinsics@1.1.0:
+ resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
+ engines: {node: '>= 0.4'}
+
+ mdn-data@2.12.2:
+ resolution: {integrity: sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==}
+
+ merge2@1.4.1:
+ resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
+ engines: {node: '>= 8'}
+
+ micromatch@4.0.8:
+ resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
+ engines: {node: '>=8.6'}
+
+ mime-db@1.52.0:
+ resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
+ engines: {node: '>= 0.6'}
+
+ mime-types@2.1.35:
+ resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
+ engines: {node: '>= 0.6'}
+
+ mimic-function@5.0.1:
+ resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==}
+ engines: {node: '>=18'}
+
+ min-indent@1.0.1:
+ resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==}
+ engines: {node: '>=4'}
+
+ minimatch@3.1.2:
+ resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
+
+ minimatch@9.0.5:
+ resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==}
+ engines: {node: '>=16 || 14 >=14.17'}
+
+ minipass@7.1.2:
+ resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==}
+ engines: {node: '>=16 || 14 >=14.17'}
+
+ minizlib@3.1.0:
+ resolution: {integrity: sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==}
+ engines: {node: '>= 18'}
+
+ ms@2.1.3:
+ resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
+
+ nano-spawn@2.0.0:
+ resolution: {integrity: sha512-tacvGzUY5o2D8CBh2rrwxyNojUsZNU2zjNTzKQrkgGJQTbGAfArVWXSKMBokBeeg6C7OLRGUEyoFlYbfeWQIqw==}
+ engines: {node: '>=20.17'}
+
+ nanoid@3.3.11:
+ resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
+ engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
+ hasBin: true
+
+ natural-compare@1.4.0:
+ resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
+
+ next-themes@0.4.6:
+ resolution: {integrity: sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA==}
+ peerDependencies:
+ react: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc
+
+ node-fetch@2.7.0:
+ resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==}
+ engines: {node: 4.x || >=6.0.0}
+ peerDependencies:
+ encoding: ^0.1.0
+ peerDependenciesMeta:
+ encoding:
+ optional: true
+
+ normalize-wheel@1.0.1:
+ resolution: {integrity: sha512-1OnlAPZ3zgrk8B91HyRj+eVv+kS5u+Z0SCsak6Xil/kmgEia50ga7zfkumayonZrImffAxPU/5WcyGhzetHNPA==}
+
+ object-assign@4.1.1:
+ resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
+ engines: {node: '>=0.10.0'}
+
+ onetime@7.0.0:
+ resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==}
+ engines: {node: '>=18'}
+
+ optionator@0.9.4:
+ resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
+ engines: {node: '>= 0.8.0'}
+
+ p-limit@3.1.0:
+ resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
+ engines: {node: '>=10'}
+
+ p-locate@5.0.0:
+ resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
+ engines: {node: '>=10'}
+
+ parent-module@1.0.1:
+ resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
+ engines: {node: '>=6'}
+
+ parse5@8.0.0:
+ resolution: {integrity: sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA==}
+
+ path-exists@4.0.0:
+ resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
+ engines: {node: '>=8'}
+
+ path-key@3.1.1:
+ resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
+ engines: {node: '>=8'}
+
+ pathe@2.0.3:
+ resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}
+
+ picocolors@1.1.1:
+ resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
+
+ picomatch@2.3.1:
+ resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
+ engines: {node: '>=8.6'}
+
+ picomatch@4.0.3:
+ resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==}
+ engines: {node: '>=12'}
+
+ pidtree@0.6.0:
+ resolution: {integrity: sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==}
+ engines: {node: '>=0.10'}
+ hasBin: true
+
+ postcss@8.5.6:
+ resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==}
+ engines: {node: ^10 || ^12 || >=14}
+
+ prelude-ls@1.2.1:
+ resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
+ engines: {node: '>= 0.8.0'}
+
+ prettier-linter-helpers@1.0.0:
+ resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==}
+ engines: {node: '>=6.0.0'}
+
+ prettier@3.6.2:
+ resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==}
+ engines: {node: '>=14'}
+ hasBin: true
+
+ pretty-format@27.5.1:
+ resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==}
+ engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+ prop-types@15.8.1:
+ resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==}
+
+ proxy-from-env@1.1.0:
+ resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
+
+ psl@1.15.0:
+ resolution: {integrity: sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==}
+
+ punycode@2.3.1:
+ resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
+ engines: {node: '>=6'}
+
+ querystringify@2.2.0:
+ resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==}
+
+ queue-microtask@1.2.3:
+ resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
+
+ radix-ui@1.4.3:
+ resolution: {integrity: sha512-aWizCQiyeAenIdUbqEpXgRA1ya65P13NKn/W8rWkcN0OPkRDxdBVLWnIEDsS2RpwCK2nobI7oMUSmexzTDyAmA==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ react-day-picker@9.11.1:
+ resolution: {integrity: sha512-l3ub6o8NlchqIjPKrRFUCkTUEq6KwemQlfv3XZzzwpUeGwmDJ+0u0Upmt38hJyd7D/vn2dQoOoLV/qAp0o3uUw==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ react: '>=16.8.0'
+
+ react-dom@19.2.0:
+ resolution: {integrity: sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==}
+ peerDependencies:
+ react: ^19.2.0
+
+ react-easy-crop@5.5.6:
+ resolution: {integrity: sha512-Jw3/ozs8uXj3NpL511Suc4AHY+mLRO23rUgipXvNYKqezcFSYHxe4QXibBymkOoY6oOtLVMPO2HNPRHYvMPyTw==}
+ peerDependencies:
+ react: '>=16.4.0'
+ react-dom: '>=16.4.0'
+
+ react-hook-form@7.65.0:
+ resolution: {integrity: sha512-xtOzDz063WcXvGWaHgLNrNzlsdFgtUWcb32E6WFaGTd7kPZG3EeDusjdZfUsPwKCKVXy1ZlntifaHZ4l8pAsmw==}
+ engines: {node: '>=18.0.0'}
+ peerDependencies:
+ react: ^16.8.0 || ^17 || ^18 || ^19
+
+ react-is@16.13.1:
+ resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
+
+ react-is@17.0.2:
+ resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==}
+
+ react-is@18.3.1:
+ resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==}
+
+ react-remove-scroll-bar@2.3.8:
+ resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==}
+ engines: {node: '>=10'}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ react-remove-scroll@2.7.1:
+ resolution: {integrity: sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA==}
+ engines: {node: '>=10'}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ react-router@7.9.4:
+ resolution: {integrity: sha512-SD3G8HKviFHg9xj7dNODUKDFgpG4xqD5nhyd0mYoB5iISepuZAvzSr8ywxgxKJ52yRzf/HWtVHc9AWwoTbljvA==}
+ engines: {node: '>=20.0.0'}
+ peerDependencies:
+ react: '>=18'
+ react-dom: '>=18'
+ peerDependenciesMeta:
+ react-dom:
+ optional: true
+
+ react-smooth@4.0.4:
+ resolution: {integrity: sha512-gnGKTpYwqL0Iii09gHobNolvX4Kiq4PKx6eWBCYYix+8cdw+cGo3do906l1NBPKkSWx1DghC1dlWG9L2uGd61Q==}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+ react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+
+ react-style-singleton@2.2.3:
+ resolution: {integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==}
+ engines: {node: '>=10'}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ react-transition-group@4.4.5:
+ resolution: {integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==}
+ peerDependencies:
+ react: '>=16.6.0'
+ react-dom: '>=16.6.0'
+
+ react@19.2.0:
+ resolution: {integrity: sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==}
+ engines: {node: '>=0.10.0'}
+
+ recharts-scale@0.4.5:
+ resolution: {integrity: sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==}
+
+ recharts@2.15.4:
+ resolution: {integrity: sha512-UT/q6fwS3c1dHbXv2uFgYJ9BMFHu3fwnd7AYZaEQhXuYQ4hgsxLvsUXzGdKeZrW5xopzDCvuA2N41WJ88I7zIw==}
+ engines: {node: '>=14'}
+ peerDependencies:
+ react: ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+ react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+
+ redent@3.0.0:
+ resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==}
+ engines: {node: '>=8'}
+
+ require-from-string@2.0.2:
+ resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
+ engines: {node: '>=0.10.0'}
+
+ requires-port@1.0.0:
+ resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==}
+
+ resolve-from@4.0.0:
+ resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
+ engines: {node: '>=4'}
+
+ restore-cursor@5.1.0:
+ resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==}
+ engines: {node: '>=18'}
+
+ reusify@1.1.0:
+ resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==}
+ engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
+
+ rfdc@1.4.1:
+ resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==}
+
+ rollup@4.52.4:
+ resolution: {integrity: sha512-CLEVl+MnPAiKh5pl4dEWSyMTpuflgNQiLGhMv8ezD5W/qP8AKvmYpCOKRRNOh7oRKnauBZ4SyeYkMS+1VSyKwQ==}
+ engines: {node: '>=18.0.0', npm: '>=8.0.0'}
+ hasBin: true
+
+ run-parallel@1.2.0:
+ resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
+
+ safer-buffer@2.1.2:
+ resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
+
+ saxes@6.0.0:
+ resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==}
+ engines: {node: '>=v12.22.7'}
+
+ scheduler@0.27.0:
+ resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==}
+
+ semver@7.7.3:
+ resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==}
+ engines: {node: '>=10'}
+ hasBin: true
+
+ set-cookie-parser@2.7.1:
+ resolution: {integrity: sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==}
+
+ shebang-command@2.0.0:
+ resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
+ engines: {node: '>=8'}
+
+ shebang-regex@3.0.0:
+ resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
+ engines: {node: '>=8'}
+
+ siginfo@2.0.0:
+ resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==}
+
+ signal-exit@4.1.0:
+ resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
+ engines: {node: '>=14'}
+
+ slice-ansi@7.1.2:
+ resolution: {integrity: sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==}
+ engines: {node: '>=18'}
+
+ sonner@2.0.7:
+ resolution: {integrity: sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w==}
+ peerDependencies:
+ react: ^18.0.0 || ^19.0.0 || ^19.0.0-rc
+ react-dom: ^18.0.0 || ^19.0.0 || ^19.0.0-rc
+
+ source-map-js@1.2.1:
+ resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
+ engines: {node: '>=0.10.0'}
+
+ stackback@0.0.2:
+ resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==}
+
+ std-env@3.10.0:
+ resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==}
+
+ string-argv@0.3.2:
+ resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==}
+ engines: {node: '>=0.6.19'}
+
+ string-width@7.2.0:
+ resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==}
+ engines: {node: '>=18'}
+
+ string-width@8.1.0:
+ resolution: {integrity: sha512-Kxl3KJGb/gxkaUMOjRsQ8IrXiGW75O4E3RPjFIINOVH8AMl2SQ/yWdTzWwF3FevIX9LcMAjJW+GRwAlAbTSXdg==}
+ engines: {node: '>=20'}
+
+ strip-ansi@7.1.2:
+ resolution: {integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==}
+ engines: {node: '>=12'}
+
+ strip-indent@3.0.0:
+ resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==}
+ engines: {node: '>=8'}
+
+ strip-json-comments@3.1.1:
+ resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
+ engines: {node: '>=8'}
+
+ supports-color@7.2.0:
+ resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
+ engines: {node: '>=8'}
+
+ symbol-tree@3.2.4:
+ resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==}
+
+ synckit@0.11.11:
+ resolution: {integrity: sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==}
+ engines: {node: ^14.18.0 || >=16.0.0}
+
+ tailwind-merge@3.3.1:
+ resolution: {integrity: sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g==}
+
+ tailwindcss@4.1.14:
+ resolution: {integrity: sha512-b7pCxjGO98LnxVkKjaZSDeNuljC4ueKUddjENJOADtubtdo8llTaJy7HwBMeLNSSo2N5QIAgklslK1+Ir8r6CA==}
+
+ tapable@2.3.0:
+ resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==}
+ engines: {node: '>=6'}
+
+ tar@7.5.1:
+ resolution: {integrity: sha512-nlGpxf+hv0v7GkWBK2V9spgactGOp0qvfWRxUMjqHyzrt3SgwE48DIv/FhqPHJYLHpgW1opq3nERbz5Anq7n1g==}
+ engines: {node: '>=18'}
+
+ tiny-invariant@1.3.3:
+ resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==}
+
+ tinybench@2.9.0:
+ resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==}
+
+ tinyexec@0.3.2:
+ resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==}
+
+ tinyglobby@0.2.15:
+ resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==}
+ engines: {node: '>=12.0.0'}
+
+ tinyrainbow@3.0.3:
+ resolution: {integrity: sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==}
+ engines: {node: '>=14.0.0'}
+
+ tldts-core@7.0.17:
+ resolution: {integrity: sha512-DieYoGrP78PWKsrXr8MZwtQ7GLCUeLxihtjC1jZsW1DnvSMdKPitJSe8OSYDM2u5H6g3kWJZpePqkp43TfLh0g==}
+
+ tldts@7.0.17:
+ resolution: {integrity: sha512-Y1KQBgDd/NUc+LfOtKS6mNsC9CCaH+m2P1RoIZy7RAPo3C3/t8X45+zgut31cRZtZ3xKPjfn3TkGTrctC2TQIQ==}
+ hasBin: true
+
+ to-regex-range@5.0.1:
+ resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
+ engines: {node: '>=8.0'}
+
+ tough-cookie@4.1.4:
+ resolution: {integrity: sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==}
+ engines: {node: '>=6'}
+
+ tough-cookie@6.0.0:
+ resolution: {integrity: sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w==}
+ engines: {node: '>=16'}
+
+ tr46@0.0.3:
+ resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
+
+ tr46@6.0.0:
+ resolution: {integrity: sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==}
+ engines: {node: '>=20'}
+
+ ts-api-utils@2.1.0:
+ resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==}
+ engines: {node: '>=18.12'}
+ peerDependencies:
+ typescript: '>=4.8.4'
+
+ tslib@2.8.1:
+ resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
+
+ tw-animate-css@1.4.0:
+ resolution: {integrity: sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ==}
+
+ type-check@0.4.0:
+ resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
+ engines: {node: '>= 0.8.0'}
+
+ typescript-eslint@8.46.1:
+ resolution: {integrity: sha512-VHgijW803JafdSsDO8I761r3SHrgk4T00IdyQ+/UsthtgPRsBWQLqoSxOolxTpxRKi1kGXK0bSz4CoAc9ObqJA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ eslint: ^8.57.0 || ^9.0.0
+ typescript: '>=4.8.4 <6.0.0'
+
+ typescript@5.9.3:
+ resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==}
+ engines: {node: '>=14.17'}
+ hasBin: true
+
+ undici-types@7.14.0:
+ resolution: {integrity: sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA==}
+
+ universalify@0.2.0:
+ resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==}
+ engines: {node: '>= 4.0.0'}
+
+ uri-js@4.4.1:
+ resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
+
+ url-parse@1.5.10:
+ resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==}
+
+ use-callback-ref@1.3.3:
+ resolution: {integrity: sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==}
+ engines: {node: '>=10'}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ use-sidecar@1.1.3:
+ resolution: {integrity: sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==}
+ engines: {node: '>=10'}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ use-sync-external-store@1.6.0:
+ resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+
+ vaul@1.1.2:
+ resolution: {integrity: sha512-ZFkClGpWyI2WUQjdLJ/BaGuV6AVQiJ3uELGk3OYtP+B6yCO7Cmn9vPFXVJkRaGkOJu3m8bQMgtyzNHixULceQA==}
+ peerDependencies:
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc
+
+ victory-vendor@36.9.2:
+ resolution: {integrity: sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==}
+
+ vite@7.1.10:
+ resolution: {integrity: sha512-CmuvUBzVJ/e3HGxhg6cYk88NGgTnBoOo7ogtfJJ0fefUWAxN/WDSUa50o+oVBxuIhO8FoEZW0j2eW7sfjs5EtA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ hasBin: true
+ peerDependencies:
+ '@types/node': ^20.19.0 || >=22.12.0
+ jiti: '>=1.21.0'
+ less: ^4.0.0
+ lightningcss: ^1.21.0
+ sass: ^1.70.0
+ sass-embedded: ^1.70.0
+ stylus: '>=0.54.8'
+ sugarss: ^5.0.0
+ terser: ^5.16.0
+ tsx: ^4.8.1
+ yaml: ^2.4.2
+ peerDependenciesMeta:
+ '@types/node':
+ optional: true
+ jiti:
+ optional: true
+ less:
+ optional: true
+ lightningcss:
+ optional: true
+ sass:
+ optional: true
+ sass-embedded:
+ optional: true
+ stylus:
+ optional: true
+ sugarss:
+ optional: true
+ terser:
+ optional: true
+ tsx:
+ optional: true
+ yaml:
+ optional: true
+
+ vitest-localstorage-mock@0.1.2:
+ resolution: {integrity: sha512-1oee6iDWhhquzVogssbpwQi6a2F3L+nCKF2+qqyCs5tH0sOYRyTqnsfj2dtmEQiL4xtJkHLn42hEjHGESlsJHw==}
+ peerDependencies:
+ vitest: '*'
+
+ vitest@4.0.6:
+ resolution: {integrity: sha512-gR7INfiVRwnEOkCk47faros/9McCZMp5LM+OMNWGLaDBSvJxIzwjgNFufkuePBNaesGRnLmNfW+ddbUJRZn0nQ==}
+ engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0}
+ hasBin: true
+ peerDependencies:
+ '@edge-runtime/vm': '*'
+ '@types/debug': ^4.1.12
+ '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0
+ '@vitest/browser-playwright': 4.0.6
+ '@vitest/browser-preview': 4.0.6
+ '@vitest/browser-webdriverio': 4.0.6
+ '@vitest/ui': 4.0.6
+ happy-dom: '*'
+ jsdom: '*'
+ peerDependenciesMeta:
+ '@edge-runtime/vm':
+ optional: true
+ '@types/debug':
+ optional: true
+ '@types/node':
+ optional: true
+ '@vitest/browser-playwright':
+ optional: true
+ '@vitest/browser-preview':
+ optional: true
+ '@vitest/browser-webdriverio':
+ optional: true
+ '@vitest/ui':
+ optional: true
+ happy-dom:
+ optional: true
+ jsdom:
+ optional: true
+
+ w3c-xmlserializer@5.0.0:
+ resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==}
+ engines: {node: '>=18'}
+
+ webidl-conversions@3.0.1:
+ resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
+
+ webidl-conversions@8.0.0:
+ resolution: {integrity: sha512-n4W4YFyz5JzOfQeA8oN7dUYpR+MBP3PIUsn2jLjWXwK5ASUzt0Jc/A5sAUZoCYFJRGF0FBKJ+1JjN43rNdsQzA==}
+ engines: {node: '>=20'}
+
+ whatwg-encoding@3.1.1:
+ resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==}
+ engines: {node: '>=18'}
+
+ whatwg-mimetype@4.0.0:
+ resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==}
+ engines: {node: '>=18'}
+
+ whatwg-url@15.1.0:
+ resolution: {integrity: sha512-2ytDk0kiEj/yu90JOAp44PVPUkO9+jVhyf+SybKlRHSDlvOOZhdPIrr7xTH64l4WixO2cP+wQIcgujkGBPPz6g==}
+ engines: {node: '>=20'}
+
+ whatwg-url@5.0.0:
+ resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
+
+ which@2.0.2:
+ resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
+ engines: {node: '>= 8'}
+ hasBin: true
+
+ why-is-node-running@2.3.0:
+ resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==}
+ engines: {node: '>=8'}
+ hasBin: true
+
+ word-wrap@1.2.5:
+ resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
+ engines: {node: '>=0.10.0'}
+
+ wrap-ansi@9.0.2:
+ resolution: {integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==}
+ engines: {node: '>=18'}
+
+ ws@7.5.10:
+ resolution: {integrity: sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==}
+ engines: {node: '>=8.3.0'}
+ peerDependencies:
+ bufferutil: ^4.0.1
+ utf-8-validate: ^5.0.2
+ peerDependenciesMeta:
+ bufferutil:
+ optional: true
+ utf-8-validate:
+ optional: true
+
+ ws@8.18.3:
+ resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==}
+ engines: {node: '>=10.0.0'}
+ peerDependencies:
+ bufferutil: ^4.0.1
+ utf-8-validate: '>=5.0.2'
+ peerDependenciesMeta:
+ bufferutil:
+ optional: true
+ utf-8-validate:
+ optional: true
+
+ xml-name-validator@5.0.0:
+ resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==}
+ engines: {node: '>=18'}
+
+ xmlchars@2.2.0:
+ resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==}
+
+ yallist@5.0.0:
+ resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==}
+ engines: {node: '>=18'}
+
+ yaml@2.8.1:
+ resolution: {integrity: sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==}
+ engines: {node: '>= 14.6'}
+ hasBin: true
+
+ yocto-queue@0.1.0:
+ resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
+ engines: {node: '>=10'}
+
+ zod@4.1.12:
+ resolution: {integrity: sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ==}
+
+ zustand@5.0.11:
+ resolution: {integrity: sha512-fdZY+dk7zn/vbWNCYmzZULHRrss0jx5pPFiOuMZ/5HJN6Yv3u+1Wswy/4MpZEkEGhtNH+pwxZB8OKgUBPzYAGg==}
+ engines: {node: '>=12.20.0'}
+ peerDependencies:
+ '@types/react': '>=18.0.0'
+ immer: '>=9.0.6'
+ react: '>=18.0.0'
+ use-sync-external-store: '>=1.2.0'
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ immer:
+ optional: true
+ react:
+ optional: true
+ use-sync-external-store:
+ optional: true
+
+snapshots:
+
+ '@acemir/cssom@0.9.23': {}
+
+ '@adobe/css-tools@4.4.4': {}
+
+ '@asamuzakjp/css-color@4.0.5':
+ dependencies:
+ '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)
+ '@csstools/css-color-parser': 3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)
+ '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4)
+ '@csstools/css-tokenizer': 3.0.4
+ lru-cache: 11.2.2
+
+ '@asamuzakjp/dom-selector@6.7.4':
+ dependencies:
+ '@asamuzakjp/nwsapi': 2.3.9
+ bidi-js: 1.0.3
+ css-tree: 3.1.0
+ is-potential-custom-element-name: 1.0.1
+ lru-cache: 11.2.2
+
+ '@asamuzakjp/nwsapi@2.3.9': {}
+
+ '@babel/code-frame@7.27.1':
+ dependencies:
+ '@babel/helper-validator-identifier': 7.28.5
+ js-tokens: 4.0.0
+ picocolors: 1.1.1
+
+ '@babel/helper-string-parser@7.27.1': {}
+
+ '@babel/helper-validator-identifier@7.28.5': {}
+
+ '@babel/parser@7.28.5':
+ dependencies:
+ '@babel/types': 7.28.5
+
+ '@babel/runtime@7.28.4': {}
+
+ '@babel/types@7.28.5':
+ dependencies:
+ '@babel/helper-string-parser': 7.27.1
+ '@babel/helper-validator-identifier': 7.28.5
+
+ '@bcoe/v8-coverage@1.0.2': {}
+
+ '@csstools/color-helpers@5.1.0': {}
+
+ '@csstools/css-calc@2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)':
+ dependencies:
+ '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4)
+ '@csstools/css-tokenizer': 3.0.4
+
+ '@csstools/css-color-parser@3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)':
+ dependencies:
+ '@csstools/color-helpers': 5.1.0
+ '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)
+ '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4)
+ '@csstools/css-tokenizer': 3.0.4
+
+ '@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4)':
+ dependencies:
+ '@csstools/css-tokenizer': 3.0.4
+
+ '@csstools/css-syntax-patches-for-csstree@1.0.16': {}
+
+ '@csstools/css-tokenizer@3.0.4': {}
+
+ '@date-fns/tz@1.4.1': {}
+
+ '@dnd-kit/accessibility@3.1.1(react@19.2.0)':
+ dependencies:
+ react: 19.2.0
+ tslib: 2.8.1
+
+ '@dnd-kit/core@6.3.1(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@dnd-kit/accessibility': 3.1.1(react@19.2.0)
+ '@dnd-kit/utilities': 3.2.2(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ tslib: 2.8.1
+
+ '@dnd-kit/modifiers@9.0.0(@dnd-kit/core@6.3.1(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@dnd-kit/core': 6.3.1(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@dnd-kit/utilities': 3.2.2(react@19.2.0)
+ react: 19.2.0
+ tslib: 2.8.1
+
+ '@dnd-kit/sortable@10.0.0(@dnd-kit/core@6.3.1(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@dnd-kit/core': 6.3.1(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@dnd-kit/utilities': 3.2.2(react@19.2.0)
+ react: 19.2.0
+ tslib: 2.8.1
+
+ '@dnd-kit/utilities@3.2.2(react@19.2.0)':
+ dependencies:
+ react: 19.2.0
+ tslib: 2.8.1
+
+ '@esbuild/aix-ppc64@0.25.11':
+ optional: true
+
+ '@esbuild/android-arm64@0.25.11':
+ optional: true
+
+ '@esbuild/android-arm@0.25.11':
+ optional: true
+
+ '@esbuild/android-x64@0.25.11':
+ optional: true
+
+ '@esbuild/darwin-arm64@0.25.11':
+ optional: true
+
+ '@esbuild/darwin-x64@0.25.11':
+ optional: true
+
+ '@esbuild/freebsd-arm64@0.25.11':
+ optional: true
+
+ '@esbuild/freebsd-x64@0.25.11':
+ optional: true
+
+ '@esbuild/linux-arm64@0.25.11':
+ optional: true
+
+ '@esbuild/linux-arm@0.25.11':
+ optional: true
+
+ '@esbuild/linux-ia32@0.25.11':
+ optional: true
+
+ '@esbuild/linux-loong64@0.25.11':
+ optional: true
+
+ '@esbuild/linux-mips64el@0.25.11':
+ optional: true
+
+ '@esbuild/linux-ppc64@0.25.11':
+ optional: true
+
+ '@esbuild/linux-riscv64@0.25.11':
+ optional: true
+
+ '@esbuild/linux-s390x@0.25.11':
+ optional: true
+
+ '@esbuild/linux-x64@0.25.11':
+ optional: true
+
+ '@esbuild/netbsd-arm64@0.25.11':
+ optional: true
+
+ '@esbuild/netbsd-x64@0.25.11':
+ optional: true
+
+ '@esbuild/openbsd-arm64@0.25.11':
+ optional: true
+
+ '@esbuild/openbsd-x64@0.25.11':
+ optional: true
+
+ '@esbuild/openharmony-arm64@0.25.11':
+ optional: true
+
+ '@esbuild/sunos-x64@0.25.11':
+ optional: true
+
+ '@esbuild/win32-arm64@0.25.11':
+ optional: true
+
+ '@esbuild/win32-ia32@0.25.11':
+ optional: true
+
+ '@esbuild/win32-x64@0.25.11':
+ optional: true
+
+ '@eslint-community/eslint-utils@4.9.0(eslint@9.37.0(jiti@2.6.1))':
+ dependencies:
+ eslint: 9.37.0(jiti@2.6.1)
+ eslint-visitor-keys: 3.4.3
+
+ '@eslint-community/regexpp@4.12.1': {}
+
+ '@eslint/config-array@0.21.0':
+ dependencies:
+ '@eslint/object-schema': 2.1.6
+ debug: 4.4.3
+ minimatch: 3.1.2
+ transitivePeerDependencies:
+ - supports-color
+
+ '@eslint/config-helpers@0.4.0':
+ dependencies:
+ '@eslint/core': 0.16.0
+
+ '@eslint/core@0.16.0':
+ dependencies:
+ '@types/json-schema': 7.0.15
+
+ '@eslint/eslintrc@3.3.1':
+ dependencies:
+ ajv: 6.12.6
+ debug: 4.4.3
+ espree: 10.4.0
+ globals: 14.0.0
+ ignore: 5.3.2
+ import-fresh: 3.3.1
+ js-yaml: 4.1.0
+ minimatch: 3.1.2
+ strip-json-comments: 3.1.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@eslint/js@9.37.0': {}
+
+ '@eslint/object-schema@2.1.6': {}
+
+ '@eslint/plugin-kit@0.4.0':
+ dependencies:
+ '@eslint/core': 0.16.0
+ levn: 0.4.1
+
+ '@floating-ui/core@1.7.3':
+ dependencies:
+ '@floating-ui/utils': 0.2.10
+
+ '@floating-ui/dom@1.7.4':
+ dependencies:
+ '@floating-ui/core': 1.7.3
+ '@floating-ui/utils': 0.2.10
+
+ '@floating-ui/react-dom@2.1.6(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@floating-ui/dom': 1.7.4
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+
+ '@floating-ui/utils@0.2.10': {}
+
+ '@hookform/resolvers@5.2.2(react-hook-form@7.65.0(react@19.2.0))':
+ dependencies:
+ '@standard-schema/utils': 0.3.0
+ react-hook-form: 7.65.0(react@19.2.0)
+
+ '@humanfs/core@0.19.1': {}
+
+ '@humanfs/node@0.16.7':
+ dependencies:
+ '@humanfs/core': 0.19.1
+ '@humanwhocodes/retry': 0.4.3
+
+ '@humanwhocodes/module-importer@1.0.1': {}
+
+ '@humanwhocodes/retry@0.4.3': {}
+
+ '@isaacs/fs-minipass@4.0.1':
+ dependencies:
+ minipass: 7.1.2
+
+ '@jridgewell/gen-mapping@0.3.13':
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.5.5
+ '@jridgewell/trace-mapping': 0.3.31
+
+ '@jridgewell/remapping@2.3.5':
+ dependencies:
+ '@jridgewell/gen-mapping': 0.3.13
+ '@jridgewell/trace-mapping': 0.3.31
+
+ '@jridgewell/resolve-uri@3.1.2': {}
+
+ '@jridgewell/sourcemap-codec@1.5.5': {}
+
+ '@jridgewell/trace-mapping@0.3.31':
+ dependencies:
+ '@jridgewell/resolve-uri': 3.1.2
+ '@jridgewell/sourcemap-codec': 1.5.5
+
+ '@microsoft/signalr@10.0.0':
+ dependencies:
+ abort-controller: 3.0.0
+ eventsource: 2.0.2
+ fetch-cookie: 2.2.0
+ node-fetch: 2.7.0
+ ws: 7.5.10
+ transitivePeerDependencies:
+ - bufferutil
+ - encoding
+ - utf-8-validate
+
+ '@nodelib/fs.scandir@2.1.5':
+ dependencies:
+ '@nodelib/fs.stat': 2.0.5
+ run-parallel: 1.2.0
+
+ '@nodelib/fs.stat@2.0.5': {}
+
+ '@nodelib/fs.walk@1.2.8':
+ dependencies:
+ '@nodelib/fs.scandir': 2.1.5
+ fastq: 1.19.1
+
+ '@pkgr/core@0.2.9': {}
+
+ '@radix-ui/number@1.1.1': {}
+
+ '@radix-ui/primitive@1.1.3': {}
+
+ '@radix-ui/react-accessible-icon@1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.2.2
+ '@types/react-dom': 19.2.2(@types/react@19.2.2)
+
+ '@radix-ui/react-accordion@1.2.12(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.3
+ '@radix-ui/react-collapsible': 1.1.12(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-context': 1.1.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-direction': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-id': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.2)(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.2.2
+ '@types/react-dom': 19.2.2(@types/react@19.2.2)
+
+ '@radix-ui/react-alert-dialog@1.1.15(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.3
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-context': 1.1.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-slot': 1.2.3(@types/react@19.2.2)(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.2.2
+ '@types/react-dom': 19.2.2(@types/react@19.2.2)
+
+ '@radix-ui/react-arrow@1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.2.2
+ '@types/react-dom': 19.2.2(@types/react@19.2.2)
+
+ '@radix-ui/react-aspect-ratio@1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.2.2
+ '@types/react-dom': 19.2.2(@types/react@19.2.2)
+
+ '@radix-ui/react-avatar@1.1.10(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/react-context': 1.1.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-use-is-hydrated': 0.1.0(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.2.2
+ '@types/react-dom': 19.2.2(@types/react@19.2.2)
+
+ '@radix-ui/react-checkbox@1.3.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.3
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-context': 1.1.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.2.2
+ '@types/react-dom': 19.2.2(@types/react@19.2.2)
+
+ '@radix-ui/react-collapsible@1.1.12(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.3
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-context': 1.1.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-id': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.2.2
+ '@types/react-dom': 19.2.2(@types/react@19.2.2)
+
+ '@radix-ui/react-collection@1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-context': 1.1.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-slot': 1.2.3(@types/react@19.2.2)(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.2.2
+ '@types/react-dom': 19.2.2(@types/react@19.2.2)
+
+ '@radix-ui/react-compose-refs@1.1.2(@types/react@19.2.2)(react@19.2.0)':
+ dependencies:
+ react: 19.2.0
+ optionalDependencies:
+ '@types/react': 19.2.2
+
+ '@radix-ui/react-context-menu@2.2.16(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.3
+ '@radix-ui/react-context': 1.1.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-menu': 2.1.16(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.2)(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.2.2
+ '@types/react-dom': 19.2.2(@types/react@19.2.2)
+
+ '@radix-ui/react-context@1.1.2(@types/react@19.2.2)(react@19.2.0)':
+ dependencies:
+ react: 19.2.0
+ optionalDependencies:
+ '@types/react': 19.2.2
+
+ '@radix-ui/react-dialog@1.1.15(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.3
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-context': 1.1.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-id': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-slot': 1.2.3(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.2)(react@19.2.0)
+ aria-hidden: 1.2.6
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ react-remove-scroll: 2.7.1(@types/react@19.2.2)(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.2.2
+ '@types/react-dom': 19.2.2(@types/react@19.2.2)
+
+ '@radix-ui/react-direction@1.1.1(@types/react@19.2.2)(react@19.2.0)':
+ dependencies:
+ react: 19.2.0
+ optionalDependencies:
+ '@types/react': 19.2.2
+
+ '@radix-ui/react-dismissable-layer@1.1.11(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.3
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.2.2
+ '@types/react-dom': 19.2.2(@types/react@19.2.2)
+
+ '@radix-ui/react-dropdown-menu@2.1.16(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.3
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-context': 1.1.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-id': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-menu': 2.1.16(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.2)(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.2.2
+ '@types/react-dom': 19.2.2(@types/react@19.2.2)
+
+ '@radix-ui/react-focus-guards@1.1.3(@types/react@19.2.2)(react@19.2.0)':
+ dependencies:
+ react: 19.2.0
+ optionalDependencies:
+ '@types/react': 19.2.2
+
+ '@radix-ui/react-focus-scope@1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.2.2
+ '@types/react-dom': 19.2.2(@types/react@19.2.2)
+
+ '@radix-ui/react-form@0.1.8(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.3
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-context': 1.1.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-id': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-label': 2.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.2.2
+ '@types/react-dom': 19.2.2(@types/react@19.2.2)
+
+ '@radix-ui/react-hover-card@1.1.15(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.3
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-context': 1.1.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.2)(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.2.2
+ '@types/react-dom': 19.2.2(@types/react@19.2.2)
+
+ '@radix-ui/react-id@1.1.1(@types/react@19.2.2)(react@19.2.0)':
+ dependencies:
+ '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ react: 19.2.0
+ optionalDependencies:
+ '@types/react': 19.2.2
+
+ '@radix-ui/react-label@2.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.2.2
+ '@types/react-dom': 19.2.2(@types/react@19.2.2)
+
+ '@radix-ui/react-menu@2.1.16(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.3
+ '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-context': 1.1.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-direction': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-id': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-slot': 1.2.3(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ aria-hidden: 1.2.6
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ react-remove-scroll: 2.7.1(@types/react@19.2.2)(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.2.2
+ '@types/react-dom': 19.2.2(@types/react@19.2.2)
+
+ '@radix-ui/react-menubar@1.1.16(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.3
+ '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-context': 1.1.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-direction': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-id': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-menu': 2.1.16(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.2)(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.2.2
+ '@types/react-dom': 19.2.2(@types/react@19.2.2)
+
+ '@radix-ui/react-navigation-menu@1.2.14(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.3
+ '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-context': 1.1.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-direction': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-id': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.2.2
+ '@types/react-dom': 19.2.2(@types/react@19.2.2)
+
+ '@radix-ui/react-one-time-password-field@0.1.8(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/number': 1.1.1
+ '@radix-ui/primitive': 1.1.3
+ '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-context': 1.1.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-direction': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-use-is-hydrated': 0.1.0(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.2.2
+ '@types/react-dom': 19.2.2(@types/react@19.2.2)
+
+ '@radix-ui/react-password-toggle-field@0.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.3
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-context': 1.1.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-id': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-use-is-hydrated': 0.1.0(@types/react@19.2.2)(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.2.2
+ '@types/react-dom': 19.2.2(@types/react@19.2.2)
+
+ '@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.3
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-context': 1.1.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-id': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-slot': 1.2.3(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.2)(react@19.2.0)
+ aria-hidden: 1.2.6
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ react-remove-scroll: 2.7.1(@types/react@19.2.2)(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.2.2
+ '@types/react-dom': 19.2.2(@types/react@19.2.2)
+
+ '@radix-ui/react-popper@1.2.8(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@floating-ui/react-dom': 2.1.6(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-arrow': 1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-context': 1.1.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-use-rect': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/rect': 1.1.1
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.2.2
+ '@types/react-dom': 19.2.2(@types/react@19.2.2)
+
+ '@radix-ui/react-portal@1.1.9(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.2.2
+ '@types/react-dom': 19.2.2(@types/react@19.2.2)
+
+ '@radix-ui/react-presence@1.1.5(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.2.2
+ '@types/react-dom': 19.2.2(@types/react@19.2.2)
+
+ '@radix-ui/react-primitive@2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/react-slot': 1.2.3(@types/react@19.2.2)(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.2.2
+ '@types/react-dom': 19.2.2(@types/react@19.2.2)
+
+ '@radix-ui/react-progress@1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/react-context': 1.1.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.2.2
+ '@types/react-dom': 19.2.2(@types/react@19.2.2)
+
+ '@radix-ui/react-radio-group@1.3.8(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.3
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-context': 1.1.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-direction': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.2.2
+ '@types/react-dom': 19.2.2(@types/react@19.2.2)
+
+ '@radix-ui/react-roving-focus@1.1.11(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.3
+ '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-context': 1.1.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-direction': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-id': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.2)(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.2.2
+ '@types/react-dom': 19.2.2(@types/react@19.2.2)
+
+ '@radix-ui/react-scroll-area@1.2.10(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/number': 1.1.1
+ '@radix-ui/primitive': 1.1.3
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-context': 1.1.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-direction': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.2.2
+ '@types/react-dom': 19.2.2(@types/react@19.2.2)
+
+ '@radix-ui/react-select@2.2.6(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/number': 1.1.1
+ '@radix-ui/primitive': 1.1.3
+ '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-context': 1.1.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-direction': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-id': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-slot': 1.2.3(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ aria-hidden: 1.2.6
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ react-remove-scroll: 2.7.1(@types/react@19.2.2)(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.2.2
+ '@types/react-dom': 19.2.2(@types/react@19.2.2)
+
+ '@radix-ui/react-separator@1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.2.2
+ '@types/react-dom': 19.2.2(@types/react@19.2.2)
+
+ '@radix-ui/react-slider@1.3.6(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/number': 1.1.1
+ '@radix-ui/primitive': 1.1.3
+ '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-context': 1.1.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-direction': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.2.2
+ '@types/react-dom': 19.2.2(@types/react@19.2.2)
+
+ '@radix-ui/react-slot@1.2.3(@types/react@19.2.2)(react@19.2.0)':
+ dependencies:
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.2.0)
+ react: 19.2.0
+ optionalDependencies:
+ '@types/react': 19.2.2
+
+ '@radix-ui/react-switch@1.2.6(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.3
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-context': 1.1.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.2.2
+ '@types/react-dom': 19.2.2(@types/react@19.2.2)
+
+ '@radix-ui/react-tabs@1.1.13(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.3
+ '@radix-ui/react-context': 1.1.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-direction': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-id': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.2)(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.2.2
+ '@types/react-dom': 19.2.2(@types/react@19.2.2)
+
+ '@radix-ui/react-toast@1.2.15(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.3
+ '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-context': 1.1.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.2.2
+ '@types/react-dom': 19.2.2(@types/react@19.2.2)
+
+ '@radix-ui/react-toggle-group@1.1.11(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.3
+ '@radix-ui/react-context': 1.1.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-direction': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-toggle': 1.1.10(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.2)(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.2.2
+ '@types/react-dom': 19.2.2(@types/react@19.2.2)
+
+ '@radix-ui/react-toggle@1.1.10(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.3
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.2)(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.2.2
+ '@types/react-dom': 19.2.2(@types/react@19.2.2)
+
+ '@radix-ui/react-toolbar@1.1.11(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.3
+ '@radix-ui/react-context': 1.1.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-direction': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-separator': 1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-toggle-group': 1.1.11(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.2.2
+ '@types/react-dom': 19.2.2(@types/react@19.2.2)
+
+ '@radix-ui/react-tooltip@1.2.8(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.3
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-context': 1.1.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-id': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-slot': 1.2.3(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.2.2
+ '@types/react-dom': 19.2.2(@types/react@19.2.2)
+
+ '@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.2.2)(react@19.2.0)':
+ dependencies:
+ react: 19.2.0
+ optionalDependencies:
+ '@types/react': 19.2.2
+
+ '@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.2.2)(react@19.2.0)':
+ dependencies:
+ '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ react: 19.2.0
+ optionalDependencies:
+ '@types/react': 19.2.2
+
+ '@radix-ui/react-use-effect-event@0.0.2(@types/react@19.2.2)(react@19.2.0)':
+ dependencies:
+ '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ react: 19.2.0
+ optionalDependencies:
+ '@types/react': 19.2.2
+
+ '@radix-ui/react-use-escape-keydown@1.1.1(@types/react@19.2.2)(react@19.2.0)':
+ dependencies:
+ '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ react: 19.2.0
+ optionalDependencies:
+ '@types/react': 19.2.2
+
+ '@radix-ui/react-use-is-hydrated@0.1.0(@types/react@19.2.2)(react@19.2.0)':
+ dependencies:
+ react: 19.2.0
+ use-sync-external-store: 1.6.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.2.2
+
+ '@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.2.2)(react@19.2.0)':
+ dependencies:
+ react: 19.2.0
+ optionalDependencies:
+ '@types/react': 19.2.2
+
+ '@radix-ui/react-use-previous@1.1.1(@types/react@19.2.2)(react@19.2.0)':
+ dependencies:
+ react: 19.2.0
+ optionalDependencies:
+ '@types/react': 19.2.2
+
+ '@radix-ui/react-use-rect@1.1.1(@types/react@19.2.2)(react@19.2.0)':
+ dependencies:
+ '@radix-ui/rect': 1.1.1
+ react: 19.2.0
+ optionalDependencies:
+ '@types/react': 19.2.2
+
+ '@radix-ui/react-use-size@1.1.1(@types/react@19.2.2)(react@19.2.0)':
+ dependencies:
+ '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ react: 19.2.0
+ optionalDependencies:
+ '@types/react': 19.2.2
+
+ '@radix-ui/react-visually-hidden@1.2.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.2.2
+ '@types/react-dom': 19.2.2(@types/react@19.2.2)
+
+ '@radix-ui/rect@1.1.1': {}
+
+ '@rolldown/pluginutils@1.0.0-beta.35': {}
+
+ '@rollup/rollup-android-arm-eabi@4.52.4':
+ optional: true
+
+ '@rollup/rollup-android-arm64@4.52.4':
+ optional: true
+
+ '@rollup/rollup-darwin-arm64@4.52.4':
+ optional: true
+
+ '@rollup/rollup-darwin-x64@4.52.4':
+ optional: true
+
+ '@rollup/rollup-freebsd-arm64@4.52.4':
+ optional: true
+
+ '@rollup/rollup-freebsd-x64@4.52.4':
+ optional: true
+
+ '@rollup/rollup-linux-arm-gnueabihf@4.52.4':
+ optional: true
+
+ '@rollup/rollup-linux-arm-musleabihf@4.52.4':
+ optional: true
+
+ '@rollup/rollup-linux-arm64-gnu@4.52.4':
+ optional: true
+
+ '@rollup/rollup-linux-arm64-musl@4.52.4':
+ optional: true
+
+ '@rollup/rollup-linux-loong64-gnu@4.52.4':
+ optional: true
+
+ '@rollup/rollup-linux-ppc64-gnu@4.52.4':
+ optional: true
+
+ '@rollup/rollup-linux-riscv64-gnu@4.52.4':
+ optional: true
+
+ '@rollup/rollup-linux-riscv64-musl@4.52.4':
+ optional: true
+
+ '@rollup/rollup-linux-s390x-gnu@4.52.4':
+ optional: true
+
+ '@rollup/rollup-linux-x64-gnu@4.52.4':
+ optional: true
+
+ '@rollup/rollup-linux-x64-musl@4.52.4':
+ optional: true
+
+ '@rollup/rollup-openharmony-arm64@4.52.4':
+ optional: true
+
+ '@rollup/rollup-win32-arm64-msvc@4.52.4':
+ optional: true
+
+ '@rollup/rollup-win32-ia32-msvc@4.52.4':
+ optional: true
+
+ '@rollup/rollup-win32-x64-gnu@4.52.4':
+ optional: true
+
+ '@rollup/rollup-win32-x64-msvc@4.52.4':
+ optional: true
+
+ '@standard-schema/spec@1.0.0': {}
+
+ '@standard-schema/utils@0.3.0': {}
+
+ '@swc/core-darwin-arm64@1.13.5':
+ optional: true
+
+ '@swc/core-darwin-x64@1.13.5':
+ optional: true
+
+ '@swc/core-linux-arm-gnueabihf@1.13.5':
+ optional: true
+
+ '@swc/core-linux-arm64-gnu@1.13.5':
+ optional: true
+
+ '@swc/core-linux-arm64-musl@1.13.5':
+ optional: true
+
+ '@swc/core-linux-x64-gnu@1.13.5':
+ optional: true
+
+ '@swc/core-linux-x64-musl@1.13.5':
+ optional: true
+
+ '@swc/core-win32-arm64-msvc@1.13.5':
+ optional: true
+
+ '@swc/core-win32-ia32-msvc@1.13.5':
+ optional: true
+
+ '@swc/core-win32-x64-msvc@1.13.5':
+ optional: true
+
+ '@swc/core@1.13.5':
+ dependencies:
+ '@swc/counter': 0.1.3
+ '@swc/types': 0.1.25
+ optionalDependencies:
+ '@swc/core-darwin-arm64': 1.13.5
+ '@swc/core-darwin-x64': 1.13.5
+ '@swc/core-linux-arm-gnueabihf': 1.13.5
+ '@swc/core-linux-arm64-gnu': 1.13.5
+ '@swc/core-linux-arm64-musl': 1.13.5
+ '@swc/core-linux-x64-gnu': 1.13.5
+ '@swc/core-linux-x64-musl': 1.13.5
+ '@swc/core-win32-arm64-msvc': 1.13.5
+ '@swc/core-win32-ia32-msvc': 1.13.5
+ '@swc/core-win32-x64-msvc': 1.13.5
+
+ '@swc/counter@0.1.3': {}
+
+ '@swc/types@0.1.25':
+ dependencies:
+ '@swc/counter': 0.1.3
+
+ '@tailwindcss/node@4.1.14':
+ dependencies:
+ '@jridgewell/remapping': 2.3.5
+ enhanced-resolve: 5.18.3
+ jiti: 2.6.1
+ lightningcss: 1.30.1
+ magic-string: 0.30.19
+ source-map-js: 1.2.1
+ tailwindcss: 4.1.14
+
+ '@tailwindcss/oxide-android-arm64@4.1.14':
+ optional: true
+
+ '@tailwindcss/oxide-darwin-arm64@4.1.14':
+ optional: true
+
+ '@tailwindcss/oxide-darwin-x64@4.1.14':
+ optional: true
+
+ '@tailwindcss/oxide-freebsd-x64@4.1.14':
+ optional: true
+
+ '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.14':
+ optional: true
+
+ '@tailwindcss/oxide-linux-arm64-gnu@4.1.14':
+ optional: true
+
+ '@tailwindcss/oxide-linux-arm64-musl@4.1.14':
+ optional: true
+
+ '@tailwindcss/oxide-linux-x64-gnu@4.1.14':
+ optional: true
+
+ '@tailwindcss/oxide-linux-x64-musl@4.1.14':
+ optional: true
+
+ '@tailwindcss/oxide-wasm32-wasi@4.1.14':
+ optional: true
+
+ '@tailwindcss/oxide-win32-arm64-msvc@4.1.14':
+ optional: true
+
+ '@tailwindcss/oxide-win32-x64-msvc@4.1.14':
+ optional: true
+
+ '@tailwindcss/oxide@4.1.14':
+ dependencies:
+ detect-libc: 2.1.2
+ tar: 7.5.1
+ optionalDependencies:
+ '@tailwindcss/oxide-android-arm64': 4.1.14
+ '@tailwindcss/oxide-darwin-arm64': 4.1.14
+ '@tailwindcss/oxide-darwin-x64': 4.1.14
+ '@tailwindcss/oxide-freebsd-x64': 4.1.14
+ '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.14
+ '@tailwindcss/oxide-linux-arm64-gnu': 4.1.14
+ '@tailwindcss/oxide-linux-arm64-musl': 4.1.14
+ '@tailwindcss/oxide-linux-x64-gnu': 4.1.14
+ '@tailwindcss/oxide-linux-x64-musl': 4.1.14
+ '@tailwindcss/oxide-wasm32-wasi': 4.1.14
+ '@tailwindcss/oxide-win32-arm64-msvc': 4.1.14
+ '@tailwindcss/oxide-win32-x64-msvc': 4.1.14
+
+ '@tailwindcss/vite@4.1.14(vite@7.1.10(@types/node@24.8.1)(jiti@2.6.1)(lightningcss@1.30.1)(yaml@2.8.1))':
+ dependencies:
+ '@tailwindcss/node': 4.1.14
+ '@tailwindcss/oxide': 4.1.14
+ tailwindcss: 4.1.14
+ vite: 7.1.10(@types/node@24.8.1)(jiti@2.6.1)(lightningcss@1.30.1)(yaml@2.8.1)
+
+ '@tanstack/query-core@5.90.6': {}
+
+ '@tanstack/query-devtools@5.90.1': {}
+
+ '@tanstack/react-query-devtools@5.90.2(@tanstack/react-query@5.90.6(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@tanstack/query-devtools': 5.90.1
+ '@tanstack/react-query': 5.90.6(react@19.2.0)
+ react: 19.2.0
+
+ '@tanstack/react-query@5.90.6(react@19.2.0)':
+ dependencies:
+ '@tanstack/query-core': 5.90.6
+ react: 19.2.0
+
+ '@tanstack/react-table@8.21.3(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@tanstack/table-core': 8.21.3
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+
+ '@tanstack/table-core@8.21.3': {}
+
+ '@testing-library/dom@10.4.1':
+ dependencies:
+ '@babel/code-frame': 7.27.1
+ '@babel/runtime': 7.28.4
+ '@types/aria-query': 5.0.4
+ aria-query: 5.3.0
+ dom-accessibility-api: 0.5.16
+ lz-string: 1.5.0
+ picocolors: 1.1.1
+ pretty-format: 27.5.1
+
+ '@testing-library/jest-dom@6.9.1':
+ dependencies:
+ '@adobe/css-tools': 4.4.4
+ aria-query: 5.3.0
+ css.escape: 1.5.1
+ dom-accessibility-api: 0.6.3
+ picocolors: 1.1.1
+ redent: 3.0.0
+
+ '@testing-library/react@16.3.0(@testing-library/dom@10.4.1)(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@babel/runtime': 7.28.4
+ '@testing-library/dom': 10.4.1
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.2.2
+ '@types/react-dom': 19.2.2(@types/react@19.2.2)
+
+ '@testing-library/user-event@14.6.1(@testing-library/dom@10.4.1)':
+ dependencies:
+ '@testing-library/dom': 10.4.1
+
+ '@types/aria-query@5.0.4': {}
+
+ '@types/chai@5.2.3':
+ dependencies:
+ '@types/deep-eql': 4.0.2
+ assertion-error: 2.0.1
+
+ '@types/d3-array@3.2.2': {}
+
+ '@types/d3-color@3.1.3': {}
+
+ '@types/d3-ease@3.0.2': {}
+
+ '@types/d3-interpolate@3.0.4':
+ dependencies:
+ '@types/d3-color': 3.1.3
+
+ '@types/d3-path@3.1.1': {}
+
+ '@types/d3-scale@4.0.9':
+ dependencies:
+ '@types/d3-time': 3.0.4
+
+ '@types/d3-shape@3.1.8':
+ dependencies:
+ '@types/d3-path': 3.1.1
+
+ '@types/d3-time@3.0.4': {}
+
+ '@types/d3-timer@3.0.2': {}
+
+ '@types/deep-eql@4.0.2': {}
+
+ '@types/estree@1.0.8': {}
+
+ '@types/json-schema@7.0.15': {}
+
+ '@types/node@24.8.1':
+ dependencies:
+ undici-types: 7.14.0
+
+ '@types/react-dom@19.2.2(@types/react@19.2.2)':
+ dependencies:
+ '@types/react': 19.2.2
+
+ '@types/react@19.2.2':
+ dependencies:
+ csstype: 3.1.3
+
+ '@typescript-eslint/eslint-plugin@8.46.1(@typescript-eslint/parser@8.46.1(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3)':
+ dependencies:
+ '@eslint-community/regexpp': 4.12.1
+ '@typescript-eslint/parser': 8.46.1(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3)
+ '@typescript-eslint/scope-manager': 8.46.1
+ '@typescript-eslint/type-utils': 8.46.1(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3)
+ '@typescript-eslint/utils': 8.46.1(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3)
+ '@typescript-eslint/visitor-keys': 8.46.1
+ eslint: 9.37.0(jiti@2.6.1)
+ graphemer: 1.4.0
+ ignore: 7.0.5
+ natural-compare: 1.4.0
+ ts-api-utils: 2.1.0(typescript@5.9.3)
+ typescript: 5.9.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/parser@8.46.1(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3)':
+ dependencies:
+ '@typescript-eslint/scope-manager': 8.46.1
+ '@typescript-eslint/types': 8.46.1
+ '@typescript-eslint/typescript-estree': 8.46.1(typescript@5.9.3)
+ '@typescript-eslint/visitor-keys': 8.46.1
+ debug: 4.4.3
+ eslint: 9.37.0(jiti@2.6.1)
+ typescript: 5.9.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/project-service@8.46.1(typescript@5.9.3)':
+ dependencies:
+ '@typescript-eslint/tsconfig-utils': 8.46.1(typescript@5.9.3)
+ '@typescript-eslint/types': 8.46.1
+ debug: 4.4.3
+ typescript: 5.9.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/scope-manager@8.46.1':
+ dependencies:
+ '@typescript-eslint/types': 8.46.1
+ '@typescript-eslint/visitor-keys': 8.46.1
+
+ '@typescript-eslint/tsconfig-utils@8.46.1(typescript@5.9.3)':
+ dependencies:
+ typescript: 5.9.3
+
+ '@typescript-eslint/type-utils@8.46.1(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3)':
+ dependencies:
+ '@typescript-eslint/types': 8.46.1
+ '@typescript-eslint/typescript-estree': 8.46.1(typescript@5.9.3)
+ '@typescript-eslint/utils': 8.46.1(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3)
+ debug: 4.4.3
+ eslint: 9.37.0(jiti@2.6.1)
+ ts-api-utils: 2.1.0(typescript@5.9.3)
+ typescript: 5.9.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/types@8.46.1': {}
+
+ '@typescript-eslint/typescript-estree@8.46.1(typescript@5.9.3)':
+ dependencies:
+ '@typescript-eslint/project-service': 8.46.1(typescript@5.9.3)
+ '@typescript-eslint/tsconfig-utils': 8.46.1(typescript@5.9.3)
+ '@typescript-eslint/types': 8.46.1
+ '@typescript-eslint/visitor-keys': 8.46.1
+ debug: 4.4.3
+ fast-glob: 3.3.3
+ is-glob: 4.0.3
+ minimatch: 9.0.5
+ semver: 7.7.3
+ ts-api-utils: 2.1.0(typescript@5.9.3)
+ typescript: 5.9.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/utils@8.46.1(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3)':
+ dependencies:
+ '@eslint-community/eslint-utils': 4.9.0(eslint@9.37.0(jiti@2.6.1))
+ '@typescript-eslint/scope-manager': 8.46.1
+ '@typescript-eslint/types': 8.46.1
+ '@typescript-eslint/typescript-estree': 8.46.1(typescript@5.9.3)
+ eslint: 9.37.0(jiti@2.6.1)
+ typescript: 5.9.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/visitor-keys@8.46.1':
+ dependencies:
+ '@typescript-eslint/types': 8.46.1
+ eslint-visitor-keys: 4.2.1
+
+ '@vitejs/plugin-react-swc@4.1.0(vite@7.1.10(@types/node@24.8.1)(jiti@2.6.1)(lightningcss@1.30.1)(yaml@2.8.1))':
+ dependencies:
+ '@rolldown/pluginutils': 1.0.0-beta.35
+ '@swc/core': 1.13.5
+ vite: 7.1.10(@types/node@24.8.1)(jiti@2.6.1)(lightningcss@1.30.1)(yaml@2.8.1)
+ transitivePeerDependencies:
+ - '@swc/helpers'
+
+ '@vitest/coverage-v8@4.0.6(vitest@4.0.6(@types/node@24.8.1)(jiti@2.6.1)(jsdom@27.2.0)(lightningcss@1.30.1)(yaml@2.8.1))':
+ dependencies:
+ '@bcoe/v8-coverage': 1.0.2
+ '@vitest/utils': 4.0.6
+ ast-v8-to-istanbul: 0.3.8
+ debug: 4.4.3
+ istanbul-lib-coverage: 3.2.2
+ istanbul-lib-report: 3.0.1
+ istanbul-lib-source-maps: 5.0.6
+ istanbul-reports: 3.2.0
+ magicast: 0.3.5
+ std-env: 3.10.0
+ tinyrainbow: 3.0.3
+ vitest: 4.0.6(@types/node@24.8.1)(jiti@2.6.1)(jsdom@27.2.0)(lightningcss@1.30.1)(yaml@2.8.1)
+ transitivePeerDependencies:
+ - supports-color
+
+ '@vitest/expect@4.0.6':
+ dependencies:
+ '@standard-schema/spec': 1.0.0
+ '@types/chai': 5.2.3
+ '@vitest/spy': 4.0.6
+ '@vitest/utils': 4.0.6
+ chai: 6.2.0
+ tinyrainbow: 3.0.3
+
+ '@vitest/mocker@4.0.6(vite@7.1.10(@types/node@24.8.1)(jiti@2.6.1)(lightningcss@1.30.1)(yaml@2.8.1))':
+ dependencies:
+ '@vitest/spy': 4.0.6
+ estree-walker: 3.0.3
+ magic-string: 0.30.19
+ optionalDependencies:
+ vite: 7.1.10(@types/node@24.8.1)(jiti@2.6.1)(lightningcss@1.30.1)(yaml@2.8.1)
+
+ '@vitest/pretty-format@4.0.6':
+ dependencies:
+ tinyrainbow: 3.0.3
+
+ '@vitest/runner@4.0.6':
+ dependencies:
+ '@vitest/utils': 4.0.6
+ pathe: 2.0.3
+
+ '@vitest/snapshot@4.0.6':
+ dependencies:
+ '@vitest/pretty-format': 4.0.6
+ magic-string: 0.30.19
+ pathe: 2.0.3
+
+ '@vitest/spy@4.0.6': {}
+
+ '@vitest/utils@4.0.6':
+ dependencies:
+ '@vitest/pretty-format': 4.0.6
+ tinyrainbow: 3.0.3
+
+ abort-controller@3.0.0:
+ dependencies:
+ event-target-shim: 5.0.1
+
+ acorn-jsx@5.3.2(acorn@8.15.0):
+ dependencies:
+ acorn: 8.15.0
+
+ acorn@8.15.0: {}
+
+ agent-base@7.1.4: {}
+
+ ajv@6.12.6:
+ dependencies:
+ fast-deep-equal: 3.1.3
+ fast-json-stable-stringify: 2.1.0
+ json-schema-traverse: 0.4.1
+ uri-js: 4.4.1
+
+ ansi-escapes@7.2.0:
+ dependencies:
+ environment: 1.1.0
+
+ ansi-regex@5.0.1: {}
+
+ ansi-regex@6.2.2: {}
+
+ ansi-styles@4.3.0:
+ dependencies:
+ color-convert: 2.0.1
+
+ ansi-styles@5.2.0: {}
+
+ ansi-styles@6.2.3: {}
+
+ argparse@2.0.1: {}
+
+ aria-hidden@1.2.6:
+ dependencies:
+ tslib: 2.8.1
+
+ aria-query@5.3.0:
+ dependencies:
+ dequal: 2.0.3
+
+ assertion-error@2.0.1: {}
+
+ ast-v8-to-istanbul@0.3.8:
+ dependencies:
+ '@jridgewell/trace-mapping': 0.3.31
+ estree-walker: 3.0.3
+ js-tokens: 9.0.1
+
+ asynckit@0.4.0: {}
+
+ axios@1.13.2:
+ dependencies:
+ follow-redirects: 1.15.11
+ form-data: 4.0.5
+ proxy-from-env: 1.1.0
+ transitivePeerDependencies:
+ - debug
+
+ balanced-match@1.0.2: {}
+
+ bidi-js@1.0.3:
+ dependencies:
+ require-from-string: 2.0.2
+
+ brace-expansion@1.1.12:
+ dependencies:
+ balanced-match: 1.0.2
+ concat-map: 0.0.1
+
+ brace-expansion@2.0.2:
+ dependencies:
+ balanced-match: 1.0.2
+
+ braces@3.0.3:
+ dependencies:
+ fill-range: 7.1.1
+
+ call-bind-apply-helpers@1.0.2:
+ dependencies:
+ es-errors: 1.3.0
+ function-bind: 1.1.2
+
+ callsites@3.1.0: {}
+
+ chai@6.2.0: {}
+
+ chalk@4.1.2:
+ dependencies:
+ ansi-styles: 4.3.0
+ supports-color: 7.2.0
+
+ chownr@3.0.0: {}
+
+ class-variance-authority@0.7.1:
+ dependencies:
+ clsx: 2.1.1
+
+ cli-cursor@5.0.0:
+ dependencies:
+ restore-cursor: 5.1.0
+
+ cli-truncate@5.1.1:
+ dependencies:
+ slice-ansi: 7.1.2
+ string-width: 8.1.0
+
+ clsx@2.1.1: {}
+
+ cmdk@1.1.1(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0):
+ dependencies:
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-id': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ transitivePeerDependencies:
+ - '@types/react'
+ - '@types/react-dom'
+
+ color-convert@2.0.1:
+ dependencies:
+ color-name: 1.1.4
+
+ color-name@1.1.4: {}
+
+ colorette@2.0.20: {}
+
+ combined-stream@1.0.8:
+ dependencies:
+ delayed-stream: 1.0.0
+
+ commander@14.0.2: {}
+
+ concat-map@0.0.1: {}
+
+ cookie@1.0.2: {}
+
+ cross-spawn@7.0.6:
+ dependencies:
+ path-key: 3.1.1
+ shebang-command: 2.0.0
+ which: 2.0.2
+
+ css-tree@3.1.0:
+ dependencies:
+ mdn-data: 2.12.2
+ source-map-js: 1.2.1
+
+ css.escape@1.5.1: {}
+
+ cssstyle@5.3.3:
+ dependencies:
+ '@asamuzakjp/css-color': 4.0.5
+ '@csstools/css-syntax-patches-for-csstree': 1.0.16
+ css-tree: 3.1.0
+
+ csstype@3.1.3: {}
+
+ d3-array@3.2.4:
+ dependencies:
+ internmap: 2.0.3
+
+ d3-color@3.1.0: {}
+
+ d3-ease@3.0.1: {}
+
+ d3-format@3.1.2: {}
+
+ d3-interpolate@3.0.1:
+ dependencies:
+ d3-color: 3.1.0
+
+ d3-path@3.1.0: {}
+
+ d3-scale@4.0.2:
+ dependencies:
+ d3-array: 3.2.4
+ d3-format: 3.1.2
+ d3-interpolate: 3.0.1
+ d3-time: 3.1.0
+ d3-time-format: 4.1.0
+
+ d3-shape@3.2.0:
+ dependencies:
+ d3-path: 3.1.0
+
+ d3-time-format@4.1.0:
+ dependencies:
+ d3-time: 3.1.0
+
+ d3-time@3.1.0:
+ dependencies:
+ d3-array: 3.2.4
+
+ d3-timer@3.0.1: {}
+
+ data-urls@6.0.0:
+ dependencies:
+ whatwg-mimetype: 4.0.0
+ whatwg-url: 15.1.0
+
+ date-fns-jalali@4.1.0-0: {}
+
+ date-fns@4.1.0: {}
+
+ debug@4.4.3:
+ dependencies:
+ ms: 2.1.3
+
+ decimal.js-light@2.5.1: {}
+
+ decimal.js@10.6.0: {}
+
+ deep-is@0.1.4: {}
+
+ delayed-stream@1.0.0: {}
+
+ dequal@2.0.3: {}
+
+ detect-libc@2.1.2: {}
+
+ detect-node-es@1.1.0: {}
+
+ dom-accessibility-api@0.5.16: {}
+
+ dom-accessibility-api@0.6.3: {}
+
+ dom-helpers@5.2.1:
+ dependencies:
+ '@babel/runtime': 7.28.4
+ csstype: 3.1.3
+
+ dunder-proto@1.0.1:
+ dependencies:
+ call-bind-apply-helpers: 1.0.2
+ es-errors: 1.3.0
+ gopd: 1.2.0
+
+ embla-carousel-react@8.6.0(react@19.2.0):
+ dependencies:
+ embla-carousel: 8.6.0
+ embla-carousel-reactive-utils: 8.6.0(embla-carousel@8.6.0)
+ react: 19.2.0
+
+ embla-carousel-reactive-utils@8.6.0(embla-carousel@8.6.0):
+ dependencies:
+ embla-carousel: 8.6.0
+
+ embla-carousel@8.6.0: {}
+
+ emoji-regex@10.6.0: {}
+
+ enhanced-resolve@5.18.3:
+ dependencies:
+ graceful-fs: 4.2.11
+ tapable: 2.3.0
+
+ entities@6.0.1: {}
+
+ environment@1.1.0: {}
+
+ es-define-property@1.0.1: {}
+
+ es-errors@1.3.0: {}
+
+ es-module-lexer@1.7.0: {}
+
+ es-object-atoms@1.1.1:
+ dependencies:
+ es-errors: 1.3.0
+
+ es-set-tostringtag@2.1.0:
+ dependencies:
+ es-errors: 1.3.0
+ get-intrinsic: 1.3.0
+ has-tostringtag: 1.0.2
+ hasown: 2.0.2
+
+ esbuild@0.25.11:
+ optionalDependencies:
+ '@esbuild/aix-ppc64': 0.25.11
+ '@esbuild/android-arm': 0.25.11
+ '@esbuild/android-arm64': 0.25.11
+ '@esbuild/android-x64': 0.25.11
+ '@esbuild/darwin-arm64': 0.25.11
+ '@esbuild/darwin-x64': 0.25.11
+ '@esbuild/freebsd-arm64': 0.25.11
+ '@esbuild/freebsd-x64': 0.25.11
+ '@esbuild/linux-arm': 0.25.11
+ '@esbuild/linux-arm64': 0.25.11
+ '@esbuild/linux-ia32': 0.25.11
+ '@esbuild/linux-loong64': 0.25.11
+ '@esbuild/linux-mips64el': 0.25.11
+ '@esbuild/linux-ppc64': 0.25.11
+ '@esbuild/linux-riscv64': 0.25.11
+ '@esbuild/linux-s390x': 0.25.11
+ '@esbuild/linux-x64': 0.25.11
+ '@esbuild/netbsd-arm64': 0.25.11
+ '@esbuild/netbsd-x64': 0.25.11
+ '@esbuild/openbsd-arm64': 0.25.11
+ '@esbuild/openbsd-x64': 0.25.11
+ '@esbuild/openharmony-arm64': 0.25.11
+ '@esbuild/sunos-x64': 0.25.11
+ '@esbuild/win32-arm64': 0.25.11
+ '@esbuild/win32-ia32': 0.25.11
+ '@esbuild/win32-x64': 0.25.11
+
+ escape-string-regexp@4.0.0: {}
+
+ eslint-config-prettier@10.1.8(eslint@9.37.0(jiti@2.6.1)):
+ dependencies:
+ eslint: 9.37.0(jiti@2.6.1)
+
+ eslint-plugin-prettier@5.5.4(eslint-config-prettier@10.1.8(eslint@9.37.0(jiti@2.6.1)))(eslint@9.37.0(jiti@2.6.1))(prettier@3.6.2):
+ dependencies:
+ eslint: 9.37.0(jiti@2.6.1)
+ prettier: 3.6.2
+ prettier-linter-helpers: 1.0.0
+ synckit: 0.11.11
+ optionalDependencies:
+ eslint-config-prettier: 10.1.8(eslint@9.37.0(jiti@2.6.1))
+
+ eslint-plugin-react-hooks@5.2.0(eslint@9.37.0(jiti@2.6.1)):
+ dependencies:
+ eslint: 9.37.0(jiti@2.6.1)
+
+ eslint-plugin-react-refresh@0.4.24(eslint@9.37.0(jiti@2.6.1)):
+ dependencies:
+ eslint: 9.37.0(jiti@2.6.1)
+
+ eslint-scope@8.4.0:
+ dependencies:
+ esrecurse: 4.3.0
+ estraverse: 5.3.0
+
+ eslint-visitor-keys@3.4.3: {}
+
+ eslint-visitor-keys@4.2.1: {}
+
+ eslint@9.37.0(jiti@2.6.1):
+ dependencies:
+ '@eslint-community/eslint-utils': 4.9.0(eslint@9.37.0(jiti@2.6.1))
+ '@eslint-community/regexpp': 4.12.1
+ '@eslint/config-array': 0.21.0
+ '@eslint/config-helpers': 0.4.0
+ '@eslint/core': 0.16.0
+ '@eslint/eslintrc': 3.3.1
+ '@eslint/js': 9.37.0
+ '@eslint/plugin-kit': 0.4.0
+ '@humanfs/node': 0.16.7
+ '@humanwhocodes/module-importer': 1.0.1
+ '@humanwhocodes/retry': 0.4.3
+ '@types/estree': 1.0.8
+ '@types/json-schema': 7.0.15
+ ajv: 6.12.6
+ chalk: 4.1.2
+ cross-spawn: 7.0.6
+ debug: 4.4.3
+ escape-string-regexp: 4.0.0
+ eslint-scope: 8.4.0
+ eslint-visitor-keys: 4.2.1
+ espree: 10.4.0
+ esquery: 1.6.0
+ esutils: 2.0.3
+ fast-deep-equal: 3.1.3
+ file-entry-cache: 8.0.0
+ find-up: 5.0.0
+ glob-parent: 6.0.2
+ ignore: 5.3.2
+ imurmurhash: 0.1.4
+ is-glob: 4.0.3
+ json-stable-stringify-without-jsonify: 1.0.1
+ lodash.merge: 4.6.2
+ minimatch: 3.1.2
+ natural-compare: 1.4.0
+ optionator: 0.9.4
+ optionalDependencies:
+ jiti: 2.6.1
+ transitivePeerDependencies:
+ - supports-color
+
+ espree@10.4.0:
+ dependencies:
+ acorn: 8.15.0
+ acorn-jsx: 5.3.2(acorn@8.15.0)
+ eslint-visitor-keys: 4.2.1
+
+ esquery@1.6.0:
+ dependencies:
+ estraverse: 5.3.0
+
+ esrecurse@4.3.0:
+ dependencies:
+ estraverse: 5.3.0
+
+ estraverse@5.3.0: {}
+
+ estree-walker@3.0.3:
+ dependencies:
+ '@types/estree': 1.0.8
+
+ esutils@2.0.3: {}
+
+ event-target-shim@5.0.1: {}
+
+ eventemitter3@4.0.7: {}
+
+ eventemitter3@5.0.1: {}
+
+ eventsource@2.0.2: {}
+
+ expect-type@1.2.2: {}
+
+ fast-deep-equal@3.1.3: {}
+
+ fast-diff@1.3.0: {}
+
+ fast-equals@5.4.0: {}
+
+ fast-glob@3.3.3:
+ dependencies:
+ '@nodelib/fs.stat': 2.0.5
+ '@nodelib/fs.walk': 1.2.8
+ glob-parent: 5.1.2
+ merge2: 1.4.1
+ micromatch: 4.0.8
+
+ fast-json-stable-stringify@2.1.0: {}
+
+ fast-levenshtein@2.0.6: {}
+
+ fastq@1.19.1:
+ dependencies:
+ reusify: 1.1.0
+
+ fdir@6.5.0(picomatch@4.0.3):
+ optionalDependencies:
+ picomatch: 4.0.3
+
+ fetch-cookie@2.2.0:
+ dependencies:
+ set-cookie-parser: 2.7.1
+ tough-cookie: 4.1.4
+
+ file-entry-cache@8.0.0:
+ dependencies:
+ flat-cache: 4.0.1
+
+ fill-range@7.1.1:
+ dependencies:
+ to-regex-range: 5.0.1
+
+ find-up@5.0.0:
+ dependencies:
+ locate-path: 6.0.0
+ path-exists: 4.0.0
+
+ flat-cache@4.0.1:
+ dependencies:
+ flatted: 3.3.3
+ keyv: 4.5.4
+
+ flatted@3.3.3: {}
+
+ follow-redirects@1.15.11: {}
+
+ form-data@4.0.5:
+ dependencies:
+ asynckit: 0.4.0
+ combined-stream: 1.0.8
+ es-set-tostringtag: 2.1.0
+ hasown: 2.0.2
+ mime-types: 2.1.35
+
+ fsevents@2.3.3:
+ optional: true
+
+ function-bind@1.1.2: {}
+
+ get-east-asian-width@1.4.0: {}
+
+ get-intrinsic@1.3.0:
+ dependencies:
+ call-bind-apply-helpers: 1.0.2
+ es-define-property: 1.0.1
+ es-errors: 1.3.0
+ es-object-atoms: 1.1.1
+ function-bind: 1.1.2
+ get-proto: 1.0.1
+ gopd: 1.2.0
+ has-symbols: 1.1.0
+ hasown: 2.0.2
+ math-intrinsics: 1.1.0
+
+ get-nonce@1.0.1: {}
+
+ get-proto@1.0.1:
+ dependencies:
+ dunder-proto: 1.0.1
+ es-object-atoms: 1.1.1
+
+ glob-parent@5.1.2:
+ dependencies:
+ is-glob: 4.0.3
+
+ glob-parent@6.0.2:
+ dependencies:
+ is-glob: 4.0.3
+
+ globals@14.0.0: {}
+
+ globals@16.4.0: {}
+
+ gopd@1.2.0: {}
+
+ graceful-fs@4.2.11: {}
+
+ graphemer@1.4.0: {}
+
+ has-flag@4.0.0: {}
+
+ has-symbols@1.1.0: {}
+
+ has-tostringtag@1.0.2:
+ dependencies:
+ has-symbols: 1.1.0
+
+ hasown@2.0.2:
+ dependencies:
+ function-bind: 1.1.2
+
+ html-encoding-sniffer@4.0.0:
+ dependencies:
+ whatwg-encoding: 3.1.1
+
+ html-escaper@2.0.2: {}
+
+ http-proxy-agent@7.0.2:
+ dependencies:
+ agent-base: 7.1.4
+ debug: 4.4.3
+ transitivePeerDependencies:
+ - supports-color
+
+ https-proxy-agent@7.0.6:
+ dependencies:
+ agent-base: 7.1.4
+ debug: 4.4.3
+ transitivePeerDependencies:
+ - supports-color
+
+ husky@9.1.7: {}
+
+ iconv-lite@0.6.3:
+ dependencies:
+ safer-buffer: 2.1.2
+
+ ignore@5.3.2: {}
+
+ ignore@7.0.5: {}
+
+ import-fresh@3.3.1:
+ dependencies:
+ parent-module: 1.0.1
+ resolve-from: 4.0.0
+
+ imurmurhash@0.1.4: {}
+
+ indent-string@4.0.0: {}
+
+ internmap@2.0.3: {}
+
+ is-extglob@2.1.1: {}
+
+ is-fullwidth-code-point@5.1.0:
+ dependencies:
+ get-east-asian-width: 1.4.0
+
+ is-glob@4.0.3:
+ dependencies:
+ is-extglob: 2.1.1
+
+ is-number@7.0.0: {}
+
+ is-potential-custom-element-name@1.0.1: {}
+
+ isexe@2.0.0: {}
+
+ istanbul-lib-coverage@3.2.2: {}
+
+ istanbul-lib-report@3.0.1:
+ dependencies:
+ istanbul-lib-coverage: 3.2.2
+ make-dir: 4.0.0
+ supports-color: 7.2.0
+
+ istanbul-lib-source-maps@5.0.6:
+ dependencies:
+ '@jridgewell/trace-mapping': 0.3.31
+ debug: 4.4.3
+ istanbul-lib-coverage: 3.2.2
+ transitivePeerDependencies:
+ - supports-color
+
+ istanbul-reports@3.2.0:
+ dependencies:
+ html-escaper: 2.0.2
+ istanbul-lib-report: 3.0.1
+
+ jiti@2.6.1: {}
+
+ js-tokens@4.0.0: {}
+
+ js-tokens@9.0.1: {}
+
+ js-yaml@4.1.0:
+ dependencies:
+ argparse: 2.0.1
+
+ jsdom@27.2.0:
+ dependencies:
+ '@acemir/cssom': 0.9.23
+ '@asamuzakjp/dom-selector': 6.7.4
+ cssstyle: 5.3.3
+ data-urls: 6.0.0
+ decimal.js: 10.6.0
+ html-encoding-sniffer: 4.0.0
+ http-proxy-agent: 7.0.2
+ https-proxy-agent: 7.0.6
+ is-potential-custom-element-name: 1.0.1
+ parse5: 8.0.0
+ saxes: 6.0.0
+ symbol-tree: 3.2.4
+ tough-cookie: 6.0.0
+ w3c-xmlserializer: 5.0.0
+ webidl-conversions: 8.0.0
+ whatwg-encoding: 3.1.1
+ whatwg-mimetype: 4.0.0
+ whatwg-url: 15.1.0
+ ws: 8.18.3
+ xml-name-validator: 5.0.0
+ transitivePeerDependencies:
+ - bufferutil
+ - supports-color
+ - utf-8-validate
+
+ json-buffer@3.0.1: {}
+
+ json-schema-traverse@0.4.1: {}
+
+ json-stable-stringify-without-jsonify@1.0.1: {}
+
+ jwt-decode@4.0.0: {}
+
+ keyv@4.5.4:
+ dependencies:
+ json-buffer: 3.0.1
+
+ levn@0.4.1:
+ dependencies:
+ prelude-ls: 1.2.1
+ type-check: 0.4.0
+
+ lightningcss-darwin-arm64@1.30.1:
+ optional: true
+
+ lightningcss-darwin-x64@1.30.1:
+ optional: true
+
+ lightningcss-freebsd-x64@1.30.1:
+ optional: true
+
+ lightningcss-linux-arm-gnueabihf@1.30.1:
+ optional: true
+
+ lightningcss-linux-arm64-gnu@1.30.1:
+ optional: true
+
+ lightningcss-linux-arm64-musl@1.30.1:
+ optional: true
+
+ lightningcss-linux-x64-gnu@1.30.1:
+ optional: true
+
+ lightningcss-linux-x64-musl@1.30.1:
+ optional: true
+
+ lightningcss-win32-arm64-msvc@1.30.1:
+ optional: true
+
+ lightningcss-win32-x64-msvc@1.30.1:
+ optional: true
+
+ lightningcss@1.30.1:
+ dependencies:
+ detect-libc: 2.1.2
+ optionalDependencies:
+ lightningcss-darwin-arm64: 1.30.1
+ lightningcss-darwin-x64: 1.30.1
+ lightningcss-freebsd-x64: 1.30.1
+ lightningcss-linux-arm-gnueabihf: 1.30.1
+ lightningcss-linux-arm64-gnu: 1.30.1
+ lightningcss-linux-arm64-musl: 1.30.1
+ lightningcss-linux-x64-gnu: 1.30.1
+ lightningcss-linux-x64-musl: 1.30.1
+ lightningcss-win32-arm64-msvc: 1.30.1
+ lightningcss-win32-x64-msvc: 1.30.1
+
+ lint-staged@16.2.6:
+ dependencies:
+ commander: 14.0.2
+ listr2: 9.0.5
+ micromatch: 4.0.8
+ nano-spawn: 2.0.0
+ pidtree: 0.6.0
+ string-argv: 0.3.2
+ yaml: 2.8.1
+
+ listr2@9.0.5:
+ dependencies:
+ cli-truncate: 5.1.1
+ colorette: 2.0.20
+ eventemitter3: 5.0.1
+ log-update: 6.1.0
+ rfdc: 1.4.1
+ wrap-ansi: 9.0.2
+
+ locate-path@6.0.0:
+ dependencies:
+ p-locate: 5.0.0
+
+ lodash.merge@4.6.2: {}
+
+ lodash@4.17.21: {}
+
+ log-update@6.1.0:
+ dependencies:
+ ansi-escapes: 7.2.0
+ cli-cursor: 5.0.0
+ slice-ansi: 7.1.2
+ strip-ansi: 7.1.2
+ wrap-ansi: 9.0.2
+
+ loose-envify@1.4.0:
+ dependencies:
+ js-tokens: 4.0.0
+
+ lru-cache@11.2.2: {}
+
+ lucide-react@0.546.0(react@19.2.0):
+ dependencies:
+ react: 19.2.0
+
+ lz-string@1.5.0: {}
+
+ magic-string@0.30.19:
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.5.5
+
+ magicast@0.3.5:
+ dependencies:
+ '@babel/parser': 7.28.5
+ '@babel/types': 7.28.5
+ source-map-js: 1.2.1
+
+ make-dir@4.0.0:
+ dependencies:
+ semver: 7.7.3
+
+ math-intrinsics@1.1.0: {}
+
+ mdn-data@2.12.2: {}
+
+ merge2@1.4.1: {}
+
+ micromatch@4.0.8:
+ dependencies:
+ braces: 3.0.3
+ picomatch: 2.3.1
+
+ mime-db@1.52.0: {}
+
+ mime-types@2.1.35:
+ dependencies:
+ mime-db: 1.52.0
+
+ mimic-function@5.0.1: {}
+
+ min-indent@1.0.1: {}
+
+ minimatch@3.1.2:
+ dependencies:
+ brace-expansion: 1.1.12
+
+ minimatch@9.0.5:
+ dependencies:
+ brace-expansion: 2.0.2
+
+ minipass@7.1.2: {}
+
+ minizlib@3.1.0:
+ dependencies:
+ minipass: 7.1.2
+
+ ms@2.1.3: {}
+
+ nano-spawn@2.0.0: {}
+
+ nanoid@3.3.11: {}
+
+ natural-compare@1.4.0: {}
+
+ next-themes@0.4.6(react-dom@19.2.0(react@19.2.0))(react@19.2.0):
+ dependencies:
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+
+ node-fetch@2.7.0:
+ dependencies:
+ whatwg-url: 5.0.0
+
+ normalize-wheel@1.0.1: {}
+
+ object-assign@4.1.1: {}
+
+ onetime@7.0.0:
+ dependencies:
+ mimic-function: 5.0.1
+
+ optionator@0.9.4:
+ dependencies:
+ deep-is: 0.1.4
+ fast-levenshtein: 2.0.6
+ levn: 0.4.1
+ prelude-ls: 1.2.1
+ type-check: 0.4.0
+ word-wrap: 1.2.5
+
+ p-limit@3.1.0:
+ dependencies:
+ yocto-queue: 0.1.0
+
+ p-locate@5.0.0:
+ dependencies:
+ p-limit: 3.1.0
+
+ parent-module@1.0.1:
+ dependencies:
+ callsites: 3.1.0
+
+ parse5@8.0.0:
+ dependencies:
+ entities: 6.0.1
+
+ path-exists@4.0.0: {}
+
+ path-key@3.1.1: {}
+
+ pathe@2.0.3: {}
+
+ picocolors@1.1.1: {}
+
+ picomatch@2.3.1: {}
+
+ picomatch@4.0.3: {}
+
+ pidtree@0.6.0: {}
+
+ postcss@8.5.6:
+ dependencies:
+ nanoid: 3.3.11
+ picocolors: 1.1.1
+ source-map-js: 1.2.1
+
+ prelude-ls@1.2.1: {}
+
+ prettier-linter-helpers@1.0.0:
+ dependencies:
+ fast-diff: 1.3.0
+
+ prettier@3.6.2: {}
+
+ pretty-format@27.5.1:
+ dependencies:
+ ansi-regex: 5.0.1
+ ansi-styles: 5.2.0
+ react-is: 17.0.2
+
+ prop-types@15.8.1:
+ dependencies:
+ loose-envify: 1.4.0
+ object-assign: 4.1.1
+ react-is: 16.13.1
+
+ proxy-from-env@1.1.0: {}
+
+ psl@1.15.0:
+ dependencies:
+ punycode: 2.3.1
+
+ punycode@2.3.1: {}
+
+ querystringify@2.2.0: {}
+
+ queue-microtask@1.2.3: {}
+
+ radix-ui@1.4.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0):
+ dependencies:
+ '@radix-ui/primitive': 1.1.3
+ '@radix-ui/react-accessible-icon': 1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-accordion': 1.2.12(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-alert-dialog': 1.1.15(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-arrow': 1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-aspect-ratio': 1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-avatar': 1.1.10(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-checkbox': 1.3.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-collapsible': 1.1.12(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-context': 1.1.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-context-menu': 2.2.16(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-direction': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-dropdown-menu': 2.1.16(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-form': 0.1.8(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-hover-card': 1.1.15(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-label': 2.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-menu': 2.1.16(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-menubar': 1.1.16(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-navigation-menu': 1.2.14(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-one-time-password-field': 0.1.8(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-password-toggle-field': 0.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-popover': 1.1.15(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-progress': 1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-radio-group': 1.3.8(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-scroll-area': 1.2.10(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-select': 2.2.6(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-separator': 1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-slider': 1.3.6(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-slot': 1.2.3(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-switch': 1.2.6(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-tabs': 1.1.13(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-toast': 1.2.15(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-toggle': 1.1.10(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-toggle-group': 1.1.11(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-toolbar': 1.1.11(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-tooltip': 1.2.8(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-use-is-hydrated': 0.1.0(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.2.2
+ '@types/react-dom': 19.2.2(@types/react@19.2.2)
+
+ react-day-picker@9.11.1(react@19.2.0):
+ dependencies:
+ '@date-fns/tz': 1.4.1
+ date-fns: 4.1.0
+ date-fns-jalali: 4.1.0-0
+ react: 19.2.0
+
+ react-dom@19.2.0(react@19.2.0):
+ dependencies:
+ react: 19.2.0
+ scheduler: 0.27.0
+
+ react-easy-crop@5.5.6(react-dom@19.2.0(react@19.2.0))(react@19.2.0):
+ dependencies:
+ normalize-wheel: 1.0.1
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ tslib: 2.8.1
+
+ react-hook-form@7.65.0(react@19.2.0):
+ dependencies:
+ react: 19.2.0
+
+ react-is@16.13.1: {}
+
+ react-is@17.0.2: {}
+
+ react-is@18.3.1: {}
+
+ react-remove-scroll-bar@2.3.8(@types/react@19.2.2)(react@19.2.0):
+ dependencies:
+ react: 19.2.0
+ react-style-singleton: 2.2.3(@types/react@19.2.2)(react@19.2.0)
+ tslib: 2.8.1
+ optionalDependencies:
+ '@types/react': 19.2.2
+
+ react-remove-scroll@2.7.1(@types/react@19.2.2)(react@19.2.0):
+ dependencies:
+ react: 19.2.0
+ react-remove-scroll-bar: 2.3.8(@types/react@19.2.2)(react@19.2.0)
+ react-style-singleton: 2.2.3(@types/react@19.2.2)(react@19.2.0)
+ tslib: 2.8.1
+ use-callback-ref: 1.3.3(@types/react@19.2.2)(react@19.2.0)
+ use-sidecar: 1.1.3(@types/react@19.2.2)(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.2.2
+
+ react-router@7.9.4(react-dom@19.2.0(react@19.2.0))(react@19.2.0):
+ dependencies:
+ cookie: 1.0.2
+ react: 19.2.0
+ set-cookie-parser: 2.7.1
+ optionalDependencies:
+ react-dom: 19.2.0(react@19.2.0)
+
+ react-smooth@4.0.4(react-dom@19.2.0(react@19.2.0))(react@19.2.0):
+ dependencies:
+ fast-equals: 5.4.0
+ prop-types: 15.8.1
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ react-transition-group: 4.4.5(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+
+ react-style-singleton@2.2.3(@types/react@19.2.2)(react@19.2.0):
+ dependencies:
+ get-nonce: 1.0.1
+ react: 19.2.0
+ tslib: 2.8.1
+ optionalDependencies:
+ '@types/react': 19.2.2
+
+ react-transition-group@4.4.5(react-dom@19.2.0(react@19.2.0))(react@19.2.0):
+ dependencies:
+ '@babel/runtime': 7.28.4
+ dom-helpers: 5.2.1
+ loose-envify: 1.4.0
+ prop-types: 15.8.1
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+
+ react@19.2.0: {}
+
+ recharts-scale@0.4.5:
+ dependencies:
+ decimal.js-light: 2.5.1
+
+ recharts@2.15.4(react-dom@19.2.0(react@19.2.0))(react@19.2.0):
+ dependencies:
+ clsx: 2.1.1
+ eventemitter3: 4.0.7
+ lodash: 4.17.21
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ react-is: 18.3.1
+ react-smooth: 4.0.4(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ recharts-scale: 0.4.5
+ tiny-invariant: 1.3.3
+ victory-vendor: 36.9.2
+
+ redent@3.0.0:
+ dependencies:
+ indent-string: 4.0.0
+ strip-indent: 3.0.0
+
+ require-from-string@2.0.2: {}
+
+ requires-port@1.0.0: {}
+
+ resolve-from@4.0.0: {}
+
+ restore-cursor@5.1.0:
+ dependencies:
+ onetime: 7.0.0
+ signal-exit: 4.1.0
+
+ reusify@1.1.0: {}
+
+ rfdc@1.4.1: {}
+
+ rollup@4.52.4:
+ dependencies:
+ '@types/estree': 1.0.8
+ optionalDependencies:
+ '@rollup/rollup-android-arm-eabi': 4.52.4
+ '@rollup/rollup-android-arm64': 4.52.4
+ '@rollup/rollup-darwin-arm64': 4.52.4
+ '@rollup/rollup-darwin-x64': 4.52.4
+ '@rollup/rollup-freebsd-arm64': 4.52.4
+ '@rollup/rollup-freebsd-x64': 4.52.4
+ '@rollup/rollup-linux-arm-gnueabihf': 4.52.4
+ '@rollup/rollup-linux-arm-musleabihf': 4.52.4
+ '@rollup/rollup-linux-arm64-gnu': 4.52.4
+ '@rollup/rollup-linux-arm64-musl': 4.52.4
+ '@rollup/rollup-linux-loong64-gnu': 4.52.4
+ '@rollup/rollup-linux-ppc64-gnu': 4.52.4
+ '@rollup/rollup-linux-riscv64-gnu': 4.52.4
+ '@rollup/rollup-linux-riscv64-musl': 4.52.4
+ '@rollup/rollup-linux-s390x-gnu': 4.52.4
+ '@rollup/rollup-linux-x64-gnu': 4.52.4
+ '@rollup/rollup-linux-x64-musl': 4.52.4
+ '@rollup/rollup-openharmony-arm64': 4.52.4
+ '@rollup/rollup-win32-arm64-msvc': 4.52.4
+ '@rollup/rollup-win32-ia32-msvc': 4.52.4
+ '@rollup/rollup-win32-x64-gnu': 4.52.4
+ '@rollup/rollup-win32-x64-msvc': 4.52.4
+ fsevents: 2.3.3
+
+ run-parallel@1.2.0:
+ dependencies:
+ queue-microtask: 1.2.3
+
+ safer-buffer@2.1.2: {}
+
+ saxes@6.0.0:
+ dependencies:
+ xmlchars: 2.2.0
+
+ scheduler@0.27.0: {}
+
+ semver@7.7.3: {}
+
+ set-cookie-parser@2.7.1: {}
+
+ shebang-command@2.0.0:
+ dependencies:
+ shebang-regex: 3.0.0
+
+ shebang-regex@3.0.0: {}
+
+ siginfo@2.0.0: {}
+
+ signal-exit@4.1.0: {}
+
+ slice-ansi@7.1.2:
+ dependencies:
+ ansi-styles: 6.2.3
+ is-fullwidth-code-point: 5.1.0
+
+ sonner@2.0.7(react-dom@19.2.0(react@19.2.0))(react@19.2.0):
+ dependencies:
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+
+ source-map-js@1.2.1: {}
+
+ stackback@0.0.2: {}
+
+ std-env@3.10.0: {}
+
+ string-argv@0.3.2: {}
+
+ string-width@7.2.0:
+ dependencies:
+ emoji-regex: 10.6.0
+ get-east-asian-width: 1.4.0
+ strip-ansi: 7.1.2
+
+ string-width@8.1.0:
+ dependencies:
+ get-east-asian-width: 1.4.0
+ strip-ansi: 7.1.2
+
+ strip-ansi@7.1.2:
+ dependencies:
+ ansi-regex: 6.2.2
+
+ strip-indent@3.0.0:
+ dependencies:
+ min-indent: 1.0.1
+
+ strip-json-comments@3.1.1: {}
+
+ supports-color@7.2.0:
+ dependencies:
+ has-flag: 4.0.0
+
+ symbol-tree@3.2.4: {}
+
+ synckit@0.11.11:
+ dependencies:
+ '@pkgr/core': 0.2.9
+
+ tailwind-merge@3.3.1: {}
+
+ tailwindcss@4.1.14: {}
+
+ tapable@2.3.0: {}
+
+ tar@7.5.1:
+ dependencies:
+ '@isaacs/fs-minipass': 4.0.1
+ chownr: 3.0.0
+ minipass: 7.1.2
+ minizlib: 3.1.0
+ yallist: 5.0.0
+
+ tiny-invariant@1.3.3: {}
+
+ tinybench@2.9.0: {}
+
+ tinyexec@0.3.2: {}
+
+ tinyglobby@0.2.15:
+ dependencies:
+ fdir: 6.5.0(picomatch@4.0.3)
+ picomatch: 4.0.3
+
+ tinyrainbow@3.0.3: {}
+
+ tldts-core@7.0.17: {}
+
+ tldts@7.0.17:
+ dependencies:
+ tldts-core: 7.0.17
+
+ to-regex-range@5.0.1:
+ dependencies:
+ is-number: 7.0.0
+
+ tough-cookie@4.1.4:
+ dependencies:
+ psl: 1.15.0
+ punycode: 2.3.1
+ universalify: 0.2.0
+ url-parse: 1.5.10
+
+ tough-cookie@6.0.0:
+ dependencies:
+ tldts: 7.0.17
+
+ tr46@0.0.3: {}
+
+ tr46@6.0.0:
+ dependencies:
+ punycode: 2.3.1
+
+ ts-api-utils@2.1.0(typescript@5.9.3):
+ dependencies:
+ typescript: 5.9.3
+
+ tslib@2.8.1: {}
+
+ tw-animate-css@1.4.0: {}
+
+ type-check@0.4.0:
+ dependencies:
+ prelude-ls: 1.2.1
+
+ typescript-eslint@8.46.1(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3):
+ dependencies:
+ '@typescript-eslint/eslint-plugin': 8.46.1(@typescript-eslint/parser@8.46.1(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3)
+ '@typescript-eslint/parser': 8.46.1(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3)
+ '@typescript-eslint/typescript-estree': 8.46.1(typescript@5.9.3)
+ '@typescript-eslint/utils': 8.46.1(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3)
+ eslint: 9.37.0(jiti@2.6.1)
+ typescript: 5.9.3
+ transitivePeerDependencies:
+ - supports-color
+
+ typescript@5.9.3: {}
+
+ undici-types@7.14.0: {}
+
+ universalify@0.2.0: {}
+
+ uri-js@4.4.1:
+ dependencies:
+ punycode: 2.3.1
+
+ url-parse@1.5.10:
+ dependencies:
+ querystringify: 2.2.0
+ requires-port: 1.0.0
+
+ use-callback-ref@1.3.3(@types/react@19.2.2)(react@19.2.0):
+ dependencies:
+ react: 19.2.0
+ tslib: 2.8.1
+ optionalDependencies:
+ '@types/react': 19.2.2
+
+ use-sidecar@1.1.3(@types/react@19.2.2)(react@19.2.0):
+ dependencies:
+ detect-node-es: 1.1.0
+ react: 19.2.0
+ tslib: 2.8.1
+ optionalDependencies:
+ '@types/react': 19.2.2
+
+ use-sync-external-store@1.6.0(react@19.2.0):
+ dependencies:
+ react: 19.2.0
+
+ vaul@1.1.2(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0):
+ dependencies:
+ '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ transitivePeerDependencies:
+ - '@types/react'
+ - '@types/react-dom'
+
+ victory-vendor@36.9.2:
+ dependencies:
+ '@types/d3-array': 3.2.2
+ '@types/d3-ease': 3.0.2
+ '@types/d3-interpolate': 3.0.4
+ '@types/d3-scale': 4.0.9
+ '@types/d3-shape': 3.1.8
+ '@types/d3-time': 3.0.4
+ '@types/d3-timer': 3.0.2
+ d3-array: 3.2.4
+ d3-ease: 3.0.1
+ d3-interpolate: 3.0.1
+ d3-scale: 4.0.2
+ d3-shape: 3.2.0
+ d3-time: 3.1.0
+ d3-timer: 3.0.1
+
+ vite@7.1.10(@types/node@24.8.1)(jiti@2.6.1)(lightningcss@1.30.1)(yaml@2.8.1):
+ dependencies:
+ esbuild: 0.25.11
+ fdir: 6.5.0(picomatch@4.0.3)
+ picomatch: 4.0.3
+ postcss: 8.5.6
+ rollup: 4.52.4
+ tinyglobby: 0.2.15
+ optionalDependencies:
+ '@types/node': 24.8.1
+ fsevents: 2.3.3
+ jiti: 2.6.1
+ lightningcss: 1.30.1
+ yaml: 2.8.1
+
+ vitest-localstorage-mock@0.1.2(vitest@4.0.6(@types/node@24.8.1)(jiti@2.6.1)(jsdom@27.2.0)(lightningcss@1.30.1)(yaml@2.8.1)):
+ dependencies:
+ vitest: 4.0.6(@types/node@24.8.1)(jiti@2.6.1)(jsdom@27.2.0)(lightningcss@1.30.1)(yaml@2.8.1)
+
+ vitest@4.0.6(@types/node@24.8.1)(jiti@2.6.1)(jsdom@27.2.0)(lightningcss@1.30.1)(yaml@2.8.1):
+ dependencies:
+ '@vitest/expect': 4.0.6
+ '@vitest/mocker': 4.0.6(vite@7.1.10(@types/node@24.8.1)(jiti@2.6.1)(lightningcss@1.30.1)(yaml@2.8.1))
+ '@vitest/pretty-format': 4.0.6
+ '@vitest/runner': 4.0.6
+ '@vitest/snapshot': 4.0.6
+ '@vitest/spy': 4.0.6
+ '@vitest/utils': 4.0.6
+ debug: 4.4.3
+ es-module-lexer: 1.7.0
+ expect-type: 1.2.2
+ magic-string: 0.30.19
+ pathe: 2.0.3
+ picomatch: 4.0.3
+ std-env: 3.10.0
+ tinybench: 2.9.0
+ tinyexec: 0.3.2
+ tinyglobby: 0.2.15
+ tinyrainbow: 3.0.3
+ vite: 7.1.10(@types/node@24.8.1)(jiti@2.6.1)(lightningcss@1.30.1)(yaml@2.8.1)
+ why-is-node-running: 2.3.0
+ optionalDependencies:
+ '@types/node': 24.8.1
+ jsdom: 27.2.0
+ transitivePeerDependencies:
+ - jiti
+ - less
+ - lightningcss
+ - msw
+ - sass
+ - sass-embedded
+ - stylus
+ - sugarss
+ - supports-color
+ - terser
+ - tsx
+ - yaml
+
+ w3c-xmlserializer@5.0.0:
+ dependencies:
+ xml-name-validator: 5.0.0
+
+ webidl-conversions@3.0.1: {}
+
+ webidl-conversions@8.0.0: {}
+
+ whatwg-encoding@3.1.1:
+ dependencies:
+ iconv-lite: 0.6.3
+
+ whatwg-mimetype@4.0.0: {}
+
+ whatwg-url@15.1.0:
+ dependencies:
+ tr46: 6.0.0
+ webidl-conversions: 8.0.0
+
+ whatwg-url@5.0.0:
+ dependencies:
+ tr46: 0.0.3
+ webidl-conversions: 3.0.1
+
+ which@2.0.2:
+ dependencies:
+ isexe: 2.0.0
+
+ why-is-node-running@2.3.0:
+ dependencies:
+ siginfo: 2.0.0
+ stackback: 0.0.2
+
+ word-wrap@1.2.5: {}
+
+ wrap-ansi@9.0.2:
+ dependencies:
+ ansi-styles: 6.2.3
+ string-width: 7.2.0
+ strip-ansi: 7.1.2
+
+ ws@7.5.10: {}
+
+ ws@8.18.3: {}
+
+ xml-name-validator@5.0.0: {}
+
+ xmlchars@2.2.0: {}
+
+ yallist@5.0.0: {}
+
+ yaml@2.8.1: {}
+
+ yocto-queue@0.1.0: {}
+
+ zod@4.1.12: {}
+
+ zustand@5.0.11(@types/react@19.2.2)(react@19.2.0)(use-sync-external-store@1.6.0(react@19.2.0)):
+ optionalDependencies:
+ '@types/react': 19.2.2
+ react: 19.2.0
+ use-sync-external-store: 1.6.0(react@19.2.0)
diff --git a/UIs/Customers/pnpm-workspace.yaml b/UIs/Customers/pnpm-workspace.yaml
new file mode 100644
index 0000000..d24d030
--- /dev/null
+++ b/UIs/Customers/pnpm-workspace.yaml
@@ -0,0 +1,5 @@
+onlyBuiltDependencies:
+ - '@swc/core'
+ - '@tailwindcss/oxide'
+ - esbuild
+ - supabase
diff --git a/UIs/Customers/pr-validation.yml b/UIs/Customers/pr-validation.yml
new file mode 100644
index 0000000..3677ad5
--- /dev/null
+++ b/UIs/Customers/pr-validation.yml
@@ -0,0 +1,30 @@
+name: smarttechinnovate.administration.ui - pr validation
+
+trigger:
+ branches:
+ exclude:
+ - main
+
+pr: none
+
+pool:
+ vmImage: 'ubuntu-latest'
+
+steps:
+- task: NodeTool@0
+ inputs:
+ versionSpec: '20.x'
+ displayName: 'install Node.js 20.x'
+
+- script: npm install -g pnpm
+ displayName: 'install pnpm'
+
+- script: pnpm install
+ displayName: 'install project dependencies'
+
+- script: pnpm build
+ displayName: 'build Vite application'
+ env:
+ VITE_GATEWAY_API: https://smartconsig-gateway-live.victoriousocean-22a8a528.brazilsouth.azurecontainerapps.io/api/v1
+ VITE_AUTH_API: https://feijuca-auth-api.victoriousocean-22a8a528.brazilsouth.azurecontainerapps.io/api/v1
+ CI: true
diff --git a/UIs/Customers/production.yml b/UIs/Customers/production.yml
new file mode 100644
index 0000000..24507e6
--- /dev/null
+++ b/UIs/Customers/production.yml
@@ -0,0 +1,36 @@
+name: smarttechinnovate.admin.ui - production deploy
+
+trigger:
+ branches:
+ include:
+ - main
+
+pool:
+ vmImage: 'ubuntu-latest'
+
+steps:
+- task: NodeTool@0
+ inputs:
+ versionSpec: '20.x'
+ displayName: 'install Node.js 20.x'
+
+- script: npm install -g pnpm
+ displayName: 'install pnpm'
+
+- script: pnpm install
+ displayName: 'install project dependencies'
+
+- script: pnpm build
+ displayName: 'build Vite application'
+ env:
+ VITE_GATEWAY_API: https://smartconsig-gateway-live.victoriousocean-22a8a528.brazilsouth.azurecontainerapps.io/api/v1
+ VITE_AUTH_API: https://feijuca-auth-api.victoriousocean-22a8a528.brazilsouth.azurecontainerapps.io/api/v1
+ CI: true
+
+- script: |
+ npm install -g netlify-cli
+ netlify deploy --site a7857b39-8740-4b09-b1ac-7f2a8927bc5e --auth $(NETLIFY_AUTH_TOKEN) --dir=dist --prod --message "deploy from azure pipeline $(Build.BuildNumber)"
+ displayName: 'Deploy to Netlify Production'
+ env:
+ NETLIFY_SITE_ID: a7857b39-8740-4b09-b1ac-7f2a8927bc5e
+ NETLIFY_AUTH_TOKEN: $(NETLIFY_AUTH_TOKEN)
diff --git a/UIs/Customers/public/_redirects b/UIs/Customers/public/_redirects
new file mode 100644
index 0000000..abe2ab3
--- /dev/null
+++ b/UIs/Customers/public/_redirects
@@ -0,0 +1 @@
+/* /index.html 200
\ No newline at end of file
diff --git a/UIs/Customers/src/app/app.tsx b/UIs/Customers/src/app/app.tsx
new file mode 100644
index 0000000..1748834
--- /dev/null
+++ b/UIs/Customers/src/app/app.tsx
@@ -0,0 +1,14 @@
+import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
+import { AppRouters } from './routers';
+import { Toaster } from '../shared/components/ui/sonner';
+import { AppProviders } from './providers';
+
+export default function App() {
+ return (
+
+
+
+
+
+ );
+}
diff --git a/UIs/Customers/src/app/config/react-query/index.tsx b/UIs/Customers/src/app/config/react-query/index.tsx
new file mode 100644
index 0000000..15cc68b
--- /dev/null
+++ b/UIs/Customers/src/app/config/react-query/index.tsx
@@ -0,0 +1,29 @@
+import { MutationCache, QueryClient } from '@tanstack/react-query';
+import { AxiosError } from 'axios';
+import { toast } from 'sonner';
+
+export const queryClient = new QueryClient({
+ mutationCache: new MutationCache({
+ onError: (error, _variables, _context, mutation) => {
+ if (mutation.meta?.suppressErrorToast) return;
+
+ const customMessage = mutation.meta?.errorMessage;
+ const backendMessage =
+ error instanceof AxiosError ? error.message : 'Erro desconhecido';
+
+ toast.error(customMessage || `Erro na operação: ${backendMessage}`);
+ },
+ onSuccess: (_data, _variables, _context, mutation) => {
+ if (mutation.meta?.successMessage) {
+ toast.success(mutation.meta.successMessage);
+ }
+ }
+ }),
+
+ defaultOptions: {
+ queries: {
+ retry: false,
+ refetchOnWindowFocus: false
+ }
+ }
+});
diff --git a/UIs/Customers/src/app/config/react-query/types/tanstack-query.d.ts b/UIs/Customers/src/app/config/react-query/types/tanstack-query.d.ts
new file mode 100644
index 0000000..517d244
--- /dev/null
+++ b/UIs/Customers/src/app/config/react-query/types/tanstack-query.d.ts
@@ -0,0 +1,11 @@
+import '@tanstack/react-query';
+
+declare module '@tanstack/react-query' {
+ interface Register {
+ mutationMeta: {
+ errorMessage?: string;
+ successMessage?: string;
+ suppressErrorToast?: boolean;
+ };
+ }
+}
diff --git a/UIs/Customers/src/app/layouts/public/index.tsx b/UIs/Customers/src/app/layouts/public/index.tsx
new file mode 100644
index 0000000..adee0f9
--- /dev/null
+++ b/UIs/Customers/src/app/layouts/public/index.tsx
@@ -0,0 +1,11 @@
+import { Outlet } from 'react-router';
+
+export function PublicLayout() {
+ return (
+
+
+
+
+
+ );
+}
diff --git a/UIs/Customers/src/app/layouts/system/components/button-logout/index.tsx b/UIs/Customers/src/app/layouts/system/components/button-logout/index.tsx
new file mode 100644
index 0000000..dccc6c7
--- /dev/null
+++ b/UIs/Customers/src/app/layouts/system/components/button-logout/index.tsx
@@ -0,0 +1,50 @@
+import {
+ AlertDialog,
+ AlertDialogAction,
+ AlertDialogCancel,
+ AlertDialogContent,
+ AlertDialogDescription,
+ AlertDialogFooter,
+ AlertDialogHeader,
+ AlertDialogTitle,
+ AlertDialogTrigger
+} from '@/shared/components/ui/alert-dialog';
+import { LogOut } from 'lucide-react';
+import { useAuthStore } from '@/features/auth/store';
+import { DropdownMenuItem } from '@/shared/components/ui/dropdown-menu';
+
+export const ButtonLogout = () => {
+ const logout = useAuthStore((state) => state.logout);
+
+ return (
+
+
+ e.preventDefault()}
+ className="cursor-pointer text-destructive focus:text-destructive"
+ >
+
+ Encerrar sessão
+
+
+
+
+
+ Deseja encerrar a sessão atual?
+
+ Após esta ação, você será deslogado do sistema e redirecionado para
+ se autenticar novamente.
+
+
+
+
+ Cancelar
+
+
+ Encerrar sessão
+
+
+
+
+ );
+};
diff --git a/UIs/Customers/src/app/layouts/system/components/header-menu/index.tsx b/UIs/Customers/src/app/layouts/system/components/header-menu/index.tsx
new file mode 100644
index 0000000..49cadb7
--- /dev/null
+++ b/UIs/Customers/src/app/layouts/system/components/header-menu/index.tsx
@@ -0,0 +1,11 @@
+import { ToggleTheme } from '@/app/theme/toggle-theme';
+import { UserMenu } from '../user-menu';
+
+export function HeaderMenu() {
+ return (
+
+
+
+
+ );
+}
diff --git a/UIs/Customers/src/app/layouts/system/components/header/index.tsx b/UIs/Customers/src/app/layouts/system/components/header/index.tsx
new file mode 100644
index 0000000..b999eb0
--- /dev/null
+++ b/UIs/Customers/src/app/layouts/system/components/header/index.tsx
@@ -0,0 +1,13 @@
+
+import { HeaderMenu } from '../header-menu';
+
+export const HeaderLayout = () => {
+ return (
+
+ );
+};
diff --git a/UIs/Customers/src/app/layouts/system/components/sidebar/index.tsx b/UIs/Customers/src/app/layouts/system/components/sidebar/index.tsx
new file mode 100644
index 0000000..585fd8b
--- /dev/null
+++ b/UIs/Customers/src/app/layouts/system/components/sidebar/index.tsx
@@ -0,0 +1,21 @@
+import {
+ Sidebar,
+ SidebarContent,
+ SidebarRail
+} from '@/shared/components/ui/sidebar';
+
+import { NavItems } from './nav-items';
+import { Navlinks } from './nav-links';
+
+export function SystemSidebar({
+ ...props
+}: React.ComponentProps) {
+ return (
+
+
+
+
+
+
+ );
+}
diff --git a/UIs/Customers/src/app/layouts/system/components/sidebar/nav-items.tsx b/UIs/Customers/src/app/layouts/system/components/sidebar/nav-items.tsx
new file mode 100644
index 0000000..910230c
--- /dev/null
+++ b/UIs/Customers/src/app/layouts/system/components/sidebar/nav-items.tsx
@@ -0,0 +1,52 @@
+import { type LucideIcon } from 'lucide-react';
+import {
+ SidebarGroup,
+ SidebarMenu,
+ SidebarMenuButton
+} from '@/shared/components/ui/sidebar';
+import { NavLink } from 'react-router';
+import { RestrictedLink } from '@/shared/components/global/restricted-link';
+
+export type NavItem = {
+ title: string;
+ url: string;
+ icon?: LucideIcon;
+ isRestricted?: boolean;
+};
+
+export function NavItems({ items }: { items: NavItem[] }) {
+ return (
+
+
+ {items.map((item) => {
+ const LinkWrapper = item.isRestricted ? RestrictedLink : NavLink;
+
+ return (
+
+ `relative h-full py-1.5 cursor-pointer transition-colors duration-20 flex items-center overflow-hidden outline-none select-none text-foreground hover:text-red-500 ${isActive ? ' text-red-500' : ''}`
+ }
+ >
+
+ {item.icon && (
+
+
+
+ )}
+
+ {item.title}
+
+ {item.title}
+
+
+ );
+ })}
+
+
+ );
+}
diff --git a/UIs/Customers/src/app/layouts/system/components/sidebar/nav-links.ts b/UIs/Customers/src/app/layouts/system/components/sidebar/nav-links.ts
new file mode 100644
index 0000000..b795cdf
--- /dev/null
+++ b/UIs/Customers/src/app/layouts/system/components/sidebar/nav-links.ts
@@ -0,0 +1,10 @@
+import { Home } from 'lucide-react';
+import type { NavItem } from './nav-items';
+
+export const Navlinks: NavItem[] = [
+ {
+ title: 'Dashboard',
+ url: '/dashboard',
+ icon: Home
+ },
+];
diff --git a/UIs/Customers/src/app/layouts/system/components/user-menu/index.tsx b/UIs/Customers/src/app/layouts/system/components/user-menu/index.tsx
new file mode 100644
index 0000000..1d9d7b7
--- /dev/null
+++ b/UIs/Customers/src/app/layouts/system/components/user-menu/index.tsx
@@ -0,0 +1,51 @@
+import { UsersIcon } from 'lucide-react';
+import { useAuthStore } from '@/features/auth/store';
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuLabel,
+ DropdownMenuSeparator,
+ DropdownMenuTrigger
+} from '@/shared/components/ui/dropdown-menu';
+import { Avatar, AvatarFallback } from '@/shared/components/ui/avatar';
+import { Button } from '@/shared/components/ui/button';
+import { getInitials, mapGroupName } from '@/shared/utils';
+import { ButtonLogout } from '../button-logout';
+
+export const UserMenu = () => {
+ const user = useAuthStore((state) => state.user);
+
+ if (!user) return null;
+
+ return (
+
+
+
+
+
+
+
+
+
{user.name}
+
+
+ {user.email}
+
+
+
+
+
+
+
+ );
+};
diff --git a/UIs/Customers/src/app/layouts/system/index.tsx b/UIs/Customers/src/app/layouts/system/index.tsx
new file mode 100644
index 0000000..9a22c15
--- /dev/null
+++ b/UIs/Customers/src/app/layouts/system/index.tsx
@@ -0,0 +1,31 @@
+import { SidebarInset, SidebarProvider } from '@/shared/components/ui/sidebar';
+import { HeaderLayout } from './components/header';
+import { Outlet } from 'react-router';
+import { SystemSidebar } from './components/sidebar';
+
+export const SystemLayout = () => {
+ return (
+
+ );
+};
diff --git a/UIs/Customers/src/app/providers/index.tsx b/UIs/Customers/src/app/providers/index.tsx
new file mode 100644
index 0000000..d9ddfe3
--- /dev/null
+++ b/UIs/Customers/src/app/providers/index.tsx
@@ -0,0 +1,12 @@
+import type { ReactNode } from 'react';
+import { QueryClientProvider } from '@tanstack/react-query';
+import { queryClient } from '../config/react-query';
+import { ThemeProvider } from '../theme/theme-provider';
+
+export const AppProviders = ({ children }: { children: ReactNode }) => {
+ return (
+
+ {children}
+
+ );
+};
diff --git a/UIs/Customers/src/app/routers/guards/auth-guard.tsx b/UIs/Customers/src/app/routers/guards/auth-guard.tsx
new file mode 100644
index 0000000..a756da6
--- /dev/null
+++ b/UIs/Customers/src/app/routers/guards/auth-guard.tsx
@@ -0,0 +1,23 @@
+import { useEffect } from 'react';
+import { Outlet, useLocation, Navigate } from 'react-router';
+import { useAuthStore } from '@/features/auth/store';
+
+export const AuthGuard = () => {
+ const location = useLocation();
+ const status = useAuthStore((state) => state.status);
+ const checkSession = useAuthStore((state) => state.checkSession);
+
+ useEffect(() => {
+ checkSession();
+ }, [checkSession]);
+
+ if (status === 'loading' || status === 'idle') {
+ return null;
+ }
+
+ if (status !== 'authenticated') {
+ return ;
+ }
+
+ return ;
+};
diff --git a/UIs/Customers/src/app/routers/guards/guest-guard.tsx b/UIs/Customers/src/app/routers/guards/guest-guard.tsx
new file mode 100644
index 0000000..eb9ce35
--- /dev/null
+++ b/UIs/Customers/src/app/routers/guards/guest-guard.tsx
@@ -0,0 +1,16 @@
+import { Outlet, Navigate } from 'react-router';
+import { useAuthStore } from '@/features/auth/store';
+
+export const GuestGuard = () => {
+ const status = useAuthStore((state) => state.status);
+
+ if (status === 'loading' || status === 'idle') {
+ return null;
+ }
+
+ if (status === 'authenticated') {
+ return ;
+ }
+
+ return ;
+};
diff --git a/UIs/Customers/src/app/routers/index.tsx b/UIs/Customers/src/app/routers/index.tsx
new file mode 100644
index 0000000..3315eef
--- /dev/null
+++ b/UIs/Customers/src/app/routers/index.tsx
@@ -0,0 +1,62 @@
+import { createBrowserRouter, RouterProvider } from 'react-router';
+import { AuthGuard } from './guards/auth-guard';
+import { GuestGuard } from './guards/guest-guard';
+import { PublicLayout } from '../layouts/public';
+
+export const routers = createBrowserRouter([
+ {
+ element: ,
+ children: [
+ {
+ element: ,
+ children: [
+ {
+ path: '/login',
+ lazy: async () => {
+ const { LoginForm } = await import(
+ '@/features/auth/components/login-form'
+ );
+ return { Component: LoginForm };
+ }
+ }
+ ]
+ }
+ ]
+ },
+ {
+ element: ,
+ children: [
+ {
+ path: '/',
+ lazy: async () => {
+ const { SystemLayout } = await import('@/app/layouts/system');
+ return { Component: SystemLayout };
+ },
+ children: [
+ {
+ index: true,
+ lazy: async () => {
+ const { DashboardPage } = await import(
+ '@/features/dashboard/pages'
+ );
+ return { Component: DashboardPage };
+ }
+ },
+ {
+ path: 'dashboard',
+ lazy: async () => {
+ const { DashboardPage } = await import(
+ '@/features/dashboard/pages'
+ );
+ return { Component: DashboardPage };
+ }
+ },
+ ]
+ }
+ ]
+ }
+]);
+
+export function AppRouters() {
+ return ;
+}
diff --git a/UIs/Customers/src/app/theme/theme-provider.tsx b/UIs/Customers/src/app/theme/theme-provider.tsx
new file mode 100644
index 0000000..83f3008
--- /dev/null
+++ b/UIs/Customers/src/app/theme/theme-provider.tsx
@@ -0,0 +1,18 @@
+import type { ComponentProps } from 'react';
+import { ThemeProvider as NextThemesProvider } from 'next-themes';
+
+export function ThemeProvider({
+ children,
+ ...props
+}: ComponentProps) {
+ return (
+
+ {children}
+
+ );
+}
diff --git a/UIs/Customers/src/app/theme/toggle-theme.tsx b/UIs/Customers/src/app/theme/toggle-theme.tsx
new file mode 100644
index 0000000..d8d60da
--- /dev/null
+++ b/UIs/Customers/src/app/theme/toggle-theme.tsx
@@ -0,0 +1,23 @@
+import { useTheme } from 'next-themes';
+import { MoonIcon, SunIcon } from 'lucide-react';
+import { Button } from '../../shared/components/ui/button';
+
+export function ToggleTheme() {
+ const { setTheme, resolvedTheme } = useTheme();
+ const toggleTheme = () =>
+ setTheme(resolvedTheme === 'dark' ? 'light' : 'dark');
+
+ return (
+
+ );
+}
diff --git a/UIs/Customers/src/features/auth/components/login-form/credentials.tsx b/UIs/Customers/src/features/auth/components/login-form/credentials.tsx
new file mode 100644
index 0000000..e12a059
--- /dev/null
+++ b/UIs/Customers/src/features/auth/components/login-form/credentials.tsx
@@ -0,0 +1,70 @@
+import {
+ FormControl,
+ FormField,
+ FormItem,
+ FormLabel,
+ FormMessage
+} from '@/shared/components/ui/form';
+
+import { Input } from '@/shared/components/ui/input';
+import {LockIcon, MailIcon } from 'lucide-react';
+import type { UseFormReturn } from 'react-hook-form';
+import type { LoginFormSchema } from './schema';
+
+interface LoginFormCredentialsProps {
+ form: UseFormReturn;
+}
+
+export function CredentialsFields({
+ form
+}: Readonly) {
+ return (
+ <>
+ (
+
+ E-mail
+
+
+
+
+
+
+
+
+ )}
+ />
+
+ (
+
+ Senha
+
+
+
+
+
+
+
+
+ )}
+ />
+ >
+ );
+}
diff --git a/UIs/Customers/src/features/auth/components/login-form/header.tsx b/UIs/Customers/src/features/auth/components/login-form/header.tsx
new file mode 100644
index 0000000..b64853b
--- /dev/null
+++ b/UIs/Customers/src/features/auth/components/login-form/header.tsx
@@ -0,0 +1,20 @@
+import { AppLogo } from '@/shared/components/global/logo';
+
+export function LoginHeader() {
+ return (
+
+
+
+
Faça login na sua conta.
+
+
+
+ );
+}
diff --git a/UIs/Customers/src/features/auth/components/login-form/index.tsx b/UIs/Customers/src/features/auth/components/login-form/index.tsx
new file mode 100644
index 0000000..65799d9
--- /dev/null
+++ b/UIs/Customers/src/features/auth/components/login-form/index.tsx
@@ -0,0 +1,53 @@
+import { useForm } from 'react-hook-form';
+import { zodResolver } from '@hookform/resolvers/zod';
+import { Form } from '@/shared/components/ui/form';
+import { useLogin } from '../../hooks/use-login';
+import { loginFormSchema, type LoginFormSchema } from './schema';
+import { LoginHeader } from './header';
+import { CredentialsFields } from './credentials';
+import { LoginTermsAndServices } from './terms-and-services';
+import { formDraftService } from '@/shared/services/form-draft';
+import { SubmitingButton } from '@/shared/components/global/submiting-button';
+
+export function LoginForm() {
+ const { mutateAsync: login, isPending } = useLogin();
+
+ const form = useForm({
+ resolver: zodResolver(loginFormSchema),
+ defaultValues: {
+ username: '',
+ password: ''
+ }
+ });
+
+ const onSubmit = (data: LoginFormSchema) => {
+ login({
+ username: data.username,
+ password: data.password,
+ });
+
+ data.password = '********';
+ formDraftService.write('login', data);
+ };
+
+ return (
+
+ );
+}
diff --git a/UIs/Customers/src/features/auth/components/login-form/schema/index.ts b/UIs/Customers/src/features/auth/components/login-form/schema/index.ts
new file mode 100644
index 0000000..c8a083e
--- /dev/null
+++ b/UIs/Customers/src/features/auth/components/login-form/schema/index.ts
@@ -0,0 +1,8 @@
+import { z } from 'zod';
+
+export const loginFormSchema = z.object({
+ username: z.string().email('O formato do e-mail informado é inválido'),
+ password: z.string().min(4, 'A senha deve ter no mínimo 3 caracteres.')
+});
+
+export type LoginFormSchema = z.infer;
diff --git a/UIs/Customers/src/features/auth/components/login-form/terms-and-services.tsx b/UIs/Customers/src/features/auth/components/login-form/terms-and-services.tsx
new file mode 100644
index 0000000..602cce7
--- /dev/null
+++ b/UIs/Customers/src/features/auth/components/login-form/terms-and-services.tsx
@@ -0,0 +1,9 @@
+export function LoginTermsAndServices() {
+ return (
+
+ );
+}
diff --git a/UIs/Customers/src/features/auth/hooks/use-login.ts b/UIs/Customers/src/features/auth/hooks/use-login.ts
new file mode 100644
index 0000000..c81f44e
--- /dev/null
+++ b/UIs/Customers/src/features/auth/hooks/use-login.ts
@@ -0,0 +1,20 @@
+import { useMutation } from '@tanstack/react-query';
+import { authService } from '../services';
+import { useAuthStore } from '../store';
+import type { LoginCredentials } from '../types';
+
+export const useLogin = () => {
+ const setSession = useAuthStore((state) => state.setSession);
+
+ return useMutation({
+ mutationFn: (credentials: LoginCredentials) =>
+ authService.login(credentials),
+ meta: {
+ successMessage: 'Login realizado com sucesso! Redirecionando...'
+ },
+ onSuccess: (session) => {
+ console.log(session)
+ setSession(session);
+ }
+ });
+};
diff --git a/UIs/Customers/src/features/auth/services/index.ts b/UIs/Customers/src/features/auth/services/index.ts
new file mode 100644
index 0000000..3b5c092
--- /dev/null
+++ b/UIs/Customers/src/features/auth/services/index.ts
@@ -0,0 +1,30 @@
+
+import type {
+ AuthSession,
+ LoginCredentials,
+} from '../types';
+
+
+export const authService = {
+ login: async (credentials: LoginCredentials): Promise => {
+ const now = new Date()
+ console.log(now)
+ const expiresAt = new Date(now.getFullYear(), now.getMonth(), now.getDay(), now.getHours() + 2, now.getMinutes(), now.getSeconds())
+ console.log(expiresAt)
+
+ return {
+ token: "TOKENT.EMPLATE.EXAMPLE",
+ status: "authenticated",
+ expiresAt: expiresAt.getTime(),
+ user: {
+ name: "User Template Example",
+ email: credentials.username,
+ id: "123456",
+ }
+ };
+ },
+
+ logout: async () => {
+ return Promise.resolve();
+ }
+};
diff --git a/UIs/Customers/src/features/auth/store/index.ts b/UIs/Customers/src/features/auth/store/index.ts
new file mode 100644
index 0000000..aa90179
--- /dev/null
+++ b/UIs/Customers/src/features/auth/store/index.ts
@@ -0,0 +1,54 @@
+import { create } from 'zustand';
+import { persist, createJSONStorage } from 'zustand/middleware';
+import type { AuthSession } from '../types';
+import { authService } from '../services';
+
+interface AuthState extends AuthSession {
+ setSession: (session: AuthSession) => void;
+ logout: () => void;
+ checkSession: () => void;
+}
+
+const initialState: AuthSession = {
+ token: null,
+ user: null,
+ status: 'unauthenticated',
+ expiresAt: null
+};
+
+export const useAuthStore = create()(
+ persist(
+ (set, get) => ({
+ ...initialState,
+
+ setSession: (session) => {
+ console.log(session)
+ set(session);
+ },
+
+ logout: () => {
+ authService.logout();
+ set(initialState);
+ },
+
+ checkSession: () => {
+ const { expiresAt, token } = get();
+ console.log(expiresAt)
+ console.log(Date.now())
+
+ if (!token || !expiresAt) return;
+
+ }
+ }),
+ {
+ name: 'template-app',
+ storage: createJSONStorage(() => localStorage),
+ partialize: (state) => ({
+ token: state.token,
+ user: state.user,
+ expiresAt: state.expiresAt,
+ status: state.token ? 'authenticated' : 'unauthenticated'
+ })
+ }
+ )
+);
diff --git a/UIs/Customers/src/features/auth/types/index.ts b/UIs/Customers/src/features/auth/types/index.ts
new file mode 100644
index 0000000..28793d8
--- /dev/null
+++ b/UIs/Customers/src/features/auth/types/index.ts
@@ -0,0 +1,2 @@
+export * from './dtos';
+export * from './models';
diff --git a/UIs/Customers/src/features/auth/types/models.ts b/UIs/Customers/src/features/auth/types/models.ts
new file mode 100644
index 0000000..835207c
--- /dev/null
+++ b/UIs/Customers/src/features/auth/types/models.ts
@@ -0,0 +1,25 @@
+export type SessionStatus =
+ | 'loading'
+ | 'authenticated'
+ | 'unauthenticated'
+ | 'expired'
+ | 'idle';
+
+export interface LoginCredentials {
+ username: string;
+ password: string;
+}
+
+export interface User {
+ id: string;
+ email: string;
+ name: string;
+
+}
+
+export interface AuthSession {
+ token: string | null;
+ expiresAt: number | null;
+ user: User | null;
+ status: SessionStatus;
+}
diff --git a/UIs/Customers/src/features/dashboard/components/chart/index.tsx b/UIs/Customers/src/features/dashboard/components/chart/index.tsx
new file mode 100644
index 0000000..6e4cfb9
--- /dev/null
+++ b/UIs/Customers/src/features/dashboard/components/chart/index.tsx
@@ -0,0 +1,288 @@
+'use client';
+
+import * as React from 'react';
+import { Area, AreaChart, CartesianGrid, XAxis } from 'recharts';
+
+import { useIsMobile } from '@/shared/hooks/use-mobile';
+import {
+ Card,
+ CardAction,
+ CardContent,
+ CardDescription,
+ CardHeader,
+ CardTitle
+} from '@/shared/components/ui/card';
+import {
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig
+} from '@/shared/components/ui/chart';
+import {
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+ SelectValue
+} from '@/shared/components/ui/select';
+import { ToggleGroup, ToggleGroupItem } from '@/shared/components/ui/toggle-group';
+
+export const description = 'An interactive area chart';
+
+const chartData = [
+ { date: '2024-04-01', desktop: 222, mobile: 150 },
+ { date: '2024-04-02', desktop: 97, mobile: 180 },
+ { date: '2024-04-03', desktop: 167, mobile: 120 },
+ { date: '2024-04-04', desktop: 242, mobile: 260 },
+ { date: '2024-04-05', desktop: 373, mobile: 290 },
+ { date: '2024-04-06', desktop: 301, mobile: 340 },
+ { date: '2024-04-07', desktop: 245, mobile: 180 },
+ { date: '2024-04-08', desktop: 409, mobile: 320 },
+ { date: '2024-04-09', desktop: 59, mobile: 110 },
+ { date: '2024-04-10', desktop: 261, mobile: 190 },
+ { date: '2024-04-11', desktop: 327, mobile: 350 },
+ { date: '2024-04-12', desktop: 292, mobile: 210 },
+ { date: '2024-04-13', desktop: 342, mobile: 380 },
+ { date: '2024-04-14', desktop: 137, mobile: 220 },
+ { date: '2024-04-15', desktop: 120, mobile: 170 },
+ { date: '2024-04-16', desktop: 138, mobile: 190 },
+ { date: '2024-04-17', desktop: 446, mobile: 360 },
+ { date: '2024-04-18', desktop: 364, mobile: 410 },
+ { date: '2024-04-19', desktop: 243, mobile: 180 },
+ { date: '2024-04-20', desktop: 89, mobile: 150 },
+ { date: '2024-04-21', desktop: 137, mobile: 200 },
+ { date: '2024-04-22', desktop: 224, mobile: 170 },
+ { date: '2024-04-23', desktop: 138, mobile: 230 },
+ { date: '2024-04-24', desktop: 387, mobile: 290 },
+ { date: '2024-04-25', desktop: 215, mobile: 250 },
+ { date: '2024-04-26', desktop: 75, mobile: 130 },
+ { date: '2024-04-27', desktop: 383, mobile: 420 },
+ { date: '2024-04-28', desktop: 122, mobile: 180 },
+ { date: '2024-04-29', desktop: 315, mobile: 240 },
+ { date: '2024-04-30', desktop: 454, mobile: 380 },
+ { date: '2024-05-01', desktop: 165, mobile: 220 },
+ { date: '2024-05-02', desktop: 293, mobile: 310 },
+ { date: '2024-05-03', desktop: 247, mobile: 190 },
+ { date: '2024-05-04', desktop: 385, mobile: 420 },
+ { date: '2024-05-05', desktop: 481, mobile: 390 },
+ { date: '2024-05-06', desktop: 498, mobile: 520 },
+ { date: '2024-05-07', desktop: 388, mobile: 300 },
+ { date: '2024-05-08', desktop: 149, mobile: 210 },
+ { date: '2024-05-09', desktop: 227, mobile: 180 },
+ { date: '2024-05-10', desktop: 293, mobile: 330 },
+ { date: '2024-05-11', desktop: 335, mobile: 270 },
+ { date: '2024-05-12', desktop: 197, mobile: 240 },
+ { date: '2024-05-13', desktop: 197, mobile: 160 },
+ { date: '2024-05-14', desktop: 448, mobile: 490 },
+ { date: '2024-05-15', desktop: 473, mobile: 380 },
+ { date: '2024-05-16', desktop: 338, mobile: 400 },
+ { date: '2024-05-17', desktop: 499, mobile: 420 },
+ { date: '2024-05-18', desktop: 315, mobile: 350 },
+ { date: '2024-05-19', desktop: 235, mobile: 180 },
+ { date: '2024-05-20', desktop: 177, mobile: 230 },
+ { date: '2024-05-21', desktop: 82, mobile: 140 },
+ { date: '2024-05-22', desktop: 81, mobile: 120 },
+ { date: '2024-05-23', desktop: 252, mobile: 290 },
+ { date: '2024-05-24', desktop: 294, mobile: 220 },
+ { date: '2024-05-25', desktop: 201, mobile: 250 },
+ { date: '2024-05-26', desktop: 213, mobile: 170 },
+ { date: '2024-05-27', desktop: 420, mobile: 460 },
+ { date: '2024-05-28', desktop: 233, mobile: 190 },
+ { date: '2024-05-29', desktop: 78, mobile: 130 },
+ { date: '2024-05-30', desktop: 340, mobile: 280 },
+ { date: '2024-05-31', desktop: 178, mobile: 230 },
+ { date: '2024-06-01', desktop: 178, mobile: 200 },
+ { date: '2024-06-02', desktop: 470, mobile: 410 },
+ { date: '2024-06-03', desktop: 103, mobile: 160 },
+ { date: '2024-06-04', desktop: 439, mobile: 380 },
+ { date: '2024-06-05', desktop: 88, mobile: 140 },
+ { date: '2024-06-06', desktop: 294, mobile: 250 },
+ { date: '2024-06-07', desktop: 323, mobile: 370 },
+ { date: '2024-06-08', desktop: 385, mobile: 320 },
+ { date: '2024-06-09', desktop: 438, mobile: 480 },
+ { date: '2024-06-10', desktop: 155, mobile: 200 },
+ { date: '2024-06-11', desktop: 92, mobile: 150 },
+ { date: '2024-06-12', desktop: 492, mobile: 420 },
+ { date: '2024-06-13', desktop: 81, mobile: 130 },
+ { date: '2024-06-14', desktop: 426, mobile: 380 },
+ { date: '2024-06-15', desktop: 307, mobile: 350 },
+ { date: '2024-06-16', desktop: 371, mobile: 310 },
+ { date: '2024-06-17', desktop: 475, mobile: 520 },
+ { date: '2024-06-18', desktop: 107, mobile: 170 },
+ { date: '2024-06-19', desktop: 341, mobile: 290 },
+ { date: '2024-06-20', desktop: 408, mobile: 450 },
+ { date: '2024-06-21', desktop: 169, mobile: 210 },
+ { date: '2024-06-22', desktop: 317, mobile: 270 },
+ { date: '2024-06-23', desktop: 480, mobile: 530 },
+ { date: '2024-06-24', desktop: 132, mobile: 180 },
+ { date: '2024-06-25', desktop: 141, mobile: 190 },
+ { date: '2024-06-26', desktop: 434, mobile: 380 },
+ { date: '2024-06-27', desktop: 448, mobile: 490 },
+ { date: '2024-06-28', desktop: 149, mobile: 200 },
+ { date: '2024-06-29', desktop: 103, mobile: 160 },
+ { date: '2024-06-30', desktop: 446, mobile: 400 }
+];
+
+const chartConfig = {
+ visitors: {
+ label: 'Visitors'
+ },
+ desktop: {
+ label: 'Desktop',
+ color: 'var(--chart-1)'
+ },
+ mobile: {
+ label: 'Mobile',
+ color: 'var(--chart-2)'
+ }
+} satisfies ChartConfig;
+
+export function ChartAreaInteractive() {
+ const isMobile = useIsMobile();
+ const [timeRange, setTimeRange] = React.useState('90d');
+
+ React.useEffect(() => {
+ if (isMobile) {
+ setTimeRange('7d');
+ }
+ }, [isMobile]);
+
+ const filteredData = chartData.filter((item) => {
+ const date = new Date(item.date);
+ const referenceDate = new Date('2024-06-30');
+ let daysToSubtract = 90;
+ if (timeRange === '30d') {
+ daysToSubtract = 30;
+ } else if (timeRange === '7d') {
+ daysToSubtract = 7;
+ }
+ const startDate = new Date(referenceDate);
+ startDate.setDate(startDate.getDate() - daysToSubtract);
+ return date >= startDate;
+ });
+
+ return (
+
+
+ Total Visitors
+
+
+ Total for the last 3 months
+
+ Last 3 months
+
+
+
+ Last 3 months
+ Last 30 days
+ Last 7 days
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {
+ const date = new Date(value);
+ return date.toLocaleDateString('en-US', {
+ month: 'short',
+ day: 'numeric'
+ });
+ }}
+ />
+ {
+ return new Date(value).toLocaleDateString('en-US', {
+ month: 'short',
+ day: 'numeric'
+ });
+ }}
+ indicator="dot"
+ />
+ }
+ />
+
+
+
+
+
+
+ );
+}
diff --git a/UIs/Customers/src/features/dashboard/components/stats/index.tsx b/UIs/Customers/src/features/dashboard/components/stats/index.tsx
new file mode 100644
index 0000000..4ca66e5
--- /dev/null
+++ b/UIs/Customers/src/features/dashboard/components/stats/index.tsx
@@ -0,0 +1,101 @@
+import { Badge } from '@/shared/components/ui/badge';
+import {
+ Card,
+ CardAction,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle
+} from '@/shared/components/ui/card';
+import { TrendingDown, TrendingUp } from 'lucide-react';
+
+export function StatsCards() {
+ return (
+
+
+
+ Total Revenue
+
+ $1,250.00
+
+
+
+
+ +12.5%
+
+
+
+
+
+ Trending up this month
+
+
+ Visitors for the last 6 months
+
+
+
+
+
+ New Customers
+
+ 1,234
+
+
+
+
+ -20%
+
+
+
+
+
+ Down 20% this period
+
+
+ Acquisition needs attention
+
+
+
+
+
+ Active Accounts
+
+ 45,678
+
+
+
+
+ +12.5%
+
+
+
+
+
+ Strong user retention
+
+ Engagement exceed targets
+
+
+
+
+ Growth Rate
+
+ 4.5%
+
+
+
+
+ +4.5%
+
+
+
+
+
+ Steady performance increase
+
+ Meets growth projections
+
+
+
+ );
+}
diff --git a/UIs/Customers/src/features/dashboard/pages/index.tsx b/UIs/Customers/src/features/dashboard/pages/index.tsx
new file mode 100644
index 0000000..af53c3d
--- /dev/null
+++ b/UIs/Customers/src/features/dashboard/pages/index.tsx
@@ -0,0 +1,17 @@
+import { PageHeader } from '@/shared/components/global/page-header';
+import { ChartAreaInteractive } from '../components/chart';
+import { StatsCards } from '../components/stats';
+
+export const DashboardPage = () => {
+ return (
+ <>
+
+
+ >
+ );
+};
diff --git a/UIs/Customers/src/index.css b/UIs/Customers/src/index.css
new file mode 100644
index 0000000..f668f48
--- /dev/null
+++ b/UIs/Customers/src/index.css
@@ -0,0 +1,179 @@
+@import "tailwindcss";
+@import "tw-animate-css";
+
+@custom-variant dark (&:is(.dark *));
+
+@theme inline {
+ --radius-sm: calc(var(--radius) - 4px);
+ --radius-md: calc(var(--radius) - 2px);
+ --radius-lg: var(--radius);
+ --radius-xl: calc(var(--radius) + 4px);
+ --color-background: var(--background);
+ --color-foreground: var(--foreground);
+ --color-card: var(--card);
+ --color-card-foreground: var(--card-foreground);
+ --color-popover: var(--popover);
+ --color-popover-foreground: var(--popover-foreground);
+ --color-primary: var(--primary);
+ --color-primary-foreground: var(--primary-foreground);
+ --color-secondary: var(--secondary);
+ --color-secondary-foreground: var(--secondary-foreground);
+ --color-muted: var(--muted);
+ --color-muted-foreground: var(--muted-foreground);
+ --color-accent: var(--accent);
+ --color-accent-foreground: var(--accent-foreground);
+ --color-destructive: var(--destructive);
+ --color-border: var(--border);
+ --color-input: var(--input);
+ --color-ring: var(--ring);
+ --color-chart-1: var(--chart-1);
+ --color-chart-2: var(--chart-2);
+ --color-chart-3: var(--chart-3);
+ --color-chart-4: var(--chart-4);
+ --color-chart-5: var(--chart-5);
+ --color-sidebar: var(--sidebar);
+ --color-sidebar-foreground: var(--sidebar-foreground);
+ --color-sidebar-primary: var(--sidebar-primary);
+ --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
+ --color-sidebar-accent: var(--sidebar-accent);
+ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
+ --color-sidebar-border: var(--sidebar-border);
+ --color-sidebar-ring: var(--sidebar-ring);
+}
+
+:root {
+ font-family: 'Poppins', sans-serif;
+ font-synthesis: none;
+ text-rendering: optimizeLegibility;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+ --radius: 0.625rem;
+ --background: oklch(1 0 0);
+ --foreground: oklch(0.13 0.028 261.692);
+ --card: oklch(1 0 0);
+ --card-foreground: oklch(0.13 0.028 261.692);
+ --popover: oklch(1 0 0);
+ --popover-foreground: oklch(0.13 0.028 261.692);
+ --primary: oklch(0.21 0.034 264.665);
+ --primary-foreground: oklch(0.985 0.002 247.839);
+ --secondary: oklch(0.967 0.003 264.542);
+ --secondary-foreground: oklch(0.21 0.034 264.665);
+ --muted: oklch(0.967 0.003 264.542);
+ --muted-foreground: oklch(0.551 0.027 264.364);
+ --accent: oklch(0.967 0.003 264.542);
+ --accent-foreground: oklch(0.21 0.034 264.665);
+ --destructive: oklch(0.577 0.245 27.325);
+ --border: oklch(0.928 0.006 264.531);
+ --input: oklch(0.928 0.006 264.531);
+ --ring: oklch(0.707 0.022 261.325);
+ --chart-1: var(--color-red-900);
+ --chart-2: var(--color-red-500);
+ --chart-3: oklch(0.398 0.07 227.392);
+ --chart-4: oklch(0.828 0.189 84.429);
+ --chart-5: oklch(0.769 0.188 70.08);
+ --sidebar: oklch(0.985 0.002 247.839);
+ --sidebar-foreground: oklch(0.13 0.028 261.692);
+ --sidebar-primary: oklch(0.21 0.034 264.665);
+ --sidebar-primary-foreground: oklch(0.985 0.002 247.839);
+ --sidebar-accent: oklch(0.967 0.003 264.542);
+ --sidebar-accent-foreground: oklch(0.21 0.034 264.665);
+ --sidebar-border: oklch(0.928 0.006 264.531);
+ --sidebar-ring: oklch(0.707 0.022 261.325);
+}
+
+.dark {
+ --background: oklch(0.141 0.005 285.823);
+ --foreground: oklch(0.985 0 0);
+ --card: oklch(0.21 0.006 285.885);
+ --card-foreground: oklch(0.985 0 0);
+ --popover: oklch(0.21 0.006 285.885);
+ --popover-foreground: oklch(0.985 0 0);
+ --primary: oklch(0.92 0.004 286.32);
+ --primary-foreground: oklch(0.21 0.006 285.885);
+ --secondary: oklch(0.274 0.006 286.033);
+ --secondary-foreground: oklch(0.985 0 0);
+ --muted: oklch(0.274 0.006 286.033);
+ --muted-foreground: oklch(0.705 0.015 286.067);
+ --accent: oklch(0.274 0.006 286.033);
+ --accent-foreground: oklch(0.985 0 0);
+ --destructive: oklch(0.704 0.191 22.216);
+ --border: oklch(1 0 0 / 10%);
+ --input: oklch(1 0 0 / 15%);
+ --ring: oklch(0.552 0.016 285.938);
+ --chart-1: var(--color-red-700);
+ --chart-2: var(--color-red-300);
+ --chart-3: oklch(0.769 0.188 70.08);
+ --chart-4: oklch(0.627 0.265 303.9);
+ --chart-5: oklch(0.645 0.246 16.439);
+ --sidebar: oklch(0.21 0.006 285.885);
+ --sidebar-foreground: oklch(0.985 0 0);
+ --sidebar-primary: oklch(0.488 0.243 264.376);
+ --sidebar-primary-foreground: oklch(0.985 0 0);
+ --sidebar-accent: oklch(0.274 0.006 286.033);
+ --sidebar-accent-foreground: oklch(0.985 0 0);
+ --sidebar-border: oklch(1 0 0 / 10%);
+ --sidebar-ring: oklch(0.552 0.016 285.938);
+}
+
+
+@layer base {
+ * {
+ @apply border-border outline-ring/50;
+ }
+ body {
+ @apply bg-background text-foreground;
+ margin: 0;
+ font-family: 'Poppins', sans-serif;
+ }
+ :-webkit-scrollbar {
+ width: 10px;
+ height: 10px;
+ }
+
+ ::-webkit-scrollbar-track {
+ background: transparent;
+ }
+
+ ::-webkit-scrollbar-thumb {
+ background-color: #b91c1c;
+ border-radius: 10px;
+ border: 2px solid var(--background);
+ }
+
+ ::-webkit-scrollbar-thumb:hover {
+ background-color: #991b1b;
+ }
+
+ * {
+ scrollbar-width: thin;
+ scrollbar-color: #b91c1c transparent;
+ }
+
+ .font-extralight,
+ .font-light,
+ .font-medium,
+ .font-normal {
+ font-family: 'Poppins', sans-serif;
+ }
+
+ .font-semibold,
+ .font-bold,
+ .font-extrabold,
+ .font-black {
+ font-family: 'Montserrat', sans-serif;
+ }
+
+ .font-weight-200,
+ .font-weight-300,
+ .font-weight-400 {
+ font-family: 'Poppins', sans-serif;
+ }
+
+ .font-weight-500,
+ .font-weight-600,
+ .font-weight-700,
+ .font-weight-800,
+ .font-weight-900 {
+ font-family: 'Montserrat', sans-serif;
+ }
+}
\ No newline at end of file
diff --git a/UIs/Customers/src/main.tsx b/UIs/Customers/src/main.tsx
new file mode 100644
index 0000000..b5bfb2c
--- /dev/null
+++ b/UIs/Customers/src/main.tsx
@@ -0,0 +1,11 @@
+import './index.css';
+import App from './app/app';
+
+import { createRoot } from 'react-dom/client';
+import { StrictMode } from 'react';
+
+createRoot(document.getElementById('root')!).render(
+
+
+
+);
diff --git a/UIs/Customers/src/shared/components/global/datatable/hook/usetable.test.tsx b/UIs/Customers/src/shared/components/global/datatable/hook/usetable.test.tsx
new file mode 100644
index 0000000..7103830
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/datatable/hook/usetable.test.tsx
@@ -0,0 +1,37 @@
+import { renderHook } from '@testing-library/react';
+import { describe, it, expect, vi } from 'vitest';
+import { useDataTable, DataTableContext } from './usetable';
+import { type Table, type Row } from '@tanstack/react-table';
+
+const MockTableInstance = {
+ getCoreRowModel: vi.fn(),
+ setPageSize: vi.fn()
+} as unknown as Table;
+
+const MockProvider = ({ children }: { children: React.ReactNode }) => (
+ , index: number) => React.ReactNode
+ }}
+ >
+ {children}
+
+);
+
+describe('useDataTable', () => {
+ it('should return the context value when used inside a DataTable provider', () => {
+ const { result } = renderHook(() => useDataTable<{ id: string }>(), {
+ wrapper: MockProvider
+ });
+
+ expect(result.current.table).toBe(MockTableInstance);
+ expect(result.current.renderSubRow).toBeInstanceOf(Function);
+ });
+
+ it('should throw an error when used outside of a DataTable provider (Guard Clause)', () => {
+ expect(() => renderHook(() => useDataTable())).toThrow(
+ 'useDataTable must be used within a DataTable'
+ );
+ });
+});
diff --git a/UIs/Customers/src/shared/components/global/datatable/hook/usetable.tsx b/UIs/Customers/src/shared/components/global/datatable/hook/usetable.tsx
new file mode 100644
index 0000000..ce3a0f5
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/datatable/hook/usetable.tsx
@@ -0,0 +1,26 @@
+'use client';
+
+import type { Row, Table } from '@tanstack/react-table';
+import { createContext, useContext, type ReactNode } from 'react';
+
+type DataTableContextType = {
+ table: Table;
+ emptyMessage?: string;
+ renderSubRow?: (row: Row, index: number) => ReactNode;
+};
+
+export const DataTableContext = createContext<
+ DataTableContextType | undefined
+>(undefined);
+
+export const useDataTable = () => {
+ const context = useContext(
+ DataTableContext as React.Context | undefined>
+ );
+
+ if (!context) {
+ throw new Error('useDataTable must be used within a DataTable');
+ }
+
+ return context;
+};
diff --git a/UIs/Customers/src/shared/components/global/datatable/index.test.tsx b/UIs/Customers/src/shared/components/global/datatable/index.test.tsx
new file mode 100644
index 0000000..bc3e2b2
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/datatable/index.test.tsx
@@ -0,0 +1,229 @@
+import { render, screen, act, fireEvent } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import { describe, it, expect, vi, afterEach, beforeAll } from 'vitest';
+import {
+ getCoreRowModel,
+ getPaginationRowModel,
+ getFilteredRowModel,
+ getSortedRowModel,
+ getExpandedRowModel,
+ type ColumnDef,
+ type TableState
+} from '@tanstack/react-table';
+
+import { DataTable } from './pieces/datatable';
+import { DataTableBody } from './pieces/datatable-body';
+import { PaginationControllers } from './pieces/datatable-pagination-controllers';
+import { DataTableHeader } from './pieces/datatable-header';
+import { DataTableTextFilter } from './pieces/datatable-text-filter';
+import { DataTableHeaderSortableColumn } from './pieces/datatable-header-sortable-column';
+import { DataTableDropdownColumnsVisibility } from './pieces/datatable-dropdown-column-visibility';
+
+beforeAll(() => {
+ globalThis.ResizeObserver = class ResizeObserver {
+ observe() {}
+ unobserve() {}
+ disconnect() {}
+ };
+ window.HTMLElement.prototype.scrollIntoView = vi.fn();
+ window.HTMLElement.prototype.hasPointerCapture = vi.fn();
+ window.HTMLElement.prototype.setPointerCapture = vi.fn();
+ window.HTMLElement.prototype.releasePointerCapture = vi.fn();
+});
+
+type TestData = {
+ id: string;
+ name: string;
+ price: number;
+};
+
+const mockData: TestData[] = Array.from({ length: 25 }, (_, i) => ({
+ id: `id-${i}`,
+ name: `Produto ${i < 10 ? '0' + i : i}`,
+ price: (i + 1) * 10
+}));
+
+const columns: ColumnDef[] = [
+ {
+ accessorKey: 'name',
+ header: ({ column }) => (
+
+ ),
+ meta: { nameInFilters: 'Nome do Produto' }
+ },
+ {
+ accessorKey: 'price',
+ header: 'Preço',
+ cell: ({ getValue }) => `R$ ${getValue()}`
+ }
+];
+
+const IntegrationTable = ({
+ data = mockData,
+ customEmptyMessage,
+ showSubRow = false,
+ initialState = {}
+}: {
+ data?: TestData[];
+ customEmptyMessage?: string;
+ showSubRow?: boolean;
+ initialState?: Partial;
+}) => (
+ (
+
+ Detalhes: {row.original.name}
+
+ )
+ : undefined
+ }
+ tableOptions={{
+ data,
+ columns,
+ getCoreRowModel: getCoreRowModel(),
+ getPaginationRowModel: getPaginationRowModel(),
+ getFilteredRowModel: getFilteredRowModel(),
+ getSortedRowModel: getSortedRowModel(),
+ getExpandedRowModel: getExpandedRowModel(),
+ getRowCanExpand: () => true,
+ initialState: {
+ pagination: { pageSize: 10 },
+ ...initialState
+ }
+ }}
+ >
+
+
+
+
+
+
+);
+
+describe('DataTable Integration & Coverage', () => {
+ afterEach(() => {
+ vi.useRealTimers();
+ });
+
+ it('should render custom empty message', () => {
+ render(
+
+ );
+ expect(screen.getByText('Nada encontrado!')).toBeInTheDocument();
+ });
+
+ it('should navigate using ALL pagination buttons (First, Prev, Next, Last)', async () => {
+ const user = userEvent.setup();
+ render();
+
+ const firstBtn = screen.getByRole('button', { name: 'Primeira página' });
+ const prevBtn = screen.getByRole('button', { name: 'Página anterior' });
+ const nextBtn = screen.getByRole('button', { name: 'Próxima página' });
+ const lastBtn = screen.getByRole('button', { name: 'Última página' });
+
+ expect(prevBtn).toBeDisabled();
+ expect(nextBtn).toBeEnabled();
+ expect(screen.getByText('Produto 00')).toBeInTheDocument();
+
+ await user.click(nextBtn);
+ expect(screen.getByText('Produto 10')).toBeInTheDocument();
+ expect(prevBtn).toBeEnabled();
+
+ await user.click(lastBtn);
+ expect(screen.getByText('Produto 24')).toBeInTheDocument();
+ expect(nextBtn).toBeDisabled();
+
+ await user.click(prevBtn);
+ expect(screen.getByText('Produto 10')).toBeInTheDocument();
+
+ await user.click(firstBtn);
+ expect(screen.getByText('Produto 00')).toBeInTheDocument();
+ });
+
+ it('should filter by specific column AND global filter', async () => {
+ vi.useFakeTimers();
+ render();
+
+ const globalInput = screen.getByPlaceholderText('Buscar Geral...');
+ fireEvent.change(globalInput, { target: { value: 'Produto 2' } });
+
+ act(() => {
+ vi.advanceTimersByTime(500);
+ });
+ expect(screen.getByText('Produto 20')).toBeInTheDocument();
+
+ const nameInput = screen.getByPlaceholderText('Filtrar Nome...');
+ fireEvent.change(nameInput, { target: { value: '23' } });
+
+ act(() => {
+ vi.advanceTimersByTime(500);
+ });
+
+ expect(screen.getByText('Produto 23')).toBeInTheDocument();
+ expect(screen.queryByText('Produto 20')).not.toBeInTheDocument();
+ });
+
+ it('should sort Ascending and Descending', async () => {
+ const user = userEvent.setup();
+ render();
+
+ const headerBtn = screen.getByRole('button', { name: 'Nome do Produto' });
+
+ await user.click(headerBtn);
+ await user.click(screen.getByRole('menuitem', { name: 'Desc' }));
+ let rows = screen.getAllByRole('row');
+ expect(rows[1]).toHaveTextContent('Produto 24');
+
+ await user.click(headerBtn);
+ await user.click(screen.getByRole('menuitem', { name: 'Asc' }));
+
+ rows = screen.getAllByRole('row');
+ expect(rows[1]).toHaveTextContent('Produto 00');
+ });
+
+ it('should render sub-row content when expanded', () => {
+ render(
+
+ );
+
+ const subRows = screen.getAllByTestId('sub-row-content');
+
+ expect(subRows.length).toBeGreaterThan(0);
+ expect(subRows[0]).toHaveTextContent('Detalhes: Produto 00');
+ });
+
+ it('should change page size via select', async () => {
+ const user = userEvent.setup();
+ render();
+
+ const selectTrigger = screen.getByRole('combobox');
+ await user.click(selectTrigger);
+
+ const option20 = await screen.findByRole('option', { name: '20' });
+ await user.click(option20);
+
+ expect(screen.getByText('Produto 19')).toBeInTheDocument();
+ expect(screen.getByText(/Página 1 de 2/i)).toBeInTheDocument();
+ });
+
+ it('should toggle column visibility', async () => {
+ const user = userEvent.setup();
+ render();
+
+ expect(screen.getByText('Preço')).toBeInTheDocument();
+
+ const toggleBtn = screen.getByRole('button', { name: /Exibir Colunas/i });
+ await user.click(toggleBtn);
+
+ const priceOption = screen.getByRole('menuitemcheckbox', { name: 'Preço' });
+ await user.click(priceOption);
+
+ expect(screen.queryByText('Preço')).not.toBeInTheDocument();
+ });
+});
diff --git a/UIs/Customers/src/shared/components/global/datatable/index.tsx b/UIs/Customers/src/shared/components/global/datatable/index.tsx
new file mode 100644
index 0000000..f28973b
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/datatable/index.tsx
@@ -0,0 +1,8 @@
+export { DataTable, type IDataTable } from './pieces/datatable';
+export { DataTableContent } from './pieces/datatable-content';
+export { DataTableDropdownColumnsVisibility } from './pieces/datatable-dropdown-column-visibility';
+export { PaginationControllers } from './pieces/datatable-pagination-controllers';
+export { DataTableTextFilter } from './pieces/datatable-text-filter';
+export { NestedDataTable } from './pieces/datatable-nested-table';
+export { DataTableSelectFilter } from './pieces/datatable-select-filter';
+export { DataTableDateRangeFilter } from './pieces/datatable-date-range-filter';
diff --git a/UIs/Customers/src/shared/components/global/datatable/pieces/data-table-tabs-filter/index.tsx b/UIs/Customers/src/shared/components/global/datatable/pieces/data-table-tabs-filter/index.tsx
new file mode 100644
index 0000000..0f06501
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/datatable/pieces/data-table-tabs-filter/index.tsx
@@ -0,0 +1,52 @@
+import { Tabs, TabsList, TabsTrigger } from '@/shared/components/ui/tabs';
+import { useDataTable } from '../../hook/usetable';
+import { useState } from 'react';
+
+export interface IDataTableTabFilter {
+ column: string;
+ options: {
+ value: T;
+ label: string;
+ }[];
+ initialValue: T;
+ onValueChange?: (value: T) => void;
+}
+
+export function DataTableTabFilter({
+ column,
+ options,
+ initialValue,
+ onValueChange
+}: IDataTableTabFilter) {
+ const { table } = useDataTable();
+ const [currentValue, setCurrentValue] = useState(initialValue);
+ const tableColumn = table.getColumn(column);
+
+ const handleValueChange = (newValue: T) => {
+ tableColumn?.setFilterValue(newValue);
+ setCurrentValue(newValue);
+ onValueChange?.(newValue);
+ };
+
+ return (
+ void}
+ className="w-auto"
+ >
+
+ {options.map(({ value, label }) => {
+ return (
+
+ {label}
+
+ );
+ })}
+
+
+ );
+}
diff --git a/UIs/Customers/src/shared/components/global/datatable/pieces/datatable-body/index.tsx b/UIs/Customers/src/shared/components/global/datatable/pieces/datatable-body/index.tsx
new file mode 100644
index 0000000..051cdf5
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/datatable/pieces/datatable-body/index.tsx
@@ -0,0 +1,47 @@
+import { Fragment, memo } from 'react';
+import { flexRender } from '@tanstack/react-table';
+import { TableBody, TableCell, TableRow } from '@/shared/components/ui/table';
+import { useDataTable } from '../../hook/usetable';
+
+export function DataTableBody() {
+ const { table, emptyMessage, renderSubRow } = useDataTable();
+
+ return (
+
+ {table.getRowModel().rows.length > 0 ? (
+ table.getRowModel().rows.map((row, index) => (
+
+
+ {row.getVisibleCells().map((cell) => (
+
+ {flexRender(cell.column.columnDef.cell, cell.getContext())}
+
+ ))}
+
+ {renderSubRow && row.getIsExpanded() && (
+
+
+ {renderSubRow(row, index)}
+
+
+ )}
+
+ ))
+ ) : (
+
+
+ {emptyMessage || 'Nenhum registro disponível.'}
+
+
+ )}
+
+ );
+}
+
+export const MemoizedDataTableBody = memo(DataTableBody);
diff --git a/UIs/Customers/src/shared/components/global/datatable/pieces/datatable-content/index.tsx b/UIs/Customers/src/shared/components/global/datatable/pieces/datatable-content/index.tsx
new file mode 100644
index 0000000..81387eb
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/datatable/pieces/datatable-content/index.tsx
@@ -0,0 +1,33 @@
+import { useMemo } from 'react';
+import { useDataTable } from '../../hook/usetable';
+import { Table } from '@/shared/components/ui/table';
+import { DataTableHeader } from '../datatable-header';
+import { DataTableBody, MemoizedDataTableBody } from '../datatable-body';
+
+export function DataTableContent() {
+ const { table } = useDataTable();
+ const { columnSizingInfo, columnSizing } = table.getState();
+
+ const colSizeVariables = useMemo(
+ () =>
+ table.getFlatHeaders().reduce>(
+ (acc, header) => ({
+ ...acc,
+ [`--th-${header.id}-size`]: header.getSize(),
+ [`--col-${header.column.id}-size`]: header.column.getSize()
+ }),
+ {}
+ ),
+
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ [columnSizing, columnSizingInfo, table.getFlatHeaders]
+ );
+
+ return (
+
+
+ {columnSizingInfo.isResizingColumn && }
+ {!columnSizingInfo.isResizingColumn && }
+
+ );
+}
diff --git a/UIs/Customers/src/shared/components/global/datatable/pieces/datatable-date-range-filter/index.tsx b/UIs/Customers/src/shared/components/global/datatable/pieces/datatable-date-range-filter/index.tsx
new file mode 100644
index 0000000..291541a
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/datatable/pieces/datatable-date-range-filter/index.tsx
@@ -0,0 +1,92 @@
+import * as React from 'react';
+import { format } from 'date-fns';
+import { ptBR } from 'date-fns/locale';
+import { Calendar as CalendarIcon, X } from 'lucide-react';
+import { type DateRange } from 'react-day-picker';
+
+import { cn } from '@/shared/utils';
+import { Button } from '@/shared/components/ui/button';
+import { Calendar } from '@/shared/components/ui/calendar';
+import { useDataTable } from '../../hook/usetable';
+
+import {
+ Popover,
+ PopoverContent,
+ PopoverTrigger
+} from '@/shared/components/ui/popover';
+
+interface DataTableDateRangeFilterProps {
+ column: string;
+ title?: string;
+}
+
+export function DataTableDateRangeFilter({
+ column,
+ title = 'Selecione uma data'
+}: DataTableDateRangeFilterProps) {
+ const { table } = useDataTable();
+ const tableColumn = table.getColumn(column);
+
+ const date = tableColumn?.getFilterValue() as DateRange | undefined;
+
+ const setDate = (newDate: DateRange | undefined) => {
+ tableColumn?.setFilterValue(newDate);
+ };
+
+ const clearFilter = (e: React.MouseEvent) => {
+ e.stopPropagation();
+ tableColumn?.setFilterValue(undefined);
+ };
+
+ return (
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/UIs/Customers/src/shared/components/global/datatable/pieces/datatable-dropdown-column-visibility/index.tsx b/UIs/Customers/src/shared/components/global/datatable/pieces/datatable-dropdown-column-visibility/index.tsx
new file mode 100644
index 0000000..ead64b2
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/datatable/pieces/datatable-dropdown-column-visibility/index.tsx
@@ -0,0 +1,39 @@
+import { useDataTable } from '../../hook/usetable';
+import { Button } from '@/shared/components/ui/button';
+import { Eye } from 'lucide-react';
+import {
+ DropdownMenuCheckboxItem,
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuTrigger
+} from '@/shared/components/ui/dropdown-menu';
+
+export function DataTableDropdownColumnsVisibility() {
+ const { table } = useDataTable();
+
+ return (
+
+
+
+
+
+ {table.getAllColumns().map(
+ (column) =>
+ column.getCanHide() && (
+
+ {column.columnDef.meta?.nameInFilters ||
+ column.columnDef.header?.toString()}
+
+ )
+ )}
+
+
+ );
+}
diff --git a/UIs/Customers/src/shared/components/global/datatable/pieces/datatable-header-sortable-column/index.tsx b/UIs/Customers/src/shared/components/global/datatable/pieces/datatable-header-sortable-column/index.tsx
new file mode 100644
index 0000000..7a7edc4
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/datatable/pieces/datatable-header-sortable-column/index.tsx
@@ -0,0 +1,61 @@
+import type { Column } from '@tanstack/react-table';
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuTrigger
+} from '@/shared/components/ui/dropdown-menu';
+import { Button } from '@/shared/components/ui/button';
+import type { ReactNode } from 'react';
+import { ArrowDown, ArrowUp, ChevronsUpDown } from 'lucide-react';
+
+type THeaderSortableColumn = {
+ column: Column;
+ title: ReactNode | string;
+};
+
+export function DataTableHeaderSortableColumn({
+ column,
+ title
+}: THeaderSortableColumn) {
+ return (
+
+
+
+
+
+ {column.getCanSort() && (
+ <>
+ column.toggleSorting(false)}
+ >
+
+ Asc
+
+ column.toggleSorting(true)}
+ >
+
+ Desc
+
+ >
+ )}
+
+
+ );
+}
diff --git a/UIs/Customers/src/shared/components/global/datatable/pieces/datatable-header/index.tsx b/UIs/Customers/src/shared/components/global/datatable/pieces/datatable-header/index.tsx
new file mode 100644
index 0000000..1c13269
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/datatable/pieces/datatable-header/index.tsx
@@ -0,0 +1,41 @@
+import { TableHead, TableHeader, TableRow } from '@/shared/components/ui/table';
+import { useDataTable } from '../../hook/usetable';
+import { flexRender } from '@tanstack/react-table';
+import { cn } from '@/shared/utils';
+
+export function DataTableHeader() {
+ const { table } = useDataTable();
+
+ return (
+
+ {table.getHeaderGroups().map((headerGroup) => (
+
+ {headerGroup.headers.map((header) => (
+
+ ))}
+
+ ))}
+
+ );
+}
diff --git a/UIs/Customers/src/shared/components/global/datatable/pieces/datatable-nested-table/index.tsx b/UIs/Customers/src/shared/components/global/datatable/pieces/datatable-nested-table/index.tsx
new file mode 100644
index 0000000..23368ee
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/datatable/pieces/datatable-nested-table/index.tsx
@@ -0,0 +1,67 @@
+import {
+ useReactTable,
+ getCoreRowModel,
+ flexRender,
+ type ColumnDef
+} from '@tanstack/react-table';
+
+import {
+ Table,
+ TableBody,
+ TableRow,
+ TableCell
+} from '@/shared/components/ui/table';
+import { useDataTable } from '../../hook/usetable';
+
+interface TNestedDataTable {
+ data: TData[];
+
+ columns: ColumnDef[];
+
+ parentData?: any;
+}
+
+export function NestedDataTable({
+ data,
+ columns,
+ parentData
+}: TNestedDataTable) {
+ const { table: parentTable } = useDataTable();
+ const parentColumns = parentTable.getVisibleFlatColumns();
+
+ const nestedTable = useReactTable({
+ data,
+ columns,
+ getCoreRowModel: getCoreRowModel(),
+ meta: {
+ parentData: parentData
+ }
+ });
+
+ return (
+
+
+ {nestedTable.getRowModel().rows.map((row, index) => (
+
+ {row.getVisibleCells().map((cell, idx) => {
+ const parentCol = parentColumns[idx];
+
+ return (
+
+ {flexRender(cell.column.columnDef.cell, cell.getContext())}
+
+ );
+ })}
+
+ ))}
+
+
+ );
+}
diff --git a/UIs/Customers/src/shared/components/global/datatable/pieces/datatable-pagination-controllers/index.tsx b/UIs/Customers/src/shared/components/global/datatable/pieces/datatable-pagination-controllers/index.tsx
new file mode 100644
index 0000000..73e38af
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/datatable/pieces/datatable-pagination-controllers/index.tsx
@@ -0,0 +1,91 @@
+import { Button } from '@/shared/components/ui/button';
+import { ButtonGroup } from '@/shared/components/ui/button-group';
+import {
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+ SelectValue
+} from '@/shared/components/ui/select';
+import {
+ ChevronLeft,
+ ChevronRight,
+ ChevronsLeft,
+ ChevronsRight
+} from 'lucide-react';
+import { useDataTable } from '../../hook/usetable';
+
+export function PaginationControllers() {
+ const { table } = useDataTable();
+
+ return (
+
+
+ Resultados por página:
+
+
+
+
+ Página {table.getState().pagination.pageIndex + 1} de{' '}
+ {table.getPageCount()}
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/UIs/Customers/src/shared/components/global/datatable/pieces/datatable-select-filter/index.tsx b/UIs/Customers/src/shared/components/global/datatable/pieces/datatable-select-filter/index.tsx
new file mode 100644
index 0000000..4a34346
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/datatable/pieces/datatable-select-filter/index.tsx
@@ -0,0 +1,65 @@
+import {
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+ SelectValue
+} from '@/shared/components/ui/select';
+import { useDataTable } from '../../hook/usetable';
+import { cn } from '@/shared/utils';
+
+export interface DataTableSelectFilter {
+ placeholder: string;
+ column: string;
+ className?: string;
+ options: {
+ value: string | number | boolean;
+ label: string;
+ }[];
+}
+
+export function DataTableSelectFilter({
+ placeholder,
+ column,
+ className,
+ options
+}: DataTableSelectFilter) {
+ const { table } = useDataTable();
+ const tableColumn = table.getColumn(column);
+ const filterValue = tableColumn?.getFilterValue() as string;
+ const currentValue = filterValue ?? 'all';
+
+ const handleOnChangeValue = (newValue: string) => {
+ if (newValue === 'all') {
+ tableColumn?.setFilterValue(undefined);
+
+ return;
+ }
+
+ tableColumn?.setFilterValue(newValue);
+ };
+
+ return (
+
+ );
+}
diff --git a/UIs/Customers/src/shared/components/global/datatable/pieces/datatable-text-filter/index.test.tsx b/UIs/Customers/src/shared/components/global/datatable/pieces/datatable-text-filter/index.test.tsx
new file mode 100644
index 0000000..498c552
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/datatable/pieces/datatable-text-filter/index.test.tsx
@@ -0,0 +1,129 @@
+import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest';
+import {
+ render,
+ screen,
+ fireEvent,
+ cleanup,
+ act
+} from '@testing-library/react';
+import { DataTableTextFilter } from '.';
+
+const { mockUseDataTable, MockInput, mockSetGlobalFilter, mockSetFilterValue } =
+ vi.hoisted(() => {
+ const mockSetGlobalFilter = vi.fn();
+ const mockSetFilterValue = vi.fn();
+ const mockGetFilterValue = vi.fn(() => 'valor inicial');
+
+ const mockColumn = {
+ getFilterValue: mockGetFilterValue,
+ setFilterValue: mockSetFilterValue
+ };
+
+ const mockTable = {
+ getState: vi.fn(() => ({ globalFilter: '' })),
+ setGlobalFilter: mockSetGlobalFilter,
+ getColumn: vi.fn((id) => (id === 'name' ? mockColumn : undefined))
+ };
+
+ const mockUseDataTable = vi.fn(() => ({ table: mockTable }));
+
+ const MockInput = vi.fn((props) => (
+ props.onChange && props.onChange(e)}
+ value={props.value ?? ''}
+ />
+ ));
+
+ return {
+ mockUseDataTable,
+ MockInput,
+ mockSetGlobalFilter,
+ mockSetFilterValue,
+ mockGetFilterValue
+ };
+ });
+
+vi.mock('../../hook/usetable', () => ({ useDataTable: mockUseDataTable }));
+vi.mock('@/app/components/ui/input', () => ({ Input: MockInput }));
+
+describe('DataTableTextFilter', () => {
+ beforeEach(() => {
+ vi.clearAllMocks();
+ vi.useFakeTimers();
+ });
+
+ afterEach(() => {
+ cleanup();
+ vi.useRealTimers();
+ });
+
+ it('must call `table.setGlobalFilter` after debounce delay', async () => {
+ const placeholder = 'Buscar em tudo...';
+ render();
+
+ expect(MockInput).toHaveBeenCalledWith(
+ expect.objectContaining({ placeholder: placeholder }),
+ undefined
+ );
+
+ const typedValue = 'teste global';
+
+ fireEvent.change(screen.getByTestId('text-filter-input'), {
+ target: { value: typedValue }
+ });
+
+ expect(mockSetGlobalFilter).not.toHaveBeenCalled();
+
+ act(() => {
+ vi.advanceTimersByTime(500);
+ });
+
+ expect(mockSetGlobalFilter).toHaveBeenCalledWith(typedValue);
+ expect(mockSetGlobalFilter).toHaveBeenCalledTimes(1);
+ });
+
+ it('must call setFilterValue for specific column after debounce delay', () => {
+ const placeholder = 'Buscar por nome...';
+ render();
+
+ expect(MockInput).toHaveBeenCalledWith(
+ expect.objectContaining({ value: 'valor inicial' }),
+ undefined
+ );
+
+ const newValue = 'novo filtro';
+
+ fireEvent.change(screen.getByTestId('text-filter-input'), {
+ target: { value: newValue }
+ });
+
+ expect(mockSetFilterValue).not.toHaveBeenCalled();
+
+ act(() => {
+ vi.advanceTimersByTime(500);
+ });
+
+ expect(mockSetFilterValue).toHaveBeenCalledWith(newValue);
+ expect(mockSetGlobalFilter).not.toHaveBeenCalled();
+ });
+
+ it('should fail silently if the column is not found', () => {
+ const placeholder = 'Coluna Inexistente';
+
+ render(
+
+ );
+
+ fireEvent.change(screen.getByTestId('text-filter-input'), {
+ target: { value: 'teste' }
+ });
+
+ act(() => {
+ vi.advanceTimersByTime(500);
+ });
+
+ expect(mockSetFilterValue).not.toHaveBeenCalled();
+ });
+});
diff --git a/UIs/Customers/src/shared/components/global/datatable/pieces/datatable-text-filter/index.tsx b/UIs/Customers/src/shared/components/global/datatable/pieces/datatable-text-filter/index.tsx
new file mode 100644
index 0000000..8ac0aad
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/datatable/pieces/datatable-text-filter/index.tsx
@@ -0,0 +1,55 @@
+import { Input } from '@/shared/components/ui/input';
+import { useDataTable } from '../../hook/usetable';
+import { Search } from 'lucide-react';
+import { useMemo, useState } from 'react';
+import { cn, debounce } from '@/shared/utils';
+
+export interface IDataTableTextFilter {
+ placeholder: string;
+ column?: string;
+ className?: string;
+}
+
+export function DataTableTextFilter({
+ placeholder,
+ column,
+ className
+}: IDataTableTextFilter) {
+ const { table } = useDataTable();
+ const [localValue, setLocalValue] = useState(
+ () =>
+ ((column
+ ? table.getColumn(column)?.getFilterValue()
+ : table.getState().globalFilter) as string) ?? ''
+ );
+
+ const updateTableFilter = useMemo(
+ () =>
+ debounce((value: string) => {
+ if (column) {
+ table.getColumn(column)?.setFilterValue(value);
+ } else {
+ table.setGlobalFilter(value);
+ }
+ }, 1000),
+ [table, column]
+ );
+
+ const onChange = (event: React.ChangeEvent) => {
+ const newValue = event.target.value;
+ setLocalValue(newValue);
+ updateTableFilter(newValue);
+ };
+
+ return (
+
+
+
+
+ );
+}
diff --git a/UIs/Customers/src/shared/components/global/datatable/pieces/datatable/index.tsx b/UIs/Customers/src/shared/components/global/datatable/pieces/datatable/index.tsx
new file mode 100644
index 0000000..0b31b7b
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/datatable/pieces/datatable/index.tsx
@@ -0,0 +1,28 @@
+import type { ReactNode } from 'react';
+import {
+ useReactTable,
+ type Row,
+ type TableOptions
+} from '@tanstack/react-table';
+import { DataTableContext } from '../../hook/usetable';
+
+export interface IDataTable {
+ tableOptions: TableOptions;
+ emptyMessage?: string;
+ renderSubRow?: (row: Row, index: number) => ReactNode;
+ children?: ReactNode;
+}
+export function DataTable({
+ tableOptions,
+ emptyMessage,
+ renderSubRow,
+ children
+}: IDataTable) {
+ const table = useReactTable(tableOptions);
+
+ return (
+
+ {children}
+
+ );
+}
diff --git a/UIs/Customers/src/shared/components/global/datatable/types/table.d.ts b/UIs/Customers/src/shared/components/global/datatable/types/table.d.ts
new file mode 100644
index 0000000..2e4cbc6
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/datatable/types/table.d.ts
@@ -0,0 +1,15 @@
+/* eslint-disable @typescript-eslint/no-unused-vars */
+/* eslint-disable @typescript-eslint/no-empty-object-type */
+import '@tanstack/react-table';
+
+interface InventtoTableMeta {
+ nameInFilters?: string;
+ parentData?: TParentData;
+}
+
+declare module '@tanstack/react-table' {
+ interface ColumnMeta
+ extends InventtoTableMeta {}
+
+ interface TableMeta extends InventtoTableMeta {}
+}
diff --git a/UIs/Customers/src/shared/components/global/datatable/utils/index.ts b/UIs/Customers/src/shared/components/global/datatable/utils/index.ts
new file mode 100644
index 0000000..40541ff
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/datatable/utils/index.ts
@@ -0,0 +1,21 @@
+import type { Row } from '@tanstack/react-table';
+
+export const dateRangeFilter = (
+ row: Row,
+ columnId: string,
+ value: any
+) => {
+ const dateValue = row.getValue(columnId);
+ const { from, to } = value || {};
+
+ if (!from && !to) return true;
+ if (!dateValue) return false;
+
+ const rowDate = new Date(dateValue as string | number);
+
+ if (to) {
+ return rowDate >= from && rowDate <= to;
+ }
+
+ return rowDate >= from;
+};
diff --git a/UIs/Customers/src/shared/components/global/drag-datatable/hook/use-drag-table.tsx b/UIs/Customers/src/shared/components/global/drag-datatable/hook/use-drag-table.tsx
new file mode 100644
index 0000000..e69de29
diff --git a/UIs/Customers/src/shared/components/global/drag-datatable/index.tsx b/UIs/Customers/src/shared/components/global/drag-datatable/index.tsx
new file mode 100644
index 0000000..56fd8de
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/drag-datatable/index.tsx
@@ -0,0 +1,6 @@
+export * from './pieces/drag-handle';
+export * from './pieces/drag-table';
+export * from './pieces/drag-table-body';
+export * from './pieces/drag-table-content';
+export * from './pieces/drag-table-row';
+export * from './utils';
diff --git a/UIs/Customers/src/shared/components/global/drag-datatable/pieces/drag-handle/index.tsx b/UIs/Customers/src/shared/components/global/drag-datatable/pieces/drag-handle/index.tsx
new file mode 100644
index 0000000..a23f4ac
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/drag-datatable/pieces/drag-handle/index.tsx
@@ -0,0 +1,20 @@
+import { useSortable } from '@dnd-kit/sortable';
+import { Button } from '@/shared/components/ui/button';
+import { GripVertical } from 'lucide-react';
+
+export function DragHandle({ id }: { id: string | number }) {
+ const { attributes, listeners } = useSortable({ id });
+
+ return (
+
+ );
+}
diff --git a/UIs/Customers/src/shared/components/global/drag-datatable/pieces/drag-table-body/index.tsx b/UIs/Customers/src/shared/components/global/drag-datatable/pieces/drag-table-body/index.tsx
new file mode 100644
index 0000000..1b9cb93
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/drag-datatable/pieces/drag-table-body/index.tsx
@@ -0,0 +1,36 @@
+import { TableBody, TableCell, TableRow } from '@/shared/components/ui/table';
+import {
+ SortableContext,
+ verticalListSortingStrategy
+} from '@dnd-kit/sortable';
+
+import { useDataTable } from '../../../datatable/hook/usetable';
+import { DragTableRow } from '../drag-table-row';
+
+export function DragTableBody() {
+ const { table, emptyMessage } = useDataTable();
+ const { rows } = table.getRowModel();
+
+ const dataIds = rows.map((row) => row.id);
+
+ return (
+
+ {rows.length > 0 ? (
+
+ {rows.map((row) => (
+
+ ))}
+
+ ) : (
+
+
+ {emptyMessage || 'Nenhum registro disponível.'}
+
+
+ )}
+
+ );
+}
diff --git a/UIs/Customers/src/shared/components/global/drag-datatable/pieces/drag-table-content/index.tsx b/UIs/Customers/src/shared/components/global/drag-datatable/pieces/drag-table-content/index.tsx
new file mode 100644
index 0000000..9153be1
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/drag-datatable/pieces/drag-table-content/index.tsx
@@ -0,0 +1,31 @@
+import { useMemo } from 'react';
+import { Table } from '@/shared/components/ui/table';
+import { useDataTable } from '../../../datatable/hook/usetable';
+import { DataTableHeader } from '../../../datatable/pieces/datatable-header';
+import { DragTableBody } from '../drag-table-body';
+
+export function DragTableContent() {
+ const { table } = useDataTable();
+ const { columnSizingInfo, columnSizing } = table.getState();
+
+ const colSizeVariables = useMemo(
+ () =>
+ table.getFlatHeaders().reduce>(
+ (acc, header) => ({
+ ...acc,
+ [`--th-${header.id}-size`]: header.getSize(),
+ [`--col-${header.column.id}-size`]: header.column.getSize()
+ }),
+ {}
+ ),
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ [columnSizing, columnSizingInfo, table.getFlatHeaders]
+ );
+
+ return (
+
+ );
+}
diff --git a/UIs/Customers/src/shared/components/global/drag-datatable/pieces/drag-table-row/index.tsx b/UIs/Customers/src/shared/components/global/drag-datatable/pieces/drag-table-row/index.tsx
new file mode 100644
index 0000000..a03666d
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/drag-datatable/pieces/drag-table-row/index.tsx
@@ -0,0 +1,44 @@
+import { TableCell, TableRow } from '@/shared/components/ui/table';
+import { useSortable } from '@dnd-kit/sortable';
+import { CSS } from '@dnd-kit/utilities';
+import { flexRender, type Row } from '@tanstack/react-table';
+import { type CSSProperties } from 'react';
+
+interface DragTableRowProps {
+ row: Row;
+}
+
+export function DragTableRow({ row }: DragTableRowProps) {
+ const { transform, transition, setNodeRef, isDragging } = useSortable({
+ id: row.id
+ });
+
+ const style: CSSProperties = {
+ transform: CSS.Transform.toString(transform),
+ transition: transition,
+ position: 'relative',
+ zIndex: isDragging ? 10 : 0,
+ opacity: isDragging ? 0.6 : 1
+ };
+
+ return (
+
+ {row.getVisibleCells().map((cell) => (
+
+ {flexRender(cell.column.columnDef.cell, cell.getContext())}
+
+ ))}
+
+ );
+}
diff --git a/UIs/Customers/src/shared/components/global/drag-datatable/pieces/drag-table/index.tsx b/UIs/Customers/src/shared/components/global/drag-datatable/pieces/drag-table/index.tsx
new file mode 100644
index 0000000..b8eb205
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/drag-datatable/pieces/drag-table/index.tsx
@@ -0,0 +1,49 @@
+import { type ReactNode } from 'react';
+import { useReactTable, type TableOptions } from '@tanstack/react-table';
+import {
+ closestCenter,
+ DndContext,
+ type DragEndEvent,
+ KeyboardSensor,
+ MouseSensor,
+ TouchSensor,
+ useSensor,
+ useSensors
+} from '@dnd-kit/core';
+import { restrictToVerticalAxis } from '@dnd-kit/modifiers';
+import { DataTableContext } from '../../../datatable/hook/usetable';
+
+export interface IDragTable {
+ tableOptions: TableOptions;
+ onDragEnd: (event: DragEndEvent) => void;
+ emptyMessage?: string;
+ children?: ReactNode;
+}
+
+export function DragTable({
+ tableOptions,
+ onDragEnd,
+ emptyMessage,
+ children
+}: IDragTable) {
+ const table = useReactTable(tableOptions);
+
+ const sensors = useSensors(
+ useSensor(MouseSensor, {}),
+ useSensor(TouchSensor, {}),
+ useSensor(KeyboardSensor, {})
+ );
+
+ return (
+
+
+ {children}
+
+
+ );
+}
diff --git a/UIs/Customers/src/shared/components/global/drag-datatable/types/index.ts b/UIs/Customers/src/shared/components/global/drag-datatable/types/index.ts
new file mode 100644
index 0000000..1bb0d12
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/drag-datatable/types/index.ts
@@ -0,0 +1,11 @@
+import type { ColumnDef, Row } from '@tanstack/react-table';
+import type { ReactNode } from 'react';
+
+export interface DragTableProps {
+ data: TData[];
+ columns: ColumnDef[];
+ onReorder: (newData: TData[]) => void;
+ renderDragHandle?: (row: Row) => ReactNode;
+ emptyMessage?: string;
+ children?: ReactNode;
+}
diff --git a/UIs/Customers/src/shared/components/global/drag-datatable/utils/index.tsx b/UIs/Customers/src/shared/components/global/drag-datatable/utils/index.tsx
new file mode 100644
index 0000000..5ca0074
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/drag-datatable/utils/index.tsx
@@ -0,0 +1,37 @@
+import { type ColumnDef } from '@tanstack/react-table';
+import { DragHandle } from '../pieces/drag-handle';
+import {
+ Tooltip,
+ TooltipContent,
+ TooltipTrigger
+} from '@/shared/components/ui/tooltip';
+
+/**
+ * Prepend the drag handle column to the existing columns definition.
+ * @param columns The original columns definition.
+ * @returns A new array of columns with the drag handle as the first column.
+ */
+export function getDragTableColumns(
+ columns: ColumnDef[]
+): ColumnDef[] {
+ const dragColumn: ColumnDef = {
+ id: 'drag',
+ header: () => null,
+ cell: ({ row }) => (
+
+
+
+
+
+ Arraste para alterar a prioridade do banco
+
+
+ ),
+ size: 50,
+ enableSorting: false,
+ enableHiding: false,
+ enableResizing: false
+ };
+
+ return [dragColumn, ...columns];
+}
diff --git a/UIs/Customers/src/shared/components/global/file-picker/hooks/index.test.tsx b/UIs/Customers/src/shared/components/global/file-picker/hooks/index.test.tsx
new file mode 100644
index 0000000..ddfe4fa
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/file-picker/hooks/index.test.tsx
@@ -0,0 +1,40 @@
+import { renderHook } from '@testing-library/react';
+import { vi, describe, it, expect } from 'vitest';
+import { useFilePickerContext, FilePickerContext } from '.';
+
+const mockContextValue = [
+ { errors: ['erro teste'], files: [{ id: 'a', name: 'a.jpg' }] },
+ {
+ addFiles: vi.fn(),
+ removeFile: vi.fn(),
+ clearFiles: vi.fn(),
+ setPrimaryFile: vi.fn(),
+ clearErrors: vi.fn(),
+ handleFileChange: vi.fn(),
+ openFileDialog: vi.fn(),
+ getInputProps: vi.fn()
+ }
+];
+
+const MockProvider = ({ children }: { children: React.ReactNode }) => (
+
+ {children}
+
+);
+
+describe('useFilePickerContext', () => {
+ it('should return the context value when used inside the provider (Happy Path)', () => {
+ const { result } = renderHook(() => useFilePickerContext(), {
+ wrapper: MockProvider
+ });
+
+ expect(result.current[0].errors).toEqual(['erro teste']);
+ expect(result.current[1].addFiles).toBe(mockContextValue[1].addFiles);
+ });
+
+ it('should throw an error when used outside of the provider (Guard Clause)', () => {
+ expect(() => renderHook(() => useFilePickerContext())).toThrow(
+ 'useFilePickerContext deve ser usado dentro de '
+ );
+ });
+});
diff --git a/UIs/Customers/src/shared/components/global/file-picker/hooks/index.tsx b/UIs/Customers/src/shared/components/global/file-picker/hooks/index.tsx
new file mode 100644
index 0000000..975dbba
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/file-picker/hooks/index.tsx
@@ -0,0 +1,27 @@
+import { createContext, useContext } from 'react';
+import type {
+ FilePickerActions,
+ FilePickerState,
+ FileWithPreview
+} from '../types';
+
+type FilePickerContextType = [
+ state: FilePickerState & { files: FileWithPreview[] },
+ actions: FilePickerActions
+];
+
+export const FilePickerContext = createContext(
+ null
+);
+
+export function useFilePickerContext() {
+ const context = useContext(FilePickerContext);
+
+ if (!context) {
+ throw new Error(
+ 'useFilePickerContext deve ser usado dentro de '
+ );
+ }
+
+ return context;
+}
diff --git a/UIs/Customers/src/shared/components/global/file-picker/hooks/use-file-picker.test.ts b/UIs/Customers/src/shared/components/global/file-picker/hooks/use-file-picker.test.ts
new file mode 100644
index 0000000..d5264a6
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/file-picker/hooks/use-file-picker.test.ts
@@ -0,0 +1,251 @@
+import { renderHook, act } from '@testing-library/react';
+import { vi, describe, it, expect, beforeEach } from 'vitest';
+import { useFilePicker } from './use-file-picker';
+
+const mockFileA = {
+ id: 'a',
+ name: 'A.jpg',
+ size: 100,
+ src: 'blob:a',
+ file: new File([''], 'A.jpg'),
+ isPrimary: true
+};
+
+const mockFileB = {
+ id: 'b',
+ name: 'B.png',
+ size: 50,
+ src: 'blob:b',
+ file: new File([''], 'B.png'),
+ isPrimary: false
+};
+
+const mockFileC = {
+ id: 'c',
+ name: 'C.gif',
+ size: 200,
+ src: 'blob:c',
+ file: new File([''], 'C.gif'),
+ isPrimary: false
+};
+
+const { mockProcessNewFiles } = vi.hoisted(() => {
+ const mockProcessNewFiles = vi.fn();
+
+ return {
+ mockProcessNewFiles
+ };
+});
+
+vi.mock('../utils/', () => ({
+ processNewFiles: mockProcessNewFiles
+}));
+
+globalThis.URL.revokeObjectURL = vi.fn();
+
+describe('useFilePicker', () => {
+ const mockOnFilesChange = vi.fn();
+ const mockOnFilesAdded = vi.fn();
+
+ beforeEach(() => {
+ vi.clearAllMocks();
+ });
+
+ const renderMultiHook = (initialFiles = [], options = {}) => {
+ return renderHook(() =>
+ useFilePicker({
+ files: initialFiles as any,
+ onFilesChange: mockOnFilesChange,
+ onFilesAdded: mockOnFilesAdded,
+ maxFiles: 5,
+ maxSize: 1000,
+ multiple: true,
+ accept: 'image/*',
+ ...options
+ })
+ );
+ };
+
+ describe('addFiles / handleFileChange (Array & Validation Logic)', () => {
+ it('should ignore adding files if the list is null or empty', () => {
+ const { result } = renderMultiHook();
+ const { addFiles } = result.current[1];
+
+ addFiles(null as any);
+ addFiles([] as any);
+
+ expect(mockProcessNewFiles).not.toHaveBeenCalled();
+ expect(mockOnFilesChange).not.toHaveBeenCalled();
+ });
+
+ it('should call clearFiles and replace array when "multiple" is false', () => {
+ const { result } = renderMultiHook([mockFileA] as never[], {
+ multiple: false
+ });
+ const { addFiles } = result.current[1];
+
+ mockProcessNewFiles.mockReturnValue({
+ validFiles: [mockFileB],
+ errors: []
+ });
+
+ act(() => {
+ addFiles([mockFileB.file] as any);
+ });
+
+ expect(mockOnFilesChange).toHaveBeenCalledWith([mockFileB]);
+ });
+
+ it('should concatenate files when "multiple" is true', () => {
+ const { result } = renderMultiHook([mockFileA] as never[], {
+ multiple: true
+ });
+ const { addFiles } = result.current[1];
+
+ mockProcessNewFiles.mockReturnValue({
+ validFiles: [mockFileB],
+ errors: []
+ });
+
+ act(() => {
+ addFiles([mockFileB.file] as any);
+ });
+
+ expect(mockOnFilesChange).toHaveBeenCalledWith([mockFileA, mockFileB]);
+ });
+
+ it('should set error and NOT call onFilesChange if maxFiles limit is exceeded', () => {
+ const initialFiles = [mockFileA, mockFileB, mockFileA, mockFileB];
+
+ const { result } = renderMultiHook(initialFiles as never[], {
+ maxFiles: 5,
+ multiple: true
+ });
+ const { addFiles } = result.current[1];
+
+ act(() => {
+ addFiles([mockFileC.file, mockFileC.file] as any);
+ });
+
+ expect(result.current[0].errors).toEqual([
+ 'Você pode enviar no máximo 5 arquivos.'
+ ]);
+
+ expect(mockProcessNewFiles).not.toHaveBeenCalled();
+ expect(mockOnFilesChange).not.toHaveBeenCalled();
+ });
+
+ it('should call onFilesAdded if validFiles exist', () => {
+ const { result } = renderMultiHook();
+ const { addFiles } = result.current[1];
+
+ mockProcessNewFiles.mockReturnValue({
+ validFiles: [mockFileA],
+ errors: []
+ });
+
+ act(() => {
+ addFiles([mockFileA.file] as any);
+ });
+
+ expect(mockOnFilesAdded).toHaveBeenCalledWith([mockFileA]);
+ });
+ });
+
+ describe('removeFile (Revocation & Primary Rollover)', () => {
+ it('should remove file and revokeObjectURL for existing blob URLs', () => {
+ const { result } = renderMultiHook([mockFileA, mockFileB] as never[]);
+ const { removeFile } = result.current[1];
+ const expectedRolloverFiles = [{ ...mockFileB, isPrimary: true }];
+
+ act(() => {
+ removeFile('a');
+ });
+
+ expect(URL.revokeObjectURL).toHaveBeenCalledWith('blob:a');
+ expect(mockOnFilesChange).toHaveBeenCalledWith(expectedRolloverFiles);
+ });
+
+ it('should assign isPrimary to the next file if the primary file is removed', () => {
+ const { result } = renderMultiHook([mockFileA, mockFileB] as never[]);
+ const { removeFile } = result.current[1];
+
+ act(() => {
+ removeFile('a');
+ });
+
+ const expectedNewFiles = [{ ...mockFileB, isPrimary: true }];
+
+ expect(mockOnFilesChange).toHaveBeenCalledWith(expectedNewFiles);
+ });
+
+ it('should NOT assign isPrimary if another primary file exists', () => {
+ const mockFileCPrimary = { ...mockFileC, isPrimary: true };
+ const initialFiles = [mockFileA, mockFileB, mockFileCPrimary];
+
+ const { result } = renderMultiHook(initialFiles as never[]);
+
+ const { removeFile } = result.current[1];
+
+ act(() => {
+ removeFile('a');
+ });
+
+ expect(mockOnFilesChange).toHaveBeenCalledWith([
+ mockFileB,
+ mockFileCPrimary
+ ]);
+ });
+ });
+
+ describe('setPrimaryFile (Sorting)', () => {
+ it('should mark the selected file as primary and move it to the first position', () => {
+ //@ts-expect-error mockFileA
+ const { result } = renderMultiHook([mockFileA, mockFileB]);
+ const { setPrimaryFile } = result.current[1];
+
+ act(() => {
+ setPrimaryFile('b');
+ });
+
+ const expectedFiles = [
+ { ...mockFileB, isPrimary: true },
+ { ...mockFileA, isPrimary: false }
+ ];
+
+ expect(mockOnFilesChange).toHaveBeenCalledWith(expectedFiles);
+ });
+ });
+
+ describe('clearFiles', () => {
+ it('should call revokeObjectURL for all files and reset state', () => {
+ //@ts-expect-error mockFileA
+ const { result } = renderMultiHook([mockFileA, mockFileB]);
+ const { clearFiles } = result.current[1];
+
+ act(() => {
+ clearFiles();
+ });
+
+ expect(URL.revokeObjectURL).toHaveBeenCalledWith('blob:a');
+ expect(URL.revokeObjectURL).toHaveBeenCalledWith('blob:b');
+ expect(mockOnFilesChange).toHaveBeenCalledWith([]);
+ expect(result.current[0].errors).toEqual([]);
+ });
+ });
+
+ describe('getInputProps', () => {
+ it('should correctly configure input props and wire the onChange handler', () => {
+ const { result } = renderMultiHook();
+ const { getInputProps } = result.current[1];
+ const inputProps = getInputProps({ className: 'custom' });
+
+ expect(inputProps.type).toBe('file');
+ expect(inputProps.accept).toBe('image/*');
+ expect(inputProps.multiple).toBe(true);
+ expect(inputProps.className).toBe('custom');
+ expect(inputProps.onChange).toBeInstanceOf(Function);
+ expect(inputProps.ref.current).toBeDefined();
+ });
+ });
+});
diff --git a/UIs/Customers/src/shared/components/global/file-picker/hooks/use-file-picker.ts b/UIs/Customers/src/shared/components/global/file-picker/hooks/use-file-picker.ts
new file mode 100644
index 0000000..731ba7f
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/file-picker/hooks/use-file-picker.ts
@@ -0,0 +1,193 @@
+'use client';
+
+import {
+ useCallback,
+ useRef,
+ useState,
+ type ChangeEvent,
+ type InputHTMLAttributes
+} from 'react';
+
+import type {
+ FilePickerActions,
+ FilePickerOptions,
+ FilePickerState
+} from '../types';
+
+import { processNewFiles } from '../utils';
+
+export const useFilePicker = (
+ options: FilePickerOptions
+): [FilePickerState, FilePickerActions] => {
+ const {
+ files,
+ onFilesChange,
+ onFilesAdded,
+ maxFiles = Infinity,
+ maxSize = Infinity,
+ accept = '*',
+ multiple = false
+ } = options;
+
+ const [errors, setErrors] = useState([]);
+
+ const inputRef = useRef(null);
+
+ const clearFiles = useCallback(() => {
+ files.forEach((file) => {
+ if (file.url && file.file instanceof File) {
+ URL.revokeObjectURL(file.url);
+ }
+ });
+
+ if (inputRef.current) {
+ inputRef.current.value = '';
+ }
+
+ onFilesChange([]);
+ setErrors([]);
+ }, [onFilesChange, files]);
+
+ const addFiles = useCallback(
+ (newFiles: FileList | File[]) => {
+ if (!newFiles || newFiles.length === 0) return;
+
+ const newFilesArray = Array.from(newFiles);
+ setErrors([]);
+
+ if (!multiple) {
+ clearFiles();
+ }
+
+ if (
+ multiple &&
+ maxFiles !== Infinity &&
+ files.length + newFilesArray.length > maxFiles
+ ) {
+ setErrors([`Você pode enviar no máximo ${maxFiles} arquivos.`]);
+ return;
+ }
+
+ const { validFiles, errors: validationErrors } = processNewFiles(
+ newFilesArray,
+ files,
+ { maxSize, accept, multiple }
+ );
+ setErrors(validationErrors);
+
+ if (validFiles.length > 0) {
+ onFilesAdded?.(validFiles);
+
+ const updatedFiles = !multiple ? validFiles : [...files, ...validFiles];
+
+ onFilesChange(updatedFiles);
+ }
+
+ if (inputRef.current) {
+ inputRef.current.value = '';
+ }
+ },
+ [
+ files,
+ maxFiles,
+ multiple,
+ maxSize,
+ accept,
+ clearFiles,
+ onFilesChange,
+ onFilesAdded
+ ]
+ );
+
+ const removeFile = useCallback(
+ (id: string) => {
+ const fileToRemove = files.find((file) => file.id === id);
+ if (
+ fileToRemove &&
+ fileToRemove.url &&
+ fileToRemove.file instanceof File
+ ) {
+ URL.revokeObjectURL(fileToRemove.url);
+ }
+
+ const newFiles = files.filter((file) => file.id !== id);
+
+ if (
+ fileToRemove?.isPrimary &&
+ newFiles.length > 0 &&
+ !newFiles.some((f) => f.isPrimary)
+ ) {
+ newFiles[0] = { ...newFiles[0], isPrimary: true };
+ }
+
+ onFilesChange(newFiles);
+ },
+ [files, onFilesChange]
+ );
+
+ const setPrimaryFile = useCallback(
+ (fileId: string) => {
+ const newFiles =
+ files?.map((file) => ({
+ ...file,
+ isPrimary: file.id === fileId
+ })) || [];
+
+ newFiles.sort((a, b) => {
+ if (a.isPrimary === true) return -1;
+ if (b.isPrimary === true) return 1;
+ return 0;
+ });
+
+ onFilesChange(newFiles);
+ },
+ [files, onFilesChange]
+ );
+
+ const clearErrors = useCallback(() => {
+ setErrors([]);
+ }, []);
+
+ const handleFileChange = useCallback(
+ (e: ChangeEvent) => {
+ if (e.target.files && e.target.files.length > 0) {
+ addFiles(e.target.files);
+ }
+ },
+ [addFiles]
+ );
+
+ const openFileDialog = useCallback(() => {
+ if (inputRef.current) {
+ inputRef.current.click();
+ }
+ }, []);
+
+ const getInputProps = useCallback(
+ (props: InputHTMLAttributes = {}) => {
+ return {
+ ...props,
+ type: 'file' as const,
+ onChange: handleFileChange,
+ accept: props.accept || accept,
+ multiple: props.multiple !== undefined ? props.multiple : multiple,
+ ref: inputRef
+ };
+ },
+ [accept, multiple, handleFileChange]
+ );
+
+ const state: FilePickerState = { errors };
+ const actions: FilePickerActions = {
+ addFiles,
+ removeFile,
+ clearFiles,
+ setPrimaryFile,
+ clearErrors,
+ handleFileChange,
+ openFileDialog,
+ getInputProps
+ };
+
+ return [state, actions];
+};
diff --git a/UIs/Customers/src/shared/components/global/file-picker/index.test.tsx b/UIs/Customers/src/shared/components/global/file-picker/index.test.tsx
new file mode 100644
index 0000000..207d749
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/file-picker/index.test.tsx
@@ -0,0 +1,265 @@
+import React from 'react';
+import { describe, it, expect, vi, beforeAll } from 'vitest';
+import { fireEvent, render, screen, waitFor } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+
+import {
+ FilePicker,
+ FilePickerContent,
+ FilePickerInput,
+ FilePickerHeader,
+ FilePickerEmpty,
+ FilePickerError,
+ FilePickerButton,
+ FilePickerRemoveAllButton,
+ FilePickerDrag,
+ FilePickerCount,
+ FilePickerAddMoreButton
+} from '.';
+
+vi.mock('@/app/services/image-upload/utils', () => ({
+ createCloudinaryThumbnail: (publicId: string) =>
+ `https://res.cloudinary.com/demo/image/upload/w_450/${publicId}`
+}));
+
+beforeAll(() => {
+ globalThis.URL.createObjectURL = vi.fn(() => 'blob:mock-url');
+ globalThis.URL.revokeObjectURL = vi.fn();
+});
+
+const IntegrationFilePicker = ({
+ maxSizeMB,
+ maxFiles,
+ accept,
+ multiple,
+ initialFiles = []
+}: {
+ maxSizeMB?: number;
+ maxFiles?: number;
+ accept?: string;
+ multiple?: boolean;
+ initialFiles?: any[];
+}) => {
+ const [files, setFiles] = React.useState(initialFiles);
+
+ return (
+
+
+
+
+
+
+
+
+
+ Arraste e solte seus arquivos aqui
+
+
+
+
+
+
+
+
+ );
+};
+
+describe('FilePicker Integration', () => {
+ it('should render empty state correctly (header hidden)', () => {
+ render();
+
+ expect(screen.getByText(/Arraste e solte/i)).toBeInTheDocument();
+ expect(screen.getByText('Arquivos (0)')).toBeInTheDocument();
+
+ expect(
+ screen.queryByRole('button', { name: /Adicionar mais/i })
+ ).not.toBeInTheDocument();
+ });
+
+ it('should handle drag and drop of files and show header buttons', async () => {
+ render();
+
+ const file = new File(['dummy'], 'dropped.png', { type: 'image/png' });
+ const dropzone = screen.getByText(/Arraste e solte/i).closest('div');
+
+ if (!dropzone) throw new Error('Dropzone not found');
+
+ fireEvent.drop(dropzone, {
+ dataTransfer: {
+ files: [file],
+ types: ['Files']
+ }
+ });
+
+ await screen.findByAltText('dropped.png');
+
+ expect(
+ screen.getByRole('button', { name: /Adicionar mais/i })
+ ).toBeInTheDocument();
+ expect(
+ screen.getByRole('button', { name: /Remover tudo/i })
+ ).toBeInTheDocument();
+ expect(screen.getByText('Arquivos (1)')).toBeInTheDocument();
+ });
+
+ it('should upload a valid file via input change', async () => {
+ render();
+
+ const input = document.querySelector(
+ 'input[type="file"]'
+ ) as HTMLInputElement;
+ const file = new File(['dummy content'], 'teste.png', {
+ type: 'image/png'
+ });
+
+ fireEvent.change(input, { target: { files: [file] } });
+
+ await screen.findByAltText('teste.png');
+ expect(screen.getByText('Arquivos (1)')).toBeInTheDocument();
+ });
+
+ it('should show error when file is too large', async () => {
+ render();
+
+ const input = document.querySelector(
+ 'input[type="file"]'
+ ) as HTMLInputElement;
+ const largeFile = new File(['a'.repeat(2 * 1024 * 1024)], 'large.png', {
+ type: 'image/png'
+ });
+
+ fireEvent.change(input, { target: { files: [largeFile] } });
+
+ expect(
+ await screen.findByText(/excede o tamanho máximo/i)
+ ).toBeInTheDocument();
+ expect(screen.queryByAltText('large.png')).not.toBeInTheDocument();
+ });
+
+ it('should remove a file when clicking remove button', async () => {
+ const user = userEvent.setup();
+ render();
+
+ const input = document.querySelector(
+ 'input[type="file"]'
+ ) as HTMLInputElement;
+ const file = new File(['content'], 'teste.jpg', { type: 'image/jpeg' });
+
+ fireEvent.change(input, { target: { files: [file] } });
+
+ await screen.findByAltText('teste.jpg');
+ expect(screen.getByText('Arquivos (1)')).toBeInTheDocument();
+
+ const removeBtn = screen.getByRole('button', { name: /Remove image/i });
+ await user.click(removeBtn);
+
+ await waitFor(() => {
+ expect(screen.queryByAltText('teste.jpg')).not.toBeInTheDocument();
+ });
+ expect(screen.getByText('Arquivos (0)')).toBeInTheDocument();
+ });
+
+ it('should clear all files when clicking Remove All', async () => {
+ const user = userEvent.setup();
+ render();
+
+ const input = document.querySelector(
+ 'input[type="file"]'
+ ) as HTMLInputElement;
+ const file1 = new File(['c1'], 'a.jpg', { type: 'image/jpeg' });
+ const file2 = new File(['c2'], 'b.jpg', { type: 'image/jpeg' });
+
+ fireEvent.change(input, { target: { files: [file1, file2] } });
+
+ await screen.findByAltText('a.jpg');
+
+ const clearBtn = screen.getByRole('button', { name: /Remover tudo/i });
+ await user.click(clearBtn);
+
+ await waitFor(() => {
+ expect(screen.queryByAltText('a.jpg')).not.toBeInTheDocument();
+ });
+
+ expect(
+ screen.queryByRole('button', { name: /Remover tudo/i })
+ ).not.toBeInTheDocument();
+ });
+
+ it('should use default values when optional props are missing', () => {
+ const TestDefaults = () => {
+ const [files, setFiles] = React.useState([]);
+ return (
+
+
+
+ );
+ };
+
+ render();
+
+ const input = document.querySelector(
+ 'input[type="file"]'
+ ) as HTMLInputElement;
+ expect(input.multiple).toBe(true);
+ });
+
+ it('should allow setting a file as primary', async () => {
+ const user = userEvent.setup();
+ const initialFiles = [
+ {
+ id: '1',
+ name: 'file1.jpg',
+ src: 'blob:url1',
+ type: 'image/jpeg',
+ isPrimary: true
+ },
+ {
+ id: '2',
+ name: 'file2.jpg',
+ src: 'blob:url2',
+ type: 'image/jpeg',
+ isPrimary: false
+ }
+ ];
+
+ render();
+
+ const setPrimaryButtons = screen.getAllByRole('button', {
+ name: /Definir como principal/i
+ });
+ expect(setPrimaryButtons).toHaveLength(1);
+
+ await user.click(setPrimaryButtons[0]);
+
+ const updatedPrimaryButtons = screen.queryAllByRole('button', {
+ name: /Definir como principal/i
+ });
+ expect(updatedPrimaryButtons).toHaveLength(1);
+ });
+
+ it('should render Cloudinary thumbnail URL when publicId is present', () => {
+ const initialFiles = [
+ {
+ id: 'cloud-1',
+ name: 'cloud.jpg',
+ src: 'http://original-src',
+ type: 'image/jpeg',
+ publicId: 'folder/image-123'
+ }
+ ];
+
+ render();
+
+ const img = screen.getByAltText('cloud.jpg') as HTMLImageElement;
+ expect(img.src).toContain(
+ 'https://res.cloudinary.com/demo/image/upload/w_450/folder/image-123'
+ );
+ });
+});
diff --git a/UIs/Customers/src/shared/components/global/file-picker/index.tsx b/UIs/Customers/src/shared/components/global/file-picker/index.tsx
new file mode 100644
index 0000000..6238e9e
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/file-picker/index.tsx
@@ -0,0 +1,12 @@
+export { FilePicker } from './pieces/file-picker/index';
+export { FilePickerContent } from './pieces/content';
+export { FilePickerCount } from './pieces/count';
+export { FilePickerDrag } from './pieces/drag';
+export { FilePickerEmpty } from './pieces/empty';
+export { FilePickerHeader } from './pieces/header';
+export { FilePickerInput } from './pieces/input';
+export { FilePickerError } from './pieces/error';
+export { FilePickerAddMoreButton } from './pieces/buttons/add-more';
+export { FilePickerButton } from './pieces/buttons/import-files';
+export { FilePickerRemoveAllButton } from './pieces/buttons/remove-all';
+export { FilePickerRemoveFileButton } from './pieces/buttons/remove-file';
diff --git a/UIs/Customers/src/shared/components/global/file-picker/pieces/buttons/add-more/index.tsx b/UIs/Customers/src/shared/components/global/file-picker/pieces/buttons/add-more/index.tsx
new file mode 100644
index 0000000..a477584
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/file-picker/pieces/buttons/add-more/index.tsx
@@ -0,0 +1,20 @@
+import { Button } from '@/shared/components/ui/button';
+import { UploadIcon } from 'lucide-react';
+import { useFilePickerContext } from '../../../hooks';
+
+export function FilePickerAddMoreButton({ label }: { label: string }) {
+ const [, { openFileDialog }] = useFilePickerContext();
+
+ return (
+
+ );
+}
diff --git a/UIs/Customers/src/shared/components/global/file-picker/pieces/buttons/import-files/index.tsx b/UIs/Customers/src/shared/components/global/file-picker/pieces/buttons/import-files/index.tsx
new file mode 100644
index 0000000..7bd6664
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/file-picker/pieces/buttons/import-files/index.tsx
@@ -0,0 +1,19 @@
+import { Button } from '@/shared/components/ui/button';
+import { UploadIcon } from 'lucide-react';
+import { useFilePickerContext } from '../../../hooks';
+
+export function FilePickerButton({ label }: { label: string }) {
+ const [, { openFileDialog }] = useFilePickerContext();
+
+ return (
+
+ );
+}
diff --git a/UIs/Customers/src/shared/components/global/file-picker/pieces/buttons/remove-all/index.tsx b/UIs/Customers/src/shared/components/global/file-picker/pieces/buttons/remove-all/index.tsx
new file mode 100644
index 0000000..f80a925
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/file-picker/pieces/buttons/remove-all/index.tsx
@@ -0,0 +1,14 @@
+import { Button } from '@/shared/components/ui/button';
+import { useFilePickerContext } from '../../../hooks';
+import { Trash2Icon } from 'lucide-react';
+
+export function FilePickerRemoveAllButton({ label }: { label: string }) {
+ const [, { clearFiles }] = useFilePickerContext();
+
+ return (
+
+ );
+}
diff --git a/UIs/Customers/src/shared/components/global/file-picker/pieces/buttons/remove-file/index.tsx b/UIs/Customers/src/shared/components/global/file-picker/pieces/buttons/remove-file/index.tsx
new file mode 100644
index 0000000..6ba4b54
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/file-picker/pieces/buttons/remove-file/index.tsx
@@ -0,0 +1,21 @@
+import { Button } from '@/shared/components/ui/button';
+import { useFilePickerContext } from '../../../hooks';
+import type { FileWithPreview } from '../../../types';
+import { XIcon } from 'lucide-react';
+
+export function FilePickerRemoveFileButton({
+ id
+}: Pick) {
+ const [, { removeFile }] = useFilePickerContext();
+
+ return (
+
+ );
+}
diff --git a/UIs/Customers/src/shared/components/global/file-picker/pieces/content/index.tsx b/UIs/Customers/src/shared/components/global/file-picker/pieces/content/index.tsx
new file mode 100644
index 0000000..e80f0fc
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/file-picker/pieces/content/index.tsx
@@ -0,0 +1,61 @@
+import type { ComponentProps } from 'react';
+import { useFilePickerContext } from '../../hooks';
+import { getFilePreview } from '../../utils';
+import { cn } from '@/shared/utils';
+import { FilePickerRemoveFileButton } from '../buttons/remove-file';
+import { Star } from 'lucide-react';
+import { Button } from '@/shared/components/ui/button';
+
+export function FilePickerContent({
+ className,
+ ...props
+}: ComponentProps<'div'>) {
+ const [{ files }, { setPrimaryFile }] = useFilePickerContext();
+ return (
+
+ {files.map((file) => (
+
+ {
+ //@ts-expect-error file
+ !file.url && getFilePreview(file)
+ }
+

+
+ {file.isPrimary && (
+
+
+
+ )}
+ {!file.isPrimary && (
+
+ )}
+
+
+
+
+ ))}
+
+ );
+}
diff --git a/UIs/Customers/src/shared/components/global/file-picker/pieces/count/index.tsx b/UIs/Customers/src/shared/components/global/file-picker/pieces/count/index.tsx
new file mode 100644
index 0000000..8b8aab3
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/file-picker/pieces/count/index.tsx
@@ -0,0 +1,10 @@
+import { useFilePickerContext } from '../../hooks';
+
+export function FilePickerCount({ label }: { label: string }) {
+ const [{ files }] = useFilePickerContext();
+ return (
+
+ {label} ({files.length})
+
+ );
+}
diff --git a/UIs/Customers/src/shared/components/global/file-picker/pieces/drag/index.tsx b/UIs/Customers/src/shared/components/global/file-picker/pieces/drag/index.tsx
new file mode 100644
index 0000000..aa22d81
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/file-picker/pieces/drag/index.tsx
@@ -0,0 +1,32 @@
+import type { ReactNode, DragEvent } from 'react';
+import { useCallback } from 'react';
+import { useFilePickerContext } from '../../hooks';
+import { useDropzone } from '@/shared/hooks/use-dropzone';
+
+export function FilePickerDrag({ children }: { children?: ReactNode }) {
+ const [{ files }, { addFiles }] = useFilePickerContext();
+
+ const handleDrop = useCallback(
+ (e: DragEvent) => {
+ if (e.dataTransfer.files && e.dataTransfer.files.length > 0) {
+ addFiles(e.dataTransfer.files);
+ }
+ },
+ [addFiles]
+ );
+
+ const { isDragging, dropzoneProps } = useDropzone({
+ onDrop: handleDrop
+ });
+
+ return (
+ 0 || undefined}
+ className="relative flex min-h-56 flex-col items-center overflow-hidden rounded-xl border-2 border-muted-foreground/50 border-dashed p-4 transition-colors not-data-[files]:justify-center has-[input:focus]:border-ring has-[input:focus]:ring-[3px] has-[input:focus]:ring-ring/50 data-[dragging=true]:bg-accent/50"
+ >
+ {children}
+
+ );
+}
diff --git a/UIs/Customers/src/shared/components/global/file-picker/pieces/empty/index.tsx b/UIs/Customers/src/shared/components/global/file-picker/pieces/empty/index.tsx
new file mode 100644
index 0000000..8579764
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/file-picker/pieces/empty/index.tsx
@@ -0,0 +1,17 @@
+import type { ReactNode } from 'react';
+import { useFilePickerContext } from '../../hooks';
+
+type TFilePickerEmpty = {
+ children?: ReactNode;
+};
+
+export function FilePickerEmpty({ children }: TFilePickerEmpty) {
+ const [{ files }] = useFilePickerContext();
+ return (
+ files.length < 1 && (
+
+ {children}
+
+ )
+ );
+}
diff --git a/UIs/Customers/src/shared/components/global/file-picker/pieces/error/index.tsx b/UIs/Customers/src/shared/components/global/file-picker/pieces/error/index.tsx
new file mode 100644
index 0000000..a115ac3
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/file-picker/pieces/error/index.tsx
@@ -0,0 +1,24 @@
+import { AlertCircleIcon } from 'lucide-react';
+import { useFilePickerContext } from '../../hooks';
+
+export function FilePickerError() {
+ const [{ errors }] = useFilePickerContext();
+
+ if (errors.length === 0) {
+ return null;
+ }
+
+ return (
+
+ {errors.map((error, index) => (
+
+ ))}
+
+ );
+}
diff --git a/UIs/Customers/src/shared/components/global/file-picker/pieces/file-picker/index.tsx b/UIs/Customers/src/shared/components/global/file-picker/pieces/file-picker/index.tsx
new file mode 100644
index 0000000..5185bb9
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/file-picker/pieces/file-picker/index.tsx
@@ -0,0 +1,47 @@
+import { useMemo, type ReactNode } from 'react';
+import { useFilePicker } from '../../hooks/use-file-picker';
+import { FilePickerContext } from '../../hooks';
+import type { FilePickerOptions, FileWithPreview } from '../../types';
+
+type TFilePicker = Omit<
+ FilePickerOptions,
+ 'maxSize' | 'files' | 'onFilesChange'
+> & {
+ files: FileWithPreview[];
+ onFilesChange: (files: FileWithPreview[]) => void;
+ maxSizeMB?: number;
+ children?: ReactNode;
+};
+
+export function FilePicker({
+ children,
+ maxSizeMB = 5,
+ files,
+ onFilesChange,
+ ...options
+}: TFilePicker) {
+ const filePickerOptions: FilePickerOptions = {
+ ...options,
+ files,
+ onFilesChange,
+ maxFiles: options.maxFiles ? options.maxFiles : 5,
+ multiple: options.multiple || true,
+ maxSize: maxSizeMB * 1024 * 1024
+ };
+
+ const [state, actions] = useFilePicker(filePickerOptions);
+
+ const contextValue: [typeof state & { files: typeof files }, typeof actions] =
+ useMemo(() => {
+ return [{ ...state, files }, actions] as [
+ typeof state & { files: typeof files },
+ typeof actions
+ ];
+ }, [state, files, actions]);
+
+ return (
+
+ {children}
+
+ );
+}
diff --git a/UIs/Customers/src/shared/components/global/file-picker/pieces/header/index.tsx b/UIs/Customers/src/shared/components/global/file-picker/pieces/header/index.tsx
new file mode 100644
index 0000000..b579d3f
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/file-picker/pieces/header/index.tsx
@@ -0,0 +1,13 @@
+import type { ReactNode } from 'react';
+import { useFilePickerContext } from '../../hooks';
+
+export function FilePickerHeader({ children }: { children: ReactNode }) {
+ const [{ files }] = useFilePickerContext();
+ return (
+ files.length > 0 && (
+
+ {children}
+
+ )
+ );
+}
diff --git a/UIs/Customers/src/shared/components/global/file-picker/pieces/input/index.tsx b/UIs/Customers/src/shared/components/global/file-picker/pieces/input/index.tsx
new file mode 100644
index 0000000..424281d
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/file-picker/pieces/input/index.tsx
@@ -0,0 +1,19 @@
+import { Input } from '@/shared/components/ui/input';
+import { useFilePickerContext } from '../../hooks';
+
+export function FilePickerInput({ ...props }: React.ComponentProps<'input'>) {
+ const [, { getInputProps }] = useFilePickerContext();
+
+ const { ref, ...inputProps } = getInputProps();
+
+ return (
+
+ );
+}
diff --git a/UIs/Customers/src/shared/components/global/file-picker/types/index.ts b/UIs/Customers/src/shared/components/global/file-picker/types/index.ts
new file mode 100644
index 0000000..95ce2eb
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/file-picker/types/index.ts
@@ -0,0 +1,49 @@
+import type { ChangeEvent, InputHTMLAttributes, RefObject } from 'react';
+
+export type FileMetadata = {
+ name: string;
+ size: number;
+ type: string;
+ url: string;
+ id: string;
+};
+
+export type FileWithPreview = {
+ id: string;
+ file?: File | FileMetadata;
+ name: string;
+ url: string;
+ type: string;
+ publicId?: string;
+ isPrimary?: boolean;
+};
+
+export type FilePickerOptions = {
+ files: FileWithPreview[];
+ onFilesChange: (files: FileWithPreview[]) => void;
+ onFilesAdded?: (addedFiles: FileWithPreview[]) => void;
+
+ maxFiles?: number;
+ maxSize?: number;
+ accept?: string;
+ multiple?: boolean;
+};
+
+export type FilePickerState = {
+ errors: string[];
+};
+
+export type FilePickerActions = {
+ addFiles: (files: FileList | File[]) => void;
+ removeFile: (id: string) => void;
+ setPrimaryFile: (id: string) => void;
+ clearFiles: () => void;
+ clearErrors: () => void;
+ handleFileChange: (e: ChangeEvent) => void;
+ openFileDialog: () => void;
+ getInputProps: (
+ props?: InputHTMLAttributes
+ ) => InputHTMLAttributes & {
+ ref: RefObject;
+ };
+};
diff --git a/UIs/Customers/src/shared/components/global/file-picker/utils/generate-unique-id/index.test.ts b/UIs/Customers/src/shared/components/global/file-picker/utils/generate-unique-id/index.test.ts
new file mode 100644
index 0000000..7cfa5e5
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/file-picker/utils/generate-unique-id/index.test.ts
@@ -0,0 +1,59 @@
+import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
+import { generateUniqueId } from '.';
+import type { FileMetadata } from '../../types';
+
+const MOCK_TIME = 1600000000000;
+const MOCK_FILE_NAME = 'image.png';
+const MOCK_FILE_SIZE = 50000;
+const MOCK_RANDOM_VALUE = 0.5;
+const mockFile = new File(['content'], MOCK_FILE_NAME, {
+ lastModified: MOCK_TIME
+});
+
+Object.defineProperty(mockFile, 'size', {
+ value: MOCK_FILE_SIZE,
+ writable: true
+});
+
+describe('generateUniqueId', () => {
+ beforeEach(() => {
+ vi.spyOn(Math, 'random').mockReturnValue(MOCK_RANDOM_VALUE);
+ });
+
+ afterEach(() => {
+ vi.restoreAllMocks();
+ });
+
+ it('should generate a composite ID using name, lastModified, size, and a random suffix', () => {
+ const generatedId = generateUniqueId(mockFile);
+
+ expect(generatedId).toContain(
+ `${MOCK_FILE_NAME}-${MOCK_TIME}-${MOCK_FILE_SIZE}`
+ );
+
+ expect(generatedId.length).toBeGreaterThan(
+ MOCK_FILE_NAME.length +
+ String(MOCK_TIME).length +
+ String(MOCK_FILE_SIZE).length +
+ 1
+ );
+
+ expect(Math.random).toHaveBeenCalledTimes(1);
+ });
+
+ it('should return the existing ID if the object is FileMetadata', () => {
+ const EXISTING_ID = 'db-id-555';
+ const metadata: FileMetadata = {
+ id: EXISTING_ID,
+ name: 'db.jpg',
+ size: 8,
+ url: '',
+ type: 'image'
+ };
+
+ const returnedId = generateUniqueId(metadata);
+
+ expect(returnedId).toBe(EXISTING_ID);
+ expect(Math.random).not.toHaveBeenCalled();
+ });
+});
diff --git a/UIs/Customers/src/shared/components/global/file-picker/utils/generate-unique-id/index.tsx b/UIs/Customers/src/shared/components/global/file-picker/utils/generate-unique-id/index.tsx
new file mode 100644
index 0000000..16a261e
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/file-picker/utils/generate-unique-id/index.tsx
@@ -0,0 +1,9 @@
+import type { FileMetadata } from '../../types';
+
+export function generateUniqueId(file: File | FileMetadata): string {
+ if (file instanceof File) {
+ return `${file.name}-${file.lastModified}-${file.size}-${Math.random().toString(36).substring(2, 9)}`;
+ }
+
+ return file.id;
+}
diff --git a/UIs/Customers/src/shared/components/global/file-picker/utils/get-file-icon/index.test.tsx b/UIs/Customers/src/shared/components/global/file-picker/utils/get-file-icon/index.test.tsx
new file mode 100644
index 0000000..6ce5748
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/file-picker/utils/get-file-icon/index.test.tsx
@@ -0,0 +1,120 @@
+import { vi, describe, it, expect, beforeEach } from 'vitest';
+import { getFileIcon } from '.';
+import {
+ FileArchiveIcon,
+ FileSpreadsheetIcon,
+ FileTextIcon,
+ HeadphonesIcon,
+ ImageIcon,
+ VideoIcon,
+ FileIcon
+} from 'lucide-react';
+
+vi.mock('lucide-react', async (importOriginal) => {
+ const actual = await importOriginal();
+ return {
+ ...actual,
+ FileArchiveIcon: vi.fn(),
+ FileSpreadsheetIcon: vi.fn(),
+ FileTextIcon: vi.fn(),
+ HeadphonesIcon: vi.fn(),
+ ImageIcon: vi.fn(),
+ VideoIcon: vi.fn(),
+ FileIcon: vi.fn()
+ };
+});
+
+const createFileInput = (
+ type: string,
+ name: string,
+ isFileInstance: boolean = false
+) => {
+ const fileData = { type, name } as { type: string; name: string };
+ if (isFileInstance) {
+ const file = new File([], name, { type });
+
+ return { file };
+ }
+ return { file: fileData };
+};
+
+describe('getFileIcon', () => {
+ beforeEach(() => {
+ vi.clearAllMocks();
+ });
+
+ it('should return the correct icon for image formats', () => {
+ const result = getFileIcon(createFileInput('image/jpeg', 'photo.jpg'));
+
+ expect(result?.type).toBe(ImageIcon);
+ });
+
+ it('should return the correct icon for video formats', () => {
+ const result = getFileIcon(createFileInput('video/mp4', 'clip.mp4'));
+
+ expect(result?.type).toBe(VideoIcon);
+ });
+
+ it('should return the correct icon for audio formats', () => {
+ const result = getFileIcon(createFileInput('audio/mpeg', 'song.mp3'));
+
+ expect(result?.type).toBe(HeadphonesIcon);
+ });
+
+ it('The PDF icon should be restored for PDF files (by type) or DOCX files (by name)', () => {
+ expect(
+ getFileIcon(createFileInput('application/pdf', 'doc.pdf'))?.type
+ ).toBe(FileTextIcon);
+
+ expect(
+ getFileIcon(createFileInput('application/octet-stream', 'report.docx'))
+ ?.type
+ ).toBe(FileTextIcon);
+
+ expect(
+ getFileIcon(createFileInput('application/msword', 'letter.doc'))?.type
+ ).toBe(FileTextIcon);
+ });
+
+ it('The COMPRESSED FILE icon should revert to ZIP or RAR', () => {
+ expect(
+ getFileIcon(createFileInput('application/zip', 'archive.zip'))?.type
+ ).toBe(FileArchiveIcon);
+
+ expect(
+ getFileIcon(createFileInput('application/octet-stream', 'files.rar'))
+ ?.type
+ ).toBe(FileArchiveIcon);
+ });
+
+ it('The Excel icon should be restored for spreadsheets', () => {
+ expect(
+ getFileIcon(createFileInput('application/vnd.ms-excel', 'data.xls'))?.type
+ ).toBe(FileSpreadsheetIcon);
+
+ expect(
+ getFileIcon(createFileInput('application/octet-stream', 'data.xlsx'))
+ ?.type
+ ).toBe(FileSpreadsheetIcon);
+ });
+
+ it('should return the fallback icon (FileIcon) when the type is unknown', () => {
+ const result = getFileIcon(
+ createFileInput('application/json', 'config.json')
+ );
+
+ expect(result?.type).toBe(FileIcon);
+ });
+
+ it('should return undefined (guard clause) if fileType is false (e.g., null or empty)', () => {
+ const result = getFileIcon(createFileInput('', 'no-type.bin'));
+
+ expect(result).toBeUndefined();
+ });
+
+ it('must correctly process a File object (instanceof File)', () => {
+ const result = getFileIcon(createFileInput('image/png', 'photo.png', true));
+
+ expect(result?.type).toBe(ImageIcon);
+ });
+});
diff --git a/UIs/Customers/src/shared/components/global/file-picker/utils/get-file-icon/index.tsx b/UIs/Customers/src/shared/components/global/file-picker/utils/get-file-icon/index.tsx
new file mode 100644
index 0000000..1019060
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/file-picker/utils/get-file-icon/index.tsx
@@ -0,0 +1,63 @@
+import {
+ FileArchiveIcon,
+ FileIcon,
+ FileSpreadsheetIcon,
+ FileTextIcon,
+ HeadphonesIcon,
+ ImageIcon,
+ VideoIcon
+} from 'lucide-react';
+
+const iconMap = {
+ pdf: {
+ icon: FileTextIcon,
+ conditions: (type: string, name: string) =>
+ type.includes('pdf') ||
+ name.endsWith('.pdf') ||
+ type.includes('word') ||
+ name.endsWith('.doc') ||
+ name.endsWith('.docx')
+ },
+ archive: {
+ icon: FileArchiveIcon,
+ conditions: (type: string, name: string) =>
+ type.includes('zip') ||
+ type.includes('archive') ||
+ name.endsWith('.zip') ||
+ name.endsWith('.rar')
+ },
+ excel: {
+ icon: FileSpreadsheetIcon,
+ conditions: (type: string, name: string) =>
+ type.includes('excel') || name.endsWith('.xls') || name.endsWith('.xlsx')
+ },
+ video: {
+ icon: VideoIcon,
+ conditions: (type: string) => type.includes('video/')
+ },
+ audio: {
+ icon: HeadphonesIcon,
+ conditions: (type: string) => type.includes('audio/')
+ },
+ image: {
+ icon: ImageIcon,
+ conditions: (type: string) => type.startsWith('image/')
+ }
+};
+
+export function getFileIcon(file: {
+ file: File | { type: string; name: string };
+}) {
+ const fileType = file.file instanceof File ? file.file.type : file.file.type;
+ const fileName = file.file instanceof File ? file.file.name : file.file.name;
+
+ if (!fileType) return;
+
+ for (const { icon: Icon, conditions } of Object.values(iconMap)) {
+ if (conditions(fileType, fileName)) {
+ return ;
+ }
+ }
+
+ return ;
+}
diff --git a/UIs/Customers/src/shared/components/global/file-picker/utils/get-file-preview/index.tsx b/UIs/Customers/src/shared/components/global/file-picker/utils/get-file-preview/index.tsx
new file mode 100644
index 0000000..205dbd3
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/file-picker/utils/get-file-preview/index.tsx
@@ -0,0 +1,42 @@
+import { ImageIcon } from 'lucide-react';
+import { getFileIcon } from '../get-file-icon';
+
+export const getFilePreview = (
+ file:
+ | {
+ file: File | { type: string; name: string; src: string };
+ }
+ | undefined
+) => {
+ if (!file) return;
+
+ const fileType = file.file instanceof File ? file.file.type : file.file.type;
+ const fileName = file.file instanceof File ? file.file.name : file.file.name;
+
+ const renderImage = (src: string) => (
+
+ );
+
+ return (
+
+ {fileType?.startsWith('image/') ? (
+ file.file instanceof File ? (
+ (() => {
+ const previewUrl = URL.createObjectURL(file.file);
+ return renderImage(previewUrl);
+ })()
+ ) : file.file.src ? (
+ renderImage(file.file.src)
+ ) : (
+
+ )
+ ) : (
+ getFileIcon(file)
+ )}
+
+ );
+};
diff --git a/UIs/Customers/src/shared/components/global/file-picker/utils/index.tsx b/UIs/Customers/src/shared/components/global/file-picker/utils/index.tsx
new file mode 100644
index 0000000..dfbdc6b
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/file-picker/utils/index.tsx
@@ -0,0 +1,5 @@
+export { getFileIcon } from './get-file-icon';
+export { getFilePreview } from './get-file-preview';
+export { generateUniqueId } from './generate-unique-id';
+export { validateFile } from './validate-file';
+export { processNewFiles } from './process-new-files';
diff --git a/UIs/Customers/src/shared/components/global/file-picker/utils/process-new-files/index.test.tsx b/UIs/Customers/src/shared/components/global/file-picker/utils/process-new-files/index.test.tsx
new file mode 100644
index 0000000..6de68db
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/file-picker/utils/process-new-files/index.test.tsx
@@ -0,0 +1,143 @@
+import { vi, describe, it, expect, beforeEach } from 'vitest';
+import { processNewFiles } from '.';
+import type { FileWithPreview } from '../../types';
+
+const { mockGenerateUniqueId, mockValidateFile } = vi.hoisted(() => {
+ const mockGenerateUniqueId = vi.fn(() => 'unique-id-mock');
+ const mockValidateFile = vi.fn();
+
+ return {
+ mockGenerateUniqueId,
+ mockValidateFile
+ };
+});
+
+vi.mock('../generate-unique-id', () => ({
+ generateUniqueId: mockGenerateUniqueId
+}));
+
+vi.mock('../validate-file', () => ({
+ validateFile: mockValidateFile
+}));
+
+//@ts-expect-error global is not defined
+global.URL.createObjectURL = vi.fn((file) => `blob-url-mock-${file.name}`);
+
+const existingFileInstance = new File(['data'], 'A.jpg', {
+ lastModified: 1000
+});
+
+Object.defineProperty(existingFileInstance, 'size', {
+ value: 100,
+ writable: true
+});
+
+const mockFileA = {
+ name: 'A.jpg',
+ size: 100,
+ lastModified: 1000,
+ type: 'image/jpeg'
+} as File;
+
+const mockFileB = {
+ name: 'B.png',
+ size: 200,
+ lastModified: 2000,
+ type: 'image/png'
+} as File;
+
+const mockFileC = {
+ name: 'A.jpg',
+ size: 100,
+ lastModified: 1000,
+ type: 'image/jpeg'
+} as File;
+
+const existingFileA: FileWithPreview = {
+ file: existingFileInstance,
+ id: 'idA',
+ url: 'blobA',
+ name: 'A.jpg',
+ type: 'image/jpeg',
+ isPrimary: false
+};
+
+const defaultOptions = {
+ maxSize: 500,
+ accept: 'image/*',
+ multiple: true
+};
+
+describe('processNewFiles', () => {
+ beforeEach(() => {
+ vi.clearAllMocks();
+ mockValidateFile.mockReturnValue(null);
+ });
+
+ it('must structure valid files with a unique ID and Blob URL', () => {
+ const { validFiles, errors } = processNewFiles(
+ [mockFileB],
+ [],
+ defaultOptions
+ );
+
+ expect(errors).toEqual([]);
+ expect(validFiles).toHaveLength(1);
+ expect(validFiles[0]).toEqual({
+ file: mockFileB,
+ id: 'unique-id-mock',
+ src: 'blob-url-mock-B.png',
+ publicId: undefined,
+ isPrimary: false,
+ name: mockFileB.name,
+ type: mockFileB.type
+ });
+
+ expect(mockGenerateUniqueId).toHaveBeenCalledWith(mockFileB);
+ //@ts-expect-error global is not defined
+ expect(global.URL.createObjectURL).toHaveBeenCalledWith(mockFileB);
+ });
+
+ it('should ignore files that are exact duplicates in "multiple" mode', () => {
+ const newFiles = [mockFileC];
+
+ const { validFiles, errors } = processNewFiles(
+ newFiles,
+ [existingFileA],
+ defaultOptions
+ );
+
+ expect(errors).toEqual([]);
+ expect(validFiles).toEqual([]);
+
+ expect(mockValidateFile).not.toHaveBeenCalled();
+ });
+
+ it('should add the error to the list and skip the file if the validation fails', () => {
+ const VALIDATION_ERROR = 'Arquivo muito grande!';
+
+ mockValidateFile.mockReturnValue(VALIDATION_ERROR);
+
+ const { validFiles, errors } = processNewFiles(
+ [mockFileB],
+ [],
+ defaultOptions
+ );
+
+ expect(errors).toEqual([VALIDATION_ERROR]);
+ expect(validFiles).toEqual([]);
+
+ //@ts-expect-error global is not defined
+ expect(global.URL.createObjectURL).not.toHaveBeenCalled();
+ });
+
+ it('The file should be processed in "single" mode (skipping the duplicate check)', () => {
+ const { validFiles } = processNewFiles([mockFileA], [existingFileA], {
+ ...defaultOptions,
+ multiple: false
+ });
+
+ expect(validFiles).toHaveLength(1);
+ expect(mockValidateFile).toHaveBeenCalledTimes(1);
+ });
+});
diff --git a/UIs/Customers/src/shared/components/global/file-picker/utils/process-new-files/index.tsx b/UIs/Customers/src/shared/components/global/file-picker/utils/process-new-files/index.tsx
new file mode 100644
index 0000000..e4fad6f
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/file-picker/utils/process-new-files/index.tsx
@@ -0,0 +1,49 @@
+import type { FileWithPreview } from '../../types';
+import { generateUniqueId } from '../generate-unique-id';
+import { validateFile } from '../validate-file';
+
+export function processNewFiles(
+ newFilesArray: File[],
+ existingFiles: FileWithPreview[],
+ options: {
+ maxSize: number;
+ accept: string;
+ multiple: boolean;
+ }
+): { validFiles: FileWithPreview[]; errors: string[] } {
+ const errors: string[] = [];
+ const validFiles: FileWithPreview[] = [];
+
+ newFilesArray.forEach((file) => {
+ if (options.multiple) {
+ const isDuplicate = existingFiles.some(
+ (existingFile) =>
+ existingFile.file instanceof File &&
+ existingFile.file.name === file.name &&
+ existingFile.file.size === file.size &&
+ existingFile.file.lastModified === file.lastModified
+ );
+ if (isDuplicate) {
+ return;
+ }
+ }
+
+ const error = validateFile(file, options.maxSize, options.accept);
+
+ if (error) {
+ errors.push(error);
+ } else {
+ validFiles.push({
+ file,
+ id: generateUniqueId(file),
+ url: URL.createObjectURL(file),
+ publicId: undefined,
+ isPrimary: false,
+ name: file.name,
+ type: file.type
+ });
+ }
+ });
+
+ return { validFiles, errors };
+}
diff --git a/UIs/Customers/src/shared/components/global/file-picker/utils/validate-file/index.test.tsx b/UIs/Customers/src/shared/components/global/file-picker/utils/validate-file/index.test.tsx
new file mode 100644
index 0000000..b35984e
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/file-picker/utils/validate-file/index.test.tsx
@@ -0,0 +1,101 @@
+import { vi, describe, it, expect, beforeEach } from 'vitest';
+import { validateFile } from '.';
+
+const { mockFormatBytes } = vi.hoisted(() => {
+ const mockFormatBytes = vi.fn((size) => `${size / 1024} KB`);
+
+ return {
+ mockFormatBytes
+ };
+});
+
+vi.mock('@/shared/utils', () => ({ formatBytes: mockFormatBytes }));
+
+const createMockFile = (name: string, size: number, type: string = '') =>
+ ({
+ name,
+ size,
+ type,
+ lastModified: 0,
+ slice: vi.fn()
+ }) as unknown as File;
+
+describe('validateFile', () => {
+ const MAX_SIZE_BYTES = 500000; // 500 KB
+ const FILE_NAME = 'test-file.pdf';
+
+ beforeEach(() => {
+ vi.clearAllMocks();
+ });
+
+ it('should return an error if the file size exceeds maxSize', () => {
+ const largeFile = createMockFile(FILE_NAME, MAX_SIZE_BYTES + 1000); // 501 KB
+ const error = validateFile(largeFile, MAX_SIZE_BYTES, '*');
+
+ expect(error).toContain(`O arquivo "${FILE_NAME}" excede o tamanho máximo`);
+
+ expect(mockFormatBytes).toHaveBeenCalledWith(MAX_SIZE_BYTES);
+ });
+
+ it('should return null if the file size is within the limit', () => {
+ const smallFile = createMockFile(FILE_NAME, MAX_SIZE_BYTES - 100);
+ const error = validateFile(smallFile, MAX_SIZE_BYTES, '*');
+
+ expect(error).toBeNull();
+ });
+
+ it('It should return null if "accept" is "*" (Acceptance guard)', () => {
+ const file = createMockFile(FILE_NAME, 100, 'application/whatever');
+ const error = validateFile(file, MAX_SIZE_BYTES, '*');
+
+ expect(error).toBeNull();
+ });
+
+ it('must successfully accept Wildcard type (image/*)', () => {
+ const file = createMockFile('photo.jpeg', 100, 'image/jpeg');
+ const error = validateFile(file, MAX_SIZE_BYTES, 'image/*,application/pdf');
+
+ expect(error).toBeNull();
+ });
+
+ it('must successfully accept exact MIME type', () => {
+ const file = createMockFile('doc.pdf', 100, 'application/pdf');
+ const error = validateFile(file, MAX_SIZE_BYTES, 'image/*,application/pdf');
+
+ expect(error).toBeNull();
+ });
+
+ it('should return an error indicating that the MIME type is not accepted', () => {
+ const file = createMockFile('video.mp4', 100, 'video/mp4');
+ const error = validateFile(file, MAX_SIZE_BYTES, 'image/*,application/pdf');
+
+ expect(error).toContain('não tem um formato aceito.');
+ });
+
+ it('should successfully accept files with the extension (.pdf)', () => {
+ const file = createMockFile('report.pdf', 100, 'application/octet-stream');
+ const error = validateFile(file, MAX_SIZE_BYTES, '.pdf, .doc');
+
+ expect(error).toBeNull();
+ });
+
+ it('should return an error if the extension is not accepted', () => {
+ const file = createMockFile('image.jpg', 100, 'image/jpeg');
+ const error = validateFile(file, MAX_SIZE_BYTES, '.pdf,.png');
+
+ expect(error).toContain('não tem um formato aceito.');
+ });
+
+ it('should prioritize size failure if both (size and type) fail', () => {
+ const largeFile = createMockFile(
+ 'bad.mp4',
+ MAX_SIZE_BYTES + 1000,
+ 'video/mp4'
+ );
+
+ const error = validateFile(largeFile, MAX_SIZE_BYTES, 'image/png');
+
+ expect(error).toContain('excede o tamanho máximo');
+ expect(error).not.toContain('formato aceito');
+ });
+});
diff --git a/UIs/Customers/src/shared/components/global/file-picker/utils/validate-file/index.tsx b/UIs/Customers/src/shared/components/global/file-picker/utils/validate-file/index.tsx
new file mode 100644
index 0000000..4b3187d
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/file-picker/utils/validate-file/index.tsx
@@ -0,0 +1,33 @@
+import { formatBytes } from '@/shared/utils';
+export function validateFile(
+ file: File,
+ maxSize: number,
+ accept: string
+): string | null {
+ if (file.size > maxSize) {
+ return `O arquivo "${file.name}" excede o tamanho máximo de ${formatBytes(maxSize)}.`;
+ }
+
+ if (accept !== '*') {
+ const acceptedTypes = accept.split(',').map((type) => type.trim());
+ const fileType = file.type || '';
+ const fileExtension = `.${file.name.split('.').pop() || ''}`;
+
+ const isAccepted = acceptedTypes.some((type) => {
+ if (type.startsWith('.')) {
+ return fileExtension.toLowerCase() === type.toLowerCase();
+ }
+ if (type.endsWith('/*')) {
+ const baseType = type.split('/')[0];
+ return fileType.startsWith(`${baseType}/`);
+ }
+ return fileType === type;
+ });
+
+ if (!isAccepted) {
+ return `O arquivo "${file.name}" não tem um formato aceito.`;
+ }
+ }
+
+ return null;
+}
diff --git a/UIs/Customers/src/shared/components/global/floating-navigation/index.tsx b/UIs/Customers/src/shared/components/global/floating-navigation/index.tsx
new file mode 100644
index 0000000..4f4e4b0
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/floating-navigation/index.tsx
@@ -0,0 +1,64 @@
+import { Button } from '../../ui/button';
+import type{ ElementType } from 'react';
+
+interface NavigationItem {
+ name: string;
+ href: string;
+ icon: ElementType;
+ active?: boolean;
+}
+
+interface FloatingNavigationProps {
+ items: NavigationItem[];
+}
+export const FloatingNavigation = ({ items }: FloatingNavigationProps) => {
+
+ return (
+
+ );
+};
diff --git a/UIs/Customers/src/shared/components/global/image-card/index.test.tsx b/UIs/Customers/src/shared/components/global/image-card/index.test.tsx
new file mode 100644
index 0000000..21edb58
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/image-card/index.test.tsx
@@ -0,0 +1,120 @@
+import { render, screen, fireEvent, waitFor } from '@testing-library/react';
+import { describe, it, expect } from 'vitest';
+import { ImageCard } from '.';
+
+const mockBrowserImageCache = (isCached: boolean) => {
+ const originalComplete = Object.getOwnPropertyDescriptor(
+ HTMLImageElement.prototype,
+ 'complete'
+ );
+
+ if (isCached) {
+ Object.defineProperty(HTMLImageElement.prototype, 'complete', {
+ configurable: true,
+ get: () => true
+ });
+
+ Object.defineProperty(HTMLImageElement.prototype, 'naturalWidth', {
+ configurable: true,
+ get: () => 100
+ });
+ }
+
+ return () => {
+ if (originalComplete) {
+ Object.defineProperty(
+ HTMLImageElement.prototype,
+ 'complete',
+ originalComplete
+ );
+ }
+ };
+};
+
+describe('ImageCard Integration', () => {
+ const MOCK_SRC = 'https://example.com/photo.jpg';
+ const MOCK_ALT = 'Foto do Produto';
+
+ describe('Visual States', () => {
+ it('should render the real Skeleton while loading (Initial State)', () => {
+ render();
+
+ const img = screen.getByRole('img', { name: MOCK_ALT });
+
+ expect(screen.getByText('Carregando imagem...')).toBeInTheDocument();
+ expect(img).toHaveClass('opacity-0');
+ });
+
+ it('should show the image and hide Skeleton when loaded successfully', () => {
+ render();
+
+ const img = screen.getByRole('img', { name: MOCK_ALT });
+
+ fireEvent.load(img);
+
+ expect(img).toHaveClass('opacity-100');
+ expect(
+ screen.queryByText('Carregando imagem...')
+ ).not.toBeInTheDocument();
+ });
+
+ it('should show error state and icon when loading fails', () => {
+ render();
+
+ const img = screen.getByRole('img', { name: MOCK_ALT });
+
+ fireEvent.error(img);
+
+ expect(screen.getByText('Não disponível')).toBeInTheDocument();
+ expect(img).toHaveClass('opacity-0');
+ expect(
+ screen.queryByText('Carregando imagem...')
+ ).not.toBeInTheDocument();
+ });
+ });
+
+ describe('Props & Behavior', () => {
+ it('should not render Skeleton if showSkeleton is false', () => {
+ render();
+
+ expect(
+ screen.queryByText('Carregando imagem...')
+ ).not.toBeInTheDocument();
+ });
+
+ it('should apply correct object-fit class based on prop', () => {
+ const { rerender } = render(
+
+ );
+
+ const img = screen.getByRole('img', { name: MOCK_ALT });
+
+ expect(img).toHaveClass('object-contain');
+ expect(img).not.toHaveClass('object-cover');
+
+ rerender();
+
+ expect(img).toHaveClass('object-cover');
+ expect(img).not.toHaveClass('object-contain');
+ });
+
+ it('should handle browser caching correctly (Instant Load)', async () => {
+ const restore = mockBrowserImageCache(true);
+
+ try {
+ render();
+
+ await waitFor(() => {
+ const img = screen.getByRole('img', { name: MOCK_ALT });
+
+ expect(img).toHaveClass('opacity-100');
+ expect(
+ screen.queryByText('Carregando imagem...')
+ ).not.toBeInTheDocument();
+ });
+ } finally {
+ restore();
+ }
+ });
+ });
+});
diff --git a/UIs/Customers/src/shared/components/global/image-card/index.tsx b/UIs/Customers/src/shared/components/global/image-card/index.tsx
new file mode 100644
index 0000000..57fa877
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/image-card/index.tsx
@@ -0,0 +1,73 @@
+import { useEffect, useRef, useState } from 'react';
+import { cn } from '@/shared/utils';
+import { ImageIcon, ImageOff } from 'lucide-react';
+import { Skeleton } from '../../ui/skeleton';
+
+type TImageCard = {
+ src: string;
+ alt: string;
+ showSkeleton?: boolean;
+ className?: string;
+ objectFit?: 'cover' | 'contain';
+};
+
+export function ImageCard({
+ src,
+ alt,
+ showSkeleton = true,
+ className,
+ objectFit = 'cover'
+}: TImageCard) {
+ const [status, setStatus] = useState<'loading' | 'loaded' | 'error'>(
+ 'loading'
+ );
+ const imgRef = useRef(null);
+
+ useEffect(() => {
+ setStatus('loading');
+ const img = imgRef.current;
+
+ if (img && img.complete && img.naturalWidth > 0) {
+ setStatus('loaded');
+ }
+ }, [src]);
+
+ return (
+
+ {status === 'loading' && showSkeleton && (
+
+
+ Carregando imagem...
+
+ )}
+
+ {status === 'error' && (
+
+
+ Não disponível
+
+ )}
+
+
+
setStatus('loaded')}
+ onError={() => setStatus('error')}
+ />
+
+
+ );
+}
diff --git a/UIs/Customers/src/shared/components/global/input-builder/index.tsx b/UIs/Customers/src/shared/components/global/input-builder/index.tsx
new file mode 100644
index 0000000..758c70e
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/input-builder/index.tsx
@@ -0,0 +1,45 @@
+import type { ComponentProps } from 'react';
+import { Input } from '@/shared/components/ui/input';
+import { InputPercent } from './inputs/input-percent';
+import { InputMonth } from './inputs/input-month';
+import { InputYear } from './inputs/input-year';
+import { InputInstallment } from './inputs/input-installment';
+import { InputMoney } from './inputs/input-money';
+
+export enum InputType {
+ Money = 'Money',
+ Percent = 'Percent',
+ Month = 'Month',
+ Year = 'Year',
+ Installments = 'Installments'
+}
+
+interface InputBuilderProps extends ComponentProps<'input'> {
+ onChangeHandler?: (value: string) => void;
+ inputType: InputType;
+}
+
+
+const INPUT_STRATEGIES: Record = {
+ [InputType.Money]: InputMoney,
+ [InputType.Percent]: InputPercent,
+ [InputType.Month]: InputMonth,
+ [InputType.Year]: InputYear,
+ [InputType.Installments]: InputInstallment
+};
+
+export const InputBuilder = ({
+ inputType,
+ onChangeHandler,
+ ...props
+}: InputBuilderProps) => {
+ const InputComponent = INPUT_STRATEGIES[inputType];
+
+ if (InputComponent) {
+ return ;
+ }
+
+ return (
+ onChangeHandler?.(e.target.value)} />
+ );
+};
diff --git a/UIs/Customers/src/shared/components/global/input-builder/inputs/input-installment.tsx b/UIs/Customers/src/shared/components/global/input-builder/inputs/input-installment.tsx
new file mode 100644
index 0000000..eb17ca8
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/input-builder/inputs/input-installment.tsx
@@ -0,0 +1,27 @@
+import { Input } from '@/shared/components/ui/input';
+import type { ComponentProps } from 'react';
+
+interface InputInstallmentProps extends ComponentProps<'input'> {
+ onChangeHandler?: (value: string) => void;
+}
+
+export const InputInstallment = ({
+ onChangeHandler,
+ ...props
+}: InputInstallmentProps) => {
+ return (
+
+ onChangeHandler?.(e.target.value)}
+ />
+
+ parcelas
+
+
+ );
+};
diff --git a/UIs/Customers/src/shared/components/global/input-builder/inputs/input-money.tsx b/UIs/Customers/src/shared/components/global/input-builder/inputs/input-money.tsx
new file mode 100644
index 0000000..5e2e8d6
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/input-builder/inputs/input-money.tsx
@@ -0,0 +1,56 @@
+import { Input } from '@/shared/components/ui/input';
+import { maskInputMoneyBR } from '@/shared/utils/masks/mask-money';
+import type { ComponentProps } from 'react';
+
+interface InputMoneyProps extends ComponentProps<'input'> {
+ onChangeHandler?: (value: string) => void;
+}
+
+const parseToNumber = (value: any) => {
+ if (value === null || value === undefined || value === '') return undefined;
+ if (typeof value === 'number') return value;
+
+ let stringValue = String(value);
+
+ if (stringValue.includes('.') && stringValue.includes(',')) {
+ stringValue = stringValue.replace(/\./g, '').replace(',', '.');
+ } else if (stringValue.includes(',')) {
+ stringValue = stringValue.replace(',', '.');
+ }
+
+ const parsed = parseFloat(stringValue);
+ return isNaN(parsed) ? undefined : parsed;
+};
+
+function formatCurrencyPTBR(value: any) {
+ const number = parseToNumber(value);
+ if (number === undefined || number === null || isNaN(number))
+ return 'R$ 0,00';
+ return new Intl.NumberFormat('pt-BR', {
+ style: 'currency',
+ currency: 'BRL'
+ }).format(number);
+}
+
+export const InputMoney = ({
+ onChangeHandler,
+ value,
+ defaultValue,
+ ...props
+}: InputMoneyProps) => {
+ const maskedValue = defaultValue
+ ? formatCurrencyPTBR(defaultValue)
+ : value
+ ? formatCurrencyPTBR(value)
+ : '';
+ return (
+ {
+ const masked = maskInputMoneyBR(e.target.value);
+ onChangeHandler?.(masked);
+ }}
+ />
+ );
+};
diff --git a/UIs/Customers/src/shared/components/global/input-builder/inputs/input-month.tsx b/UIs/Customers/src/shared/components/global/input-builder/inputs/input-month.tsx
new file mode 100644
index 0000000..f9cbabc
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/input-builder/inputs/input-month.tsx
@@ -0,0 +1,20 @@
+import { InputWithSuffix } from '@/shared/components/ui/input-with-sufix';
+import type { ComponentProps } from 'react';
+
+interface InputMonthsProps extends ComponentProps<'input'> {
+ onChangeHandler?: (value: string) => void;
+}
+
+export const InputMonth = ({ onChangeHandler, ...props }: InputMonthsProps) => {
+ return (
+ onChangeHandler?.(e.target.value)}
+ suffix="meses"
+ />
+ );
+};
diff --git a/UIs/Customers/src/shared/components/global/input-builder/inputs/input-percent.tsx b/UIs/Customers/src/shared/components/global/input-builder/inputs/input-percent.tsx
new file mode 100644
index 0000000..ad1be4d
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/input-builder/inputs/input-percent.tsx
@@ -0,0 +1,38 @@
+import { Input } from '@/shared/components/ui/input';
+import { maskPercent } from '@/shared/utils/masks/mask-percentage';
+import { useState, type ComponentProps } from 'react';
+
+type InputPercentProps = ComponentProps<'input'> & {
+ onChangeHandler?: (value: string) => void;
+ minDecimals?: number;
+ maxDecimals?: number;
+};
+
+export const InputPercent = ({
+ value,
+ defaultValue,
+ maxDecimals,
+ ...props
+}: InputPercentProps) => {
+ const valueFormated = value || defaultValue;
+ const [percentValue, setPercentValue] = useState(valueFormated || '');
+
+ return (
+
+ {
+ const value = e.target.value;
+ const sanitized = maskPercent(value, maxDecimals);
+ setPercentValue(sanitized);
+ }}
+ className="pr-8"
+ />
+
+ %
+
+
+ );
+};
diff --git a/UIs/Customers/src/shared/components/global/input-builder/inputs/input-year.tsx b/UIs/Customers/src/shared/components/global/input-builder/inputs/input-year.tsx
new file mode 100644
index 0000000..ad04ed3
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/input-builder/inputs/input-year.tsx
@@ -0,0 +1,20 @@
+import { InputWithSuffix } from '@/shared/components/ui/input-with-sufix';
+import type { ComponentProps } from 'react';
+
+interface InputYearProps extends ComponentProps<'input'> {
+ onChangeHandler?: (value: string) => void;
+}
+
+export const InputYear = ({ onChangeHandler, ...props }: InputYearProps) => {
+ return (
+ onChangeHandler?.(e.target.value)}
+ suffix="anos"
+ />
+ );
+};
diff --git a/UIs/Customers/src/shared/components/global/logo/index.tsx b/UIs/Customers/src/shared/components/global/logo/index.tsx
new file mode 100644
index 0000000..a1a085c
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/logo/index.tsx
@@ -0,0 +1,17 @@
+import { cn } from '@/shared/utils';
+
+interface AppLogoProps {
+ className?: string;
+}
+
+export function AppLogo({ className }: AppLogoProps) {
+ return (
+
+ );
+}
diff --git a/UIs/Customers/src/shared/components/global/page-header/index.tsx b/UIs/Customers/src/shared/components/global/page-header/index.tsx
new file mode 100644
index 0000000..c0d731a
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/page-header/index.tsx
@@ -0,0 +1,16 @@
+import { SidebarTrigger } from '../../ui/sidebar';
+
+type pageHeaderProps = {
+ title: string;
+};
+
+export const PageHeader = ({ title }: pageHeaderProps) => {
+ return (
+
+ );
+};
diff --git a/UIs/Customers/src/shared/components/global/placeholder-page/index.test.tsx b/UIs/Customers/src/shared/components/global/placeholder-page/index.test.tsx
new file mode 100644
index 0000000..b728362
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/placeholder-page/index.test.tsx
@@ -0,0 +1,40 @@
+import { render, screen } from '@testing-library/react';
+import { describe, it, expect } from 'vitest';
+import { PlaceholderPage } from './index';
+import { Wrench } from 'lucide-react';
+
+describe('PlaceholderPage Component', () => {
+ it('should render default content when no props are provided', () => {
+ render();
+
+ expect(
+ screen.getByRole('heading', { name: 'Página em Desenvolvimento' })
+ ).toBeInTheDocument();
+
+ expect(
+ screen.getByText(
+ 'Esta funcionalidade ainda está sendo construída pela nossa equipe.'
+ )
+ ).toBeInTheDocument();
+
+ expect(document.querySelector('svg')).toBeInTheDocument();
+ });
+
+ it('should render custom title and message', () => {
+ const customTitle = 'Manutenção Programada';
+ const customMessage = 'Voltamos em breve.';
+
+ render();
+
+ expect(screen.getByText(customMessage)).toBeInTheDocument();
+ expect(
+ screen.getByRole('heading', { name: customTitle })
+ ).toBeInTheDocument();
+ });
+
+ it('should render a custom icon', () => {
+ render();
+
+ expect(document.querySelector('svg')).toBeInTheDocument();
+ });
+});
diff --git a/UIs/Customers/src/shared/components/global/placeholder-page/index.tsx b/UIs/Customers/src/shared/components/global/placeholder-page/index.tsx
new file mode 100644
index 0000000..835e6a5
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/placeholder-page/index.tsx
@@ -0,0 +1,32 @@
+import { HardHat, type LucideIcon } from 'lucide-react';
+import { cn } from '@/shared/utils';
+
+interface PlaceholderPageProps {
+ title?: string;
+ message?: string;
+ icon?: LucideIcon;
+ className?: string;
+}
+
+export function PlaceholderPage({
+ title = 'Página em Desenvolvimento',
+ message = 'Esta funcionalidade ainda está sendo construída pela nossa equipe.',
+ icon: Icon = HardHat,
+ className
+}: PlaceholderPageProps) {
+ return (
+
+
+
+
+
{title}
+
{message}
+
+ );
+}
diff --git a/UIs/Customers/src/shared/components/global/restricted-link/index.tsx b/UIs/Customers/src/shared/components/global/restricted-link/index.tsx
new file mode 100644
index 0000000..b5f47a4
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/restricted-link/index.tsx
@@ -0,0 +1,12 @@
+//import { getCurrentTenant } from "@/libs/axios";
+import { NavLink, type NavLinkProps } from 'react-router';
+
+export function RestrictedLink({ children, ...props }: NavLinkProps) {
+ const tenant = 'smartconsig'; // Mock tenant value for demonstration
+
+ if (tenant !== 'smartconsig') {
+ return null;
+ }
+
+ return {children};
+}
diff --git a/UIs/Customers/src/shared/components/global/simple-data-table/index.test.tsx b/UIs/Customers/src/shared/components/global/simple-data-table/index.test.tsx
new file mode 100644
index 0000000..0840f32
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/simple-data-table/index.test.tsx
@@ -0,0 +1,75 @@
+import { describe, it, expect } from 'vitest';
+import { render, screen } from '@testing-library/react';
+import type { ColumnDef } from '@tanstack/react-table';
+import { SimpleDataTable } from './index';
+
+interface TestData {
+ id: string;
+ name: string;
+ role: string;
+}
+
+const mockData: TestData[] = [
+ { id: '1', name: 'Ana Silva', role: 'Admin' },
+ { id: '2', name: 'João Santos', role: 'User' }
+];
+
+const mockColumns: ColumnDef[] = [
+ {
+ accessorKey: 'name',
+ header: 'Nome Completo'
+ },
+ {
+ accessorKey: 'role',
+ header: 'Função'
+ }
+];
+
+describe('SimpleDataTable Component', () => {
+ describe('Rendering Data', () => {
+ it('should render headers and rows correctly when data is provided', () => {
+ render();
+
+ expect(
+ screen.getByRole('columnheader', { name: 'Nome Completo' })
+ ).toBeInTheDocument();
+
+ expect(
+ screen.getByRole('columnheader', { name: 'Função' })
+ ).toBeInTheDocument();
+
+ expect(screen.getByText('Ana Silva')).toBeInTheDocument();
+ expect(screen.getByText('Admin')).toBeInTheDocument();
+ expect(screen.getByText('João Santos')).toBeInTheDocument();
+ });
+ });
+
+ describe('Empty States (Branch Coverage)', () => {
+ it('should render the default empty message when data is empty and no custom message is provided', () => {
+ render();
+
+ const cell = screen.getByRole('cell');
+
+ expect(cell).toHaveTextContent('Nenhum registro disponível.');
+ expect(cell).toHaveClass('text-muted-foreground');
+ expect(cell).toHaveAttribute('colspan', '2');
+ });
+
+ it('should render the custom empty message when provided', () => {
+ const customMessage = 'Não há itens para exibir neste momento.';
+
+ render(
+
+ );
+
+ expect(screen.getByText(customMessage)).toBeInTheDocument();
+ expect(
+ screen.queryByText('Nenhum registro disponível.')
+ ).not.toBeInTheDocument();
+ });
+ });
+});
diff --git a/UIs/Customers/src/shared/components/global/simple-data-table/index.tsx b/UIs/Customers/src/shared/components/global/simple-data-table/index.tsx
new file mode 100644
index 0000000..979bf2f
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/simple-data-table/index.tsx
@@ -0,0 +1,79 @@
+import {
+ useReactTable,
+ getCoreRowModel,
+ flexRender,
+ type ColumnDef,
+ type TableMeta
+} from '@tanstack/react-table';
+
+import {
+ Table,
+ TableHeader,
+ TableHead,
+ TableBody,
+ TableRow,
+ TableCell
+} from '@/shared/components/ui/table';
+
+interface TSimpleDataTable {
+ data: TData[];
+ emptyMessage?: string;
+ columns: ColumnDef[];
+ meta?: TableMeta;
+}
+
+export function SimpleDataTable({
+ data,
+ emptyMessage,
+ columns,
+ meta
+}: TSimpleDataTable) {
+ const table = useReactTable({
+ data,
+ columns,
+ getCoreRowModel: getCoreRowModel(),
+ meta: meta
+ });
+
+ return (
+
+
+ {table.getHeaderGroups().map((headerGroup) => (
+
+ {headerGroup.headers.map((header) => (
+
+ {!header.isPlaceholder &&
+ flexRender(
+ header.column.columnDef.header,
+ header.getContext()
+ )}
+
+ ))}
+
+ ))}
+
+
+ {table.getRowModel().rows.length > 0 ? (
+ table.getRowModel().rows.map((row) => (
+
+ {row.getVisibleCells().map((cell) => (
+
+ {flexRender(cell.column.columnDef.cell, cell.getContext())}
+
+ ))}
+
+ ))
+ ) : (
+
+
+ {emptyMessage || 'Nenhum registro disponível.'}
+
+
+ )}
+
+
+ );
+}
diff --git a/UIs/Customers/src/shared/components/global/submiting-button/index.tsx b/UIs/Customers/src/shared/components/global/submiting-button/index.tsx
new file mode 100644
index 0000000..6643a14
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/submiting-button/index.tsx
@@ -0,0 +1,31 @@
+import { Button } from '@/shared/components/ui/button';
+import { Loader2, type LucideIcon } from 'lucide-react';
+import type { ComponentProps } from 'react';
+
+interface SubmitingButtonProps extends ComponentProps {
+ label?: string;
+ Icon?: LucideIcon;
+ showLabel?: boolean;
+ state: boolean;
+ onClick?: () => void;
+}
+
+export const SubmitingButton = ({
+ label,
+ state,
+ showLabel = true,
+ Icon,
+ ...props
+}: SubmitingButtonProps) => {
+ return state ? (
+
+ ) : (
+
+ );
+};
diff --git a/UIs/Customers/src/shared/components/global/wizard/hooks/index.tsx b/UIs/Customers/src/shared/components/global/wizard/hooks/index.tsx
new file mode 100644
index 0000000..9895678
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/wizard/hooks/index.tsx
@@ -0,0 +1,187 @@
+import {
+ createContext,
+ useEffect,
+ useMemo,
+ useState,
+ useCallback,
+ useContext
+} from 'react';
+import { useSearchParams } from 'react-router';
+import type { WizardContextType, WizardRootProps, WizardStep } from '../types';
+
+export const WizardContext = createContext(null);
+
+export function WizardProvider({
+ children,
+ steps,
+ urlParamKey = 'step',
+ onBeforeNextStep,
+ onFinish,
+ onCancel
+}: WizardRootProps) {
+ const [searchParams, setSearchParams] = useSearchParams();
+ const [isLoading, setIsLoading] = useState(false);
+ const currentStepId = searchParams.get(urlParamKey);
+ const stepsHash = steps.map((s) => s.id).join(',');
+
+ const currentStepIndex = useMemo(() => {
+ if (!currentStepId) return 0;
+
+ const foundIndex = steps.findIndex(
+ (s: WizardStep) => s.id === currentStepId
+ );
+
+ return foundIndex >= 0 ? foundIndex : 0;
+ }, [currentStepId, steps]);
+
+ const currentStep = steps[currentStepIndex];
+ const isFirstStep = currentStepIndex === 0;
+ const isLastStep = currentStepIndex === steps.length - 1;
+
+ useEffect(() => {
+ const isUrlEmptyOrInvalid =
+ !currentStepId ||
+ steps.findIndex((s: WizardStep) => s.id === currentStepId) === -1;
+
+ if (isUrlEmptyOrInvalid && steps.length > 0) {
+ setSearchParams(
+ (prev) => {
+ const newParams = new URLSearchParams(prev);
+
+ newParams.set(urlParamKey, steps[0].id);
+
+ return newParams;
+ },
+ { replace: true }
+ );
+ }
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [currentStepId, stepsHash, urlParamKey, setSearchParams]);
+
+ const changeStep = useCallback(
+ (newIndex: number) => {
+ if (newIndex < 0 || newIndex >= steps.length) return;
+
+ const step = steps[newIndex];
+
+ setSearchParams((prev) => {
+ const newParams = new URLSearchParams(prev);
+
+ newParams.set(urlParamKey, step.id);
+
+ return newParams;
+ });
+ },
+ [steps, urlParamKey, setSearchParams]
+ );
+
+ const nextStep = useCallback(async () => {
+ if (isLoading) return;
+
+ setIsLoading(true);
+
+ if (onBeforeNextStep) {
+ const canProceed = await onBeforeNextStep(currentStep);
+
+ if (!canProceed) {
+ setIsLoading(false);
+
+ return;
+ }
+ }
+
+ if (!isLastStep) {
+ changeStep(currentStepIndex + 1);
+ }
+
+ setIsLoading(false);
+ }, [
+ isLoading,
+ onBeforeNextStep,
+ currentStep,
+ isLastStep,
+ changeStep,
+ currentStepIndex
+ ]);
+
+ const prevStep = useCallback(() => {
+ if (isFirstStep || isLoading) return;
+
+ changeStep(currentStepIndex - 1);
+ }, [isFirstStep, isLoading, changeStep, currentStepIndex]);
+
+ const goToStep = useCallback(
+ (index: number) => {
+ if (isLoading) return;
+
+ changeStep(index);
+ },
+ [isLoading, changeStep]
+ );
+
+ const handleFinish = useCallback(async () => {
+ if (isLoading) return;
+ setIsLoading(true);
+
+ try {
+ await onFinish();
+ } catch (error) {
+ console.error(error);
+ } finally {
+ setIsLoading(false);
+ }
+ }, [isLoading, onFinish]);
+
+ const handleCancel = useCallback(() => {
+ onCancel?.();
+ }, [onCancel]);
+
+ const value = useMemo(
+ () => ({
+ state: {
+ currentStep,
+ currentStepIndex,
+ totalSteps: steps.length,
+ isFirstStep,
+ isLastStep,
+ isLoading
+ },
+ actions: {
+ nextStep,
+ prevStep,
+ goToStep,
+ handleCancel,
+ handleFinish
+ }
+ }),
+ [
+ currentStep,
+ currentStepIndex,
+ steps.length,
+ isFirstStep,
+ isLastStep,
+ isLoading,
+ nextStep,
+ prevStep,
+ goToStep,
+ handleCancel,
+ handleFinish
+ ]
+ );
+
+ return (
+ {children}
+ );
+}
+
+export function useWizard() {
+ const context = useContext(WizardContext);
+
+ if (!context) {
+ throw new Error(
+ 'useWizard deve ser usado dentro de um componente '
+ );
+ }
+
+ return context;
+}
diff --git a/UIs/Customers/src/shared/components/global/wizard/index.test.tsx b/UIs/Customers/src/shared/components/global/wizard/index.test.tsx
new file mode 100644
index 0000000..2a07290
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/wizard/index.test.tsx
@@ -0,0 +1,303 @@
+import { render, screen, waitFor, act } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import { describe, it, expect, vi, afterEach } from 'vitest';
+import { MemoryRouter } from 'react-router';
+import {
+ Wizard,
+ WizardHeader,
+ WizardContent,
+ WizardControl,
+ useWizard
+} from './index';
+
+const steps = [
+ { id: 'step-1', label: 'Dados Pessoais', component: Passo 1: Dados
},
+ { id: 'step-2', label: 'Endereço', component: Passo 2: Endereço
},
+ { id: 'step-3', label: 'Revisão', component: Passo 3: Revisão
}
+];
+
+const WizardContextSpy = ({ onContext }: { onContext: (ctx: any) => void }) => {
+ const context = useWizard();
+
+ onContext(context);
+
+ return null;
+};
+
+const renderWizard = (
+ initialEntries = ['/'],
+ props: any = {},
+ extraChildren: React.ReactNode = null
+) => {
+ return render(
+
+
+
+
+ {extraChildren}
+
+
+
+ );
+};
+
+describe('Wizard Component', () => {
+ afterEach(() => {
+ vi.useRealTimers();
+ vi.restoreAllMocks();
+ });
+
+ describe('Navigation Flow (UI Integration)', () => {
+ it('should render the first step by default', () => {
+ renderWizard();
+
+ expect(
+ screen.getByRole('heading', { name: 'Passo 1: Dados' })
+ ).toBeInTheDocument();
+
+ expect(
+ screen.queryByRole('button', { name: /Voltar/i })
+ ).not.toBeInTheDocument();
+ });
+
+ it('should navigate to the next step when clicking "Next"', async () => {
+ const user = userEvent.setup();
+
+ renderWizard();
+
+ const nextBtn = screen.getByRole('button', { name: /Avançar/i });
+
+ await user.click(nextBtn);
+
+ expect(
+ await screen.findByRole('heading', { name: 'Passo 2: Endereço' })
+ ).toBeInTheDocument();
+ });
+
+ it('should navigate back when clicking "Back"', async () => {
+ const user = userEvent.setup();
+
+ renderWizard(['/?step=step-2']);
+
+ expect(
+ screen.getByRole('heading', { name: 'Passo 2: Endereço' })
+ ).toBeInTheDocument();
+
+ const prevBtn = screen.getByRole('button', { name: /Voltar/i });
+
+ await user.click(prevBtn);
+
+ expect(
+ await screen.findByRole('heading', { name: 'Passo 1: Dados' })
+ ).toBeInTheDocument();
+ });
+ });
+
+ describe('URL Synchronization', () => {
+ it('should initialize correctly based on URL params', () => {
+ renderWizard(['/?step=step-3']);
+
+ expect(
+ screen.getByRole('heading', { name: 'Passo 3: Revisão' })
+ ).toBeInTheDocument();
+ });
+
+ it('should fallback to step 1 for invalid URL params', () => {
+ renderWizard(['/?step=invalid-step-id']);
+
+ expect(
+ screen.getByRole('heading', { name: 'Passo 1: Dados' })
+ ).toBeInTheDocument();
+ });
+ });
+
+ describe('Validation Logic (onBeforeNextStep)', () => {
+ it('should block navigation if validation returns false', async () => {
+ const user = userEvent.setup();
+ const mockValidate = vi.fn().mockReturnValue(false);
+
+ renderWizard(['/'], { onBeforeNextStep: mockValidate });
+
+ await user.click(screen.getByRole('button', { name: /Avançar/i }));
+
+ expect(mockValidate).toHaveBeenCalledWith(
+ expect.objectContaining({ id: 'step-1' })
+ );
+
+ expect(
+ screen.getByRole('heading', { name: 'Passo 1: Dados' })
+ ).toBeInTheDocument();
+ });
+
+ it('should show loading state and wait for async validation', async () => {
+ const user = userEvent.setup();
+
+ let resolveValidation: (value: boolean) => void = () => {};
+ const validationPromise = new Promise((resolve) => {
+ resolveValidation = resolve;
+ });
+
+ const mockAsyncValidate = vi.fn().mockReturnValue(validationPromise);
+
+ renderWizard(['/'], { onBeforeNextStep: mockAsyncValidate });
+
+ const nextBtn = screen.getByRole('button', { name: /Avançar/i });
+
+ const clickPromise = user.click(nextBtn);
+
+ await waitFor(() => {
+ expect(nextBtn).toBeDisabled();
+ });
+
+ resolveValidation(true);
+
+ await clickPromise;
+
+ expect(
+ await screen.findByRole('heading', { name: 'Passo 2: Endereço' })
+ ).toBeInTheDocument();
+ });
+ });
+
+ describe('Completion', () => {
+ it('should call onFinish only on the last step', async () => {
+ const user = userEvent.setup();
+ const handleFinish = vi.fn();
+
+ renderWizard(['/?step=step-3'], { onFinish: handleFinish });
+
+ const finishBtn = screen.getByRole('button', { name: /Finalizar/i });
+
+ expect(
+ screen.queryByRole('button', { name: /Avançar/i })
+ ).not.toBeInTheDocument();
+
+ await user.click(finishBtn);
+
+ expect(handleFinish).toHaveBeenCalledTimes(1);
+ });
+ });
+
+ describe('Guard Clauses & Branch Coverage (Whitebox Testing)', () => {
+ it('should ignore prevStep() when on the first step', () => {
+ let context: any;
+
+ renderWizard(
+ ['/'],
+ {},
+ (context = ctx)} />
+ );
+
+ act(() => {
+ context.actions.prevStep();
+ });
+
+ expect(context.state.currentStepIndex).toBe(0);
+ expect(
+ screen.getByRole('heading', { name: 'Passo 1: Dados' })
+ ).toBeInTheDocument();
+ });
+
+ it('should ignore nextStep() when on the last step (without explicit finish)', () => {
+ let context: any;
+
+ renderWizard(
+ ['/?step=step-3'],
+ {},
+ (context = ctx)} />
+ );
+
+ act(() => {
+ context.actions.nextStep();
+ });
+
+ expect(context.state.currentStepIndex).toBe(2);
+ expect(
+ screen.getByRole('heading', { name: 'Passo 3: Revisão' })
+ ).toBeInTheDocument();
+ });
+
+ it('should ignore goToStep() with invalid indices (negative or out of bounds)', () => {
+ let context: any;
+
+ renderWizard(
+ ['/'],
+ {},
+ (context = ctx)} />
+ );
+
+ act(() => {
+ context.actions.goToStep(-1);
+ });
+
+ expect(context.state.currentStepIndex).toBe(0);
+
+ act(() => {
+ context.actions.goToStep(99);
+ });
+
+ expect(context.state.currentStepIndex).toBe(0);
+ });
+
+ it('should ignore all navigation actions while isLoading is true', async () => {
+ let context: any;
+ const pendingPromise = new Promise(() => {});
+ const mockValidate = vi.fn().mockReturnValue(pendingPromise);
+
+ renderWizard(
+ ['/'],
+ { onBeforeNextStep: mockValidate },
+ (context = ctx)} />
+ );
+
+ await act(async () => {
+ context.actions.nextStep();
+ });
+
+ expect(context.state.isLoading).toBe(true);
+
+ act(() => {
+ context.actions.goToStep(2);
+ context.actions.prevStep();
+ context.actions.nextStep();
+ context.actions.handleFinish();
+ });
+
+ expect(context.state.currentStepIndex).toBe(0);
+ expect(mockValidate).toHaveBeenCalledTimes(1);
+ });
+
+ it('should handle errors thrown outside the provider', () => {
+ const consoleSpy = vi
+ .spyOn(console, 'error')
+ .mockImplementation(() => {});
+
+ const BadComponent = () => {
+ useWizard();
+ return null;
+ };
+
+ expect(() => render()).toThrow(
+ 'useWizard deve ser usado dentro de um componente '
+ );
+
+ consoleSpy.mockRestore();
+ });
+
+ it('should handle cancel action safely when onCancel is undefined', () => {
+ let context: any;
+
+ renderWizard(
+ ['/'],
+ {},
+ (context = ctx)} />
+ );
+
+ act(() => {
+ context.actions.handleCancel();
+ });
+
+ expect(context.state.currentStepIndex).toBe(0);
+ });
+ });
+});
diff --git a/UIs/Customers/src/shared/components/global/wizard/index.tsx b/UIs/Customers/src/shared/components/global/wizard/index.tsx
new file mode 100644
index 0000000..be05bc1
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/wizard/index.tsx
@@ -0,0 +1,6 @@
+export { WizardContext, WizardProvider, useWizard } from './hooks';
+export { WizardHeader } from './pieces/wizard-header';
+export { WizardContent } from './pieces/wizard-content';
+export { WizardControl } from './pieces/wizard-controls';
+export { Wizard } from './pieces/wizard';
+export type { WizardStep, WizardRootProps } from './types';
diff --git a/UIs/Customers/src/shared/components/global/wizard/pieces/wizard-content/index.tsx b/UIs/Customers/src/shared/components/global/wizard/pieces/wizard-content/index.tsx
new file mode 100644
index 0000000..adc9afd
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/wizard/pieces/wizard-content/index.tsx
@@ -0,0 +1,17 @@
+import type { ComponentProps } from 'react';
+import { useWizard } from '../../hooks';
+import { Card } from '@/shared/components/ui/card';
+import { cn } from '@/shared/utils';
+
+export function WizardContent({ className, ...props }: ComponentProps<'div'>) {
+ const { state } = useWizard();
+
+ return (
+
+ {state.currentStep?.component}
+
+ );
+}
diff --git a/UIs/Customers/src/shared/components/global/wizard/pieces/wizard-controls/index.tsx b/UIs/Customers/src/shared/components/global/wizard/pieces/wizard-controls/index.tsx
new file mode 100644
index 0000000..8e7a1b3
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/wizard/pieces/wizard-controls/index.tsx
@@ -0,0 +1,78 @@
+import type { ComponentProps } from 'react';
+import { Loader2 } from 'lucide-react';
+import { Button } from '@/shared/components/ui/button';
+import { cn } from '@/shared/utils';
+import { useWizard } from '../../hooks';
+
+interface WizardControlProps extends ComponentProps<'div'> {
+ labels?: {
+ next?: string;
+ back?: string;
+ finish?: string;
+ cancel?: string;
+ };
+}
+
+export function WizardControl({
+ className,
+ labels,
+ ...props
+}: WizardControlProps) {
+ const { state, actions } = useWizard();
+
+ return (
+
+
+ {!state.isFirstStep && (
+
+ )}
+
+
+
+
+
+ {state.isLastStep ? (
+
+ ) : (
+
+ )}
+
+
+ );
+}
diff --git a/UIs/Customers/src/shared/components/global/wizard/pieces/wizard-header/index.tsx b/UIs/Customers/src/shared/components/global/wizard/pieces/wizard-header/index.tsx
new file mode 100644
index 0000000..f39c85a
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/wizard/pieces/wizard-header/index.tsx
@@ -0,0 +1,34 @@
+import type { ComponentProps } from 'react';
+import { useWizard } from '../../hooks';
+import { cn } from '@/shared/utils';
+import {
+ Breadcrumb,
+ BreadcrumbItem,
+ BreadcrumbList,
+ BreadcrumbPage,
+ BreadcrumbSeparator
+} from '@/shared/components/ui/breadcrumb';
+import { Progress } from '@/shared/components/ui/progress';
+
+export function WizardHeader({
+ className,
+ label,
+ ...props
+}: { label?: string } & ComponentProps<'div'>) {
+ const { state } = useWizard();
+
+ return (
+
+
+
+ {label}
+
+ {state.currentStep?.label}
+
+
+
+
+ );
+}
diff --git a/UIs/Customers/src/shared/components/global/wizard/pieces/wizard/index.tsx b/UIs/Customers/src/shared/components/global/wizard/pieces/wizard/index.tsx
new file mode 100644
index 0000000..f6fc201
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/wizard/pieces/wizard/index.tsx
@@ -0,0 +1,6 @@
+import { WizardProvider } from '../../hooks';
+import type { WizardRootProps } from '../../types';
+
+export function Wizard(props: WizardRootProps) {
+ return ;
+}
diff --git a/UIs/Customers/src/shared/components/global/wizard/types/index.ts b/UIs/Customers/src/shared/components/global/wizard/types/index.ts
new file mode 100644
index 0000000..6a83386
--- /dev/null
+++ b/UIs/Customers/src/shared/components/global/wizard/types/index.ts
@@ -0,0 +1,38 @@
+import type { ReactNode } from 'react';
+
+export interface WizardStep {
+ id: string;
+ label: string;
+ component: ReactNode;
+}
+
+type WizardState = {
+ currentStep: WizardStep;
+ currentStepIndex: number;
+ totalSteps: number;
+ isFirstStep: boolean;
+ isLastStep: boolean;
+ isLoading: boolean;
+};
+
+type WizardActions = {
+ nextStep: () => Promise;
+ prevStep: () => void;
+ goToStep: (index: number) => void;
+ handleCancel: () => void;
+ handleFinish: () => Promise;
+};
+
+export interface WizardContextType {
+ state: WizardState;
+ actions: WizardActions;
+}
+
+export interface WizardRootProps {
+ children: ReactNode;
+ steps: WizardStep[];
+ urlParamKey?: string;
+ onBeforeNextStep?: (step: WizardStep) => Promise | boolean;
+ onFinish: () => void | Promise;
+ onCancel?: () => void;
+}
diff --git a/UIs/Customers/src/shared/components/ui/accordion.tsx b/UIs/Customers/src/shared/components/ui/accordion.tsx
new file mode 100644
index 0000000..46ccea1
--- /dev/null
+++ b/UIs/Customers/src/shared/components/ui/accordion.tsx
@@ -0,0 +1,64 @@
+import * as React from 'react';
+import { ChevronDownIcon } from 'lucide-react';
+import { Accordion as AccordionPrimitive } from 'radix-ui';
+
+import { cn } from '@/shared/utils';
+
+function Accordion({
+ ...props
+}: React.ComponentProps) {
+ return ;
+}
+
+function AccordionItem({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ );
+}
+
+function AccordionTrigger({
+ className,
+ children,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ svg]:rotate-180',
+ className
+ )}
+ {...props}
+ >
+ {children}
+
+
+
+ );
+}
+
+function AccordionContent({
+ className,
+ children,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ {children}
+
+ );
+}
+
+export { Accordion, AccordionItem, AccordionTrigger, AccordionContent };
diff --git a/UIs/Customers/src/shared/components/ui/alert-dialog.tsx b/UIs/Customers/src/shared/components/ui/alert-dialog.tsx
new file mode 100644
index 0000000..b2eed19
--- /dev/null
+++ b/UIs/Customers/src/shared/components/ui/alert-dialog.tsx
@@ -0,0 +1,194 @@
+import * as React from 'react';
+import { AlertDialog as AlertDialogPrimitive } from 'radix-ui';
+
+import { cn } from '@/shared/utils';
+import { Button } from '@/shared/components/ui/button';
+
+function AlertDialog({
+ ...props
+}: React.ComponentProps) {
+ return ;
+}
+
+function AlertDialogTrigger({
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ );
+}
+
+function AlertDialogPortal({
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ );
+}
+
+function AlertDialogOverlay({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ );
+}
+
+function AlertDialogContent({
+ className,
+ size = 'default',
+ ...props
+}: React.ComponentProps & {
+ size?: 'default' | 'sm';
+}) {
+ return (
+
+
+
+
+ );
+}
+
+function AlertDialogHeader({
+ className,
+ ...props
+}: React.ComponentProps<'div'>) {
+ return (
+
+ );
+}
+
+function AlertDialogFooter({
+ className,
+ ...props
+}: React.ComponentProps<'div'>) {
+ return (
+
+ );
+}
+
+function AlertDialogTitle({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ );
+}
+
+function AlertDialogDescription({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ );
+}
+
+function AlertDialogMedia({
+ className,
+ ...props
+}: React.ComponentProps<'div'>) {
+ return (
+
+ );
+}
+
+function AlertDialogAction({
+ className,
+ variant = 'default',
+ size = 'default',
+ ...props
+}: React.ComponentProps &
+ Pick, 'variant' | 'size'>) {
+ return (
+
+ );
+}
+
+function AlertDialogCancel({
+ className,
+ variant = 'outline',
+ size = 'default',
+ ...props
+}: React.ComponentProps &
+ Pick, 'variant' | 'size'>) {
+ return (
+
+ );
+}
+
+export {
+ AlertDialog,
+ AlertDialogAction,
+ AlertDialogCancel,
+ AlertDialogContent,
+ AlertDialogDescription,
+ AlertDialogFooter,
+ AlertDialogHeader,
+ AlertDialogMedia,
+ AlertDialogOverlay,
+ AlertDialogPortal,
+ AlertDialogTitle,
+ AlertDialogTrigger
+};
diff --git a/UIs/Customers/src/shared/components/ui/alert.tsx b/UIs/Customers/src/shared/components/ui/alert.tsx
new file mode 100644
index 0000000..4aae193
--- /dev/null
+++ b/UIs/Customers/src/shared/components/ui/alert.tsx
@@ -0,0 +1,66 @@
+import * as React from 'react';
+import { cva, type VariantProps } from 'class-variance-authority';
+
+import { cn } from '@/shared/utils';
+
+const alertVariants = cva(
+ 'relative w-full rounded-lg border px-4 py-3 text-sm grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current',
+ {
+ variants: {
+ variant: {
+ default: 'bg-card text-card-foreground',
+ destructive:
+ 'text-destructive bg-card [&>svg]:text-current *:data-[slot=alert-description]:text-destructive/90'
+ }
+ },
+ defaultVariants: {
+ variant: 'default'
+ }
+ }
+);
+
+function Alert({
+ className,
+ variant,
+ ...props
+}: React.ComponentProps<'div'> & VariantProps) {
+ return (
+
+ );
+}
+
+function AlertTitle({ className, ...props }: React.ComponentProps<'div'>) {
+ return (
+
+ );
+}
+
+function AlertDescription({
+ className,
+ ...props
+}: React.ComponentProps<'div'>) {
+ return (
+
+ );
+}
+
+export { Alert, AlertTitle, AlertDescription };
diff --git a/UIs/Customers/src/shared/components/ui/avatar.tsx b/UIs/Customers/src/shared/components/ui/avatar.tsx
new file mode 100644
index 0000000..788bab9
--- /dev/null
+++ b/UIs/Customers/src/shared/components/ui/avatar.tsx
@@ -0,0 +1,51 @@
+import * as React from 'react';
+import * as AvatarPrimitive from '@radix-ui/react-avatar';
+
+import { cn } from '@/shared/utils';
+
+function Avatar({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ );
+}
+
+function AvatarImage({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ );
+}
+
+function AvatarFallback({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ );
+}
+
+export { Avatar, AvatarImage, AvatarFallback };
diff --git a/UIs/Customers/src/shared/components/ui/badge.tsx b/UIs/Customers/src/shared/components/ui/badge.tsx
new file mode 100644
index 0000000..8735dc8
--- /dev/null
+++ b/UIs/Customers/src/shared/components/ui/badge.tsx
@@ -0,0 +1,46 @@
+import * as React from 'react';
+import { Slot } from '@radix-ui/react-slot';
+import { cva, type VariantProps } from 'class-variance-authority';
+
+import { cn } from '@/shared/utils';
+
+const badgeVariants = cva(
+ 'inline-flex items-center justify-center rounded-full border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden',
+ {
+ variants: {
+ variant: {
+ default:
+ 'border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90',
+ secondary:
+ 'border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90',
+ destructive:
+ 'border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60',
+ outline:
+ 'text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground'
+ }
+ },
+ defaultVariants: {
+ variant: 'default'
+ }
+ }
+);
+
+function Badge({
+ className,
+ variant,
+ asChild = false,
+ ...props
+}: React.ComponentProps<'span'> &
+ VariantProps & { asChild?: boolean }) {
+ const Comp = asChild ? Slot : 'span';
+
+ return (
+
+ );
+}
+
+export { Badge, badgeVariants };
diff --git a/UIs/Customers/src/shared/components/ui/breadcrumb.tsx b/UIs/Customers/src/shared/components/ui/breadcrumb.tsx
new file mode 100644
index 0000000..e87ef15
--- /dev/null
+++ b/UIs/Customers/src/shared/components/ui/breadcrumb.tsx
@@ -0,0 +1,109 @@
+import * as React from 'react';
+import { Slot } from '@radix-ui/react-slot';
+import { ChevronRight, MoreHorizontal } from 'lucide-react';
+
+import { cn } from '@/shared/utils';
+
+function Breadcrumb({ ...props }: React.ComponentProps<'nav'>) {
+ return ;
+}
+
+function BreadcrumbList({ className, ...props }: React.ComponentProps<'ol'>) {
+ return (
+
+ );
+}
+
+function BreadcrumbItem({ className, ...props }: React.ComponentProps<'li'>) {
+ return (
+
+ );
+}
+
+function BreadcrumbLink({
+ asChild,
+ className,
+ ...props
+}: React.ComponentProps<'a'> & {
+ asChild?: boolean;
+}) {
+ const Comp = asChild ? Slot : 'a';
+
+ return (
+
+ );
+}
+
+function BreadcrumbPage({ className, ...props }: React.ComponentProps<'span'>) {
+ return (
+
+ );
+}
+
+function BreadcrumbSeparator({
+ children,
+ className,
+ ...props
+}: React.ComponentProps<'li'>) {
+ return (
+ svg]:size-3.5', className)}
+ {...props}
+ >
+ {children ?? }
+
+ );
+}
+
+function BreadcrumbEllipsis({
+ className,
+ ...props
+}: React.ComponentProps<'span'>) {
+ return (
+
+
+ More
+
+ );
+}
+
+export {
+ Breadcrumb,
+ BreadcrumbList,
+ BreadcrumbItem,
+ BreadcrumbLink,
+ BreadcrumbPage,
+ BreadcrumbSeparator,
+ BreadcrumbEllipsis
+};
diff --git a/UIs/Customers/src/shared/components/ui/button-group.tsx b/UIs/Customers/src/shared/components/ui/button-group.tsx
new file mode 100644
index 0000000..68505c8
--- /dev/null
+++ b/UIs/Customers/src/shared/components/ui/button-group.tsx
@@ -0,0 +1,83 @@
+import { Slot } from '@radix-ui/react-slot';
+import { cva, type VariantProps } from 'class-variance-authority';
+
+import { cn } from '@/shared/utils';
+import { Separator } from '@/shared/components/ui/separator';
+
+const buttonGroupVariants = cva(
+ "flex w-fit items-stretch [&>*]:focus-visible:z-10 [&>*]:focus-visible:relative [&>[data-slot=select-trigger]:not([class*='w-'])]:w-fit [&>input]:flex-1 has-[select[aria-hidden=true]:last-child]:[&>[data-slot=select-trigger]:last-of-type]:rounded-r-md has-[>[data-slot=button-group]]:gap-2",
+ {
+ variants: {
+ orientation: {
+ horizontal:
+ '[&>*:not(:first-child)]:rounded-l-none [&>*:not(:first-child)]:border-l-0 [&>*:not(:last-child)]:rounded-r-none',
+ vertical:
+ 'flex-col [&>*:not(:first-child)]:rounded-t-none [&>*:not(:first-child)]:border-t-0 [&>*:not(:last-child)]:rounded-b-none'
+ }
+ },
+ defaultVariants: {
+ orientation: 'horizontal'
+ }
+ }
+);
+
+function ButtonGroup({
+ className,
+ orientation,
+ ...props
+}: React.ComponentProps<'div'> & VariantProps) {
+ return (
+
+ );
+}
+
+function ButtonGroupText({
+ className,
+ asChild = false,
+ ...props
+}: React.ComponentProps<'div'> & {
+ asChild?: boolean;
+}) {
+ const Comp = asChild ? Slot : 'div';
+
+ return (
+
+ );
+}
+
+function ButtonGroupSeparator({
+ className,
+ orientation = 'vertical',
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ );
+}
+
+export {
+ ButtonGroup,
+ ButtonGroupSeparator,
+ ButtonGroupText,
+ buttonGroupVariants
+};
diff --git a/UIs/Customers/src/shared/components/ui/button.tsx b/UIs/Customers/src/shared/components/ui/button.tsx
new file mode 100644
index 0000000..04e47de
--- /dev/null
+++ b/UIs/Customers/src/shared/components/ui/button.tsx
@@ -0,0 +1,60 @@
+import * as React from 'react';
+import { Slot } from '@radix-ui/react-slot';
+import { cva, type VariantProps } from 'class-variance-authority';
+
+import { cn } from '@/shared/utils';
+
+const buttonVariants = cva(
+ "inline-flex items-center cursor-pointer justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
+ {
+ variants: {
+ variant: {
+ default: 'bg-primary text-primary-foreground hover:bg-primary/90',
+ destructive:
+ 'bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60',
+ outline:
+ 'border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50',
+ secondary:
+ 'bg-secondary text-secondary-foreground hover:bg-secondary/80',
+ ghost:
+ 'hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50',
+ link: 'text-primary underline-offset-4 hover:underline'
+ },
+ size: {
+ default: 'h-9 px-4 py-2 has-[>svg]:px-3',
+ sm: 'h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5',
+ lg: 'h-10 rounded-md px-6 has-[>svg]:px-4',
+ icon: 'size-9',
+ 'icon-sm': 'size-8',
+ 'icon-lg': 'size-10'
+ }
+ },
+ defaultVariants: {
+ variant: 'default',
+ size: 'default'
+ }
+ }
+);
+
+function Button({
+ className,
+ variant,
+ size,
+ asChild = false,
+ ...props
+}: React.ComponentProps<'button'> &
+ VariantProps & {
+ asChild?: boolean;
+ }) {
+ const Comp = asChild ? Slot : 'button';
+
+ return (
+
+ );
+}
+
+export { Button, buttonVariants };
diff --git a/UIs/Customers/src/shared/components/ui/calendar.tsx b/UIs/Customers/src/shared/components/ui/calendar.tsx
new file mode 100644
index 0000000..0c7ff96
--- /dev/null
+++ b/UIs/Customers/src/shared/components/ui/calendar.tsx
@@ -0,0 +1,214 @@
+import * as React from 'react';
+import {
+ ChevronDownIcon,
+ ChevronLeftIcon,
+ ChevronRightIcon
+} from 'lucide-react';
+import { DayButton, DayPicker, getDefaultClassNames } from 'react-day-picker';
+
+import { cn } from '@/shared/utils';
+import { Button, buttonVariants } from '@/shared/components/ui/button';
+
+function Calendar({
+ className,
+ classNames,
+ showOutsideDays = true,
+ captionLayout = 'label',
+ buttonVariant = 'ghost',
+ formatters,
+ components,
+ ...props
+}: React.ComponentProps & {
+ buttonVariant?: React.ComponentProps['variant'];
+}) {
+ const defaultClassNames = getDefaultClassNames();
+
+ return (
+ svg]:rotate-180`,
+ String.raw`rtl:**:[.rdp-button\_previous>svg]:rotate-180`,
+ className
+ )}
+ captionLayout={captionLayout}
+ formatters={{
+ formatMonthDropdown: (date) =>
+ date.toLocaleString('default', { month: 'short' }),
+ ...formatters
+ }}
+ classNames={{
+ root: cn('w-fit', defaultClassNames.root),
+ months: cn(
+ 'flex gap-4 flex-col md:flex-row relative',
+ defaultClassNames.months
+ ),
+ month: cn('flex flex-col w-full gap-4', defaultClassNames.month),
+ nav: cn(
+ 'flex items-center gap-1 w-full absolute top-0 inset-x-0 justify-between',
+ defaultClassNames.nav
+ ),
+ button_previous: cn(
+ buttonVariants({ variant: buttonVariant }),
+ 'size-(--cell-size) aria-disabled:opacity-50 p-0 select-none',
+ defaultClassNames.button_previous
+ ),
+ button_next: cn(
+ buttonVariants({ variant: buttonVariant }),
+ 'size-(--cell-size) aria-disabled:opacity-50 p-0 select-none',
+ defaultClassNames.button_next
+ ),
+ month_caption: cn(
+ 'flex items-center justify-center h-(--cell-size) w-full px-(--cell-size)',
+ defaultClassNames.month_caption
+ ),
+ dropdowns: cn(
+ 'w-full flex items-center text-sm font-medium justify-center h-(--cell-size) gap-1.5',
+ defaultClassNames.dropdowns
+ ),
+ dropdown_root: cn(
+ 'relative has-focus:border-ring border border-input shadow-xs has-focus:ring-ring/50 has-focus:ring-[3px] rounded-md',
+ defaultClassNames.dropdown_root
+ ),
+ dropdown: cn(
+ 'absolute bg-popover inset-0 opacity-0',
+ defaultClassNames.dropdown
+ ),
+ caption_label: cn(
+ 'select-none font-medium',
+ captionLayout === 'label'
+ ? 'text-sm'
+ : 'rounded-md pl-2 pr-1 flex items-center gap-1 text-sm h-8 [&>svg]:text-muted-foreground [&>svg]:size-3.5',
+ defaultClassNames.caption_label
+ ),
+ table: 'w-full border-collapse',
+ weekdays: cn('flex', defaultClassNames.weekdays),
+ weekday: cn(
+ 'text-muted-foreground rounded-md flex-1 font-normal text-[0.8rem] select-none',
+ defaultClassNames.weekday
+ ),
+ week: cn('flex w-full mt-2', defaultClassNames.week),
+ week_number_header: cn(
+ 'select-none w-(--cell-size)',
+ defaultClassNames.week_number_header
+ ),
+ week_number: cn(
+ 'text-[0.8rem] select-none text-muted-foreground',
+ defaultClassNames.week_number
+ ),
+ day: cn(
+ 'relative w-full h-full p-0 text-center [&:last-child[data-selected=true]_button]:rounded-r-md group/day aspect-square select-none',
+ props.showWeekNumber
+ ? '[&:nth-child(2)[data-selected=true]_button]:rounded-l-md'
+ : '[&:first-child[data-selected=true]_button]:rounded-l-md',
+ defaultClassNames.day
+ ),
+ range_start: cn(
+ 'rounded-l-md bg-accent',
+ defaultClassNames.range_start
+ ),
+ range_middle: cn('rounded-none', defaultClassNames.range_middle),
+ range_end: cn('rounded-r-md bg-accent', defaultClassNames.range_end),
+ today: cn(
+ 'bg-accent text-accent-foreground rounded-md data-[selected=true]:rounded-none',
+ defaultClassNames.today
+ ),
+ outside: cn(
+ 'text-muted-foreground aria-selected:text-muted-foreground',
+ defaultClassNames.outside
+ ),
+ disabled: cn(
+ 'text-muted-foreground opacity-50',
+ defaultClassNames.disabled
+ ),
+ hidden: cn('invisible', defaultClassNames.hidden),
+ ...classNames
+ }}
+ components={{
+ Root: ({ className, rootRef, ...props }) => {
+ return (
+
+ );
+ },
+ Chevron: ({ className, orientation, ...props }) => {
+ if (orientation === 'left') {
+ return (
+
+ );
+ }
+
+ if (orientation === 'right') {
+ return (
+
+ );
+ }
+
+ return (
+
+ );
+ },
+ DayButton: CalendarDayButton,
+ WeekNumber: ({ children, ...props }) => {
+ return (
+
+
+ {children}
+
+ |
+ );
+ },
+ ...components
+ }}
+ {...props}
+ />
+ );
+}
+
+function CalendarDayButton({
+ className,
+ day,
+ modifiers,
+ ...props
+}: React.ComponentProps) {
+ const defaultClassNames = getDefaultClassNames();
+
+ const ref = React.useRef(null);
+ React.useEffect(() => {
+ if (modifiers.focused) ref.current?.focus();
+ }, [modifiers.focused]);
+
+ return (
+