# Patrones de Frontend - GAMILIT **Documento:** Analisis de Patrones de Frontend React + TypeScript **Proyecto de Referencia:** GAMILIT **Fecha:** 2025-11-23 **Analista:** Architecture-Analyst Agent --- ## 1. VISION GENERAL GAMILIT implementa un **frontend moderno con React 18+, Vite 5+ y TypeScript 5+** siguiendo la arquitectura Feature-Sliced Design (FSD) con 180+ componentes reutilizables. **Stack tecnologico:** - **Framework:** React 18+ con hooks - **Build Tool:** Vite 5+ (HMR rapido) - **Lenguaje:** TypeScript 5+ (strict mode) - **Styling:** Tailwind CSS 3+ (utility-first) - **Router:** React Router v6 - **State:** Zustand (8 stores especializados) - **Forms:** React Hook Form + Zod validation - **HTTP:** Axios con interceptors - **Animations:** Framer Motion - **Testing:** Vitest + React Testing Library (13% coverage - GAP) - **Docs:** Storybook 7+ **Metricas:** - **LOC:** ~85,000 lineas (65% del proyecto) - **Componentes:** 180+ componentes reutilizables - **Paginas:** ~50 vistas - **Hooks personalizados:** ~30 - **Zustand Stores:** 8 stores - **Tests:** 15 tests (13% coverage - GAP CRITICO) --- ## 2. FEATURE-SLICED DESIGN (FSD) ### 2.1 Arquitectura de Capas ``` frontend/src/ ├── shared/ # Capa compartida (180+ componentes) │ ├── components/ # UI components genericos │ ├── hooks/ # Custom React hooks │ ├── utils/ # Utilidades generales │ ├── types/ # Tipos TypeScript compartidos │ └── constants/ # Constantes (sincronizadas con backend) │ ├── features/ # Features de negocio por dominio/rol │ ├── student/ # Portal estudiante │ ├── teacher/ # Portal profesor │ └── admin/ # Portal administrador │ ├── pages/ # Paginas/Vistas (composicion de features) │ ├── student/ │ ├── teacher/ │ └── admin/ │ ├── services/ # Servicios externos │ ├── api/ # API clients (Axios) │ └── websocket/ # Socket.IO client │ └── app/ # Capa de aplicacion ├── providers/ # Context providers ├── layouts/ # Layouts principales └── router/ # Configuracion de rutas ``` ### 2.2 Principios de FSD Aplicados 1. **Layered Architecture:** - `shared` - Codigo reutilizable sin dependencias de negocio - `features` - Logica de negocio por dominio - `pages` - Composicion de features - `app` - Configuracion global 2. **Public API:** Cada feature expone API publica via `index.ts` 3. **Low Coupling:** Features no dependen entre si 4. **High Cohesion:** Todo relacionado a una feature esta junto ### 2.3 Aplicabilidad a ERP Generico ⭐⭐⭐⭐ (ALTA) **Decision:** ✅ **ADOPTAR Y ADAPTAR** **Propuesta para ERP:** ``` frontend/src/ ├── shared/ # Componentes reutilizables ├── features/ │ ├── administrator/ # Portal administrador │ ├── accountant/ # Portal contador │ ├── supervisor/ # Portal supervisor de obra │ ├── purchaser/ # Portal comprador │ └── hr/ # Portal RRHH ├── pages/ ├── services/ └── app/ ``` --- ## 3. SHARED COMPONENTS (180+) ### 3.1 Categorias de Componentes **1. Atomos (Elementos basicos):** ``` shared/components/atoms/ ├── Button/ │ ├── Button.tsx │ ├── Button.stories.tsx │ ├── Button.test.tsx │ └── index.ts ├── Input/ ├── Label/ ├── Badge/ ├── Avatar/ └── Icon/ ``` **2. Moleculas (Combinaciones de atomos):** ``` shared/components/molecules/ ├── FormField/ # Label + Input + Error ├── SearchBar/ # Input + Icon + Button ├── UserCard/ # Avatar + Name + Badge └── Notification/ # Icon + Title + Message ``` **3. Organismos (Componentes complejos):** ``` shared/components/organisms/ ├── Navbar/ ├── Sidebar/ ├── DataTable/ ├── Modal/ ├── Dropdown/ └── Pagination/ ``` **4. Templates (Layouts):** ``` shared/components/templates/ ├── DashboardLayout/ ├── AuthLayout/ ├── SettingsLayout/ └── EmptyState/ ``` ### 3.2 Patron de Componente **Ejemplo:** `shared/components/atoms/Button/Button.tsx` ```typescript import { ButtonHTMLAttributes, ReactNode } from 'react'; import { cva, type VariantProps } from 'class-variance-authority'; // Variants con Tailwind CSS const buttonVariants = cva( 'inline-flex items-center justify-center rounded-md font-medium transition-colors', { variants: { variant: { primary: 'bg-blue-600 text-white hover:bg-blue-700', secondary: 'bg-gray-200 text-gray-900 hover:bg-gray-300', outline: 'border border-gray-300 hover:bg-gray-100', ghost: 'hover:bg-gray-100', danger: 'bg-red-600 text-white hover:bg-red-700', }, size: { sm: 'h-8 px-3 text-sm', md: 'h-10 px-4', lg: 'h-12 px-6 text-lg', }, fullWidth: { true: 'w-full', }, }, defaultVariants: { variant: 'primary', size: 'md', }, } ); export interface ButtonProps extends ButtonHTMLAttributes, VariantProps { children: ReactNode; isLoading?: boolean; } export function Button({ children, variant, size, fullWidth, isLoading, disabled, className, ...props }: ButtonProps) { return ( ); } ``` ### 3.3 Aplicabilidad a ERP Generico ⭐⭐⭐⭐⭐ (MAXIMA) **Decision:** ✅ **ADOPTAR COMPLETAMENTE** **Componentes criticos para ERP:** - Button, Input, Select, Checkbox (atomos) - FormField, SearchBar, DateRangePicker (moleculas) - DataTable, Modal, Sidebar, Navbar (organismos) - DashboardLayout, ReportLayout (templates) --- ## 4. PATH ALIASES FRONTEND ### 4.1 Configuracion **tsconfig.json:** ```json { "compilerOptions": { "baseUrl": "./src", "paths": { "@/*": ["./*"], "@shared/*": ["shared/*"], "@components/*": ["shared/components/*"], "@hooks/*": ["shared/hooks/*"], "@utils/*": ["shared/utils/*"], "@types/*": ["shared/types/*"], "@services/*": ["services/*"], "@app/*": ["app/*"], "@features/*": ["features/*"], "@pages/*": ["pages/*"] } } } ``` **vite.config.ts:** ```typescript import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; import path from 'path'; export default defineConfig({ plugins: [react()], resolve: { alias: { '@': path.resolve(__dirname, './src'), '@shared': path.resolve(__dirname, './src/shared'), '@components': path.resolve(__dirname, './src/shared/components'), '@hooks': path.resolve(__dirname, './src/shared/hooks'), '@utils': path.resolve(__dirname, './src/shared/utils'), '@types': path.resolve(__dirname, './src/shared/types'), '@services': path.resolve(__dirname, './src/services'), '@app': path.resolve(__dirname, './src/app'), '@features': path.resolve(__dirname, './src/features'), '@pages': path.resolve(__dirname, './src/pages'), }, }, }); ``` ### 4.2 Uso ```typescript // ❌ Sin aliases import { Button } from '../../../shared/components/atoms/Button'; import { useAuth } from '../../../shared/hooks/useAuth'; // ✅ Con aliases import { Button } from '@components/atoms/Button'; import { useAuth } from '@hooks/useAuth'; ``` ### 4.3 Aplicabilidad a ERP Generico ⭐⭐⭐⭐⭐ (MAXIMA) **Decision:** ✅ **ADOPTAR COMPLETAMENTE** --- ## 5. STATE MANAGEMENT CON ZUSTAND ### 5.1 Los 8 Stores de GAMILIT ``` stores/ ├── useAuthStore.ts # Autenticacion (user, token, logout) ├── useGamificationStore.ts # Gamificacion (XP, coins, logros) ├── useProgressStore.ts # Progreso de aprendizaje ├── useExerciseStore.ts # Estado de ejercicios actuales ├── useNotificationStore.ts # Notificaciones en tiempo real ├── useSocialStore.ts # Features sociales ├── useTenantStore.ts # Multi-tenancy └── useUIStore.ts # Estado UI (modals, sidebar, theme) ``` ### 5.2 Patron de Store (ejemplo: useAuthStore) ```typescript import { create } from 'zustand'; import { persist } from 'zustand/middleware'; interface AuthState { // Estado user: User | null; token: string | null; isAuthenticated: boolean; // Acciones login: (email: string, password: string) => Promise; logout: () => void; refreshToken: () => Promise; updateProfile: (data: Partial) => Promise; } export const useAuthStore = create()( persist( (set, get) => ({ // Estado inicial user: null, token: null, isAuthenticated: false, // Acciones login: async (email, password) => { const response = await authApi.login({ email, password }); set({ user: response.user, token: response.token, isAuthenticated: true, }); }, logout: () => { set({ user: null, token: null, isAuthenticated: false, }); }, refreshToken: async () => { const { token } = get(); const response = await authApi.refresh(token); set({ token: response.token }); }, updateProfile: async (data) => { const { user } = get(); const updated = await userApi.update(user!.id, data); set({ user: updated }); }, }), { name: 'auth-storage', // Key en localStorage partialize: (state) => ({ token: state.token, user: state.user, }), } ) ); ``` ### 5.3 Uso en Componentes ```typescript function UserProfile() { const { user, logout, updateProfile } = useAuthStore(); if (!user) return ; return (

