feat(sprint-1-5): cierre Etapa 2 - formatPayback + fixes de marca

[
This commit is contained in:
2026-05-16 19:26:19 -03:00
parent f9a008bce5
commit eaaac88fc3
16 changed files with 1626 additions and 55 deletions

View File

@@ -1,5 +1,5 @@
import { TrendingDown, Calendar, BarChart3, DollarSign, Percent } from 'lucide-react'
import { formatCurrency, formatPercent } from '@/lib/format'
import { formatCurrency, formatPayback, formatPercent } from '@/lib/format'
import type { RoiResult } from '@/domain/roi'
interface RoiKpiBarProps {
@@ -9,7 +9,7 @@ interface RoiKpiBarProps {
}
function RoiKpiCard({
label, value, sub, icon, iconColor, valueColor,
label, value, sub, icon, iconColor, valueColor, compact,
}: {
label: string
value: string
@@ -17,6 +17,7 @@ function RoiKpiCard({
icon: React.ReactNode
iconColor: string
valueColor?: string
compact?: boolean
}) {
return (
<div data-testid="roi-kpi-card" className="bg-white rounded-xl border border-slate-200 shadow-sm p-5 flex items-start gap-4">
@@ -25,7 +26,7 @@ function RoiKpiCard({
</div>
<div className="min-w-0 flex-1">
<p className="text-xs font-medium text-slate-500 uppercase tracking-wide mb-1">{label}</p>
<p className={`text-2xl font-bold font-mono leading-tight ${valueColor ?? 'text-slate-900'}`}>{value}</p>
<p className={`${compact ? 'text-sm' : 'text-2xl'} font-bold font-mono leading-tight ${valueColor ?? 'text-slate-900'}`}>{value}</p>
{sub && <p className="text-xs text-slate-400 mt-0.5 leading-snug">{sub}</p>}
</div>
</div>
@@ -33,13 +34,7 @@ function RoiKpiCard({
}
export function RoiKpiBar({ roi, currency, horizonYears }: RoiKpiBarProps) {
const paybackStr = !isFinite(roi.paybackMonths)
? 'No se recupera'
: roi.paybackMonths === 0
? 'Inmediato'
: roi.paybackMonths < 24
? `${roi.paybackMonths.toFixed(1)} meses`
: `${roi.paybackYears.toFixed(1)} años`
const paybackStr = formatPayback(roi.paybackMonths)
const roiStr = !isFinite(roi.roiAnnualPercent)
? '∞%'
@@ -88,6 +83,7 @@ export function RoiKpiBar({ roi, currency, horizonYears }: RoiKpiBarProps) {
icon={<Calendar className="h-5 w-5 text-primary" />}
iconColor="bg-primary/10"
valueColor={roi.breaksEvenInHorizon ? 'text-emerald-700' : 'text-slate-900'}
compact
/>
<RoiKpiCard
label={`Acumulado (${horizonYears} años)`}

View File

@@ -73,7 +73,7 @@ export function TransparencySection({ activities, investment, roi }: Transparenc
<span className="font-medium">
ROI inusualmente alto detectado: {formatPercent(roi.roiAnnualPercent)}.
</span>{' '}
Un ROI superior al {formatPercent(ROI_HIGH_THRESHOLD)} anual suele indicar uno de estos casos:
Un ROI superior al {formatPercent(ROI_HIGH_THRESHOLD, 0)} anual suele indicar uno de estos casos:
</p>
<ul className="list-disc list-inside space-y-0.5 ml-1">
<li>Frecuencia anual del proceso sobreestimada</li>