feat: add Detail Level BSR metric to experiments analysis

This commit is contained in:
Christian Vidal Wolf
2026-03-10 13:58:44 +01:00
parent 2363982144
commit c15642d62d
7 changed files with 70 additions and 24 deletions
+14 -16
View File
@@ -5,6 +5,7 @@ import Dashboard from './components/Dashboard';
import FilterBar from './components/FilterBar';
import AIChat from './components/AIChat';
import CrazeLogo from './components/CrazeLogo';
import TopMovers from './components/TopMovers';
import { processCSV, processExcel, filterData, aggregateData, processAdsCSV, processAdsExcel, mergeSalesAndAdsData, processTrafficExcel, processStockExcel, filterAdsData, calculateForecastViewData, processVendorStockExcel, processUKInventoryExcel, calculateVelocityMap, getUniqueValues, processForecastExcel, processBuyBoxExcel, processBSRExcel, filterBsrData } from './services/dataProcessor';
import { SalesRecord, FilterState, AggregatedData, AdsRecord, TrafficRecord, ForecastRecord, ProductForecastData, ActiveExperiment, BSRRecord } from './types';
import { queryGemini } from './services/geminiService';
@@ -18,7 +19,6 @@ import {
// Lazy load heavy components for better initial performance
const DataGrid = lazy(() => import('./components/DataGrid'));
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'));
@@ -564,15 +564,20 @@ const App: React.FC = () => {
// Determine which dataset to use for velocity calculation based on top50Mode
// Derived Data for Vendor (BSR)
const filteredBsrData = useMemo(() => {
return filterBsrData(bsrData, filters, globalAsinMetadata, stockMap, vendorStockMap, top50Mode);
}, [bsrData, filters, globalAsinMetadata, stockMap, vendorStockMap, top50Mode]);
// Combine Sales & Ads Data dynamically based on current filters
const combinedAdsData = useMemo(() => {
return mergeSalesAndAdsData(filteredData, filteredAdsData, globalAsinMetadata, trafficData, velocityMap);
}, [filteredData, filteredAdsData, globalAsinMetadata, trafficData, velocityMap]);
return mergeSalesAndAdsData(filteredData, filteredAdsData, globalAsinMetadata, trafficData, velocityMap, filteredBsrData);
}, [filteredData, filteredAdsData, globalAsinMetadata, trafficData, velocityMap, filteredBsrData]);
// Unfiltered combined data for Experiment calculations (so they aren't affected by UI filters)
const unfilteredCombinedData = useMemo(() => {
return mergeSalesAndAdsData(rawData, adsData, globalAsinMetadata, trafficData, velocityMap);
}, [rawData, adsData, globalAsinMetadata, trafficData, velocityMap]);
return mergeSalesAndAdsData(rawData, adsData, globalAsinMetadata, trafficData, velocityMap, bsrData);
}, [rawData, adsData, globalAsinMetadata, trafficData, velocityMap, bsrData]);
// Derived Data for Grid (YTD Filtered - Isolated)
// 1. Identify "Current Year" max week
@@ -627,11 +632,6 @@ const App: React.FC = () => {
return aggregateData(ytdFilteredData);
}, [ytdFilteredData]);
// Derived Data for Vendor (BSR)
const filteredBsrData = useMemo(() => {
return filterBsrData(bsrData, filters, globalAsinMetadata, stockMap, vendorStockMap, top50Mode);
}, [bsrData, filters, globalAsinMetadata, stockMap, vendorStockMap, top50Mode]);
// Derived Data for Views
const years = useMemo(() => getUniqueValues(rawData, 'year').sort().reverse(), [rawData]);
@@ -823,7 +823,7 @@ const App: React.FC = () => {
<FilterBar filters={filters} onFilterChange={handleFilterChange} options={filterOptions} />
<div className="mt-6">
<div className={view === 'dashboard' ? '' : 'hidden'}>
<Dashboard data={ytdAggregatedData} filterState={filters} adsData={ytdFilteredAdsData} stockMap={stockMap} />
<Dashboard data={ytdAggregatedData} filterState={filters} adsData={ytdFilteredAdsData} stockMap={stockMap} rawData={rawData} />
</div>
<Suspense fallback={<LoadingSpinner />}>
@@ -868,11 +868,9 @@ const App: React.FC = () => {
</div>
</Suspense>
<Suspense fallback={<LoadingSpinner />}>
<div className={view === 'movers' ? '' : 'hidden'}>
<TopMovers data={filteredData} stockMap={stockMap} />
</div>
</Suspense>
<div className={view === 'movers' ? '' : 'hidden'}>
<TopMovers data={filteredData} />
</div>
<Suspense fallback={<LoadingSpinner />}>
<div className={view === 'ads' ? '' : 'hidden'}>