fix(dataProcessor): parse DD/M/YY dates from column C in sell-out CSV

Add support for European day-first date format (e.g. "23/2/26" = 23 Feb 2026)
in the sell-out CSV pipeline so months and years are correctly extracted from
column C of Amazon Sell Out 2023-2025.csv.

- normalizeMonth: detect DD/MM/YY when first part > 12, return "Mon-YY"
- mapRowToRecord: add 'C', 'Date', 'Fecha', 'DATA' to month column aliases
- validateSellOutHeaders: accept date columns as substitute for YEAR + MONTH

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Christian Vidal Wolf
2026-03-03 13:07:59 +01:00
co-authored by Claude Sonnet 4.6
parent ad1695d360
commit 2eed94b94b
10 changed files with 112 additions and 747 deletions
+54 -76
View File
@@ -12,64 +12,24 @@ npm run preview # Preview production build
No test or lint scripts are configured. ESLint config exists (`eslint.config.js`) but has no npm script.
## Environment
## Deployment
Requires Node.js >= 18. Set `GEMINI_API_KEY` in `.env.local` for the AI chat feature (Google Gemini API).
## Project Structure
Project: `craze-analytix2` under Vercel team `christians-projects-dd62b5fc`.
```bash
vercel --prod --scope christians-projects-dd62b5fc
```
├── App.tsx # Main app component, global state orchestrator
├── index.tsx # React entry point
├── index.html # HTML template (Tailwind CDN loaded here)
├── types.ts # All TypeScript interfaces
├── vite.config.ts # Vite config (port 3000, proxy routes, path alias)
├── vercel.json # Vercel deployment config (API rewrites, SPA routing)
├── firebase.json # Firebase hosting config (alternative deployment)
├── api/ # Vercel serverless functions
│ ├── ask-gemini.ts # Gemini AI chat proxy
│ ├── fetch-data.ts # Sales CSV from Dropbox
│ ├── fetch-ads.ts # Ads Excel from Dropbox
│ ├── fetch-traffic.ts # Traffic data
│ ├── fetch-stock.ts # Item availability
│ ├── fetch-paneu-stock.ts # PAN-EU vendor stock
│ ├── fetch-uk-inventory.ts# UK inventory
│ ├── fetch-buybox.ts # Buy Box tracker
│ ├── fetch-forecast.ts # Forecast data
│ └── fetch-vendor-stock.ts
├── components/ # React components
│ ├── Dashboard.tsx # KPI cards, charts, YoY comparisons
│ ├── DataGrid.tsx # Pivot table with Excel-style filters (~96KB)
│ ├── WeeklyGrid.tsx # Weekly time-series view
│ ├── TopMovers.tsx # Growth/decline analysis
│ ├── AdsPerformance.tsx # Ad spend, ROAS, ACOS metrics
│ ├── ForecastView.tsx # Product forecasts with seasonality
│ ├── FilterBar.tsx # Sticky filter controls
│ ├── AIChat.tsx # AI assistant sidebar
│ ├── FileUpload.tsx # Manual data upload modal
│ ├── ExcelFilter.tsx # Advanced column filtering UI
│ ├── MultiSelectDropdown.tsx # Reusable filter dropdown
│ ├── StockBadge.tsx # Stock status indicator
│ ├── VendorStockBadge.tsx # Vendor stock indicator
│ ├── BuyBoxWarningBadge.tsx # Buy Box loss warnings
│ ├── Top50Badge.tsx # Top 50 product badge
│ ├── InColumnStockFilter.tsx # In-column stock filter
│ ├── NumericColumnFilter.tsx # Numeric column filter
│ ├── CrazeLogo.tsx # Header branding
│ ├── ErrorBoundary.tsx # Error handling wrapper
│ └── Icons.tsx # SVG icon library
├── services/ # Business logic and data layer
│ ├── dataProcessor.ts # Core data engine (~2400 lines)
│ ├── storage.ts # IndexedDB + localStorage caching
│ ├── geminiService.ts # AI context builder + API calls
│ └── filterHelper.ts # Filter utilities
└── public/ # Static data files
├── fc 26.xlsx # EU forecast
├── fc UK 26.xlsx # UK forecast
├── Item Availability.xlsx
├── Buy_Box_tracker.xlsx
└── Vendor Stock.xlsx
```
The `.vercel/project.json` must point to `projectId: prj_TYI5xVvuZ0kPI5Ap8zmX7cqfjsCi` and `orgId: team_5AhzZHthpZINNw9vOrKxOINr`.
## Environment Variables
| Variable | Used in | Purpose |
|---|---|---|
| `GEMINI_API_KEY` | `.env.local` | AI chat (Google Gemini) |
| `SUPABASE_URL` | Vercel env / serverless | Supabase project URL |
| `SUPABASE_SERVICE_KEY` | `api/experiments.ts`, `api/upload-vendor-data.ts` | Server-side Supabase access |
| `SUPABASE_ANON_KEY` | `services/supabase.ts` | Client-side Supabase access |
## Architecture
@@ -87,41 +47,59 @@ Dropbox (CSV/Excel files)
Data is cached in IndexedDB via `services/storage.ts`. On load, cached data displays immediately while fresh data fetches in the background.
All data sources are fetched automatically from Dropbox — there is no manual file upload.
### Key Files
- **App.tsx** — Main orchestrator. Holds all global state (rawData, adsData, trafficData, filters, etc.) and passes data/handlers as props to views.
- **services/dataProcessor.ts** (~2400 lines) — Core data engine. Handles CSV/Excel parsing, currency normalization (EU `1.234,56` and US `1,234.56` formats), Spanish/English month mapping, filtering (`filterData`, `filterAdsData`), aggregation (`aggregateData`), and pivot table generation (`pivotSalesData`).
- **types.ts** — All TypeScript interfaces: `SalesRecord`, `AdsRecord`, `TrafficRecord`, `ForecastRecord`, `FilterState`, `AggregatedData`, `PivotRow`, etc.
- **services/storage.ts** — IndexedDB + localStorage caching with schema versioning.
- **services/geminiService.ts** — Builds structured context from aggregated data and sends to Gemini API.
- **App.tsx** — Main orchestrator. Holds all global state and passes data/handlers as props to views. All data fetching (`handleDataFetch`, `handleAdsFetch`, `handleBSRFetch`, etc.) is initiated here.
- **services/dataProcessor.ts** (~2400 lines) — Core data engine. Handles CSV/Excel parsing, currency normalization (EU `1.234,56` and US `1,234.56` formats), Spanish/English month mapping, filtering (`filterData`, `filterAdsData`, `filterBsrData`), aggregation, and pivot table generation.
- **types.ts** — All TypeScript interfaces: `SalesRecord`, `AdsRecord`, `TrafficRecord`, `ForecastRecord`, `BSRRecord`, `FilterState`, `AggregatedData`, `PivotRow`, `Experiment`, etc.
- **services/storage.ts** — IndexedDB + localStorage caching with schema versioning. Increment `SCHEMA_VERSION` when changing cached data shapes.
- **services/experiments.ts** — CRUD operations for experiments via `/api/experiments` (Supabase-backed). Includes ASIN resolution logic for line-level experiments.
- **services/experimentAnalysis.ts** — Difference-in-Differences (DiD) statistical analysis engine. Uses ISO week boundaries (Monday start).
### Views (rendered conditionally by `view` state in App.tsx)
### Views
| View | Component | Purpose |
|------|-----------|---------|
| dashboard | Dashboard.tsx | KPI cards, charts, YoY comparisons |
| table | DataGrid.tsx | Pivot table with Excel-style column filters |
| weekly | WeeklyGrid.tsx | Weekly time-series breakdown |
| movers | TopMovers.tsx | Top growth/decline products |
| ads | AdsPerformance.tsx | Ad spend, ROAS, ACOS metrics |
| forecast | ForecastView.tsx | Product forecasts with velocity mapping |
| View key | Component | Purpose |
|----------|-----------|---------|
| `dashboard` | Dashboard.tsx | KPI cards, charts, YoY comparisons |
| `table` | DataGrid.tsx | Pivot table with Excel-style column filters |
| `weekly` | WeeklyGrid.tsx | Weekly time-series breakdown |
| `movers` | TopMovers.tsx | Top growth/decline products |
| `ads` | AdsPerformance.tsx | Ad spend, ROAS, ACOS metrics |
| `forecast` | ForecastView.tsx | Product forecasts with velocity mapping |
| `vendor` | VendorDataView.tsx | BSR trends, ratings per market. Shows product card (SKU/ASIN/title/BuyBox) when a single ASIN is filtered |
| `experiments` | ExperimentsView.tsx | A/B experiment tracking with DiD analysis and Bayesian verdicts |
### API Routes (`/api/`)
All serverless functions fetch data from Dropbox (direct download URLs with `dl=1`). Each returns the raw file content for client-side processing:
- `fetch-data.ts` (sales CSV), `fetch-ads.ts` (ads Excel), `fetch-traffic.ts`, `fetch-stock.ts`, `fetch-paneu-stock.ts`, `fetch-uk-inventory.ts`, `fetch-buybox.ts`, `fetch-forecast.ts`
- `ask-gemini.ts` — Proxies chat requests to Google Gemini API
All fetch routes proxy Dropbox direct-download URLs (`dl=1`) and return raw file content for client-side parsing:
- `fetch-data.ts` — Sales CSV
- `fetch-ads.ts` — Ads Excel (weekly)
- `fetch-traffic.ts` — Traffic/Glance Views Excel
- `fetch-stock.ts` — Item availability
- `fetch-paneu-stock.ts` — PAN-EU vendor stock
- `fetch-uk-inventory.ts` — UK inventory
- `fetch-buybox.ts` — Buy Box tracker Excel
- `fetch-forecast.ts` — Forecast Excel
- `fetch-bsr.ts` — BSR/ratings Excel (feeds Vendor tab)
- `ask-gemini.ts` — Proxies AI chat to Gemini API
- `experiments.ts` — CRUD for experiments stored in Supabase
- `upload-vendor-data.ts` — Batch upsert of vendor rows to Supabase
### Important Constants (in dataProcessor.ts)
- `PAN_EU_COUNTRIES = ['Amazon DE', 'Amazon IT', 'Amazon FR', 'Amazon ES']` — Default country filter when no customer is selected
- `MONTH_ORDER`, `MONTH_MAP` — Month normalization including Spanish names (Enero→Jan, etc.)
- `parseCurrency()` — Handles both EU and US number formats
- Country mapping normalizes various spellings (e.g., "Germany", "Deutschland", "DE", "Alemania" → "Amazon DE")
- `parseCurrency()` — Handles both EU (`1.234,56`) and US (`1,234.56`) number formats
- Country mapping normalizes various spellings → `Amazon DE`, `Amazon UK`, etc.
- `MONTH_MAP` — Normalizes Spanish month names (Enero→Jan, etc.)
### Filtering Architecture
Filters flow from `FilterBar.tsx``App.tsx` state → `filterData()`/`filterAdsData()` in dataProcessor.ts. When no customer filter is selected, ads data defaults to PAN_EU_COUNTRIES only. Sales data and ads data have separate filter functions.
Filters flow: `FilterBar.tsx``App.tsx` state → `filterData()` / `filterAdsData()` / `filterBsrData()` in dataProcessor.ts. When no customer filter is selected, ads data defaults to PAN_EU_COUNTRIES only. Sales and ads data have separate filter functions.
`globalAsinMetadata` (`Map<string, {sku, title, line}>`) is built from `rawData` in App.tsx and passed to views that need product metadata lookups.
### Path Alias