Skip to content

Arquitectura Frontend - PWA

Resumen

Frontend PWA independiente y separado del ERP administrativo, optimizado para clientes móviles. Build estático servido por Apache (sin Node.js en producción).

Stack Técnico

Decisiones Tecnológicas

Tecnologías principales:

  • Vite: Build tool y dev server
  • React 19: Framework UI
  • TypeScript: Type safety
  • Tailwind CSS 4: Styling
  • React Router 7: Navegación SPA
  • Axios: Cliente HTTP
  • Zustand: Estado global ligero
  • Zod: Validación de esquemas

¿Por qué Vite?

  • Build estático servido por Apache (sin Node.js en producción)
  • Extremadamente rápido en desarrollo (HMR instantáneo)
  • Build optimizado y ligero automáticamente
  • PWA con plugin oficial
  • TypeScript nativo
  • Configuración simple

¿Por qué React 19?

  • Componentes modernos con hooks
  • Ecosistema maduro
  • Fácil para equipo PHP que migra a frontend moderno
  • Server Components y Suspense para optimización

¿Por qué Tailwind CSS?

  • Rápido para prototipar
  • No require CSS personalizado
  • Responsive por defecto
  • Pequeño bundle size con purge

¿Por qué NO Next.js?

  • No se necesita SSR (es una PWA)
  • No se necesita servidor Node.js en producción
  • Vite es más simple y rápido para SPA

Arquitectura de Componentes

┌─────────────────────────────────────────────────────────┐
│                    APP SHELL (Layout)                   │
│  - Header (logo empresa, nombre cliente)               │
│  - Navigation (Dashboard, Deudas, Pagar, Historial)    │
│  - Footer                                               │
└────────────────────────┬────────────────────────────────┘

        ┌────────────────┴────────────────┐
        │                                 │
┌───────▼────────┐              ┌─────────▼────────┐
│  PAGES         │              │  CONTEXTS        │
│  - /login      │◄─────────────┤  - AuthContext   │
│  - /dashboard  │              │  - BrandingCtx   │
│  - /deudas     │              │  - TenantContext │
│  - /pagar      │              └──────────────────┘
│  - /cupones    │                        │
│  - /historial  │                        │
└───────┬────────┘                        │
        │                                 │
        └────────────────┬────────────────┘

        ┌────────────────▼────────────────┐
        │                                 │
┌───────▼─────────┐            ┌──────────▼────────┐
│  COMPONENTS     │            │  API CLIENT       │
│  - DeudaCard    │◄───────────┤  - auth           │
│  - DeudaList    │            │  - ctacte         │
│  - PayButton    │            │  - pagos          │
│  - CuponPDF     │            │  - cupones        │
│  - CodigoBarra  │            └───────────────────┘
└─────────────────┘

Capas:

  1. App Shell: Layout persistente con header/nav/footer
  2. Pages: Rutas principales (login, dashboard, deudas, etc.)
  3. Contexts: Estado global compartido (auth, branding, tenant)
  4. Components: Componentes reutilizables de UI
  5. API Client: Capa de comunicación con backend

Estructura de Proyecto

portal-clientes/
├── src/
│   ├── main.tsx              # Entry point
│   ├── App.tsx               # Router + layout
│   ├── pages/                # Páginas principales
│   │   ├── LoginPage.tsx
│   │   ├── DashboardPage.tsx
│   │   ├── DeudasPage.tsx
│   │   ├── PagarPage.tsx
│   │   ├── CuponesPage.tsx
│   │   └── HistorialPagosPage.tsx
│   ├── components/           # Componentes reutilizables
│   │   ├── layout/           # Header, Footer, Navigation
│   │   ├── deudas/           # DeudaCard, DeudaList
│   │   ├── pagos/            # PaymentButton, SelectFacturas
│   │   ├── cupones/          # CuponPDF, CodigoBarrasITF
│   │   └── ui/               # Button, Card, Input, Modal
│   ├── lib/
│   │   ├── api/              # Cliente API (axios)
│   │   ├── hooks/            # Custom hooks (useAuth, useDeudas)
│   │   ├── contexts/         # React Contexts
│   │   └── utils/            # Formateo, validación
│   └── types/                # TypeScript types
└── public/
    ├── icons/                # Iconos PWA
    └── manifest.json         # PWA manifest

Contextos (Estado Global)

AuthContext

Propósito: Gestionar autenticación del cliente en toda la app.

