mirror of
https://github.com/christianvidalwolf-prog/CrazeAnalytix.git
synced 2026-08-03 17:55:24 +02:00
- Add date? field to BSRRecord (ISO string YYYY-MM-DD) - processBSRExcel now extracts and stores the Date column as an ISO date (handles both Excel serial numbers and string dates) - VendorDataView groups chart data by date (daily) when dates are available, falling back to weekly buckets — eliminates straight-line charts caused by only 2 weekly data points Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
412 lines
10 KiB
TypeScript
412 lines
10 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;
|
|
}
|
|
|
|
export interface BSRRecord {
|
|
week: number;
|
|
date?: string; // ISO date string (YYYY-MM-DD) when available — enables daily resolution charts
|
|
market: string;
|
|
asin: string;
|
|
topLevelBSR: number | null;
|
|
topLevelName: string | null;
|
|
detailLevelBSR: number | null;
|
|
detailLevelName: string | null;
|
|
avgRating: number | null;
|
|
}
|
|
|
|
// Experiment Tracking Types
|
|
export type ExperimentType = 'pricing' | 'advertising' | 'content' | 'promotion' | 'seo';
|
|
export type ExperimentStatus = 'planned' | 'active' | 'completed' | 'paused';
|
|
export type ExperimentMetric = 'units' | 'sessions' | 'cvr' | 'ctr' | 'roas' | 'revenue' | 'acos';
|
|
export type ExperimentVerdict = 'winner' | 'loser' | 'inconclusive';
|
|
|
|
export interface ExperimentChangeAnnotation {
|
|
field: string;
|
|
before_value: string;
|
|
after_value: string;
|
|
}
|
|
|
|
export interface DiDMetricResult {
|
|
treatment_before: number;
|
|
treatment_after: number;
|
|
control_before: number;
|
|
control_after: number;
|
|
did_estimate: number;
|
|
lift_percent: number;
|
|
posterior_prob_positive: number;
|
|
}
|
|
|
|
export interface DifferenceInDifferencesResult {
|
|
metrics: Record<string, DiDMetricResult>;
|
|
computed_at: string;
|
|
}
|
|
|
|
export interface Experiment {
|
|
id: string;
|
|
name: string;
|
|
description?: string;
|
|
type: ExperimentType;
|
|
status: ExperimentStatus;
|
|
asins: string[];
|
|
control_asins: string[];
|
|
marketplace: string;
|
|
start_date: string;
|
|
end_date?: string;
|
|
baseline_start_date?: string;
|
|
baseline_end_date?: string;
|
|
created_at: string;
|
|
updated_at: string;
|
|
hypothesis?: string;
|
|
primary_metric: ExperimentMetric;
|
|
target_lift_percent?: number;
|
|
changes: ExperimentChangeAnnotation[];
|
|
|
|
// Legacy results (kept for backward compat)
|
|
baseline_units?: number;
|
|
baseline_revenue?: number;
|
|
baseline_gv?: number;
|
|
baseline_cvr?: number;
|
|
baseline_acos?: number;
|
|
experiment_units?: number;
|
|
experiment_revenue?: number;
|
|
experiment_gv?: number;
|
|
experiment_cvr?: number;
|
|
experiment_acos?: number;
|
|
actual_lift_percent?: number;
|
|
statistical_significance?: number;
|
|
|
|
// DiD results
|
|
did_results?: DifferenceInDifferencesResult;
|
|
verdict?: ExperimentVerdict;
|
|
verdict_probability?: number;
|
|
|
|
learnings?: string;
|
|
owner?: string;
|
|
}
|
|
|
|
export interface ExperimentCreateInput {
|
|
name: string;
|
|
description?: string;
|
|
type: ExperimentType;
|
|
asins: string[];
|
|
control_asins: string[];
|
|
marketplace: string;
|
|
start_date: string;
|
|
end_date?: string;
|
|
baseline_start_date?: string;
|
|
baseline_end_date?: string;
|
|
hypothesis?: string;
|
|
primary_metric: ExperimentMetric;
|
|
target_lift_percent?: number;
|
|
changes: ExperimentChangeAnnotation[];
|
|
owner?: string;
|
|
}
|
|
|
|
export interface ExperimentListItem {
|
|
id: string;
|
|
name: string;
|
|
type: ExperimentType;
|
|
status: ExperimentStatus;
|
|
asin_count: number;
|
|
asins: string[];
|
|
control_asin_count: number;
|
|
marketplace: string;
|
|
start_date: string;
|
|
end_date?: string;
|
|
progress_percent: number;
|
|
primary_metric: ExperimentMetric;
|
|
verdict?: ExperimentVerdict;
|
|
verdict_probability?: 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;
|
|
}
|