-
-
-
- {ranks.map((r, i) => (
-
- ))}
- onDrillDown?.(row.sku)}
- className={`text-xs font-black uppercase tracking-tighter truncate max-w-[120px] transition-all
- ${onDrillDown ? 'text-indigo-400 cursor-pointer hover:text-indigo-300 hover:underline' : 'text-indigo-400/70'}`}
- title={onDrillDown ? `Click to see Ads detail for ${row.sku}` : ''}
+ <>
+ {displayRows.map(row => (
+
+ ))}
+ {displayCount < sortedRows.length && (
+
+ |
+ |
- {weeks.map((week, idx) => {
- const val = row.unitsByWeek[week] || 0;
- const prevVal = row.unitsByWeek[weeks[idx + 1]] || 0;
- const spend = row.spendByWeek[week] || 0;
- const prevSpend = row.spendByWeek[weeks[idx + 1]] || 0;
- return (
-
-
-
- 0 ? (sortConfig?.key === week && sortConfig.metric === 'units' ? 'text-indigo-400' : 'text-white') : 'text-slate-700'}`}>
- {val > 0 ? val.toLocaleString('de-DE') : '-'}
-
- {val > 0 && renderGrowth(val, prevVal)}
-
-
- {spend > 0 && (
-
-
- €{spend.toLocaleString('de-DE', { minimumFractionDigits: 0, maximumFractionDigits: 0 })}
-
- {renderGrowth(spend, prevSpend)}
-
- )}
- {row.gvByWeek?.[week] > 0 && (
-
-
- GV: {row.gvByWeek[week].toLocaleString('de-DE')}
-
- {renderGrowth(row.gvByWeek[week], row.gvByWeek?.[weeks[idx + 1]] || 0)}
-
- )}
-
-
+ Load More SKUs ({sortedRows.length - displayCount} remaining)
+
|
- );
- })}
-
+ |
+ )}
+ >
);
- })
+ })()
) : (
|
diff --git a/services/dataProcessor.ts b/services/dataProcessor.ts
index 19a4c57..8d41c7d 100644
--- a/services/dataProcessor.ts
+++ b/services/dataProcessor.ts
@@ -511,13 +511,14 @@ export const mergeSalesAndAdsData = (
const createKey = (asin: string, customer: string, year: number, week: number) =>
`${asin.trim().toUpperCase()}|${customer.trim().toUpperCase()}|${year}|${week}`;
- // Build traffic lookup map
+ // Build traffic lookup map - Use a more efficient key
const trafficMap = new Map();
if (trafficData) {
- trafficData.forEach(t => {
- const key = createKey(t.asin, t.country, t.year, t.week);
- trafficMap.set(key, (trafficMap.get(key) || 0) + t.glanceViews);
- });
+ for (let i = 0; i < trafficData.length; i++) {
+ const t = trafficData[i];
+ const key = `${t.asin.trim().toUpperCase()}|${t.country.trim().toUpperCase()}|${t.year}|${t.week}`;
+ trafficMap.set(key, (trafficMap.get(key) || 0) + (t.glanceViews || 0));
+ }
}
// 1. Initialize metadata lookup map with provided global map if available, otherwise build from current sales
@@ -537,23 +538,24 @@ export const mergeSalesAndAdsData = (
month: string;
}>();
- salesData.forEach(sale => {
+ for (let i = 0; i < salesData.length; i++) {
+ const sale = salesData[i];
const weekNum = sale.week || 0;
- if (weekNum === 0) return;
+ if (weekNum === 0) continue;
- const key = createKey(sale.asin, sale.customer, sale.year, weekNum);
+ const asinUpper = sale.asin.trim().toUpperCase();
+ const key = `${asinUpper}|${sale.customer.trim().toUpperCase()}|${sale.year}|${weekNum}`;
// If no global map provided, build it on the fly
if (!asinMetadataMap) {
- const metaKey = sale.asin.trim().toUpperCase();
- const existingMeta = asinMetadata.get(metaKey);
+ const existingMeta = asinMetadata.get(asinUpper);
if (!existingMeta || (sale.title && sale.title.length > (existingMeta.title?.length || 0))) {
- asinMetadata.set(metaKey, { sku: sale.sku, title: sale.title, line: sale.line });
+ asinMetadata.set(asinUpper, { sku: sale.sku, title: sale.title, line: sale.line });
}
}
- if (salesMap.has(key)) {
- const existing = salesMap.get(key)!;
+ const existing = salesMap.get(key);
+ if (existing) {
existing.sellOut += sale.sellOut;
existing.units += sale.units;
if (sale.title && sale.title.length > (existing.title?.length || 0)) {
@@ -576,14 +578,15 @@ export const mergeSalesAndAdsData = (
month: sale.month
});
}
- });
+ }
// 3. Aggregate Ads by ASIN|Customer|Year|Week
const adsMap = new Map();
- adsData.forEach(ad => {
- const key = createKey(ad.asin, ad.country, ad.year, ad.week);
- if (adsMap.has(key)) {
- const existing = adsMap.get(key)!;
+ for (let i = 0; i < adsData.length; i++) {
+ const ad = adsData[i];
+ const key = `${ad.asin.trim().toUpperCase()}|${ad.country.trim().toUpperCase()}|${ad.year}|${ad.week}`;
+ const existing = adsMap.get(key);
+ if (existing) {
existing.cost += ad.cost;
existing.clicks += ad.clicks;
existing.impressions += ad.impressions;
@@ -593,7 +596,7 @@ export const mergeSalesAndAdsData = (
} else {
adsMap.set(key, { ...ad });
}
- });
+ }
const mergedData: CombinedKPIs[] = [];
const processedKeys = new Set();
@@ -1492,13 +1495,14 @@ export const pivotWeeklySalesData = (data: CombinedKPIs[]): {
const map = new Map();
- data.forEach(record => {
- // Use ASIN as primary key since mergeSalesAndAdsData outputs one record per ASIN/week
+ for (let i = 0; i < data.length; i++) {
+ const record = data[i];
const key = record.asin || record.sku || `${record.title}-${record.line}`;
- if (!key) return;
+ if (!key) continue;
- if (!map.has(key)) {
- map.set(key, {
+ let row = map.get(key);
+ if (!row) {
+ row = {
id: key,
sku: record.sku || '',
title: record.title || '',
@@ -1508,19 +1512,17 @@ export const pivotWeeklySalesData = (data: CombinedKPIs[]): {
unitsByWeek: {},
spendByWeek: {},
gvByWeek: {}
- });
+ };
+ map.set(key, row);
}
- const row = map.get(key)!;
if (record.week) {
const weekKey = `${record.year}-${String(record.week).padStart(2, '0')}`;
- row.unitsByWeek[weekKey] = (row.unitsByWeek[weekKey] || 0) + record.unitsTotal;
- // Only add cost if we haven't already added it for this ASIN/week
- // Since mergeSalesAndAdsData now outputs one record per ASIN/week, this should be clean
+ row.unitsByWeek[weekKey] = (row.unitsByWeek[weekKey] || 0) + (record.unitsTotal || 0);
row.spendByWeek[weekKey] = (row.spendByWeek[weekKey] || 0) + (record.cost || 0);
row.gvByWeek[weekKey] = (row.gvByWeek[weekKey] || 0) + (record.glanceViews || 0);
}
- });
+ }
return {
rows: Array.from(map.values()),
|