mirror of
https://github.com/christianvidalwolf-prog/CrazeAnalytix.git
synced 2026-08-03 17:35:23 +02:00
feat: add sorting by Top 50 rank in Weekly Sales
- Updated SortConfig to support 'rank' metric - Implemented sorting logic by 2025 rank (1-50) - Automatically trigger rank sort when 'Top 50 Only' filter is active - Added sortable header for 'Product Details' column - Added subtle animation for sort arrow
This commit is contained in:
@@ -9,9 +9,9 @@ interface WeeklyGridProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type SortConfig = {
|
type SortConfig = {
|
||||||
key: string; // weekKey
|
key: string; // weekKey or 'rank'
|
||||||
direction: 'asc' | 'desc';
|
direction: 'asc' | 'desc';
|
||||||
metric: 'units' | 'spend';
|
metric: 'units' | 'spend' | 'rank';
|
||||||
} | null;
|
} | null;
|
||||||
|
|
||||||
const ROWS_PER_PAGE = 50;
|
const ROWS_PER_PAGE = 50;
|
||||||
@@ -64,12 +64,12 @@ const WeeklyGrid: React.FC<WeeklyGridProps> = ({ data, top50Ranking }) => {
|
|||||||
setCurrentPage(1);
|
setCurrentPage(1);
|
||||||
}, [debouncedSearch, showOnlyTop50]);
|
}, [debouncedSearch, showOnlyTop50]);
|
||||||
|
|
||||||
const handleSort = useCallback((weekKey: string, metric: 'units' | 'spend') => {
|
const handleSort = useCallback((weekKey: string, metric: 'units' | 'spend' | 'rank') => {
|
||||||
setSortConfig(prev => {
|
setSortConfig(prev => {
|
||||||
if (prev?.key === weekKey && prev.metric === metric) {
|
if (prev?.key === weekKey && prev.metric === metric) {
|
||||||
return { key: weekKey, direction: prev.direction === 'asc' ? 'desc' : 'asc', metric };
|
return { key: weekKey, direction: prev.direction === 'asc' ? 'desc' : 'asc', metric };
|
||||||
}
|
}
|
||||||
return { key: weekKey, direction: 'desc', metric };
|
return { key: weekKey, direction: metric === 'rank' ? 'asc' : 'desc', metric };
|
||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@@ -149,6 +149,11 @@ const WeeklyGrid: React.FC<WeeklyGridProps> = ({ data, top50Ranking }) => {
|
|||||||
const direction = sortConfig.direction;
|
const direction = sortConfig.direction;
|
||||||
|
|
||||||
result.sort((a, b) => {
|
result.sort((a, b) => {
|
||||||
|
if (sortConfig.metric === 'rank') {
|
||||||
|
const rankA = top50Ranking?.get(a.asin.trim().toUpperCase()) || 999;
|
||||||
|
const rankB = top50Ranking?.get(b.asin.trim().toUpperCase()) || 999;
|
||||||
|
return direction === 'asc' ? rankA - rankB : rankB - rankA;
|
||||||
|
}
|
||||||
const valA = a[metricKey][weekKey] || 0;
|
const valA = a[metricKey][weekKey] || 0;
|
||||||
const valB = b[metricKey][weekKey] || 0;
|
const valB = b[metricKey][weekKey] || 0;
|
||||||
return direction === 'asc' ? valA - valB : valB - valA;
|
return direction === 'asc' ? valA - valB : valB - valA;
|
||||||
@@ -248,7 +253,13 @@ const WeeklyGrid: React.FC<WeeklyGridProps> = ({ data, top50Ranking }) => {
|
|||||||
{/* Top 50 Filter Toggle */}
|
{/* Top 50 Filter Toggle */}
|
||||||
{top50Ranking && top50Ranking.size > 0 && (
|
{top50Ranking && top50Ranking.size > 0 && (
|
||||||
<button
|
<button
|
||||||
onClick={() => setShowOnlyTop50(!showOnlyTop50)}
|
onClick={() => {
|
||||||
|
const newMode = !showOnlyTop50;
|
||||||
|
setShowOnlyTop50(newMode);
|
||||||
|
if (newMode) {
|
||||||
|
handleSort('rank', 'rank');
|
||||||
|
}
|
||||||
|
}}
|
||||||
className={`flex items-center gap-2 px-3 py-1.5 rounded-lg text-xs font-bold transition-all border ${showOnlyTop50
|
className={`flex items-center gap-2 px-3 py-1.5 rounded-lg text-xs font-bold transition-all border ${showOnlyTop50
|
||||||
? 'bg-gradient-to-r from-amber-500 to-orange-500 text-white border-amber-400 shadow-lg shadow-amber-500/20'
|
? 'bg-gradient-to-r from-amber-500 to-orange-500 text-white border-amber-400 shadow-lg shadow-amber-500/20'
|
||||||
: 'bg-slate-950/50 text-slate-400 border-white/10 hover:border-amber-500/50 hover:text-amber-400'
|
: 'bg-slate-950/50 text-slate-400 border-white/10 hover:border-amber-500/50 hover:text-amber-400'
|
||||||
@@ -307,7 +318,19 @@ const WeeklyGrid: React.FC<WeeklyGridProps> = ({ data, top50Ranking }) => {
|
|||||||
<table className="w-full text-left border-collapse min-w-[max-content]">
|
<table className="w-full text-left border-collapse min-w-[max-content]">
|
||||||
<thead className="sticky top-0 z-20 bg-slate-900">
|
<thead className="sticky top-0 z-20 bg-slate-900">
|
||||||
<tr className="bg-slate-900 border-b border-white/10">
|
<tr className="bg-slate-900 border-b border-white/10">
|
||||||
<th className="p-3 text-[11px] font-black text-slate-400 uppercase tracking-widest sticky left-0 z-40 bg-slate-900 border-r border-white/10 min-w-[240px]">Product Details</th>
|
<th
|
||||||
|
onClick={() => handleSort('rank', 'rank')}
|
||||||
|
className="p-3 text-[11px] font-black text-slate-400 uppercase tracking-widest sticky left-0 z-40 bg-slate-900 border-r border-white/10 min-w-[240px] cursor-pointer hover:bg-white/5 transition-colors group"
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span>Product Details</span>
|
||||||
|
{sortConfig?.metric === 'rank' && (
|
||||||
|
<span className="text-amber-500 font-black text-sm animate-bounce-subtle">
|
||||||
|
{sortConfig.direction === 'asc' ? '↑' : '↓'}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
{weeks.map(week => (
|
{weeks.map(week => (
|
||||||
<th
|
<th
|
||||||
key={week}
|
key={week}
|
||||||
|
|||||||
Reference in New Issue
Block a user