Estructura del frontend
Repo aura-front: React 18 + TypeScript + Vite, Tailwind + shadcn/ui (Radix), TanStack
Query, lucide-react. Auth con @cuatro-quinas/ajolote-client-web.
Separación socket / hook / UI
El core del dashboard separa la fuente de eventos del consumidor:
CopilotSocket(src/api/socket/): interface con dos implementaciones —RealCopilotSocket(WebSocket nativo, reconexión con backoff) yMockCopilotSocket(fixtures para demos/dev). Se elige porVITE_USE_MOCK.useAgentSocket(socket)(src/hooks/): hook puro conuseReducersobre los eventos. Sin datos hardcodeados, testeable con un socket fake.- Componentes de presentación (
src/components/copilot/): reciben estado y disparan callbacks; no tocan el WebSocket.
Cliente API
Todo el acceso al backend pasa por src/api/client.ts → apiFetch(path, init), que
resuelve la base URL y usa fetchAuthed (Bearer + refresh). Los módulos de src/api/*
(metrics, audit, feedback, audio-fork, retell) lo importan; no redefinen el fetch.
apiFetch / fetchAuthedCualquier request autenticado debe ir por apiFetch. Un fetch plano no manda el Bearer,
y el backend no puede identificar al usuario (req.user vacío) → los eventos de auditoría
salen sin user_id y los endpoints gateados dan 403.
Data fetching (React Query)
Los paneles usan useQuery con key [endpoint, ...filtros]. Esto:
- evita races: al cambiar filtros rápido, respuestas viejas no pisan a las nuevas (React Query descarta resultados de keys obsoletas);
- da caché y dedupe gratis.
No usar useEffect + fetch manual para data de servidor.
Componentes y helpers compartidos
Para no duplicar entre paneles:
| Módulo | Qué es |
|---|---|
components/copilot/shared/segmented-control.tsx | Toggle group (Día/Semana/Mes, filtros) — accesible (role=group, aria-pressed) |
components/copilot/shared/period-selector.tsx | Dropdown de período |
components/copilot/shared/kpi-card.tsx | Tarjeta de KPI (label + número + delta) sobre ui/Card |
lib/period.ts | getPeriodOptions, isoDate, daysInRange, pct, deltaLabel, tipos |
lib/chart-colors.ts | Paleta compartida de recharts |
components/ui/* | Primitivos shadcn (Card, Badge, Button, Select, PanelCard…) |
Reusar los primitivos
ui/en vez de<div>con clases repetidas.
Roles en la UI
Ver Autenticación. En resumen: useRole() +
RequireRole (rutas) + roles? en NAV_ITEMS (nav). El front solo oculta; la autoridad
es el backend.
Agregar una pantalla nueva
- Crear el componente en
components/copilot/. - Cliente en
src/api/<recurso>.tsusandoapiFetch. - Data con
useQuery. - Ruta en
agent-copilot.tsx(envuelta en<RequireRole>si aplica). - Item en
app-nav.tsx(conroles?si es de supervisor). - Reusar
SegmentedControl/PeriodSelector/KpiCard/ui/*.