Files
HoruxDespachosNuevo/docs/superpowers/specs/2026-04-27-drill-down-sort-by-name-design.md

4.2 KiB

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 <th> 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:

    useTableSort<Cfdi, 'fecha' | 'total' | 'pago' | 'iva' | 'emisor' | 'receptor'>
    
  2. Agregar dos accesores al objeto pasado al hook:

    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 <th> planos por SortableHeader:

    // antes
    <th className="pb-3 font-medium">Nombre Emisor</th>
    <th className="pb-3 font-medium">Nombre Receptor</th>
    
    // después
    <SortableHeader label="Nombre Emisor"
                    active={getSortIndicator('emisor')}
                    onClick={() => toggleSort('emisor')} />
    <SortableHeader label="Nombre Receptor"
                    active={getSortIndicator('receptor')}
                    onClick={() => 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 <th> 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).