Estado:

  • cliente: Datos del cliente logueado (nombre, ID, email)
  • isAuthenticated: Si está autenticado
  • isLoading: Cargando estado inicial

Acciones:

  • login(identifier, type): Identificar cliente
  • logout(): Cerrar sesión

BrandingContext

Propósito: Aplicar branding dinámico según el tenant (colores, logo, nombre).

Estado:

  • appName: Nombre de la aplicación
  • logo: URL del logo
  • primaryColor: Color principal
  • themeColor: Color del tema

Uso: Se obtiene del backend al cargar el dominio, se aplica con CSS variables.

TenantContext

Propósito: Información del tenant actual (empresa) según el dominio.

Estado:

  • tenantId: ID del tenant
  • domain: Dominio usado
  • database: Base de datos del tenant

Páginas Principales

1. /login

Propósito: Identificación del cliente sin contraseña (DNI/CUIT/ID).

UI:

┌──────────────────────────┐
│     [Logo Empresa]       │
│ Portal de Clientes       │
│                          │
│ Tipo: ( ) DNI  ( ) CUIT  │
│       ( ) Cliente ID     │
│                          │
│ Número: [___________]    │
│                          │
│ [    INGRESAR    ]       │
└──────────────────────────┘

Flujo:

  1. Usuario selecciona tipo de identificación
  2. Ingresa número
  3. Submit → API valida
  4. Si OK → Redirect a /dashboard
  5. Si error → Muestra mensaje

API: POST /portal/auth/identify-client

2. /dashboard

Propósito: Resumen rápido del estado de cuenta.

UI:

┌──────────────────────────┐
│ Hola, Juan Pérez        │
│                          │
│ Saldo Total: $15,000.00  │
│                          │
│ Facturas Vencidas: 3     │
│ Próximas a Vencer: 2     │
│                          │
│ Último Pago: 15/01/26    │
│                          │
│ [ Pagar Deudas ]         │
│ [ Generar Cupón ]        │
│ [ Ver Historial ]        │
└──────────────────────────┘

Datos mostrados:

  • Nombre del cliente
  • Saldo total pendiente
  • Cantidad de facturas vencidas
  • Cantidad de facturas próximas a vencer
  • Último pago realizado

API: GET /portal/mi-cuenta

3. /deudas

Propósito: Listar todas las facturas pendientes del cliente.

UI: Cards o tabla con facturas pendientes, agrupadas por estado (vencidas/pendientes).

Datos por factura:

  • Tipo y número (ej: "Factura A - 123")
  • Fecha de emisión
  • Fecha de vencimiento
  • Monto pendiente
  • Estado visual (VENCIDA en rojo, Pendiente en amarillo)
  • Días de vencimiento (si aplica)

Acciones:

  • Seleccionar facturas para pagar
  • Botón "Pagar Seleccionadas"

API: GET /portal/deudas

4. /pagar

Propósito: Seleccionar facturas y pagar online.

UI:

┌──────────────────────────┐
│ Seleccione facturas:     │
│ [✓] Factura A  $10,000   │
│ [✓] Factura B  $5,000    │
│ [ ] Factura C  $3,000    │
│                          │
│ Total: $15,000.00        │
│                          │
│ [ Pagar Online ]         │
└──────────────────────────┘

Flujo:

  1. Cliente selecciona facturas a pagar
  2. Se muestra total acumulado
  3. Click "Pagar Online"
  4. Backend crea pago y retorna URL de redirección
  5. Cliente es redirigido al gateway (MercadoPago, PagoTIC, etc.)
  6. Completa el pago en el gateway
  7. Gateway redirige de vuelta a /pagar/exito
  8. Se muestra confirmación

APIs:

  • GET /portal/deudas: Obtener facturas
  • POST /portal/pagos/iniciar: Iniciar pago

5. /cupones

Propósito: Ver cupones generados y generar nuevos para pago físico.

UI: Lista de cupones con filtros por estado (Todos, Pendientes, Usados, Vencidos).

Datos por cupón:

  • Código de barras (19 dígitos)
  • Monto
  • Estado (Pendiente, Usado, Vencido, Cancelado)
  • Fecha de vencimiento
  • Recibo vinculado (si fue usado)

Acciones:

  • Ver detalle de cupón
  • Descargar PDF
  • Generar nuevo cupón

APIs:

  • GET /portal/cupones/cliente/{id}: Listar cupones
  • POST /portal/cupones/generar: Generar nuevo cupón

5.1. /cupones/generar

