diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..4a94448
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,29 @@
+# Dependencies
+node_modules
+npm-debug.log
+
+# Build output
+dist
+
+# IDE
+.idea
+.vscode
+*.swp
+*.swo
+
+# Environment
+.env
+.env.*
+!.env.example
+
+# Git
+.git
+.gitignore
+
+# Docker
+Dockerfile
+docker-compose*.yml
+.dockerignore
+
+# Documentation
+README.md
diff --git a/.env b/.env
new file mode 100644
index 0000000..2bd547a
--- /dev/null
+++ b/.env
@@ -0,0 +1,9 @@
+# MiChangarrito Frontend - Environment Variables
+
+# API Configuration
+VITE_API_URL=http://localhost:3141
+VITE_API_PREFIX=/api/v1
+
+# App Configuration
+VITE_APP_NAME=MiChangarrito
+VITE_APP_VERSION=1.0.0
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..a547bf3
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,24 @@
+# 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?
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..81a2b1f
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,63 @@
+# =============================================================================
+# MiChangarrito - Frontend Dockerfile
+# =============================================================================
+# Multi-stage build for React + Vite application
+# Served by nginx
+# Puerto: 80 (interno) -> 3140 (host)
+# =============================================================================
+
+# Build stage
+FROM node:20-alpine AS builder
+
+# Build argument for API URL
+ARG VITE_API_URL=http://localhost:3141/api/v1
+
+WORKDIR /app
+
+# Copy package files
+COPY package*.json ./
+
+# Install dependencies
+RUN npm ci
+
+# Copy source code
+COPY . .
+
+# Set environment variable for build
+ENV VITE_API_URL=$VITE_API_URL
+
+# Build application
+RUN npm run build
+
+# Production stage with nginx
+FROM nginx:alpine AS production
+
+# Labels
+LABEL maintainer="ISEM"
+LABEL description="MiChangarrito Frontend Web"
+LABEL version="1.0.0"
+
+# Copy custom nginx config
+COPY nginx.conf /etc/nginx/conf.d/default.conf
+
+# Copy built assets from builder
+COPY --from=builder /app/dist /usr/share/nginx/html
+
+# Create non-root user
+RUN addgroup -g 1001 -S nginx-user && \
+ adduser -S nginx-user -u 1001 -G nginx-user && \
+ chown -R nginx-user:nginx-user /usr/share/nginx/html && \
+ chown -R nginx-user:nginx-user /var/cache/nginx && \
+ chown -R nginx-user:nginx-user /var/log/nginx && \
+ touch /var/run/nginx.pid && \
+ chown -R nginx-user:nginx-user /var/run/nginx.pid
+
+# Expose port
+EXPOSE 80
+
+# Health check
+HEALTHCHECK --interval=30s --timeout=10s --retries=3 \
+ CMD wget --no-verbose --tries=1 --spider http://localhost:80 || exit 1
+
+# Start nginx
+CMD ["nginx", "-g", "daemon off;"]
diff --git a/README.md b/README.md
index bb44adf..d2e7761 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,73 @@
-# michangarrito-frontend-v2
+# React + TypeScript + Vite
-Frontend de michangarrito - Workspace V2
\ No newline at end of file
+This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
+
+Currently, two official plugins are available:
+
+- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) (or [oxc](https://oxc.rs) when used in [rolldown-vite](https://vite.dev/guide/rolldown)) for Fast Refresh
+- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
+
+## React Compiler
+
+The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation).
+
+## Expanding the ESLint configuration
+
+If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:
+
+```js
+export default defineConfig([
+ globalIgnores(['dist']),
+ {
+ files: ['**/*.{ts,tsx}'],
+ extends: [
+ // Other configs...
+
+ // Remove tseslint.configs.recommended and replace with this
+ tseslint.configs.recommendedTypeChecked,
+ // Alternatively, use this for stricter rules
+ tseslint.configs.strictTypeChecked,
+ // Optionally, add this for stylistic rules
+ tseslint.configs.stylisticTypeChecked,
+
+ // Other configs...
+ ],
+ languageOptions: {
+ parserOptions: {
+ project: ['./tsconfig.node.json', './tsconfig.app.json'],
+ tsconfigRootDir: import.meta.dirname,
+ },
+ // other options...
+ },
+ },
+])
+```
+
+You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules:
+
+```js
+// eslint.config.js
+import reactX from 'eslint-plugin-react-x'
+import reactDom from 'eslint-plugin-react-dom'
+
+export default defineConfig([
+ globalIgnores(['dist']),
+ {
+ files: ['**/*.{ts,tsx}'],
+ extends: [
+ // Other configs...
+ // Enable lint rules for React
+ reactX.configs['recommended-typescript'],
+ // Enable lint rules for React DOM
+ reactDom.configs.recommended,
+ ],
+ languageOptions: {
+ parserOptions: {
+ project: ['./tsconfig.node.json', './tsconfig.app.json'],
+ tsconfigRootDir: import.meta.dirname,
+ },
+ // other options...
+ },
+ },
+])
+```
diff --git a/e2e/auth.spec.ts b/e2e/auth.spec.ts
new file mode 100644
index 0000000..0c7f85c
--- /dev/null
+++ b/e2e/auth.spec.ts
@@ -0,0 +1,144 @@
+import { test, expect } from '@playwright/test';
+
+/**
+ * E2E Tests - Autenticacion
+ * MiChangarrito Frontend
+ */
+
+test.describe('Autenticacion', () => {
+ test.beforeEach(async ({ page }) => {
+ await page.goto('/login');
+ });
+
+ test('debe mostrar la pagina de login', async ({ page }) => {
+ // Verificar titulo
+ await expect(page.locator('h1')).toContainText('MiChangarrito');
+ await expect(page.locator('h2')).toContainText('Iniciar sesion');
+
+ // Verificar campos del formulario
+ await expect(page.locator('input[name="phone"]')).toBeVisible();
+ await expect(page.locator('input[name="pin"]')).toBeVisible();
+ await expect(page.locator('button[type="submit"]')).toBeVisible();
+
+ // Verificar link a registro
+ await expect(page.locator('a[href="/register"]')).toBeVisible();
+ });
+
+ test('debe validar campos requeridos', async ({ page }) => {
+ // Intentar enviar formulario vacio
+ await page.locator('button[type="submit"]').click();
+
+ // Los campos tienen required, el navegador debe validar
+ const phoneInput = page.locator('input[name="phone"]');
+ await expect(phoneInput).toHaveAttribute('required', '');
+ });
+
+ test('debe aceptar solo numeros en telefono', async ({ page }) => {
+ const phoneInput = page.locator('input[name="phone"]');
+
+ // Escribir texto con letras
+ await phoneInput.fill('abc123def456');
+
+ // Debe filtrar solo numeros
+ await expect(phoneInput).toHaveValue('123456');
+ });
+
+ test('debe limitar telefono a 10 digitos', async ({ page }) => {
+ const phoneInput = page.locator('input[name="phone"]');
+
+ await phoneInput.fill('12345678901234');
+
+ // maxLength=10
+ await expect(phoneInput).toHaveAttribute('maxLength', '10');
+ });
+
+ test('debe aceptar solo numeros en PIN', async ({ page }) => {
+ const pinInput = page.locator('input[name="pin"]');
+
+ await pinInput.fill('abc1234');
+
+ // Debe filtrar solo numeros
+ await expect(pinInput).toHaveValue('1234');
+ });
+
+ test('debe mostrar error con credenciales invalidas', async ({ page }) => {
+ // Llenar formulario con datos invalidos
+ await page.locator('input[name="phone"]').fill('5500000000');
+ await page.locator('input[name="pin"]').fill('0000');
+
+ // Enviar
+ await page.locator('button[type="submit"]').click();
+
+ // Esperar respuesta del servidor
+ await page.waitForTimeout(1000);
+
+ // Debe mostrar mensaje de error (si el backend esta corriendo)
+ // Si no hay backend, verificamos que el boton vuelve a estar habilitado
+ const submitButton = page.locator('button[type="submit"]');
+ await expect(submitButton).not.toBeDisabled();
+ });
+
+ test('debe mostrar estado de carga al enviar', async ({ page }) => {
+ // Llenar formulario
+ await page.locator('input[name="phone"]').fill('5512345678');
+ await page.locator('input[name="pin"]').fill('1234');
+
+ // Enviar y verificar estado de carga
+ const submitButton = page.locator('button[type="submit"]');
+ await submitButton.click();
+
+ // Verificar que muestra "Ingresando..." mientras carga
+ // Nota: Esto es rapido, puede no capturarse siempre
+ await expect(submitButton).toContainText(/Ingresar|Ingresando/);
+ });
+
+ test('debe navegar a registro', async ({ page }) => {
+ await page.locator('a[href="/register"]').click();
+
+ await expect(page).toHaveURL('/register');
+ });
+
+ test('debe redirigir a login si no esta autenticado', async ({ page }) => {
+ // Intentar acceder a ruta protegida
+ await page.goto('/dashboard');
+
+ // Debe redirigir a login
+ await expect(page).toHaveURL('/login');
+ });
+
+ test('debe redirigir a login desde rutas protegidas', async ({ page }) => {
+ const protectedRoutes = [
+ '/dashboard',
+ '/products',
+ '/orders',
+ '/customers',
+ '/fiado',
+ '/inventory',
+ '/settings',
+ ];
+
+ for (const route of protectedRoutes) {
+ await page.goto(route);
+ await expect(page).toHaveURL('/login');
+ }
+ });
+});
+
+test.describe('Registro', () => {
+ test.beforeEach(async ({ page }) => {
+ await page.goto('/register');
+ });
+
+ test('debe mostrar la pagina de registro', async ({ page }) => {
+ await expect(page.locator('h1')).toContainText('MiChangarrito');
+
+ // Verificar link a login
+ await expect(page.locator('a[href="/login"]')).toBeVisible();
+ });
+
+ test('debe navegar a login desde registro', async ({ page }) => {
+ await page.locator('a[href="/login"]').click();
+
+ await expect(page).toHaveURL('/login');
+ });
+});
diff --git a/e2e/fixtures/test-data.ts b/e2e/fixtures/test-data.ts
new file mode 100644
index 0000000..dd5aad6
--- /dev/null
+++ b/e2e/fixtures/test-data.ts
@@ -0,0 +1,48 @@
+/**
+ * Test Data Fixtures - MiChangarrito E2E Tests
+ */
+
+export const TEST_USER = {
+ phone: '5512345678',
+ pin: '1234',
+ name: 'Usuario Test',
+ businessName: 'Tienda Test',
+};
+
+export const TEST_PRODUCT = {
+ name: 'Coca-Cola 600ml',
+ sku: 'CC600',
+ barcode: '7501055300000',
+ price: 18.00,
+ cost: 12.00,
+ stock: 50,
+ category: 'Bebidas',
+};
+
+export const TEST_CUSTOMER = {
+ name: 'Juan Perez',
+ phone: '5598765432',
+ email: 'juan@test.com',
+ creditLimit: 500,
+};
+
+export const TEST_ORDER = {
+ items: [
+ { productId: '1', quantity: 2, price: 18.00 },
+ { productId: '2', quantity: 1, price: 25.00 },
+ ],
+ total: 61.00,
+ paymentMethod: 'cash',
+};
+
+export const ROUTES = {
+ login: '/login',
+ register: '/register',
+ dashboard: '/dashboard',
+ products: '/products',
+ orders: '/orders',
+ customers: '/customers',
+ fiado: '/fiado',
+ inventory: '/inventory',
+ settings: '/settings',
+};
diff --git a/e2e/navigation.spec.ts b/e2e/navigation.spec.ts
new file mode 100644
index 0000000..eca032b
--- /dev/null
+++ b/e2e/navigation.spec.ts
@@ -0,0 +1,120 @@
+import { test, expect } from '@playwright/test';
+
+/**
+ * E2E Tests - Navegacion
+ * MiChangarrito Frontend
+ *
+ * Nota: Estos tests requieren autenticacion
+ * Se usan con un usuario de prueba pre-configurado
+ */
+
+test.describe('Navegacion Publica', () => {
+ test('debe cargar la aplicacion', async ({ page }) => {
+ await page.goto('/');
+
+ // Al no estar autenticado, debe redirigir a login
+ await expect(page).toHaveURL('/login');
+ });
+
+ test('rutas invalidas redirigen a login', async ({ page }) => {
+ await page.goto('/ruta-que-no-existe');
+
+ await expect(page).toHaveURL('/login');
+ });
+
+ test('login page tiene titulo correcto', async ({ page }) => {
+ await page.goto('/login');
+
+ await expect(page).toHaveTitle(/MiChangarrito/i);
+ });
+});
+
+test.describe('UI Responsiva', () => {
+ test('login se adapta a mobile', async ({ page }) => {
+ // Viewport mobile
+ await page.setViewportSize({ width: 375, height: 667 });
+ await page.goto('/login');
+
+ // El formulario debe ser visible y usable
+ await expect(page.locator('form')).toBeVisible();
+ await expect(page.locator('input[name="phone"]')).toBeVisible();
+ await expect(page.locator('input[name="pin"]')).toBeVisible();
+ await expect(page.locator('button[type="submit"]')).toBeVisible();
+ });
+
+ test('login se adapta a tablet', async ({ page }) => {
+ // Viewport tablet
+ await page.setViewportSize({ width: 768, height: 1024 });
+ await page.goto('/login');
+
+ await expect(page.locator('form')).toBeVisible();
+ });
+
+ test('login se adapta a desktop', async ({ page }) => {
+ // Viewport desktop
+ await page.setViewportSize({ width: 1920, height: 1080 });
+ await page.goto('/login');
+
+ await expect(page.locator('form')).toBeVisible();
+ });
+});
+
+test.describe('Accesibilidad', () => {
+ test('campos tienen labels', async ({ page }) => {
+ await page.goto('/login');
+
+ // Verificar que los inputs tienen labels asociados
+ const phoneLabel = page.locator('label[for="phone"]');
+ const pinLabel = page.locator('label[for="pin"]');
+
+ await expect(phoneLabel).toBeVisible();
+ await expect(pinLabel).toBeVisible();
+ });
+
+ test('formulario es navegable con teclado', async ({ page }) => {
+ await page.goto('/login');
+
+ // Tab al primer campo
+ await page.keyboard.press('Tab');
+ const phoneInput = page.locator('input[name="phone"]');
+ await expect(phoneInput).toBeFocused();
+
+ // Tab al segundo campo
+ await page.keyboard.press('Tab');
+ const pinInput = page.locator('input[name="pin"]');
+ await expect(pinInput).toBeFocused();
+
+ // Tab al boton
+ await page.keyboard.press('Tab');
+ const submitButton = page.locator('button[type="submit"]');
+ await expect(submitButton).toBeFocused();
+ });
+
+ test('formulario puede enviarse con Enter', async ({ page }) => {
+ await page.goto('/login');
+
+ // Llenar campos
+ await page.locator('input[name="phone"]').fill('5512345678');
+ await page.locator('input[name="pin"]').fill('1234');
+
+ // Enviar con Enter
+ await page.keyboard.press('Enter');
+
+ // El formulario debe intentar enviarse (el boton estara en estado loading)
+ const submitButton = page.locator('button[type="submit"]');
+ // Puede estar disabled durante el envio o ya haber terminado
+ await page.waitForTimeout(500);
+ });
+});
+
+test.describe('Performance', () => {
+ test('login carga en menos de 3 segundos', async ({ page }) => {
+ const startTime = Date.now();
+
+ await page.goto('/login');
+ await expect(page.locator('h1')).toBeVisible();
+
+ const loadTime = Date.now() - startTime;
+ expect(loadTime).toBeLessThan(3000);
+ });
+});
diff --git a/e2e/orders.spec.ts b/e2e/orders.spec.ts
new file mode 100644
index 0000000..7d36151
--- /dev/null
+++ b/e2e/orders.spec.ts
@@ -0,0 +1,149 @@
+import { test, expect } from '@playwright/test';
+import { TEST_ORDER, TEST_CUSTOMER, ROUTES } from './fixtures/test-data';
+
+/**
+ * E2E Tests - Pedidos y Fiado
+ * MiChangarrito Frontend
+ *
+ * Nota: Estos tests requieren autenticacion previa
+ * y un backend corriendo con datos de prueba
+ */
+
+test.describe('Pedidos - Sin Autenticacion', () => {
+ test('redirige a login si no esta autenticado', async ({ page }) => {
+ await page.goto(ROUTES.orders);
+ await expect(page).toHaveURL('/login');
+ });
+
+ test('fiado redirige a login sin autenticacion', async ({ page }) => {
+ await page.goto(ROUTES.fiado);
+ await expect(page).toHaveURL('/login');
+ });
+
+ test('clientes redirige a login sin autenticacion', async ({ page }) => {
+ await page.goto(ROUTES.customers);
+ await expect(page).toHaveURL('/login');
+ });
+});
+
+test.describe('Pedidos - Estructura de Datos', () => {
+ test('datos de pedido de prueba tienen estructura correcta', () => {
+ expect(TEST_ORDER.items).toBeInstanceOf(Array);
+ expect(TEST_ORDER.items.length).toBeGreaterThan(0);
+ expect(TEST_ORDER.total).toBeGreaterThan(0);
+ expect(TEST_ORDER.paymentMethod).toBeDefined();
+
+ // Verificar estructura de items
+ const item = TEST_ORDER.items[0];
+ expect(item.productId).toBeDefined();
+ expect(item.quantity).toBeGreaterThan(0);
+ expect(item.price).toBeGreaterThan(0);
+ });
+
+ test('datos de cliente de prueba tienen estructura correcta', () => {
+ expect(TEST_CUSTOMER.name).toBeDefined();
+ expect(TEST_CUSTOMER.phone).toBeDefined();
+ expect(TEST_CUSTOMER.phone.length).toBe(10);
+ expect(TEST_CUSTOMER.creditLimit).toBeGreaterThanOrEqual(0);
+ });
+
+ test('total del pedido es correcto', () => {
+ // Verificar calculo: 2 * 18.00 + 1 * 25.00 = 61.00
+ const calculatedTotal = TEST_ORDER.items.reduce(
+ (sum, item) => sum + item.quantity * item.price,
+ 0
+ );
+ expect(calculatedTotal).toBe(TEST_ORDER.total);
+ });
+});
+
+test.describe('Sistema de Fiado', () => {
+ test('rutas de fiado protegidas', async ({ page }) => {
+ const fiadoRoutes = [
+ '/fiado',
+ '/customers',
+ ];
+
+ for (const route of fiadoRoutes) {
+ await page.goto(route);
+ await expect(page).toHaveURL('/login');
+ }
+ });
+});
+
+test.describe('Historial de Pedidos', () => {
+ test('ruta de pedidos protegida', async ({ page }) => {
+ await page.goto('/orders');
+ await expect(page).toHaveURL('/login');
+ });
+
+ test('ruta de dashboard protegida', async ({ page }) => {
+ await page.goto(ROUTES.dashboard);
+ await expect(page).toHaveURL('/login');
+ });
+});
+
+test.describe('Clientes', () => {
+ test('ruta de clientes protegida', async ({ page }) => {
+ await page.goto('/customers');
+ await expect(page).toHaveURL('/login');
+ });
+
+ test('validacion de limite de credito', () => {
+ // El limite de credito debe ser un numero positivo o cero
+ expect(TEST_CUSTOMER.creditLimit).toBeGreaterThanOrEqual(0);
+ expect(typeof TEST_CUSTOMER.creditLimit).toBe('number');
+ });
+});
+
+test.describe('Metodos de Pago', () => {
+ test('metodo de pago valido', () => {
+ const validPaymentMethods = ['cash', 'card', 'transfer', 'fiado'];
+ expect(validPaymentMethods).toContain(TEST_ORDER.paymentMethod);
+ });
+});
+
+test.describe('Configuracion', () => {
+ test('ruta de configuracion protegida', async ({ page }) => {
+ await page.goto(ROUTES.settings);
+ await expect(page).toHaveURL('/login');
+ });
+});
+
+test.describe('Rutas Completas', () => {
+ test('todas las rutas definidas en fixtures existen', () => {
+ const expectedRoutes = [
+ 'login',
+ 'register',
+ 'dashboard',
+ 'products',
+ 'orders',
+ 'customers',
+ 'fiado',
+ 'inventory',
+ 'settings',
+ ];
+
+ for (const route of expectedRoutes) {
+ expect(ROUTES[route as keyof typeof ROUTES]).toBeDefined();
+ expect(ROUTES[route as keyof typeof ROUTES]).toMatch(/^\//);
+ }
+ });
+
+ test('todas las rutas protegidas redirigen a login', async ({ page }) => {
+ const protectedRoutes = [
+ ROUTES.dashboard,
+ ROUTES.products,
+ ROUTES.orders,
+ ROUTES.customers,
+ ROUTES.fiado,
+ ROUTES.inventory,
+ ROUTES.settings,
+ ];
+
+ for (const route of protectedRoutes) {
+ await page.goto(route);
+ await expect(page).toHaveURL('/login');
+ }
+ });
+});
diff --git a/e2e/pos.spec.ts b/e2e/pos.spec.ts
new file mode 100644
index 0000000..462fd5e
--- /dev/null
+++ b/e2e/pos.spec.ts
@@ -0,0 +1,103 @@
+import { test, expect } from '@playwright/test';
+import { TEST_PRODUCT, ROUTES } from './fixtures/test-data';
+
+/**
+ * E2E Tests - Punto de Venta (POS)
+ * MiChangarrito Frontend
+ *
+ * Nota: Estos tests requieren autenticacion previa
+ * y un backend corriendo con datos de prueba
+ */
+
+test.describe('Punto de Venta - Sin Autenticacion', () => {
+ test('redirige a login si no esta autenticado', async ({ page }) => {
+ await page.goto('/pos');
+ await expect(page).toHaveURL('/login');
+ });
+});
+
+test.describe('Punto de Venta - UI Basica', () => {
+ // Estos tests verifican la estructura esperada del POS
+ // cuando el usuario esta autenticado
+
+ test.beforeEach(async ({ page }) => {
+ // Mock de autenticacion para tests
+ // En un escenario real, se usaria un fixture de login
+ await page.goto('/login');
+ });
+
+ test('pagina de productos existe', async ({ page }) => {
+ await page.goto(ROUTES.products);
+ // Sin auth, redirige a login
+ await expect(page).toHaveURL('/login');
+ });
+});
+
+test.describe('Punto de Venta - Flujo de Venta', () => {
+ // Tests de flujo completo de venta
+ // Requieren setup de autenticacion
+
+ test('estructura del formulario de busqueda', async ({ page }) => {
+ await page.goto('/login');
+
+ // Verificar que la pagina de login tiene estructura correcta
+ // antes de poder probar el POS
+ await expect(page.locator('form')).toBeVisible();
+ });
+});
+
+test.describe('Carrito de Compras', () => {
+ test.beforeEach(async ({ page }) => {
+ await page.goto('/login');
+ });
+
+ test('login tiene campos necesarios para acceder al POS', async ({ page }) => {
+ // Verificar campos necesarios
+ await expect(page.locator('input[name="phone"]')).toBeVisible();
+ await expect(page.locator('input[name="pin"]')).toBeVisible();
+ await expect(page.locator('button[type="submit"]')).toBeVisible();
+ });
+});
+
+test.describe('Busqueda de Productos', () => {
+ test('rutas de productos protegidas', async ({ page }) => {
+ // Verificar que todas las rutas relacionadas al POS estan protegidas
+ const posRoutes = [
+ '/products',
+ '/pos',
+ '/inventory',
+ ];
+
+ for (const route of posRoutes) {
+ await page.goto(route);
+ await expect(page).toHaveURL('/login');
+ }
+ });
+});
+
+test.describe('Metodos de Pago', () => {
+ test('verificar existencia de ruta de pedidos', async ({ page }) => {
+ await page.goto(ROUTES.orders);
+ // Sin auth, redirige
+ await expect(page).toHaveURL('/login');
+ });
+});
+
+test.describe('Recibos y Tickets', () => {
+ test('estructura de datos de producto de prueba', () => {
+ // Verificar que los datos de prueba tienen la estructura correcta
+ expect(TEST_PRODUCT.name).toBeDefined();
+ expect(TEST_PRODUCT.price).toBeGreaterThan(0);
+ expect(TEST_PRODUCT.cost).toBeGreaterThan(0);
+ expect(TEST_PRODUCT.stock).toBeGreaterThanOrEqual(0);
+ expect(TEST_PRODUCT.sku).toBeDefined();
+ expect(TEST_PRODUCT.barcode).toBeDefined();
+ });
+});
+
+test.describe('Inventario desde POS', () => {
+ test('ruta de inventario protegida', async ({ page }) => {
+ await page.goto(ROUTES.inventory);
+ await expect(page).toHaveURL('/login');
+ });
+});
diff --git a/eslint.config.js b/eslint.config.js
new file mode 100644
index 0000000..5e6b472
--- /dev/null
+++ b/eslint.config.js
@@ -0,0 +1,23 @@
+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 { defineConfig, globalIgnores } from 'eslint/config'
+
+export default defineConfig([
+ globalIgnores(['dist']),
+ {
+ files: ['**/*.{ts,tsx}'],
+ extends: [
+ js.configs.recommended,
+ tseslint.configs.recommended,
+ reactHooks.configs.flat.recommended,
+ reactRefresh.configs.vite,
+ ],
+ languageOptions: {
+ ecmaVersion: 2020,
+ globals: globals.browser,
+ },
+ },
+])
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..072a57e
--- /dev/null
+++ b/index.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+ frontend
+
+
+
+
+
+
diff --git a/nginx.conf b/nginx.conf
new file mode 100644
index 0000000..683c656
--- /dev/null
+++ b/nginx.conf
@@ -0,0 +1,42 @@
+server {
+ listen 80;
+ server_name localhost;
+ root /usr/share/nginx/html;
+ index index.html;
+
+ # Gzip compression
+ gzip on;
+ gzip_vary on;
+ gzip_min_length 1024;
+ gzip_proxied expired no-cache no-store private auth;
+ gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml application/javascript;
+
+ # Security headers
+ add_header X-Frame-Options "SAMEORIGIN" always;
+ add_header X-Content-Type-Options "nosniff" always;
+ add_header X-XSS-Protection "1; mode=block" always;
+ add_header Referrer-Policy "strict-origin-when-cross-origin" always;
+
+ # Cache static assets
+ location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
+ expires 1y;
+ add_header Cache-Control "public, immutable";
+ }
+
+ # SPA routing - serve index.html for all routes
+ location / {
+ try_files $uri $uri/ /index.html;
+ }
+
+ # Health check endpoint
+ location /health {
+ access_log off;
+ return 200 "healthy\n";
+ add_header Content-Type text/plain;
+ }
+
+ # Deny access to hidden files
+ location ~ /\. {
+ deny all;
+ }
+}
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000..3a41f7d
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,4317 @@
+{
+ "name": "frontend",
+ "version": "0.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "frontend",
+ "version": "0.0.0",
+ "dependencies": {
+ "@tanstack/react-query": "^5.90.16",
+ "axios": "^1.13.2",
+ "clsx": "^2.1.1",
+ "lucide-react": "^0.562.0",
+ "react": "^19.2.0",
+ "react-dom": "^19.2.0",
+ "react-router-dom": "^7.11.0"
+ },
+ "devDependencies": {
+ "@eslint/js": "^9.39.1",
+ "@tailwindcss/forms": "^0.5.11",
+ "@tailwindcss/postcss": "^4.1.18",
+ "@types/node": "^24.10.1",
+ "@types/react": "^19.2.5",
+ "@types/react-dom": "^19.2.3",
+ "@vitejs/plugin-react": "^5.1.1",
+ "autoprefixer": "^10.4.23",
+ "eslint": "^9.39.1",
+ "eslint-plugin-react-hooks": "^7.0.1",
+ "eslint-plugin-react-refresh": "^0.4.24",
+ "globals": "^16.5.0",
+ "postcss": "^8.5.6",
+ "tailwindcss": "^4.1.18",
+ "typescript": "~5.9.3",
+ "typescript-eslint": "^8.46.4",
+ "vite": "^7.2.4"
+ }
+ },
+ "node_modules/@alloc/quick-lru": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz",
+ "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@babel/code-frame": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
+ "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-validator-identifier": "^7.27.1",
+ "js-tokens": "^4.0.0",
+ "picocolors": "^1.1.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/compat-data": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz",
+ "integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/core": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz",
+ "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/code-frame": "^7.27.1",
+ "@babel/generator": "^7.28.5",
+ "@babel/helper-compilation-targets": "^7.27.2",
+ "@babel/helper-module-transforms": "^7.28.3",
+ "@babel/helpers": "^7.28.4",
+ "@babel/parser": "^7.28.5",
+ "@babel/template": "^7.27.2",
+ "@babel/traverse": "^7.28.5",
+ "@babel/types": "^7.28.5",
+ "@jridgewell/remapping": "^2.3.5",
+ "convert-source-map": "^2.0.0",
+ "debug": "^4.1.0",
+ "gensync": "^1.0.0-beta.2",
+ "json5": "^2.2.3",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/babel"
+ }
+ },
+ "node_modules/@babel/generator": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz",
+ "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.28.5",
+ "@babel/types": "^7.28.5",
+ "@jridgewell/gen-mapping": "^0.3.12",
+ "@jridgewell/trace-mapping": "^0.3.28",
+ "jsesc": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-compilation-targets": {
+ "version": "7.27.2",
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz",
+ "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/compat-data": "^7.27.2",
+ "@babel/helper-validator-option": "^7.27.1",
+ "browserslist": "^4.24.0",
+ "lru-cache": "^5.1.1",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-globals": {
+ "version": "7.28.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz",
+ "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-imports": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz",
+ "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/traverse": "^7.27.1",
+ "@babel/types": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-transforms": {
+ "version": "7.28.3",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz",
+ "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.27.1",
+ "@babel/helper-validator-identifier": "^7.27.1",
+ "@babel/traverse": "^7.28.3"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-plugin-utils": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz",
+ "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-string-parser": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
+ "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
+ "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-option": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz",
+ "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helpers": {
+ "version": "7.28.4",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz",
+ "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/template": "^7.27.2",
+ "@babel/types": "^7.28.4"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/parser": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz",
+ "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.28.5"
+ },
+ "bin": {
+ "parser": "bin/babel-parser.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-react-jsx-self": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz",
+ "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-react-jsx-source": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz",
+ "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/template": {
+ "version": "7.27.2",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz",
+ "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.27.1",
+ "@babel/parser": "^7.27.2",
+ "@babel/types": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/traverse": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz",
+ "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.27.1",
+ "@babel/generator": "^7.28.5",
+ "@babel/helper-globals": "^7.28.0",
+ "@babel/parser": "^7.28.5",
+ "@babel/template": "^7.27.2",
+ "@babel/types": "^7.28.5",
+ "debug": "^4.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/types": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz",
+ "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-string-parser": "^7.27.1",
+ "@babel/helper-validator-identifier": "^7.28.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@esbuild/aix-ppc64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz",
+ "integrity": "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.2.tgz",
+ "integrity": "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.2.tgz",
+ "integrity": "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-x64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.2.tgz",
+ "integrity": "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-arm64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.2.tgz",
+ "integrity": "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-x64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.2.tgz",
+ "integrity": "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.2.tgz",
+ "integrity": "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-x64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.2.tgz",
+ "integrity": "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.2.tgz",
+ "integrity": "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.2.tgz",
+ "integrity": "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ia32": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.2.tgz",
+ "integrity": "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.2.tgz",
+ "integrity": "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-mips64el": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.2.tgz",
+ "integrity": "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ppc64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.2.tgz",
+ "integrity": "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-riscv64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.2.tgz",
+ "integrity": "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-s390x": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.2.tgz",
+ "integrity": "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-x64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.2.tgz",
+ "integrity": "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-arm64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.2.tgz",
+ "integrity": "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-x64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.2.tgz",
+ "integrity": "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.2.tgz",
+ "integrity": "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-x64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.2.tgz",
+ "integrity": "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openharmony-arm64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.2.tgz",
+ "integrity": "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/sunos-x64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.2.tgz",
+ "integrity": "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-arm64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.2.tgz",
+ "integrity": "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-ia32": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.2.tgz",
+ "integrity": "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-x64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.2.tgz",
+ "integrity": "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils": {
+ "version": "4.9.1",
+ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz",
+ "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eslint-visitor-keys": "^3.4.3"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint-community/regexpp": {
+ "version": "4.12.2",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz",
+ "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@eslint/config-array": {
+ "version": "0.21.1",
+ "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz",
+ "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/object-schema": "^2.1.7",
+ "debug": "^4.3.1",
+ "minimatch": "^3.1.2"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/config-helpers": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz",
+ "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/core": "^0.17.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/core": {
+ "version": "0.17.0",
+ "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz",
+ "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@types/json-schema": "^7.0.15"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/eslintrc": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.3.tgz",
+ "integrity": "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ajv": "^6.12.4",
+ "debug": "^4.3.2",
+ "espree": "^10.0.1",
+ "globals": "^14.0.0",
+ "ignore": "^5.2.0",
+ "import-fresh": "^3.2.1",
+ "js-yaml": "^4.1.1",
+ "minimatch": "^3.1.2",
+ "strip-json-comments": "^3.1.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint/eslintrc/node_modules/globals": {
+ "version": "14.0.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
+ "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@eslint/js": {
+ "version": "9.39.2",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.2.tgz",
+ "integrity": "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://eslint.org/donate"
+ }
+ },
+ "node_modules/@eslint/object-schema": {
+ "version": "2.1.7",
+ "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz",
+ "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/plugin-kit": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz",
+ "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/core": "^0.17.0",
+ "levn": "^0.4.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@humanfs/core": {
+ "version": "0.19.1",
+ "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
+ "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/@humanfs/node": {
+ "version": "0.16.7",
+ "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz",
+ "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@humanfs/core": "^0.19.1",
+ "@humanwhocodes/retry": "^0.4.0"
+ },
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/@humanwhocodes/module-importer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+ "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=12.22"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@humanwhocodes/retry": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz",
+ "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.13",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
+ "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.0",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/remapping": {
+ "version": "2.3.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
+ "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.5.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
+ "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.31",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
+ "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
+ }
+ },
+ "node_modules/@rolldown/pluginutils": {
+ "version": "1.0.0-beta.53",
+ "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.53.tgz",
+ "integrity": "sha512-vENRlFU4YbrwVqNDZ7fLvy+JR1CRkyr01jhSiDpE1u6py3OMzQfztQU2jxykW3ALNxO4kSlqIDeYyD0Y9RcQeQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@rollup/rollup-android-arm-eabi": {
+ "version": "4.54.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.54.0.tgz",
+ "integrity": "sha512-OywsdRHrFvCdvsewAInDKCNyR3laPA2mc9bRYJ6LBp5IyvF3fvXbbNR0bSzHlZVFtn6E0xw2oZlyjg4rKCVcng==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-android-arm64": {
+ "version": "4.54.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.54.0.tgz",
+ "integrity": "sha512-Skx39Uv+u7H224Af+bDgNinitlmHyQX1K/atIA32JP3JQw6hVODX5tkbi2zof/E69M1qH2UoN3Xdxgs90mmNYw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-arm64": {
+ "version": "4.54.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.54.0.tgz",
+ "integrity": "sha512-k43D4qta/+6Fq+nCDhhv9yP2HdeKeP56QrUUTW7E6PhZP1US6NDqpJj4MY0jBHlJivVJD5P8NxrjuobZBJTCRw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-x64": {
+ "version": "4.54.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.54.0.tgz",
+ "integrity": "sha512-cOo7biqwkpawslEfox5Vs8/qj83M/aZCSSNIWpVzfU2CYHa2G3P1UN5WF01RdTHSgCkri7XOlTdtk17BezlV3A==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-arm64": {
+ "version": "4.54.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.54.0.tgz",
+ "integrity": "sha512-miSvuFkmvFbgJ1BevMa4CPCFt5MPGw094knM64W9I0giUIMMmRYcGW/JWZDriaw/k1kOBtsWh1z6nIFV1vPNtA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-x64": {
+ "version": "4.54.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.54.0.tgz",
+ "integrity": "sha512-KGXIs55+b/ZfZsq9aR026tmr/+7tq6VG6MsnrvF4H8VhwflTIuYh+LFUlIsRdQSgrgmtM3fVATzEAj4hBQlaqQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
+ "version": "4.54.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.54.0.tgz",
+ "integrity": "sha512-EHMUcDwhtdRGlXZsGSIuXSYwD5kOT9NVnx9sqzYiwAc91wfYOE1g1djOEDseZJKKqtHAHGwnGPQu3kytmfaXLQ==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-musleabihf": {
+ "version": "4.54.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.54.0.tgz",
+ "integrity": "sha512-+pBrqEjaakN2ySv5RVrj/qLytYhPKEUwk+e3SFU5jTLHIcAtqh2rLrd/OkbNuHJpsBgxsD8ccJt5ga/SeG0JmA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-gnu": {
+ "version": "4.54.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.54.0.tgz",
+ "integrity": "sha512-NSqc7rE9wuUaRBsBp5ckQ5CVz5aIRKCwsoa6WMF7G01sX3/qHUw/z4pv+D+ahL1EIKy6Enpcnz1RY8pf7bjwng==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-musl": {
+ "version": "4.54.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.54.0.tgz",
+ "integrity": "sha512-gr5vDbg3Bakga5kbdpqx81m2n9IX8M6gIMlQQIXiLTNeQW6CucvuInJ91EuCJ/JYvc+rcLLsDFcfAD1K7fMofg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-loong64-gnu": {
+ "version": "4.54.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.54.0.tgz",
+ "integrity": "sha512-gsrtB1NA3ZYj2vq0Rzkylo9ylCtW/PhpLEivlgWe0bpgtX5+9j9EZa0wtZiCjgu6zmSeZWyI/e2YRX1URozpIw==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-ppc64-gnu": {
+ "version": "4.54.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.54.0.tgz",
+ "integrity": "sha512-y3qNOfTBStmFNq+t4s7Tmc9hW2ENtPg8FeUD/VShI7rKxNW7O4fFeaYbMsd3tpFlIg1Q8IapFgy7Q9i2BqeBvA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-gnu": {
+ "version": "4.54.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.54.0.tgz",
+ "integrity": "sha512-89sepv7h2lIVPsFma8iwmccN7Yjjtgz0Rj/Ou6fEqg3HDhpCa+Et+YSufy27i6b0Wav69Qv4WBNl3Rs6pwhebQ==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-musl": {
+ "version": "4.54.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.54.0.tgz",
+ "integrity": "sha512-ZcU77ieh0M2Q8Ur7D5X7KvK+UxbXeDHwiOt/CPSBTI1fBmeDMivW0dPkdqkT4rOgDjrDDBUed9x4EgraIKoR2A==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-s390x-gnu": {
+ "version": "4.54.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.54.0.tgz",
+ "integrity": "sha512-2AdWy5RdDF5+4YfG/YesGDDtbyJlC9LHmL6rZw6FurBJ5n4vFGupsOBGfwMRjBYH7qRQowT8D/U4LoSvVwOhSQ==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-gnu": {
+ "version": "4.54.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.54.0.tgz",
+ "integrity": "sha512-WGt5J8Ij/rvyqpFexxk3ffKqqbLf9AqrTBbWDk7ApGUzaIs6V+s2s84kAxklFwmMF/vBNGrVdYgbblCOFFezMQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-musl": {
+ "version": "4.54.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.54.0.tgz",
+ "integrity": "sha512-JzQmb38ATzHjxlPHuTH6tE7ojnMKM2kYNzt44LO/jJi8BpceEC8QuXYA908n8r3CNuG/B3BV8VR3Hi1rYtmPiw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-openharmony-arm64": {
+ "version": "4.54.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.54.0.tgz",
+ "integrity": "sha512-huT3fd0iC7jigGh7n3q/+lfPcXxBi+om/Rs3yiFxjvSxbSB6aohDFXbWvlspaqjeOh+hx7DDHS+5Es5qRkWkZg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-arm64-msvc": {
+ "version": "4.54.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.54.0.tgz",
+ "integrity": "sha512-c2V0W1bsKIKfbLMBu/WGBz6Yci8nJ/ZJdheE0EwB73N3MvHYKiKGs3mVilX4Gs70eGeDaMqEob25Tw2Gb9Nqyw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-ia32-msvc": {
+ "version": "4.54.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.54.0.tgz",
+ "integrity": "sha512-woEHgqQqDCkAzrDhvDipnSirm5vxUXtSKDYTVpZG3nUdW/VVB5VdCYA2iReSj/u3yCZzXID4kuKG7OynPnB3WQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-gnu": {
+ "version": "4.54.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.54.0.tgz",
+ "integrity": "sha512-dzAc53LOuFvHwbCEOS0rPbXp6SIhAf2txMP5p6mGyOXXw5mWY8NGGbPMPrs4P1WItkfApDathBj/NzMLUZ9rtQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-msvc": {
+ "version": "4.54.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.54.0.tgz",
+ "integrity": "sha512-hYT5d3YNdSh3mbCU1gwQyPgQd3T2ne0A3KG8KSBdav5TiBg6eInVmV+TeR5uHufiIgSFg0XsOWGW5/RhNcSvPg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@tailwindcss/forms": {
+ "version": "0.5.11",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.11.tgz",
+ "integrity": "sha512-h9wegbZDPurxG22xZSoWtdzc41/OlNEUQERNqI/0fOwa2aVlWGu7C35E/x6LDyD3lgtztFSSjKZyuVM0hxhbgA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mini-svg-data-uri": "^1.2.3"
+ },
+ "peerDependencies": {
+ "tailwindcss": ">=3.0.0 || >= 3.0.0-alpha.1 || >= 4.0.0-alpha.20 || >= 4.0.0-beta.1"
+ }
+ },
+ "node_modules/@tailwindcss/node": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.18.tgz",
+ "integrity": "sha512-DoR7U1P7iYhw16qJ49fgXUlry1t4CpXeErJHnQ44JgTSKMaZUdf17cfn5mHchfJ4KRBZRFA/Coo+MUF5+gOaCQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/remapping": "^2.3.4",
+ "enhanced-resolve": "^5.18.3",
+ "jiti": "^2.6.1",
+ "lightningcss": "1.30.2",
+ "magic-string": "^0.30.21",
+ "source-map-js": "^1.2.1",
+ "tailwindcss": "4.1.18"
+ }
+ },
+ "node_modules/@tailwindcss/oxide": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.18.tgz",
+ "integrity": "sha512-EgCR5tTS5bUSKQgzeMClT6iCY3ToqE1y+ZB0AKldj809QXk1Y+3jB0upOYZrn9aGIzPtUsP7sX4QQ4XtjBB95A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 10"
+ },
+ "optionalDependencies": {
+ "@tailwindcss/oxide-android-arm64": "4.1.18",
+ "@tailwindcss/oxide-darwin-arm64": "4.1.18",
+ "@tailwindcss/oxide-darwin-x64": "4.1.18",
+ "@tailwindcss/oxide-freebsd-x64": "4.1.18",
+ "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.18",
+ "@tailwindcss/oxide-linux-arm64-gnu": "4.1.18",
+ "@tailwindcss/oxide-linux-arm64-musl": "4.1.18",
+ "@tailwindcss/oxide-linux-x64-gnu": "4.1.18",
+ "@tailwindcss/oxide-linux-x64-musl": "4.1.18",
+ "@tailwindcss/oxide-wasm32-wasi": "4.1.18",
+ "@tailwindcss/oxide-win32-arm64-msvc": "4.1.18",
+ "@tailwindcss/oxide-win32-x64-msvc": "4.1.18"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-android-arm64": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.18.tgz",
+ "integrity": "sha512-dJHz7+Ugr9U/diKJA0W6N/6/cjI+ZTAoxPf9Iz9BFRF2GzEX8IvXxFIi/dZBloVJX/MZGvRuFA9rqwdiIEZQ0Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-darwin-arm64": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.18.tgz",
+ "integrity": "sha512-Gc2q4Qhs660bhjyBSKgq6BYvwDz4G+BuyJ5H1xfhmDR3D8HnHCmT/BSkvSL0vQLy/nkMLY20PQ2OoYMO15Jd0A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-darwin-x64": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.18.tgz",
+ "integrity": "sha512-FL5oxr2xQsFrc3X9o1fjHKBYBMD1QZNyc1Xzw/h5Qu4XnEBi3dZn96HcHm41c/euGV+GRiXFfh2hUCyKi/e+yw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-freebsd-x64": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.18.tgz",
+ "integrity": "sha512-Fj+RHgu5bDodmV1dM9yAxlfJwkkWvLiRjbhuO2LEtwtlYlBgiAT4x/j5wQr1tC3SANAgD+0YcmWVrj8R9trVMA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.18.tgz",
+ "integrity": "sha512-Fp+Wzk/Ws4dZn+LV2Nqx3IilnhH51YZoRaYHQsVq3RQvEl+71VGKFpkfHrLM/Li+kt5c0DJe/bHXK1eHgDmdiA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-arm64-gnu": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.18.tgz",
+ "integrity": "sha512-S0n3jboLysNbh55Vrt7pk9wgpyTTPD0fdQeh7wQfMqLPM/Hrxi+dVsLsPrycQjGKEQk85Kgbx+6+QnYNiHalnw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-arm64-musl": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.18.tgz",
+ "integrity": "sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-x64-gnu": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.18.tgz",
+ "integrity": "sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-x64-musl": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.18.tgz",
+ "integrity": "sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-wasm32-wasi": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.18.tgz",
+ "integrity": "sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA==",
+ "bundleDependencies": [
+ "@napi-rs/wasm-runtime",
+ "@emnapi/core",
+ "@emnapi/runtime",
+ "@tybys/wasm-util",
+ "@emnapi/wasi-threads",
+ "tslib"
+ ],
+ "cpu": [
+ "wasm32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/core": "^1.7.1",
+ "@emnapi/runtime": "^1.7.1",
+ "@emnapi/wasi-threads": "^1.1.0",
+ "@napi-rs/wasm-runtime": "^1.1.0",
+ "@tybys/wasm-util": "^0.10.1",
+ "tslib": "^2.4.0"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-win32-arm64-msvc": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.18.tgz",
+ "integrity": "sha512-HjSA7mr9HmC8fu6bdsZvZ+dhjyGCLdotjVOgLA2vEqxEBZaQo9YTX4kwgEvPCpRh8o4uWc4J/wEoFzhEmjvPbA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-win32-x64-msvc": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.18.tgz",
+ "integrity": "sha512-bJWbyYpUlqamC8dpR7pfjA0I7vdF6t5VpUGMWRkXVE3AXgIZjYUYAK7II1GNaxR8J1SSrSrppRar8G++JekE3Q==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/postcss": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.1.18.tgz",
+ "integrity": "sha512-Ce0GFnzAOuPyfV5SxjXGn0CubwGcuDB0zcdaPuCSzAa/2vII24JTkH+I6jcbXLb1ctjZMZZI6OjDaLPJQL1S0g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@alloc/quick-lru": "^5.2.0",
+ "@tailwindcss/node": "4.1.18",
+ "@tailwindcss/oxide": "4.1.18",
+ "postcss": "^8.4.41",
+ "tailwindcss": "4.1.18"
+ }
+ },
+ "node_modules/@tanstack/query-core": {
+ "version": "5.90.16",
+ "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.90.16.tgz",
+ "integrity": "sha512-MvtWckSVufs/ja463/K4PyJeqT+HMlJWtw6PrCpywznd2NSgO3m4KwO9RqbFqGg6iDE8vVMFWMeQI4Io3eEYww==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ }
+ },
+ "node_modules/@tanstack/react-query": {
+ "version": "5.90.16",
+ "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.90.16.tgz",
+ "integrity": "sha512-bpMGOmV4OPmif7TNMteU/Ehf/hoC0Kf98PDc0F4BZkFrEapRMEqI/V6YS0lyzwSV6PQpY1y4xxArUIfBW5LVxQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@tanstack/query-core": "5.90.16"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ },
+ "peerDependencies": {
+ "react": "^18 || ^19"
+ }
+ },
+ "node_modules/@types/babel__core": {
+ "version": "7.20.5",
+ "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
+ "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.20.7",
+ "@babel/types": "^7.20.7",
+ "@types/babel__generator": "*",
+ "@types/babel__template": "*",
+ "@types/babel__traverse": "*"
+ }
+ },
+ "node_modules/@types/babel__generator": {
+ "version": "7.27.0",
+ "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz",
+ "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "node_modules/@types/babel__template": {
+ "version": "7.4.4",
+ "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz",
+ "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.1.0",
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "node_modules/@types/babel__traverse": {
+ "version": "7.28.0",
+ "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz",
+ "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.28.2"
+ }
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
+ "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/json-schema": {
+ "version": "7.0.15",
+ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
+ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/node": {
+ "version": "24.10.4",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.4.tgz",
+ "integrity": "sha512-vnDVpYPMzs4wunl27jHrfmwojOGKya0xyM3sH+UE5iv5uPS6vX7UIoh6m+vQc5LGBq52HBKPIn/zcSZVzeDEZg==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "undici-types": "~7.16.0"
+ }
+ },
+ "node_modules/@types/react": {
+ "version": "19.2.7",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.7.tgz",
+ "integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "csstype": "^3.2.2"
+ }
+ },
+ "node_modules/@types/react-dom": {
+ "version": "19.2.3",
+ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz",
+ "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "^19.2.0"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin": {
+ "version": "8.51.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.51.0.tgz",
+ "integrity": "sha512-XtssGWJvypyM2ytBnSnKtHYOGT+4ZwTnBVl36TA4nRO2f4PRNGz5/1OszHzcZCvcBMh+qb7I06uoCmLTRdR9og==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/regexpp": "^4.10.0",
+ "@typescript-eslint/scope-manager": "8.51.0",
+ "@typescript-eslint/type-utils": "8.51.0",
+ "@typescript-eslint/utils": "8.51.0",
+ "@typescript-eslint/visitor-keys": "8.51.0",
+ "ignore": "^7.0.0",
+ "natural-compare": "^1.4.0",
+ "ts-api-utils": "^2.2.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "@typescript-eslint/parser": "^8.51.0",
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": {
+ "version": "7.0.5",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
+ "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/@typescript-eslint/parser": {
+ "version": "8.51.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.51.0.tgz",
+ "integrity": "sha512-3xP4XzzDNQOIqBMWogftkwxhg5oMKApqY0BAflmLZiFYHqyhSOxv/cd/zPQLTcCXr4AkaKb25joocY0BD1WC6A==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@typescript-eslint/scope-manager": "8.51.0",
+ "@typescript-eslint/types": "8.51.0",
+ "@typescript-eslint/typescript-estree": "8.51.0",
+ "@typescript-eslint/visitor-keys": "8.51.0",
+ "debug": "^4.3.4"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/project-service": {
+ "version": "8.51.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.51.0.tgz",
+ "integrity": "sha512-Luv/GafO07Z7HpiI7qeEW5NW8HUtZI/fo/kE0YbtQEFpJRUuR0ajcWfCE5bnMvL7QQFrmT/odMe8QZww8X2nfQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/tsconfig-utils": "^8.51.0",
+ "@typescript-eslint/types": "^8.51.0",
+ "debug": "^4.3.4"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/scope-manager": {
+ "version": "8.51.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.51.0.tgz",
+ "integrity": "sha512-JhhJDVwsSx4hiOEQPeajGhCWgBMBwVkxC/Pet53EpBVs7zHHtayKefw1jtPaNRXpI9RA2uocdmpdfE7T+NrizA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.51.0",
+ "@typescript-eslint/visitor-keys": "8.51.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/tsconfig-utils": {
+ "version": "8.51.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.51.0.tgz",
+ "integrity": "sha512-Qi5bSy/vuHeWyir2C8u/uqGMIlIDu8fuiYWv48ZGlZ/k+PRPHtaAu7erpc7p5bzw2WNNSniuxoMSO4Ar6V9OXw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/type-utils": {
+ "version": "8.51.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.51.0.tgz",
+ "integrity": "sha512-0XVtYzxnobc9K0VU7wRWg1yiUrw4oQzexCG2V2IDxxCxhqBMSMbjB+6o91A+Uc0GWtgjCa3Y8bi7hwI0Tu4n5Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.51.0",
+ "@typescript-eslint/typescript-estree": "8.51.0",
+ "@typescript-eslint/utils": "8.51.0",
+ "debug": "^4.3.4",
+ "ts-api-utils": "^2.2.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/types": {
+ "version": "8.51.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.51.0.tgz",
+ "integrity": "sha512-TizAvWYFM6sSscmEakjY3sPqGwxZRSywSsPEiuZF6d5GmGD9Gvlsv0f6N8FvAAA0CD06l3rIcWNbsN1e5F/9Ag==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree": {
+ "version": "8.51.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.51.0.tgz",
+ "integrity": "sha512-1qNjGqFRmlq0VW5iVlcyHBbCjPB7y6SxpBkrbhNWMy/65ZoncXCEPJxkRZL8McrseNH6lFhaxCIaX+vBuFnRng==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/project-service": "8.51.0",
+ "@typescript-eslint/tsconfig-utils": "8.51.0",
+ "@typescript-eslint/types": "8.51.0",
+ "@typescript-eslint/visitor-keys": "8.51.0",
+ "debug": "^4.3.4",
+ "minimatch": "^9.0.4",
+ "semver": "^7.6.0",
+ "tinyglobby": "^0.2.15",
+ "ts-api-utils": "^2.2.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
+ "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+ "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": {
+ "version": "7.7.3",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
+ "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@typescript-eslint/utils": {
+ "version": "8.51.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.51.0.tgz",
+ "integrity": "sha512-11rZYxSe0zabiKaCP2QAwRf/dnmgFgvTmeDTtZvUvXG3UuAdg/GU02NExmmIXzz3vLGgMdtrIosI84jITQOxUA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.7.0",
+ "@typescript-eslint/scope-manager": "8.51.0",
+ "@typescript-eslint/types": "8.51.0",
+ "@typescript-eslint/typescript-estree": "8.51.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/visitor-keys": {
+ "version": "8.51.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.51.0.tgz",
+ "integrity": "sha512-mM/JRQOzhVN1ykejrvwnBRV3+7yTKK8tVANVN3o1O0t0v7o+jqdVu9crPy5Y9dov15TJk/FTIgoUGHrTOVL3Zg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.51.0",
+ "eslint-visitor-keys": "^4.2.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@vitejs/plugin-react": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.1.2.tgz",
+ "integrity": "sha512-EcA07pHJouywpzsoTUqNh5NwGayl2PPVEJKUSinGGSxFGYn+shYbqMGBg6FXDqgXum9Ou/ecb+411ssw8HImJQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/core": "^7.28.5",
+ "@babel/plugin-transform-react-jsx-self": "^7.27.1",
+ "@babel/plugin-transform-react-jsx-source": "^7.27.1",
+ "@rolldown/pluginutils": "1.0.0-beta.53",
+ "@types/babel__core": "^7.20.5",
+ "react-refresh": "^0.18.0"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "peerDependencies": {
+ "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
+ }
+ },
+ "node_modules/acorn": {
+ "version": "8.15.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
+ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-jsx": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true,
+ "license": "Python-2.0"
+ },
+ "node_modules/asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
+ "license": "MIT"
+ },
+ "node_modules/autoprefixer": {
+ "version": "10.4.23",
+ "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.23.tgz",
+ "integrity": "sha512-YYTXSFulfwytnjAPlw8QHncHJmlvFKtczb8InXaAx9Q0LbfDnfEYDE55omerIJKihhmU61Ft+cAOSzQVaBUmeA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/autoprefixer"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "browserslist": "^4.28.1",
+ "caniuse-lite": "^1.0.30001760",
+ "fraction.js": "^5.3.4",
+ "picocolors": "^1.1.1",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "bin": {
+ "autoprefixer": "bin/autoprefixer"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ },
+ "peerDependencies": {
+ "postcss": "^8.1.0"
+ }
+ },
+ "node_modules/axios": {
+ "version": "1.13.2",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz",
+ "integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==",
+ "license": "MIT",
+ "dependencies": {
+ "follow-redirects": "^1.15.6",
+ "form-data": "^4.0.4",
+ "proxy-from-env": "^1.1.0"
+ }
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/baseline-browser-mapping": {
+ "version": "2.9.11",
+ "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.11.tgz",
+ "integrity": "sha512-Sg0xJUNDU1sJNGdfGWhVHX0kkZ+HWcvmVymJbj6NSgZZmW/8S9Y2HQ5euytnIgakgxN6papOAWiwDo1ctFDcoQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "baseline-browser-mapping": "dist/cli.js"
+ }
+ },
+ "node_modules/brace-expansion": {
+ "version": "1.1.12",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
+ "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/browserslist": {
+ "version": "4.28.1",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz",
+ "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "baseline-browser-mapping": "^2.9.0",
+ "caniuse-lite": "^1.0.30001759",
+ "electron-to-chromium": "^1.5.263",
+ "node-releases": "^2.0.27",
+ "update-browserslist-db": "^1.2.0"
+ },
+ "bin": {
+ "browserslist": "cli.js"
+ },
+ "engines": {
+ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+ }
+ },
+ "node_modules/call-bind-apply-helpers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+ "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001762",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001762.tgz",
+ "integrity": "sha512-PxZwGNvH7Ak8WX5iXzoK1KPZttBXNPuaOvI2ZYU7NrlM+d9Ov+TUvlLOBNGzVXAntMSMMlJPd+jY6ovrVjSmUw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "CC-BY-4.0"
+ },
+ "node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/clsx": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
+ "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "license": "MIT",
+ "dependencies": {
+ "delayed-stream": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/convert-source-map": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
+ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/cookie": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz",
+ "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/cross-spawn": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/csstype": {
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
+ "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/debug": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/deep-is": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/detect-libc": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
+ "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/dunder-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
+ "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/electron-to-chromium": {
+ "version": "1.5.267",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz",
+ "integrity": "sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/enhanced-resolve": {
+ "version": "5.18.4",
+ "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.4.tgz",
+ "integrity": "sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "graceful-fs": "^4.2.4",
+ "tapable": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/es-define-property": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+ "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-object-atoms": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
+ "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-set-tostringtag": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
+ "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6",
+ "has-tostringtag": "^1.0.2",
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/esbuild": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.2.tgz",
+ "integrity": "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.27.2",
+ "@esbuild/android-arm": "0.27.2",
+ "@esbuild/android-arm64": "0.27.2",
+ "@esbuild/android-x64": "0.27.2",
+ "@esbuild/darwin-arm64": "0.27.2",
+ "@esbuild/darwin-x64": "0.27.2",
+ "@esbuild/freebsd-arm64": "0.27.2",
+ "@esbuild/freebsd-x64": "0.27.2",
+ "@esbuild/linux-arm": "0.27.2",
+ "@esbuild/linux-arm64": "0.27.2",
+ "@esbuild/linux-ia32": "0.27.2",
+ "@esbuild/linux-loong64": "0.27.2",
+ "@esbuild/linux-mips64el": "0.27.2",
+ "@esbuild/linux-ppc64": "0.27.2",
+ "@esbuild/linux-riscv64": "0.27.2",
+ "@esbuild/linux-s390x": "0.27.2",
+ "@esbuild/linux-x64": "0.27.2",
+ "@esbuild/netbsd-arm64": "0.27.2",
+ "@esbuild/netbsd-x64": "0.27.2",
+ "@esbuild/openbsd-arm64": "0.27.2",
+ "@esbuild/openbsd-x64": "0.27.2",
+ "@esbuild/openharmony-arm64": "0.27.2",
+ "@esbuild/sunos-x64": "0.27.2",
+ "@esbuild/win32-arm64": "0.27.2",
+ "@esbuild/win32-ia32": "0.27.2",
+ "@esbuild/win32-x64": "0.27.2"
+ }
+ },
+ "node_modules/escalade": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
+ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint": {
+ "version": "9.39.2",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.2.tgz",
+ "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.8.0",
+ "@eslint-community/regexpp": "^4.12.1",
+ "@eslint/config-array": "^0.21.1",
+ "@eslint/config-helpers": "^0.4.2",
+ "@eslint/core": "^0.17.0",
+ "@eslint/eslintrc": "^3.3.1",
+ "@eslint/js": "9.39.2",
+ "@eslint/plugin-kit": "^0.4.1",
+ "@humanfs/node": "^0.16.6",
+ "@humanwhocodes/module-importer": "^1.0.1",
+ "@humanwhocodes/retry": "^0.4.2",
+ "@types/estree": "^1.0.6",
+ "ajv": "^6.12.4",
+ "chalk": "^4.0.0",
+ "cross-spawn": "^7.0.6",
+ "debug": "^4.3.2",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^8.4.0",
+ "eslint-visitor-keys": "^4.2.1",
+ "espree": "^10.4.0",
+ "esquery": "^1.5.0",
+ "esutils": "^2.0.2",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^8.0.0",
+ "find-up": "^5.0.0",
+ "glob-parent": "^6.0.2",
+ "ignore": "^5.2.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "lodash.merge": "^4.6.2",
+ "minimatch": "^3.1.2",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.3"
+ },
+ "bin": {
+ "eslint": "bin/eslint.js"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://eslint.org/donate"
+ },
+ "peerDependencies": {
+ "jiti": "*"
+ },
+ "peerDependenciesMeta": {
+ "jiti": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-plugin-react-hooks": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.0.1.tgz",
+ "integrity": "sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/core": "^7.24.4",
+ "@babel/parser": "^7.24.4",
+ "hermes-parser": "^0.25.1",
+ "zod": "^3.25.0 || ^4.0.0",
+ "zod-validation-error": "^3.5.0 || ^4.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0"
+ }
+ },
+ "node_modules/eslint-plugin-react-refresh": {
+ "version": "0.4.26",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.26.tgz",
+ "integrity": "sha512-1RETEylht2O6FM/MvgnyvT+8K21wLqDNg4qD51Zj3guhjt433XbnnkVttHMyaVyAFD03QSV4LPS5iE3VQmO7XQ==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "eslint": ">=8.40"
+ }
+ },
+ "node_modules/eslint-scope": {
+ "version": "8.4.0",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz",
+ "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint-visitor-keys": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
+ "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/espree": {
+ "version": "10.4.0",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz",
+ "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "acorn": "^8.15.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^4.2.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/esquery": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz",
+ "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "estraverse": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/esrecurse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fdir": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
+ "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/file-entry-cache": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
+ "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flat-cache": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/find-up": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/flat-cache": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
+ "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flatted": "^3.2.9",
+ "keyv": "^4.5.4"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/flatted": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz",
+ "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/follow-redirects": {
+ "version": "1.15.11",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz",
+ "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/RubenVerborgh"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=4.0"
+ },
+ "peerDependenciesMeta": {
+ "debug": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/form-data": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
+ "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
+ "license": "MIT",
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "es-set-tostringtag": "^2.1.0",
+ "hasown": "^2.0.2",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/fraction.js": {
+ "version": "5.3.4",
+ "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz",
+ "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/rawify"
+ }
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/gensync": {
+ "version": "1.0.0-beta.2",
+ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
+ "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/get-intrinsic": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+ "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+ "license": "MIT",
+ "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"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
+ "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/glob-parent": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+ "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/globals": {
+ "version": "16.5.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz",
+ "integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/gopd": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
+ "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/graceful-fs": {
+ "version": "4.2.11",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/has-symbols": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
+ "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-tostringtag": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+ "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+ "license": "MIT",
+ "dependencies": {
+ "has-symbols": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/hasown": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
+ "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+ "license": "MIT",
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/hermes-estree": {
+ "version": "0.25.1",
+ "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz",
+ "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/hermes-parser": {
+ "version": "0.25.1",
+ "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz",
+ "integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "hermes-estree": "0.25.1"
+ }
+ },
+ "node_modules/ignore": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
+ "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/import-fresh": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
+ "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8.19"
+ }
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/jiti": {
+ "version": "2.6.1",
+ "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz",
+ "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "bin": {
+ "jiti": "lib/jiti-cli.mjs"
+ }
+ },
+ "node_modules/js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/js-yaml": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
+ "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/jsesc": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
+ "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "jsesc": "bin/jsesc"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/json-buffer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
+ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json5": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
+ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "json5": "lib/cli.js"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/keyv": {
+ "version": "4.5.4",
+ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
+ "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "json-buffer": "3.0.1"
+ }
+ },
+ "node_modules/levn": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/lightningcss": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.2.tgz",
+ "integrity": "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==",
+ "dev": true,
+ "license": "MPL-2.0",
+ "peer": true,
+ "dependencies": {
+ "detect-libc": "^2.0.3"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ },
+ "optionalDependencies": {
+ "lightningcss-android-arm64": "1.30.2",
+ "lightningcss-darwin-arm64": "1.30.2",
+ "lightningcss-darwin-x64": "1.30.2",
+ "lightningcss-freebsd-x64": "1.30.2",
+ "lightningcss-linux-arm-gnueabihf": "1.30.2",
+ "lightningcss-linux-arm64-gnu": "1.30.2",
+ "lightningcss-linux-arm64-musl": "1.30.2",
+ "lightningcss-linux-x64-gnu": "1.30.2",
+ "lightningcss-linux-x64-musl": "1.30.2",
+ "lightningcss-win32-arm64-msvc": "1.30.2",
+ "lightningcss-win32-x64-msvc": "1.30.2"
+ }
+ },
+ "node_modules/lightningcss-android-arm64": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.30.2.tgz",
+ "integrity": "sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-darwin-arm64": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.2.tgz",
+ "integrity": "sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-darwin-x64": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.2.tgz",
+ "integrity": "sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-freebsd-x64": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.2.tgz",
+ "integrity": "sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm-gnueabihf": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.2.tgz",
+ "integrity": "sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm64-gnu": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.2.tgz",
+ "integrity": "sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm64-musl": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.2.tgz",
+ "integrity": "sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-x64-gnu": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.2.tgz",
+ "integrity": "sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-x64-musl": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.2.tgz",
+ "integrity": "sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-win32-arm64-msvc": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.2.tgz",
+ "integrity": "sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-win32-x64-msvc": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.2.tgz",
+ "integrity": "sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/locate-path": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-locate": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/lodash.merge": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lru-cache": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "yallist": "^3.0.2"
+ }
+ },
+ "node_modules/lucide-react": {
+ "version": "0.562.0",
+ "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.562.0.tgz",
+ "integrity": "sha512-82hOAu7y0dbVuFfmO4bYF1XEwYk/mEbM5E+b1jgci/udUBEE/R7LF5Ip0CCEmXe8AybRM8L+04eP+LGZeDvkiw==",
+ "license": "ISC",
+ "peerDependencies": {
+ "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/magic-string": {
+ "version": "0.30.21",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
+ "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.5"
+ }
+ },
+ "node_modules/math-intrinsics": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+ "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mini-svg-data-uri": {
+ "version": "1.4.4",
+ "resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz",
+ "integrity": "sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "mini-svg-data-uri": "cli.js"
+ }
+ },
+ "node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.11",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
+ "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/node-releases": {
+ "version": "2.0.27",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz",
+ "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/optionator": {
+ "version": "0.9.4",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
+ "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+ "levn": "^0.4.1",
+ "prelude-ls": "^1.2.1",
+ "type-check": "^0.4.0",
+ "word-wrap": "^1.2.5"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/p-limit": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "yocto-queue": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-locate": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-limit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "callsites": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/picomatch": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
+ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/postcss": {
+ "version": "8.5.6",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
+ "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "nanoid": "^3.3.11",
+ "picocolors": "^1.1.1",
+ "source-map-js": "^1.2.1"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/postcss-value-parser": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
+ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/prelude-ls": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/proxy-from-env": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
+ "license": "MIT"
+ },
+ "node_modules/punycode": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
+ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/react": {
+ "version": "19.2.3",
+ "resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz",
+ "integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==",
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-dom": {
+ "version": "19.2.3",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz",
+ "integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "scheduler": "^0.27.0"
+ },
+ "peerDependencies": {
+ "react": "^19.2.3"
+ }
+ },
+ "node_modules/react-refresh": {
+ "version": "0.18.0",
+ "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz",
+ "integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-router": {
+ "version": "7.11.0",
+ "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.11.0.tgz",
+ "integrity": "sha512-uI4JkMmjbWCZc01WVP2cH7ZfSzH91JAZUDd7/nIprDgWxBV1TkkmLToFh7EbMTcMak8URFRa2YoBL/W8GWnCTQ==",
+ "license": "MIT",
+ "dependencies": {
+ "cookie": "^1.0.1",
+ "set-cookie-parser": "^2.6.0"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=18",
+ "react-dom": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/react-router-dom": {
+ "version": "7.11.0",
+ "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.11.0.tgz",
+ "integrity": "sha512-e49Ir/kMGRzFOOrYQBdoitq3ULigw4lKbAyKusnvtDu2t4dBX4AGYPrzNvorXmVuOyeakai6FUPW5MmibvVG8g==",
+ "license": "MIT",
+ "dependencies": {
+ "react-router": "7.11.0"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=18",
+ "react-dom": ">=18"
+ }
+ },
+ "node_modules/resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/rollup": {
+ "version": "4.54.0",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.54.0.tgz",
+ "integrity": "sha512-3nk8Y3a9Ea8szgKhinMlGMhGMw89mqule3KWczxhIzqudyHdCIOHw8WJlj/r329fACjKLEh13ZSk7oE22kyeIw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "1.0.8"
+ },
+ "bin": {
+ "rollup": "dist/bin/rollup"
+ },
+ "engines": {
+ "node": ">=18.0.0",
+ "npm": ">=8.0.0"
+ },
+ "optionalDependencies": {
+ "@rollup/rollup-android-arm-eabi": "4.54.0",
+ "@rollup/rollup-android-arm64": "4.54.0",
+ "@rollup/rollup-darwin-arm64": "4.54.0",
+ "@rollup/rollup-darwin-x64": "4.54.0",
+ "@rollup/rollup-freebsd-arm64": "4.54.0",
+ "@rollup/rollup-freebsd-x64": "4.54.0",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.54.0",
+ "@rollup/rollup-linux-arm-musleabihf": "4.54.0",
+ "@rollup/rollup-linux-arm64-gnu": "4.54.0",
+ "@rollup/rollup-linux-arm64-musl": "4.54.0",
+ "@rollup/rollup-linux-loong64-gnu": "4.54.0",
+ "@rollup/rollup-linux-ppc64-gnu": "4.54.0",
+ "@rollup/rollup-linux-riscv64-gnu": "4.54.0",
+ "@rollup/rollup-linux-riscv64-musl": "4.54.0",
+ "@rollup/rollup-linux-s390x-gnu": "4.54.0",
+ "@rollup/rollup-linux-x64-gnu": "4.54.0",
+ "@rollup/rollup-linux-x64-musl": "4.54.0",
+ "@rollup/rollup-openharmony-arm64": "4.54.0",
+ "@rollup/rollup-win32-arm64-msvc": "4.54.0",
+ "@rollup/rollup-win32-ia32-msvc": "4.54.0",
+ "@rollup/rollup-win32-x64-gnu": "4.54.0",
+ "@rollup/rollup-win32-x64-msvc": "4.54.0",
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/scheduler": {
+ "version": "0.27.0",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz",
+ "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==",
+ "license": "MIT"
+ },
+ "node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/set-cookie-parser": {
+ "version": "2.7.2",
+ "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz",
+ "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==",
+ "license": "MIT"
+ },
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/strip-json-comments": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/tailwindcss": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.18.tgz",
+ "integrity": "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/tapable": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz",
+ "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ }
+ },
+ "node_modules/tinyglobby": {
+ "version": "0.2.15",
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
+ "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/SuperchupuDev"
+ }
+ },
+ "node_modules/ts-api-utils": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz",
+ "integrity": "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.12"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4"
+ }
+ },
+ "node_modules/type-check": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/typescript": {
+ "version": "5.9.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
+ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "peer": true,
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/typescript-eslint": {
+ "version": "8.51.0",
+ "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.51.0.tgz",
+ "integrity": "sha512-jh8ZuM5oEh2PSdyQG9YAEM1TCGuWenLSuSUhf/irbVUNW9O5FhbFVONviN2TgMTBnUmyHv7E56rYnfLZK6TkiA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/eslint-plugin": "8.51.0",
+ "@typescript-eslint/parser": "8.51.0",
+ "@typescript-eslint/typescript-estree": "8.51.0",
+ "@typescript-eslint/utils": "8.51.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "7.16.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
+ "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/update-browserslist-db": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
+ "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "escalade": "^3.2.0",
+ "picocolors": "^1.1.1"
+ },
+ "bin": {
+ "update-browserslist-db": "cli.js"
+ },
+ "peerDependencies": {
+ "browserslist": ">= 4.21.0"
+ }
+ },
+ "node_modules/uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "node_modules/vite": {
+ "version": "7.3.0",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.0.tgz",
+ "integrity": "sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "esbuild": "^0.27.0",
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.3",
+ "postcss": "^8.5.6",
+ "rollup": "^4.43.0",
+ "tinyglobby": "^0.2.15"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "funding": {
+ "url": "https://github.com/vitejs/vite?sponsor=1"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ },
+ "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
+ }
+ }
+ },
+ "node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/word-wrap": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
+ "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/yallist": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/yocto-queue": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/zod": {
+ "version": "4.3.5",
+ "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.5.tgz",
+ "integrity": "sha512-k7Nwx6vuWx1IJ9Bjuf4Zt1PEllcwe7cls3VNzm4CQ1/hgtFUK2bRNG3rvnpPUhFjmqJKAKtjV576KnUkHocg/g==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "funding": {
+ "url": "https://github.com/sponsors/colinhacks"
+ }
+ },
+ "node_modules/zod-validation-error": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz",
+ "integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "peerDependencies": {
+ "zod": "^3.25.0 || ^4.0.0"
+ }
+ }
+ }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..b76509f
--- /dev/null
+++ b/package.json
@@ -0,0 +1,45 @@
+{
+ "name": "frontend",
+ "private": true,
+ "version": "0.0.0",
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "tsc -b && vite build",
+ "lint": "eslint .",
+ "preview": "vite preview",
+ "test:e2e": "playwright test",
+ "test:e2e:ui": "playwright test --ui",
+ "test:e2e:headed": "playwright test --headed",
+ "test:e2e:report": "playwright show-report"
+ },
+ "dependencies": {
+ "@tanstack/react-query": "^5.90.16",
+ "axios": "^1.13.2",
+ "clsx": "^2.1.1",
+ "lucide-react": "^0.562.0",
+ "react": "^19.2.0",
+ "react-dom": "^19.2.0",
+ "react-router-dom": "^7.11.0"
+ },
+ "devDependencies": {
+ "@playwright/test": "^1.50.1",
+ "@eslint/js": "^9.39.1",
+ "@tailwindcss/forms": "^0.5.11",
+ "@tailwindcss/postcss": "^4.1.18",
+ "@types/node": "^24.10.1",
+ "@types/react": "^19.2.5",
+ "@types/react-dom": "^19.2.3",
+ "@vitejs/plugin-react": "^5.1.1",
+ "autoprefixer": "^10.4.23",
+ "eslint": "^9.39.1",
+ "eslint-plugin-react-hooks": "^7.0.1",
+ "eslint-plugin-react-refresh": "^0.4.24",
+ "globals": "^16.5.0",
+ "postcss": "^8.5.6",
+ "tailwindcss": "^4.1.18",
+ "typescript": "~5.9.3",
+ "typescript-eslint": "^8.46.4",
+ "vite": "^7.2.4"
+ }
+}
diff --git a/playwright.config.ts b/playwright.config.ts
new file mode 100644
index 0000000..cb556df
--- /dev/null
+++ b/playwright.config.ts
@@ -0,0 +1,53 @@
+import { defineConfig, devices } from '@playwright/test';
+
+/**
+ * Playwright E2E Test Configuration - MiChangarrito Frontend
+ * @see https://playwright.dev/docs/test-configuration
+ */
+export default defineConfig({
+ testDir: './e2e',
+ fullyParallel: true,
+ forbidOnly: !!process.env.CI,
+ retries: process.env.CI ? 2 : 0,
+ workers: process.env.CI ? 1 : undefined,
+ reporter: [
+ ['html', { outputFolder: 'playwright-report' }],
+ ['list'],
+ ],
+ use: {
+ baseURL: 'http://localhost:3140',
+ trace: 'on-first-retry',
+ screenshot: 'only-on-failure',
+ video: 'retain-on-failure',
+ },
+
+ projects: [
+ {
+ name: 'chromium',
+ use: { ...devices['Desktop Chrome'] },
+ },
+ {
+ name: 'firefox',
+ use: { ...devices['Desktop Firefox'] },
+ },
+ {
+ name: 'webkit',
+ use: { ...devices['Desktop Safari'] },
+ },
+ {
+ name: 'Mobile Chrome',
+ use: { ...devices['Pixel 5'] },
+ },
+ {
+ name: 'Mobile Safari',
+ use: { ...devices['iPhone 12'] },
+ },
+ ],
+
+ webServer: {
+ command: 'npm run dev',
+ url: 'http://localhost:3140',
+ reuseExistingServer: !process.env.CI,
+ timeout: 120 * 1000,
+ },
+});
diff --git a/postcss.config.js b/postcss.config.js
new file mode 100644
index 0000000..1c87846
--- /dev/null
+++ b/postcss.config.js
@@ -0,0 +1,6 @@
+export default {
+ plugins: {
+ '@tailwindcss/postcss': {},
+ autoprefixer: {},
+ },
+}
diff --git a/public/vite.svg b/public/vite.svg
new file mode 100644
index 0000000..e7b8dfb
--- /dev/null
+++ b/public/vite.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/App.tsx b/src/App.tsx
new file mode 100644
index 0000000..6c79a46
--- /dev/null
+++ b/src/App.tsx
@@ -0,0 +1,95 @@
+import { BrowserRouter, Routes, Route, Navigate, Outlet } from 'react-router-dom';
+import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
+import { AuthProvider, useAuth } from './contexts/AuthContext';
+import { Layout } from './components/Layout';
+import { Dashboard } from './pages/Dashboard';
+import { Products } from './pages/Products';
+import { Orders } from './pages/Orders';
+import { Customers } from './pages/Customers';
+import { Fiado } from './pages/Fiado';
+import { Inventory } from './pages/Inventory';
+import { Settings } from './pages/Settings';
+import { Referrals } from './pages/Referrals';
+import { Invoices } from './pages/Invoices';
+import { Marketplace } from './pages/Marketplace';
+import Login from './pages/Login';
+import Register from './pages/Register';
+
+const queryClient = new QueryClient({
+ defaultOptions: {
+ queries: {
+ staleTime: 1000 * 60 * 5, // 5 minutes
+ retry: 1,
+ },
+ },
+});
+
+// Protected route wrapper
+function ProtectedRoute() {
+ const { isAuthenticated, isLoading } = useAuth();
+
+ if (isLoading) {
+ return (
+
+ );
+ }
+
+ return isAuthenticated ? : ;
+}
+
+// Public route wrapper (redirect if authenticated)
+function PublicRoute() {
+ const { isAuthenticated, isLoading } = useAuth();
+
+ if (isLoading) {
+ return (
+
+ );
+ }
+
+ return isAuthenticated ? : ;
+}
+
+function App() {
+ return (
+
+
+
+
+ {/* Public routes */}
+ }>
+ } />
+ } />
+
+
+ {/* Protected routes */}
+ }>
+ }>
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+
+
+
+ {/* Catch all */}
+ } />
+
+
+
+
+ );
+}
+
+export default App;
diff --git a/src/assets/react.svg b/src/assets/react.svg
new file mode 100644
index 0000000..6c87de9
--- /dev/null
+++ b/src/assets/react.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/components/Layout.tsx b/src/components/Layout.tsx
new file mode 100644
index 0000000..174cc3e
--- /dev/null
+++ b/src/components/Layout.tsx
@@ -0,0 +1,173 @@
+import { Outlet, NavLink, useNavigate } from 'react-router-dom';
+import {
+ LayoutDashboard,
+ Package,
+ ShoppingCart,
+ Users,
+ CreditCard,
+ Boxes,
+ Settings,
+ Menu,
+ X,
+ Store,
+ LogOut,
+ Gift,
+ FileText,
+ Truck,
+} from 'lucide-react';
+import { useState } from 'react';
+import clsx from 'clsx';
+import { useAuth } from '../contexts/AuthContext';
+
+const navigation = [
+ { name: 'Dashboard', href: '/dashboard', icon: LayoutDashboard },
+ { name: 'Productos', href: '/products', icon: Package },
+ { name: 'Pedidos', href: '/orders', icon: ShoppingCart },
+ { name: 'Clientes', href: '/customers', icon: Users },
+ { name: 'Fiado', href: '/fiado', icon: CreditCard },
+ { name: 'Inventario', href: '/inventory', icon: Boxes },
+ { name: 'Facturacion', href: '/invoices', icon: FileText },
+ { name: 'Proveedores', href: '/marketplace', icon: Truck },
+ { name: 'Referidos', href: '/referrals', icon: Gift },
+ { name: 'Ajustes', href: '/settings', icon: Settings },
+];
+
+export function Layout() {
+ const [sidebarOpen, setSidebarOpen] = useState(false);
+ const { user, tenant, logout } = useAuth();
+ const navigate = useNavigate();
+
+ const handleLogout = () => {
+ logout();
+ navigate('/login');
+ };
+
+ const getInitials = (name: string) => {
+ return name
+ .split(' ')
+ .map((n) => n[0])
+ .join('')
+ .toUpperCase()
+ .slice(0, 2);
+ };
+
+ return (
+
+ {/* Mobile sidebar */}
+
+
setSidebarOpen(false)}
+ />
+
+
+
+
+ MiChangarrito
+
+
+
+
+
+
+
+ {/* Desktop sidebar */}
+
+
+
+
+
+
+
+
+
+ {tenant ? getInitials(tenant.name) : 'MC'}
+
+
+
+
{tenant?.name || 'Mi Negocio'}
+
{user?.name}
+
+
+
+
+
+
+
+
+ {/* Main content */}
+
+ {/* Mobile header */}
+
+
+
+
+ MiChangarrito
+
+
+
+ {/* Page content */}
+
+
+
+
+
+ );
+}
diff --git a/src/components/payments/ClabeDisplay.tsx b/src/components/payments/ClabeDisplay.tsx
new file mode 100644
index 0000000..49c6309
--- /dev/null
+++ b/src/components/payments/ClabeDisplay.tsx
@@ -0,0 +1,112 @@
+import { useState } from 'react';
+import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
+import { Copy, Check, Building } from 'lucide-react';
+import { codiSpeiApi } from '../../lib/api';
+
+interface ClabeDisplayProps {
+ showCreateButton?: boolean;
+ beneficiaryName?: string;
+}
+
+export function ClabeDisplay({ showCreateButton = true, beneficiaryName }: ClabeDisplayProps) {
+ const [copied, setCopied] = useState(false);
+ const queryClient = useQueryClient();
+
+ const { data, isLoading } = useQuery({
+ queryKey: ['clabe'],
+ queryFn: async () => {
+ const res = await codiSpeiApi.getClabe();
+ return res.data;
+ },
+ });
+
+ const createMutation = useMutation({
+ mutationFn: (name: string) => codiSpeiApi.createClabe(name),
+ onSuccess: () => {
+ queryClient.invalidateQueries({ queryKey: ['clabe'] });
+ },
+ });
+
+ const copyClabe = async () => {
+ if (data?.clabe) {
+ await navigator.clipboard.writeText(data.clabe);
+ setCopied(true);
+ setTimeout(() => setCopied(false), 2000);
+ }
+ };
+
+ const formatClabe = (clabe: string) => {
+ // Format: XXX XXX XXXX XXXX XXXX
+ return clabe.replace(/(\d{3})(\d{3})(\d{4})(\d{4})(\d{4})/, '$1 $2 $3 $4 $5');
+ };
+
+ if (isLoading) {
+ return (
+
+ );
+ }
+
+ if (!data?.clabe && showCreateButton) {
+ return (
+
+
+
+
+
CLABE Virtual
+
Recibe transferencias SPEI
+
+
+
+
+ );
+ }
+
+ if (!data?.clabe) {
+ return null;
+ }
+
+ return (
+
+
+
+
+ Tu CLABE para recibir SPEI
+
+
+
+
+
+
+ {formatClabe(data.clabe)}
+
+ {data.beneficiaryName && (
+
+ {data.beneficiaryName}
+
+ )}
+
+
+
+ Las transferencias se reflejan automaticamente
+
+
+ );
+}
diff --git a/src/components/payments/CodiQR.tsx b/src/components/payments/CodiQR.tsx
new file mode 100644
index 0000000..7416066
--- /dev/null
+++ b/src/components/payments/CodiQR.tsx
@@ -0,0 +1,167 @@
+import { useState, useEffect } from 'react';
+import { useMutation, useQuery } from '@tanstack/react-query';
+import { QrCode, Clock, Check, X, RefreshCw } from 'lucide-react';
+import { codiSpeiApi } from '../../lib/api';
+
+interface CodiQRProps {
+ amount: number;
+ description?: string;
+ saleId?: string;
+ onSuccess?: () => void;
+ onCancel?: () => void;
+}
+
+export function CodiQR({ amount, description, saleId, onSuccess, onCancel }: CodiQRProps) {
+ const [transactionId, setTransactionId] = useState
(null);
+ const [timeLeft, setTimeLeft] = useState(300); // 5 minutes in seconds
+
+ const generateMutation = useMutation({
+ mutationFn: () => codiSpeiApi.generateQr({ amount, description, saleId }),
+ onSuccess: (res) => {
+ setTransactionId(res.data.id);
+ setTimeLeft(300);
+ },
+ });
+
+ const { data: status, refetch } = useQuery({
+ queryKey: ['codi-status', transactionId],
+ queryFn: async () => {
+ if (!transactionId) return null;
+ const res = await codiSpeiApi.getCodiStatus(transactionId);
+ return res.data;
+ },
+ enabled: !!transactionId,
+ refetchInterval: 3000, // Poll every 3 seconds
+ });
+
+ // Generate QR on mount
+ useEffect(() => {
+ generateMutation.mutate();
+ }, []);
+
+ // Countdown timer
+ useEffect(() => {
+ if (timeLeft <= 0) return;
+
+ const timer = setInterval(() => {
+ setTimeLeft((prev) => prev - 1);
+ }, 1000);
+
+ return () => clearInterval(timer);
+ }, [timeLeft]);
+
+ // Handle status changes
+ useEffect(() => {
+ if (status?.status === 'confirmed') {
+ onSuccess?.();
+ } else if (status?.status === 'expired' || status?.status === 'cancelled') {
+ // QR expired or cancelled
+ }
+ }, [status?.status]);
+
+ const formatTime = (seconds: number) => {
+ const mins = Math.floor(seconds / 60);
+ const secs = seconds % 60;
+ return `${mins}:${secs.toString().padStart(2, '0')}`;
+ };
+
+ const handleRefresh = () => {
+ generateMutation.mutate();
+ };
+
+ if (generateMutation.isPending) {
+ return (
+
+
+
Generando QR de cobro...
+
+ );
+ }
+
+ if (status?.status === 'confirmed') {
+ return (
+
+
+
+
+
Pago Confirmado
+
El pago de ${amount.toFixed(2)} fue recibido
+
+ );
+ }
+
+ if (status?.status === 'expired' || timeLeft <= 0) {
+ return (
+
+
+
+
+
QR Expirado
+
El codigo QR ha expirado
+
+
+ );
+ }
+
+ return (
+
+
+
Pagar con CoDi
+
${amount.toFixed(2)}
+ {description &&
{description}
}
+
+
+ {/* QR Code Display */}
+
+
+ {/* In production, generate actual QR code from qrData */}
+
+
+
Escanea con tu app bancaria
+
+
+
+
+ {/* Timer */}
+
+
+ Expira en: {formatTime(timeLeft)}
+
+
+ {/* Instructions */}
+
+
1. Abre la app de tu banco
+
2. Escanea el codigo QR
+
3. Confirma el pago
+
+
+ {/* Actions */}
+
+
+
+
+
+ {/* Reference */}
+ {status?.reference && (
+
Ref: {status.reference}
+ )}
+
+ );
+}
diff --git a/src/contexts/AuthContext.tsx b/src/contexts/AuthContext.tsx
new file mode 100644
index 0000000..7de3735
--- /dev/null
+++ b/src/contexts/AuthContext.tsx
@@ -0,0 +1,118 @@
+import { createContext, useContext, useState, useEffect } from 'react';
+import type { ReactNode } from 'react';
+import { authApi } from '../lib/api';
+
+interface User {
+ id: string;
+ name: string;
+ role: string;
+ phone: string;
+}
+
+interface Tenant {
+ id: string;
+ name: string;
+ slug: string;
+ businessType: string;
+ subscriptionStatus: string;
+}
+
+interface AuthContextType {
+ user: User | null;
+ tenant: Tenant | null;
+ isAuthenticated: boolean;
+ isLoading: boolean;
+ login: (phone: string, pin: string) => Promise;
+ register: (data: RegisterData) => Promise;
+ logout: () => void;
+}
+
+interface RegisterData {
+ name: string;
+ ownerName: string;
+ businessType: string;
+ phone: string;
+ pin: string;
+ email?: string;
+ whatsapp?: string;
+}
+
+const AuthContext = createContext(undefined);
+
+export function AuthProvider({ children }: { children: ReactNode }) {
+ const [user, setUser] = useState(null);
+ const [tenant, setTenant] = useState(null);
+ const [isLoading, setIsLoading] = useState(true);
+
+ useEffect(() => {
+ // Check for existing session
+ const storedUser = localStorage.getItem('user');
+ const storedTenant = localStorage.getItem('tenant');
+ const accessToken = localStorage.getItem('accessToken');
+
+ if (storedUser && storedTenant && accessToken) {
+ setUser(JSON.parse(storedUser));
+ setTenant(JSON.parse(storedTenant));
+ }
+ setIsLoading(false);
+ }, []);
+
+ const login = async (phone: string, pin: string) => {
+ const response = await authApi.login({ phone, pin });
+ const { accessToken, refreshToken, user: userData, tenant: tenantData } = response.data;
+
+ localStorage.setItem('accessToken', accessToken);
+ localStorage.setItem('refreshToken', refreshToken);
+ localStorage.setItem('user', JSON.stringify(userData));
+ localStorage.setItem('tenant', JSON.stringify(tenantData));
+
+ setUser(userData);
+ setTenant(tenantData);
+ };
+
+ const register = async (data: RegisterData) => {
+ const response = await authApi.register(data);
+ const { accessToken, refreshToken, user: userData, tenant: tenantData } = response.data;
+
+ localStorage.setItem('accessToken', accessToken);
+ localStorage.setItem('refreshToken', refreshToken);
+ localStorage.setItem('user', JSON.stringify(userData));
+ localStorage.setItem('tenant', JSON.stringify(tenantData));
+
+ setUser(userData);
+ setTenant(tenantData);
+ };
+
+ const logout = () => {
+ localStorage.removeItem('accessToken');
+ localStorage.removeItem('refreshToken');
+ localStorage.removeItem('user');
+ localStorage.removeItem('tenant');
+ setUser(null);
+ setTenant(null);
+ };
+
+ return (
+
+ {children}
+
+ );
+}
+
+export function useAuth() {
+ const context = useContext(AuthContext);
+ if (context === undefined) {
+ throw new Error('useAuth must be used within an AuthProvider');
+ }
+ return context;
+}
diff --git a/src/index.css b/src/index.css
new file mode 100644
index 0000000..c48d4d6
--- /dev/null
+++ b/src/index.css
@@ -0,0 +1,53 @@
+@import "tailwindcss";
+
+@theme {
+ --color-primary-50: #fff7ed;
+ --color-primary-100: #ffedd5;
+ --color-primary-200: #fed7aa;
+ --color-primary-300: #fdba74;
+ --color-primary-400: #fb923c;
+ --color-primary-500: #f97316;
+ --color-primary-600: #ea580c;
+ --color-primary-700: #c2410c;
+ --color-primary-800: #9a3412;
+ --color-primary-900: #7c2d12;
+
+ --color-secondary-50: #f0fdf4;
+ --color-secondary-100: #dcfce7;
+ --color-secondary-200: #bbf7d0;
+ --color-secondary-300: #86efac;
+ --color-secondary-400: #4ade80;
+ --color-secondary-500: #22c55e;
+ --color-secondary-600: #16a34a;
+ --color-secondary-700: #15803d;
+ --color-secondary-800: #166534;
+ --color-secondary-900: #14532d;
+}
+
+@layer base {
+ body {
+ @apply bg-gray-50 text-gray-900;
+ }
+}
+
+@layer components {
+ .btn-primary {
+ @apply bg-primary-500 hover:bg-primary-600 text-white font-medium py-2 px-4 rounded-lg transition-colors;
+ }
+
+ .btn-secondary {
+ @apply bg-secondary-500 hover:bg-secondary-600 text-white font-medium py-2 px-4 rounded-lg transition-colors;
+ }
+
+ .btn-outline {
+ @apply border border-gray-300 hover:border-gray-400 text-gray-700 font-medium py-2 px-4 rounded-lg transition-colors;
+ }
+
+ .card {
+ @apply bg-white rounded-xl shadow-sm border border-gray-200 p-4;
+ }
+
+ .input {
+ @apply w-full rounded-lg border-gray-300 shadow-sm focus:border-primary-500 focus:ring-primary-500;
+ }
+}
diff --git a/src/lib/api.ts b/src/lib/api.ts
new file mode 100644
index 0000000..65ac10f
--- /dev/null
+++ b/src/lib/api.ts
@@ -0,0 +1,216 @@
+import axios, { AxiosError } from 'axios';
+
+// Use proxy in development (vite will forward to backend)
+// In production, use VITE_API_URL
+const API_BASE = import.meta.env.VITE_API_URL || '';
+const API_PREFIX = import.meta.env.VITE_API_PREFIX || '/api/v1';
+
+export const api = axios.create({
+ baseURL: `${API_BASE}${API_PREFIX}`,
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+});
+
+// Add auth interceptor - attach token to requests
+api.interceptors.request.use((config) => {
+ const token = localStorage.getItem('accessToken');
+ if (token) {
+ config.headers.Authorization = `Bearer ${token}`;
+ }
+ return config;
+});
+
+// Response interceptor - handle 401 errors
+api.interceptors.response.use(
+ (response) => response,
+ async (error: AxiosError) => {
+ if (error.response?.status === 401) {
+ // Try refresh token
+ const refreshToken = localStorage.getItem('refreshToken');
+ if (refreshToken) {
+ try {
+ const response = await axios.post(`${API_BASE}${API_PREFIX}/auth/refresh`, {
+ refreshToken,
+ });
+ const { accessToken, refreshToken: newRefresh } = response.data;
+ localStorage.setItem('accessToken', accessToken);
+ localStorage.setItem('refreshToken', newRefresh);
+
+ // Retry original request
+ if (error.config) {
+ error.config.headers.Authorization = `Bearer ${accessToken}`;
+ return axios(error.config);
+ }
+ } catch {
+ // Refresh failed, clear tokens and redirect to login
+ localStorage.removeItem('accessToken');
+ localStorage.removeItem('refreshToken');
+ localStorage.removeItem('user');
+ localStorage.removeItem('tenant');
+ window.location.href = '/login';
+ }
+ } else {
+ // No refresh token, redirect to login
+ localStorage.removeItem('accessToken');
+ localStorage.removeItem('user');
+ localStorage.removeItem('tenant');
+ window.location.href = '/login';
+ }
+ }
+ return Promise.reject(error);
+ }
+);
+
+// Auth API
+export const authApi = {
+ register: (data: {
+ name: string;
+ ownerName: string;
+ businessType: string;
+ phone: string;
+ pin: string;
+ email?: string;
+ whatsapp?: string;
+ }) => api.post('/auth/register', data),
+
+ login: (data: { phone: string; pin: string }) =>
+ api.post('/auth/login', data),
+
+ refresh: (refreshToken: string) =>
+ api.post('/auth/refresh', { refreshToken }),
+
+ changePin: (data: { currentPin: string; newPin: string }) =>
+ api.post('/auth/change-pin', data),
+};
+
+// Products API
+export const productsApi = {
+ getAll: (params?: { category?: string; search?: string }) =>
+ api.get('/products', { params }),
+ getById: (id: string) => api.get(`/products/${id}`),
+ create: (data: any) => api.post('/products', data),
+ update: (id: string, data: any) => api.patch(`/products/${id}`, data),
+ delete: (id: string) => api.delete(`/products/${id}`),
+};
+
+// Orders API
+export const ordersApi = {
+ getAll: (params?: { status?: string; date?: string }) =>
+ api.get('/orders', { params }),
+ getById: (id: string) => api.get(`/orders/${id}`),
+ create: (data: any) => api.post('/orders', data),
+ updateStatus: (id: string, status: string) =>
+ api.patch(`/orders/${id}/status`, { status }),
+};
+
+// Customers API
+export const customersApi = {
+ getAll: (params?: { search?: string }) =>
+ api.get('/customers', { params }),
+ getById: (id: string) => api.get(`/customers/${id}`),
+ create: (data: any) => api.post('/customers', data),
+ update: (id: string, data: any) => api.patch(`/customers/${id}`, data),
+ getFiado: (id: string) => api.get(`/customers/${id}/fiado`),
+};
+
+// Inventory API
+export const inventoryApi = {
+ getMovements: (params?: { productId?: string; type?: string }) =>
+ api.get('/inventory/movements', { params }),
+ createMovement: (data: any) => api.post('/inventory/movements', data),
+ getLowStock: () => api.get('/inventory/low-stock'),
+ getAlerts: () => api.get('/inventory/alerts'),
+};
+
+// Dashboard API
+export const dashboardApi = {
+ getStats: () => api.get('/dashboard/stats'),
+ getSalesChart: (period: string) => api.get('/dashboard/sales', { params: { period } }),
+ getTopProducts: () => api.get('/dashboard/top-products'),
+};
+
+// Referrals API
+export const referralsApi = {
+ getMyCode: () => api.get('/referrals/my-code'),
+ generateCode: () => api.post('/referrals/generate-code'),
+ validateCode: (code: string) => api.get(`/referrals/validate/${code}`),
+ applyCode: (code: string) => api.post('/referrals/apply-code', { code }),
+ getMyReferrals: () => api.get('/referrals/list'),
+ getStats: () => api.get('/referrals/stats'),
+ getRewards: () => api.get('/referrals/rewards'),
+ getAvailableMonths: () => api.get('/referrals/rewards/available-months'),
+ getDiscount: () => api.get('/referrals/discount'),
+};
+
+// CoDi/SPEI API
+export const codiSpeiApi = {
+ // CoDi
+ generateQr: (data: { amount: number; description?: string; saleId?: string }) =>
+ api.post('/codi/generate-qr', data),
+ getCodiStatus: (id: string) => api.get(`/codi/status/${id}`),
+ getCodiTransactions: (limit?: number) =>
+ api.get('/codi/transactions', { params: { limit } }),
+
+ // SPEI
+ getClabe: () => api.get('/spei/clabe'),
+ createClabe: (beneficiaryName: string) =>
+ api.post('/spei/create-clabe', { beneficiaryName }),
+ getSpeiTransactions: (limit?: number) =>
+ api.get('/spei/transactions', { params: { limit } }),
+
+ // Summary
+ getSummary: (date?: string) =>
+ api.get('/payments/summary', { params: { date } }),
+};
+
+// Invoices API (SAT/CFDI)
+export const invoicesApi = {
+ // Tax Config
+ getTaxConfig: () => api.get('/invoices/tax-config'),
+ saveTaxConfig: (data: any) => api.post('/invoices/tax-config', data),
+
+ // Invoices
+ getAll: (params?: { status?: string; from?: string; to?: string; limit?: number }) =>
+ api.get('/invoices', { params }),
+ getById: (id: string) => api.get(`/invoices/${id}`),
+ create: (data: any) => api.post('/invoices', data),
+ stamp: (id: string) => api.post(`/invoices/${id}/stamp`),
+ cancel: (id: string, reason: string, uuidReplacement?: string) =>
+ api.post(`/invoices/${id}/cancel`, { reason, uuidReplacement }),
+ send: (id: string, email?: string) =>
+ api.post(`/invoices/${id}/send`, { email }),
+ getSummary: (month?: string) =>
+ api.get('/invoices/summary', { params: { month } }),
+};
+
+// Marketplace API
+export const marketplaceApi = {
+ // Suppliers
+ getSuppliers: (params?: { category?: string; zipCode?: string; search?: string; limit?: number }) =>
+ api.get('/marketplace/suppliers', { params }),
+ getSupplier: (id: string) => api.get(`/marketplace/suppliers/${id}`),
+ getSupplierProducts: (id: string, params?: { category?: string; search?: string }) =>
+ api.get(`/marketplace/suppliers/${id}/products`, { params }),
+ getSupplierReviews: (id: string, params?: { limit?: number }) =>
+ api.get(`/marketplace/suppliers/${id}/reviews`, { params }),
+
+ // Orders
+ createOrder: (data: any) => api.post('/marketplace/orders', data),
+ getOrders: (params?: { status?: string; supplierId?: string; limit?: number }) =>
+ api.get('/marketplace/orders', { params }),
+ getOrder: (id: string) => api.get(`/marketplace/orders/${id}`),
+ cancelOrder: (id: string, reason: string) =>
+ api.put(`/marketplace/orders/${id}/cancel`, { reason }),
+
+ // Reviews
+ createReview: (data: any) => api.post('/marketplace/reviews', data),
+
+ // Favorites
+ getFavorites: () => api.get('/marketplace/favorites'),
+ addFavorite: (supplierId: string) => api.post(`/marketplace/favorites/${supplierId}`),
+ removeFavorite: (supplierId: string) => api.delete(`/marketplace/favorites/${supplierId}`),
+
+ // Stats
+ getStats: () => api.get('/marketplace/stats'),
+};
diff --git a/src/lib/i18n.ts b/src/lib/i18n.ts
new file mode 100644
index 0000000..d79338b
--- /dev/null
+++ b/src/lib/i18n.ts
@@ -0,0 +1,72 @@
+import i18n from 'i18next';
+import { initReactI18next } from 'react-i18next';
+import LanguageDetector from 'i18next-browser-languagedetector';
+
+// Import translations
+import esMX from '../locales/es-MX';
+import esCO from '../locales/es-CO';
+import esAR from '../locales/es-AR';
+import ptBR from '../locales/pt-BR';
+
+const resources = {
+ 'es-MX': { translation: esMX },
+ 'es-CO': { translation: esCO },
+ 'es-AR': { translation: esAR },
+ 'pt-BR': { translation: ptBR },
+};
+
+i18n
+ .use(LanguageDetector)
+ .use(initReactI18next)
+ .init({
+ resources,
+ fallbackLng: 'es-MX',
+ defaultNS: 'translation',
+ interpolation: {
+ escapeValue: false,
+ },
+ detection: {
+ order: ['localStorage', 'navigator'],
+ caches: ['localStorage'],
+ },
+ });
+
+export default i18n;
+
+// Currency formatting by locale
+export const currencyConfig: Record = {
+ 'es-MX': { currency: 'MXN', symbol: '$' },
+ 'es-CO': { currency: 'COP', symbol: '$' },
+ 'es-AR': { currency: 'ARS', symbol: '$' },
+ 'pt-BR': { currency: 'BRL', symbol: 'R$' },
+};
+
+export function formatCurrency(amount: number, locale: string = 'es-MX'): string {
+ const config = currencyConfig[locale] || currencyConfig['es-MX'];
+ return new Intl.NumberFormat(locale, {
+ style: 'currency',
+ currency: config.currency,
+ minimumFractionDigits: 2,
+ maximumFractionDigits: 2,
+ }).format(amount);
+}
+
+export function formatDate(date: Date | string, locale: string = 'es-MX'): string {
+ const d = typeof date === 'string' ? new Date(date) : date;
+ return new Intl.DateTimeFormat(locale, {
+ day: 'numeric',
+ month: 'short',
+ year: 'numeric',
+ }).format(d);
+}
+
+export function formatDateTime(date: Date | string, locale: string = 'es-MX'): string {
+ const d = typeof date === 'string' ? new Date(date) : date;
+ return new Intl.DateTimeFormat(locale, {
+ day: 'numeric',
+ month: 'short',
+ year: 'numeric',
+ hour: '2-digit',
+ minute: '2-digit',
+ }).format(d);
+}
diff --git a/src/locales/es-AR/index.ts b/src/locales/es-AR/index.ts
new file mode 100644
index 0000000..ed36f71
--- /dev/null
+++ b/src/locales/es-AR/index.ts
@@ -0,0 +1,63 @@
+import esMX from '../es-MX';
+
+// Argentina Spanish - Override specific terms
+const esAR = {
+ ...esMX,
+
+ // Navigation overrides
+ nav: {
+ ...esMX.nav,
+ // Same in Argentina
+ },
+
+ // Auth overrides
+ auth: {
+ ...esMX.auth,
+ phone: 'Celular',
+ enterPhone: 'Ingresa tu celular',
+ },
+
+ // Dashboard overrides
+ dashboard: {
+ ...esMX.dashboard,
+ // Same in Argentina
+ },
+
+ // Products overrides - Argentine terminology
+ products: {
+ ...esMX.products,
+ subtitle: 'Administra tu stock',
+ stock: 'Stock',
+ },
+
+ // Fiado overrides - Argentine terminology
+ fiado: {
+ ...esMX.fiado,
+ title: 'Cuenta',
+ subtitle: 'Control de cuentas corrientes',
+ registerPayment: 'Registrar pago',
+ },
+
+ // Payments overrides
+ payments: {
+ ...esMX.payments,
+ fiado: 'Cuenta corriente',
+ transfer: 'Transferencia',
+ change: 'Vuelto',
+ },
+
+ // Business Types - Argentine terminology
+ businessTypes: {
+ tienda: 'Almacen',
+ papeleria: 'Libreria',
+ farmacia: 'Farmacia',
+ ferreteria: 'Ferreteria',
+ carniceria: 'Carniceria',
+ verduleria: 'Verduleria',
+ panaderia: 'Panaderia',
+ tortilleria: 'Rotiseria',
+ otro: 'Otro',
+ },
+};
+
+export default esAR;
diff --git a/src/locales/es-CO/index.ts b/src/locales/es-CO/index.ts
new file mode 100644
index 0000000..0e375b0
--- /dev/null
+++ b/src/locales/es-CO/index.ts
@@ -0,0 +1,60 @@
+import esMX from '../es-MX';
+
+// Colombia Spanish - Override specific terms
+const esCO = {
+ ...esMX,
+
+ // Navigation overrides
+ nav: {
+ ...esMX.nav,
+ // Same in Colombia
+ },
+
+ // Auth overrides
+ auth: {
+ ...esMX.auth,
+ phone: 'Celular',
+ enterPhone: 'Ingresa tu celular',
+ },
+
+ // Dashboard overrides
+ dashboard: {
+ ...esMX.dashboard,
+ // Same in Colombia
+ },
+
+ // Products overrides - Colombian terminology
+ products: {
+ ...esMX.products,
+ subtitle: 'Administra tu inventario',
+ },
+
+ // Fiado overrides
+ fiado: {
+ ...esMX.fiado,
+ title: 'Credito',
+ subtitle: 'Control de creditos a clientes',
+ },
+
+ // Payments overrides
+ payments: {
+ ...esMX.payments,
+ fiado: 'Credito',
+ transfer: 'Transferencia bancaria',
+ },
+
+ // Business Types - Colombian terminology
+ businessTypes: {
+ tienda: 'Tienda',
+ papeleria: 'Papeleria',
+ farmacia: 'Drogueria',
+ ferreteria: 'Ferreteria',
+ carniceria: 'Carniceria',
+ verduleria: 'Fruteria',
+ panaderia: 'Panaderia',
+ tortilleria: 'Areperia',
+ otro: 'Otro',
+ },
+};
+
+export default esCO;
diff --git a/src/locales/es-MX/index.ts b/src/locales/es-MX/index.ts
new file mode 100644
index 0000000..d5dab5d
--- /dev/null
+++ b/src/locales/es-MX/index.ts
@@ -0,0 +1,231 @@
+export default {
+ // Common
+ common: {
+ save: 'Guardar',
+ cancel: 'Cancelar',
+ delete: 'Eliminar',
+ edit: 'Editar',
+ add: 'Agregar',
+ search: 'Buscar',
+ loading: 'Cargando...',
+ error: 'Error',
+ success: 'Exito',
+ confirm: 'Confirmar',
+ back: 'Volver',
+ next: 'Siguiente',
+ close: 'Cerrar',
+ yes: 'Si',
+ no: 'No',
+ all: 'Todos',
+ none: 'Ninguno',
+ required: 'Requerido',
+ optional: 'Opcional',
+ },
+
+ // Navigation
+ nav: {
+ dashboard: 'Dashboard',
+ products: 'Productos',
+ orders: 'Pedidos',
+ customers: 'Clientes',
+ fiado: 'Fiado',
+ inventory: 'Inventario',
+ referrals: 'Referidos',
+ settings: 'Ajustes',
+ logout: 'Cerrar sesion',
+ },
+
+ // Auth
+ auth: {
+ login: 'Iniciar sesion',
+ register: 'Registrarse',
+ phone: 'Telefono',
+ pin: 'PIN',
+ enterPhone: 'Ingresa tu telefono',
+ enterPin: 'Ingresa tu PIN',
+ forgotPin: 'Olvide mi PIN',
+ noAccount: 'No tienes cuenta?',
+ hasAccount: 'Ya tienes cuenta?',
+ createAccount: 'Crear cuenta',
+ businessName: 'Nombre del negocio',
+ ownerName: 'Tu nombre',
+ businessType: 'Tipo de negocio',
+ },
+
+ // Dashboard
+ dashboard: {
+ title: 'Dashboard',
+ todaySales: 'Ventas de hoy',
+ weekSales: 'Ventas de la semana',
+ monthSales: 'Ventas del mes',
+ transactions: 'transacciones',
+ lowStock: 'Stock bajo',
+ pendingOrders: 'Pedidos pendientes',
+ pendingCredits: 'Fiados pendientes',
+ topProducts: 'Productos mas vendidos',
+ },
+
+ // Products
+ products: {
+ title: 'Productos',
+ subtitle: 'Administra tu catalogo',
+ addProduct: 'Agregar producto',
+ editProduct: 'Editar producto',
+ name: 'Nombre',
+ price: 'Precio',
+ cost: 'Costo',
+ stock: 'Stock',
+ sku: 'SKU',
+ barcode: 'Codigo de barras',
+ category: 'Categoria',
+ description: 'Descripcion',
+ noProducts: 'No hay productos',
+ scanBarcode: 'Escanear codigo',
+ },
+
+ // Orders
+ orders: {
+ title: 'Pedidos',
+ subtitle: 'Gestiona los pedidos',
+ newOrder: 'Nuevo pedido',
+ orderNumber: 'Pedido #{{number}}',
+ status: 'Estado',
+ pending: 'Pendiente',
+ preparing: 'Preparando',
+ ready: 'Listo',
+ delivered: 'Entregado',
+ cancelled: 'Cancelado',
+ total: 'Total',
+ items: 'articulos',
+ noOrders: 'No hay pedidos',
+ },
+
+ // Customers
+ customers: {
+ title: 'Clientes',
+ subtitle: 'Administra tus clientes',
+ addCustomer: 'Agregar cliente',
+ name: 'Nombre',
+ phone: 'Telefono',
+ email: 'Correo',
+ creditLimit: 'Limite de credito',
+ currentBalance: 'Saldo actual',
+ noCustomers: 'No hay clientes',
+ },
+
+ // Fiado (Credit)
+ fiado: {
+ title: 'Fiado',
+ subtitle: 'Control de creditos',
+ totalOwed: 'Total adeudado',
+ overdueAmount: 'Vencido',
+ customersWithCredit: 'Clientes con fiado',
+ registerPayment: 'Registrar abono',
+ paymentHistory: 'Historial de pagos',
+ dueDate: 'Fecha de vencimiento',
+ overdue: 'Vencido',
+ noCredits: 'No hay fiados pendientes',
+ },
+
+ // Inventory
+ inventory: {
+ title: 'Inventario',
+ subtitle: 'Control de existencias',
+ movements: 'Movimientos',
+ addMovement: 'Agregar movimiento',
+ entry: 'Entrada',
+ exit: 'Salida',
+ adjustment: 'Ajuste',
+ lowStockAlerts: 'Alertas de stock bajo',
+ reorder: 'Reabastecer',
+ noMovements: 'No hay movimientos',
+ },
+
+ // Referrals
+ referrals: {
+ title: 'Programa de Referidos',
+ subtitle: 'Invita amigos y gana',
+ yourCode: 'Tu codigo de referido',
+ copy: 'Copiar',
+ share: 'Compartir',
+ shareWhatsApp: 'Compartir por WhatsApp',
+ invited: 'Invitados',
+ converted: 'Convertidos',
+ monthsEarned: 'Meses ganados',
+ monthsAvailable: 'Disponibles',
+ howItWorks: 'Como funciona',
+ step1: 'Comparte tu codigo',
+ step1Desc: 'Envia tu codigo a amigos por WhatsApp',
+ step2: 'Tu amigo se registra',
+ step2Desc: 'Obtiene 50% de descuento en su primer mes',
+ step3: 'Tu ganas 1 mes gratis',
+ step3Desc: 'Cuando tu amigo paga su primer mes',
+ yourReferrals: 'Tus referidos',
+ noReferrals: 'Aun no tienes referidos',
+ },
+
+ // Settings
+ settings: {
+ title: 'Ajustes',
+ subtitle: 'Configura tu tienda',
+ businessInfo: 'Informacion del negocio',
+ fiadoSettings: 'Configuracion de fiado',
+ whatsapp: 'WhatsApp Business',
+ notifications: 'Notificaciones',
+ subscription: 'Suscripcion',
+ language: 'Idioma',
+ enableFiado: 'Habilitar fiado',
+ defaultCreditLimit: 'Limite de credito por defecto',
+ gracePeriod: 'Dias de gracia',
+ connected: 'Conectado',
+ autoResponses: 'Respuestas automaticas',
+ lowStockAlerts: 'Alertas de stock bajo',
+ overdueAlerts: 'Alertas de fiados vencidos',
+ newOrderAlerts: 'Alertas de nuevos pedidos',
+ currentPlan: 'Plan actual',
+ upgradePlan: 'Mejorar plan',
+ },
+
+ // Payments
+ payments: {
+ cash: 'Efectivo',
+ card: 'Tarjeta',
+ transfer: 'Transferencia',
+ fiado: 'Fiado',
+ codi: 'CoDi',
+ spei: 'SPEI',
+ change: 'Cambio',
+ total: 'Total',
+ subtotal: 'Subtotal',
+ tax: 'IVA',
+ discount: 'Descuento',
+ payNow: 'Pagar ahora',
+ generateQR: 'Generar QR',
+ scanQR: 'Escanea el QR con tu app bancaria',
+ paymentReceived: 'Pago recibido',
+ paymentFailed: 'Pago fallido',
+ },
+
+ // Errors
+ errors: {
+ generic: 'Algo salio mal',
+ networkError: 'Error de conexion',
+ unauthorized: 'No autorizado',
+ notFound: 'No encontrado',
+ validationError: 'Error de validacion',
+ serverError: 'Error del servidor',
+ },
+
+ // Business Types
+ businessTypes: {
+ tienda: 'Tienda de abarrotes',
+ papeleria: 'Papeleria',
+ farmacia: 'Farmacia',
+ ferreteria: 'Ferreteria',
+ carniceria: 'Carniceria',
+ verduleria: 'Verduleria',
+ panaderia: 'Panaderia',
+ tortilleria: 'Tortilleria',
+ otro: 'Otro',
+ },
+};
diff --git a/src/locales/pt-BR/index.ts b/src/locales/pt-BR/index.ts
new file mode 100644
index 0000000..708e3f2
--- /dev/null
+++ b/src/locales/pt-BR/index.ts
@@ -0,0 +1,234 @@
+// Brazilian Portuguese
+const ptBR = {
+ // Common
+ common: {
+ save: 'Salvar',
+ cancel: 'Cancelar',
+ delete: 'Excluir',
+ edit: 'Editar',
+ add: 'Adicionar',
+ search: 'Buscar',
+ loading: 'Carregando...',
+ error: 'Erro',
+ success: 'Sucesso',
+ confirm: 'Confirmar',
+ back: 'Voltar',
+ next: 'Proximo',
+ close: 'Fechar',
+ yes: 'Sim',
+ no: 'Nao',
+ all: 'Todos',
+ none: 'Nenhum',
+ required: 'Obrigatorio',
+ optional: 'Opcional',
+ },
+
+ // Navigation
+ nav: {
+ dashboard: 'Painel',
+ products: 'Produtos',
+ orders: 'Pedidos',
+ customers: 'Clientes',
+ fiado: 'Fiado',
+ inventory: 'Estoque',
+ referrals: 'Indicacoes',
+ settings: 'Configuracoes',
+ logout: 'Sair',
+ },
+
+ // Auth
+ auth: {
+ login: 'Entrar',
+ register: 'Cadastrar',
+ phone: 'Celular',
+ pin: 'PIN',
+ enterPhone: 'Digite seu celular',
+ enterPin: 'Digite seu PIN',
+ forgotPin: 'Esqueci meu PIN',
+ noAccount: 'Nao tem conta?',
+ hasAccount: 'Ja tem conta?',
+ createAccount: 'Criar conta',
+ businessName: 'Nome do negocio',
+ ownerName: 'Seu nome',
+ businessType: 'Tipo de negocio',
+ },
+
+ // Dashboard
+ dashboard: {
+ title: 'Painel',
+ todaySales: 'Vendas de hoje',
+ weekSales: 'Vendas da semana',
+ monthSales: 'Vendas do mes',
+ transactions: 'transacoes',
+ lowStock: 'Estoque baixo',
+ pendingOrders: 'Pedidos pendentes',
+ pendingCredits: 'Fiados pendentes',
+ topProducts: 'Produtos mais vendidos',
+ },
+
+ // Products
+ products: {
+ title: 'Produtos',
+ subtitle: 'Gerencie seu catalogo',
+ addProduct: 'Adicionar produto',
+ editProduct: 'Editar produto',
+ name: 'Nome',
+ price: 'Preco',
+ cost: 'Custo',
+ stock: 'Estoque',
+ sku: 'SKU',
+ barcode: 'Codigo de barras',
+ category: 'Categoria',
+ description: 'Descricao',
+ noProducts: 'Nenhum produto',
+ scanBarcode: 'Escanear codigo',
+ },
+
+ // Orders
+ orders: {
+ title: 'Pedidos',
+ subtitle: 'Gerencie os pedidos',
+ newOrder: 'Novo pedido',
+ orderNumber: 'Pedido #{{number}}',
+ status: 'Status',
+ pending: 'Pendente',
+ preparing: 'Preparando',
+ ready: 'Pronto',
+ delivered: 'Entregue',
+ cancelled: 'Cancelado',
+ total: 'Total',
+ items: 'itens',
+ noOrders: 'Nenhum pedido',
+ },
+
+ // Customers
+ customers: {
+ title: 'Clientes',
+ subtitle: 'Gerencie seus clientes',
+ addCustomer: 'Adicionar cliente',
+ name: 'Nome',
+ phone: 'Celular',
+ email: 'Email',
+ creditLimit: 'Limite de credito',
+ currentBalance: 'Saldo atual',
+ noCustomers: 'Nenhum cliente',
+ },
+
+ // Fiado (Credit)
+ fiado: {
+ title: 'Fiado',
+ subtitle: 'Controle de creditos',
+ totalOwed: 'Total devido',
+ overdueAmount: 'Vencido',
+ customersWithCredit: 'Clientes com fiado',
+ registerPayment: 'Registrar pagamento',
+ paymentHistory: 'Historico de pagamentos',
+ dueDate: 'Data de vencimento',
+ overdue: 'Vencido',
+ noCredits: 'Nenhum fiado pendente',
+ },
+
+ // Inventory
+ inventory: {
+ title: 'Estoque',
+ subtitle: 'Controle de estoque',
+ movements: 'Movimentacoes',
+ addMovement: 'Adicionar movimentacao',
+ entry: 'Entrada',
+ exit: 'Saida',
+ adjustment: 'Ajuste',
+ lowStockAlerts: 'Alertas de estoque baixo',
+ reorder: 'Repor',
+ noMovements: 'Nenhuma movimentacao',
+ },
+
+ // Referrals
+ referrals: {
+ title: 'Programa de Indicacoes',
+ subtitle: 'Indique amigos e ganhe',
+ yourCode: 'Seu codigo de indicacao',
+ copy: 'Copiar',
+ share: 'Compartilhar',
+ shareWhatsApp: 'Compartilhar por WhatsApp',
+ invited: 'Indicados',
+ converted: 'Convertidos',
+ monthsEarned: 'Meses ganhos',
+ monthsAvailable: 'Disponiveis',
+ howItWorks: 'Como funciona',
+ step1: 'Compartilhe seu codigo',
+ step1Desc: 'Envie seu codigo para amigos por WhatsApp',
+ step2: 'Seu amigo se cadastra',
+ step2Desc: 'Ganha 50% de desconto no primeiro mes',
+ step3: 'Voce ganha 1 mes gratis',
+ step3Desc: 'Quando seu amigo paga o primeiro mes',
+ yourReferrals: 'Suas indicacoes',
+ noReferrals: 'Voce ainda nao tem indicacoes',
+ },
+
+ // Settings
+ settings: {
+ title: 'Configuracoes',
+ subtitle: 'Configure sua loja',
+ businessInfo: 'Informacoes do negocio',
+ fiadoSettings: 'Configuracao de fiado',
+ whatsapp: 'WhatsApp Business',
+ notifications: 'Notificacoes',
+ subscription: 'Assinatura',
+ language: 'Idioma',
+ enableFiado: 'Habilitar fiado',
+ defaultCreditLimit: 'Limite de credito padrao',
+ gracePeriod: 'Dias de carencia',
+ connected: 'Conectado',
+ autoResponses: 'Respostas automaticas',
+ lowStockAlerts: 'Alertas de estoque baixo',
+ overdueAlerts: 'Alertas de fiados vencidos',
+ newOrderAlerts: 'Alertas de novos pedidos',
+ currentPlan: 'Plano atual',
+ upgradePlan: 'Melhorar plano',
+ },
+
+ // Payments
+ payments: {
+ cash: 'Dinheiro',
+ card: 'Cartao',
+ transfer: 'Transferencia',
+ fiado: 'Fiado',
+ codi: 'Pix',
+ spei: 'TED',
+ change: 'Troco',
+ total: 'Total',
+ subtotal: 'Subtotal',
+ tax: 'Impostos',
+ discount: 'Desconto',
+ payNow: 'Pagar agora',
+ generateQR: 'Gerar QR',
+ scanQR: 'Escaneie o QR com seu app do banco',
+ paymentReceived: 'Pagamento recebido',
+ paymentFailed: 'Pagamento falhou',
+ },
+
+ // Errors
+ errors: {
+ generic: 'Algo deu errado',
+ networkError: 'Erro de conexao',
+ unauthorized: 'Nao autorizado',
+ notFound: 'Nao encontrado',
+ validationError: 'Erro de validacao',
+ serverError: 'Erro do servidor',
+ },
+
+ // Business Types
+ businessTypes: {
+ tienda: 'Loja',
+ papeleria: 'Papelaria',
+ farmacia: 'Farmacia',
+ ferreteria: 'Ferragem',
+ carniceria: 'Acougue',
+ verduleria: 'Hortifruti',
+ panaderia: 'Padaria',
+ tortilleria: 'Tapiocaria',
+ otro: 'Outro',
+ },
+};
+
+export default ptBR;
diff --git a/src/main.tsx b/src/main.tsx
new file mode 100644
index 0000000..bef5202
--- /dev/null
+++ b/src/main.tsx
@@ -0,0 +1,10 @@
+import { StrictMode } from 'react'
+import { createRoot } from 'react-dom/client'
+import './index.css'
+import App from './App.tsx'
+
+createRoot(document.getElementById('root')!).render(
+
+
+ ,
+)
diff --git a/src/pages/Customers.tsx b/src/pages/Customers.tsx
new file mode 100644
index 0000000..ba759aa
--- /dev/null
+++ b/src/pages/Customers.tsx
@@ -0,0 +1,134 @@
+import { useState } from 'react';
+import { Search, Plus, Phone, Mail, MapPin, CreditCard } from 'lucide-react';
+
+const mockCustomers = [
+ {
+ id: '1',
+ name: 'Maria Lopez',
+ phone: '5551234567',
+ email: 'maria@email.com',
+ address: 'Calle 1 #123',
+ totalPurchases: 45,
+ totalSpent: 3450.00,
+ fiadoBalance: 150.00,
+ fiadoLimit: 500.00,
+ lastVisit: '2024-01-15',
+ },
+ {
+ id: '2',
+ name: 'Juan Perez',
+ phone: '5559876543',
+ email: null,
+ address: null,
+ totalPurchases: 23,
+ totalSpent: 1890.00,
+ fiadoBalance: 0,
+ fiadoLimit: 300.00,
+ lastVisit: '2024-01-14',
+ },
+ {
+ id: '3',
+ name: 'Ana Garcia',
+ phone: '5555555555',
+ email: 'ana@email.com',
+ address: 'Av. Principal #456',
+ totalPurchases: 67,
+ totalSpent: 5670.00,
+ fiadoBalance: 320.00,
+ fiadoLimit: 1000.00,
+ lastVisit: '2024-01-15',
+ },
+];
+
+export function Customers() {
+ const [search, setSearch] = useState('');
+
+ const filteredCustomers = mockCustomers.filter((customer) =>
+ customer.name.toLowerCase().includes(search.toLowerCase()) ||
+ customer.phone.includes(search)
+ );
+
+ return (
+
+
+
+
Clientes
+
{mockCustomers.length} clientes registrados
+
+
+
+
+ {/* Search */}
+
+
+ setSearch(e.target.value)}
+ className="input pl-10"
+ />
+
+
+ {/* Customers List */}
+
+ {filteredCustomers.map((customer) => (
+
+
+
+
{customer.name}
+
+ {customer.email && (
+
+
+ {customer.email}
+
+ )}
+ {customer.address && (
+
+
+ {customer.address}
+
+ )}
+
+
+
Ultima visita
+
{customer.lastVisit}
+
+
+
+
+
+
Compras
+
{customer.totalPurchases}
+
+
+
Total gastado
+
${customer.totalSpent.toFixed(0)}
+
+
+
Fiado
+
+
0 ? 'text-orange-500' : 'text-green-500'
+ }`} />
+ 0 ? 'text-orange-600' : 'text-green-600'
+ }`}>
+ ${customer.fiadoBalance.toFixed(0)}
+
+
+
+
+
+ ))}
+
+
+ );
+}
diff --git a/src/pages/Dashboard.tsx b/src/pages/Dashboard.tsx
new file mode 100644
index 0000000..477d4e6
--- /dev/null
+++ b/src/pages/Dashboard.tsx
@@ -0,0 +1,131 @@
+import {
+ TrendingUp,
+ ShoppingCart,
+ Users,
+ CreditCard,
+ Package,
+ AlertCircle,
+} from 'lucide-react';
+
+const stats = [
+ { name: 'Ventas Hoy', value: '$1,240', change: '+12%', icon: TrendingUp, color: 'green' },
+ { name: 'Pedidos', value: '23', change: '+5', icon: ShoppingCart, color: 'blue' },
+ { name: 'Clientes', value: '156', change: '+3', icon: Users, color: 'purple' },
+ { name: 'Fiados Pendientes', value: '$2,100', change: '-$450', icon: CreditCard, color: 'orange' },
+];
+
+const recentOrders = [
+ { id: 'MCH-001', customer: 'Maria Lopez', total: 156.00, status: 'ready', time: '10:30 AM' },
+ { id: 'MCH-002', customer: 'Juan Perez', total: 89.50, status: 'preparing', time: '10:45 AM' },
+ { id: 'MCH-003', customer: 'Ana Garcia', total: 234.00, status: 'pending', time: '11:00 AM' },
+];
+
+const lowStockProducts = [
+ { name: 'Coca-Cola 600ml', stock: 5, minStock: 10 },
+ { name: 'Pan Bimbo', stock: 2, minStock: 5 },
+ { name: 'Leche Lala 1L', stock: 3, minStock: 8 },
+];
+
+const statusColors: Record = {
+ pending: 'bg-yellow-100 text-yellow-800',
+ preparing: 'bg-blue-100 text-blue-800',
+ ready: 'bg-green-100 text-green-800',
+ completed: 'bg-gray-100 text-gray-800',
+};
+
+export function Dashboard() {
+ return (
+
+
+
Dashboard
+
Bienvenido a MiChangarrito
+
+
+ {/* Stats Grid */}
+
+ {stats.map((stat) => (
+
+
+
+
{stat.name}
+
{stat.value}
+
+ {stat.change}
+
+
+
+
+
+
+
+ ))}
+
+
+
+ {/* Recent Orders */}
+
+
+
+ {recentOrders.map((order) => (
+
+
+
{order.id}
+
{order.customer}
+
+
+
${order.total.toFixed(2)}
+
+ {order.status}
+
+
+
+ ))}
+
+
+
+ {/* Low Stock Alert */}
+
+
+
+ {lowStockProducts.map((product) => (
+
+
+
+
{product.stock} unidades
+
Min: {product.minStock}
+
+
+ ))}
+
+
+
+
+ );
+}
diff --git a/src/pages/Fiado.tsx b/src/pages/Fiado.tsx
new file mode 100644
index 0000000..19a75c7
--- /dev/null
+++ b/src/pages/Fiado.tsx
@@ -0,0 +1,180 @@
+import { useState } from 'react';
+import { CreditCard, AlertTriangle, CheckCircle, Clock, Plus } from 'lucide-react';
+import clsx from 'clsx';
+
+const mockFiados = [
+ {
+ id: '1',
+ customer: 'Maria Lopez',
+ phone: '5551234567',
+ amount: 150.00,
+ description: 'Compra del 15/01',
+ status: 'pending',
+ dueDate: '2024-01-30',
+ createdAt: '2024-01-15',
+ },
+ {
+ id: '2',
+ customer: 'Ana Garcia',
+ phone: '5555555555',
+ amount: 320.00,
+ description: 'Productos varios',
+ status: 'overdue',
+ dueDate: '2024-01-10',
+ createdAt: '2024-01-01',
+ },
+ {
+ id: '3',
+ customer: 'Pedro Martinez',
+ phone: '5553334444',
+ amount: 89.50,
+ description: 'Bebidas y botanas',
+ status: 'pending',
+ dueDate: '2024-02-01',
+ createdAt: '2024-01-14',
+ },
+];
+
+const recentPayments = [
+ { customer: 'Juan Perez', amount: 200.00, date: '2024-01-15' },
+ { customer: 'Laura Sanchez', amount: 150.00, date: '2024-01-14' },
+ { customer: 'Carlos Ruiz', amount: 75.00, date: '2024-01-13' },
+];
+
+export function Fiado() {
+ const [filter, setFilter] = useState<'all' | 'pending' | 'overdue'>('all');
+
+ const totalPending = mockFiados.reduce((sum, f) => sum + f.amount, 0);
+ const overdueCount = mockFiados.filter(f => f.status === 'overdue').length;
+
+ const filteredFiados = filter === 'all'
+ ? mockFiados
+ : mockFiados.filter(f => f.status === filter);
+
+ return (
+
+
+
+
Fiado
+
Gestiona las cuentas de credito de tus clientes
+
+
+
+
+ {/* Summary Cards */}
+
+
+
+
+
+
Total Pendiente
+
${totalPending.toFixed(2)}
+
+
+
+
+
+
+
+
Vencidos
+
{overdueCount} cuentas
+
+
+
+
+
+
+
+
Cobrado este mes
+
$425.00
+
+
+
+
+
+
+ {/* Fiados List */}
+
+
+ {(['all', 'pending', 'overdue'] as const).map((status) => (
+
+ ))}
+
+
+ {filteredFiados.map((fiado) => (
+
+
+
+
{fiado.customer}
+
{fiado.phone}
+
{fiado.description}
+
+
+
+ ${fiado.amount.toFixed(2)}
+
+
+ {fiado.status === 'overdue' ? (
+
+ ) : (
+
+ )}
+ {fiado.status === 'overdue' ? 'Vencido' : 'Pendiente'}
+
+
+
+
+
+ Creado: {fiado.createdAt}
+ |
+ Vence: {fiado.dueDate}
+
+
+
+
+
+
+
+ ))}
+
+
+ {/* Recent Payments */}
+
+
Pagos Recientes
+
+ {recentPayments.map((payment, i) => (
+
+
+
{payment.customer}
+
{payment.date}
+
+
+${payment.amount.toFixed(2)}
+
+ ))}
+
+
+
+
+ );
+}
diff --git a/src/pages/Inventory.tsx b/src/pages/Inventory.tsx
new file mode 100644
index 0000000..49bb256
--- /dev/null
+++ b/src/pages/Inventory.tsx
@@ -0,0 +1,175 @@
+import { useState } from 'react';
+import { Package, TrendingUp, TrendingDown, AlertTriangle, Plus, Minus } from 'lucide-react';
+import clsx from 'clsx';
+
+const mockInventory = [
+ { id: '1', name: 'Coca-Cola 600ml', category: 'bebidas', stock: 24, minStock: 10, maxStock: 50, cost: 12.00, price: 18.00 },
+ { id: '2', name: 'Sabritas Original', category: 'botanas', stock: 12, minStock: 5, maxStock: 30, cost: 10.00, price: 15.00 },
+ { id: '3', name: 'Leche Lala 1L', category: 'lacteos', stock: 3, minStock: 8, maxStock: 20, cost: 22.00, price: 28.00 },
+ { id: '4', name: 'Pan Bimbo Grande', category: 'panaderia', stock: 2, minStock: 5, maxStock: 15, cost: 35.00, price: 45.00 },
+ { id: '5', name: 'Fabuloso 1L', category: 'limpieza', stock: 6, minStock: 5, maxStock: 20, cost: 25.00, price: 32.00 },
+];
+
+const recentMovements = [
+ { product: 'Coca-Cola 600ml', type: 'sale', quantity: -2, date: '10:30 AM' },
+ { product: 'Sabritas', type: 'sale', quantity: -1, date: '10:15 AM' },
+ { product: 'Leche Lala 1L', type: 'purchase', quantity: +12, date: '09:00 AM' },
+ { product: 'Pan Bimbo', type: 'sale', quantity: -3, date: '08:45 AM' },
+];
+
+export function Inventory() {
+ const [showLowStock, setShowLowStock] = useState(false);
+
+ const lowStockItems = mockInventory.filter(item => item.stock <= item.minStock);
+ const totalValue = mockInventory.reduce((sum, item) => sum + (item.stock * item.cost), 0);
+ const totalItems = mockInventory.reduce((sum, item) => sum + item.stock, 0);
+
+ const displayItems = showLowStock
+ ? lowStockItems
+ : mockInventory;
+
+ return (
+
+
+
+
Inventario
+
Control de existencias
+
+
+
+
+
+
+
+ {/* Summary */}
+
+
+
+
+
+
Total Unidades
+
{totalItems}
+
+
+
+
+
+
+
+
Valor del Inventario
+
${totalValue.toFixed(2)}
+
+
+
+
setShowLowStock(!showLowStock)}
+ >
+
+
+
+
Stock Bajo
+
{lowStockItems.length} productos
+
+
+
+
+
+
+ {/* Inventory Table */}
+
+
+
+
+
+ | Producto |
+ Stock |
+ Min/Max |
+ Valor |
+
+
+
+ {displayItems.map((item) => {
+ const isLow = item.stock <= item.minStock;
+ const percentage = (item.stock / item.maxStock) * 100;
+
+ return (
+
+ |
+ {item.name}
+ {item.category}
+ |
+
+
+ |
+
+ {item.minStock} / {item.maxStock}
+ |
+
+ ${(item.stock * item.cost).toFixed(2)}
+ |
+
+ );
+ })}
+
+
+
+
+
+ {/* Recent Movements */}
+
+
Movimientos Recientes
+
+ {recentMovements.map((mov, i) => (
+
+
+ {mov.type === 'sale' ? (
+
+ ) : (
+
+ )}
+
+
{mov.product}
+
{mov.date}
+
+
+
0 ? 'text-green-600' : 'text-red-600'
+ )}>
+ {mov.quantity > 0 ? '+' : ''}{mov.quantity}
+
+
+ ))}
+
+
+
+
+ );
+}
diff --git a/src/pages/Invoices.tsx b/src/pages/Invoices.tsx
new file mode 100644
index 0000000..a30e90b
--- /dev/null
+++ b/src/pages/Invoices.tsx
@@ -0,0 +1,668 @@
+import { useState } from 'react';
+import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
+import {
+ FileText,
+ Send,
+ XCircle,
+ CheckCircle,
+ Clock,
+ Download,
+ Search,
+ Plus,
+ Settings,
+ AlertCircle,
+} from 'lucide-react';
+import clsx from 'clsx';
+import { invoicesApi } from '../lib/api';
+
+const statusConfig = {
+ draft: { label: 'Borrador', color: 'gray', icon: Clock },
+ pending: { label: 'Pendiente', color: 'yellow', icon: Clock },
+ stamped: { label: 'Timbrada', color: 'green', icon: CheckCircle },
+ sent: { label: 'Enviada', color: 'blue', icon: Send },
+ cancelled: { label: 'Cancelada', color: 'red', icon: XCircle },
+};
+
+export function Invoices() {
+ const [filter, setFilter] = useState('all');
+ const [showConfig, setShowConfig] = useState(false);
+ const [showNewInvoice, setShowNewInvoice] = useState(false);
+ const queryClient = useQueryClient();
+
+ const { data: invoices = [], isLoading } = useQuery({
+ queryKey: ['invoices', filter],
+ queryFn: async () => {
+ const params = filter !== 'all' ? { status: filter } : {};
+ const response = await invoicesApi.getAll(params);
+ return response.data;
+ },
+ });
+
+ const { data: summary } = useQuery({
+ queryKey: ['invoices-summary'],
+ queryFn: async () => {
+ const response = await invoicesApi.getSummary();
+ return response.data;
+ },
+ });
+
+ const { data: taxConfig } = useQuery({
+ queryKey: ['tax-config'],
+ queryFn: async () => {
+ const response = await invoicesApi.getTaxConfig();
+ return response.data;
+ },
+ });
+
+ const stampMutation = useMutation({
+ mutationFn: (id: string) => invoicesApi.stamp(id),
+ onSuccess: () => {
+ queryClient.invalidateQueries({ queryKey: ['invoices'] });
+ },
+ });
+
+ const sendMutation = useMutation({
+ mutationFn: (id: string) => invoicesApi.send(id),
+ onSuccess: () => {
+ queryClient.invalidateQueries({ queryKey: ['invoices'] });
+ },
+ });
+
+ const hasActiveConfig = taxConfig?.status === 'active';
+
+ return (
+
+
+
+
Facturacion
+
Emite facturas electronicas (CFDI)
+
+
+
+
+
+
+
+ {/* Alert if no config */}
+ {!hasActiveConfig && (
+
+
+
+
Configuracion fiscal requerida
+
+ Para emitir facturas, primero configura tus datos fiscales y certificados.
+
+
+
+ )}
+
+ {/* Summary Cards */}
+
+
+
Facturas del mes
+
{summary?.total_invoices || 0}
+
+
+
Monto facturado
+
+ ${(summary?.total_amount || 0).toLocaleString('es-MX', { minimumFractionDigits: 2 })}
+
+
+
+
Canceladas
+
{summary?.total_cancelled || 0}
+
+
+
RFC Emisor
+
{taxConfig?.rfc || 'No configurado'}
+
+
+
+ {/* Status Filter */}
+
+
+ {Object.entries(statusConfig).map(([status, config]) => (
+
+ ))}
+
+
+ {/* Invoices List */}
+ {isLoading ? (
+
+
+
Cargando facturas...
+
+ ) : invoices.length === 0 ? (
+
+
+
No hay facturas
+
Las facturas emitidas apareceran aqui
+
+ ) : (
+
+ {invoices.map((invoice: any) => {
+ const status = statusConfig[invoice.status as keyof typeof statusConfig] || statusConfig.draft;
+ const StatusIcon = status.icon;
+
+ return (
+
+
+
+
+
+
+
+
+
+ {invoice.serie}-{invoice.folio}
+
+
+ {status.label}
+
+
+
{invoice.receptorNombre}
+
{invoice.receptorRfc}
+ {invoice.uuid && (
+
+ UUID: {invoice.uuid}
+
+ )}
+
+
+
+
+
+
+ ${Number(invoice.total).toLocaleString('es-MX', { minimumFractionDigits: 2 })}
+
+
+ {new Date(invoice.createdAt).toLocaleDateString('es-MX')}
+
+
+
+
+ {invoice.status === 'draft' && (
+
+ )}
+ {invoice.status === 'stamped' && (
+
+ )}
+ {(invoice.status === 'stamped' || invoice.status === 'sent') && (
+
+ )}
+
+
+
+
+ );
+ })}
+
+ )}
+
+ {/* Tax Config Modal */}
+ {showConfig && (
+
setShowConfig(false)}
+ onSave={() => {
+ queryClient.invalidateQueries({ queryKey: ['tax-config'] });
+ setShowConfig(false);
+ }}
+ />
+ )}
+
+ {/* New Invoice Modal */}
+ {showNewInvoice && (
+ setShowNewInvoice(false)}
+ onSuccess={() => {
+ queryClient.invalidateQueries({ queryKey: ['invoices'] });
+ setShowNewInvoice(false);
+ }}
+ />
+ )}
+
+ );
+}
+
+function TaxConfigModal({
+ config,
+ onClose,
+ onSave,
+}: {
+ config: any;
+ onClose: () => void;
+ onSave: () => void;
+}) {
+ const [formData, setFormData] = useState({
+ rfc: config?.rfc || '',
+ razonSocial: config?.razonSocial || '',
+ regimenFiscal: config?.regimenFiscal || '601',
+ codigoPostal: config?.codigoPostal || '',
+ serie: config?.serie || 'A',
+ pacProvider: config?.pacProvider || 'facturapi',
+ pacSandbox: config?.pacSandbox ?? true,
+ });
+
+ const mutation = useMutation({
+ mutationFn: (data: any) => invoicesApi.saveTaxConfig(data),
+ onSuccess: onSave,
+ });
+
+ return (
+
+
+
+
Configuracion Fiscal
+
+
+
+
+
+ );
+}
+
+function NewInvoiceModal({
+ onClose,
+ onSuccess,
+}: {
+ onClose: () => void;
+ onSuccess: () => void;
+}) {
+ const [formData, setFormData] = useState({
+ receptorRfc: '',
+ receptorNombre: '',
+ receptorRegimenFiscal: '601',
+ receptorCodigoPostal: '',
+ receptorUsoCfdi: 'G03',
+ receptorEmail: '',
+ formaPago: '01',
+ metodoPago: 'PUE',
+ items: [{ descripcion: '', cantidad: 1, valorUnitario: 0, claveProdServ: '01010101', claveUnidad: 'H87' }],
+ });
+
+ const mutation = useMutation({
+ mutationFn: (data: any) => invoicesApi.create(data),
+ onSuccess,
+ });
+
+ const addItem = () => {
+ setFormData({
+ ...formData,
+ items: [
+ ...formData.items,
+ { descripcion: '', cantidad: 1, valorUnitario: 0, claveProdServ: '01010101', claveUnidad: 'H87' },
+ ],
+ });
+ };
+
+ const updateItem = (index: number, field: string, value: any) => {
+ const newItems = [...formData.items];
+ newItems[index] = { ...newItems[index], [field]: value };
+ setFormData({ ...formData, items: newItems });
+ };
+
+ const removeItem = (index: number) => {
+ if (formData.items.length > 1) {
+ setFormData({
+ ...formData,
+ items: formData.items.filter((_, i) => i !== index),
+ });
+ }
+ };
+
+ const total = formData.items.reduce(
+ (sum, item) => sum + item.cantidad * item.valorUnitario * 1.16,
+ 0
+ );
+
+ return (
+
+
+
+
Nueva Factura
+
+
+
+
+
+ );
+}
diff --git a/src/pages/Login.tsx b/src/pages/Login.tsx
new file mode 100644
index 0000000..cfe8dee
--- /dev/null
+++ b/src/pages/Login.tsx
@@ -0,0 +1,103 @@
+import { useState } from 'react';
+import { useNavigate, Link } from 'react-router-dom';
+import { useAuth } from '../contexts/AuthContext';
+
+export default function Login() {
+ const [phone, setPhone] = useState('');
+ const [pin, setPin] = useState('');
+ const [error, setError] = useState('');
+ const [isLoading, setIsLoading] = useState(false);
+ const { login } = useAuth();
+ const navigate = useNavigate();
+
+ const handleSubmit = async (e: React.FormEvent) => {
+ e.preventDefault();
+ setError('');
+ setIsLoading(true);
+
+ try {
+ await login(phone, pin);
+ navigate('/');
+ } catch (err: any) {
+ setError(err.response?.data?.message || 'Error al iniciar sesion');
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ return (
+
+
+
+
+ MiChangarrito
+
+
+ Iniciar sesion
+
+
+ O{' '}
+
+ registra tu negocio
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/pages/Marketplace.tsx b/src/pages/Marketplace.tsx
new file mode 100644
index 0000000..84d6c61
--- /dev/null
+++ b/src/pages/Marketplace.tsx
@@ -0,0 +1,731 @@
+import { useState } from 'react';
+import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
+import {
+ Store,
+ Search,
+ Star,
+ Heart,
+ ShoppingCart,
+ Package,
+ Truck,
+ Clock,
+ CheckCircle,
+ XCircle,
+ MapPin,
+ Phone,
+ ChevronRight,
+ Filter,
+} from 'lucide-react';
+import clsx from 'clsx';
+import { marketplaceApi } from '../lib/api';
+
+const categories = [
+ { id: 'bebidas', name: 'Bebidas', icon: '🥤' },
+ { id: 'botanas', name: 'Botanas', icon: '🍿' },
+ { id: 'lacteos', name: 'Lacteos', icon: '🥛' },
+ { id: 'pan', name: 'Pan', icon: '🍞' },
+ { id: 'abarrotes', name: 'Abarrotes', icon: '🛒' },
+ { id: 'limpieza', name: 'Limpieza', icon: '🧹' },
+];
+
+const orderStatusConfig = {
+ pending: { label: 'Pendiente', color: 'yellow', icon: Clock },
+ confirmed: { label: 'Confirmado', color: 'blue', icon: CheckCircle },
+ preparing: { label: 'Preparando', color: 'indigo', icon: Package },
+ shipped: { label: 'En camino', color: 'purple', icon: Truck },
+ delivered: { label: 'Entregado', color: 'green', icon: CheckCircle },
+ cancelled: { label: 'Cancelado', color: 'red', icon: XCircle },
+ rejected: { label: 'Rechazado', color: 'red', icon: XCircle },
+};
+
+export function Marketplace() {
+ const [view, setView] = useState<'suppliers' | 'orders' | 'favorites'>('suppliers');
+ const [selectedCategory, setSelectedCategory] = useState(null);
+ const [searchQuery, setSearchQuery] = useState('');
+ const [selectedSupplier, setSelectedSupplier] = useState(null);
+ const [showCart, setShowCart] = useState(false);
+ const [cart, setCart] = useState<{ product: any; quantity: number }[]>([]);
+ const queryClient = useQueryClient();
+
+ const { data: suppliers = [], isLoading: loadingSuppliers } = useQuery({
+ queryKey: ['suppliers', selectedCategory, searchQuery],
+ queryFn: async () => {
+ const response = await marketplaceApi.getSuppliers({
+ category: selectedCategory || undefined,
+ search: searchQuery || undefined,
+ });
+ return response.data;
+ },
+ });
+
+ const { data: orders = [], isLoading: loadingOrders } = useQuery({
+ queryKey: ['marketplace-orders'],
+ queryFn: async () => {
+ const response = await marketplaceApi.getOrders();
+ return response.data;
+ },
+ enabled: view === 'orders',
+ });
+
+ const { data: favorites = [] } = useQuery({
+ queryKey: ['supplier-favorites'],
+ queryFn: async () => {
+ const response = await marketplaceApi.getFavorites();
+ return response.data;
+ },
+ enabled: view === 'favorites',
+ });
+
+ const addToCart = (product: any) => {
+ const existing = cart.find((item) => item.product.id === product.id);
+ if (existing) {
+ setCart(
+ cart.map((item) =>
+ item.product.id === product.id
+ ? { ...item, quantity: item.quantity + 1 }
+ : item
+ )
+ );
+ } else {
+ setCart([...cart, { product, quantity: 1 }]);
+ }
+ };
+
+ const removeFromCart = (productId: string) => {
+ setCart(cart.filter((item) => item.product.id !== productId));
+ };
+
+ const updateCartQuantity = (productId: string, quantity: number) => {
+ if (quantity <= 0) {
+ removeFromCart(productId);
+ } else {
+ setCart(
+ cart.map((item) =>
+ item.product.id === productId ? { ...item, quantity } : item
+ )
+ );
+ }
+ };
+
+ const cartTotal = cart.reduce(
+ (sum, item) => sum + Number(item.product.unitPrice) * item.quantity,
+ 0
+ );
+
+ return (
+
+
+
+
Marketplace
+
Encuentra proveedores para tu negocio
+
+
+ {cart.length > 0 && (
+
+ )}
+
+
+ {/* Tabs */}
+
+
+
+
+
+
+ {view === 'suppliers' && (
+ <>
+ {/* Search & Filter */}
+
+
+
+ setSearchQuery(e.target.value)}
+ placeholder="Buscar proveedores..."
+ className="input pl-10"
+ />
+
+
+
+ {/* Categories */}
+
+
+ {categories.map((category) => (
+
+ ))}
+
+
+ {/* Suppliers Grid */}
+ {loadingSuppliers ? (
+
+
+
Buscando proveedores...
+
+ ) : suppliers.length === 0 ? (
+
+
+
No hay proveedores
+
Pronto habra mas proveedores disponibles
+
+ ) : (
+
+ {suppliers.map((supplier: any) => (
+ setSelectedSupplier(supplier)}
+ />
+ ))}
+
+ )}
+ >
+ )}
+
+ {view === 'orders' && (
+
+ {loadingOrders ? (
+
+ ) : orders.length === 0 ? (
+
+
+
No hay pedidos
+
Tus pedidos a proveedores apareceran aqui
+
+ ) : (
+ orders.map((order: any) => (
+
+ ))
+ )}
+
+ )}
+
+ {view === 'favorites' && (
+
+ {favorites.length === 0 ? (
+
+
+
Sin favoritos
+
Agrega proveedores a tus favoritos
+
+ ) : (
+ favorites.map((supplier: any) => (
+
setSelectedSupplier(supplier)}
+ />
+ ))
+ )}
+
+ )}
+
+ {/* Supplier Detail Modal */}
+ {selectedSupplier && (
+
setSelectedSupplier(null)}
+ onAddToCart={addToCart}
+ cart={cart}
+ />
+ )}
+
+ {/* Cart Modal */}
+ {showCart && (
+ setShowCart(false)}
+ onUpdateQuantity={updateCartQuantity}
+ onRemove={removeFromCart}
+ onOrderSuccess={() => {
+ setCart([]);
+ setShowCart(false);
+ queryClient.invalidateQueries({ queryKey: ['marketplace-orders'] });
+ }}
+ />
+ )}
+
+ );
+}
+
+function SupplierCard({
+ supplier,
+ onClick,
+}: {
+ supplier: any;
+ onClick: () => void;
+}) {
+ return (
+
+
+
+ {supplier.logoUrl ? (
+

+ ) : (
+
+ )}
+
+
+
+
{supplier.name}
+ {supplier.verified && (
+
+ )}
+
+
+
+
+ {Number(supplier.rating).toFixed(1)}
+
+
·
+
{supplier.totalReviews} resenas
+
+
+ {supplier.categories?.slice(0, 3).map((cat: string) => (
+
+ {cat}
+
+ ))}
+
+
+ Min: ${Number(supplier.minOrderAmount).toFixed(0)}
+ {Number(supplier.deliveryFee) > 0 ? (
+ Envio: ${Number(supplier.deliveryFee).toFixed(0)}
+ ) : (
+ Envio gratis
+ )}
+
+
+
+
+
+ );
+}
+
+function OrderCard({ order }: { order: any }) {
+ const status = orderStatusConfig[order.status as keyof typeof orderStatusConfig] || orderStatusConfig.pending;
+ const StatusIcon = status.icon;
+
+ return (
+
+
+
+
+
+
+
+
+
Pedido #{order.orderNumber}
+
+ {status.label}
+
+
+
{order.supplier?.name}
+
+ {order.items?.length} productos
+
+
+
+
+
+
+ ${Number(order.total).toLocaleString('es-MX', { minimumFractionDigits: 2 })}
+
+
+ {new Date(order.createdAt).toLocaleDateString('es-MX')}
+
+
+
+
+ );
+}
+
+function SupplierDetailModal({
+ supplier,
+ onClose,
+ onAddToCart,
+ cart,
+}: {
+ supplier: any;
+ onClose: () => void;
+ onAddToCart: (product: any) => void;
+ cart: { product: any; quantity: number }[];
+}) {
+ const [search, setSearch] = useState('');
+ const queryClient = useQueryClient();
+
+ const { data: products = [], isLoading } = useQuery({
+ queryKey: ['supplier-products', supplier.id, search],
+ queryFn: async () => {
+ const response = await marketplaceApi.getSupplierProducts(supplier.id, {
+ search: search || undefined,
+ });
+ return response.data;
+ },
+ });
+
+ const favoriteMutation = useMutation({
+ mutationFn: () => marketplaceApi.addFavorite(supplier.id),
+ onSuccess: () => {
+ queryClient.invalidateQueries({ queryKey: ['supplier-favorites'] });
+ },
+ });
+
+ const getCartQuantity = (productId: string) => {
+ const item = cart.find((i) => i.product.id === productId);
+ return item?.quantity || 0;
+ };
+
+ return (
+
+
+ {/* Header */}
+
+
+
+ {supplier.logoUrl ? (
+

+ ) : (
+
+ )}
+
+
+
+
{supplier.name}
+
+
+
+
+ {Number(supplier.rating).toFixed(1)}
+ ·
+ {supplier.totalReviews} resenas
+
+ {supplier.address && (
+
+
+ {supplier.city}, {supplier.state}
+
+ )}
+
+
+
+
+
+ {supplier.contactPhone && (
+
+
+ Llamar
+
+ )}
+
+
+
+ {/* Search */}
+
+
+
+ setSearch(e.target.value)}
+ placeholder="Buscar productos..."
+ className="input pl-10"
+ />
+
+
+
+ {/* Products */}
+
+ {isLoading ? (
+
+ ) : products.length === 0 ? (
+
+ No hay productos disponibles
+
+ ) : (
+
+ {products.map((product: any) => {
+ const inCart = getCartQuantity(product.id);
+ return (
+
+
+ {product.imageUrl ? (
+

+ ) : (
+
+ )}
+
+
+
{product.name}
+
+ Min: {product.minQuantity} {product.unitType}
+
+
+
+ ${Number(product.unitPrice).toFixed(2)}
+
+ {inCart > 0 ? (
+
+ {inCart} en carrito
+
+ ) : (
+
+ )}
+
+
+
+ );
+ })}
+
+ )}
+
+
+
+ );
+}
+
+function CartModal({
+ cart,
+ supplier,
+ onClose,
+ onUpdateQuantity,
+ onRemove,
+ onOrderSuccess,
+}: {
+ cart: { product: any; quantity: number }[];
+ supplier: any;
+ onClose: () => void;
+ onUpdateQuantity: (productId: string, quantity: number) => void;
+ onRemove: (productId: string) => void;
+ onOrderSuccess: () => void;
+}) {
+ const [deliveryAddress, setDeliveryAddress] = useState('');
+ const [deliveryPhone, setDeliveryPhone] = useState('');
+ const [notes, setNotes] = useState('');
+
+ const subtotal = cart.reduce(
+ (sum, item) => sum + Number(item.product.unitPrice) * item.quantity,
+ 0
+ );
+
+ const orderMutation = useMutation({
+ mutationFn: (data: any) => marketplaceApi.createOrder(data),
+ onSuccess: onOrderSuccess,
+ });
+
+ const handleSubmit = (e: React.FormEvent) => {
+ e.preventDefault();
+
+ const supplierId = cart[0]?.product?.supplierId;
+ if (!supplierId) return;
+
+ orderMutation.mutate({
+ supplierId,
+ items: cart.map((item) => ({
+ productId: item.product.id,
+ quantity: item.quantity,
+ })),
+ deliveryAddress,
+ deliveryPhone,
+ notes,
+ });
+ };
+
+ return (
+
+
+
+
Tu Carrito
+
+
+
+
+
+
+ );
+}
diff --git a/src/pages/Orders.tsx b/src/pages/Orders.tsx
new file mode 100644
index 0000000..6fea074
--- /dev/null
+++ b/src/pages/Orders.tsx
@@ -0,0 +1,181 @@
+import { useState } from 'react';
+import { Clock, CheckCircle, XCircle, ChefHat, Package } from 'lucide-react';
+import clsx from 'clsx';
+
+const mockOrders = [
+ {
+ id: 'MCH-001',
+ customer: 'Maria Lopez',
+ phone: '5551234567',
+ items: [
+ { name: 'Coca-Cola 600ml', qty: 2, price: 18.00 },
+ { name: 'Sabritas', qty: 3, price: 15.00 },
+ ],
+ total: 81.00,
+ status: 'pending',
+ source: 'whatsapp',
+ createdAt: '2024-01-15T10:30:00',
+ },
+ {
+ id: 'MCH-002',
+ customer: 'Juan Perez',
+ phone: '5559876543',
+ items: [
+ { name: 'Leche Lala 1L', qty: 2, price: 28.00 },
+ { name: 'Pan Bimbo', qty: 1, price: 45.00 },
+ ],
+ total: 101.00,
+ status: 'preparing',
+ source: 'pos',
+ createdAt: '2024-01-15T10:45:00',
+ },
+ {
+ id: 'MCH-003',
+ customer: 'Ana Garcia',
+ phone: '5555555555',
+ items: [
+ { name: 'Fabuloso 1L', qty: 1, price: 32.00 },
+ ],
+ total: 32.00,
+ status: 'ready',
+ source: 'whatsapp',
+ createdAt: '2024-01-15T11:00:00',
+ },
+];
+
+const statusConfig = {
+ pending: { label: 'Pendiente', color: 'yellow', icon: Clock },
+ confirmed: { label: 'Confirmado', color: 'blue', icon: CheckCircle },
+ preparing: { label: 'Preparando', color: 'indigo', icon: ChefHat },
+ ready: { label: 'Listo', color: 'green', icon: Package },
+ completed: { label: 'Completado', color: 'gray', icon: CheckCircle },
+ cancelled: { label: 'Cancelado', color: 'red', icon: XCircle },
+};
+
+const statusFlow = ['pending', 'confirmed', 'preparing', 'ready', 'completed'];
+
+export function Orders() {
+ const [filter, setFilter] = useState('all');
+
+ const filteredOrders = filter === 'all'
+ ? mockOrders
+ : mockOrders.filter(o => o.status === filter);
+
+ const updateStatus = (orderId: string, currentStatus: string) => {
+ const currentIndex = statusFlow.indexOf(currentStatus);
+ if (currentIndex < statusFlow.length - 1) {
+ const nextStatus = statusFlow[currentIndex + 1];
+ console.log(`Updating ${orderId} to ${nextStatus}`);
+ // API call would go here
+ }
+ };
+
+ return (
+
+
+
Pedidos
+
Gestiona los pedidos de tu tienda
+
+
+ {/* Status Tabs */}
+
+
+ {Object.entries(statusConfig).slice(0, 4).map(([status, config]) => {
+ const count = mockOrders.filter(o => o.status === status).length;
+ return (
+
+ );
+ })}
+
+
+ {/* Orders List */}
+
+ {filteredOrders.map((order) => {
+ const status = statusConfig[order.status as keyof typeof statusConfig];
+ const StatusIcon = status.icon;
+
+ return (
+
+
+
+
+
+
+
+
+
{order.id}
+
+ {status.label}
+
+
+ {order.source === 'whatsapp' ? 'WhatsApp' : 'POS'}
+
+
+
{order.customer}
+
{order.phone}
+
+
+
+
+
+ {order.items.map((item, i) => (
+
+ {item.qty}x {item.name}
+ {i < order.items.length - 1 ? ', ' : ''}
+
+ ))}
+
+
+
+
+
+
${order.total.toFixed(2)}
+
+ {new Date(order.createdAt).toLocaleTimeString('es-MX', {
+ hour: '2-digit',
+ minute: '2-digit',
+ })}
+
+
+
+ {order.status !== 'completed' && order.status !== 'cancelled' && (
+
+ )}
+
+
+
+ );
+ })}
+
+
+ );
+}
diff --git a/src/pages/Products.tsx b/src/pages/Products.tsx
new file mode 100644
index 0000000..8014c11
--- /dev/null
+++ b/src/pages/Products.tsx
@@ -0,0 +1,103 @@
+import { useState } from 'react';
+import { Search, Plus, Edit, Trash2, Package } from 'lucide-react';
+
+const mockProducts = [
+ { id: '1', name: 'Coca-Cola 600ml', category: 'bebidas', price: 18.00, stock: 24, barcode: '7501055300051' },
+ { id: '2', name: 'Sabritas Original', category: 'botanas', price: 15.00, stock: 12, barcode: '7501000111111' },
+ { id: '3', name: 'Leche Lala 1L', category: 'lacteos', price: 28.00, stock: 8, barcode: '7501020500001' },
+ { id: '4', name: 'Pan Bimbo Grande', category: 'panaderia', price: 45.00, stock: 5, barcode: '7501030400001' },
+ { id: '5', name: 'Fabuloso 1L', category: 'limpieza', price: 32.00, stock: 6, barcode: '7501040300001' },
+ { id: '6', name: 'Pepsi 600ml', category: 'bebidas', price: 17.00, stock: 20, barcode: '7501055300052' },
+];
+
+const categories = [
+ { id: 'all', name: 'Todos' },
+ { id: 'bebidas', name: 'Bebidas' },
+ { id: 'botanas', name: 'Botanas' },
+ { id: 'lacteos', name: 'Lacteos' },
+ { id: 'panaderia', name: 'Panaderia' },
+ { id: 'limpieza', name: 'Limpieza' },
+];
+
+export function Products() {
+ const [search, setSearch] = useState('');
+ const [category, setCategory] = useState('all');
+
+ const filteredProducts = mockProducts.filter((product) => {
+ const matchesSearch = product.name.toLowerCase().includes(search.toLowerCase());
+ const matchesCategory = category === 'all' || product.category === category;
+ return matchesSearch && matchesCategory;
+ });
+
+ return (
+
+
+
+
Productos
+
{mockProducts.length} productos en catalogo
+
+
+
+
+ {/* Filters */}
+
+
+
+ setSearch(e.target.value)}
+ className="input pl-10"
+ />
+
+
+
+
+ {/* Products Grid */}
+
+ {filteredProducts.map((product) => (
+
+
+
{product.name}
+
{product.category}
+
+
+ ${product.price.toFixed(2)}
+
+ 10 ? 'text-green-600' : product.stock > 5 ? 'text-yellow-600' : 'text-red-600'
+ }`}>
+ {product.stock} en stock
+
+
+
{product.barcode}
+
+ ))}
+
+
+ );
+}
diff --git a/src/pages/Referrals.tsx b/src/pages/Referrals.tsx
new file mode 100644
index 0000000..883449c
--- /dev/null
+++ b/src/pages/Referrals.tsx
@@ -0,0 +1,276 @@
+import { useState } from 'react';
+import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
+import { Gift, Users, Copy, Share2, Check, Clock, X } from 'lucide-react';
+import { referralsApi } from '../lib/api';
+
+interface ReferralStats {
+ code: string;
+ totalInvited: number;
+ totalConverted: number;
+ totalPending: number;
+ totalExpired: number;
+ monthsEarned: number;
+ monthsAvailable: number;
+}
+
+interface Referral {
+ id: string;
+ referredTenantId: string;
+ codeUsed: string;
+ status: 'pending' | 'converted' | 'rewarded' | 'expired';
+ createdAt: string;
+ convertedAt?: string;
+}
+
+export function Referrals() {
+ const [copied, setCopied] = useState(false);
+ const queryClient = useQueryClient();
+
+ const { data: stats, isLoading } = useQuery({
+ queryKey: ['referral-stats'],
+ queryFn: async () => {
+ const res = await referralsApi.getStats();
+ return res.data;
+ },
+ });
+
+ const { data: referrals } = useQuery({
+ queryKey: ['referrals'],
+ queryFn: async () => {
+ const res = await referralsApi.getMyReferrals();
+ return res.data;
+ },
+ });
+
+ const generateCodeMutation = useMutation({
+ mutationFn: () => referralsApi.generateCode(),
+ onSuccess: () => {
+ queryClient.invalidateQueries({ queryKey: ['referral-stats'] });
+ },
+ });
+
+ const copyCode = async () => {
+ if (stats?.code) {
+ await navigator.clipboard.writeText(stats.code);
+ setCopied(true);
+ setTimeout(() => setCopied(false), 2000);
+ }
+ };
+
+ const shareWhatsApp = () => {
+ if (stats?.code) {
+ const text = `Usa mi codigo ${stats.code} para registrarte en MiChangarrito y obtener 50% de descuento en tu primer mes! https://michangarrito.com/r/${stats.code}`;
+ window.open(`https://wa.me/?text=${encodeURIComponent(text)}`, '_blank');
+ }
+ };
+
+ const getStatusBadge = (status: string) => {
+ switch (status) {
+ case 'pending':
+ return (
+
+
+ Pendiente
+
+ );
+ case 'converted':
+ case 'rewarded':
+ return (
+
+
+ Convertido
+
+ );
+ case 'expired':
+ return (
+
+
+ Expirado
+
+ );
+ default:
+ return null;
+ }
+ };
+
+ if (isLoading) {
+ return (
+
+ );
+ }
+
+ return (
+
+
+
Programa de Referidos
+
Invita amigos y gana meses gratis
+
+
+ {/* Share Card */}
+
+
+
+
+
Invita amigos y gana
+
Por cada amigo que se suscriba, ganas 1 mes gratis
+
+
+
+
+
Tu codigo de referido:
+
+ {stats?.code || 'Generando...'}
+
+
+
+
+
+
+
+
+
+
+ {/* Stats Cards */}
+
+
+
+
+
+
+
+
{stats?.totalInvited || 0}
+
Invitados
+
+
+
+
+
+
+
+
+
+
+
{stats?.totalConverted || 0}
+
Convertidos
+
+
+
+
+
+
+
+
+
+
+
{stats?.monthsEarned || 0}
+
Meses ganados
+
+
+
+
+
+
+
+
+
+
+
{stats?.monthsAvailable || 0}
+
Disponibles
+
+
+
+
+
+ {/* How it works */}
+
+
Como funciona
+
+
+
+ 1
+
+
Comparte tu codigo
+
Envia tu codigo a amigos por WhatsApp
+
+
+
+ 2
+
+
Tu amigo se registra
+
Obtiene 50% de descuento en su primer mes
+
+
+
+ 3
+
+
Tu ganas 1 mes gratis
+
Cuando tu amigo paga su primer mes
+
+
+
+
+ {/* Referrals List */}
+
+
Tus referidos
+ {referrals && referrals.length > 0 ? (
+
+
+
+
+ | Fecha |
+ Estado |
+ Conversion |
+
+
+
+ {referrals.map((referral) => (
+
+ |
+ {new Date(referral.createdAt).toLocaleDateString('es-MX', {
+ day: 'numeric',
+ month: 'short',
+ year: 'numeric',
+ })}
+ |
+ {getStatusBadge(referral.status)} |
+
+ {referral.convertedAt
+ ? new Date(referral.convertedAt).toLocaleDateString('es-MX')
+ : '-'}
+ |
+
+ ))}
+
+
+
+ ) : (
+
+
+
Aun no tienes referidos
+
Comparte tu codigo para empezar a ganar
+
+ )}
+
+
+ );
+}
diff --git a/src/pages/Register.tsx b/src/pages/Register.tsx
new file mode 100644
index 0000000..9d9751c
--- /dev/null
+++ b/src/pages/Register.tsx
@@ -0,0 +1,241 @@
+import { useState } from 'react';
+import { useNavigate, Link } from 'react-router-dom';
+import { useAuth } from '../contexts/AuthContext';
+
+const BUSINESS_TYPES = [
+ { value: 'tiendita', label: 'Tiendita / Abarrotes' },
+ { value: 'fonda', label: 'Fonda / Comida' },
+ { value: 'taqueria', label: 'Taqueria' },
+ { value: 'tortilleria', label: 'Tortilleria' },
+ { value: 'verduleria', label: 'Verduleria' },
+ { value: 'otro', label: 'Otro' },
+];
+
+export default function Register() {
+ const [formData, setFormData] = useState({
+ name: '',
+ ownerName: '',
+ businessType: 'tiendita',
+ phone: '',
+ pin: '',
+ confirmPin: '',
+ email: '',
+ });
+ const [error, setError] = useState('');
+ const [isLoading, setIsLoading] = useState(false);
+ const { register } = useAuth();
+ const navigate = useNavigate();
+
+ const handleChange = (e: React.ChangeEvent) => {
+ const { name, value } = e.target;
+ if (name === 'phone' || name === 'pin' || name === 'confirmPin') {
+ setFormData({ ...formData, [name]: value.replace(/\D/g, '') });
+ } else {
+ setFormData({ ...formData, [name]: value });
+ }
+ };
+
+ const handleSubmit = async (e: React.FormEvent) => {
+ e.preventDefault();
+ setError('');
+
+ if (formData.pin !== formData.confirmPin) {
+ setError('Los PINs no coinciden');
+ return;
+ }
+
+ if (formData.pin.length < 4) {
+ setError('El PIN debe tener al menos 4 digitos');
+ return;
+ }
+
+ setIsLoading(true);
+
+ try {
+ await register({
+ name: formData.name,
+ ownerName: formData.ownerName,
+ businessType: formData.businessType,
+ phone: formData.phone,
+ pin: formData.pin,
+ email: formData.email || undefined,
+ });
+ navigate('/');
+ } catch (err: any) {
+ setError(err.response?.data?.message || 'Error al registrar');
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ return (
+
+
+
+
+ MiChangarrito
+
+
+ Registra tu negocio
+
+
+ Ya tienes cuenta?{' '}
+
+ Inicia sesion
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/pages/Settings.tsx b/src/pages/Settings.tsx
new file mode 100644
index 0000000..d0b6cde
--- /dev/null
+++ b/src/pages/Settings.tsx
@@ -0,0 +1,165 @@
+import { Store, CreditCard, Bell, MessageSquare, Shield } from 'lucide-react';
+
+export function Settings() {
+ return (
+
+
+
Ajustes
+
Configura tu tienda
+
+
+
+ {/* Business Info */}
+
+
+
+
Informacion del Negocio
+
+
+
+
+ {/* Fiado Settings */}
+
+
+
+
Configuracion de Fiado
+
+
+
+
+
Habilitar fiado
+
Permite credito a clientes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* WhatsApp Settings */}
+
+
+
+
WhatsApp Business
+
+
+
+
Conectado
+
+52 555 123 4567
+
+
+
+
Respuestas automaticas
+
Usa IA para responder
+
+
+
+
+
+
Notificar pedidos
+
Avisa cuando hay pedidos nuevos
+
+
+
+
+
+
+ {/* Notifications */}
+
+
+
+
Notificaciones
+
+
+
+
+
Stock bajo
+
Alerta cuando hay poco inventario
+
+
+
+
+
+
Fiados vencidos
+
Recordatorio de cobros pendientes
+
+
+
+
+
+
Nuevos pedidos
+
Sonido al recibir pedidos
+
+
+
+
+
+
+
+ {/* Subscription */}
+
+
+
+
Plan de Suscripcion
+
+
+
+
Plan Basico
+
$299/mes - 1,000 mensajes IA incluidos
+
Renueva: 15 de febrero, 2024
+
+
+
+
+
+ );
+}
diff --git a/tailwind.config.js b/tailwind.config.js
new file mode 100644
index 0000000..cc11449
--- /dev/null
+++ b/tailwind.config.js
@@ -0,0 +1,40 @@
+/** @type {import('tailwindcss').Config} */
+export default {
+ content: [
+ "./index.html",
+ "./src/**/*.{js,ts,jsx,tsx}",
+ ],
+ theme: {
+ extend: {
+ colors: {
+ primary: {
+ 50: '#fff7ed',
+ 100: '#ffedd5',
+ 200: '#fed7aa',
+ 300: '#fdba74',
+ 400: '#fb923c',
+ 500: '#f97316',
+ 600: '#ea580c',
+ 700: '#c2410c',
+ 800: '#9a3412',
+ 900: '#7c2d12',
+ },
+ secondary: {
+ 50: '#f0fdf4',
+ 100: '#dcfce7',
+ 200: '#bbf7d0',
+ 300: '#86efac',
+ 400: '#4ade80',
+ 500: '#22c55e',
+ 600: '#16a34a',
+ 700: '#15803d',
+ 800: '#166534',
+ 900: '#14532d',
+ },
+ },
+ },
+ },
+ plugins: [
+ require('@tailwindcss/forms'),
+ ],
+}
diff --git a/tsconfig.app.json b/tsconfig.app.json
new file mode 100644
index 0000000..a9b5a59
--- /dev/null
+++ b/tsconfig.app.json
@@ -0,0 +1,28 @@
+{
+ "compilerOptions": {
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
+ "target": "ES2022",
+ "useDefineForClassFields": true,
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
+ "module": "ESNext",
+ "types": ["vite/client"],
+ "skipLibCheck": true,
+
+ /* Bundler mode */
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "verbatimModuleSyntax": true,
+ "moduleDetection": "force",
+ "noEmit": true,
+ "jsx": "react-jsx",
+
+ /* Linting */
+ "strict": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "erasableSyntaxOnly": true,
+ "noFallthroughCasesInSwitch": true,
+ "noUncheckedSideEffectImports": true
+ },
+ "include": ["src"]
+}
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..1ffef60
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,7 @@
+{
+ "files": [],
+ "references": [
+ { "path": "./tsconfig.app.json" },
+ { "path": "./tsconfig.node.json" }
+ ]
+}
diff --git a/tsconfig.node.json b/tsconfig.node.json
new file mode 100644
index 0000000..8a67f62
--- /dev/null
+++ b/tsconfig.node.json
@@ -0,0 +1,26 @@
+{
+ "compilerOptions": {
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
+ "target": "ES2023",
+ "lib": ["ES2023"],
+ "module": "ESNext",
+ "types": ["node"],
+ "skipLibCheck": true,
+
+ /* Bundler mode */
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "verbatimModuleSyntax": true,
+ "moduleDetection": "force",
+ "noEmit": true,
+
+ /* Linting */
+ "strict": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "erasableSyntaxOnly": true,
+ "noFallthroughCasesInSwitch": true,
+ "noUncheckedSideEffectImports": true
+ },
+ "include": ["vite.config.ts"]
+}
diff --git a/vite.config.ts b/vite.config.ts
new file mode 100644
index 0000000..56a481c
--- /dev/null
+++ b/vite.config.ts
@@ -0,0 +1,17 @@
+import { defineConfig } from 'vite'
+import react from '@vitejs/plugin-react'
+
+// https://vite.dev/config/
+export default defineConfig({
+ plugins: [react()],
+ server: {
+ port: 3140,
+ proxy: {
+ '/api': {
+ target: 'http://localhost:3141',
+ changeOrigin: true,
+ secure: false,
+ },
+ },
+ },
+})