feat(sprint-6/etapa-2): panel de análisis de sensibilidad + doble línea en CumulativeSavingsChart
Some checks failed
/ Deploy to Cloudflare Pages (push) Has been cancelled

This commit is contained in:
2026-06-24 16:42:32 -03:00
parent b9cd57d148
commit 1e4dbc0bed
4 changed files with 373 additions and 39 deletions

View File

@@ -22,6 +22,7 @@ import { CostByResourceChart } from './CostByResourceChart'
import { ActivitiesTable } from './ActivitiesTable'
import { ComparisonTable } from './ComparisonTable'
import { CumulativeSavingsChart } from './CumulativeSavingsChart'
import { SensitivityPanel } from './SensitivityPanel'
import { TopSavingsChart } from './TopSavingsChart'
import { TransparencySection } from './TransparencySection'
import { AutomationScenarioNote } from './AutomationScenarioNote'
@@ -67,6 +68,24 @@ export function ReportPage() {
})
}, [simulation, process])
// Sliders del panel de sensibilidad — efímero, no persiste (Sprint 6 Etapa 2)
const [volumeAdjust, setVolumeAdjust] = useState(0) // entero: -50 a +100
const [investmentAdjust, setInvestmentAdjust] = useState(0) // entero: -50 a +100
// ROI ajustado por sliders de sensibilidad — recalcula solo los agregados anuales,
// sin tocar costPerExecutionActual ni costPerExecutionAutomated (son estables, no se re-simula).
const adjustedRoi = useMemo(() => {
if (!roi || !simulation?.result || !simulation?.resultAutomated || !process) return null
if (volumeAdjust === 0 && investmentAdjust === 0) return roi // sin cambios → mismo objeto
return calculateRoi({
costPerExecutionActual: simulation.result.totalCost,
costPerExecutionAutomated: simulation.resultAutomated.totalCost,
annualFrequency: process.annualFrequency * (1 + volumeAdjust / 100),
analysisHorizonYears: process.analysisHorizonYears,
automationInvestment: process.automationInvestment * (1 + investmentAdjust / 100),
})
}, [roi, simulation, process, volumeAdjust, investmentAdjust])
// Breakdown del escenario actual, indexado por activityId, para comparación lado a lado en tab Automatizado
const actualBreakdownById = useMemo(() => {
if (!simulation?.result) return undefined
@@ -509,14 +528,24 @@ export function ReportPage() {
<NeedsResimulate processId={processId} />
) : (
<>
{/* KPIs de ROI */}
{/* KPIs de ROI — actualizados por sliders de sensibilidad */}
<RoiKpiBar
roi={roi}
roi={adjustedRoi ?? roi}
currency={process.currency}
horizonYears={process.analysisHorizonYears}
/>
{/* Tabla de comparación actividad por actividad */}
{/* Panel de sensibilidad — colapsado por defecto */}
<SensitivityPanel
process={process}
volumeAdjust={volumeAdjust}
investmentAdjust={investmentAdjust}
onVolumeChange={setVolumeAdjust}
onInvestmentChange={setInvestmentAdjust}
onReset={() => { setVolumeAdjust(0); setInvestmentAdjust(0) }}
/>
{/* Tabla de comparación actividad por actividad — per-ejecución, no varía con los sliders */}
<div className="mb-6">
<h2 className="text-base font-semibold text-slate-800 mb-4">
Comparación por actividad
@@ -536,10 +565,11 @@ export function ReportPage() {
Ahorro acumulado neto ({process.analysisHorizonYears} años)
</h3>
<CumulativeSavingsChart
roi={roi}
roi={adjustedRoi ?? roi}
roiBase={volumeAdjust !== 0 || investmentAdjust !== 0 ? roi : undefined}
horizonYears={process.analysisHorizonYears}
currency={process.currency}
investment={process.automationInvestment}
investment={process.automationInvestment * (1 + investmentAdjust / 100)}
/>
</div>
<div className="bg-white rounded-xl border border-slate-200 shadow-sm p-5">