Separated query string date handling. Fixed all time date.

This commit is contained in:
Mike Cao 2025-10-03 01:19:35 -07:00
parent 1def80ba42
commit b36cd48b4a
7 changed files with 86 additions and 66 deletions

View file

@ -1,21 +1,24 @@
import { useState } from 'react';
import { DateFilter } from '@/components/input/DateFilter'; import { DateFilter } from '@/components/input/DateFilter';
import { Button, Row } from '@umami/react-zen'; import { Button, Row } from '@umami/react-zen';
import { useDateRange, useMessages } from '@/components/hooks'; import { useMessages } from '@/components/hooks';
import { DEFAULT_DATE_RANGE_VALUE } from '@/lib/constants'; import { DATE_RANGE_CONFIG, DEFAULT_DATE_RANGE_VALUE } from '@/lib/constants';
import { setItem, getItem } from '@/lib/storage';
export function DateRangeSetting() { export function DateRangeSetting() {
const { formatMessage, labels } = useMessages(); const { formatMessage, labels } = useMessages();
const { dateRange, saveDateRange } = useDateRange(); const [date, setDate] = useState(getItem(DATE_RANGE_CONFIG) || DEFAULT_DATE_RANGE_VALUE);
const { value } = dateRange;
const handleChange = (value: string) => { 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 ( return (
<Row gap="3"> <Row gap="3">
<DateFilter value={value} onChange={handleChange} placement="bottom start" /> <DateFilter value={date} onChange={handleChange} placement="bottom start" />
<Button onPress={handleReset}>{formatMessage(labels.reset)}</Button> <Button onPress={handleReset}>{formatMessage(labels.reset)}</Button>
</Row> </Row>
); );

View file

@ -15,7 +15,7 @@ export function WebsiteChart({
const { startDate, endDate, unit, value } = dateRange; const { startDate, endDate, unit, value } = dateRange;
const { data, isLoading, isFetching, error } = useWebsitePageviewsQuery({ const { data, isLoading, isFetching, error } = useWebsitePageviewsQuery({
websiteId, websiteId,
compare: compareMode ? dateCompare : undefined, compare: compareMode ? dateCompare?.['value'] : undefined,
}); });
const { pageviews, sessions, compare } = (data || {}) as any; const { pageviews, sessions, compare } = (data || {}) as any;

View file

@ -9,6 +9,7 @@ export * from './context/useWebsite';
// Query hooks // Query hooks
export * from './queries/useActiveUsersQuery'; export * from './queries/useActiveUsersQuery';
export * from './queries/useDateRangeQuery';
export * from './queries/useDeleteQuery'; export * from './queries/useDeleteQuery';
export * from './queries/useEventDataEventsQuery'; export * from './queries/useEventDataEventsQuery';
export * from './queries/useEventDataPropertiesQuery'; export * from './queries/useEventDataPropertiesQuery';
@ -76,6 +77,7 @@ export * from './useModified';
export * from './useNavigation'; export * from './useNavigation';
export * from './usePagedQuery'; export * from './usePagedQuery';
export * from './usePageParameters'; export * from './usePageParameters';
export * from './useQueryStringDate';
export * from './useRegionNames'; export * from './useRegionNames';
export * from './useSlug'; export * from './useSlug';
export * from './useSticky'; export * from './useSticky';

View file

@ -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<any>({
queryKey: ['date-range', websiteId],
queryFn: () => get(`/websites/${websiteId}/daterange`),
enabled: !!websiteId,
...options,
});
}

View file

@ -1,65 +1,35 @@
import { useMemo } from 'react'; import { getMinimumUnit, parseDateRange } from '@/lib/date';
import { getMinimumUnit, parseDateRange, getOffsetDateRange } from '@/lib/date'; import { useLocale } from '@/components/hooks/useLocale';
import { setItem } from '@/lib/storage'; import { useApi } from '@/components/hooks//useApi';
import { DATE_RANGE_CONFIG, DEFAULT_DATE_COMPARE, DEFAULT_DATE_RANGE_VALUE } from '@/lib/constants'; import { useQueryStringDate } from '@/components/hooks/useQueryStringDate';
import { setWebsiteDateCompare, setWebsiteDateRange, useWebsites } from '@/store/websites'; import { useGlobalState } from '@/components/hooks/useGlobalState';
import { setDateRangeValue, useApp } from '@/store/app';
import { useLocale } from './useLocale';
import { useApi } from './useApi';
import { useNavigation } from './useNavigation';
export interface UseDateRangeOptions { export function useDateRange(websiteId: string) {
ignoreOffset?: boolean;
}
export function useDateRange(websiteId?: string, options: UseDateRangeOptions = {}) {
const { get } = useApi(); const { get } = useApi();
const { locale } = useLocale(); const { locale } = useLocale();
const { const { dateRange: defaultDateRange, dateCompare } = useQueryStringDate();
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 = useMemo(() => { const [dateRange, setDateRange] = useGlobalState(`date-range:${websiteId}`, defaultDateRange);
const dateRangeObject = parseDateRange(dateValue, locale);
return !options.ignoreOffset && offset const setDateRangeValue = async (value: string) => {
? getOffsetDateRange(dateRangeObject, +offset) if (value === 'all') {
: dateRangeObject; const result = await get(`/websites/${websiteId}/daterange`);
}, [date, offset, dateValue, options]); 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) => { setDateRange({
if (websiteId) { startDate,
if (value === 'all') { endDate,
const result: any = await get(`/websites/${websiteId}/daterange`); unit,
const { mindate, maxdate } = result; value,
});
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));
}
} else { } else {
setItem(DATE_RANGE_CONFIG, value); setDateRange(parseDateRange(value, locale));
setDateRangeValue(value);
} }
}; };
const saveDateCompare = (value: string) => { return { dateRange, dateCompare, setDateRange, setDateRangeValue };
setWebsiteDateCompare(websiteId, value);
};
return { dateRange, saveDateRange, dateCompare, saveDateCompare };
} }

View file

@ -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 };
}

