siesa-agents 2.1.45 → 2.1.47

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,56 +1,410 @@
1
1
  # Frontend Development Standards
2
2
 
3
- ## Architecture Principles
4
-
5
- ### Clean Architecture Implementation
6
- - **Domain Layer**: Business entities, value objects, and business rules only
7
- - **Application Layer**: Use cases, custom hooks, and state management
8
- - **Infrastructure Layer**: API clients, repositories implementations, external service adapters
9
- - **Presentation Layer**: React components, pages, and UI logic only
10
-
11
- ### Dependency Rules
12
- - Inner layers must not know about outer layers
13
- - Dependencies point inward (from outer to inner layers)
14
- - Use dependency inversion for external concerns
15
-
16
- ## Technology Stack Standards
17
-
18
- ### Core Technologies
19
- - **Next.js**: 14+ with App Router and TypeScript
20
- - **React**: 18+ with functional components and hooks (via Next.js)
21
- - **TypeScript**: Strict mode enabled, no `any` types
22
- - **TailwindCSS**: Utility-first CSS framework
23
- - **Shadcn/ui + Radix UI**: Component library foundation
24
-
25
- ### Framework Selection Rules
26
- - **Default**: Always use Next.js 16+ with App Router
27
- - **Exception**: Only use pure React + Vite when user specifically requests offline-first functionality
28
- - **Reasoning**: Next.js provides better DX, built-in optimization, and easier deployment while maintaining PWA capabilities
29
-
30
- ### State Management
31
- - **Zustand**: Primary state management solution
32
- - Feature-based stores following DDD patterns
33
- - Global state only for truly global concerns
34
- - Local state for component-specific needs
35
-
36
- ### Development Tools
37
- - **Next.js**: Built-in build system and development server
38
- - **Vitest**: Unit and integration testing (configured with Next.js)
39
- - **React Testing Library**: Component testing
40
- - **MSW**: API mocking for tests
41
- - **ESLint + Prettier**: Code quality and formatting (Next.js config)
42
-
43
- ## Component Strategy
44
- - **Always First**: Check siesa-ui-kit before creating any component
45
- - **If not in siesa-ui-kit**: Ask user:
46
- [1] Use shadcn directly
47
- [2] Create custom for siesa-ui-kit (requires MR to Platform team)
48
- - **Shadcn Fallback**: Only use MCP Shadcn registry when user chooses option [1]
49
- - **Error Reduction**: 90% fewer bugs using existing components vs manual creation
50
-
51
- ## Component Standards
52
-
53
- ### Component Structure
3
+ ## Resumen
4
+
5
+ Este documento define los estándares de desarrollo frontend para aplicaciones empresariales usando **Vite** como bundler, **TanStack Router** para enrutamiento type-safe, **Zustand** para estado global, y **Clean Architecture** con estructura modular preparada para microfrontends.
6
+
7
+ ---
8
+
9
+ ## Tabla de Contenidos
10
+
11
+ 1. [Principios de Arquitectura](#1-principios-de-arquitectura)
12
+ 2. [Stack Tecnológico](#2-stack-tecnológico)
13
+ 3. [Convenciones de Routing](#3-convenciones-de-routing)
14
+ 4. [Organización de Archivos](#4-organización-de-archivos)
15
+ 5. [Estándares de Componentes](#5-estándares-de-componentes)
16
+ 6. [Patrones de Estado](#6-patrones-de-estado)
17
+ 7. [Estándares de Testing](#7-estándares-de-testing)
18
+ 8. [Accesibilidad](#8-accesibilidad)
19
+ 9. [Rendimiento](#9-rendimiento)
20
+ 10. [Seguridad](#10-seguridad)
21
+ 11. [Manejo de Errores](#11-manejo-de-errores)
22
+ 12. [Progressive Web App](#12-progressive-web-app)
23
+ 13. [Estándares de Idioma](#13-estándares-de-idioma)
24
+ 14. [Consideraciones Generales](#14-consideraciones-generales)
25
+ 15. [Configuración Base](#15-configuración-base)
26
+ 16. [Checklist de Implementación](#16-checklist-de-implementación)
27
+
28
+ ---
29
+
30
+ ## 1. Principios de Arquitectura
31
+
32
+ ### 1.1 Implementación de Clean Architecture
33
+
34
+ ```
35
+ ┌─────────────────────────────────────────────────────────────────┐
36
+ │ PRESENTATION LAYER │
37
+ │ React components, pages, UI logic, routes │
38
+ └─────────────────────────────────────────────────────────────────┘
39
+
40
+
41
+ ┌─────────────────────────────────────────────────────────────────┐
42
+ │ APPLICATION LAYER │
43
+ │ Use cases, custom hooks, Zustand stores │
44
+ └─────────────────────────────────────────────────────────────────┘
45
+
46
+
47
+ ┌─────────────────────────────────────────────────────────────────┐
48
+ │ INFRASTRUCTURE LAYER │
49
+ │ API clients, repositories impl, external adapters │
50
+ └─────────────────────────────────────────────────────────────────┘
51
+
52
+
53
+ ┌─────────────────────────────────────────────────────────────────┐
54
+ │ DOMAIN LAYER │
55
+ │ Business entities, value objects, business rules │
56
+ └─────────────────────────────────────────────────────────────────┘
57
+ ```
58
+
59
+ | Capa | Responsabilidad | Contenido |
60
+ |------|-----------------|-----------|
61
+ | **Domain** | Reglas de negocio puras | Entities, value objects, interfaces de repositorios |
62
+ | **Application** | Orquestación de casos de uso | Use cases, hooks, stores Zustand |
63
+ | **Infrastructure** | Implementaciones externas | API clients, repositorios concretos, adapters |
64
+ | **Presentation** | Interfaz de usuario | Componentes React, páginas, estilos |
65
+
66
+ ### 1.2 Reglas de Dependencia
67
+
68
+ - Las capas internas **NO deben conocer** las capas externas
69
+ - Las dependencias apuntan hacia adentro (de externo a interno)
70
+ - Usar inversión de dependencias para concerns externos
71
+ - Domain layer no importa nada de otras capas
72
+
73
+ ---
74
+
75
+ ## 2. Stack Tecnológico
76
+
77
+ ### 2.1 Tecnologías Core
78
+
79
+ | Categoría | Tecnología | Versión | Notas |
80
+ |-----------|------------|---------|-------|
81
+ | **Bundler** | Vite | 5+ | Build tool y dev server |
82
+ | **Framework** | React | 18+ | Functional components y hooks |
83
+ | **Router** | TanStack Router | 1+ | File-based routing con type-safety |
84
+ | **Lenguaje** | TypeScript | 5+ | Strict mode, sin `any` |
85
+ | **Estilos** | TailwindCSS | 4+ | Utility-first CSS |
86
+ | **Componentes** | shadcn/ui + Radix UI | - | Base de componentes |
87
+ | **Estado** | Zustand | 4+ | Estado global por feature |
88
+ | **Data Fetching** | TanStack Query | 5+ | Cache y sincronización |
89
+
90
+ ### 2.2 Reglas de Selección de Framework
91
+
92
+ | Escenario | Framework | Razón |
93
+ |-----------|-----------|-------|
94
+ | **Default** | Vite + TanStack Router | Mejor DX, type-safety, preparado para microfrontends |
95
+ | **Microfrontends** | Vite + Module Federation | Arquitectura distribuida |
96
+
97
+ ### 2.3 Herramientas de Desarrollo
98
+
99
+ | Herramienta | Propósito |
100
+ |-------------|-----------|
101
+ | **Vite** | Build system y dev server |
102
+ | **Vitest** | Unit e integration testing |
103
+ | **React Testing Library** | Component testing |
104
+ | **MSW** | API mocking para tests |
105
+ | **ESLint + Prettier** | Code quality y formatting |
106
+ | **TypeScript** | Type checking |
107
+
108
+ ---
109
+
110
+ ## 3. Convenciones de Routing
111
+
112
+ TanStack Router usa prefijos especiales en nombres de archivo para definir comportamientos.
113
+
114
+ ### 3.1 Prefijo `_` (Underscore) - Pathless Layout Routes
115
+
116
+ **Propósito:** Agrupar rutas bajo un layout compartido **SIN agregar segmentos a la URL**.
117
+
118
+ #### ❌ El Problema (sin `_`)
119
+
120
+ ```
121
+ routes/
122
+ ├── __root.tsx
123
+ ├── auth/
124
+ │ └── login.tsx → URL: /auth/login ❌
125
+ ├── app/
126
+ │ ├── dashboard.tsx → URL: /app/dashboard ❌
127
+ │ └── orders.tsx → URL: /app/orders ❌
128
+ ```
129
+
130
+ **Resultado:** Las URLs incluyen el nombre de la carpeta, lo cual es indeseable.
131
+
132
+ #### ✅ La Solución (con `_`)
133
+
134
+ ```
135
+ routes/
136
+ ├── __root.tsx
137
+ ├── _auth.tsx → NO agrega nada a la URL (solo layout)
138
+ ├── _auth/
139
+ │ └── login.tsx → URL: /login ✅
140
+
141
+ ├── _app.tsx → NO agrega nada a la URL (solo layout)
142
+ ├── _app/
143
+ │ ├── dashboard.tsx → URL: /dashboard ✅
144
+ │ └── orders.tsx → URL: /orders ✅
145
+ ```
146
+
147
+ #### Ejemplo de Implementación
148
+
149
+ ```typescript
150
+ // routes/_app.tsx - Layout protegido
151
+ import { createFileRoute, Outlet, redirect } from '@tanstack/react-router';
152
+ import { Sidebar, TopNav } from '@/shared/components/layout';
153
+ import { useAuthStore } from '@/modules/users/authentication/login/application/store';
154
+
155
+ export const Route = createFileRoute('/_app')({
156
+ beforeLoad: () => {
157
+ const { isAuthenticated } = useAuthStore.getState();
158
+ if (!isAuthenticated) {
159
+ throw redirect({ to: '/login' });
160
+ }
161
+ },
162
+ component: AppLayout,
163
+ });
164
+
165
+ function AppLayout() {
166
+ return (
167
+ <div className="flex h-screen">
168
+ <Sidebar />
169
+ <div className="flex-1 flex flex-col">
170
+ <TopNav />
171
+ <main className="flex-1 overflow-auto p-6">
172
+ <Outlet />
173
+ </main>
174
+ </div>
175
+ </div>
176
+ );
177
+ }
178
+ ```
179
+
180
+ ### 3.2 Prefijo `.` (Punto) - Flat Routing
181
+
182
+ **Propósito:** Definir rutas anidadas **sin crear carpetas**.
183
+
184
+ ```
185
+ routes/
186
+ ├── orders.tsx → /orders (layout)
187
+ ├── orders.index.tsx → /orders
188
+ ├── orders.$orderId.tsx → /orders/:orderId
189
+ ├── orders.$orderId.edit.tsx → /orders/:orderId/edit
190
+ ```
191
+
192
+ #### ¿Cuándo usar `.` vs Carpetas?
193
+
194
+ | Escenario | Recomendación |
195
+ |-----------|---------------|
196
+ | Pocas rutas anidadas (2-3) | Flat con `.` |
197
+ | Muchas rutas anidadas (4+) | Carpetas |
198
+ | Rutas con componentes colocados | Carpetas con `-components/` |
199
+
200
+ ### 3.3 Prefijo `-` (Guión) - Ignorar Archivos
201
+
202
+ **Propósito:** Excluir archivos/carpetas de la generación de rutas para colocación de código.
203
+
204
+ ```
205
+ routes/
206
+ ├── orders/
207
+ │ ├── $orderId.tsx → /orders/:orderId ✅
208
+ │ ├── -components/ → ❌ Ignorado por el router
209
+ │ │ └── OrderHeader.tsx
210
+ │ └── -hooks/ → ❌ Ignorado por el router
211
+ │ └── useOrderCalculations.ts
212
+ ```
213
+
214
+ ### 3.4 Prefijo `$` (Dólar) - Parámetros Dinámicos
215
+
216
+ ```typescript
217
+ // routes/_app/orders/$orderId.tsx
218
+ import { createFileRoute } from '@tanstack/react-router';
219
+
220
+ export const Route = createFileRoute('/_app/orders/$orderId')({
221
+ component: OrderDetail,
222
+ });
223
+
224
+ function OrderDetail() {
225
+ const { orderId } = Route.useParams(); // Tipado automático
226
+ return <div>Order: {orderId}</div>;
227
+ }
228
+ ```
229
+
230
+ ### 3.5 Resumen de Prefijos
231
+
232
+ | Prefijo | Nombre | Efecto en URL | Uso Principal |
233
+ |---------|--------|---------------|---------------|
234
+ | `_` | Pathless | **No aparece** | Layouts sin path |
235
+ | `.` | Flat | Crea anidamiento | Evitar carpetas |
236
+ | `-` | Ignore | **No genera ruta** | Colocación de código |
237
+ | `$` | Dynamic | Captura valor | Parámetros de URL |
238
+ | `__` | Root | Raíz del árbol | Solo `__root.tsx` |
239
+
240
+ ---
241
+
242
+ ## 4. Organización de Archivos
243
+
244
+ ### 4.1 Estructura Enterprise (Module/Domain/Feature)
245
+
246
+ ```
247
+ src/
248
+ ├── main.tsx # React entry point
249
+ ├── router.tsx # TanStack Router config
250
+ ├── routeTree.gen.ts # Auto-generado (NO EDITAR)
251
+ ├── globals.css # Estilos globales + Tailwind
252
+
253
+ ├── routes/ # 🛣️ SOLO definición de rutas
254
+ │ ├── __root.tsx # Layout raíz (providers)
255
+ │ ├── index.tsx # Redirect inicial
256
+ │ ├── _auth.tsx # Layout público
257
+ │ ├── _auth/
258
+ │ │ └── login.tsx
259
+ │ ├── _app.tsx # Layout protegido
260
+ │ └── _app/
261
+ │ ├── dashboard.tsx
262
+ │ ├── sales/
263
+ │ │ ├── quotes.tsx
264
+ │ │ └── invoices.tsx
265
+ │ └── inventory/
266
+ │ └── products.tsx
267
+
268
+ ├── modules/ # 🏢 Lógica de negocio por módulo
269
+ │ ├── sales/ # MODULE
270
+ │ │ ├── quotes/ # DOMAIN
271
+ │ │ │ ├── cart/ # FEATURE
272
+ │ │ │ │ ├── domain/
273
+ │ │ │ │ │ ├── entities/
274
+ │ │ │ │ │ │ └── CartItem.ts
275
+ │ │ │ │ │ ├── repositories/ # Interfaces
276
+ │ │ │ │ │ │ └── ICartRepository.ts
277
+ │ │ │ │ │ ├── services/ # Domain services
278
+ │ │ │ │ │ │ └── CartCalculator.ts
279
+ │ │ │ │ │ └── types/
280
+ │ │ │ │ │ └── cart.types.ts
281
+ │ │ │ │ ├── application/
282
+ │ │ │ │ │ ├── use-cases/
283
+ │ │ │ │ │ │ ├── AddToCart.ts
284
+ │ │ │ │ │ │ └── RemoveFromCart.ts
285
+ │ │ │ │ │ ├── hooks/
286
+ │ │ │ │ │ │ └── useCart.ts
287
+ │ │ │ │ │ └── store/
288
+ │ │ │ │ │ └── cart.store.ts
289
+ │ │ │ │ ├── infrastructure/
290
+ │ │ │ │ │ ├── repositories/ # Implementations
291
+ │ │ │ │ │ │ └── CartRepository.ts
292
+ │ │ │ │ │ ├── api/
293
+ │ │ │ │ │ │ └── cart.api.ts
294
+ │ │ │ │ │ └── adapters/
295
+ │ │ │ │ └── presentation/
296
+ │ │ │ │ ├── components/
297
+ │ │ │ │ │ ├── CartList.tsx
298
+ │ │ │ │ │ └── CartItem.tsx
299
+ │ │ │ │ └── pages/
300
+ │ │ │ │ └── CartPage.tsx
301
+ │ │ │ └── products/ # FEATURE
302
+ │ │ │ ├── domain/
303
+ │ │ │ ├── application/
304
+ │ │ │ ├── infrastructure/
305
+ │ │ │ └── presentation/
306
+ │ │ └── billing/ # DOMAIN
307
+ │ │ ├── invoices/ # FEATURE
308
+ │ │ └── reports/ # FEATURE
309
+ │ │
310
+ │ ├── inventory/ # MODULE
311
+ │ │ ├── products/ # DOMAIN
312
+ │ │ │ ├── catalog/ # FEATURE
313
+ │ │ │ └── stock/ # FEATURE
314
+ │ │ └── warehouses/ # DOMAIN
315
+ │ │
316
+ │ └── users/ # MODULE
317
+ │ └── authentication/ # DOMAIN
318
+ │ ├── login/ # FEATURE
319
+ │ └── registration/ # FEATURE
320
+
321
+ ├── shared/ # 🔄 Código reutilizable
322
+ │ ├── components/
323
+ │ │ └── ui/ # shadcn/ui + siesa-ui-kit
324
+ │ ├── hooks/
325
+ │ │ ├── useDebounce.ts
326
+ │ │ └── useLocalStorage.ts
327
+ │ ├── lib/
328
+ │ │ ├── utils.ts # cn(), formatters
329
+ │ │ ├── api-client.ts # Axios/fetch config
330
+ │ │ └── env.ts # Variables de entorno tipadas
331
+ │ ├── types/
332
+ │ │ └── common.types.ts
333
+ │ └── constants/
334
+
335
+ ├── app/ # 🎯 Configuración global
336
+ │ ├── store/ # Global store (si necesario)
337
+ │ ├── providers/ # Context providers
338
+ │ │ ├── ThemeProvider.tsx
339
+ │ │ └── QueryProvider.tsx
340
+ │ └── config/
341
+
342
+ ├── infrastructure/ # 🔌 Servicios externos globales
343
+ │ ├── api/ # API configuration
344
+ │ ├── storage/ # IndexedDB, localStorage
345
+ │ └── pwa/ # PWA configuration
346
+
347
+ ├── assets/ # 📁 Recursos estáticos
348
+ │ ├── fonts/
349
+ │ │ ├── SiesaBT-Light.otf
350
+ │ │ ├── SiesaBT-Regular.otf
351
+ │ │ └── SiesaBT-Bold.otf
352
+ │ └── images/
353
+ │ └── logos/
354
+
355
+ └── styles/
356
+ └── globals.css
357
+ ```
358
+
359
+ ### 4.2 Jerarquía de Carpetas
360
+
361
+ ```
362
+ MODULE (módulo de negocio)
363
+ └── DOMAIN (área funcional)
364
+ └── FEATURE (funcionalidad específica)
365
+ ├── domain/ # Reglas de negocio
366
+ ├── application/ # Casos de uso y estado
367
+ ├── infrastructure/ # Implementaciones externas
368
+ └── presentation/ # UI
369
+ ```
370
+
371
+ ### 4.3 Organización de Imports
372
+
373
+ ```typescript
374
+ // 1. Librerías externas
375
+ import React from 'react';
376
+ import { create } from 'zustand';
377
+ import { useQuery } from '@tanstack/react-query';
378
+
379
+ // 2. Módulos internos (por capa, de interno a externo)
380
+ import { CartItem } from '../domain/entities/CartItem';
381
+ import { addToCartUseCase } from '../application/use-cases/AddToCart';
382
+ import { cartRepository } from '../infrastructure/repositories/CartRepository';
383
+
384
+ // 3. Shared
385
+ import { cn } from '@/shared/lib/utils';
386
+ import { Button } from '@/shared/components/ui/button';
387
+
388
+ // 4. Types
389
+ import type { Cart } from '../domain/types/cart.types';
390
+ ```
391
+
392
+ ---
393
+
394
+ ## 5. Estándares de Componentes
395
+
396
+ ### 5.1 Estrategia de Componentes
397
+
398
+ | Prioridad | Acción |
399
+ |-----------|--------|
400
+ | **1. siesa-ui-kit** | Siempre verificar primero si existe el componente |
401
+ | **2. Si no existe** | Preguntar al usuario: [1] Usar shadcn directamente, [2] Crear para siesa-ui-kit (requiere MR) |
402
+ | **3. Shadcn fallback** | Solo usar registro MCP Shadcn si el usuario elige opción [1] |
403
+
404
+ > **Beneficio:** 90% menos bugs usando componentes existentes vs creación manual.
405
+
406
+ ### 5.2 Estructura de Componentes
407
+
54
408
  ```typescript
55
409
  interface ComponentProps {
56
410
  // Required props
@@ -67,9 +421,11 @@ export const Component = memo<ComponentProps>(({
67
421
  variant = 'default',
68
422
  'data-testid': testId = 'component'
69
423
  }) => {
70
- // Component implementation
71
424
  return (
72
- <div className={cn(baseStyles, variantStyles[variant], className)} data-testid={testId}>
425
+ <div
426
+ className={cn(baseStyles, variantStyles[variant], className)}
427
+ data-testid={testId}
428
+ >
73
429
  {children}
74
430
  </div>
75
431
  );
@@ -78,299 +434,742 @@ export const Component = memo<ComponentProps>(({
78
434
  Component.displayName = 'Component';
79
435
  ```
80
436
 
81
- ### Component Naming
82
- - **PascalCase** for component names
83
- - **kebab-case** for file names and data-testid attributes
84
- - **camelCase** for props and functions
85
- - Descriptive names that indicate purpose
437
+ ### 5.3 Convenciones de Nombrado
438
+
439
+ | Elemento | Convención | Ejemplo |
440
+ |----------|------------|---------|
441
+ | Componentes | PascalCase | `OrderCard.tsx` |
442
+ | Archivos | kebab-case | `order-card.tsx` |
443
+ | data-testid | kebab-case | `data-testid="order-card"` |
444
+ | Props/funciones | camelCase | `onSubmit`, `isLoading` |
445
+ | Constantes | SCREAMING_SNAKE | `MAX_ITEMS` |
446
+
447
+ ### 5.4 Guidelines de Props
448
+
449
+ - Siempre definir interfaces TypeScript para props
450
+ - Usar props opcionales con defaults sensatos
451
+ - Incluir `className` para overrides de estilos
452
+ - Agregar `data-testid` para testing
453
+ - Documentar props complejas con JSDoc
454
+
455
+ ---
86
456
 
87
- ### Props Guidelines
88
- - Always define TypeScript interfaces for props
89
- - Use optional props with sensible defaults
90
- - Include className for style overrides
91
- - Add data-testid for testing
92
- - Document complex props with JSDoc
457
+ ## 6. Patrones de Estado
93
458
 
94
- ## State Management Patterns
459
+ ### 6.1 Estructura de Zustand Store
95
460
 
96
- ### Zustand Store Structure
97
461
  ```typescript
98
- interface FeatureState {
462
+ // modules/sales/quotes/cart/application/store/cart.store.ts
463
+ import { create } from 'zustand';
464
+ import { devtools } from 'zustand/middleware';
465
+ import type { CartItem } from '../../domain/entities/CartItem';
466
+ import { addToCartUseCase } from '../use-cases/AddToCart';
467
+ import { removeFromCartUseCase } from '../use-cases/RemoveFromCart';
468
+
469
+ interface CartState {
99
470
  // Domain entities
100
- entities: Entity[];
101
- selectedEntity: Entity | null;
471
+ items: CartItem[];
102
472
 
103
473
  // UI state
104
474
  loading: boolean;
105
475
  error: string | null;
106
476
 
107
- // Actions (use case calls)
108
- loadEntities: () => Promise<void>;
109
- selectEntity: (id: string) => void;
110
- updateEntity: (entity: Entity) => Promise<void>;
477
+ // Actions (delegan a use cases)
478
+ addItem: (productId: string, quantity: number) => Promise<void>;
479
+ removeItem: (itemId: string) => Promise<void>;
480
+ clearCart: () => void;
111
481
  clearError: () => void;
112
482
  }
113
483
 
114
- export const useFeatureStore = create<FeatureState>()(
484
+ export const useCartStore = create<CartState>()(
115
485
  devtools(
116
486
  (set, get) => ({
117
487
  // Initial state
118
- entities: [],
119
- selectedEntity: null,
488
+ items: [],
120
489
  loading: false,
121
490
  error: null,
122
491
 
123
- // Actions implementation
124
- loadEntities: async () => {
492
+ // Actions
493
+ addItem: async (productId, quantity) => {
494
+ set({ loading: true, error: null });
495
+ try {
496
+ const newItem = await addToCartUseCase.execute({ productId, quantity });
497
+ set((state) => ({
498
+ items: [...state.items, newItem],
499
+ loading: false
500
+ }));
501
+ } catch (error) {
502
+ set({ error: (error as Error).message, loading: false });
503
+ }
504
+ },
505
+
506
+ removeItem: async (itemId) => {
125
507
  set({ loading: true, error: null });
126
508
  try {
127
- const entities = await loadEntitiesUseCase.execute();
128
- set({ entities, loading: false });
509
+ await removeFromCartUseCase.execute(itemId);
510
+ set((state) => ({
511
+ items: state.items.filter(item => item.id !== itemId),
512
+ loading: false
513
+ }));
129
514
  } catch (error) {
130
- set({ error: error.message, loading: false });
515
+ set({ error: (error as Error).message, loading: false });
131
516
  }
132
517
  },
133
518
 
134
- // Other actions...
519
+ clearCart: () => set({ items: [] }),
520
+ clearError: () => set({ error: null }),
135
521
  }),
136
- { name: 'featureStore' }
522
+ { name: 'cartStore' }
137
523
  )
138
524
  );
139
525
  ```
140
526
 
141
- ## Testing Standards
527
+ ### 6.2 Cuándo Usar Cada Tipo de Estado
528
+
529
+ | Tipo | Cuándo Usar | Herramienta |
530
+ |------|-------------|-------------|
531
+ | **Server State** | Datos del API, cache | TanStack Query |
532
+ | **Global Client State** | Auth, theme, cart | Zustand |
533
+ | **Local Component State** | Forms, UI toggles | useState/useReducer |
534
+ | **URL State** | Filtros, paginación | TanStack Router search params |
535
+
536
+ ### 6.3 Integración con TanStack Query
537
+
538
+ ```typescript
539
+ // modules/sales/quotes/products/infrastructure/api/products.api.ts
540
+ import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
541
+ import { productRepository } from '../repositories/ProductRepository';
542
+
543
+ export const productKeys = {
544
+ all: ['products'] as const,
545
+ lists: () => [...productKeys.all, 'list'] as const,
546
+ list: (filters: ProductFilters) => [...productKeys.lists(), filters] as const,
547
+ detail: (id: string) => [...productKeys.all, 'detail', id] as const,
548
+ };
549
+
550
+ export function useProducts(filters: ProductFilters) {
551
+ return useQuery({
552
+ queryKey: productKeys.list(filters),
553
+ queryFn: () => productRepository.getAll(filters),
554
+ });
555
+ }
556
+
557
+ export function useProduct(id: string) {
558
+ return useQuery({
559
+ queryKey: productKeys.detail(id),
560
+ queryFn: () => productRepository.getById(id),
561
+ enabled: !!id,
562
+ });
563
+ }
564
+ ```
565
+
566
+ ---
567
+
568
+ ## 7. Estándares de Testing
569
+
570
+ ### 7.1 Estrategia de Testing
571
+
572
+ | Tipo | Cobertura | Herramienta | Qué Testear |
573
+ |------|-----------|-------------|-------------|
574
+ | **Unit** | Alta | Vitest | Entities, use cases, utilities |
575
+ | **Integration** | Media | Vitest + MSW | Feature workflows, API integration |
576
+ | **Component** | Media | React Testing Library | User interactions, accessibility |
577
+ | **E2E** | Baja | Playwright | Critical user journeys |
578
+
579
+ ### 7.2 Estructura de Tests
580
+
581
+ ```typescript
582
+ // modules/sales/quotes/cart/application/use-cases/__tests__/AddToCart.test.ts
583
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
584
+ import { addToCartUseCase } from '../AddToCart';
585
+ import { mockCartRepository } from '../../__mocks__/cartRepository.mock';
586
+
587
+ describe('AddToCart UseCase', () => {
588
+ beforeEach(() => {
589
+ vi.clearAllMocks();
590
+ });
591
+
592
+ it('should add item to cart successfully', async () => {
593
+ const input = { productId: 'prod-1', quantity: 2 };
594
+
595
+ const result = await addToCartUseCase.execute(input);
596
+
597
+ expect(result).toMatchObject({
598
+ productId: 'prod-1',
599
+ quantity: 2,
600
+ });
601
+ expect(mockCartRepository.save).toHaveBeenCalledWith(expect.objectContaining(input));
602
+ });
603
+
604
+ it('should throw error when quantity is invalid', async () => {
605
+ const input = { productId: 'prod-1', quantity: -1 };
606
+
607
+ await expect(addToCartUseCase.execute(input)).rejects.toThrow(
608
+ 'La cantidad debe ser mayor a 0'
609
+ );
610
+ });
611
+ });
612
+ ```
142
613
 
143
- ### Testing Strategy
144
- - **Unit Tests**: Domain entities, use cases, utilities
145
- - **Integration Tests**: Feature workflows, API integration
146
- - **Component Tests**: User interactions, accessibility
147
- - **E2E Tests**: Critical user journeys (minimal)
614
+ ### 7.3 Component Testing
148
615
 
149
- ### Test Structure
150
616
  ```typescript
151
- describe('ComponentName', () => {
617
+ // modules/sales/quotes/cart/presentation/components/__tests__/CartItem.test.tsx
618
+ import { describe, it, expect } from 'vitest';
619
+ import { render, screen } from '@testing-library/react';
620
+ import userEvent from '@testing-library/user-event';
621
+ import { axe } from 'vitest-axe';
622
+ import { CartItem } from '../CartItem';
623
+
624
+ describe('CartItem', () => {
625
+ const defaultProps = {
626
+ item: { id: '1', name: 'Producto Test', quantity: 2, price: 100 },
627
+ onRemove: vi.fn(),
628
+ };
629
+
152
630
  it('should render correctly with default props', () => {
153
- render(<ComponentName>Test content</ComponentName>);
154
- expect(screen.getByTestId('component-name')).toBeInTheDocument();
631
+ render(<CartItem {...defaultProps} />);
632
+
633
+ expect(screen.getByTestId('cart-item')).toBeInTheDocument();
634
+ expect(screen.getByText('Producto Test')).toBeInTheDocument();
155
635
  });
156
-
157
- it('should handle user interactions', async () => {
158
- const handleClick = jest.fn();
159
- render(<ComponentName onClick={handleClick} />);
636
+
637
+ it('should handle remove action', async () => {
638
+ const user = userEvent.setup();
639
+ render(<CartItem {...defaultProps} />);
160
640
 
161
- await user.click(screen.getByRole('button'));
162
- expect(handleClick).toHaveBeenCalled();
641
+ await user.click(screen.getByRole('button', { name: /eliminar/i }));
642
+
643
+ expect(defaultProps.onRemove).toHaveBeenCalledWith('1');
163
644
  });
164
-
645
+
165
646
  it('should be accessible', async () => {
166
- const { container } = render(<ComponentName />);
647
+ const { container } = render(<CartItem {...defaultProps} />);
167
648
  const results = await axe(container);
649
+
168
650
  expect(results).toHaveNoViolations();
169
651
  });
170
652
  });
171
653
  ```
172
654
 
173
- ## Accessibility Standards
655
+ ---
656
+
657
+ ## 8. Accesibilidad
658
+
659
+ ### 8.1 Cumplimiento WCAG 2.1 AA
660
+
661
+ | Requisito | Implementación |
662
+ |-----------|----------------|
663
+ | Elementos semánticos | Usar HTML semántico (`<nav>`, `<main>`, `<article>`) |
664
+ | Jerarquía de headings | Un solo `<h1>`, headings en orden descendente |
665
+ | Navegación por teclado | Todos los elementos interactivos accesibles con Tab |
666
+ | Screen readers | Labels descriptivos, roles ARIA cuando necesario |
667
+ | Contraste de color | Mínimo 4.5:1 para texto normal |
174
668
 
175
- ### WCAG 2.1 AA Compliance
176
- - Semantic HTML elements
177
- - Proper heading hierarchy
178
- - Keyboard navigation support
179
- - Screen reader compatibility
180
- - Color contrast minimum 4.5:1
669
+ ### 8.2 Implementación ARIA
181
670
 
182
- ### ARIA Implementation
183
- - Use semantic HTML first, ARIA second
184
- - Proper labeling with aria-label or aria-labelledby
185
- - Role attributes for custom components
186
- - State communication with aria-expanded, aria-selected
187
- - Live regions for dynamic content updates
671
+ ```typescript
672
+ // Correcto - HTML semántico primero
673
+ <button onClick={handleSubmit}>Guardar</button>
674
+
675
+ // ✅ Correcto - ARIA cuando es necesario
676
+ <div
677
+ role="tabpanel"
678
+ aria-labelledby="tab-1"
679
+ aria-expanded={isOpen}
680
+ >
681
+ {content}
682
+ </div>
188
683
 
189
- ## Performance Standards
684
+ // Incorrecto - ARIA innecesario
685
+ <button role="button" aria-label="button">Guardar</button>
686
+ ```
190
687
 
191
- ### Bundle Optimization
192
- - Code splitting by route and feature
193
- - Lazy loading for non-critical components
194
- - Tree shaking for unused code
195
- - Bundle size budget: 500KB total
688
+ ---
196
689
 
197
- ### Runtime Performance
198
- - React.memo for expensive components
199
- - useMemo for expensive calculations
200
- - useCallback for event handlers passed to children
201
- - Virtual scrolling for large lists
690
+ ## 9. Rendimiento
202
691
 
203
- ### Loading Performance
204
- - Image optimization and lazy loading
205
- - Progressive loading strategies
206
- - Skeleton screens for loading states
207
- - Prefetch critical resources
692
+ ### 9.1 Optimización de Bundle
208
693
 
209
- ## Security Guidelines
694
+ | Estrategia | Implementación |
695
+ |------------|----------------|
696
+ | Code splitting | Por ruta (automático con TanStack Router) |
697
+ | Lazy loading | `React.lazy()` para componentes no críticos |
698
+ | Tree shaking | Imports específicos, no barrel exports en shared |
699
+ | Bundle budget | Máximo 500KB total |
210
700
 
211
- ### Client-Side Security
212
- - No API keys or secrets in frontend code
213
- - Input validation and sanitization
214
- - XSS prevention measures
215
- - Secure handling of user data
701
+ ### 9.2 Rendimiento en Runtime
216
702
 
217
- ### Authentication
218
- - Token-based authentication (JWT)
219
- - Secure token storage
220
- - Automatic token refresh
221
- - Proper logout handling
703
+ ```typescript
704
+ // React.memo para componentes costosos
705
+ export const ExpensiveList = memo<ListProps>(({ items }) => {
706
+ return items.map(item => <ExpensiveItem key={item.id} item={item} />);
707
+ });
222
708
 
223
- ## File Organization
709
+ // useMemo para cálculos costosos
710
+ const sortedItems = useMemo(() =>
711
+ items.sort((a, b) => a.name.localeCompare(b.name)),
712
+ [items]
713
+ );
224
714
 
225
- ### Feature Structure
715
+ // useCallback para handlers pasados a children
716
+ const handleClick = useCallback((id: string) => {
717
+ onSelect(id);
718
+ }, [onSelect]);
226
719
  ```
227
- src/
228
- ├── modules/
229
- │ ├── sales/ # MODULE
230
- │ │ ├── quotes/ # DOMAIN
231
- │ │ │ ├── cart/ # FEATURE
232
- │ │ │ │ ├── domain/
233
- │ │ │ │ │ ├── entities/
234
- │ │ │ │ │ ├── repositories/ # Interfaces
235
- │ │ │ │ │ ├── services/ # Domain services
236
- │ │ │ │ │ └── types/
237
- │ │ │ │ ├── application/
238
- │ │ │ │ │ ├── use-cases/
239
- │ │ │ │ │ ├── hooks/ # Custom hooks
240
- │ │ │ │ │ └── store/ # Zustand store
241
- │ │ │ │ ├── infrastructure/
242
- │ │ │ │ │ ├── repositories/ # Implementations
243
- │ │ │ │ │ ├── api/
244
- │ │ │ │ │ └── adapters/
245
- │ │ │ │ └── presentation/
246
- │ │ │ │ ├── components/
247
- │ │ │ │ ├── pages/
248
- │ │ │ │ └── styles/
249
- │ │ │ └── products/ # FEATURE
250
- │ │ │ ├── domain/
251
- │ │ │ ├── application/
252
- │ │ │ ├── infrastructure/
253
- │ │ │ └── presentation/
254
- │ │ └── billing/ # DOMAIN
255
- │ │ ├── invoices/ # FEATURE
256
- │ │ └── reports/ # FEATURE
257
- │ ├── inventory/ # MODULE
258
- │ │ ├── products/ # DOMAIN
259
- │ │ │ ├── catalog/ # FEATURE
260
- │ │ │ └── stock/ # FEATURE
261
- │ │ └── warehouses/ # DOMAIN
262
- │ └── users/ # MODULE
263
- │ └── authentication/ # DOMAIN
264
- │ ├── login/ # FEATURE
265
- │ └── registration/ # FEATURE
266
- ├── shared/
267
- │ ├── components/ # Shadcn/ui components
268
- │ │ └── ui/ # Base UI Kit components from proyecto/components/ui/
269
- │ ├── hooks/
270
- │ ├── utils/
271
- │ ├── types/
272
- │ └── constants/
273
- ├── app/
274
- │ ├── store/ # Global store
275
- │ ├── providers/ # Context providers
276
- │ ├── router/ # Routing config
277
- │ └── app.tsx
278
- ├── infrastructure/
279
- │ ├── api/ # API configuration
280
- │ ├── storage/ # IndexedDB, localStorage
281
- │ └── pwa/ # PWA configuration
282
- ├── assets/
283
- │ ├── fonts/ # Fonts from proyecto/public/fonts/
284
- │ │ ├── SiesaBT-Light.otf
285
- │ │ ├── SiesaBT-Regular.otf
286
- │ │ └── SiesaBT-Bold.otf
287
- │ ├── images/
288
- │ │ └── logos/ # Logos from proyecto/public/images/logos/
289
- │ │ ├── Siesa_Logosimbolo_Azul.svg
290
- │ │ ├── Siesa_Logosimbolo_Blanco.svg
291
- │ │ ├── Siesa_Simbolo_Azul.svg
292
- │ │ └── Siesa_Simbolo_Blanco.svg
293
- │ └── favicon.ico # Favicon from proyecto/public/favicon.ico
294
- └── styles/
295
- └── globals.css # Consolidated styles + Tailwind + WCAG from proyecto/styles/globals.css
720
+
721
+ ### 9.3 Loading Performance
722
+
723
+ - Optimización de imágenes y lazy loading
724
+ - Skeleton screens para estados de carga
725
+ - Prefetch de recursos críticos
726
+ - Virtual scrolling para listas grandes
727
+
728
+ ---
729
+
730
+ ## 10. Seguridad
731
+
732
+ ### 10.1 Seguridad Client-Side
733
+
734
+ | Riesgo | Mitigación |
735
+ |--------|------------|
736
+ | API keys expuestas | Nunca en código frontend, usar variables de entorno server-side |
737
+ | XSS | Sanitización de inputs, no usar `dangerouslySetInnerHTML` |
738
+ | Datos sensibles | No almacenar en localStorage sin encriptar |
739
+
740
+ ### 10.2 Autenticación
741
+
742
+ ```typescript
743
+ // Manejo seguro de tokens
744
+ const useAuthStore = create<AuthState>((set) => ({
745
+ token: null,
746
+
747
+ setToken: (token: string) => {
748
+ // Almacenar en memoria, no localStorage
749
+ set({ token });
750
+ },
751
+
752
+ logout: () => {
753
+ set({ token: null });
754
+ // Limpiar cualquier dato sensible
755
+ },
756
+ }));
296
757
  ```
297
758
 
298
- ### Import Organization
759
+ ---
760
+
761
+ ## 11. Manejo de Errores
762
+
763
+ ### 11.1 Error Boundaries por Feature
764
+
299
765
  ```typescript
300
- // External libraries
301
- import React from 'react';
302
- import { create } from 'zustand';
766
+ // shared/components/feedback/ErrorBoundary.tsx
767
+ import { Component, ErrorInfo, ReactNode } from 'react';
768
+
769
+ interface Props {
770
+ children: ReactNode;
771
+ fallback?: ReactNode;
772
+ }
773
+
774
+ interface State {
775
+ hasError: boolean;
776
+ error: Error | null;
777
+ }
778
+
779
+ export class ErrorBoundary extends Component<Props, State> {
780
+ state: State = { hasError: false, error: null };
303
781
 
304
- // Internal utilities
305
- import { cn } from '@/lib/utils';
782
+ static getDerivedStateFromError(error: Error): State {
783
+ return { hasError: true, error };
784
+ }
306
785
 
307
- // Types
308
- import type { User } from '../domain/types/User';
786
+ componentDidCatch(error: Error, errorInfo: ErrorInfo) {
787
+ console.error('Error capturado:', error, errorInfo);
788
+ // Enviar a servicio de logging
789
+ }
309
790
 
310
- // Components
311
- import { Button } from '@/components/ui/button';
791
+ render() {
792
+ if (this.state.hasError) {
793
+ return this.props.fallback || (
794
+ <div className="p-4 text-center">
795
+ <h2 className="text-lg font-semibold text-red-600">
796
+ Algo salió mal
797
+ </h2>
798
+ <p className="text-gray-600">
799
+ Por favor, intenta recargar la página
800
+ </p>
801
+ </div>
802
+ );
803
+ }
804
+
805
+ return this.props.children;
806
+ }
807
+ }
808
+ ```
809
+
810
+ ### 11.2 Manejo de Errores en API
811
+
812
+ ```typescript
813
+ // shared/lib/api-client.ts
814
+ import axios, { AxiosError } from 'axios';
815
+
816
+ const apiClient = axios.create({
817
+ baseURL: import.meta.env.VITE_API_URL,
818
+ });
819
+
820
+ apiClient.interceptors.response.use(
821
+ (response) => response,
822
+ (error: AxiosError<{ message: string }>) => {
823
+ const message = error.response?.data?.message || 'Error de conexión';
824
+
825
+ // Mensaje amigable para el usuario
826
+ return Promise.reject(new Error(message));
827
+ }
828
+ );
312
829
  ```
313
830
 
314
- ## Error Handling
831
+ ---
832
+
833
+ ## 12. Progressive Web App
834
+
835
+ ### 12.1 Features PWA
315
836
 
316
- ### Error Boundaries
317
- - Feature-level error boundaries
318
- - Graceful fallback UI
319
- - Error reporting and logging
320
- - User-friendly error messages
837
+ | Feature | Implementación |
838
+ |---------|----------------|
839
+ | Service Worker | Caching y soporte offline |
840
+ | Web App Manifest | Instalabilidad |
841
+ | Push Notifications | Cuando sea necesario |
842
+ | Background Sync | Sincronización al restaurar conexión |
321
843
 
322
- ### API Error Handling
323
- - Centralized error handling in HTTP client
324
- - Proper error typing
325
- - User-friendly error messages
326
- - Retry mechanisms for transient errors
844
+ ### 12.2 Estrategia Offline
327
845
 
328
- ## Progressive Web App Standards
846
+ | Tipo de Recurso | Estrategia |
847
+ |-----------------|------------|
848
+ | Assets estáticos | Cache-first |
849
+ | Datos dinámicos | Network-first con fallback |
850
+ | Páginas offline | Fallback pre-cacheado |
851
+ | Formularios | Queue y sync cuando online |
329
852
 
330
- ### PWA Features
331
- - Service worker for caching and offline support
332
- - Web app manifest for installability
333
- - Push notifications (when needed)
334
- - Background sync capabilities
853
+ ---
335
854
 
336
- ### Offline Strategy
337
- - Cache-first for static assets
338
- - Network-first for dynamic data
339
- - Fallback pages for offline scenarios
340
- - Sync when connection restored
855
+ ## 13. Estándares de Idioma
341
856
 
342
- ## Language Standards (Frontend & Backend)
857
+ ### 13.1 Español para Todo Contenido Visible al Usuario
343
858
 
344
- ### Spanish for All User-Facing Content (CRITICAL RULE)
859
+ **REGLA CRÍTICA: Todo texto visible al usuario final DEBE estar en español.**
345
860
 
346
- **MANDATORY: All text visible to end users MUST be in Spanish.**
861
+ #### Español Requerido
347
862
 
348
- #### Spanish Required:
349
- - UI labels, buttons, forms, messages, notifications
350
- - API responses, validation errors, email templates
351
- - Any text the user sees (frontend or backend)
863
+ - Labels de UI, botones, formularios, mensajes, notificaciones
864
+ - Respuestas de API, errores de validación, templates de email
865
+ - Cualquier texto que el usuario vea (frontend o backend)
352
866
 
353
- #### ✅ English Required:
354
- - Code (variables, functions, classes)
355
- - Technical logs, comments, git commits
356
- - Developer documentation
867
+ #### ✅ Inglés Requerido
357
868
 
358
- #### Never mix languages in user-facing text
869
+ - Código (variables, funciones, clases)
870
+ - Logs técnicos, comentarios, commits de git
871
+ - Documentación técnica para desarrolladores
872
+
873
+ #### ❌ Nunca Mezclar Idiomas en Texto Visible
359
874
 
360
- **Examples:**
361
875
  ```typescript
362
- // ✅ CORRECT
876
+ // ✅ CORRECTO
363
877
  <Button>Guardar</Button>
364
878
  toast.success("Datos guardados correctamente");
365
879
  throw new BadRequestException('No se pudo crear el usuario');
366
880
 
367
- // ❌ INCORRECT
881
+ // ❌ INCORRECTO
368
882
  <Button>Save cambios</Button>
369
883
  toast.error("Failed al guardar");
370
884
  throw new BadRequestException('Invalid datos proporcionados');
371
885
  ```
372
886
 
373
- **Implementation:**
374
- - Create message constants in Spanish
375
- - Validate all user-facing text before story completion
376
- - Technical logs stay in English, user messages in Spanish
887
+ ### 13.2 Implementación
888
+
889
+ ```typescript
890
+ // shared/constants/messages.ts
891
+ export const MESSAGES = {
892
+ SUCCESS: {
893
+ SAVED: 'Datos guardados correctamente',
894
+ DELETED: 'Elemento eliminado correctamente',
895
+ UPDATED: 'Información actualizada',
896
+ },
897
+ ERROR: {
898
+ GENERIC: 'Ha ocurrido un error. Por favor, intenta de nuevo',
899
+ NOT_FOUND: 'El recurso solicitado no fue encontrado',
900
+ UNAUTHORIZED: 'No tienes permisos para realizar esta acción',
901
+ VALIDATION: 'Por favor, verifica los datos ingresados',
902
+ },
903
+ LOADING: {
904
+ DEFAULT: 'Cargando...',
905
+ SAVING: 'Guardando...',
906
+ PROCESSING: 'Procesando...',
907
+ },
908
+ } as const;
909
+ ```
910
+
911
+ ---
912
+
913
+ ## 14. Consideraciones Generales
914
+
915
+ ### 14.1 Gestor de Paquetes
916
+
917
+ **Regla:** Usar `pnpm` como gestor de paquetes por defecto, pero respetar el gestor existente en proyectos ya iniciados.
918
+
919
+ | Escenario | Acción | Razón |
920
+ |-----------|--------|-------|
921
+ | Proyecto nuevo | Usar `pnpm` | Mejor rendimiento y manejo de dependencias |
922
+ | Proyecto con `package-lock.json` | Continuar con `npm` | Evitar conflictos de lockfiles |
923
+ | Proyecto con `yarn.lock` | Continuar con `yarn` | Evitar conflictos de lockfiles |
924
+ | Proyecto con `pnpm-lock.yaml` | Continuar con `pnpm` | Ya está configurado |
925
+
926
+ #### Cómo Identificar el Gestor Actual
927
+
928
+ ```bash
929
+ ls -la | grep -E "package-lock|yarn.lock|pnpm-lock"
930
+ ```
931
+
932
+ | Archivo encontrado | Gestor a usar |
933
+ |--------------------|---------------|
934
+ | `package-lock.json` | `npm` |
935
+ | `yarn.lock` | `yarn` |
936
+ | `pnpm-lock.yaml` | `pnpm` |
937
+ | Ninguno | `pnpm` (proyecto nuevo) |
938
+
939
+ #### Comandos Equivalentes
940
+
941
+ | Acción | pnpm | npm | yarn |
942
+ |--------|------|-----|------|
943
+ | Instalar | `pnpm install` | `npm install` | `yarn` |
944
+ | Agregar dep | `pnpm add <pkg>` | `npm install <pkg>` | `yarn add <pkg>` |
945
+ | Agregar dev | `pnpm add -D <pkg>` | `npm install -D <pkg>` | `yarn add -D <pkg>` |
946
+ | Ejecutar script | `pnpm <script>` | `npm run <script>` | `yarn <script>` |
947
+ | Remover | `pnpm remove <pkg>` | `npm uninstall <pkg>` | `yarn remove <pkg>` |
948
+
949
+ > **⚠️ Importante:** Nunca mezclar gestores de paquetes en un mismo proyecto.
950
+
951
+ ---
952
+
953
+ ## 15. Configuración Base
954
+
955
+ ### 15.1 `vite.config.ts`
956
+
957
+ ```typescript
958
+ import { defineConfig } from 'vite';
959
+ import react from '@vitejs/plugin-react';
960
+ import { TanStackRouterVite } from '@tanstack/router-plugin/vite';
961
+ import viteTsConfigPaths from 'vite-tsconfig-paths';
962
+ import tailwindcss from '@tailwindcss/vite';
963
+
964
+ export default defineConfig({
965
+ plugins: [
966
+ // TanStack Router DEBE ir primero
967
+ TanStackRouterVite({
968
+ target: 'react',
969
+ autoCodeSplitting: true,
970
+ routesDirectory: './src/routes',
971
+ generatedRouteTree: './src/routeTree.gen.ts',
972
+ routeFileIgnorePrefix: '-',
973
+ }),
974
+ react(),
975
+ viteTsConfigPaths(),
976
+ tailwindcss(),
977
+ ],
978
+ resolve: {
979
+ alias: {
980
+ '@': '/src',
981
+ '@modules': '/src/modules',
982
+ '@shared': '/src/shared',
983
+ '@app': '/src/app',
984
+ },
985
+ },
986
+ server: {
987
+ port: 3000,
988
+ },
989
+ build: {
990
+ outDir: 'dist',
991
+ },
992
+ });
993
+ ```
994
+
995
+ ### 15.2 `src/router.tsx`
996
+
997
+ ```typescript
998
+ import { createRouter as createTanStackRouter } from '@tanstack/react-router';
999
+ import { QueryClient } from '@tanstack/react-query';
1000
+ import { routerWithQueryClient } from '@tanstack/react-router-with-query';
1001
+ import { routeTree } from './routeTree.gen';
1002
+
1003
+ function NotFoundPage() {
1004
+ return (
1005
+ <div className="min-h-screen flex items-center justify-center">
1006
+ <div className="text-center">
1007
+ <h1 className="text-6xl font-bold">404</h1>
1008
+ <p className="mt-4">Página no encontrada</p>
1009
+ </div>
1010
+ </div>
1011
+ );
1012
+ }
1013
+
1014
+ function ErrorPage({ error }: { error: Error }) {
1015
+ return (
1016
+ <div className="min-h-screen flex items-center justify-center">
1017
+ <div className="text-center">
1018
+ <h1 className="text-2xl font-bold text-red-600">Error</h1>
1019
+ <p className="mt-4">{error.message}</p>
1020
+ </div>
1021
+ </div>
1022
+ );
1023
+ }
1024
+
1025
+ export function createRouter() {
1026
+ const queryClient = new QueryClient({
1027
+ defaultOptions: {
1028
+ queries: {
1029
+ staleTime: 60 * 1000,
1030
+ refetchOnWindowFocus: false,
1031
+ },
1032
+ },
1033
+ });
1034
+
1035
+ const router = createTanStackRouter({
1036
+ routeTree,
1037
+ context: { queryClient },
1038
+ defaultPreload: 'intent',
1039
+ scrollRestoration: true,
1040
+ defaultNotFoundComponent: NotFoundPage,
1041
+ defaultErrorComponent: ErrorPage,
1042
+ });
1043
+
1044
+ return routerWithQueryClient(router, queryClient);
1045
+ }
1046
+
1047
+ declare module '@tanstack/react-router' {
1048
+ interface Register {
1049
+ router: ReturnType<typeof createRouter>;
1050
+ }
1051
+ }
1052
+ ```
1053
+
1054
+ ### 15.3 `src/main.tsx`
1055
+
1056
+ ```typescript
1057
+ import { StrictMode } from 'react';
1058
+ import { createRoot } from 'react-dom/client';
1059
+ import { RouterProvider } from '@tanstack/react-router';
1060
+ import { createRouter } from './router';
1061
+ import './globals.css';
1062
+
1063
+ const router = createRouter();
1064
+
1065
+ createRoot(document.getElementById('root')!).render(
1066
+ <StrictMode>
1067
+ <RouterProvider router={router} />
1068
+ </StrictMode>
1069
+ );
1070
+ ```
1071
+
1072
+ ### 15.4 `src/routes/__root.tsx`
1073
+
1074
+ ```typescript
1075
+ import { createRootRouteWithContext, Outlet } from '@tanstack/react-router';
1076
+ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
1077
+ import { Toaster } from 'sonner';
1078
+
1079
+ interface RouterContext {
1080
+ queryClient: QueryClient;
1081
+ }
1082
+
1083
+ export const Route = createRootRouteWithContext<RouterContext>()({
1084
+ component: RootComponent,
1085
+ });
1086
+
1087
+ function RootComponent() {
1088
+ const { queryClient } = Route.useRouteContext();
1089
+
1090
+ return (
1091
+ <QueryClientProvider client={queryClient}>
1092
+ <Outlet />
1093
+ <Toaster position="bottom-right" />
1094
+ </QueryClientProvider>
1095
+ );
1096
+ }
1097
+ ```
1098
+
1099
+ ### 15.5 `vitest.config.ts`
1100
+
1101
+ ```typescript
1102
+ import { defineConfig } from 'vitest/config';
1103
+ import react from '@vitejs/plugin-react';
1104
+ import viteTsConfigPaths from 'vite-tsconfig-paths';
1105
+
1106
+ export default defineConfig({
1107
+ plugins: [react(), viteTsConfigPaths()],
1108
+ test: {
1109
+ globals: true,
1110
+ environment: 'jsdom',
1111
+ setupFiles: ['./src/test/setup.ts'],
1112
+ include: ['src/**/*.{test,spec}.{ts,tsx}'],
1113
+ coverage: {
1114
+ provider: 'v8',
1115
+ reporter: ['text', 'json', 'html'],
1116
+ exclude: [
1117
+ 'node_modules/',
1118
+ 'src/test/',
1119
+ '**/*.d.ts',
1120
+ 'src/routeTree.gen.ts',
1121
+ ],
1122
+ },
1123
+ },
1124
+ });
1125
+ ```
1126
+
1127
+ ---
1128
+
1129
+ ## 16. Checklist de Implementación
1130
+
1131
+ ### Configuración Inicial
1132
+
1133
+ - [ ] Instalar dependencias: `@tanstack/react-router`, `@tanstack/router-plugin`, `@tanstack/react-query`
1134
+ - [ ] Crear `vite.config.ts` con TanStackRouterVite plugin (PRIMERO)
1135
+ - [ ] Crear `src/router.tsx` con configuración del router
1136
+ - [ ] Crear `src/main.tsx` con RouterProvider
1137
+ - [ ] Crear `src/routes/__root.tsx`
1138
+ - [ ] Configurar path aliases en `tsconfig.json`
1139
+ - [ ] Agregar `routeTree.gen.ts` a `.prettierignore` y `.eslintignore`
1140
+ - [ ] Configurar Vitest
1141
+
1142
+ ### Estructura de Rutas
1143
+
1144
+ - [ ] Crear layouts pathless (`_auth.tsx`, `_app.tsx`)
1145
+ - [ ] Implementar guards de autenticación en `beforeLoad`
1146
+ - [ ] Usar `$param` para rutas dinámicas
1147
+ - [ ] Usar `-` para carpetas de colocación
1148
+ - [ ] Verificar que las URLs sean correctas
1149
+
1150
+ ### Arquitectura
1151
+
1152
+ - [ ] Organizar módulos siguiendo Module/Domain/Feature
1153
+ - [ ] Implementar capas de Clean Architecture por feature
1154
+ - [ ] Configurar stores Zustand por feature
1155
+ - [ ] Configurar TanStack Query para server state
1156
+ - [ ] Crear barrel exports (`index.ts`) por feature
1157
+
1158
+ ### Calidad
1159
+
1160
+ - [ ] Configurar Vitest con coverage
1161
+ - [ ] Agregar tests unitarios para use cases
1162
+ - [ ] Agregar tests de componentes
1163
+ - [ ] Verificar accesibilidad (axe)
1164
+ - [ ] Validar textos en español
1165
+
1166
+ ---
1167
+
1168
+ ## Referencias
1169
+
1170
+ - [TanStack Router Documentation](https://tanstack.com/router/latest)
1171
+ - [TanStack Query Documentation](https://tanstack.com/query/latest)
1172
+ - [Zustand Documentation](https://zustand-demo.pmnd.rs/)
1173
+ - [Vite Documentation](https://vitejs.dev/)
1174
+ - [Vitest Documentation](https://vitest.dev/)
1175
+ - [Clean Architecture - Robert C. Martin](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html)