Apariencia
0046. Dashboard de producción: ruta /produccion (no /dashboard) y kpis no filtrables
Estado
Aceptada
Contexto
Al planificar la tarea dashboard-produccion, el nombre natural para la nueva página era "el dashboard" — pero /dashboard ya existe en camaroneras_admin como la página "Inicio": el NavItem fijo HOME, sin gate de permiso, y el destino de <Navigate to="/dashboard" replace /> tanto en la ruta raíz (/) como en el fallback de ProtectedRoute cuando falta un permiso. Recién al implementar se descubrió el choque.
Dos decisiones de diseño no obvias quedaron sin registrar:
- Dónde montar la página nueva sin romper el mecanismo de fallback existente.
- Si los kpis agregados debían responder a los mismos filtros client-side (año, ciclo, sector, piscina, rango de peso) que el resto de la página.
Decisión
Ruta nueva /produccion, /dashboard intacto. La página de producción se montó en /produccion (ProtectedRoute module="dashboard" action="ver"), con su propio ítem de nav ("Dashboard de producción", sección Operación). /dashboard (Inicio) no se tocó: sigue sin gate de permiso y sigue siendo el fallback de ProtectedRoute y el destino de /. Alternativa descartada: reemplazar el contenido de /dashboard — se descartó porque gatear esa ruta con dashboard:ver habría creado un loop de redirect para cualquier rol sin ese permiso (ProtectedRoute redirige ahí precisamente cuando falta un permiso).
Nota de nomenclatura: el módulo de permiso se llama dashboard (key RBAC), pero protege /produccion, no /dashboard. Se aceptó la colisión de vocabulario por ahora (ver hallazgo del code review de cierre) — si en el futuro se generaliza el fallback de ProtectedRoute (ver Consecuencias), conviene revisar el nombre de este módulo.
Kpis globales, no filtrables. data.kpis (agregados de la organización a la fecha de corte) viene fijo del backend (computeDashboardKpis, dashboard.calc.ts) y no recalcula al aplicar los filtros client-side — solo el donut, las barras, la tabla y el selector de piscina del gráfico de línea responden a los filtros. Alternativa descartada: recalcular los kpis en el frontend sobre filteredPools. Se descartó para no duplicar las fórmulas de agregación (promedio ponderado de sobrevivencia, FCR agregado no promediado) en dos lenguajes/repos con riesgo de que diverjan silenciosamente.
También se simplificó el contrato original: el plan de la tarea sugería un query param year en el backend (GET /reports/dashboard?year=) para acotar qué ciclos se agregan. Se omitió: como todos los filtros (año incluido) son client-side sobre la respuesta ya cargada, un year en el backend no tenía consumidor — decisión tomada durante la implementación, no una instrucción explícita del dueño del producto.
Consecuencias
- Un usuario sin
dashboard:versigue teniendo una home funcional (/dashboard); solo pierde el ítem de nav y el acceso a/produccion. - Los números de los kpis de arriba de la página no coinciden con la suma manual de las filas visibles si el usuario tiene un filtro activo — es intencional (kpis = organización completa), pero puede confundir si no se explica en la UI. Documentado acá y en
modulos/09-dashboard-produccion-flow.md; si genera confusión real con usuarios, considerar una nota visible en la página (ej. "kpis de toda la organización, no del filtro actual"). - El backend solo acepta
datecomo query param (igual que/reports/weekly); si algún día se necesita acotar por año en el servidor (ej. por volumen), agregarlo entonces con su propio caso de uso concreto, no de antemano. - Queda pendiente en el backlog (code review de cierre, 2026-07-17) generalizar el fallback de
ProtectedRoute/nav para que features futuras gateadas por permiso no repitan este mismo choque con/dashboard.
Referencias
- Tarea
dashboard-produccion(2026-07-17). camaroneras_admin/src/shared/components/ProtectedRoute.tsx,src/app/router.tsx,src/app/layout/AppLayout.tsx.camaroneras_backend/src/modules/reportes/dashboard.calc.ts(computeDashboardKpis).camaroneras_docs/modulos/09-dashboard-produccion-flow.md.- ADR-0044 — mismo módulo
reportes, precedente de acotar alcance de v1 (allí export; acá el query paramyear).