diff --git a/App.tsx b/App.tsx
index 88056bc..bbe9711 100644
--- a/App.tsx
+++ b/App.tsx
@@ -531,7 +531,7 @@ const App: React.FC = () => {
{view === 'table' && 0} adsData={filteredAdsData} />}
{view === 'weekly' && }
{view === 'movers' && }
- {view === 'ads' && }
+ {view === 'ads' && }
>
diff --git a/components/AdsPerformance.tsx b/components/AdsPerformance.tsx
index 9f90397..82c0fee 100644
--- a/components/AdsPerformance.tsx
+++ b/components/AdsPerformance.tsx
@@ -4,12 +4,38 @@ import { CombinedKPIs, FilterState } from '../types';
interface AdsPerformanceProps {
data: CombinedKPIs[];
filters: FilterState;
+ top50Ranking?: {
+ eu: Map;
+ uk: Map;
+ };
}
type SortKey = keyof CombinedKPIs | 'acos' | 'roas' | 'tacos' | 'ctr' | 'cpc' | 'cvrUnits';
-const AdsPerformance: React.FC = ({ data, filters }) => {
+// Top 50 Badge Component
+const Top50Badge: React.FC<{ rank: number; label?: string; theme?: 'amber' | 'blue' | 'indigo' }> = ({ rank, label, theme = 'amber' }) => {
+ const themeClasses = {
+ amber: 'from-amber-500 to-orange-500 border-amber-400/50',
+ blue: 'from-blue-500 to-cyan-500 border-blue-400/50',
+ indigo: 'from-indigo-500 to-purple-500 border-indigo-400/50',
+ };
+
+ return (
+
+ 🏆
+ {label && {label}}
+ {rank}
+
+ );
+};
+
+const AdsPerformance: React.FC = ({ data, filters, top50Ranking }) => {
const [searchTerm, setSearchTerm] = useState('');
+ const [showOnlyTop50, setShowOnlyTop50] = useState(false);
+ const [top50Mode, setTop50Mode] = useState<'eu' | 'uk'>('eu');
const [sortConfig, setSortConfig] = useState<{ key: SortKey; direction: 'asc' | 'desc' }>({
key: 'cost',
direction: 'desc'
@@ -137,7 +163,22 @@ const AdsPerformance: React.FC = ({ data, filters }) => {
// 3. Filter and Sort
const filteredAndSorted = useMemo(() => {
- let result = aggregatedByAsin.filter(item =>
+ let result = aggregatedByAsin;
+
+ // Top 50 Filter
+ if (showOnlyTop50 && top50Ranking) {
+ result = result.filter(item => {
+ const asin = item.asin.trim().toUpperCase();
+ if (top50Mode === 'eu') {
+ return top50Ranking.eu.has(asin);
+ } else {
+ return top50Ranking.uk.has(asin);
+ }
+ });
+ }
+
+ // Search Filter
+ result = result.filter(item =>
item.asin.toLowerCase().includes(searchTerm.toLowerCase()) ||
item.sku.toLowerCase().includes(searchTerm.toLowerCase()) ||
item.title.toLowerCase().includes(searchTerm.toLowerCase())
@@ -150,7 +191,7 @@ const AdsPerformance: React.FC = ({ data, filters }) => {
});
return result;
- }, [aggregatedByAsin, searchTerm, sortConfig]);
+ }, [aggregatedByAsin, searchTerm, sortConfig, showOnlyTop50, top50Ranking, top50Mode]);
const handleSort = (key: SortKey) => {
setSortConfig(prev => ({
@@ -213,6 +254,41 @@ const AdsPerformance: React.FC = ({ data, filters }) => {
+ {/* Top 50 Filter Toggle */}
+ {top50Ranking && (
+ (top50Ranking.eu.size || 0) > 0 ||
+ (top50Ranking.uk.size || 0) > 0
+ ) && (
+
+
+
+ {showOnlyTop50 && (
+
+
+
+
+ )}
+
+ )}
= ({ data, filters }) => {
- {filteredAndSorted.map((row: any) => (
-
- |
-
- {row.asin}
- SKU: {row.sku}
- {row.title}
-
- |
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ))}
+ {filteredAndSorted.map((row: any) => {
+ const asin = row.asin.trim().toUpperCase();
+ const ranks: { rank: number; label: string; theme: 'amber' | 'blue' | 'indigo' }[] = [];
+
+ if (top50Ranking) {
+ const euRank = top50Ranking.eu.get(asin);
+ const ukRank = top50Ranking.uk.get(asin);
+ if (euRank) ranks.push({ rank: euRank, label: 'EU', theme: 'indigo' });
+ if (ukRank) ranks.push({ rank: ukRank, label: 'UK', theme: 'blue' });
+ }
+
+ return (
+
+
+
+
+ {ranks.map((r, i) => (
+
+ ))}
+ {row.asin}
+
+ SKU: {row.sku}
+ {row.title}
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+ })}