Remove global date/district filter from top bar

The global filter caused confusion — activities saved with future dates
were silently filtered out, making it appear saves didn't work. Subviews
can add their own filtering where it makes sense.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-09-04 12:16:05 -04:00
parent eb13f292e4
commit 1435fb5efc
2 changed files with 3 additions and 132 deletions

View File

@@ -1,49 +1,12 @@
'use client';
import { useData, DatePreset } from '@/lib/data-context';
import { useData } from '@/lib/data-context';
import { usePageTitle } from '@/lib/page-title-context';
import { format } from 'date-fns';
import { useState, useRef, useEffect } from 'react';
import { DISTRICTS } from '@/types/data';
const DATE_PRESETS: { value: DatePreset; label: string }[] = [
{ value: 'this_week', label: 'This Week' },
{ value: 'last_7', label: 'Last 7 Days' },
{ value: 'this_month', label: 'This Month' },
{ value: 'this_quarter', label: 'This Quarter' },
{ value: 'last_quarter', label: 'Last Quarter' },
];
export function TopBar() {
const { filters, setDistricts, setDatePreset, refresh, isLoading, lastRefreshed, clearAllFilters, clearCrossFilter } = useData();
const { filters, refresh, isLoading, lastRefreshed, clearCrossFilter } = useData();
const { title } = usePageTitle();
const [showDistrictDropdown, setShowDistrictDropdown] = useState(false);
const dropdownRef = useRef<HTMLDivElement>(null);
useEffect(() => {
function handleClick(e: MouseEvent) {
if (dropdownRef.current && !dropdownRef.current.contains(e.target as Node)) {
setShowDistrictDropdown(false);
}
}
document.addEventListener('mousedown', handleClick);
return () => document.removeEventListener('mousedown', handleClick);
}, []);
const toggleDistrict = (d: string) => {
const current = filters.districts;
if (current.includes(d)) {
setDistricts(current.filter(x => x !== d));
} else {
setDistricts([...current, d]);
}
};
const districtLabel = filters.districts.length === 0
? 'All Districts'
: filters.districts.length === 1
? filters.districts[0].replace('SE-', '')
: `${filters.districts.length} Districts`;
return (
<header className="h-14 bg-topbar-bg border-b border-topbar-border flex items-center px-4 gap-3 flex-shrink-0">
@@ -71,55 +34,6 @@ export function TopBar() {
</button>
)}
{/* Date preset */}
<select
value={filters.datePreset}
onChange={e => setDatePreset(e.target.value as DatePreset)}
className="hidden sm:block text-xs border border-card-border rounded-lg px-2.5 py-1.5 bg-white text-foreground focus:outline-none focus:ring-2 focus:ring-brand-azure/30"
>
{DATE_PRESETS.map(p => (
<option key={p.value} value={p.value}>{p.label}</option>
))}
</select>
{/* District filter */}
<div className="relative" ref={dropdownRef}>
<button
onClick={() => setShowDistrictDropdown(!showDistrictDropdown)}
className="text-xs border border-card-border rounded-lg px-2.5 py-1.5 bg-white text-foreground flex items-center gap-1.5 hover:border-brand-azure/50 transition"
>
<svg className="w-3.5 h-3.5 text-muted" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><polygon points="22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3" /></svg>
<span className="hidden sm:inline">{districtLabel}</span>
<span className="sm:hidden">
{filters.districts.length > 0 ? filters.districts.length : 'All'}
</span>
</button>
{showDistrictDropdown && (
<div className="absolute right-0 top-full mt-1 w-52 bg-white rounded-lg shadow-lg border border-card-border py-1 z-50">
<button
onClick={() => { setDistricts([]); setShowDistrictDropdown(false); }}
className={`w-full text-left px-3 py-2 text-xs hover:bg-brand-azure/5 ${filters.districts.length === 0 ? 'text-brand-azure font-semibold' : 'text-foreground'}`}
>
All Districts
</button>
{DISTRICTS.map(d => (
<button
key={d}
onClick={() => toggleDistrict(d)}
className="w-full text-left px-3 py-2 text-xs hover:bg-brand-azure/5 flex items-center gap-2"
>
<div className={`w-3.5 h-3.5 rounded border ${filters.districts.includes(d) ? 'bg-brand-azure border-brand-azure' : 'border-card-border'} flex items-center justify-center`}>
{filters.districts.includes(d) && (
<svg className="w-2.5 h-2.5 text-white" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3"><polyline points="20 6 9 17 4 12" /></svg>
)}
</div>
{d.replace('SE-', '')}
</button>
))}
</div>
)}
</div>
{/* Refresh */}
<button
onClick={refresh}

View File

@@ -2,8 +2,6 @@
import React, { createContext, useContext, useState, useMemo, useCallback, ReactNode } from 'react';
import { DashboardData, DashboardConfig, PipelineRecord, AccountRecord, ActivityRecord, TargetRecord } from '@/types/data';
import { startOfWeek, endOfWeek, startOfMonth, endOfMonth, startOfQuarter, endOfQuarter, subDays, parseISO, isWithinInterval } from 'date-fns';
export type DatePreset = 'this_week' | 'last_7' | 'this_month' | 'this_quarter' | 'last_quarter' | 'custom';
interface FilterState {
@@ -36,27 +34,6 @@ interface DataContextValue {
const DataContext = createContext<DataContextValue | null>(null);
function getDateRange(preset: DatePreset, custom?: { start: string; end: string }): { start: Date; end: Date } {
const now = new Date();
switch (preset) {
case 'this_week':
return { start: startOfWeek(now, { weekStartsOn: 1 }), end: endOfWeek(now, { weekStartsOn: 1 }) };
case 'last_7':
return { start: subDays(now, 7), end: now };
case 'this_month':
return { start: startOfMonth(now), end: endOfMonth(now) };
case 'this_quarter':
return { start: startOfQuarter(now), end: endOfQuarter(now) };
case 'last_quarter': {
const lastQ = new Date(now);
lastQ.setMonth(lastQ.getMonth() - 3);
return { start: startOfQuarter(lastQ), end: endOfQuarter(lastQ) };
}
case 'custom':
if (custom) return { start: parseISO(custom.start), end: parseISO(custom.end) };
return { start: subDays(now, 30), end: now };
}
}
export function DataProvider({ children, initialData, config }: { children: ReactNode; initialData: DashboardData; config: DashboardConfig }) {
const [data, setData] = useState<DashboardData>(initialData);
@@ -106,28 +83,13 @@ export function DataProvider({ children, initialData, config }: { children: Reac
}, []);
const filtered = useMemo(() => {
const { districts: distFilter, datePreset, dateRange, crossFilter } = filters;
const range = getDateRange(datePreset, dateRange);
const { crossFilter } = filters;
let pipeline = data.pipeline;
let accounts = data.accounts;
let activities = data.activities;
let targets = data.targets;
if (distFilter.length > 0) {
pipeline = pipeline.filter(p => distFilter.includes(p.District_Name));
accounts = accounts.filter(a => distFilter.includes(a.District_Name));
activities = activities.filter(a => distFilter.includes(a.District_Name));
}
activities = activities.filter(a => {
try {
return isWithinInterval(parseISO(a.Activity_Date), range);
} catch {
return true;
}
});
if (crossFilter) {
const { key, value } = crossFilter;
const match = (obj: object) => String((obj as never)[key as never]) === value;
@@ -137,11 +99,6 @@ export function DataProvider({ children, initialData, config }: { children: Reac
targets = targets.filter(match);
}
if (distFilter.length > 0) {
const accountNames = new Set(accounts.map(a => a.Account_Name));
targets = targets.filter(t => accountNames.has(t.Account_Name));
}
return { pipeline, accounts, activities, targets };
}, [data, filters]);