Skip to main content

Arquitectura y diseño

1. El patrón dominante: RSC + hidratación con SWR

Casi todas las pantallas siguen el mismo esquema de tres capas, que conviene entender antes de tocar cualquier página:

flowchart LR
P["page.tsx (RSC)<br/>Suspense + loading.tsx"] -->|await action| A["actions/*<br/>fetchAuth → robo-api"]
P -->|initialData| T["Tabla/formulario (cliente)"]
T -->|"useSWR(fallbackData)"| H["hooks/*"]
H -->|fetch| R["app/api/* (route handler)"]
R -->|reutiliza| A
  1. page.tsx (Server Component) llama directamente a una función de actions/ y pasa el resultado como initialData. Va envuelto en <Suspense> con su loading.tsx, así la navegación muestra un skeleton inmediato.
  2. El componente de cliente monta un hook de hooks/ que hace useSWR(..., { fallbackData: initialData }). El primer render no pide nada: usa lo que ya vino del servidor.
  3. A partir de ahí SWR revalida (revalidateOnFocus: true en casi todos los hooks) llamando a un route handler de app/api/, que a su vez reutiliza la misma función de actions/.

Es decir: la misma llamada a robo-api está disponible por dos caminos — server-side en el primer render y vía /api en las revalidaciones y mutaciones.

En el detalle de una devolución, la carga es aún más agresiva: getReturn(id) primero (necesario para conocer el país del pedido) y después Promise.allSettled de métodos, estados, estados de línea, orígenes y almacenes, de forma que un catálogo caído degrada esa parte de la UI a un array vacío en lugar de tumbar la página.

2. Capa de acceso a datos (actions/)

Una carpeta por dominio, una función por operación. Todas comparten el mismo patrón:

const response = await fetchAuth(`${process.env.ROBO_API_URL}/return-method/info`, { method: "GET" });
if (!response.ok) throw new Error(DEFAULT_ERROR_MESSAGE);
return await response.json();
CarpetaOperaciones
actions/returns/get-returns (filtrado y paginado), get-return, edit, delete
actions/methods/get-methods, get-method, search-methods, new, edit, delete
actions/reasons/get-reasons, get-reason, new, edit, delete
actions/users/get-users, get-user, new, edit
actions/warehouses/get-warehouses, edit
actions/statuses/get-statuses, get-line-statuses
actions/origins/, actions/sources/, actions/payment-methods/, actions/logs/catálogos e historial
actions/getUserRoles.tsÚnico "use server" real: lee los roles del ID token

Dos de ellas ordenan en cliente lo que la API no ordena: getWarehouses por name y getSources por cd_source_type.

3. Route handlers (app/api/)

Unos 30 handlers, casi todos envoltorios finos de la acción equivalente (try/catch + Response.json). Existen para que SWR pueda llamarlos desde el navegador. Los que hacen algo más que envolver:

RutaParticularidad
/api/returns/getReturnsTraduce pageIndex, pageSize y columnFilters (JSON) a los parámetros de robo-api
/api/export/csv/returnsGenera el CSV paginando el listado completo (ver Gestión de devoluciones)
/api/get-orderRecupera el pedido y asigna un crypto.randomUUID() a cada línea
/api/set-returnCrea la devolución añadiendo customer: <sub de Auth0> del empleado
/api/get-pdf-urlFirma la URL del PDF (HMAC)
/api/trigger-journeyPublica el evento en Marketing Cloud según la acción
/api/tokenDevuelve el access token de la sesión

4. Estado

No hay store global. El estado se reparte en tres mecanismos:

MecanismoQué guarda
SWRCaché de datos del servidor. Es la fuente de verdad de todas las listas y detalles
react-hook-formEstado de edición de los formularios; se resetea con form.reset() cada vez que SWR revalida
RefundContextAlta manual de devoluciones, persistida en localStorage (refundProcess)
sessionStorageFiltros y paginación del listado de devoluciones (RETURNS_TABLE_STATE)
RoleContextRoles del usuario, inyectados desde el layout

Las mutaciones van siempre por useSWRMutation (trigger) seguido de mutate() para revalidar la caché, y notifican con toasts de sonner.

5. Diccionario de UI

No hay framework de i18n: la interfaz está en español fijo mediante constants/dictionary.ts, un objeto plano con ~140 claves (dictionary.warehouse, dictionary.error.required_fields, dictionary.success.return_updated…). Los mensajes de error y de éxito de todo el proyecto salen de ahí, incluidos los que consumen los esquemas de zod.

Ojo con la distinción: la UI es monolingüe, pero el contenido que administra es multiidioma. constants/languages.ts define los 7 idiomas (en, es, fr, de, it, pt, el) en los que se editan los textos de métodos y motivos, y en los que se puede descargar el PDF de una devolución.

6. Tablas de datos

Base común en components/admin/data-table/, construida sobre TanStack Table. La DataTable acepta manualPagination + rowCount: solo el listado de devoluciones lo activa (paginación y filtrado en servidor, con totalElements de robo-api como total); los listados de métodos, motivos, usuarios y almacenes traen todo el catálogo y paginan/filtran en cliente. Además:

  • data-table-column-selector — mostrar/ocultar columnas.
  • data-table-quick-filters y popover-filter — filtros por catálogo con búsqueda (cmdk).
  • usePinColums — fija columnas a izquierda/derecha cuando la tabla desborda horizontalmente, reaccionando a resize y scroll.
  • status-badge — badge de color por estado, reutilizado en listado y detalle.

Cada dominio aporta su propio columns.tsx con las cabeceras tomadas del diccionario.

7. Componentes destacados

ComponenteFunción
components/returns/table.tsxListado de devoluciones con filtros, búsqueda, CSV y persistencia de estado
components/returns/return/content.tsxDetalle: formulario de edición completo con la matriz de editabilidad
components/returns/return/table.tsxTabla de líneas editables + acciones masivas Aprobar todas / Reembolsar todas
components/returns/return/logs.tsxHistorial de cambios con diff entre entradas consecutivas
components/returns/return/pdf-button.tsxDesplegable que firma y abre el PDF en cualquiera de los 7 idiomas
components/admin/language-selector.tsxAñade/quita idiomas en los campos translations de métodos y motivos
components/rich-text-editor/default.tsxEditor WYSIWYG de instrucciones y descripciones (con botón de HTML crudo)

8. Selectores para automatización

El markup expone atributos data-test-id en los puntos de interacción relevantes (return-create-button, return-edit-button, return-status-selector, return-store-selector, return-claimed-goods-selector, return-final-price-input, return-pdf-button, return-log-button, method-create-button…).

Es una convención distinta a la del portal del cliente, que usa clases qa-*. Conviene mantenerlos al refactorizar componentes.