Optimize Forecast View performance and implement WOC filter across views

This commit is contained in:
Christian Vidal Wolf
2026-01-29 08:47:03 +01:00
parent 98e8bb3904
commit d19f0cddac
6 changed files with 169 additions and 12 deletions
+25 -7
View File
@@ -50,6 +50,8 @@ const App: React.FC = () => {
const [lastUpdated, setLastUpdated] = useState<string | null>(null);
const [stockMap, setStockMap] = useState<Map<string, number>>(new Map());
const [vendorStockMap, setVendorStockMap] = useState<Map<string, { eu: number; uk: number }>>(new Map());
const [cachedForecastRecords, setCachedForecastRecords] = useState<ForecastRecord[]>([]);
const [lastForecastFile, setLastForecastFile] = useState<string | null>(null);
// Modal State
const [isDataModalOpen, setIsDataModalOpen] = useState(false);
@@ -65,6 +67,7 @@ const App: React.FC = () => {
week: [],
stock: [],
vendorStock: [],
woc: [],
});
const top50Mode = useMemo(() => {
@@ -200,12 +203,20 @@ const App: React.FC = () => {
const isUK = activeFilters.customer.includes('Amazon UK');
const filename = isUK ? '/fc UK 26.xlsx' : '/fc 26.xlsx';
console.log(`[App] Fetching forecast from ${filename}...`);
const response = await fetch(filename);
if (!response.ok) throw new Error(`Forecast file ${filename} not found`);
let fcRecords = cachedForecastRecords;
const buffer = await response.arrayBuffer();
const fcRecords = await processForecastExcel(buffer);
if (lastForecastFile !== filename || cachedForecastRecords.length === 0) {
console.log(`[App] Fetching fresh forecast from ${filename}...`);
const response = await fetch(filename);
if (!response.ok) throw new Error(`Forecast file ${filename} not found`);
const buffer = await response.arrayBuffer();
fcRecords = await processForecastExcel(buffer);
setCachedForecastRecords(fcRecords);
setLastForecastFile(filename);
} else {
console.log(`[App] Using cached forecast for ${filename}`);
}
// Build precise metadata map from GLOBAL sales records to ensure info is found even if filtered for market
const meta = new Map<string, { sku: string; title: string; line: string }>();
@@ -221,11 +232,11 @@ const App: React.FC = () => {
const viewData = calculateForecastViewData(sales, fcRecords, meta, activeFilters, velocityMap);
setForecastData(viewData);
console.log('[App] Forecast loaded:', viewData.length, 'records');
console.log('[App] Forecast calculation complete:', viewData.length, 'records');
} catch (error) {
console.warn("Forecast fetch failed:", error);
}
}, [velocityMap]); // Added velocityMap to dependencies
}, [velocityMap, cachedForecastRecords, lastForecastFile]);
// Update forecast when filters change
useEffect(() => {
@@ -253,6 +264,7 @@ const App: React.FC = () => {
week: [],
stock: [],
vendorStock: [],
woc: [],
});
};
@@ -676,6 +688,8 @@ const App: React.FC = () => {
onStockFilterChange={(s) => setFilters(prev => ({ ...prev, stock: s }))}
vendorStockFilter={filters.vendorStock}
onVendorStockFilterChange={(s) => setFilters(prev => ({ ...prev, vendorStock: s }))}
wocFilter={filters.woc}
onWocFilterChange={(s) => setFilters(prev => ({ ...prev, woc: s }))}
customerFilters={filters.customer}
top50Mode={filters.customer.includes('Amazon UK') ? 'uk' : 'eu'}
top50Ranking={top50Ranking2025}
@@ -702,6 +716,8 @@ const App: React.FC = () => {
onStockFilterChange={(s) => setFilters(prev => ({ ...prev, stock: s }))}
vendorStockFilter={filters.vendorStock}
onVendorStockFilterChange={(s) => setFilters(prev => ({ ...prev, vendorStock: s }))}
wocFilter={filters.woc}
onWocFilterChange={(s) => setFilters(prev => ({ ...prev, woc: s }))}
top50Mode={filters.customer.includes('Amazon UK') ? 'uk' : 'eu'}
/>
</div>
@@ -719,6 +735,8 @@ const App: React.FC = () => {
onStockFilterChange={(s) => setFilters(prev => ({ ...prev, stock: s }))}
vendorStockFilter={filters.vendorStock}
onVendorStockFilterChange={(s) => setFilters(prev => ({ ...prev, vendorStock: s }))}
wocFilter={filters.woc}
onWocFilterChange={(s) => setFilters(prev => ({ ...prev, woc: s }))}
top50Mode={filters.customer.includes('Amazon UK') ? 'uk' : 'eu'}
/>
</div>