View file

@ -3,6 +3,8 @@ import { isAfter } from 'date-fns';
import { ChevronRight } from '@/components/icons'; import { ChevronRight } from '@/components/icons';
import { useDateRange, useMessages, useNavigation } from '@/components/hooks'; import { useDateRange, useMessages, useNavigation } from '@/components/hooks';
import { DateFilter } from './DateFilter'; import { DateFilter } from './DateFilter';
import { getOffsetDateRange } from '@/lib/date';
import { useCallback } from 'react';
export interface WebsiteDateFilterProps { export interface WebsiteDateFilterProps {
websiteId: string; websiteId: string;
@ -18,7 +20,7 @@ export function WebsiteDateFilter({
showButtons = true, showButtons = true,
allowCompare, allowCompare,
}: WebsiteDateFilterProps) { }: WebsiteDateFilterProps) {
const { dateRange, saveDateRange } = useDateRange(websiteId); const { dateRange, setDateRange, setDateRangeValue } = useDateRange(websiteId);
const { value, endDate } = dateRange; const { value, endDate } = dateRange;
const { formatMessage, labels } = useMessages(); const { formatMessage, labels } = useMessages();
const { const {
@ -27,18 +29,25 @@ export function WebsiteDateFilter({
query: { compare = 'prev', offset = 0 }, query: { compare = 'prev', offset = 0 },
} = useNavigation(); } = useNavigation();
const isAllTime = value === 'all'; const isAllTime = value === 'all';
const isCustomRange = value.startsWith('range'); const isCustomRange = value.startsWith('range');
const disableForward = value === 'all' || isAfter(endDate, new Date()); const disableForward = value === 'all' || isAfter(endDate, new Date());
const handleChange = (date: string) => { const handleChange = (date: string) => {
saveDateRange(date); setDateRangeValue(date);
router.push(updateParams({ date, offset: undefined })); router.push(updateParams({ date, offset: undefined }));
}; };
const handleIncrement = (increment: number) => { const handleIncrement = useCallback(
router.push(updateParams({ offset: +offset + increment })); (increment: number) => {
}; const offsetDate = getOffsetDateRange(dateRange, +offset + increment);
setDateRange(offsetDate);
router.push(updateParams({ offset: +offset + increment }));
},
[offset],
);
const handleSelect = (compare: any) => { const handleSelect = (compare: any) => {
router.push(updateParams({ compare })); router.push(updateParams({ compare }));