feat(auditoria): mostrar el nombre del usuario en lugar del id (tabla y filtro)
This commit is contained in:
@@ -23,8 +23,8 @@ const respuestaOk = (data: unknown) =>
|
||||
new Response(JSON.stringify(data), { status: 200, headers: { 'Content-Type': 'application/json' } })
|
||||
|
||||
const auditoriaFake = [
|
||||
{ id: 'a1', usuarioId: 'admin', accion: 'LEAD_CREADO', entidad: 'lead', entidadId: 'l1', detalle: 'Lead nuevo', createdAt: '2026-09-01T10:00:00Z' },
|
||||
{ id: 'a2', usuarioId: 'vendedor1', accion: 'ETAPA_CAMBIADA', entidad: 'oportunidad', entidadId: 'o1', detalle: 'Cambiado a Cotizador', createdAt: '2026-09-02T14:00:00Z' },
|
||||
{ id: 'a1', usuarioId: 'admin', usuarioNombre: 'Administrador FRASA', accion: 'LEAD_CREADO', entidad: 'lead', entidadId: 'l1', detalle: 'Lead nuevo', createdAt: '2026-09-01T10:00:00Z' },
|
||||
{ id: 'a2', usuarioId: 'vendedor1', usuarioNombre: 'Martin Zavala', accion: 'ETAPA_CAMBIADA', entidad: 'oportunidad', entidadId: 'o1', detalle: 'Cambiado a Cotizador', createdAt: '2026-09-02T14:00:00Z' },
|
||||
]
|
||||
|
||||
let fetchMock: ReturnType<typeof vi.fn<FetchFn>>
|
||||
@@ -49,9 +49,9 @@ describe('AuditoriaLog', () => {
|
||||
await waitFor(() => expect(fetchMock).toHaveBeenCalledOnce())
|
||||
expect(screen.getByText('LEAD_CREADO')).toBeTruthy()
|
||||
expect(screen.getByText('ETAPA_CAMBIADA')).toBeTruthy()
|
||||
// User IDs may appear in multiple cells; verify they exist
|
||||
expect(screen.getAllByText('admin').length).toBeGreaterThanOrEqual(1)
|
||||
expect(screen.getAllByText('vendedor1').length).toBeGreaterThanOrEqual(1)
|
||||
// La tabla muestra el NOMBRE del usuario (el id solo como respaldo/tooltip)
|
||||
expect(screen.getAllByText('Administrador FRASA').length).toBeGreaterThanOrEqual(1)
|
||||
expect(screen.getAllByText('Martin Zavala').length).toBeGreaterThanOrEqual(1)
|
||||
})
|
||||
|
||||
it('muestra "Sin registros" cuando no hay datos', async () => {
|
||||
|
||||
@@ -15,7 +15,7 @@ export function AuditoriaLog() {
|
||||
const [usuario, setUsuario] = useState('')
|
||||
const [fechaDesde, setFechaDesde] = useState('')
|
||||
const [fechaHasta, setFechaHasta] = useState('')
|
||||
const [usuariosLista, setUsuariosLista] = useState<string[]>([])
|
||||
const [usuariosLista, setUsuariosLista] = useState<[string, string][]>([])
|
||||
|
||||
const cargar = useCallback(async () => {
|
||||
setCargando(true)
|
||||
@@ -29,9 +29,12 @@ export function AuditoriaLog() {
|
||||
})
|
||||
setItems(data)
|
||||
setPagina(1)
|
||||
// Extraer usuarios únicos para el select
|
||||
const usrs = [...new Set(data.map((i) => i.usuarioId).filter(Boolean))] as string[]
|
||||
setUsuariosLista(usrs)
|
||||
// Usuarios únicos para el select: se muestra el nombre y se filtra por id
|
||||
const porId = new Map<string, string>()
|
||||
for (const i of data) {
|
||||
if (i.usuarioId) porId.set(i.usuarioId, i.usuarioNombre ?? i.usuarioId)
|
||||
}
|
||||
setUsuariosLista([...porId.entries()].sort((a, b) => a[1].localeCompare(b[1])))
|
||||
} catch {
|
||||
setError('No se pudieron cargar los registros de auditoría.')
|
||||
} finally {
|
||||
@@ -74,9 +77,9 @@ export function AuditoriaLog() {
|
||||
className="ml-2 rounded-lg border border-[#D1D5DB] bg-white px-2 py-1.5 text-xs dark:border-[#334155] dark:bg-[#0F172A] dark:text-[#E2E8F0]"
|
||||
>
|
||||
<option value="">Todos</option>
|
||||
{usuariosLista.map((u) => (
|
||||
<option key={u} value={u}>
|
||||
{u}
|
||||
{usuariosLista.map(([id, nombre]) => (
|
||||
<option key={id} value={id}>
|
||||
{nombre}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
@@ -136,7 +139,9 @@ export function AuditoriaLog() {
|
||||
<td className="whitespace-nowrap px-3 py-2 text-[#374151] dark:text-[#CBD5E1]">
|
||||
{new Date(item.createdAt).toLocaleString('es-MX')}
|
||||
</td>
|
||||
<td className="px-3 py-2 text-[#374151] dark:text-[#CBD5E1]">{item.usuarioId}</td>
|
||||
<td className="whitespace-nowrap px-3 py-2 text-[#374151] dark:text-[#CBD5E1]" title={item.usuarioId}>
|
||||
{item.usuarioNombre ?? item.usuarioId}
|
||||
</td>
|
||||
<td className="px-3 py-2 text-[#374151] dark:text-[#CBD5E1]">{item.accion}</td>
|
||||
<td className="px-3 py-2 text-[#374151] dark:text-[#CBD5E1]">{item.entidad}</td>
|
||||
<td className="max-w-[200px] truncate px-3 py-2 text-[#64748B] dark:text-[#94A3B8]">{item.detalle ?? '—'}</td>
|
||||
|
||||
@@ -170,6 +170,8 @@ export async function reindexarConocimiento(): Promise<{
|
||||
export interface AuditoriaItem {
|
||||
id: string
|
||||
usuarioId: string
|
||||
/** Nombre del usuario que hizo la acción (el API lo resuelve) */
|
||||
usuarioNombre?: string
|
||||
accion: string
|
||||
entidad: string
|
||||
entidadId: string
|
||||
|
||||
Reference in New Issue
Block a user