diff --git a/src/components/logged-in/dashboard/dashboard-filters.tsx b/src/components/logged-in/dashboard/dashboard-filters.tsx
index 168b72c..07056d7 100644
--- a/src/components/logged-in/dashboard/dashboard-filters.tsx
+++ b/src/components/logged-in/dashboard/dashboard-filters.tsx
@@ -1,94 +1,16 @@
'use client'
-import { Button } from '@/components/ui/button'
-import { Calendar } from '@/components/ui/calendar'
-import {
- Popover,
- PopoverContent,
- PopoverTrigger,
-} from '@/components/ui/popover'
-import { cn } from '@/lib/utils'
-import { format } from 'date-fns'
-import { CalendarIcon } from 'lucide-react'
-import { useMemo, useState } from 'react'
-import {
- DATE_PRESETS,
- type DatePreset,
- getPresetLabel,
- useDashboardFilters,
-} from './search-params'
+import { DateRangeFilter } from '@/components/logged-in/shared/date-range-filter'
+import { useDashboardFilters } from './search-params'
export function DashboardFilters() {
const { filters, setFilters } = useDashboardFilters()
- const [isCalendarOpen, setIsCalendarOpen] = useState(false)
-
- const selectedRange = useMemo(
- () => ({
- from: filters.from ? new Date(filters.from) : undefined,
- to: filters.to ? new Date(filters.to) : undefined,
- }),
- [filters.from, filters.to],
- )
-
- const handlePresetClick = (preset: DatePreset) => {
- if (preset === 'custom') {
- setIsCalendarOpen(true)
- return
- }
- setFilters({ preset, from: null, to: null })
- }
-
- const handleDateSelect = (range: { from?: Date; to?: Date } | undefined) => {
- if (range?.from) {
- setFilters({ from: range.from, preset: 'custom' })
- }
- if (range?.to) {
- setFilters({ to: range.to, preset: 'custom' })
- setIsCalendarOpen(false)
- }
- }
return (
-
- {DATE_PRESETS.filter((p) => p !== 'custom').map((preset) => (
-
- ))}
-
-
-
-
-
-
-
-
-
-
+
)
}
diff --git a/src/components/logged-in/dashboard/search-params.ts b/src/components/logged-in/dashboard/search-params.ts
index 5ad9bbf..73e336d 100644
--- a/src/components/logged-in/dashboard/search-params.ts
+++ b/src/components/logged-in/dashboard/search-params.ts
@@ -1,68 +1,84 @@
+import {
+ DATE_PRESETS,
+ type DatePreset,
+ getActivePreset,
+ getDateRangeFromPreset,
+} from '@/components/logged-in/shared/date-range-filter'
import { parseAsLocalDate } from '@/lib/utils'
-import { startOfYear, subDays, subMonths } from 'date-fns'
-import { parseAsStringLiteral, useQueryStates } from 'nuqs'
+import { useQueryStates } from 'nuqs'
import { useCallback, useEffect, useRef } from 'react'
-export const DATE_PRESETS = ['7d', '30d', '3m', '6m', 'ytd', 'custom'] as const
-export type DatePreset = (typeof DATE_PRESETS)[number]
+export {
+ DATE_PRESETS,
+ type DatePreset,
+ getActivePreset,
+ getDateRangeFromPreset,
+}
const STORAGE_KEY = 'dashboard-date-preset'
-const DEFAULT_PRESET: DatePreset = '30d'
+const DEFAULT_PRESET: Exclude = '30d'
-function getStoredPreset(): DatePreset {
+function getStoredPreset(): Exclude {
if (typeof window === 'undefined') return DEFAULT_PRESET
try {
const stored = localStorage.getItem(STORAGE_KEY)
if (!stored) return DEFAULT_PRESET
- if (DATE_PRESETS.includes(stored as DatePreset)) {
- return stored as DatePreset
+ const validPresets = DATE_PRESETS.filter((p) => p !== 'custom')
+ if (validPresets.includes(stored as Exclude)) {
+ return stored as Exclude
}
} catch {
- // localStorage unavailable or blocked, fall through to default
+ // localStorage unavailable or blocked
}
return DEFAULT_PRESET
}
-function setStoredPreset(preset: DatePreset): void {
+function setStoredPreset(preset: Exclude): void {
if (typeof window === 'undefined') return
try {
localStorage.setItem(STORAGE_KEY, preset)
} catch {
- // localStorage unavailable or blocked, silently fail
+ // localStorage unavailable or blocked
}
}
export function useDashboardFilters() {
- const initialPreset = getStoredPreset()
- const storedPresetRef = useRef(initialPreset)
+ const initializedRef = useRef(false)
const [filters, setQueryFilters] = useQueryStates({
- preset: parseAsStringLiteral(DATE_PRESETS).withDefault(initialPreset),
from: parseAsLocalDate,
to: parseAsLocalDate,
})
- // Sync external URL changes (browser navigation) to localStorage
+ // Initialize from localStorage on first render if URL has no dates
useEffect(() => {
- if (filters.preset !== storedPresetRef.current) {
- setStoredPreset(filters.preset)
- storedPresetRef.current = filters.preset
+ if (initializedRef.current) return
+ initializedRef.current = true
+
+ if (!filters.from && !filters.to) {
+ const storedPreset = getStoredPreset()
+ const range = getDateRangeFromPreset(storedPreset)
+ setQueryFilters({ from: range.from, to: range.to })
}
- }, [filters.preset])
+ }, [filters.from, filters.to, setQueryFilters])
+
+ // Persist active preset to localStorage when dates change
+ const prevPresetRef = useRef(null)
+ useEffect(() => {
+ if (!filters.from || !filters.to) return
+
+ const activePreset = getActivePreset(filters.from, filters.to)
+ if (activePreset !== 'custom' && activePreset !== prevPresetRef.current) {
+ setStoredPreset(activePreset)
+ prevPresetRef.current = activePreset
+ }
+ }, [filters.from, filters.to])
const setFilters = useCallback(
- (updates: {
- preset?: DatePreset
- from?: Date | null
- to?: Date | null
- }) => {
- if (updates.preset !== undefined) {
- setStoredPreset(updates.preset)
- storedPresetRef.current = updates.preset
- }
+ (updates: { from: Date; to: Date }) => {
return setQueryFilters(updates)
},
[setQueryFilters],
@@ -71,49 +87,6 @@ export function useDashboardFilters() {
return { filters, setFilters }
}
-export function getDateRangeFromPreset(
- preset: DatePreset,
- customFrom?: Date | null,
- customTo?: Date | null,
-): { from: Date; to: Date } {
- const now = new Date()
-
- switch (preset) {
- case '7d':
- return { from: subDays(now, 7), to: now }
- case '30d':
- return { from: subDays(now, 30), to: now }
- case '3m':
- return { from: subMonths(now, 3), to: now }
- case '6m':
- return { from: subMonths(now, 6), to: now }
- case 'ytd':
- return { from: startOfYear(now), to: now }
- case 'custom':
- return {
- from: customFrom ?? subDays(now, 30),
- to: customTo ?? now,
- }
- }
-}
-
-export function getPresetLabel(preset: DatePreset): string {
- switch (preset) {
- case '7d':
- return 'Last 7 days'
- case '30d':
- return 'Last 30 days'
- case '3m':
- return 'Last 3 months'
- case '6m':
- return 'Last 6 months'
- case 'ytd':
- return 'Year to date'
- case 'custom':
- return 'Custom'
- }
-}
-
export function getGroupByFromPreset(
preset: DatePreset,
): 'day' | 'week' | 'month' {
diff --git a/src/components/logged-in/dashboard/spending-by-category.tsx b/src/components/logged-in/dashboard/spending-by-category.tsx
index 55f94d2..40f90d4 100644
--- a/src/components/logged-in/dashboard/spending-by-category.tsx
+++ b/src/components/logged-in/dashboard/spending-by-category.tsx
@@ -12,7 +12,7 @@ import { trpc } from '@/lib/trpc/client'
import { formatCurrency } from '@/lib/utils'
import { format } from 'date-fns'
import { Bar, BarChart, Cell, XAxis, YAxis } from 'recharts'
-import { getDateRangeFromPreset, useDashboardFilters } from './search-params'
+import { useDashboardFilters } from './search-params'
const CHART_COLORS = [
'var(--chart-1)',
@@ -31,17 +31,15 @@ const CHART_COLORS = [
export function SpendingByCategory() {
const { filters } = useDashboardFilters()
- const { from, to } = getDateRangeFromPreset(
- filters.preset,
- filters.from,
- filters.to,
- )
- const { data, isLoading } = trpc.dashboard.getSpendingByCategory.useQuery({
- from: format(from, 'yyyy-MM-dd'),
- to: format(to, 'yyyy-MM-dd'),
- limit: 8,
- })
+ const { data, isLoading } = trpc.dashboard.getSpendingByCategory.useQuery(
+ {
+ from: filters.from ? format(filters.from, 'yyyy-MM-dd') : '',
+ to: filters.to ? format(filters.to, 'yyyy-MM-dd') : '',
+ limit: 8,
+ },
+ { enabled: !!filters.from && !!filters.to },
+ )
if (isLoading) {
return (
diff --git a/src/components/logged-in/dashboard/spending-over-time.tsx b/src/components/logged-in/dashboard/spending-over-time.tsx
index 1a54dcb..5116cfe 100644
--- a/src/components/logged-in/dashboard/spending-over-time.tsx
+++ b/src/components/logged-in/dashboard/spending-over-time.tsx
@@ -13,7 +13,7 @@ import { formatCurrency } from '@/lib/utils'
import { format, parseISO } from 'date-fns'
import { Area, AreaChart, CartesianGrid, XAxis, YAxis } from 'recharts'
import {
- getDateRangeFromPreset,
+ getActivePreset,
getGroupByFromPreset,
useDashboardFilters,
} from './search-params'
@@ -27,18 +27,17 @@ const chartConfig = {
export function SpendingOverTime() {
const { filters } = useDashboardFilters()
- const { from, to } = getDateRangeFromPreset(
- filters.preset,
- filters.from,
- filters.to,
- )
- const groupBy = getGroupByFromPreset(filters.preset)
+ const activePreset = getActivePreset(filters.from, filters.to)
+ const groupBy = getGroupByFromPreset(activePreset)
- const { data, isLoading } = trpc.dashboard.getSpendingOverTime.useQuery({
- from: format(from, 'yyyy-MM-dd'),
- to: format(to, 'yyyy-MM-dd'),
- groupBy,
- })
+ const { data, isLoading } = trpc.dashboard.getSpendingOverTime.useQuery(
+ {
+ from: filters.from ? format(filters.from, 'yyyy-MM-dd') : '',
+ to: filters.to ? format(filters.to, 'yyyy-MM-dd') : '',
+ groupBy,
+ },
+ { enabled: !!filters.from && !!filters.to },
+ )
if (isLoading) {
return (
diff --git a/src/components/logged-in/dashboard/spending-summary-cards.tsx b/src/components/logged-in/dashboard/spending-summary-cards.tsx
index d25f5c5..179a8e5 100644
--- a/src/components/logged-in/dashboard/spending-summary-cards.tsx
+++ b/src/components/logged-in/dashboard/spending-summary-cards.tsx
@@ -11,7 +11,7 @@ import {
TagIcon,
TrendingUpIcon,
} from 'lucide-react'
-import { getDateRangeFromPreset, useDashboardFilters } from './search-params'
+import { useDashboardFilters } from './search-params'
function SummaryCardSkeleton() {
return (
@@ -30,17 +30,15 @@ function SummaryCardSkeleton() {
export function SpendingSummaryCards() {
const { filters } = useDashboardFilters()
- const { from, to } = getDateRangeFromPreset(
- filters.preset,
- filters.from,
- filters.to,
- )
const { data: summary, isLoading } =
- trpc.dashboard.getSpendingSummary.useQuery({
- from: format(from, 'yyyy-MM-dd'),
- to: format(to, 'yyyy-MM-dd'),
- })
+ trpc.dashboard.getSpendingSummary.useQuery(
+ {
+ from: filters.from ? format(filters.from, 'yyyy-MM-dd') : '',
+ to: filters.to ? format(filters.to, 'yyyy-MM-dd') : '',
+ },
+ { enabled: !!filters.from && !!filters.to },
+ )
if (isLoading) {
return (
diff --git a/src/components/logged-in/dashboard/top-merchants.tsx b/src/components/logged-in/dashboard/top-merchants.tsx
index 600b909..5a343b5 100644
--- a/src/components/logged-in/dashboard/top-merchants.tsx
+++ b/src/components/logged-in/dashboard/top-merchants.tsx
@@ -4,7 +4,7 @@ import { Skeleton } from '@/components/ui/skeleton'
import { trpc } from '@/lib/trpc/client'
import { formatCurrency } from '@/lib/utils'
import { format } from 'date-fns'
-import { getDateRangeFromPreset, useDashboardFilters } from './search-params'
+import { useDashboardFilters } from './search-params'
function MerchantSkeleton() {
return (
@@ -20,17 +20,15 @@ function MerchantSkeleton() {
export function TopMerchants() {
const { filters } = useDashboardFilters()
- const { from, to } = getDateRangeFromPreset(
- filters.preset,
- filters.from,
- filters.to,
- )
- const { data, isLoading } = trpc.dashboard.getTopMerchants.useQuery({
- from: format(from, 'yyyy-MM-dd'),
- to: format(to, 'yyyy-MM-dd'),
- limit: 5,
- })
+ const { data, isLoading } = trpc.dashboard.getTopMerchants.useQuery(
+ {
+ from: filters.from ? format(filters.from, 'yyyy-MM-dd') : '',
+ to: filters.to ? format(filters.to, 'yyyy-MM-dd') : '',
+ limit: 5,
+ },
+ { enabled: !!filters.from && !!filters.to },
+ )
return (
diff --git a/src/components/logged-in/dashboard/transactions-to-review.tsx b/src/components/logged-in/dashboard/transactions-to-review.tsx
index 8083004..8276c09 100644
--- a/src/components/logged-in/dashboard/transactions-to-review.tsx
+++ b/src/components/logged-in/dashboard/transactions-to-review.tsx
@@ -7,7 +7,7 @@ import { formatCurrency } from '@/lib/utils'
import { format, parseISO } from 'date-fns'
import { CheckCircleIcon } from 'lucide-react'
import Link from 'next/link'
-import { getDateRangeFromPreset, useDashboardFilters } from './search-params'
+import { useDashboardFilters } from './search-params'
function TransactionSkeleton() {
return (
@@ -23,17 +23,15 @@ function TransactionSkeleton() {
export function TransactionsToReview() {
const { filters } = useDashboardFilters()
- const { from, to } = getDateRangeFromPreset(
- filters.preset,
- filters.from,
- filters.to,
- )
- const { data, isLoading } = trpc.dashboard.getTransactionsToReview.useQuery({
- from: format(from, 'yyyy-MM-dd'),
- to: format(to, 'yyyy-MM-dd'),
- limit: 5,
- })
+ const { data, isLoading } = trpc.dashboard.getTransactionsToReview.useQuery(
+ {
+ from: filters.from ? format(filters.from, 'yyyy-MM-dd') : '',
+ to: filters.to ? format(filters.to, 'yyyy-MM-dd') : '',
+ limit: 5,
+ },
+ { enabled: !!filters.from && !!filters.to },
+ )
return (
diff --git a/src/components/logged-in/shared/date-range-filter.tsx b/src/components/logged-in/shared/date-range-filter.tsx
new file mode 100644
index 0000000..4d98766
--- /dev/null
+++ b/src/components/logged-in/shared/date-range-filter.tsx
@@ -0,0 +1,157 @@
+'use client'
+
+import { Button } from '@/components/ui/button'
+import { Calendar } from '@/components/ui/calendar'
+import {
+ Popover,
+ PopoverContent,
+ PopoverTrigger,
+} from '@/components/ui/popover'
+import { format, isSameDay, startOfYear, subDays, subMonths } from 'date-fns'
+import { CalendarIcon } from 'lucide-react'
+import { useMemo, useState } from 'react'
+import type { DateRange } from 'react-day-picker'
+
+export const DATE_PRESETS = ['7d', '30d', '3m', '6m', 'ytd', 'custom'] as const
+export type DatePreset = (typeof DATE_PRESETS)[number]
+
+export function getPresetLabel(preset: DatePreset): string {
+ switch (preset) {
+ case '7d':
+ return 'Last 7 days'
+ case '30d':
+ return 'Last 30 days'
+ case '3m':
+ return 'Last 3 months'
+ case '6m':
+ return 'Last 6 months'
+ case 'ytd':
+ return 'Year to date'
+ case 'custom':
+ return 'Custom'
+ }
+}
+
+export function getDateRangeFromPreset(preset: Exclude): {
+ from: Date
+ to: Date
+} {
+ const now = new Date()
+
+ switch (preset) {
+ case '7d':
+ return { from: subDays(now, 7), to: now }
+ case '30d':
+ return { from: subDays(now, 30), to: now }
+ case '3m':
+ return { from: subMonths(now, 3), to: now }
+ case '6m':
+ return { from: subMonths(now, 6), to: now }
+ case 'ytd':
+ return { from: startOfYear(now), to: now }
+ }
+}
+
+export function getActivePreset(
+ from: Date | null,
+ to: Date | null,
+): DatePreset {
+ if (!from || !to) return '30d'
+
+ const presets = ['7d', '30d', '3m', '6m', 'ytd'] as const
+ for (const preset of presets) {
+ const range = getDateRangeFromPreset(preset)
+ if (isSameDay(from, range.from) && isSameDay(to, range.to)) {
+ return preset
+ }
+ }
+
+ return 'custom'
+}
+
+interface DateRangeFilterProps {
+ from: Date | null
+ to: Date | null
+ onFilterChange: (updates: { from: Date; to: Date }) => void
+}
+
+export function DateRangeFilter({
+ from,
+ to,
+ onFilterChange,
+}: DateRangeFilterProps) {
+ const [isCalendarOpen, setIsCalendarOpen] = useState(false)
+ const [localRange, setLocalRange] = useState()
+ const [clickCount, setClickCount] = useState(0)
+
+ const activePreset = useMemo(() => getActivePreset(from, to), [from, to])
+
+ const handleOpenChange = (open: boolean) => {
+ setIsCalendarOpen(open)
+ if (open) {
+ setClickCount(0)
+ setLocalRange(
+ from
+ ? { from: new Date(from), to: to ? new Date(to) : undefined }
+ : undefined,
+ )
+ }
+ }
+
+ const handlePresetClick = (preset: Exclude) => {
+ const range = getDateRangeFromPreset(preset)
+ onFilterChange(range)
+ }
+
+ const handleDateSelect = (range: DateRange | undefined) => {
+ if (!range?.from) return
+ const newClickCount = clickCount + 1
+ setClickCount(newClickCount)
+ setLocalRange(range)
+ if (newClickCount >= 2 && range.to && !isSameDay(range.from, range.to)) {
+ onFilterChange({ from: range.from, to: range.to })
+ setIsCalendarOpen(false)
+ }
+ }
+
+ return (
+
+ {DATE_PRESETS.filter((p) => p !== 'custom').map((presetOption) => (
+
+ ))}
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/src/components/logged-in/transactions/filters/date-range-filter.tsx b/src/components/logged-in/transactions/filters/date-range-filter.tsx
index c3f097b..3d8decd 100644
--- a/src/components/logged-in/transactions/filters/date-range-filter.tsx
+++ b/src/components/logged-in/transactions/filters/date-range-filter.tsx
@@ -1,116 +1,23 @@
'use client'
-import { Button } from '@/components/ui/button'
-import { Calendar } from '@/components/ui/calendar'
-import { Kbd } from '@/components/ui/kbd'
-import {
- Popover,
- PopoverContent,
- PopoverTrigger,
-} from '@/components/ui/popover'
-import {
- Tooltip,
- TooltipContent,
- TooltipTrigger,
-} from '@/components/ui/tooltip'
+import { DateRangeFilter as SharedDateRangeFilter } from '@/components/logged-in/shared/date-range-filter'
import { usePagination } from '@/hooks/use-pagination'
-import { cn } from '@/lib/utils'
-import { format } from 'date-fns'
-import { CalendarIcon, XIcon } from 'lucide-react'
-import { useState } from 'react'
-import { useHotkeys } from 'react-hotkeys-hook'
import { useTransactionsFilters } from './search-params'
-const DATE_RANGE_SHORTCUT = 'D'
-
export function DateRangeFilter() {
const { transactionFilters, setTransactionFilters } = useTransactionsFilters()
const { setPagination } = usePagination('transactions')
- const [isOpen, setIsOpen] = useState(false)
-
- const hasValues = !!transactionFilters.from && !!transactionFilters.to
- const handleClear = (e: React.MouseEvent) => {
- e.stopPropagation() // Prevent popover from opening
- setTransactionFilters({
- from: null,
- to: null,
- })
+ const handleFilterChange = (updates: { from: Date; to: Date }) => {
+ setTransactionFilters(updates)
setPagination({ page: 1 })
}
- useHotkeys(DATE_RANGE_SHORTCUT, (e) => {
- e.preventDefault()
- setIsOpen(!isOpen)
- })
-
return (
-
-
-
-
-
-
-
-
- {
- if (value?.from) {
- setTransactionFilters({
- from: value.from,
- })
- }
- if (value?.to) {
- setTransactionFilters({
- to: value.to,
- })
- }
- setPagination({ page: 1 })
- }}
- captionLayout="dropdown"
- />
-
-
-
- Or press {DATE_RANGE_SHORTCUT} to pick a
- date range
-
-
+
)
}
diff --git a/src/components/logged-in/transactions/filters/index.tsx b/src/components/logged-in/transactions/filters/index.tsx
index 6231ade..f5a41b8 100644
--- a/src/components/logged-in/transactions/filters/index.tsx
+++ b/src/components/logged-in/transactions/filters/index.tsx
@@ -11,9 +11,9 @@ import { UploadFilter } from './upload-filter'
export function TransactionsFilters() {
return (
<>
-
+
+
-
diff --git a/src/components/logged-in/transactions/filters/search-params.ts b/src/components/logged-in/transactions/filters/search-params.ts
index df1318e..e910d2f 100644
--- a/src/components/logged-in/transactions/filters/search-params.ts
+++ b/src/components/logged-in/transactions/filters/search-params.ts
@@ -1,8 +1,47 @@
+import {
+ DATE_PRESETS,
+ type DatePreset,
+ getActivePreset,
+ getDateRangeFromPreset,
+} from '@/components/logged-in/shared/date-range-filter'
import { parseAsLocalDate } from '@/lib/utils'
import { parseAsArrayOf, parseAsString, useQueryStates } from 'nuqs'
+import { useCallback, useEffect, useRef } from 'react'
+
+const STORAGE_KEY = 'transactions-date-preset'
+const DEFAULT_PRESET: Exclude
= '30d'
+
+function getStoredPreset(): Exclude {
+ if (typeof window === 'undefined') return DEFAULT_PRESET
+
+ try {
+ const stored = localStorage.getItem(STORAGE_KEY)
+ if (!stored) return DEFAULT_PRESET
+
+ const validPresets = DATE_PRESETS.filter((p) => p !== 'custom')
+ if (validPresets.includes(stored as Exclude)) {
+ return stored as Exclude
+ }
+ } catch {
+ // localStorage unavailable or blocked
+ }
+
+ return DEFAULT_PRESET
+}
+
+function setStoredPreset(preset: Exclude): void {
+ if (typeof window === 'undefined') return
+ try {
+ localStorage.setItem(STORAGE_KEY, preset)
+ } catch {
+ // localStorage unavailable or blocked
+ }
+}
export function useTransactionsFilters() {
- const [transactionFilters, setTransactionFilters] = useQueryStates({
+ const initializedRef = useRef(false)
+
+ const [transactionFilters, setQueryFilters] = useQueryStates({
category: parseAsString.withDefault('all'),
from: parseAsLocalDate,
to: parseAsLocalDate,
@@ -11,6 +50,49 @@ export function useTransactionsFilters() {
uploadId: parseAsString.withDefault('all'),
})
+ // Initialize from localStorage on first render if URL has no dates
+ useEffect(() => {
+ if (initializedRef.current) return
+ initializedRef.current = true
+
+ if (!transactionFilters.from && !transactionFilters.to) {
+ const storedPreset = getStoredPreset()
+ const range = getDateRangeFromPreset(storedPreset)
+ setQueryFilters({ from: range.from, to: range.to })
+ }
+ }, [transactionFilters.from, transactionFilters.to, setQueryFilters])
+
+ // Persist active preset to localStorage when dates change
+ const prevPresetRef = useRef(null)
+ useEffect(() => {
+ if (!transactionFilters.from || !transactionFilters.to) return
+
+ const activePreset = getActivePreset(
+ transactionFilters.from,
+ transactionFilters.to,
+ )
+ if (activePreset !== 'custom' && activePreset !== prevPresetRef.current) {
+ setStoredPreset(activePreset)
+ prevPresetRef.current = activePreset
+ }
+ }, [transactionFilters.from, transactionFilters.to])
+
+ const setTransactionFilters = useCallback(
+ (
+ updates: Partial<{
+ category: string | null
+ from: Date | null
+ to: Date | null
+ query: string | null
+ ids: string[] | null
+ uploadId: string | null
+ }>,
+ ) => {
+ return setQueryFilters(updates)
+ },
+ [setQueryFilters],
+ )
+
return {
transactionFilters,
setTransactionFilters,