mirror of
https://github.com/christianvidalwolf-prog/CrazeAnalytix.git
synced 2026-08-03 13:45:23 +02:00
feat: add marketplace differentiation to Experiment Tracker
Each experiment now tracks which marketplace (Amazon DE, UK, etc.) it applies to. The form defaults to the currently active marketplace filter, and the delta % calculation filters by both product line and marketplace. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
9a115ff5be
commit
812425a1ef
@@ -11,6 +11,7 @@ export interface Experiment {
|
||||
id: string;
|
||||
week: string; // "2026-08" matching YYYY-WW format used in pivot data
|
||||
product_line: string;
|
||||
marketplace: string; // e.g. "Amazon DE", "Amazon UK"
|
||||
action_type: 'SEO' | 'Price' | 'Advertising' | 'Images' | 'A+ Content' | 'Variants' | 'Stock' | 'Other';
|
||||
description: string;
|
||||
expected_impact: 'More Sales' | 'Better Visibility' | 'Lower Costs';
|
||||
@@ -93,6 +94,7 @@ interface ExperimentTrackerProps {
|
||||
rows: WeeklyPivotRow[];
|
||||
weeks: string[]; // sorted descending (most recent first)
|
||||
primaryMetric: 'units' | 'revenue';
|
||||
customerFilters: string[]; // active marketplace filters from parent
|
||||
}
|
||||
|
||||
// --- Custom dot for experiment markers ---
|
||||
@@ -150,6 +152,7 @@ const ChartTooltipContent: React.FC<any> = ({ active, payload, label, experiment
|
||||
{exp.action_type}
|
||||
</span>
|
||||
<span className="text-[10px] text-white/70 ml-1.5">{exp.product_line}</span>
|
||||
{exp.marketplace && <span className="text-[9px] text-slate-500 ml-1">({exp.marketplace})</span>}
|
||||
<p className="text-[10px] text-slate-400 mt-0.5 leading-tight">{exp.description}</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -162,7 +165,7 @@ const ChartTooltipContent: React.FC<any> = ({ active, payload, label, experiment
|
||||
|
||||
// --- Main Component ---
|
||||
|
||||
const ExperimentTracker: React.FC<ExperimentTrackerProps> = ({ rows, weeks, primaryMetric }) => {
|
||||
const ExperimentTracker: React.FC<ExperimentTrackerProps> = ({ rows, weeks, primaryMetric, customerFilters }) => {
|
||||
const [experiments, setExperiments] = useState<Experiment[]>(loadExperiments);
|
||||
const [showForm, setShowForm] = useState(false);
|
||||
const [showPanel, setShowPanel] = useState(true);
|
||||
@@ -171,10 +174,24 @@ const ExperimentTracker: React.FC<ExperimentTrackerProps> = ({ rows, weeks, prim
|
||||
// Form state
|
||||
const [formWeek, setFormWeek] = useState(getCurrentWeek);
|
||||
const [formLine, setFormLine] = useState('');
|
||||
const [formMarketplace, setFormMarketplace] = useState('');
|
||||
const [formAction, setFormAction] = useState<ActionType>('SEO');
|
||||
const [formDesc, setFormDesc] = useState('');
|
||||
const [formImpact, setFormImpact] = useState<ExpectedImpact>('More Sales');
|
||||
|
||||
// Available marketplaces from data
|
||||
const marketplaces = useMemo(() => {
|
||||
const mks = new Set(rows.map(r => r.customer).filter(Boolean));
|
||||
return Array.from(mks).sort();
|
||||
}, [rows]);
|
||||
|
||||
// Default marketplace to active filter when opening form
|
||||
const defaultMarketplace = useMemo(() => {
|
||||
if (customerFilters.length === 1) return customerFilters[0];
|
||||
if (customerFilters.length > 0) return customerFilters[0];
|
||||
return marketplaces[0] || '';
|
||||
}, [customerFilters, marketplaces]);
|
||||
|
||||
// Available product lines from data
|
||||
const productLines = useMemo(() => {
|
||||
const lines = new Set(rows.map(r => r.line).filter(Boolean));
|
||||
@@ -218,6 +235,7 @@ const ExperimentTracker: React.FC<ExperimentTrackerProps> = ({ rows, weeks, prim
|
||||
let total = 0;
|
||||
for (const row of rows) {
|
||||
if (row.line !== exp.product_line) continue;
|
||||
if (exp.marketplace && row.customer !== exp.marketplace) continue;
|
||||
total += primaryMetric === 'units'
|
||||
? (row.unitsByWeek[w] || 0)
|
||||
: (row.revenueByWeek[w] || 0);
|
||||
@@ -252,12 +270,13 @@ const ExperimentTracker: React.FC<ExperimentTrackerProps> = ({ rows, weeks, prim
|
||||
}, []);
|
||||
|
||||
const handleSubmit = useCallback(() => {
|
||||
if (!formLine || !formDesc.trim()) return;
|
||||
if (!formLine || !formMarketplace || !formDesc.trim()) return;
|
||||
|
||||
const newExp: Experiment = {
|
||||
id: `exp_${Date.now()}`,
|
||||
week: formWeek,
|
||||
product_line: formLine,
|
||||
marketplace: formMarketplace,
|
||||
action_type: formAction,
|
||||
description: formDesc.trim(),
|
||||
expected_impact: formImpact,
|
||||
@@ -269,7 +288,7 @@ const ExperimentTracker: React.FC<ExperimentTrackerProps> = ({ rows, weeks, prim
|
||||
setShowForm(false);
|
||||
setFormDesc('');
|
||||
setFormWeek(getCurrentWeek());
|
||||
}, [formWeek, formLine, formAction, formDesc, formImpact, experiments, updateExperiments]);
|
||||
}, [formWeek, formLine, formMarketplace, formAction, formDesc, formImpact, experiments, updateExperiments]);
|
||||
|
||||
const handleStatusChange = useCallback((id: string, newStatus: ExperimentStatus) => {
|
||||
updateExperiments(experiments.map(e => e.id === id ? { ...e, status: newStatus } : e));
|
||||
@@ -299,7 +318,7 @@ const ExperimentTracker: React.FC<ExperimentTrackerProps> = ({ rows, weeks, prim
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => setShowForm(true)}
|
||||
onClick={() => { setFormMarketplace(defaultMarketplace); setShowForm(true); }}
|
||||
className="flex items-center gap-2 bg-indigo-600 hover:bg-indigo-500 text-white px-4 py-2 rounded-lg text-xs font-bold transition-colors border border-indigo-500/50 shadow-lg shadow-indigo-500/20"
|
||||
>
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
@@ -417,6 +436,21 @@ const ExperimentTracker: React.FC<ExperimentTrackerProps> = ({ rows, weeks, prim
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{/* Marketplace */}
|
||||
<div>
|
||||
<label className="text-[10px] font-black text-slate-400 uppercase tracking-widest block mb-1.5">Marketplace</label>
|
||||
<select
|
||||
value={formMarketplace}
|
||||
onChange={e => setFormMarketplace(e.target.value)}
|
||||
className="w-full bg-slate-950 border border-white/10 rounded-lg px-3 py-2 text-sm text-white focus:outline-none focus:ring-2 focus:ring-indigo-500"
|
||||
>
|
||||
<option value="">Select marketplace...</option>
|
||||
{marketplaces.map(mk => (
|
||||
<option key={mk} value={mk}>{mk}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{/* Action Type */}
|
||||
<div>
|
||||
<label className="text-[10px] font-black text-slate-400 uppercase tracking-widest block mb-1.5">Action Type</label>
|
||||
@@ -478,7 +512,7 @@ const ExperimentTracker: React.FC<ExperimentTrackerProps> = ({ rows, weeks, prim
|
||||
</button>
|
||||
<button
|
||||
onClick={handleSubmit}
|
||||
disabled={!formLine || !formDesc.trim()}
|
||||
disabled={!formLine || !formMarketplace || !formDesc.trim()}
|
||||
className="px-5 py-2 rounded-lg text-xs font-bold text-white bg-indigo-600 hover:bg-indigo-500 border border-indigo-500/50 shadow-lg shadow-indigo-500/20 transition-colors disabled:opacity-40 disabled:cursor-not-allowed"
|
||||
>
|
||||
Save Experiment
|
||||
@@ -507,10 +541,11 @@ const ExperimentTracker: React.FC<ExperimentTrackerProps> = ({ rows, weeks, prim
|
||||
|
||||
{showPanel && (
|
||||
<div className="overflow-x-auto border-t border-white/5">
|
||||
<table className="w-full text-left border-collapse min-w-[800px]">
|
||||
<table className="w-full text-left border-collapse min-w-[900px]">
|
||||
<thead>
|
||||
<tr className="border-b border-white/10">
|
||||
<th className="p-2.5 px-4 text-[10px] font-black text-slate-500 uppercase tracking-widest">Week</th>
|
||||
<th className="p-2.5 px-4 text-[10px] font-black text-slate-500 uppercase tracking-widest">Marketplace</th>
|
||||
<th className="p-2.5 px-4 text-[10px] font-black text-slate-500 uppercase tracking-widest">Product Line</th>
|
||||
<th className="p-2.5 px-4 text-[10px] font-black text-slate-500 uppercase tracking-widest">Action</th>
|
||||
<th className="p-2.5 px-4 text-[10px] font-black text-slate-500 uppercase tracking-widest">Description</th>
|
||||
@@ -528,6 +563,9 @@ const ExperimentTracker: React.FC<ExperimentTrackerProps> = ({ rows, weeks, prim
|
||||
<td className="p-2.5 px-4 text-xs font-bold text-white whitespace-nowrap">
|
||||
W{exp.week.split('-')[1]}/{exp.week.split('-')[0].slice(-2)}
|
||||
</td>
|
||||
<td className="p-2.5 px-4">
|
||||
<span className="text-[10px] text-sky-400 font-bold">{exp.marketplace || '—'}</span>
|
||||
</td>
|
||||
<td className="p-2.5 px-4">
|
||||
<span className="text-[10px] text-fuchsia-400 font-bold uppercase tracking-widest">{exp.product_line}</span>
|
||||
</td>
|
||||
|
||||
Reference in New Issue
Block a user