Skip to content

Commit 5016750

Browse files
authored
refactor: centralize date filtering with useDateFilters hook (#87)
1 parent b74bed9 commit 5016750

File tree

10 files changed

+96
-180
lines changed

10 files changed

+96
-180
lines changed

apps/dashboard/app/(main)/websites/[id]/_components/analytics-toolbar.tsx

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,12 @@
33
import { type DynamicQueryFilter, filterOptions } from '@databuddy/shared';
44
import { ArrowClockwiseIcon, XIcon } from '@phosphor-icons/react';
55
import dayjs from 'dayjs';
6-
import { useAtom } from 'jotai';
76
import { useCallback, useMemo } from 'react';
87
import type { DateRange as DayPickerRange } from 'react-day-picker';
98
import { DateRangePicker } from '@/components/date-range-picker';
109
import { Button } from '@/components/ui/button';
10+
import { useDateFilters } from '@/hooks/use-date-filters';
1111
import { operatorOptions, useFilters } from '@/hooks/use-filters';
12-
import {
13-
dateRangeAtom,
14-
setDateRangeAndAdjustGranularityAtom,
15-
timeGranularityAtom,
16-
} from '@/stores/jotai/filterAtoms';
1712
import { AddFilterForm, getOperatorShorthand } from './utils/add-filters';
1813

1914
interface AnalyticsToolbarProps {
@@ -29,16 +24,18 @@ export function AnalyticsToolbar({
2924
selectedFilters,
3025
onFiltersChange,
3126
}: AnalyticsToolbarProps) {
32-
const [currentDateRange] = useAtom(dateRangeAtom);
33-
const [currentGranularity, setCurrentGranularityAtomState] =
34-
useAtom(timeGranularityAtom);
35-
const [, setDateRangeAction] = useAtom(setDateRangeAndAdjustGranularityAtom);
36-
3727
const { addFilter, removeFilter } = useFilters({
3828
filters: selectedFilters,
3929
onFiltersChange,
4030
});
4131

32+
const {
33+
currentDateRange,
34+
currentGranularity,
35+
setCurrentGranularityAtomState,
36+
setDateRangeAction,
37+
} = useDateFilters();
38+
4239
const dayPickerSelectedRange: DayPickerRange | undefined = useMemo(
4340
() => ({
4441
from: currentDateRange.startDate,

apps/dashboard/app/(main)/websites/[id]/errors/_components/errors-page-content.tsx

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,16 @@
11
'use client';
22

3-
import type { DateRange, DynamicQueryFilter } from '@databuddy/shared';
3+
import type { DynamicQueryFilter } from '@databuddy/shared';
44
import { ArrowClockwiseIcon, BugIcon } from '@phosphor-icons/react';
55
import { useAtom } from 'jotai';
66
import { use, useCallback, useEffect, useMemo, useState } from 'react';
77
import { toast } from 'sonner';
88
import { AnimatedLoading } from '@/components/analytics/animated-loading';
99
import { Button } from '@/components/ui/button';
1010
import { Card, CardContent } from '@/components/ui/card';
11+
import { useDateFilters } from '@/hooks/use-date-filters';
1112
import { useEnhancedErrorData } from '@/hooks/use-dynamic-query';
12-
import {
13-
formattedDateRangeAtom,
14-
isAnalyticsRefreshingAtom,
15-
timeGranularityAtom,
16-
} from '@/stores/jotai/filterAtoms';
13+
import { isAnalyticsRefreshingAtom } from '@/stores/jotai/filterAtoms';
1714
import { WebsitePageHeader } from '../../_components/website-page-header';
1815
import { ErrorDataTable } from './error-data-table';
1916
// Import our separated components
@@ -32,18 +29,10 @@ export const ErrorsPageContent = ({ params }: ErrorsPageContentProps) => {
3229
const websiteId = resolvedParams.id;
3330

3431
// Use shared date range and refresh state
35-
const [formattedDateRangeState] = useAtom(formattedDateRangeAtom);
36-
const [currentGranularity] = useAtom(timeGranularityAtom);
3732
const [isRefreshing, setIsRefreshing] = useAtom(isAnalyticsRefreshingAtom);
3833

39-
const dateRange: DateRange = useMemo(
40-
() => ({
41-
start_date: formattedDateRangeState.startDate,
42-
end_date: formattedDateRangeState.endDate,
43-
granularity: currentGranularity,
44-
}),
45-
[formattedDateRangeState, currentGranularity]
46-
);
34+
const { dateRange } = useDateFilters();
35+
4736
const [loadingProgress, setLoadingProgress] = useState<number>(0);
4837

4938
// Filters state
@@ -314,7 +303,7 @@ export const ErrorsPageContent = ({ params }: ErrorsPageContentProps) => {
314303
}
315304

316305
return (
317-
<div className="mx-auto max-w-[1600px] space-y-6 mt-6">
306+
<div className="mx-auto mt-6 max-w-[1600px] space-y-6">
318307
<WebsitePageHeader
319308
description="Monitor and analyze application errors to improve user experience"
320309
icon={

apps/dashboard/app/(main)/websites/[id]/funnels/page.tsx

Lines changed: 8 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
'use client';
22

33
import { FunnelIcon, TrendDownIcon } from '@phosphor-icons/react';
4-
54
import { useAtom } from 'jotai';
65
import { useParams } from 'next/navigation';
7-
import { lazy, Suspense, useCallback, useMemo, useRef, useState } from 'react';
6+
import { lazy, Suspense, useCallback, useRef, useState } from 'react';
87
import { Card, CardContent } from '@/components/ui/card';
8+
import { useDateFilters } from '@/hooks/use-date-filters';
99
import {
1010
type CreateFunnelData,
1111
type Funnel,
@@ -15,11 +15,7 @@ import {
1515
useFunnels,
1616
} from '@/hooks/use-funnels';
1717
import { useTrackingSetup } from '@/hooks/use-tracking-setup';
18-
import {
19-
formattedDateRangeAtom,
20-
isAnalyticsRefreshingAtom,
21-
timeGranularityAtom,
22-
} from '@/stores/jotai/filterAtoms';
18+
import { isAnalyticsRefreshingAtom } from '@/stores/jotai/filterAtoms';
2319
import { WebsitePageHeader } from '../_components/website-page-header';
2420

2521
// Removed PageHeader import - using shared WebsitePageHeader
@@ -96,22 +92,9 @@ export default function FunnelsPage() {
9692
// Intersection observer for lazy loading
9793
const pageRef = useRef<HTMLDivElement>(null);
9894

99-
// Date range state
100-
const [currentGranularity] = useAtom(timeGranularityAtom);
101-
const [formattedDateRangeState] = useAtom(formattedDateRangeAtom);
102-
10395
const { isTrackingSetup, refetchTrackingSetup } = useTrackingSetup(websiteId);
10496

105-
106-
107-
const memoizedDateRangeForTabs = useMemo(
108-
() => ({
109-
start_date: formattedDateRangeState.startDate,
110-
end_date: formattedDateRangeState.endDate,
111-
granularity: currentGranularity,
112-
}),
113-
[formattedDateRangeState, currentGranularity]
114-
);
97+
const { formattedDateRangeState, dateRange } = useDateFilters();
11598

11699
const {
117100
data: funnels,
@@ -130,14 +113,9 @@ export default function FunnelsPage() {
130113
isLoading: analyticsLoading,
131114
error: analyticsError,
132115
refetch: refetchAnalytics,
133-
} = useFunnelAnalytics(
134-
websiteId,
135-
expandedFunnelId || '',
136-
memoizedDateRangeForTabs,
137-
{
138-
enabled: !!expandedFunnelId,
139-
}
140-
);
116+
} = useFunnelAnalytics(websiteId, expandedFunnelId || '', dateRange, {
117+
enabled: !!expandedFunnelId,
118+
});
141119

142120
const {
143121
data: referrerAnalyticsData,
@@ -256,10 +234,7 @@ export default function FunnelsPage() {
256234
}
257235

258236
return (
259-
<div
260-
className="mx-auto max-w-[1600px] space-y-4 mt-6"
261-
ref={pageRef}
262-
>
237+
<div className="mx-auto mt-6 max-w-[1600px] space-y-4" ref={pageRef}>
263238
<WebsitePageHeader
264239
createActionLabel="Create Funnel"
265240
description="Track user journeys and optimize conversion drop-off points"
@@ -287,8 +262,6 @@ export default function FunnelsPage() {
287262
websiteId={websiteId}
288263
/>
289264

290-
291-
292265
<Suspense fallback={<FunnelsListSkeleton />}>
293266
<FunnelsList
294267
expandedFunnelId={expandedFunnelId}

apps/dashboard/app/(main)/websites/[id]/goals/page.tsx

Lines changed: 5 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,16 @@ import {
1212
useState,
1313
} from 'react';
1414
import { Card, CardContent } from '@/components/ui/card';
15+
import { useDateFilters } from '@/hooks/use-date-filters';
1516
import { useAutocompleteData } from '@/hooks/use-funnels';
1617
import {
1718
type CreateGoalData,
1819
type Goal,
1920
useBulkGoalAnalytics,
2021
useGoals,
2122
} from '@/hooks/use-goals';
22-
import { useTrackingSetup } from '@/hooks/use-tracking-setup';
2323
import { useWebsite } from '@/hooks/use-websites';
24-
import {
25-
formattedDateRangeAtom,
26-
isAnalyticsRefreshingAtom,
27-
timeGranularityAtom,
28-
} from '@/stores/jotai/filterAtoms';
24+
import { isAnalyticsRefreshingAtom } from '@/stores/jotai/filterAtoms';
2925
import { WebsitePageHeader } from '../_components/website-page-header';
3026
import { DeleteGoalDialog } from './_components/delete-goal-dialog';
3127
import { EditGoalDialog } from './_components/edit-goal-dialog';
@@ -95,17 +91,7 @@ export default function GoalsPage() {
9591
return () => observer.disconnect();
9692
}, []);
9793

98-
const [currentGranularity] = useAtom(timeGranularityAtom);
99-
const [formattedDateRangeState] = useAtom(formattedDateRangeAtom);
100-
101-
const memoizedDateRangeForTabs = useMemo(
102-
() => ({
103-
start_date: formattedDateRangeState.startDate,
104-
end_date: formattedDateRangeState.endDate,
105-
granularity: currentGranularity,
106-
}),
107-
[formattedDateRangeState, currentGranularity]
108-
);
94+
const { dateRange } = useDateFilters();
10995

11096
const { data: websiteData } = useWebsite(websiteId);
11197

@@ -127,7 +113,7 @@ export default function GoalsPage() {
127113
data: goalAnalytics,
128114
isLoading: analyticsLoading,
129115
refetch: refetchAnalytics,
130-
} = useBulkGoalAnalytics(websiteId, goalIds, memoizedDateRangeForTabs);
116+
} = useBulkGoalAnalytics(websiteId, goalIds, dateRange);
131117

132118
const autocompleteQuery = useAutocompleteData(websiteId);
133119

@@ -210,10 +196,7 @@ export default function GoalsPage() {
210196
}
211197

212198
return (
213-
<div
214-
className="mx-auto max-w-[1600px] space-y-4 mt-6"
215-
ref={pageRef}
216-
>
199+
<div className="mx-auto max-w-[1600px] space-y-4 mt-6" ref={pageRef}>
217200
<WebsitePageHeader
218201
createActionLabel="Create Goal"
219202
description="Track key conversions and measure success"
@@ -242,8 +225,6 @@ export default function GoalsPage() {
242225
websiteName={websiteData?.name || undefined}
243226
/>
244227

245-
246-
247228
{isVisible && (
248229
<Suspense fallback={<GoalsListSkeleton />}>
249230
<GoalsList

apps/dashboard/app/(main)/websites/[id]/map/page.tsx

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,12 @@ import { Badge } from '@/components/ui/badge';
1111
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
1212
import { Skeleton } from '@/components/ui/skeleton';
1313
import { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs';
14+
import { useDateFilters } from '@/hooks/use-date-filters';
1415
import { useMapLocationData } from '@/hooks/use-dynamic-query';
1516
import { cn } from '@/lib/utils';
1617
import {
1718
dynamicQueryFiltersAtom,
18-
formattedDateRangeAtom,
1919
isAnalyticsRefreshingAtom,
20-
timeGranularityAtom,
2120
} from '@/stores/jotai/filterAtoms';
2221
import { WebsitePageHeader } from '../_components/website-page-header';
2322

@@ -46,21 +45,10 @@ function WebsiteMapPage() {
4645
const [mode, setMode] = useState<'total' | 'perCapita'>('total');
4746
const [selectedCountry, setSelectedCountry] = useState<string | null>(null);
4847

49-
// Use shared state atoms for consistency
50-
const [formattedDateRangeState] = useAtom(formattedDateRangeAtom);
51-
const [currentGranularity] = useAtom(timeGranularityAtom);
48+
const { dateRange } = useDateFilters();
5249
const [filters] = useAtom(dynamicQueryFiltersAtom);
5350
const [isRefreshing, setIsRefreshing] = useAtom(isAnalyticsRefreshingAtom);
5451

55-
const dateRange = useMemo(
56-
() => ({
57-
start_date: formattedDateRangeState.startDate,
58-
end_date: formattedDateRangeState.endDate,
59-
granularity: currentGranularity,
60-
}),
61-
[formattedDateRangeState, currentGranularity]
62-
);
63-
6452
const handleModeChange = useCallback((value: string) => {
6553
setMode(value as 'total' | 'perCapita');
6654
}, []);
@@ -69,8 +57,11 @@ function WebsiteMapPage() {
6957
setSelectedCountry(countryCode);
7058
}, []);
7159

72-
73-
const { isLoading, getDataForQuery } = useMapLocationData(id, dateRange, filters);
60+
const { isLoading, getDataForQuery } = useMapLocationData(
61+
id,
62+
dateRange,
63+
filters
64+
);
7465

7566
const countriesFromQuery = getDataForQuery('map-countries', 'country');
7667
const regionsFromQuery = getDataForQuery('map-regions', 'region');

apps/dashboard/app/(main)/websites/[id]/page.tsx

Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,24 @@
11
'use client';
22

3+
import type { DynamicQueryFilter } from '@databuddy/shared';
34
import { WarningIcon } from '@phosphor-icons/react';
45
import { useAtom } from 'jotai';
56
import dynamic from 'next/dynamic';
67
import Link from 'next/link';
78
import { useParams } from 'next/navigation';
89
import { useQueryState } from 'nuqs';
9-
import { Suspense, useCallback, useEffect, useMemo } from 'react';
10+
import { Suspense, useCallback, useEffect } from 'react';
1011
import { Button } from '@/components/ui/button';
1112
import { Skeleton } from '@/components/ui/skeleton';
1213
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
14+
import { useDateFilters } from '@/hooks/use-date-filters';
1315
import { useTrackingSetup } from '@/hooks/use-tracking-setup';
1416
import { useWebsite } from '@/hooks/use-websites';
1517
import {
1618
addDynamicFilterAtom,
1719
dynamicQueryFiltersAtom,
18-
formattedDateRangeAtom,
1920
isAnalyticsRefreshingAtom,
20-
timeGranularityAtom,
21-
timezoneAtom,
2221
} from '@/stores/jotai/filterAtoms';
23-
24-
import type { DynamicQueryFilter } from '@databuddy/shared';
2522
import type {
2623
FullTabProps,
2724
WebsiteDataTabProps,
@@ -82,22 +79,11 @@ function WebsiteDetailsPage() {
8279
defaultValue: 'overview' as TabId,
8380
});
8481
const { id } = useParams();
85-
const [currentGranularity] = useAtom(timeGranularityAtom);
86-
const [formattedDateRangeState] = useAtom(formattedDateRangeAtom);
87-
const [timezone] = useAtom(timezoneAtom);
8882
const [isRefreshing, setIsRefreshing] = useAtom(isAnalyticsRefreshingAtom);
8983
const [selectedFilters] = useAtom(dynamicQueryFiltersAtom);
9084
const [, addFilterAction] = useAtom(addDynamicFilterAtom);
9185

92-
const memoizedDateRangeForTabs = useMemo(
93-
() => ({
94-
start_date: formattedDateRangeState.startDate,
95-
end_date: formattedDateRangeState.endDate,
96-
granularity: currentGranularity,
97-
timezone,
98-
}),
99-
[formattedDateRangeState, currentGranularity, timezone]
100-
);
86+
const { dateRange } = useDateFilters();
10187

10288
const {
10389
data,
@@ -132,7 +118,7 @@ function WebsiteDetailsPage() {
132118
const key = `${tabId}-${id as string}`;
133119
const settingsProps: WebsiteDataTabProps = {
134120
websiteId: id as string,
135-
dateRange: memoizedDateRangeForTabs,
121+
dateRange,
136122
websiteData: data,
137123
onWebsiteUpdated: refetchWebsiteData,
138124
};
@@ -171,7 +157,7 @@ function WebsiteDetailsPage() {
171157
[
172158
activeTab,
173159
id,
174-
memoizedDateRangeForTabs,
160+
dateRange,
175161
data,
176162
isRefreshing,
177163
setIsRefreshing,

0 commit comments

Comments
 (0)