mirror of
https://github.com/christianvidalwolf-prog/CrazeAnalytix.git
synced 2026-08-03 15:05:22 +02:00
New Features: - Experiments tab with full CRUD operations - Create experiments by ASIN or product line groups - Track pricing, advertising, content, and promotion experiments - Performance analytics with baseline vs experiment comparison - Visual experiment badges in Weekly Sales grid - Experiment detail view with metrics and learnings Technical Changes: - Add Supabase experiments table migration - New services/experiments.ts for CRUD + calculations - New components: ExperimentsView, ExperimentDetail, ExperimentForm, ExperimentBadge - Integrate experiment indicators in WeeklyGrid - Add navigation tab (desktop + mobile) - Type definitions in types.ts Usage: 1. Run SQL migration in Supabase 2. Navigate to Experiments tab 3. Create experiments with ASINs, dates, hypothesis 4. View performance lift after completion 5. See active experiments marked in Weekly Sales Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
354 lines
8.5 KiB
TypeScript
354 lines
8.5 KiB
TypeScript
|
|
export interface SalesRecord {
|
|
id: string;
|
|
customer: string;
|
|
year: number;
|
|
month: string; // "Apr-23"
|
|
week?: number;
|
|
asin: string;
|
|
sku: string;
|
|
title: string; // Added Product Title
|
|
articleName: string; // Kept for backward compatibility if mixed files used
|
|
units: number;
|
|
sellOut: number; // Parsed numeric value
|
|
line: string;
|
|
}
|
|
|
|
export interface FilterState {
|
|
customer: string[];
|
|
year: string[];
|
|
month: string[];
|
|
line: string[];
|
|
asin: string[];
|
|
sku: string[];
|
|
title: string[]; // Added Title filter
|
|
week: string[]; // Added Week filter
|
|
stock: string[]; // Added Stock filter
|
|
vendorStock: string[]; // Added Vendor Stock filter
|
|
woc: string[]; // Added Weeks of Coverage filter
|
|
bulkSearch: string; // Added Bulk Search support
|
|
columnFilters: Record<string, ColumnFilterCondition>; // Added Excel-style column filters
|
|
}
|
|
|
|
export interface ColumnFilterCondition {
|
|
textFilter?: {
|
|
operator: 'equals' | 'notEquals' | 'contains' | 'notContains' | 'startsWith' | 'notStartsWith' | 'endsWith' | 'notEndsWith' | 'gt' | 'lt' | 'gte' | 'lte';
|
|
value: string;
|
|
};
|
|
selectedValues?: string[];
|
|
sort?: 'asc' | 'desc';
|
|
}
|
|
|
|
export interface GrowthMetric {
|
|
line: string;
|
|
currentYearSellOut: number;
|
|
previousYearSellOut: number;
|
|
sellOutGrowthValue: number;
|
|
sellOutGrowthPercentage: number;
|
|
|
|
currentYearUnits: number;
|
|
previousYearUnits: number;
|
|
unitsGrowthValue: number;
|
|
unitsGrowthPercentage: number;
|
|
}
|
|
|
|
export type LineGrowthMetric = GrowthMetric;
|
|
|
|
export interface ItemGrowthMetric {
|
|
sku: string;
|
|
asin: string;
|
|
title: string;
|
|
line: string;
|
|
currentYearSellOut: number;
|
|
previousYearSellOut: number;
|
|
sellOutGrowthValue: number;
|
|
sellOutGrowthPercentage: number;
|
|
currentYearUnits: number;
|
|
previousYearUnits: number;
|
|
unitsGrowthValue: number;
|
|
unitsGrowthPercentage: number;
|
|
}
|
|
|
|
export interface SeasonalityPoint {
|
|
name: string; // "Jan", "Feb", etc.
|
|
[year: string]: number | string; // Dynamic keys for years: "2023": 500, "2024": 600
|
|
}
|
|
|
|
export interface YearlySplitData {
|
|
name: string;
|
|
// Dynamic keys like "2023", "2024" or "2023_value", "2023_units"
|
|
[key: string]: number | string;
|
|
}
|
|
|
|
export interface AggregatedData {
|
|
totalSellOut: number;
|
|
totalUnits: number;
|
|
totalsByYear: Record<string, { sellOut: number; units: number }>;
|
|
byLine: { name: string; value: number; units: number }[];
|
|
byCustomer: { name: string; value: number }[];
|
|
seasonality: SeasonalityPoint[];
|
|
seasonalityUnits: SeasonalityPoint[];
|
|
availableYears: string[];
|
|
topMovers: GrowthMetric[];
|
|
bottomMovers: GrowthMetric[];
|
|
comparisonPeriods: { current: string; previous: string };
|
|
topLinesSplit: YearlySplitData[];
|
|
byCustomerSplit: YearlySplitData[];
|
|
byLineOverviewSplit: YearlySplitData[];
|
|
}
|
|
|
|
export interface ChatMessage {
|
|
role: 'user' | 'model';
|
|
text: string;
|
|
timestamp: Date;
|
|
}
|
|
|
|
// New Interfaces for Dynamic Pivot Grid
|
|
export interface YearlyData {
|
|
sellOut: number;
|
|
units: number;
|
|
}
|
|
|
|
export interface MonthlyPivot {
|
|
monthIndex: number;
|
|
byYear: Record<string, YearlyData>;
|
|
}
|
|
|
|
export interface PivotRow {
|
|
id: string;
|
|
customer: string;
|
|
line: string;
|
|
title: string; // Added Title
|
|
articleName: string;
|
|
sku: string;
|
|
asin: string;
|
|
|
|
// Dynamic buckets
|
|
totalsByYear: Record<string, YearlyData>;
|
|
months: MonthlyPivot[]; // Always 12 elements
|
|
|
|
// Ads data (optional, aggregated by ASIN)
|
|
adsByYear?: Record<string, {
|
|
adSpend: number;
|
|
attributedSales: number;
|
|
acos: number;
|
|
tacos: number;
|
|
}>;
|
|
}
|
|
|
|
export interface TimeSeriesData {
|
|
name: string; // e.g., "Apr '23" or "W21 '23"
|
|
sellOut: number;
|
|
units: number;
|
|
}
|
|
|
|
export interface ComparisonTimeSeriesPoint {
|
|
week: number;
|
|
name: string; // "W1", "W2", etc.
|
|
[key: string]: number | string; // Dynamic keys like "2023_sellOut", "2024_units"
|
|
}
|
|
|
|
export interface AdsRecord {
|
|
country: string; // Marketplace (Amazon DE, IT, ES, FR, UK)
|
|
year: number; // Year extracted from sheet name
|
|
week: number; // Week number (1-52)
|
|
asin: string;
|
|
cost: number; // Ad spend €
|
|
clicks: number;
|
|
impressions: number;
|
|
cpc: number; // Cost per click €
|
|
ctr: number; // Click-through rate %
|
|
acos: number; // Advertising cost of sale %
|
|
conversions: number; // Attributed conversions (30d)
|
|
attributedUnits30d: number;
|
|
attributedSales30d: number;
|
|
}
|
|
|
|
export interface TrafficRecord {
|
|
country: string; // Marketplace (DE, UK, etc.)
|
|
year: number;
|
|
week: number;
|
|
asin: string;
|
|
glanceViews: number; // Page views
|
|
}
|
|
|
|
export interface CombinedKPIs {
|
|
id: string;
|
|
marketplace: string;
|
|
customer: string; // Added for consistency with SalesRecord
|
|
month: string;
|
|
week: number; // Added for weekly analysis
|
|
year: number;
|
|
asin: string;
|
|
title: string;
|
|
line: string;
|
|
sku: string;
|
|
|
|
salesTotal: number;
|
|
unitsTotal: number;
|
|
|
|
salesAds: number;
|
|
unitsAds: number;
|
|
cost: number;
|
|
clicks: number;
|
|
impressions: number;
|
|
conversions: number;
|
|
|
|
salesOrganic: number;
|
|
unitsOrganic: number;
|
|
|
|
paidSalesShare: number;
|
|
organicSalesShare: number;
|
|
|
|
acos: number;
|
|
tacos: number;
|
|
roas: number;
|
|
ctr: number;
|
|
cpc: number;
|
|
cvrUnits: number;
|
|
glanceViews: number; // Traffic / page views
|
|
avgWeeklySales?: number; // Added for Weeks of Coverage
|
|
}
|
|
|
|
export interface ForecastRecord {
|
|
asin: string;
|
|
annualForecast: number;
|
|
sku?: string;
|
|
title?: string;
|
|
line?: string;
|
|
}
|
|
|
|
export interface MonthlyForecastPoint {
|
|
month: string;
|
|
forecastUnits: number;
|
|
actualUnits: number;
|
|
}
|
|
|
|
export interface ProductForecastData {
|
|
asin: string;
|
|
sku: string;
|
|
title: string;
|
|
line: string;
|
|
annualForecast: number;
|
|
actualUnits: number;
|
|
forecastUnits: number;
|
|
accuracy: number;
|
|
avgWeeklySales: number; // Added for Weeks of Coverage
|
|
monthlyData: Record<string, MonthlyForecastPoint>;
|
|
}
|
|
|
|
export interface BuyBoxLostData {
|
|
asin: string;
|
|
countries: string[]; // ['DE', 'FR', 'IT', 'UK', 'ES']
|
|
reasons: Record<string, string>; // { DE: 'Amazon', FR: 'Unknown' }
|
|
}
|
|
|
|
export interface VendorDailyRow {
|
|
id?: number;
|
|
date: string;
|
|
market: string;
|
|
asin: string;
|
|
product_title: string | null;
|
|
tags: string | null;
|
|
bsr_top_rank: number | null;
|
|
bsr_top_category: string | null;
|
|
bsr_detail_rank: number | null;
|
|
bsr_detail_category: string | null;
|
|
avg_rating: number | null;
|
|
num_reviews: number | null;
|
|
buybox_owner: string | null;
|
|
buybox_price: number | null;
|
|
amazon_has_buybox: boolean | null;
|
|
glance_views: number | null;
|
|
}
|
|
|
|
export interface VendorCSVRow {
|
|
Date: string;
|
|
Market: string;
|
|
ASIN: string;
|
|
'Product Title': string;
|
|
Tags: string;
|
|
'Top Level Category (Rank)': string;
|
|
'Top Level Category (Name)': string;
|
|
'Detail Level Category (Rank)': string;
|
|
'Detail Level Category (Name)': string;
|
|
'Average Rating': string;
|
|
'Number of Reviews': string;
|
|
'Buybox Seller Name': string;
|
|
'Buybox Price': string;
|
|
'Amazon Has Buybox': string;
|
|
'Glance Views': string;
|
|
}
|
|
|
|
// Experiment Tracking Types
|
|
export type ExperimentType = 'pricing' | 'advertising' | 'content' | 'promotion';
|
|
export type ExperimentStatus = 'planned' | 'active' | 'completed' | 'paused';
|
|
export type ExperimentMetric = 'units' | 'revenue' | 'acos' | 'ctr' | 'cvr' | 'bsr';
|
|
|
|
export interface Experiment {
|
|
id: string;
|
|
name: string;
|
|
description?: string;
|
|
type: ExperimentType;
|
|
status: ExperimentStatus;
|
|
asins: string[];
|
|
marketplace: string;
|
|
start_date: string;
|
|
end_date?: string;
|
|
created_at: string;
|
|
updated_at: string;
|
|
hypothesis?: string;
|
|
primary_metric: ExperimentMetric;
|
|
target_lift_percent?: number;
|
|
|
|
// Results
|
|
baseline_units?: number;
|
|
baseline_revenue?: number;
|
|
experiment_units?: number;
|
|
experiment_revenue?: number;
|
|
actual_lift_percent?: number;
|
|
statistical_significance?: number;
|
|
|
|
learnings?: string;
|
|
owner?: string;
|
|
}
|
|
|
|
export interface ExperimentCreateInput {
|
|
name: string;
|
|
description?: string;
|
|
type: ExperimentType;
|
|
asins: string[];
|
|
marketplace: string;
|
|
start_date: string;
|
|
end_date?: string;
|
|
hypothesis?: string;
|
|
primary_metric: ExperimentMetric;
|
|
target_lift_percent?: number;
|
|
owner?: string;
|
|
}
|
|
|
|
export interface ExperimentListItem {
|
|
id: string;
|
|
name: string;
|
|
type: ExperimentType;
|
|
status: ExperimentStatus;
|
|
asin_count: number;
|
|
marketplace: string;
|
|
start_date: string;
|
|
end_date?: string;
|
|
progress_percent: number;
|
|
actual_lift_percent?: number;
|
|
owner?: string;
|
|
}
|
|
|
|
export interface ActiveExperiment {
|
|
asin: string;
|
|
experiment_id: string;
|
|
experiment_name: string;
|
|
type: ExperimentType;
|
|
status: ExperimentStatus;
|
|
start_date: string;
|
|
end_date?: string;
|
|
days_remaining?: number;
|
|
}
|