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
Some checks failed
/ Deploy to Cloudflare Pages (push) Has been cancelled
This commit is contained in:
@@ -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">
|
||||
|
||||
Reference in New Issue
Block a user