diff --git a/components/ExperimentsView.tsx b/components/ExperimentsView.tsx index 884d270..a9a623c 100644 --- a/components/ExperimentsView.tsx +++ b/components/ExperimentsView.tsx @@ -474,79 +474,99 @@ const ExperimentDetailView: React.FC<{ ); } - if (experiment.verdict || didResults) { - const prob = Math.round((experiment.verdict_probability || 0) * 100); - const radius = 60; - const circumference = 2 * Math.PI * radius; - const offset = circumference - (prob / 100) * circumference; + const prob = Math.round((experiment.verdict_probability || 0) * 100); + const radius = 60; + const circumference = 2 * Math.PI * radius; + const offset = circumference - (prob / 100) * circumference; - return ( -
- {/* Header */} -
-
-
- - EXPERIMENT RESULT - - ASIN: {experiment.asins?.[0] || 'Multiple'} -
-

- {experiment.name} -

-
-
- - Test: {new Date(experiment.start_date).toLocaleDateString('en-GB', { month: 'short', day: '2-digit', year: 'numeric' })} - {experiment.end_date ? new Date(experiment.end_date).toLocaleDateString('en-GB', { month: 'short', day: '2-digit', year: 'numeric' }) : 'Ongoing'} -
-
-
- - PRIMARY GOAL: {METRIC_LABELS[experiment.primary_metric] || experiment.primary_metric} -
-
+ return ( +
+ {/* Header */} +
+
+
+ + EXPERIMENT RESULT + + ASIN: {experiment.asins?.[0] || 'Multiple'} +
-
- - - +

+ {experiment.name} +

+
+
+ + Test: {new Date(experiment.start_date).toLocaleDateString('en-GB', { month: 'short', day: '2-digit', year: 'numeric' })} - {experiment.end_date ? new Date(experiment.end_date).toLocaleDateString('en-GB', { month: 'short', day: '2-digit', year: 'numeric' }) : 'Ongoing'} +
+
+
+ + PRIMARY GOAL: {METRIC_LABELS[experiment.primary_metric] || experiment.primary_metric} +
+
+ {experiment.status === 'planned' && ( + + )} + {experiment.status === 'active' && ( + + )} + + + + +
+
- {/* Main Grid */} -
- {/* Left Column (Metrics + Content) */} -
+ {/* Main Grid */} +
+ {/* Left Column (Metrics + Content) */} +
- {/* Key Performance Metrics */} -
-
- -

Key Performance Metrics

-
-
- - - - - - - - - - - {/* Render specific metrics: Units, Revenue, CVR, BSR/ACOS (if available in didResults) */} - {['units', 'revenue', 'cvr', 'acos', 'sessions', 'roas', 'ctr'].map(metric => { - const res = didResults?.metrics?.[metric]; - if (!res) return null; - const isPositiveGood = !['acos', 'bsr'].includes(metric); - const isGood = isPositiveGood ? res.lift_percent >= 0 : res.lift_percent <= 0; + {/* Key Performance Metrics */} +
+
+ +

Key Performance Metrics

+
+
+
KPI MetricControl (A)Treatment (B)Impact Delta
+ + + + + + + + + + {/* Render specific metrics: Units, Revenue, CVR, BSR/ACOS */} + {['units', 'revenue', 'cvr', 'acos', 'sessions', 'roas', 'ctr'].map(metric => { + const res = didResults?.metrics?.[metric]; + if (!res) { return ( - - - + + + ); - })} - -
KPI MetricControl (A)Treatment (B)Impact Delta
@@ -555,563 +575,187 @@ const ExperimentDetailView: React.FC<{ PRIMARY )} {formatMetricValue(res.treatment_before, metric)}{formatMetricValue(res.treatment_after, metric)} -
- - = 0 ? '' : 'rotate-180') : 'rotate-180'}`} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={3}> - {res.lift_percent >= 0 ? '+' : ''}{res.lift_percent.toFixed(1)}% - - - DiD: {res.did_estimate >= 0 ? '+' : ''}{formatMetricValue(res.did_estimate, metric)} - -
-
-
-
- - {/* Content Comparison */} -
-
- -

Content Comparison

-
-
-
-
Original
-

- {experiment.changes[0]?.before_value || 'Original content based on baseline metrics.'} -

-
-
-
Variant B (Winner)
-

- {experiment.changes[0]?.after_value || 'New optimized content tested in this experiment.'} -

-
-
-
- -
- - {/* Right Column (Widgets) */} -
- - {/* Probability of Success */} -
-
- -

Probability of Success

-
- -
- - - - -
- {prob}% -
-
- -
- - - Significant Confidence Reached - -
- - {/* Estimated Annual Impact */} -
-
- -

Estimated Annual Impact

-
- -
-
-
- Optimistic Forecast - $65,000 -
-
-
-
-
-
-
- Expected Gain - $42,000 -
-
-
-
-
-
-
- Conservative Baseline - $18,000 -
-
-
-
-
-
-
- -
-
-
- - {/* Bottom Info Grid */} -
-
-
- -
-
Test Timeline
-
12 Weeks
- -
-
Active Test Period
-
Jan 01 - Mar 25, 2026
-
Weeks 1-12 (2026)
-
- -
-
Comparison Baseline
-
Oct 01 - Dec 24, 2025
-
Weeks 40-51 (2025)
-
-
- -
-
- -
-
- - Statistical Insight -
-

- Results are normalized against the Pre-test Baseline to account for category seasonality. -

-
- -
-
- T -
-
Content Strategy
-

- Focus on high-velocity benefit descriptors in Treatment B. -

-
-
- -
- ); - } - - return ( - <> - {/* Back Button + Header */} - - - {/* Header */} -
-
-
-

{experiment.name}

- - -
- {experiment.description && ( -

{experiment.description}

- )} -
-
- {experiment.status === 'planned' && ( - - )} - {experiment.status === 'active' && ( - - )} - - - -
-
- - {/* Verdict Card (if results exist) */} - {experiment.verdict && primaryResult && ( -
-
-
-
Primary Metric Verdict
-
- {getVerdictLabel(experiment.verdict)} -
-
- {METRIC_LABELS[experiment.primary_metric] || experiment.primary_metric} -
-
-
-
-
-
Lift
-
= 0 ? 'text-emerald-400' : 'text-red-400'}`}> - {primaryResult.lift_percent >= 0 ? '+' : ''}{primaryResult.lift_percent}% -
-
-
-
Confidence
-
- {Math.round((experiment.verdict_probability || 0) * 100)}% -
-
-
-
DiD Effect
-
- {primaryResult.did_estimate >= 0 ? '+' : ''}{formatMetricValue(primaryResult.did_estimate, experiment.primary_metric)} -
-
-
-
- {/* Confidence bar */} -
-
-
-
- )} - - {/* Info Grid */} -
- {/* Hypothesis */} - -

- {experiment.hypothesis || 'No hypothesis defined'} -

-
- - {/* Timeline */} - -
- {experiment.baseline_start_date && experiment.baseline_end_date && ( -
- Baseline Period - - {new Date(experiment.baseline_start_date).toLocaleDateString('en-GB', { day: '2-digit', month: 'short', year: 'numeric' })} - {' — '} - {new Date(experiment.baseline_end_date).toLocaleDateString('en-GB', { day: '2-digit', month: 'short', year: 'numeric' })} - - Custom Seasonal Comparison -
- )} -
- Start - {new Date(experiment.start_date).toLocaleDateString('en-GB', { day: '2-digit', month: 'short', year: 'numeric' })} -
- {experiment.end_date && ( -
- End - {new Date(experiment.end_date).toLocaleDateString('en-GB', { day: '2-digit', month: 'short', year: 'numeric' })} -
- )} -
- Primary Metric - {METRIC_LABELS[experiment.primary_metric] || experiment.primary_metric} -
- {experiment.owner && ( -
- Owner - {experiment.owner} -
- )} -
- Marketplace - {experiment.marketplace || 'All'} -
-
-
-
- - {/* Change Annotations */} - {experiment.changes && experiment.changes.length > 0 && ( - -
- {experiment.changes.map((c, i) => ( -
- {c.field} - {c.before_value} - - - - {c.after_value} -
- ))} -
-
- )} - - {/* ASINs */} -
- -
- {(experiment.asins || []).map(a => ( - {a} - ))} - {(!experiment.asins || experiment.asins.length === 0) && ( - No ASINs defined - )} -
-
- -
- {(experiment.control_asins || []).map(a => ( - {a} - ))} - {(!experiment.control_asins || experiment.control_asins.length === 0) && ( - No control group — using simple before/after comparison - )} -
-
-
- - {/* Trend Chart */} - {trendData.length > 2 && experiment.did_results && ( - -
- - - - w.split('-')[1] || w} - stroke="#475569" - /> - - { - if (!active || !payload?.length) return null; - const actual = payload.find(p => p.dataKey === 'actual'); - const counter = payload.find(p => p.dataKey === 'counterfactual'); + } + const isPositiveGood = !['acos', 'bsr'].includes(metric); + const isGood = isPositiveGood ? res.lift_percent >= 0 : res.lift_percent <= 0; return ( -
-
{label}
-
-
- Actual: - {formatMetricValue(Number(actual?.value || 0), experiment.primary_metric)} -
-
-
- Counterfactual: - {formatMetricValue(Number(counter?.value || 0), experiment.primary_metric)} -
-
+ + + {METRIC_LABELS[metric] || metric} + {metric === experiment.primary_metric && ( + PRIMARY + )} + + {formatMetricValue(res.treatment_before, metric)} + {formatMetricValue(res.treatment_after, metric)} + +
+ + = 0 ? '' : 'rotate-180') : 'rotate-180'}`} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={3}> + {res.lift_percent >= 0 ? '+' : ''}{res.lift_percent.toFixed(1)}% + + + DiD: {res.did_estimate >= 0 ? '+' : ''}{formatMetricValue(res.did_estimate, metric)} + +
+ + ); - }} - /> - {experiment.start_date && ( - { - const sd = new Date(experiment.start_date); - const week = Math.ceil((sd.getTime() - new Date(sd.getFullYear(), 0, 1).getTime()) / (7 * 86400000)); - return `${sd.getFullYear()}-W${String(week).padStart(2, '0')}`; - })()} - stroke="#f59e0b" - strokeDasharray="4 4" - label={{ value: 'Start', position: 'top', fill: '#f59e0b', fontSize: 11 }} - /> - )} - - - - -
-
-
-
- Actual performance -
-
-
- Counterfactual (without change) + })} + +
- - )} - {/* DiD Metric Cards */} - {didResults && ( -
- {(Object.entries(didResults.metrics) as [string, DiDMetricResult][]).map(([metric, result]) => { - const isPrimary = metric === experiment.primary_metric; - return ( -
-
- - {METRIC_LABELS[metric] || metric} - - {isPrimary && ( - PRIMARY - )} + {/* Content Comparison */} +
+
+ +

Content Comparison

+
+
+
+
Original
+

+ {experiment.changes[0]?.before_value || 'Original content based on baseline metrics.'} +

+
+
+
Variant B (Winner)
+

+ {experiment.changes[0]?.after_value || 'New optimized content tested in this experiment.'} +

+
+
+
+ +
+ + {/* Right Column (Widgets) */} +
+ + {/* Probability of Success */} +
+
+ +

Probability of Success

+
+ +
+ + + + +
+ {prob}% +
+
+ +
+ + + Significant Confidence Reached + +
+ + {/* Estimated Annual Impact */} +
+
+ +

Estimated Annual Impact

+
+ +
+
+
+ Optimistic Forecast + $65,000 +
+
+
+
-
= 0 ? 'text-emerald-400' : 'text-red-400'}`}> - {result.lift_percent >= 0 ? '+' : ''}{result.lift_percent}% +
+
+ Expected Gain + $42,000 +
+
+
+
-
- DiD: {result.did_estimate >= 0 ? '+' : ''}{formatMetricValue(result.did_estimate, metric)} -
-
-
-
= 0.9 ? 'bg-emerald-400' : - result.posterior_prob_positive <= 0.1 ? 'bg-red-400' : 'bg-slate-400' - }`} - style={{ width: `${Math.round(result.posterior_prob_positive * 100)}%` }} - /> +
+
+ Conservative Baseline + $18,000 +
+
+
- {Math.round(result.posterior_prob_positive * 100)}%
- ); - })} -
- )} - - {/* Before / After Summary Table */} - {didResults && ( - -
- - - - - - - - - - - - - - {(Object.entries(didResults.metrics) as [string, DiDMetricResult][]).map(([metric, r]) => ( - - - - - - - - - - ))} - -
MetricTreatment BeforeTreatment AfterControl BeforeControl AfterDiD EffectLift
{METRIC_LABELS[metric] || metric}{formatMetricValue(r.treatment_before, metric)}{formatMetricValue(r.treatment_after, metric)}{typeof r.control_before === 'number' ? formatMetricValue(r.control_before, metric) : '—'}{typeof r.control_after === 'number' ? formatMetricValue(r.control_after, metric) : '—'}= 0 ? 'text-emerald-400' : 'text-red-400'}`}> - {r.did_estimate >= 0 ? '+' : ''}{formatMetricValue(r.did_estimate, metric)} - = 0 ? 'text-emerald-400' : 'text-red-400'}`}> - {r.lift_percent >= 0 ? '+' : ''}{r.lift_percent}% -
-
-
- )} - - {/* Learnings */} - - {editing ? ( -
-