mirror of
https://github.com/christianvidalwolf-prog/CrazeAnalytix.git
synced 2026-08-03 12:45:23 +02:00
feat: enable sorting and filtering by Revenue in Weekly Sales grid
This commit is contained in:
@@ -2,7 +2,7 @@ import React, { useState, useRef, useEffect } from 'react';
|
|||||||
|
|
||||||
export interface NumericFilterConfig {
|
export interface NumericFilterConfig {
|
||||||
week: string;
|
week: string;
|
||||||
metric: 'units' | 'spend' | 'gv';
|
metric: 'units' | 'spend' | 'revenue' | 'gv';
|
||||||
operator: '=' | '>' | '>=' | '<' | '<=' | 'between';
|
operator: '=' | '>' | '>=' | '<' | '<=' | 'between';
|
||||||
value: number;
|
value: number;
|
||||||
value2?: number;
|
value2?: number;
|
||||||
@@ -10,7 +10,7 @@ export interface NumericFilterConfig {
|
|||||||
|
|
||||||
interface NumericColumnFilterProps {
|
interface NumericColumnFilterProps {
|
||||||
week: string;
|
week: string;
|
||||||
metric: 'units' | 'spend' | 'gv';
|
metric: 'units' | 'spend' | 'revenue' | 'gv';
|
||||||
currentFilter: NumericFilterConfig | null;
|
currentFilter: NumericFilterConfig | null;
|
||||||
onFilterChange: (filter: NumericFilterConfig | null) => void;
|
onFilterChange: (filter: NumericFilterConfig | null) => void;
|
||||||
accentColor?: 'indigo' | 'amber' | 'teal';
|
accentColor?: 'indigo' | 'amber' | 'teal';
|
||||||
|
|||||||
+15
-12
@@ -35,7 +35,7 @@ interface WeeklyGridProps {
|
|||||||
type SortConfig = {
|
type SortConfig = {
|
||||||
key: string; // weekKey or 'rank'
|
key: string; // weekKey or 'rank'
|
||||||
direction: 'asc' | 'desc';
|
direction: 'asc' | 'desc';
|
||||||
metric: 'units' | 'spend' | 'rank' | 'gv';
|
metric: 'units' | 'spend' | 'revenue' | 'rank' | 'gv';
|
||||||
} | null;
|
} | null;
|
||||||
|
|
||||||
const ROWS_PER_PAGE = 50;
|
const ROWS_PER_PAGE = 50;
|
||||||
@@ -370,11 +370,11 @@ const WeeklyGrid: React.FC<WeeklyGridProps & { top50Mode: 'eu' | 'uk' }> = ({
|
|||||||
const [numericFilters, setNumericFilters] = useState<NumericFilterConfig[]>([]);
|
const [numericFilters, setNumericFilters] = useState<NumericFilterConfig[]>([]);
|
||||||
|
|
||||||
// Helper to get/set filter for a specific week+metric
|
// Helper to get/set filter for a specific week+metric
|
||||||
const getNumericFilter = useCallback((week: string, metric: 'units' | 'spend' | 'gv') => {
|
const getNumericFilter = useCallback((week: string, metric: 'units' | 'spend' | 'revenue' | 'gv') => {
|
||||||
return numericFilters.find(f => f.week === week && f.metric === metric) || null;
|
return numericFilters.find(f => f.week === week && f.metric === metric) || null;
|
||||||
}, [numericFilters]);
|
}, [numericFilters]);
|
||||||
|
|
||||||
const setNumericFilter = useCallback((filter: NumericFilterConfig | null, week: string, metric: 'units' | 'spend' | 'gv') => {
|
const setNumericFilter = useCallback((filter: NumericFilterConfig | null, week: string, metric: 'units' | 'spend' | 'revenue' | 'gv') => {
|
||||||
setNumericFilters(prev => {
|
setNumericFilters(prev => {
|
||||||
// Remove existing filter for this week+metric
|
// Remove existing filter for this week+metric
|
||||||
const filtered = prev.filter(f => !(f.week === week && f.metric === metric));
|
const filtered = prev.filter(f => !(f.week === week && f.metric === metric));
|
||||||
@@ -400,7 +400,7 @@ const WeeklyGrid: React.FC<WeeklyGridProps & { top50Mode: 'eu' | 'uk' }> = ({
|
|||||||
setDisplayCount(50);
|
setDisplayCount(50);
|
||||||
}, [debouncedSearch, showOnlyTop50, top50Mode]);
|
}, [debouncedSearch, showOnlyTop50, top50Mode]);
|
||||||
|
|
||||||
const handleSort = useCallback((weekKey: string, metric: 'units' | 'spend' | 'rank' | 'gv') => {
|
const handleSort = useCallback((weekKey: string, metric: 'units' | 'spend' | 'revenue' | 'rank' | 'gv') => {
|
||||||
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 };
|
||||||
@@ -559,6 +559,8 @@ const WeeklyGrid: React.FC<WeeklyGridProps & { top50Mode: 'eu' | 'uk' }> = ({
|
|||||||
value = row.unitsByWeek[filter.week] || 0;
|
value = row.unitsByWeek[filter.week] || 0;
|
||||||
} else if (filter.metric === 'spend') {
|
} else if (filter.metric === 'spend') {
|
||||||
value = row.spendByWeek[filter.week] || 0;
|
value = row.spendByWeek[filter.week] || 0;
|
||||||
|
} else if (filter.metric === 'revenue') {
|
||||||
|
value = row.revenueByWeek?.[filter.week] || 0;
|
||||||
} else if (filter.metric === 'gv') {
|
} else if (filter.metric === 'gv') {
|
||||||
value = row.gvByWeek?.[filter.week] || 0;
|
value = row.gvByWeek?.[filter.week] || 0;
|
||||||
}
|
}
|
||||||
@@ -590,7 +592,8 @@ const WeeklyGrid: React.FC<WeeklyGridProps & { top50Mode: 'eu' | 'uk' }> = ({
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const metricKey = metric === 'units' ? 'unitsByWeek' :
|
const metricKey = metric === 'units' ? 'unitsByWeek' :
|
||||||
metric === 'spend' ? 'spendByWeek' : 'gvByWeek';
|
metric === 'spend' ? 'spendByWeek' :
|
||||||
|
metric === 'revenue' ? 'revenueByWeek' : 'gvByWeek';
|
||||||
|
|
||||||
result.sort((a, b) => {
|
result.sort((a, b) => {
|
||||||
const valA = a[metricKey][weekKey] || 0;
|
const valA = a[metricKey][weekKey] || 0;
|
||||||
@@ -889,22 +892,22 @@ const WeeklyGrid: React.FC<WeeklyGridProps & { top50Mode: 'eu' | 'uk' }> = ({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className={`flex-1 p-1.5 transition-colors flex items-center justify-center gap-1 border-b border-white/5 ${sortConfig?.key === week && sortConfig.metric === 'units' ? 'bg-indigo-500/5 text-indigo-400' : 'text-slate-500'}`}
|
className={`flex-1 p-1.5 transition-colors flex items-center justify-center gap-1 border-b border-white/5 ${sortConfig?.key === week && sortConfig.metric === (primaryMetric === 'units' ? 'units' : 'revenue') ? 'bg-indigo-500/5 text-indigo-400' : 'text-slate-500'}`}
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
onClick={() => handleSort(week, 'units')}
|
onClick={() => handleSort(week, primaryMetric === 'units' ? 'units' : 'revenue')}
|
||||||
className="text-[9px] cursor-pointer hover:text-indigo-300"
|
className="text-[9px] cursor-pointer hover:text-indigo-300"
|
||||||
>
|
>
|
||||||
Units
|
{primaryMetric === 'units' ? 'Units' : 'Revenue'}
|
||||||
</span>
|
</span>
|
||||||
{sortConfig?.key === week && sortConfig.metric === 'units' && (
|
{sortConfig?.key === week && sortConfig.metric === (primaryMetric === 'units' ? 'units' : 'revenue') && (
|
||||||
<span className="text-xs font-bold leading-none">{sortConfig.direction === 'asc' ? '↑' : '↓'}</span>
|
<span className="text-xs font-bold leading-none">{sortConfig.direction === 'asc' ? '↑' : '↓'}</span>
|
||||||
)}
|
)}
|
||||||
<NumericColumnFilter
|
<NumericColumnFilter
|
||||||
week={week}
|
week={week}
|
||||||
metric="units"
|
metric={primaryMetric === 'units' ? 'units' : 'revenue'}
|
||||||
currentFilter={getNumericFilter(week, 'units')}
|
currentFilter={getNumericFilter(week, primaryMetric === 'units' ? 'units' : 'revenue')}
|
||||||
onFilterChange={(f) => setNumericFilter(f, week, 'units')}
|
onFilterChange={(f) => setNumericFilter(f, week, primaryMetric === 'units' ? 'units' : 'revenue')}
|
||||||
accentColor="indigo"
|
accentColor="indigo"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user