From 2bce0a839bc3d835ceb1196236e8f7135bf19ee0 Mon Sep 17 00:00:00 2001 From: Christian Vidal Wolf Date: Fri, 20 Feb 2026 13:19:25 +0100 Subject: [PATCH] feat: add Supabase vendor data integration (BSR, ratings, buy box) - Create vendor_daily_data table schema and Supabase client service - Add upload API route for Vendor Central CSV parsing and upsert - Add VendorDataView with BSR trend, ratings, and buy box charts - Integrate new Vendor tab into app navigation (desktop + mobile) - Add vendor CSV upload card to FileUpload modal Co-Authored-By: Claude Opus 4.6 --- App.tsx | 42 +- api/upload-vendor-data.ts | 104 ++++ components/FileUpload.tsx | 34 +- components/VendorDataView.tsx | 310 ++++++++++ .../2026-02-20-supabase-vendor-data-design.md | 108 ++++ .../2026-02-20-supabase-vendor-data-plan.md | 568 ++++++++++++++++++ package-lock.json | 116 +++- package.json | 3 +- services/supabase.ts | 114 ++++ vite.config.ts | 8 +- 10 files changed, 1400 insertions(+), 7 deletions(-) create mode 100644 api/upload-vendor-data.ts create mode 100644 components/VendorDataView.tsx create mode 100644 docs/plans/2026-02-20-supabase-vendor-data-design.md create mode 100644 docs/plans/2026-02-20-supabase-vendor-data-plan.md create mode 100644 services/supabase.ts diff --git a/App.tsx b/App.tsx index 6b8a8d5..7d421db 100644 --- a/App.tsx +++ b/App.tsx @@ -17,6 +17,7 @@ const WeeklyGrid = lazy(() => import('./components/WeeklyGrid')); const TopMovers = lazy(() => import('./components/TopMovers')); const AdsPerformance = lazy(() => import('./components/AdsPerformance')); const ForecastView = lazy(() => import('./components/ForecastView')); +const VendorDataView = lazy(() => import('./components/VendorDataView')); // Loading fallback component const LoadingSpinner = () => ( @@ -43,7 +44,7 @@ const App: React.FC = () => { const [trafficData, setTrafficData] = useState([]); const [loading, setLoading] = useState(true); const [syncing, setSyncing] = useState(false); - const [view, setView] = useState<'dashboard' | 'table' | 'weekly' | 'movers' | 'ads' | 'forecast'>('dashboard'); // Added 'forecast' view + const [view, setView] = useState<'dashboard' | 'table' | 'weekly' | 'movers' | 'ads' | 'forecast' | 'vendor'>('dashboard'); const [forecastData, setForecastData] = useState([]); const [isChatOpen, setIsChatOpen] = useState(false); const [activeUrl, setActiveUrl] = useState(() => localStorage.getItem('craze_csv_url') || PERMANENT_DROPBOX_URL); @@ -437,6 +438,28 @@ const App: React.FC = () => { } }; + // Handle uploaded Vendor CSV (sends to Supabase via API) + const handleVendorUpload = async (file: File) => { + setSyncing(true); + try { + const text = await file.text(); + const response = await fetch('/api/upload-vendor-data', { + method: 'POST', + headers: { 'Content-Type': 'text/csv' }, + body: text, + }); + const result = await response.json(); + if (!response.ok) throw new Error(result.error); + alert(`Vendor data uploaded: ${result.rowsUpserted} rows processed.`); + setIsDataModalOpen(false); + } catch (error: any) { + console.error("Failed to upload vendor data", error); + alert(`Error uploading vendor data: ${error.message}`); + } finally { + setSyncing(false); + } + }; + // 2. Schedule Auto-Refresh (Background) useEffect(() => { const checkAndRefresh = () => { @@ -734,6 +757,13 @@ const App: React.FC = () => { > Fc 26 + @@ -841,6 +871,12 @@ const App: React.FC = () => { /> + + }> +
+ +
+
)} @@ -851,7 +887,7 @@ const App: React.FC = () => { {/* Mobile Bottom Navigation */}