Welcome, {user.name}!

); } ``` ### 5.4 Aplicabilidad a ERP Generico ⭐⭐⭐⭐⭐ (MAXIMA) **Decision:** ✅ **ADOPTAR COMPLETAMENTE** **Stores propuestos para ERP:** ``` stores/ ├── useAuthStore.ts # Autenticacion ├── useCompanyStore.ts # Empresa actual (multi-tenant) ├── useProjectStore.ts # Proyecto actual ├── useBudgetStore.ts # Presupuesto actual ├── useNotificationStore.ts # Notificaciones ├── useUIStore.ts # Estado UI └── usePermissionsStore.ts # Permisos del usuario ``` --- ## 6. CUSTOM HOOKS (~30) ### 6.1 Categorias de Hooks **1. Hooks de Estado:** ```typescript // hooks/useLocalStorage.ts export function useLocalStorage(key: string, initialValue: T) { const [storedValue, setStoredValue] = useState(() => { try { const item = window.localStorage.getItem(key); return item ? JSON.parse(item) : initialValue; } catch { return initialValue; } }); const setValue = (value: T | ((val: T) => T)) => { const valueToStore = value instanceof Function ? value(storedValue) : value; setStoredValue(valueToStore); window.localStorage.setItem(key, JSON.stringify(valueToStore)); }; return [storedValue, setValue] as const; } ``` **2. Hooks de Fetch:** ```typescript // hooks/useQuery.ts export function useQuery( queryFn: () => Promise, deps: any[] = [] ) { const [data, setData] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { setLoading(true); queryFn() .then(setData) .catch(setError) .finally(() => setLoading(false)); }, deps); return { data, loading, error, refetch: () => queryFn().then(setData) }; } ``` **3. Hooks de Utilidad:** ```typescript // hooks/useDebounce.ts export function useDebounce(value: T, delay: number): T { const [debouncedValue, setDebouncedValue] = useState(value); useEffect(() => { const handler = setTimeout(() => setDebouncedValue(value), delay); return () => clearTimeout(handler); }, [value, delay]); return debouncedValue; } ``` ### 6.2 Aplicabilidad a ERP Generico ⭐⭐⭐⭐⭐ (MAXIMA) **Decision:** ✅ **ADOPTAR COMPLETAMENTE** **Hooks criticos para ERP:** - `useQuery` - Fetching de datos - `useMutation` - Operaciones CRUD - `useDebounce` - Busquedas optimizadas - `useLocalStorage` - Persistencia local - `usePermissions` - Verificacion de permisos --- ## 7. FORMS CON REACT HOOK FORM + ZOD ### 7.1 Patron de Form ```typescript import { useForm } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import { z } from 'zod'; // Schema de validacion con Zod const loginSchema = z.object({ email: z.string().email('Email invalido'), password: z.string().min(8, 'Minimo 8 caracteres'), }); type LoginFormData = z.infer; export function LoginForm() { const { register, handleSubmit, formState: { errors, isSubmitting }, } = useForm({ resolver: zodResolver(loginSchema), }); const onSubmit = async (data: LoginFormData) => { await authApi.login(data); }; return (
); } ``` ### 7.2 Aplicabilidad a ERP Generico ⭐⭐⭐⭐⭐ (MAXIMA) **Decision:** ✅ **ADOPTAR COMPLETAMENTE** --- ## 8. API CLIENTS CON AXIOS ### 8.1 Configuracion de Axios Instance ```typescript // services/api/axios-instance.ts import axios from 'axios'; import { useAuthStore } from '@stores/useAuthStore'; const api = axios.create({ baseURL: import.meta.env.VITE_API_URL, timeout: 10000, }); // Interceptor de request (agregar token) api.interceptors.request.use((config) => { const { token } = useAuthStore.getState(); if (token) { config.headers.Authorization = `Bearer ${token}`; } return config; }); // Interceptor de response (refresh token) api.interceptors.response.use( (response) => response, async (error) => { if (error.response?.status === 401) { const { refreshToken, logout } = useAuthStore.getState(); try { await refreshToken(); return api.request(error.config); } catch { logout(); } } return Promise.reject(error); } ); export default api; ``` ### 8.2 API Clients por Modulo ```typescript // services/api/auth.api.ts import api from './axios-instance'; import { API_ENDPOINTS } from '@shared/constants'; export const authApi = { login: (data: LoginDto) => api.post(API_ENDPOINTS.AUTH.LOGIN, data).then((r) => r.data), register: (data: RegisterDto) => api.post(API_ENDPOINTS.AUTH.REGISTER, data).then((r) => r.data), logout: () => api.post(API_ENDPOINTS.AUTH.LOGOUT).then((r) => r.data), refresh: (token: string) => api.post(API_ENDPOINTS.AUTH.REFRESH, { token }).then((r) => r.data), }; ``` ### 8.3 Aplicabilidad a ERP Generico ⭐⭐⭐⭐⭐ (MAXIMA) **Decision:** ✅ **ADOPTAR COMPLETAMENTE** --- ## 9. TESTING PATTERNS (GAP CRITICO) ### 9.1 Metricas de Testing | Metrica | Actual | Objetivo | Gap | |---------|--------|----------|-----| | **Tests Frontend** | 15 | 60 | **-45 (75%)** | | **Coverage** | 13% | 70% | **-57pp** | **Critica:** Coverage extremadamente bajo. NO copiar este anti-patron. ### 9.2 Ejemplo de Test ```typescript // components/Button/Button.test.tsx import { render, screen, fireEvent } from '@testing-library/react'; import { Button } from './Button'; describe('Button', () => { it('renders with children', () => { render(); expect(screen.getByText('Click me')).toBeInTheDocument(); }); it('calls onClick when clicked', () => { const onClick = vi.fn(); render(); fireEvent.click(screen.getByText('Click')); expect(onClick).toHaveBeenCalledTimes(1); }); it('shows loading state', () => { render(); expect(screen.getByRole('button')).toBeDisabled(); }); }); ``` ### 9.3 Recomendacion para ERP Generico ⭐⭐⭐⭐⭐ (MAXIMA - CRITICO) **Decision:** ✅ **IMPLEMENTAR TESTING DESDE EL INICIO** **Objetivos:** - **Coverage:** 70%+ desde el inicio - **Tests por componente:** Unit tests - **Integration tests:** Features completas - **E2E tests:** Flujos criticos (Playwright/Cypress) --- ## 10. MATRIZ DE DECISION ### 10.1 ADOPTAR COMPLETAMENTE ✅ | Patron | Prioridad | |--------|-----------| | Feature-Sliced Design (FSD) | P0 | | Shared Components (180+) | P0 | | Path Aliases | P0 | | Zustand State Management | P0 | | Custom Hooks | P0 | | React Hook Form + Zod | P0 | | Axios Interceptors | P0 | | Tailwind CSS | P1 | | Storybook | P1 | ### 10.2 MEJORAR 🔧 | Patron | Estado Actual | Mejora | Prioridad | |--------|---------------|--------|-----------| | Testing | 13% coverage | 70%+ coverage | P0 CRITICO | | Type Safety | Parcial | Completa | P1 | | E2E Tests | Inexistentes | Playwright/Cypress | P1 | ### 10.3 EVITAR ❌ | Patron | Razon | |--------|-------| | Coverage bajo (13%) | Inaceptable para produccion | | Sin E2E tests | Flujos criticos sin validar | --- ## 11. PROPUESTA PARA ERP GENERICO ``` frontend/src/ ├── shared/ │ ├── components/ # 100+ componentes reutilizables │ ├── hooks/ # Custom hooks │ └── constants/ # Constantes (sync con backend) │ ├── features/ │ ├── administrator/ # Portal admin │ ├── accountant/ # Portal contador │ ├── supervisor/ # Portal supervisor │ ├── purchaser/ # Portal compras │ └── hr/ # Portal RRHH │ ├── pages/ ├── services/ │ └── api/ │ ├── budgets.api.ts │ ├── purchasing.api.ts │ └── projects.api.ts │ └── app/ ├── providers/ ├── layouts/ └── router/ ``` --- **Documento creado:** 2025-11-23 **Version:** 1.0 **Estado:** Completado **Proximo documento:** `ssot-system.md`