mirror of
https://github.com/christianvidalwolf-prog/CrazeAnalytix.git
synced 2026-08-03 13:05:24 +02:00
fix: resolve syntax error in App.tsx (handleTrafficFetch)
This commit is contained in:
@@ -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);
|
||||
console.log('[App] Successfully loaded', data.length, 'traffic records');
|
||||
} catch (error) {
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch/parse Traffic", error);
|
||||
}
|
||||
}
|
||||
}, []);
|
||||
|
||||
const handleStockFetch = useCallback(async () => {
|
||||
const handleStockFetch = useCallback(async () => {
|
||||
try {
|
||||
console.log('[App] Fetching stock from /api/fetch-stock...');
|
||||
const response = await fetch('/api/fetch-stock');
|
||||
@@ -145,10 +153,10 @@ const handleStockFetch = useCallback(async () => {
|
||||
} catch (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 {
|
||||
const isUK = activeFilters.customer.includes('Amazon UK');
|
||||
const filename = isUK ? '/fc UK 26.xlsx' : '/fc 26.xlsx';
|
||||
@@ -178,10 +186,10 @@ const handleForecastFetch = useCallback(async (sales: SalesRecord[], globalSales
|
||||
} catch (error) {
|
||||
console.warn("Forecast fetch failed:", error);
|
||||
}
|
||||
}, []);
|
||||
}, []);
|
||||
|
||||
// Update forecast when filters change
|
||||
useEffect(() => {
|
||||
// Update forecast when filters change
|
||||
useEffect(() => {
|
||||
if (rawData.length > 0) {
|
||||
// Filter rawData to respect customer selections but keep all years (2025 for seasonality, 2026 for actuals)
|
||||
const forecastRelevantData = filterData(rawData, {
|
||||
@@ -190,9 +198,9 @@ useEffect(() => {
|
||||
});
|
||||
handleForecastFetch(forecastRelevantData, rawData, filters);
|
||||
}
|
||||
}, [filters, rawData, handleForecastFetch]);
|
||||
}, [filters, rawData, handleForecastFetch]);
|
||||
|
||||
const initializeData = (data: SalesRecord[]) => {
|
||||
const initializeData = (data: SalesRecord[]) => {
|
||||
setRawData(data);
|
||||
setFilters({
|
||||
customer: [],
|
||||
@@ -204,19 +212,19 @@ const initializeData = (data: SalesRecord[]) => {
|
||||
title: [],
|
||||
week: [],
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
const handleSkuDrillDown = useCallback((sku: string) => {
|
||||
const handleSkuDrillDown = useCallback((sku: string) => {
|
||||
if (!sku) return;
|
||||
setFilters(prev => ({
|
||||
...prev,
|
||||
sku: [sku]
|
||||
}));
|
||||
setView('ads');
|
||||
}, []);
|
||||
}, []);
|
||||
|
||||
// 1. Initial Load from Cache (IndexedDB) or Auto-Fetch Permanent URL
|
||||
useEffect(() => {
|
||||
// 1. Initial Load from Cache (IndexedDB) or Auto-Fetch Permanent URL
|
||||
useEffect(() => {
|
||||
const initApp = async () => {
|
||||
setLoading(true);
|
||||
|
||||
@@ -278,10 +286,10 @@ useEffect(() => {
|
||||
handleForecastFetch(cachedData || [], cachedData || [], filters);
|
||||
};
|
||||
initApp();
|
||||
}, [handleDataFetch]);
|
||||
}, [handleDataFetch]);
|
||||
|
||||
// Handle uploaded Sales file (Manual)
|
||||
const handleSalesUpload = async (file: File) => {
|
||||
// Handle uploaded Sales file (Manual)
|
||||
const handleSalesUpload = async (file: File) => {
|
||||
setSyncing(true);
|
||||
try {
|
||||
const data = await processCSV(file);
|
||||
@@ -297,10 +305,10 @@ const handleSalesUpload = async (file: File) => {
|
||||
} finally {
|
||||
setSyncing(false);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// Handle uploaded Ads file (Manual) - Supports both CSV and Excel
|
||||
const handleAdsUpload = async (file: File) => {
|
||||
// Handle uploaded Ads file (Manual) - Supports both CSV and Excel
|
||||
const handleAdsUpload = async (file: File) => {
|
||||
setSyncing(true);
|
||||
try {
|
||||
const isExcel = file.name.endsWith('.xlsx') || file.name.endsWith('.xls');
|
||||
@@ -315,10 +323,10 @@ const handleAdsUpload = async (file: File) => {
|
||||
} finally {
|
||||
setSyncing(false);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// Handle uploaded Traffic file (Manual)
|
||||
const handleTrafficUpload = async (file: File) => {
|
||||
// Handle uploaded Traffic file (Manual)
|
||||
const handleTrafficUpload = async (file: File) => {
|
||||
setSyncing(true);
|
||||
try {
|
||||
const data = await processTrafficExcel(file);
|
||||
@@ -331,10 +339,10 @@ const handleTrafficUpload = async (file: File) => {
|
||||
} finally {
|
||||
setSyncing(false);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// 2. Schedule Auto-Refresh (Background)
|
||||
useEffect(() => {
|
||||
// 2. Schedule Auto-Refresh (Background)
|
||||
useEffect(() => {
|
||||
const checkAndRefresh = () => {
|
||||
const now = new Date();
|
||||
const today = now.toISOString().split('T')[0]; // YYYY-MM-DD
|
||||
@@ -361,11 +369,11 @@ useEffect(() => {
|
||||
const interval = setInterval(checkAndRefresh, 15 * 60 * 1000);
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, [handleDataFetch]);
|
||||
}, [handleDataFetch]);
|
||||
|
||||
|
||||
// Derive Data
|
||||
const globalAsinMetadata = useMemo(() => {
|
||||
// Derive Data
|
||||
const globalAsinMetadata = useMemo(() => {
|
||||
const metaMap = new Map<string, { sku: string; title: string; line: string }>();
|
||||
rawData.forEach(r => {
|
||||
const asin = r.asin.trim().toUpperCase();
|
||||
@@ -376,14 +384,14 @@ const globalAsinMetadata = useMemo(() => {
|
||||
}
|
||||
});
|
||||
return metaMap;
|
||||
}, [rawData]);
|
||||
}, [rawData]);
|
||||
|
||||
const filteredData = useMemo(() => filterData(rawData, filters), [rawData, filters]);
|
||||
const filteredAdsData = useMemo(() => filterAdsData(adsData, filters, globalAsinMetadata), [adsData, filters, globalAsinMetadata]);
|
||||
const aggregatedData = useMemo(() => aggregateData(filteredData), [filteredData]);
|
||||
const filteredData = useMemo(() => filterData(rawData, filters), [rawData, filters]);
|
||||
const filteredAdsData = useMemo(() => filterAdsData(adsData, filters, globalAsinMetadata), [adsData, filters, globalAsinMetadata]);
|
||||
const aggregatedData = useMemo(() => aggregateData(filteredData), [filteredData]);
|
||||
|
||||
// Calculate country-aware Top 50 Best Sellers for 2025
|
||||
const top50Ranking2025 = useMemo(() => {
|
||||
// Calculate country-aware Top 50 Best Sellers for 2025
|
||||
const top50Ranking2025 = useMemo(() => {
|
||||
// Filter only 2025 data
|
||||
const data2025 = rawData.filter(r => r.year === 2025);
|
||||
|
||||
@@ -412,15 +420,15 @@ const top50Ranking2025 = useMemo(() => {
|
||||
eu: calculateTop50(euData),
|
||||
uk: calculateTop50(ukData)
|
||||
};
|
||||
}, [rawData]);
|
||||
}, [rawData]);
|
||||
|
||||
// Combine Sales & Ads Data dynamically based on current filters
|
||||
const combinedAdsData = useMemo(() => {
|
||||
// Combine Sales & Ads Data dynamically based on current filters
|
||||
const combinedAdsData = useMemo(() => {
|
||||
return mergeSalesAndAdsData(filteredData, filteredAdsData, globalAsinMetadata, trafficData);
|
||||
}, [filteredData, filteredAdsData, globalAsinMetadata, trafficData]);
|
||||
}, [filteredData, filteredAdsData, globalAsinMetadata, trafficData]);
|
||||
|
||||
// Derive Context Data (Product Line Context when drilling down)
|
||||
const contextAggregatedData = useMemo(() => {
|
||||
// Derive Context Data (Product Line Context when drilling down)
|
||||
const contextAggregatedData = useMemo(() => {
|
||||
// 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;
|
||||
|
||||
@@ -444,11 +452,11 @@ const contextAggregatedData = useMemo(() => {
|
||||
const broadData = filterData(rawData, contextFilters);
|
||||
return aggregateData(broadData);
|
||||
|
||||
}, [rawData, filters, filteredData]);
|
||||
}, [rawData, filters, filteredData]);
|
||||
|
||||
|
||||
// Derive Options for Filter Dropdowns
|
||||
const filterOptions = useMemo(() => {
|
||||
// Derive Options for Filter Dropdowns
|
||||
const filterOptions = useMemo(() => {
|
||||
return {
|
||||
customer: getUniqueValues(rawData, 'customer'),
|
||||
year: getUniqueValues(rawData, 'year'),
|
||||
@@ -459,17 +467,17 @@ const filterOptions = useMemo(() => {
|
||||
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}`),
|
||||
};
|
||||
}, [rawData]);
|
||||
}, [rawData]);
|
||||
|
||||
const handleFilterChange = (key: keyof FilterState, value: string[]) => {
|
||||
const handleFilterChange = (key: keyof FilterState, value: string[]) => {
|
||||
setFilters(prev => ({ ...prev, [key]: value }));
|
||||
};
|
||||
};
|
||||
|
||||
const handleAskGemini = async (text: string) => {
|
||||
const handleAskGemini = async (text: string) => {
|
||||
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
|
||||
localStorage.removeItem('craze_csv_url');
|
||||
await clearSalesData();
|
||||
@@ -477,9 +485,9 @@ const disconnectUrl = async () => {
|
||||
setActiveUrl(null);
|
||||
setRawData([]);
|
||||
setAdsData([]);
|
||||
};
|
||||
};
|
||||
|
||||
return (
|
||||
return (
|
||||
<div className="min-h-screen flex flex-col bg-background text-slate-200">
|
||||
|
||||
{/* Header */}
|
||||
@@ -646,7 +654,7 @@ return (
|
||||
)}
|
||||
|
||||
</div>
|
||||
);
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
||||
|
||||
Reference in New Issue
Block a user