mirror of
https://github.com/christianvidalwolf-prog/CrazeAnalytix.git
synced 2026-08-03 14:05:24 +02:00
feat: add interactive sorting to weekly sales tab
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import React, { useMemo } from 'react';
|
import React, { useMemo, useState } from 'react';
|
||||||
import { CombinedKPIs } from '../types';
|
import { CombinedKPIs } from '../types';
|
||||||
import { pivotWeeklySalesData, WeeklyPivotRow } from '../services/dataProcessor';
|
import { pivotWeeklySalesData, WeeklyPivotRow } from '../services/dataProcessor';
|
||||||
|
|
||||||
@@ -6,23 +6,60 @@ interface WeeklyGridProps {
|
|||||||
data: CombinedKPIs[];
|
data: CombinedKPIs[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SortConfig = {
|
||||||
|
key: string; // weekKey
|
||||||
|
direction: 'asc' | 'desc';
|
||||||
|
} | null;
|
||||||
|
|
||||||
const WeeklyGrid: React.FC<WeeklyGridProps> = ({ data }) => {
|
const WeeklyGrid: React.FC<WeeklyGridProps> = ({ data }) => {
|
||||||
const { rows, weeks } = useMemo(() => pivotWeeklySalesData(data), [data]);
|
const { rows, weeks } = useMemo(() => pivotWeeklySalesData(data), [data]);
|
||||||
|
|
||||||
// Group rows by 'line'
|
// Default sort: most recent week, descending
|
||||||
|
const [sortConfig, setSortConfig] = useState<SortConfig>(() => {
|
||||||
|
if (weeks.length > 0) {
|
||||||
|
return { key: weeks[0], direction: 'desc' };
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleSort = (weekKey: string) => {
|
||||||
|
setSortConfig(prev => {
|
||||||
|
if (prev?.key === weekKey) {
|
||||||
|
return { key: weekKey, direction: prev.direction === 'asc' ? 'desc' : 'asc' };
|
||||||
|
}
|
||||||
|
return { key: weekKey, direction: 'desc' };
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Group and Sort rows
|
||||||
const groupedRows = useMemo(() => {
|
const groupedRows = useMemo(() => {
|
||||||
const groups: { [line: string]: WeeklyPivotRow[] } = {};
|
const groups: { [line: string]: WeeklyPivotRow[] } = {};
|
||||||
rows.forEach(row => {
|
|
||||||
|
// Clone and sort rows based on config
|
||||||
|
const sortedRows = [...rows];
|
||||||
|
if (sortConfig) {
|
||||||
|
sortedRows.sort((a, b) => {
|
||||||
|
const valA = a.unitsByWeek[sortConfig.key] || 0;
|
||||||
|
const valB = b.unitsByWeek[sortConfig.key] || 0;
|
||||||
|
if (sortConfig.direction === 'asc') {
|
||||||
|
return valA - valB;
|
||||||
|
}
|
||||||
|
return valB - valA;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
sortedRows.forEach(row => {
|
||||||
const line = row.line || 'Uncategorized';
|
const line = row.line || 'Uncategorized';
|
||||||
if (!groups[line]) groups[line] = [];
|
if (!groups[line]) groups[line] = [];
|
||||||
groups[line].push(row);
|
groups[line].push(row);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Sort lines alphabetically
|
// Sort lines alphabetically
|
||||||
return Object.keys(groups).sort().reduce((acc, line) => {
|
return Object.keys(groups).sort().reduce((acc, line) => {
|
||||||
acc[line] = groups[line];
|
acc[line] = groups[line];
|
||||||
return acc;
|
return acc;
|
||||||
}, {} as { [line: string]: WeeklyPivotRow[] });
|
}, {} as { [line: string]: WeeklyPivotRow[] });
|
||||||
}, [rows]);
|
}, [rows, sortConfig]);
|
||||||
|
|
||||||
// Calculate totals per week
|
// Calculate totals per week
|
||||||
const weekTotals = useMemo(() => {
|
const weekTotals = useMemo(() => {
|
||||||
@@ -53,8 +90,17 @@ const WeeklyGrid: React.FC<WeeklyGridProps> = ({ data }) => {
|
|||||||
<tr className="bg-slate-900 border-b border-white/10">
|
<tr className="bg-slate-900 border-b border-white/10">
|
||||||
<th className="p-2 text-[9px] font-black text-slate-400 uppercase tracking-widest sticky left-0 z-30 bg-slate-900 border-r border-white/10 min-w-[200px]">Product Details</th>
|
<th className="p-2 text-[9px] font-black text-slate-400 uppercase tracking-widest sticky left-0 z-30 bg-slate-900 border-r border-white/10 min-w-[200px]">Product Details</th>
|
||||||
{weeks.map(week => (
|
{weeks.map(week => (
|
||||||
<th key={week} className="p-2 text-[9px] font-black text-slate-400 uppercase tracking-widest text-center border-r border-white/10 w-24">
|
<th
|
||||||
{week.split('-')[1]}/{week.split('-')[0].slice(-2)}
|
key={week}
|
||||||
|
className={`p-2 text-[9px] font-black uppercase tracking-widest text-center border-r border-white/10 w-24 cursor-pointer hover:bg-white/5 transition-colors select-none ${sortConfig?.key === week ? 'text-indigo-400 bg-white/[0.02]' : 'text-slate-400'}`}
|
||||||
|
onClick={() => handleSort(week)}
|
||||||
|
>
|
||||||
|
<div className="flex flex-col items-center gap-1">
|
||||||
|
<span>{week.split('-')[1]}/{week.split('-')[0].slice(-2)}</span>
|
||||||
|
{sortConfig?.key === week && (
|
||||||
|
<span className="text-[10px]">{sortConfig.direction === 'asc' ? '↑' : '↓'}</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</th>
|
</th>
|
||||||
))}
|
))}
|
||||||
</tr>
|
</tr>
|
||||||
@@ -91,9 +137,9 @@ const WeeklyGrid: React.FC<WeeklyGridProps> = ({ data }) => {
|
|||||||
const val = row.unitsByWeek[week] || 0;
|
const val = row.unitsByWeek[week] || 0;
|
||||||
const prevVal = row.unitsByWeek[weeks[idx + 1]] || 0;
|
const prevVal = row.unitsByWeek[weeks[idx + 1]] || 0;
|
||||||
return (
|
return (
|
||||||
<td key={week} className="p-2 py-1.5 text-center border-r border-white/5 align-middle">
|
<td key={week} className={`p-2 py-1.5 text-center border-r border-white/5 align-middle ${sortConfig?.key === week ? 'bg-white/[0.01]' : ''}`}>
|
||||||
<div className="flex flex-col items-center justify-center">
|
<div className="flex flex-col items-center justify-center">
|
||||||
<span className={`text-xs font-bold ${val > 0 ? 'text-white' : 'text-slate-700'}`}>
|
<span className={`text-xs font-bold ${val > 0 ? (sortConfig?.key === week ? 'text-indigo-400' : 'text-white') : 'text-slate-700'}`}>
|
||||||
{val > 0 ? val.toLocaleString('de-DE') : '-'}
|
{val > 0 ? val.toLocaleString('de-DE') : '-'}
|
||||||
</span>
|
</span>
|
||||||
{val > 0 && renderGrowth(val, prevVal)}
|
{val > 0 && renderGrowth(val, prevVal)}
|
||||||
|
|||||||
Reference in New Issue
Block a user