import { X } from "lucide-react"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { cn } from "@/lib/utils"; import type { ActivityKind } from "@/types"; /** * URL-friendly relative time filters. The page converts each value into an * ISO timestamp before passing it to `useActivity` so the backend's * lexicographic `timestamp >= since` comparison works against the stored * ISO strings. * * `all` is the implicit default and is rendered as "All time". */ export type SinceValue = "1h" | "24h" | "7d" | "all"; const KIND_OPTIONS: { value: ActivityKind; label: string }[] = [ { value: "claim_submitted", label: "Submitted" }, { value: "claim_accepted", label: "Accepted" }, { value: "claim_paid", label: "Paid" }, { value: "claim_denied", label: "Denied" }, { value: "remit_received", label: "Remit received" }, { value: "provider_added", label: "Provider added" }, ]; const SINCE_OPTIONS: { value: SinceValue; label: string }[] = [ { value: "all", label: "All time" }, { value: "1h", label: "Last 1 hour" }, { value: "24h", label: "Last 24 hours" }, { value: "7d", label: "Last 7 days" }, ]; type ActivityFiltersProps = { /** Currently selected activity kinds (multi-select). Empty array = no filter. */ selectedKinds: ActivityKind[]; /** Toggle a kind in or out of `selectedKinds`. */ onKindsChange: (kinds: ActivityKind[]) => void; /** Active "since" window; `"all"` means no time filter. */ since: SinceValue; /** Update the "since" window. */ onSinceChange: (since: SinceValue) => void; /** Reset both filters (and any other future filters). */ onClear: () => void; }; /** * Filter strip for the Activity Log page. * * - "Kind": a row of toggleable chips (multi-select). Each chip uses * `role="checkbox"` + `aria-checked` so screen readers announce the * toggle state correctly. Selected chips lift to the electric blue * accent, matching the single-select `FilterChips` visual language. * * - "Since": a Radix Select dropdown with the four relative windows. * `SelectValue` renders the current label so the user always sees * what's applied (rather than a bare `1h` slug). * * - "Clear filters": a small X-pill appears once any filter is active. * Mirrors the single-select `FilterChips` "Clear" affordance so the * operator's mental model is consistent across pages. * * The component is purely presentational — it doesn't read or write URL * state itself. `ActivityLog` owns URL sync via `useSearchParams` so the * component is trivially testable in isolation if needed. */ export function ActivityFilters({ selectedKinds, onKindsChange, since, onSinceChange, onClear, }: ActivityFiltersProps) { const hasFilters = selectedKinds.length > 0 || since !== "all"; const toggleKind = (value: ActivityKind) => { if (selectedKinds.includes(value)) { onKindsChange(selectedKinds.filter((k) => k !== value)); } else { onKindsChange([...selectedKinds, value]); } }; return (
Kind {KIND_OPTIONS.map((opt) => { const isActive = selectedKinds.includes(opt.value); return ( ); })}
Since {hasFilters ? ( ) : null}
); }