# 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á en `ActivitySimResult`, ver abajo - Cada recurso: nombre + costo esperado (ya en `resourceCostBreakdown[].cost`) - Si `resourceCostBreakdown` está vacío, la fila sigue siendo expandible pero muestra "Sin recursos asignados — costo es 100% fijo" - Si `resourceCostBreakdown` está vacío Y `expectedDirectCost === 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:** ```tsx const [expandedRows, setExpandedRows] = useState>(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 `` 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 `` adicional inmediatamente después de la fila principal cuando está expandida): ```tsx {isExpanded && ( {/* chevron vacío */}
{/* Costo fijo */} {fixedCostExpected > 0 && (
Fijo (insumos, licencias) {formatCurrency(fixedCostExpected, currency)}
)} {/* Recursos */} {act.resourceCostBreakdown.map((r) => (
{r.resourceName} {formatCurrency(r.cost, currency)}
))} {/* Si solo hay costo fijo */} {act.resourceCostBreakdown.length === 0 && fixedCostExpected === 0 && (

Sin desglose disponible

)}
)} ``` ### 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: ```typescript 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: ```typescript 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 1. Cambiar a 16×16px 2. Verificar visualmente que no colisione con bordes de nodos chicos del BPMN (los nodos de evento son típicamente 36px de diámetro) 3. Si no hay colisión: mantener 16×16px y reportar 4. 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 ```bash 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: 1. Revertilo: `git checkout HEAD -- ` 2. Listarlo bajo "Conflictos de scope" en el reporte 3. No incluirlo en el entregable ### 2. Build limpio ```bash npm run build ``` Sin errores TypeScript. ### 3. Tests ```bash 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.tsx` ni `ResourcesPanel.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+] ```