# 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<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):
```tsx
{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:
```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 -- <archivo>`
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+]
```
