feat: add explicit period date ranges and absolute metric differences to experiment summary

This commit is contained in:
Christian Vidal Wolf
2026-02-21 23:05:10 +01:00
parent 09cfc1e6a8
commit 7ee33671cc
+43 -9
View File
@@ -254,6 +254,33 @@ const ExperimentDetail: React.FC<ExperimentDetailProps> = ({ experimentId, onClo
<div className="space-y-6"> <div className="space-y-6">
{/* Performance Cards */} {/* Performance Cards */}
{(experiment.experiment_units != null) && ( {(experiment.experiment_units != null) && (
<>
<div className="bg-slate-800/40 border border-slate-700/60 rounded-xl p-3 mb-2 flex flex-col md:flex-row gap-4 justify-between items-center text-center md:text-left">
<div className="flex flex-col">
<span className="text-[10px] text-slate-400 font-bold uppercase tracking-wider mb-1">Período Baseline (Referencia)</span>
<span className="text-sm text-slate-300 font-medium whitespace-nowrap">
{(() => {
const sd = new Date(experiment.start_date);
const ed = experiment.end_date ? new Date(experiment.end_date) : new Date();
const durationMs = ed.getTime() - sd.getTime();
const bd = new Date(sd.getTime() - durationMs);
return `${bd.toLocaleDateString('es-ES', { day: '2-digit', month: 'short', year: 'numeric' })} — ${sd.toLocaleDateString('es-ES', { day: '2-digit', month: 'short', year: 'numeric' })}`;
})()}
</span>
</div>
<div className="text-slate-600 text-xs hidden md:block">VS</div>
<div className="flex flex-col md:text-right">
<span className="text-[10px] text-indigo-400 font-bold uppercase tracking-wider mb-1">Período del Experimento</span>
<span className="text-sm text-white font-medium whitespace-nowrap">
{(() => {
const sd = new Date(experiment.start_date);
const ed = experiment.end_date ? new Date(experiment.end_date) : new Date();
return `${sd.toLocaleDateString('es-ES', { day: '2-digit', month: 'short', year: 'numeric' })} — ${ed.toLocaleDateString('es-ES', { day: '2-digit', month: 'short', year: 'numeric' })}`;
})()}
</span>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4"> <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
<MetricDeltaCard title="Units Sold" baseline={experiment.baseline_units} experimentVal={experiment.experiment_units} format="number" /> <MetricDeltaCard title="Units Sold" baseline={experiment.baseline_units} experimentVal={experiment.experiment_units} format="number" />
<MetricDeltaCard title="Revenue" baseline={experiment.baseline_revenue} experimentVal={experiment.experiment_revenue} format="currency" /> <MetricDeltaCard title="Revenue" baseline={experiment.baseline_revenue} experimentVal={experiment.experiment_revenue} format="currency" />
@@ -264,7 +291,7 @@ const ExperimentDetail: React.FC<ExperimentDetailProps> = ({ experimentId, onClo
<PerformanceCard <PerformanceCard
label="Primary Target Lift" label="Primary Target Lift"
value={experiment.actual_lift_percent != null value={experiment.actual_lift_percent != null
? `${experiment.actual_lift_percent >= 0 ? '+' : ''}${experiment.actual_lift_percent}%` ? `${experiment.actual_lift_percent >= 0 ? '+' : ''}${experiment.actual_lift_percent.toFixed(1)}%`
: '—'} : '—'}
color={experiment.actual_lift_percent != null && experiment.actual_lift_percent >= 0 ? 'emerald' : 'red'} color={experiment.actual_lift_percent != null && experiment.actual_lift_percent >= 0 ? 'emerald' : 'red'}
/> />
@@ -276,6 +303,7 @@ const ExperimentDetail: React.FC<ExperimentDetailProps> = ({ experimentId, onClo
color="purple" color="purple"
/> />
</div> </div>
</>
)} )}
{/* Calculate Results Button */} {/* Calculate Results Button */}
@@ -640,30 +668,36 @@ const MetricDeltaCard: React.FC<{
}> = ({ title, baseline, experimentVal, format = 'number', invertColors = false }) => { }> = ({ title, baseline, experimentVal, format = 'number', invertColors = false }) => {
if (baseline == null || experimentVal == null) return null; if (baseline == null || experimentVal == null) return null;
const delta = baseline > 0 ? ((experimentVal - baseline) / baseline) * 100 : 0; const delta = baseline > 0 ? ((experimentVal - baseline) / baseline) * 100 : 0;
const absDiff = experimentVal - baseline;
const isPositive = invertColors ? delta <= 0 : delta >= 0; const isPositive = invertColors ? delta <= 0 : delta >= 0;
const colorClass = isPositive ? 'text-emerald-400' : 'text-red-400'; const colorClass = isPositive ? 'text-emerald-400' : 'text-red-400';
const bgClass = isPositive ? 'bg-emerald-500/10 border-emerald-500/30' : 'bg-red-500/10 border-red-500/30'; const bgClass = isPositive ? 'bg-emerald-500/10 border-emerald-500/30' : 'bg-red-500/10 border-red-500/30';
const formatter = (val: number) => { const formatter = (val: number) => {
if (format === 'percent') return `${val}%`; if (format === 'percent') return `${val.toFixed(2)}%`;
if (format === 'currency') return `${val.toLocaleString('de-DE', { minimumFractionDigits: 0, maximumFractionDigits: 0 })}`; if (format === 'currency') return `${Math.round(val).toLocaleString('de-DE')}`;
return val.toLocaleString('de-DE'); return Math.round(val).toLocaleString('de-DE');
}; };
const formattedAbsDiff = `${absDiff >= 0 ? '+' : ''}${formatter(absDiff)}`;
return ( return (
<div className={`p-4 rounded-xl border ${bgClass}`}> <div className={`p-4 rounded-xl border ${bgClass} flex flex-col justify-between`}>
<div className="text-xs font-semibold text-slate-300 mb-2">{title}</div> <div className="text-xs font-semibold text-slate-300 mb-3">{title}</div>
<div className="flex flex-col gap-1"> <div className="flex items-end justify-between mb-1">
<div className="flex items-end justify-between">
<div className="text-2xl font-bold text-white">{formatter(experimentVal)}</div> <div className="text-2xl font-bold text-white">{formatter(experimentVal)}</div>
<div className={`text-right flex flex-col items-end`}>
<div className={`text-sm font-bold ${colorClass}`}> <div className={`text-sm font-bold ${colorClass}`}>
{delta > 0 ? '+' : ''}{delta.toFixed(1)}% {delta > 0 ? '+' : ''}{delta.toFixed(1)}%
</div> </div>
<div className={`text-[10px] ${colorClass} opacity-80 font-medium`}>
{formattedAbsDiff}
</div> </div>
<div className="text-[10px] text-slate-500 font-medium">vs {formatter(baseline)} baseline avg</div>
</div> </div>
</div> </div>
<div className="text-[11px] text-slate-400 font-medium mt-1">vs <span className="text-slate-300">{formatter(baseline)}</span> (Baseline)</div>
</div>
); );
}; };