# Drill-down genérica — sort por nombre emisor/receptor ## Contexto La tabla de la página `/drill-down` (apps/web/app/(dashboard)/drill-down/page.tsx) actualmente permite ordenar por `Fecha`, `Total MXN`, `Monto Pago` e `IVA Trasl.` mediante el hook `useTableSort` y el componente `SortableHeader` de `@horux/shared-ui`. Las columnas `Nombre Emisor` y `Nombre Receptor` se renderizan como `` planos no ordenables. ## Objetivo Permitir ordenar también por nombre del emisor y por nombre del receptor, sin remover ninguna de las columnas ordenables existentes. Alcance limitado a la drill-down genérica. Las 9 páginas de `/alertas/*` quedan fuera de este cambio (decisión del owner — se evaluarán después). ## Cambios Archivo único: `apps/web/app/(dashboard)/drill-down/page.tsx`. 1. Extender el segundo parámetro de tipo de `useTableSort` para incluir las nuevas keys: ```ts useTableSort ``` 2. Agregar dos accesores al objeto pasado al hook: ```ts emisor: (c) => c.nombreEmisor || '', receptor: (c) => c.nombreReceptor || '', ``` `useTableSort` ya soporta accesores de tipo `string` — usa `String.prototype.localeCompare` cuando ambos valores son strings, lo cual maneja la collation del español correctamente. 3. Reemplazar los dos `` planos por `SortableHeader`: ```tsx // antes Nombre Emisor Nombre Receptor // después toggleSort('emisor')} /> toggleSort('receptor')} /> ``` 4. Mantener el `initialKey = 'fecha'` y `initialDir = 'desc'` (default actual). ## No-cambios - No se tocan: `useTableSort`, `SortableHeader`, ni cualquier otro archivo en `@horux/shared-ui`. - No se tocan controllers ni services del API. El sort es 100% client-side. - No se tocan las columnas RFC Emisor, RFC Receptor, UUID, Comp., M. Pago, Reg. E ni Reg. R — siguen siendo `` planos no ordenables. - No se modifica el export a Excel: ya consume `sortedData`, así que el orden vigente del usuario se respeta automáticamente. ## Comportamiento esperado - Click sobre "Nombre Emisor": ordena ascendente por nombre. Re-click: descendente. Cambia el sort activo (un solo sort a la vez, ya es el contrato del hook). - Click sobre "Nombre Receptor": idéntico, reemplaza al sort previo. - Filas con `nombreEmisor` o `nombreReceptor` null/undefined: el accesor retorna string vacío `''`, así que en `asc` aparecen primero. Es el comportamiento estándar de `localeCompare` y se considera aceptable (un CFDI sin nombre emisor/receptor es raro y debería ser visible al ordenar por nombre). ## Riesgo Mínimo: - Cambio puramente client-side, una sola página, ~6 líneas netas. - No introduce dependencias nuevas. - `pnpm typecheck` debería seguir limpio (las nuevas keys están dentro del union genérico, los accesores cumplen el contrato `(row: T) => number | string`). ## Plan de pruebas (smoke) 1. `pnpm typecheck` debe seguir en 0 errores. 2. Abrir `/drill-down` desde cualquier KPI del dashboard. 3. Click en "Nombre Emisor" → verificar orden alfabético ascendente y flecha en el header. Re-click → descendente. 4. Click en "Nombre Receptor" → mismo comportamiento. 5. Click en "Fecha" / "Total MXN" → confirmar que los sorts pre-existentes siguen funcionando. 6. Exportar a Excel después de ordenar por "Nombre Emisor" → confirmar que el archivo descargado mantiene el mismo orden. ## Pendientes derivados - Replicar el patrón en las 9 páginas de `/alertas/*` (cancelaciones, cancelaciones-periodo-anterior, efectivo, tipo-relacion-sospechosa, concentracion-clientes, concentracion-proveedores, discrepancia-regimen, lista-negra-clientes, lista-negra-proveedores). Decisión del owner cuándo abordarlas. Para `lista-negra-*` además habrá que introducir `useTableSort` desde cero (hoy no lo usan).