Propósito: Generar PDF con código de barras para pagar en ubicación física.

Flujo:

  1. Cliente selecciona facturas
  2. Se genera código de barras único (ITF-14)
  3. Se crea PDF con:
    • Datos del cliente
    • Lista de facturas
    • Código de barras
    • Fecha de vencimiento
  4. Cliente puede descargar o imprimir

API: POST /portal/cupones/generar

6. /historial-pagos

Propósito: Ver historial completo de pagos realizados.

UI: Lista cronológica de pagos.

Datos por pago:

  • Fecha del pago
  • Método (Pago Online, Cupón)
  • Monto
  • Recibo vinculado
  • Estado

Acciones:

  • Ver detalle del pago
  • Ver recibo

API: GET /portal/pagos/historial

Comunicación con Backend

Cliente API (Axios)

Configuración:

  • Base URL desde variable de entorno
  • Timeout de 10 segundos
  • Headers automáticos:
    • Authorization: Bearer token de sesión
    • X-Client-Domain: Dominio actual (para multi-tenant)

Interceptores:

  • Request: Agregar token y dominio a cada request
  • Response: Redirigir a login si 401 (no autorizado)

Módulos API

Cada módulo agrupa endpoints relacionados:

  • auth.ts: Identificación de cliente
  • ctacte.ts: Cuenta corriente (deudas, saldos)
  • pagos.ts: Iniciar pagos, historial
  • cupones.ts: Generar, listar, obtener cupones

PWA (Progressive Web App)

Características:

  • Instalable: Se puede instalar en el home screen del móvil
  • Offline: Service Worker para cache de assets
  • Responsive: Diseño mobile-first
  • App-like: Se ve y funciona como app nativa

Manifest:

  • Nombre de la app (dinámico por tenant)
  • Iconos en 192x192 y 512x512
  • Color de tema (dinámico por tenant)
  • Orientación portrait

Caching Strategy:

  • API calls: NetworkFirst (datos frescos, fallback a cache)
  • Assets estáticos: CacheFirst (imágenes, CSS, JS)

Branding Dinámico

Propósito: Cada tenant tiene su propia identidad visual.

Configuración por tenant (desde backend):

  • app_name: Nombre de la aplicación
  • short_name: Nombre corto para PWA
  • logo_url: URL del logo
  • primary_color: Color principal (hex)
  • theme_color: Color del tema PWA

Aplicación:

  1. Al cargar el dominio, se obtiene configuración del tenant
  2. Se inyectan CSS variables en document.documentElement:
    • --color-primary
    • --color-secondary
  3. Se actualiza manifest.json dinámicamente
  4. Se carga logo desde URL del tenant

Ejemplo:

  • Empresa A usa azul (#1e40af) y logo propio
  • Empresa B usa verde (#10b981) y logo propio
  • Misma aplicación, diferente branding

Custom Hooks

useAuth: Gestionar estado de autenticación useDeudas: Cargar y filtrar deudas usePagos: Iniciar pagos y ver historial useCupones: Listar y generar cupones useBranding: Obtener y aplicar branding del tenant

Ventajas de hooks:

  • Reutilización de lógica
  • Separación de UI y lógica
  • Testing más fácil

Optimizaciones

Code Splitting

Cargar páginas pesadas solo cuando se necesitan (lazy loading):

  • PDF generation
  • Barcode rendering
  • Páginas de historial con muchos datos

Service Worker

Cachear automáticamente:

  • HTML/CSS/JS de la aplicación
  • API responses (corto tiempo)
  • Imágenes y assets estáticos

Beneficios:

  • Carga más rápida
  • Funciona offline (parcialmente)
  • Reduce uso de datos móviles

Flujo de Usuario Completo

  1. Cliente ingresa al dominio del tenant (ej: ctacte.empresaa.com.ar)
  2. Sistema resuelve tenant y carga branding
  3. Cliente se identifica con DNI/CUIT
  4. Ve dashboard con resumen de cuenta
  5. Consulta deudas pendientes
  6. Opción A: Pago Online
    • Selecciona facturas
    • Paga en gateway
    • Ve confirmación
  7. Opción B: Generar Cupón
    • Selecciona facturas
    • Genera PDF con código de barras
    • Paga en ubicación física
  8. Ve historial de pagos realizados

Próximos Pasos

  1. Ver documentación de páginas individuales en pages/
  2. Revisar componentes reutilizables en components/
  3. Consultar integración de API en detalle