From b36cd48b4ac4245d4a014be3b57c036cd2755e5a Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Fri, 3 Oct 2025 01:19:35 -0700 Subject: [PATCH] Separated query string date handling. Fixed all time date. --- .../settings/preferences/DateRangeSetting.tsx | 17 +++-- .../websites/[websiteId]/WebsiteChart.tsx | 2 +- src/components/hooks/index.ts | 2 + .../hooks/queries/useDateRangeQuery.ts | 12 +++ src/components/hooks/useDateRange.ts | 76 ++++++------------- src/components/hooks/useQueryStringDate.ts | 24 ++++++ src/components/input/WebsiteDateFilter.tsx | 19 +++-- 7 files changed, 86 insertions(+), 66 deletions(-) create mode 100644 src/components/hooks/queries/useDateRangeQuery.ts create mode 100644 src/components/hooks/useQueryStringDate.ts diff --git a/src/app/(main)/settings/preferences/DateRangeSetting.tsx b/src/app/(main)/settings/preferences/DateRangeSetting.tsx index 2b81985f..e47ce0b6 100644 --- a/src/app/(main)/settings/preferences/DateRangeSetting.tsx +++ b/src/app/(main)/settings/preferences/DateRangeSetting.tsx @@ -1,21 +1,24 @@ +import { useState } from 'react'; import { DateFilter } from '@/components/input/DateFilter'; import { Button, Row } from '@umami/react-zen'; -import { useDateRange, useMessages } from '@/components/hooks'; -import { DEFAULT_DATE_RANGE_VALUE } from '@/lib/constants'; +import { useMessages } from '@/components/hooks'; +import { DATE_RANGE_CONFIG, DEFAULT_DATE_RANGE_VALUE } from '@/lib/constants'; +import { setItem, getItem } from '@/lib/storage'; export function DateRangeSetting() { const { formatMessage, labels } = useMessages(); - const { dateRange, saveDateRange } = useDateRange(); - const { value } = dateRange; + const [date, setDate] = useState(getItem(DATE_RANGE_CONFIG) || DEFAULT_DATE_RANGE_VALUE); const handleChange = (value: string) => { - saveDateRange(value); + setItem(DATE_RANGE_CONFIG, value); + setDate(value); }; - const handleReset = () => saveDateRange(DEFAULT_DATE_RANGE_VALUE); + + const handleReset = () => setItem(DATE_RANGE_CONFIG, DEFAULT_DATE_RANGE_VALUE); return ( - + ); diff --git a/src/app/(main)/websites/[websiteId]/WebsiteChart.tsx b/src/app/(main)/websites/[websiteId]/WebsiteChart.tsx index b72f00a0..67412d5c 100644 --- a/src/app/(main)/websites/[websiteId]/WebsiteChart.tsx +++ b/src/app/(main)/websites/[websiteId]/WebsiteChart.tsx @@ -15,7 +15,7 @@ export function WebsiteChart({ const { startDate, endDate, unit, value } = dateRange; const { data, isLoading, isFetching, error } = useWebsitePageviewsQuery({ websiteId, - compare: compareMode ? dateCompare : undefined, + compare: compareMode ? dateCompare?.['value'] : undefined, }); const { pageviews, sessions, compare } = (data || {}) as any; diff --git a/src/components/hooks/index.ts b/src/components/hooks/index.ts index 6af16999..2731e268 100644 --- a/src/components/hooks/index.ts +++ b/src/components/hooks/index.ts @@ -9,6 +9,7 @@ export * from './context/useWebsite'; // Query hooks export * from './queries/useActiveUsersQuery'; +export * from './queries/useDateRangeQuery'; export * from './queries/useDeleteQuery'; export * from './queries/useEventDataEventsQuery'; export * from './queries/useEventDataPropertiesQuery'; @@ -76,6 +77,7 @@ export * from './useModified'; export * from './useNavigation'; export * from './usePagedQuery'; export * from './usePageParameters'; +export * from './useQueryStringDate'; export * from './useRegionNames'; export * from './useSlug'; export * from './useSticky'; diff --git a/src/components/hooks/queries/useDateRangeQuery.ts b/src/components/hooks/queries/useDateRangeQuery.ts new file mode 100644 index 00000000..4af2011c --- /dev/null +++ b/src/components/hooks/queries/useDateRangeQuery.ts @@ -0,0 +1,12 @@ +import { useApi } from '../useApi'; +import { ReactQueryOptions } from '@/lib/types'; + +export function useDateRangeQuery(websiteId: string, options?: ReactQueryOptions) { + const { get, useQuery } = useApi(); + return useQuery({ + queryKey: ['date-range', websiteId], + queryFn: () => get(`/websites/${websiteId}/daterange`), + enabled: !!websiteId, + ...options, + }); +} diff --git a/src/components/hooks/useDateRange.ts b/src/components/hooks/useDateRange.ts index 0a82d5a3..dc81cfd7 100644 --- a/src/components/hooks/useDateRange.ts +++ b/src/components/hooks/useDateRange.ts @@ -1,65 +1,35 @@ -import { useMemo } from 'react'; -import { getMinimumUnit, parseDateRange, getOffsetDateRange } from '@/lib/date'; -import { setItem } from '@/lib/storage'; -import { DATE_RANGE_CONFIG, DEFAULT_DATE_COMPARE, DEFAULT_DATE_RANGE_VALUE } from '@/lib/constants'; -import { setWebsiteDateCompare, setWebsiteDateRange, useWebsites } from '@/store/websites'; -import { setDateRangeValue, useApp } from '@/store/app'; -import { useLocale } from './useLocale'; -import { useApi } from './useApi'; -import { useNavigation } from './useNavigation'; +import { getMinimumUnit, parseDateRange } from '@/lib/date'; +import { useLocale } from '@/components/hooks/useLocale'; +import { useApi } from '@/components/hooks//useApi'; +import { useQueryStringDate } from '@/components/hooks/useQueryStringDate'; +import { useGlobalState } from '@/components/hooks/useGlobalState'; -export interface UseDateRangeOptions { - ignoreOffset?: boolean; -} - -export function useDateRange(websiteId?: string, options: UseDateRangeOptions = {}) { +export function useDateRange(websiteId: string) { const { get } = useApi(); const { locale } = useLocale(); - const { - query: { date, offset = 0 }, - } = useNavigation(); - const websiteConfig = useWebsites(state => state[websiteId]?.dateRange); - const globalConfig = useApp(state => state.dateRangeValue); - const dateValue = websiteConfig?.value || date || globalConfig || DEFAULT_DATE_RANGE_VALUE; + const { dateRange: defaultDateRange, dateCompare } = useQueryStringDate(); - const dateRange = useMemo(() => { - const dateRangeObject = parseDateRange(dateValue, locale); + const [dateRange, setDateRange] = useGlobalState(`date-range:${websiteId}`, defaultDateRange); - return !options.ignoreOffset && offset - ? getOffsetDateRange(dateRangeObject, +offset) - : dateRangeObject; - }, [date, offset, dateValue, options]); + const setDateRangeValue = async (value: string) => { + if (value === 'all') { + const result = await get(`/websites/${websiteId}/daterange`); + const { mindate, maxdate } = result; - const dateCompare = useWebsites(state => state[websiteId]?.dateCompare || DEFAULT_DATE_COMPARE); + const startDate = new Date(mindate); + const endDate = new Date(maxdate); + const unit = getMinimumUnit(startDate, endDate); - const saveDateRange = async (value: string) => { - if (websiteId) { - if (value === 'all') { - const result: any = await get(`/websites/${websiteId}/daterange`); - const { mindate, maxdate } = result; - - const startDate = new Date(mindate); - const endDate = new Date(maxdate); - const unit = getMinimumUnit(startDate, endDate); - - setWebsiteDateRange(websiteId, { - startDate, - endDate, - unit, - value, - }); - } else { - setWebsiteDateRange(websiteId, parseDateRange(value, locale)); - } + setDateRange({ + startDate, + endDate, + unit, + value, + }); } else { - setItem(DATE_RANGE_CONFIG, value); - setDateRangeValue(value); + setDateRange(parseDateRange(value, locale)); } }; - const saveDateCompare = (value: string) => { - setWebsiteDateCompare(websiteId, value); - }; - - return { dateRange, saveDateRange, dateCompare, saveDateCompare }; + return { dateRange, dateCompare, setDateRange, setDateRangeValue }; } diff --git a/src/components/hooks/useQueryStringDate.ts b/src/components/hooks/useQueryStringDate.ts new file mode 100644 index 00000000..3b6431a1 --- /dev/null +++ b/src/components/hooks/useQueryStringDate.ts @@ -0,0 +1,24 @@ +import { useNavigation } from '@/components/hooks/useNavigation'; +import { useMemo } from 'react'; +import { getCompareDate, getOffsetDateRange, parseDateRange } from '@/lib/date'; +import { useLocale } from '@/components/hooks/useLocale'; +import { DEFAULT_DATE_RANGE_VALUE } from '@/lib/constants'; + +export function useQueryStringDate(options: { ignoreOffset?: boolean } = {}) { + const { + query: { date = DEFAULT_DATE_RANGE_VALUE, offset = 0, compare = 'prev' }, + } = useNavigation(); + const { locale } = useLocale(); + + const dateRange = useMemo(() => { + const dateRangeObject = parseDateRange(date, locale); + + return !options.ignoreOffset && offset + ? getOffsetDateRange(dateRangeObject, +offset) + : dateRangeObject; + }, [date, offset, options]); + + const dateCompare = getCompareDate(compare, dateRange.startDate, dateRange.endDate); + + return { date, offset, dateRange, dateCompare }; +} diff --git a/src/components/input/WebsiteDateFilter.tsx b/src/components/input/WebsiteDateFilter.tsx index 1045de21..469da419 100644 --- a/src/components/input/WebsiteDateFilter.tsx +++ b/src/components/input/WebsiteDateFilter.tsx @@ -3,6 +3,8 @@ import { isAfter } from 'date-fns'; import { ChevronRight } from '@/components/icons'; import { useDateRange, useMessages, useNavigation } from '@/components/hooks'; import { DateFilter } from './DateFilter'; +import { getOffsetDateRange } from '@/lib/date'; +import { useCallback } from 'react'; export interface WebsiteDateFilterProps { websiteId: string; @@ -18,7 +20,7 @@ export function WebsiteDateFilter({ showButtons = true, allowCompare, }: WebsiteDateFilterProps) { - const { dateRange, saveDateRange } = useDateRange(websiteId); + const { dateRange, setDateRange, setDateRangeValue } = useDateRange(websiteId); const { value, endDate } = dateRange; const { formatMessage, labels } = useMessages(); const { @@ -27,18 +29,25 @@ export function WebsiteDateFilter({ query: { compare = 'prev', offset = 0 }, } = useNavigation(); const isAllTime = value === 'all'; + const isCustomRange = value.startsWith('range'); const disableForward = value === 'all' || isAfter(endDate, new Date()); const handleChange = (date: string) => { - saveDateRange(date); + setDateRangeValue(date); router.push(updateParams({ date, offset: undefined })); }; - const handleIncrement = (increment: number) => { - router.push(updateParams({ offset: +offset + increment })); - }; + const handleIncrement = useCallback( + (increment: number) => { + const offsetDate = getOffsetDateRange(dateRange, +offset + increment); + + setDateRange(offsetDate); + router.push(updateParams({ offset: +offset + increment })); + }, + [offset], + ); const handleSelect = (compare: any) => { router.push(updateParams({ compare }));