9.3 KiB
Sprint 2 — Etapa 3: Reporte web + cierre de deuda técnica
Tipo: UI de reporte + deuda técnica. Sin cambios en dominio, persistencia ni workspace. Objetivo: agregar desglose de recursos por actividad en la tabla del reporte, y cerrar los tres ítems de TECH_DEBT pendientes (CumulativeSavingsChart, payback label, badge ⚡). Resultado esperado: tabla con filas expandibles mostrando el desglose de costos, gráfico financieramente correcto, badge evaluado. Validación visual del director requerida antes del OK formal.
Archivos a modificar
src/features/report/ActivitiesTable.tsx ← principal (expandable rows)
src/features/report/CumulativeSavingsChart.tsx ← verificar -inversión + payback label
src/features/workspace/BpmnCanvas.tsx ← badge ⚡ tamaño (si la decisión es cambiar)
Si durante la implementación identificás que otro archivo necesita cambio, detenete y reportá el conflicto antes de tocarlo.
Feature 1 — ActivitiesTable: filas expandibles con desglose de recursos
Contexto
ActivitySimResult ya incluye resourceCostBreakdown: Array<{ resourceId, resourceName, cost }>. Los datos están disponibles — solo falta mostrarlos.
El expectedDirectCost actual ya incluye el costo de recursos (es fixedCost + resourceCost). Lo que la tabla no muestra es el desglose interno de ese costo directo.
Comportamiento esperado
- Cada fila de la tabla tiene un chevron expandible a la izquierda del indicador de color
- Al hacer click en la fila (o en el chevron), se expande una fila secundaria inline
- La fila expandida muestra el desglose del costo directo:
- Fijo (insumos, licencias):
directCostFixed— nota: este valor no está enActivitySimResult, ver abajo - Cada recurso: nombre + costo esperado (ya en
resourceCostBreakdown[].cost)
- Fijo (insumos, licencias):
- Si
resourceCostBreakdownestá vacío, la fila sigue siendo expandible pero muestra "Sin recursos asignados — costo es 100% fijo" - Si
resourceCostBreakdownestá vacío YexpectedDirectCost === 0, no mostrar chevron (no hay desglose útil)
Sobre el costo fijo vs. costo de recursos
ActivitySimResult tiene expectedDirectCost (total) y resourceCostBreakdown (recursos). El costo fijo esperado = expectedDirectCost - sum(resourceCostBreakdown[].cost).
No tocar el motor de simulación ni los tipos. Calcular el costo fijo esperado en el componente con esa resta.
Cambios en ActivitiesTable.tsx
Agregar al estado local:
const [expandedRows, setExpandedRows] = useState<Set<string>>(new Set())
function toggleRow(activityId: string) {
setExpandedRows(prev => {
const next = new Set(prev)
if (next.has(activityId)) next.delete(activityId)
else next.add(activityId)
return next
})
}
Cambio en el header: reemplazar la columna de color <TableHead className="w-8" /> por una columna ligeramente más ancha que incluye tanto el chevron como el indicador de color. O bien agregar una columna de chevron antes del indicador.
En cada fila: reemplazar el onClick={() => onRowClick(act.bpmnElementId)} para manejar expansión Y selección en el heatmap. La expansión se dispara desde el chevron, el click en la fila sigue navegando al heatmap.
Fila expandida (insertar como <TableRow> adicional inmediatamente después de la fila principal cuando está expandida):
{isExpanded && (
<TableRow className="bg-slate-50/70 hover:bg-slate-50/70">
<TableCell /> {/* chevron vacío */}
<TableCell colSpan={7} className="py-2 pl-4">
<div className="space-y-1">
{/* Costo fijo */}
{fixedCostExpected > 0 && (
<div className="flex justify-between text-xs text-slate-500">
<span>Fijo (insumos, licencias)</span>
<span className="font-mono">{formatCurrency(fixedCostExpected, currency)}</span>
</div>
)}
{/* Recursos */}
{act.resourceCostBreakdown.map((r) => (
<div key={r.resourceId} className="flex justify-between text-xs text-slate-500">
<span>{r.resourceName}</span>
<span className="font-mono">{formatCurrency(r.cost, currency)}</span>
</div>
))}
{/* Si solo hay costo fijo */}
{act.resourceCostBreakdown.length === 0 && fixedCostExpected === 0 && (
<p className="text-xs text-slate-400">Sin desglose disponible</p>
)}
</div>
</TableCell>
</TableRow>
)}
Versión con wow
El chevron rota 90° al expandir (transición CSS). El color del desglose de recursos usa un tono ligeramente más cálido para el total (naranja muy sutil, no el naranja InQ completo).
Feature 2 — CumulativeSavingsChart: verificar -inversión + fix payback label
2.1 Verificación de -inversión
Antes de tocar el archivo, verificar la línea:
const values = years.map((y) => roi.annualSavings * y - investment)
Cuando y=0: 0 - investment = -investment. Si esto es lo que hay en el código, el ítem de TECH_DEBT ya está resuelto. Documentarlo en el reporte y cerrar el ítem. No hacer ningún cambio en ese aspecto.
Si por alguna razón el código es diferente (ej: roi.annualSavings * y sin restar la inversión), corregirlo.
2.2 Fix del payback label superpuesto
El label actual tiene position: 'insideEndTop'. Cuando el payback cae en años tempranos (cerca de x=0), el label se superpone con los labels del eje Y izquierdo.
Cambiar a position: 'end' con un offset explícito, o usar position: 'insideStartTop' con padding. La solución más robusta:
label: {
formatter: `Payback\n${roi.paybackYears.toFixed(1)} años`,
position: 'insideEndTop',
fontSize: 10,
color: '#16A34A',
padding: [0, 0, 0, 8], // padding izquierdo para separar del eje Y
offset: [4, 0],
},
Verificar visualmente que el label no colisione con el eje Y en casos de payback temprano (año 1) ni tardío (año 3).
Feature 3 — Badge ⚡: evaluar tamaño
Contexto
El badge actual es 14×14px (width:14px; height:14px; line-height:14px en makeBadgeHtml()). El director observó "podría ser un poquito más grande".
Decisión a tomar en la implementación
- Cambiar a 16×16px
- Verificar visualmente que no colisione con bordes de nodos chicos del BPMN (los nodos de evento son típicamente 36px de diámetro)
- Si no hay colisión: mantener 16×16px y reportar
- Si hay colisión visible: revertir a 14×14px y documentar por qué
El badge se posiciona 8px fuera del borde derecho del nodo. Con 16×16px, verificar también que ese offset siga siendo visualmente correcto.
Verificaciones obligatorias antes de reportar
1. Verificación de scope con git diff — OBLIGATORIO
git diff --name-only HEAD
El resultado debe contener únicamente:
src/features/report/ActivitiesTable.tsx
src/features/report/CumulativeSavingsChart.tsx
src/features/workspace/BpmnCanvas.tsx ← solo si se cambió el badge
Si aparece cualquier archivo no listado:
- Revertilo:
git checkout HEAD -- <archivo> - Listarlo bajo "Conflictos de scope" en el reporte
- No incluirlo en el entregable
2. Build limpio
npm run build
Sin errores TypeScript.
3. Tests
npm run test
Todos los tests existentes deben seguir verdes. Esta etapa no agrega tests nuevos (cambios de UI pura y fix trivial de layout).
4. Smoke test visual
- Tabla del reporte: chevron visible en actividades con datos
- Expandir una actividad con recursos: muestra desglose fijo + recursos
- Expandir una actividad sin recursos: muestra "Sin recursos asignados"
- CumulativeSavingsChart: el gráfico arranca debajo de 0 cuando hay inversión
- Payback label: no colisiona con eje Y
- Badge ⚡: tamaño correcto, sin colisión con nodos chicos
Alcance estricto — lo que NO entra en esta etapa
- ❌
ActivityPanel.tsxniResourcesPanel.tsx— Etapa 2 ya los cerró - ❌ Edición inline de proceso/cliente — Etapa 4
- ❌ PDFs — Etapa 5
- ❌ Indicador "datos pendientes de simular" — BACKLOG (requiere decisión de UX)
- ❌
ComparisonTable.tsx— no está en scope de esta etapa - ❌ Cualquier cambio en dominio (
src/domain/) o persistencia (src/persistence/)
Reporte esperado al terminar
## Etapa 3 completada
### Archivos modificados
- src/features/report/ActivitiesTable.tsx — filas expandibles con desglose
- src/features/report/CumulativeSavingsChart.tsx — [qué se cambió]
- src/features/workspace/BpmnCanvas.tsx — [badge 14→16px / mantenido en 14px, razón]
### Cambios realizados
- ActivitiesTable: expandable rows con chevron, desglose fijo + recursos
- CumulativeSavingsChart -inversión: [ya estaba correcto / corregido]
- Payback label: offset ajustado, sin colisión
- Badge: [16px sin colisión / mantenido 14px por colisión en nodos chicos]
### TECH_DEBT cerrados
- CumulativeSavingsChart arranca en -inversión: ✅ [ya estaba / corregido]
- Payback label superpuesto: ✅ corregido
- Badge ⚡ tamaño: ✅ [16px / 14px mantenido con razón]
### Verificaciones
- git diff --name-only HEAD: [lista de archivos] ✅
- npm run build: ✅
- npm run test: X/X tests verdes ✅
- Smoke test: [descripción breve]
### Conflictos de scope
[ninguno / lista de archivos revertidos y razón]
### Notas para Etapas siguientes
[hallazgos para Etapas 4+]