fix: resolve syntax error in App.tsx (handleTrafficFetch)

This commit is contained in:
Christian Vidal Wolf
2026-01-27 17:00:32 +01:00
parent 0d1d2c77d4
commit f0d920de84
+62 -54
View File
@@ -125,14 +125,22 @@ const App: React.FC = () => {
} }
}, []); }, []);
const handleTrafficFetch = useCallback(async () => {
try {
console.log('[App] Fetching traffic from /api/fetch-traffic...');
const response = await fetch('/api/fetch-traffic');
if (!response.ok) throw new Error(`Failed to fetch traffic: ${response.status}`);
const buffer = await response.arrayBuffer();
const data = await processTrafficExcel(buffer);
setTrafficData(data); setTrafficData(data);
console.log('[App] Successfully loaded', data.length, 'traffic records'); console.log('[App] Successfully loaded', data.length, 'traffic records');
} catch (error) { } catch (error) {
console.error("Failed to fetch/parse Traffic", error); console.error("Failed to fetch/parse Traffic", error);
} }
}, []); }, []);
const handleStockFetch = useCallback(async () => { const handleStockFetch = useCallback(async () => {
try { try {
console.log('[App] Fetching stock from /api/fetch-stock...'); console.log('[App] Fetching stock from /api/fetch-stock...');
const response = await fetch('/api/fetch-stock'); const response = await fetch('/api/fetch-stock');
@@ -145,10 +153,10 @@ const handleStockFetch = useCallback(async () => {
} catch (error) { } catch (error) {
console.error("Failed to fetch/parse Stock", error); console.error("Failed to fetch/parse Stock", error);
} }
}, []); }, []);
const handleForecastFetch = useCallback(async (sales: SalesRecord[], globalSales: SalesRecord[], activeFilters: FilterState) => { const handleForecastFetch = useCallback(async (sales: SalesRecord[], globalSales: SalesRecord[], activeFilters: FilterState) => {
try { try {
const isUK = activeFilters.customer.includes('Amazon UK'); const isUK = activeFilters.customer.includes('Amazon UK');
const filename = isUK ? '/fc UK 26.xlsx' : '/fc 26.xlsx'; const filename = isUK ? '/fc UK 26.xlsx' : '/fc 26.xlsx';
@@ -178,10 +186,10 @@ const handleForecastFetch = useCallback(async (sales: SalesRecord[], globalSales
} catch (error) { } catch (error) {
console.warn("Forecast fetch failed:", error); console.warn("Forecast fetch failed:", error);
} }
}, []); }, []);
// Update forecast when filters change // Update forecast when filters change
useEffect(() => { useEffect(() => {
if (rawData.length > 0) { if (rawData.length > 0) {
// Filter rawData to respect customer selections but keep all years (2025 for seasonality, 2026 for actuals) // Filter rawData to respect customer selections but keep all years (2025 for seasonality, 2026 for actuals)
const forecastRelevantData = filterData(rawData, { const forecastRelevantData = filterData(rawData, {
@@ -190,9 +198,9 @@ useEffect(() => {
}); });
handleForecastFetch(forecastRelevantData, rawData, filters); handleForecastFetch(forecastRelevantData, rawData, filters);
} }
}, [filters, rawData, handleForecastFetch]); }, [filters, rawData, handleForecastFetch]);
const initializeData = (data: SalesRecord[]) => { const initializeData = (data: SalesRecord[]) => {
setRawData(data); setRawData(data);
setFilters({ setFilters({
customer: [], customer: [],
@@ -204,19 +212,19 @@ const initializeData = (data: SalesRecord[]) => {
title: [], title: [],
week: [], week: [],
}); });
}; };
const handleSkuDrillDown = useCallback((sku: string) => { const handleSkuDrillDown = useCallback((sku: string) => {
if (!sku) return; if (!sku) return;
setFilters(prev => ({ setFilters(prev => ({
...prev, ...prev,
sku: [sku] sku: [sku]
})); }));
setView('ads'); setView('ads');
}, []); }, []);
// 1. Initial Load from Cache (IndexedDB) or Auto-Fetch Permanent URL // 1. Initial Load from Cache (IndexedDB) or Auto-Fetch Permanent URL
useEffect(() => { useEffect(() => {
const initApp = async () => { const initApp = async () => {
setLoading(true); setLoading(true);
@@ -278,10 +286,10 @@ useEffect(() => {
handleForecastFetch(cachedData || [], cachedData || [], filters); handleForecastFetch(cachedData || [], cachedData || [], filters);
}; };
initApp(); initApp();
}, [handleDataFetch]); }, [handleDataFetch]);
// Handle uploaded Sales file (Manual) // Handle uploaded Sales file (Manual)
const handleSalesUpload = async (file: File) => { const handleSalesUpload = async (file: File) => {
setSyncing(true); setSyncing(true);
try { try {
const data = await processCSV(file); const data = await processCSV(file);
@@ -297,10 +305,10 @@ const handleSalesUpload = async (file: File) => {
} finally { } finally {
setSyncing(false); setSyncing(false);
} }
}; };
// Handle uploaded Ads file (Manual) - Supports both CSV and Excel // Handle uploaded Ads file (Manual) - Supports both CSV and Excel
const handleAdsUpload = async (file: File) => { const handleAdsUpload = async (file: File) => {
setSyncing(true); setSyncing(true);
try { try {
const isExcel = file.name.endsWith('.xlsx') || file.name.endsWith('.xls'); const isExcel = file.name.endsWith('.xlsx') || file.name.endsWith('.xls');
@@ -315,10 +323,10 @@ const handleAdsUpload = async (file: File) => {
} finally { } finally {
setSyncing(false); setSyncing(false);
} }
}; };
// Handle uploaded Traffic file (Manual) // Handle uploaded Traffic file (Manual)
const handleTrafficUpload = async (file: File) => { const handleTrafficUpload = async (file: File) => {
setSyncing(true); setSyncing(true);
try { try {
const data = await processTrafficExcel(file); const data = await processTrafficExcel(file);
@@ -331,10 +339,10 @@ const handleTrafficUpload = async (file: File) => {
} finally { } finally {
setSyncing(false); setSyncing(false);
} }
}; };
// 2. Schedule Auto-Refresh (Background) // 2. Schedule Auto-Refresh (Background)
useEffect(() => { useEffect(() => {
const checkAndRefresh = () => { const checkAndRefresh = () => {
const now = new Date(); const now = new Date();
const today = now.toISOString().split('T')[0]; // YYYY-MM-DD const today = now.toISOString().split('T')[0]; // YYYY-MM-DD
@@ -361,11 +369,11 @@ useEffect(() => {
const interval = setInterval(checkAndRefresh, 15 * 60 * 1000); const interval = setInterval(checkAndRefresh, 15 * 60 * 1000);
return () => clearInterval(interval); return () => clearInterval(interval);
}, [handleDataFetch]); }, [handleDataFetch]);
// Derive Data // Derive Data
const globalAsinMetadata = useMemo(() => { const globalAsinMetadata = useMemo(() => {
const metaMap = new Map<string, { sku: string; title: string; line: string }>(); const metaMap = new Map<string, { sku: string; title: string; line: string }>();
rawData.forEach(r => { rawData.forEach(r => {
const asin = r.asin.trim().toUpperCase(); const asin = r.asin.trim().toUpperCase();
@@ -376,14 +384,14 @@ const globalAsinMetadata = useMemo(() => {
} }
}); });
return metaMap; return metaMap;
}, [rawData]); }, [rawData]);
const filteredData = useMemo(() => filterData(rawData, filters), [rawData, filters]); const filteredData = useMemo(() => filterData(rawData, filters), [rawData, filters]);
const filteredAdsData = useMemo(() => filterAdsData(adsData, filters, globalAsinMetadata), [adsData, filters, globalAsinMetadata]); const filteredAdsData = useMemo(() => filterAdsData(adsData, filters, globalAsinMetadata), [adsData, filters, globalAsinMetadata]);
const aggregatedData = useMemo(() => aggregateData(filteredData), [filteredData]); const aggregatedData = useMemo(() => aggregateData(filteredData), [filteredData]);
// Calculate country-aware Top 50 Best Sellers for 2025 // Calculate country-aware Top 50 Best Sellers for 2025
const top50Ranking2025 = useMemo(() => { const top50Ranking2025 = useMemo(() => {
// Filter only 2025 data // Filter only 2025 data
const data2025 = rawData.filter(r => r.year === 2025); const data2025 = rawData.filter(r => r.year === 2025);
@@ -412,15 +420,15 @@ const top50Ranking2025 = useMemo(() => {
eu: calculateTop50(euData), eu: calculateTop50(euData),
uk: calculateTop50(ukData) uk: calculateTop50(ukData)
}; };
}, [rawData]); }, [rawData]);
// Combine Sales & Ads Data dynamically based on current filters // Combine Sales & Ads Data dynamically based on current filters
const combinedAdsData = useMemo(() => { const combinedAdsData = useMemo(() => {
return mergeSalesAndAdsData(filteredData, filteredAdsData, globalAsinMetadata, trafficData); return mergeSalesAndAdsData(filteredData, filteredAdsData, globalAsinMetadata, trafficData);
}, [filteredData, filteredAdsData, globalAsinMetadata, trafficData]); }, [filteredData, filteredAdsData, globalAsinMetadata, trafficData]);
// Derive Context Data (Product Line Context when drilling down) // Derive Context Data (Product Line Context when drilling down)
const contextAggregatedData = useMemo(() => { const contextAggregatedData = useMemo(() => {
// Check if we are filtering by specific items (SKU, ASIN, Title) // Check if we are filtering by specific items (SKU, ASIN, Title)
const hasItemFilters = filters.sku.length > 0 || filters.asin.length > 0 || filters.title.length > 0; const hasItemFilters = filters.sku.length > 0 || filters.asin.length > 0 || filters.title.length > 0;
@@ -444,11 +452,11 @@ const contextAggregatedData = useMemo(() => {
const broadData = filterData(rawData, contextFilters); const broadData = filterData(rawData, contextFilters);
return aggregateData(broadData); return aggregateData(broadData);
}, [rawData, filters, filteredData]); }, [rawData, filters, filteredData]);
// Derive Options for Filter Dropdowns // Derive Options for Filter Dropdowns
const filterOptions = useMemo(() => { const filterOptions = useMemo(() => {
return { return {
customer: getUniqueValues(rawData, 'customer'), customer: getUniqueValues(rawData, 'customer'),
year: getUniqueValues(rawData, 'year'), year: getUniqueValues(rawData, 'year'),
@@ -459,17 +467,17 @@ const filterOptions = useMemo(() => {
title: getUniqueValues(rawData, 'title'), title: getUniqueValues(rawData, 'title'),
week: Array.from(new Set(rawData.map(r => r.week).filter(w => w !== undefined))).sort((a, b) => (a as number) - (b as number)).map(w => `W${w}`), week: Array.from(new Set(rawData.map(r => r.week).filter(w => w !== undefined))).sort((a, b) => (a as number) - (b as number)).map(w => `W${w}`),
}; };
}, [rawData]); }, [rawData]);
const handleFilterChange = (key: keyof FilterState, value: string[]) => { const handleFilterChange = (key: keyof FilterState, value: string[]) => {
setFilters(prev => ({ ...prev, [key]: value })); setFilters(prev => ({ ...prev, [key]: value }));
}; };
const handleAskGemini = async (text: string) => { const handleAskGemini = async (text: string) => {
return await queryGemini(text, aggregatedData, filteredData.length); return await queryGemini(text, aggregatedData, filteredData.length);
}; };
const disconnectUrl = async () => { const disconnectUrl = async () => {
// Allow disconnecting to clear data, but the app will likely re-connect on next reload due to "Permanent" requirement // Allow disconnecting to clear data, but the app will likely re-connect on next reload due to "Permanent" requirement
localStorage.removeItem('craze_csv_url'); localStorage.removeItem('craze_csv_url');
await clearSalesData(); await clearSalesData();
@@ -477,9 +485,9 @@ const disconnectUrl = async () => {
setActiveUrl(null); setActiveUrl(null);
setRawData([]); setRawData([]);
setAdsData([]); setAdsData([]);
}; };
return ( return (
<div className="min-h-screen flex flex-col bg-background text-slate-200"> <div className="min-h-screen flex flex-col bg-background text-slate-200">
{/* Header */} {/* Header */}
@@ -646,7 +654,7 @@ return (
)} )}
</div> </div>
); );
}; };
export default App; export default App;