mirror of
https://github.com/christianvidalwolf-prog/CrazeAnalytix.git
synced 2026-08-03 14:05:24 +02:00
feat: add explicit period date ranges and absolute metric differences to experiment summary
This commit is contained in:
@@ -254,28 +254,56 @@ const ExperimentDetail: React.FC<ExperimentDetailProps> = ({ experimentId, onClo
|
||||
<div className="space-y-6">
|
||||
{/* Performance Cards */}
|
||||
{(experiment.experiment_units != null) && (
|
||||
<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="Revenue" baseline={experiment.baseline_revenue} experimentVal={experiment.experiment_revenue} format="currency" />
|
||||
<MetricDeltaCard title="Glance Views" baseline={experiment.baseline_gv} experimentVal={experiment.experiment_gv} format="number" />
|
||||
<MetricDeltaCard title="Conversion Rate" baseline={experiment.baseline_cvr} experimentVal={experiment.experiment_cvr} format="percent" />
|
||||
<MetricDeltaCard title="ACOS" baseline={experiment.baseline_acos} experimentVal={experiment.experiment_acos} format="percent" invertColors={true} />
|
||||
<>
|
||||
<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>
|
||||
|
||||
<PerformanceCard
|
||||
label="Primary Target Lift"
|
||||
value={experiment.actual_lift_percent != null
|
||||
? `${experiment.actual_lift_percent >= 0 ? '+' : ''}${experiment.actual_lift_percent}%`
|
||||
: '—'}
|
||||
color={experiment.actual_lift_percent != null && experiment.actual_lift_percent >= 0 ? 'emerald' : 'red'}
|
||||
/>
|
||||
<PerformanceCard
|
||||
label="Significance"
|
||||
value={experiment.statistical_significance
|
||||
? `${(experiment.statistical_significance * 100).toFixed(1)}%`
|
||||
: 'Pending'}
|
||||
color="purple"
|
||||
/>
|
||||
</div>
|
||||
<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="Revenue" baseline={experiment.baseline_revenue} experimentVal={experiment.experiment_revenue} format="currency" />
|
||||
<MetricDeltaCard title="Glance Views" baseline={experiment.baseline_gv} experimentVal={experiment.experiment_gv} format="number" />
|
||||
<MetricDeltaCard title="Conversion Rate" baseline={experiment.baseline_cvr} experimentVal={experiment.experiment_cvr} format="percent" />
|
||||
<MetricDeltaCard title="ACOS" baseline={experiment.baseline_acos} experimentVal={experiment.experiment_acos} format="percent" invertColors={true} />
|
||||
|
||||
<PerformanceCard
|
||||
label="Primary Target Lift"
|
||||
value={experiment.actual_lift_percent != null
|
||||
? `${experiment.actual_lift_percent >= 0 ? '+' : ''}${experiment.actual_lift_percent.toFixed(1)}%`
|
||||
: '—'}
|
||||
color={experiment.actual_lift_percent != null && experiment.actual_lift_percent >= 0 ? 'emerald' : 'red'}
|
||||
/>
|
||||
<PerformanceCard
|
||||
label="Significance"
|
||||
value={experiment.statistical_significance
|
||||
? `${(experiment.statistical_significance * 100).toFixed(1)}%`
|
||||
: 'Pending'}
|
||||
color="purple"
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* Calculate Results Button */}
|
||||
@@ -640,29 +668,35 @@ const MetricDeltaCard: React.FC<{
|
||||
}> = ({ title, baseline, experimentVal, format = 'number', invertColors = false }) => {
|
||||
if (baseline == null || experimentVal == null) return null;
|
||||
const delta = baseline > 0 ? ((experimentVal - baseline) / baseline) * 100 : 0;
|
||||
const absDiff = experimentVal - baseline;
|
||||
|
||||
const isPositive = invertColors ? delta <= 0 : delta >= 0;
|
||||
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 formatter = (val: number) => {
|
||||
if (format === 'percent') return `${val}%`;
|
||||
if (format === 'currency') return `€${val.toLocaleString('de-DE', { minimumFractionDigits: 0, maximumFractionDigits: 0 })}`;
|
||||
return val.toLocaleString('de-DE');
|
||||
if (format === 'percent') return `${val.toFixed(2)}%`;
|
||||
if (format === 'currency') return `€${Math.round(val).toLocaleString('de-DE')}`;
|
||||
return Math.round(val).toLocaleString('de-DE');
|
||||
};
|
||||
|
||||
const formattedAbsDiff = `${absDiff >= 0 ? '+' : ''}${formatter(absDiff)}`;
|
||||
|
||||
return (
|
||||
<div className={`p-4 rounded-xl border ${bgClass}`}>
|
||||
<div className="text-xs font-semibold text-slate-300 mb-2">{title}</div>
|
||||
<div className="flex flex-col gap-1">
|
||||
<div className="flex items-end justify-between">
|
||||
<div className="text-2xl font-bold text-white">{formatter(experimentVal)}</div>
|
||||
<div className={`p-4 rounded-xl border ${bgClass} flex flex-col justify-between`}>
|
||||
<div className="text-xs font-semibold text-slate-300 mb-3">{title}</div>
|
||||
<div className="flex items-end justify-between mb-1">
|
||||
<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}`}>
|
||||
{delta > 0 ? '+' : ''}{delta.toFixed(1)}%
|
||||
</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 className="text-[11px] text-slate-400 font-medium mt-1">vs <span className="text-slate-300">{formatter(baseline)}</span> (Baseline)</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user