mirror of
https://github.com/christianvidalwolf-prog/CrazeAnalytix.git
synced 2026-08-03 13:05:24 +02:00
style: polish weekly sales UI with grouping and compact styling
This commit is contained in:
+59
-31
@@ -9,6 +9,21 @@ interface WeeklyGridProps {
|
|||||||
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'
|
||||||
|
const groupedRows = useMemo(() => {
|
||||||
|
const groups: { [line: string]: WeeklyPivotRow[] } = {};
|
||||||
|
rows.forEach(row => {
|
||||||
|
const line = row.line || 'Uncategorized';
|
||||||
|
if (!groups[line]) groups[line] = [];
|
||||||
|
groups[line].push(row);
|
||||||
|
});
|
||||||
|
// Sort lines alphabetically
|
||||||
|
return Object.keys(groups).sort().reduce((acc, line) => {
|
||||||
|
acc[line] = groups[line];
|
||||||
|
return acc;
|
||||||
|
}, {} as { [line: string]: WeeklyPivotRow[] });
|
||||||
|
}, [rows]);
|
||||||
|
|
||||||
// Calculate totals per week
|
// Calculate totals per week
|
||||||
const weekTotals = useMemo(() => {
|
const weekTotals = useMemo(() => {
|
||||||
const totals: { [weekKey: string]: number } = {};
|
const totals: { [weekKey: string]: number } = {};
|
||||||
@@ -24,58 +39,71 @@ const WeeklyGrid: React.FC<WeeklyGridProps> = ({ data }) => {
|
|||||||
const isPositive = pct >= 0;
|
const isPositive = pct >= 0;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`text-[10px] flex items-center gap-0.5 mt-0.5 font-bold ${isPositive ? 'text-emerald-400' : 'text-red-400'}`}>
|
<div className={`text-[8px] leading-none flex items-center justify-center gap-0.5 mt-0.5 font-bold ${isPositive ? 'text-emerald-400' : 'text-red-400'}`}>
|
||||||
{isPositive ? '▲' : '▼'}{Math.abs(pct).toFixed(0)}%
|
{isPositive ? '▲' : '▼'}{Math.abs(pct).toFixed(0)}%
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-slate-900/50 border border-white/10 rounded-xl overflow-hidden animate-fade-in shadow-2xl">
|
<div className="bg-slate-900 border border-white/10 rounded-xl overflow-hidden animate-fade-in shadow-2xl">
|
||||||
<div className="overflow-x-auto overflow-y-auto max-h-[calc(100vh-280px)]">
|
<div className="overflow-x-auto overflow-y-auto max-h-[calc(100vh-280px)]">
|
||||||
<table className="w-full text-left border-collapse min-w-[1000px]">
|
<table className="w-full text-left border-collapse min-w-[max-content]">
|
||||||
<thead className="sticky top-0 z-20">
|
<thead className="sticky top-0 z-20">
|
||||||
<tr className="bg-slate-900 border-b border-white/10">
|
<tr className="bg-slate-900 border-b border-white/10">
|
||||||
<th className="p-4 text-xs font-black text-slate-400 uppercase tracking-widest sticky left-0 z-30 bg-slate-900 border-r border-white/5 w-[300px]">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-4 text-xs font-black text-slate-400 uppercase tracking-widest text-center border-r border-white/5">
|
<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">
|
||||||
{week.split('-')[1]}/{week.split('-')[0].slice(-2)}
|
{week.split('-')[1]}/{week.split('-')[0].slice(-2)}
|
||||||
</th>
|
</th>
|
||||||
))}
|
))}
|
||||||
</tr>
|
</tr>
|
||||||
<tr className="bg-slate-800/80 backdrop-blur-md border-b border-white/10 italic">
|
<tr className="bg-indigo-950/40 backdrop-blur-md border-b border-white/10">
|
||||||
<th className="p-3 text-sm font-black text-white sticky left-0 z-30 bg-slate-800 border-r border-white/5">Totals</th>
|
<th className="p-2 text-xs font-black text-white sticky left-0 z-30 bg-indigo-900/60 border-r border-white/10">TOTALS</th>
|
||||||
{weeks.map((week, idx) => (
|
{weeks.map((week, idx) => (
|
||||||
<th key={week} className="p-3 text-sm font-black text-white text-center border-r border-white/5">
|
<th key={week} className="p-2 text-xs font-black text-white text-center border-r border-white/10">
|
||||||
{weekTotals[week]?.toLocaleString('de-DE')}
|
<div className="flex flex-col items-center">
|
||||||
{renderGrowth(weekTotals[week], weekTotals[weeks[idx + 1]])}
|
<span>{weekTotals[week]?.toLocaleString('de-DE')}</span>
|
||||||
|
{renderGrowth(weekTotals[week], weekTotals[weeks[idx + 1]])}
|
||||||
|
</div>
|
||||||
</th>
|
</th>
|
||||||
))}
|
))}
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody className="divide-y divide-white/5">
|
<tbody className="divide-y divide-white/5">
|
||||||
{rows.map((row) => (
|
{Object.entries(groupedRows).map(([line, lineRows]) => (
|
||||||
<tr key={row.id} className="hover:bg-white/[0.02] transition-colors group">
|
<React.Fragment key={line}>
|
||||||
<td className="p-4 sticky left-0 z-10 bg-slate-900 md:bg-slate-900/90 backdrop-blur-sm group-hover:bg-slate-800 border-r border-white/5">
|
{/* Category Header */}
|
||||||
<div className="flex flex-col">
|
<tr className="bg-slate-800/50">
|
||||||
<span className="text-xs font-black text-indigo-400 uppercase tracking-wider mb-0.5">{row.sku}</span>
|
<td colSpan={weeks.length + 1} className="p-1 px-3 text-[10px] font-black text-fuchsia-400 uppercase tracking-widest bg-slate-800/80 border-b border-white/5">
|
||||||
<span className="text-[11px] text-white/80 line-clamp-1 group-hover:line-clamp-none transition-all">{row.title}</span>
|
{line}
|
||||||
<span className="text-[10px] text-fuchsia-400/70 font-bold mt-1 uppercase tracking-tighter">{row.line}</span>
|
</td>
|
||||||
</div>
|
</tr>
|
||||||
</td>
|
{lineRows.map((row) => (
|
||||||
{weeks.map((week, idx) => {
|
<tr key={row.id} className="hover:bg-white/[0.02] transition-colors group">
|
||||||
const val = row.unitsByWeek[week] || 0;
|
<td className="p-2 py-1.5 sticky left-0 z-10 bg-slate-900 md:bg-slate-900/95 backdrop-blur-sm group-hover:bg-slate-800 border-r border-white/10">
|
||||||
const prevVal = row.unitsByWeek[weeks[idx + 1]] || 0;
|
<div className="flex flex-col">
|
||||||
return (
|
<span className="text-[10px] font-black text-indigo-400 uppercase tracking-tighter truncate w-[180px]">{row.sku}</span>
|
||||||
<td key={week} className="p-4 text-center border-r border-white/5 align-top">
|
<span className="text-[9px] text-white/60 truncate w-[180px] leading-tight" title={row.title}>{row.title}</span>
|
||||||
<span className={`text-sm font-black ${val > 0 ? 'text-white' : 'text-slate-600'}`}>
|
</div>
|
||||||
{val > 0 ? val.toLocaleString('de-DE') : '-'}
|
|
||||||
</span>
|
|
||||||
{val > 0 && renderGrowth(val, prevVal)}
|
|
||||||
</td>
|
</td>
|
||||||
);
|
{weeks.map((week, idx) => {
|
||||||
})}
|
const val = row.unitsByWeek[week] || 0;
|
||||||
</tr>
|
const prevVal = row.unitsByWeek[weeks[idx + 1]] || 0;
|
||||||
|
return (
|
||||||
|
<td key={week} className="p-2 py-1.5 text-center border-r border-white/5 align-middle">
|
||||||
|
<div className="flex flex-col items-center justify-center">
|
||||||
|
<span className={`text-xs font-bold ${val > 0 ? 'text-white' : 'text-slate-700'}`}>
|
||||||
|
{val > 0 ? val.toLocaleString('de-DE') : '-'}
|
||||||
|
</span>
|
||||||
|
{val > 0 && renderGrowth(val, prevVal)}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</React.Fragment>
|
||||||
))}
|
))}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
Reference in New Issue
Block a user