Revert "Move timezone selection to query parameters"

This reverts commit 4d7930060c.
This commit is contained in:
Francis Cao committed 2026-07-27 10:32:28 -07:00
1 parent 4d7930060c
commit 3136e3590a
19 files changed
+147 -129

No files matched your search

+3 -3
View File
@@ -13,10 +13,10 @@ export function useDateParameters() {
const {
dateRange: { startDate, endDate, unit },
} = useDateRange();
const { timezone, toUtc, canonicalizeTimezone } = useTimezone();
const { timezone, localToUtc, canonicalizeTimezone } = useTimezone();
const utcStart = toUtc(startDate);
const utcEnd = toUtc(endDate);
const utcStart = localToUtc(startDate);
const utcEnd = localToUtc(endDate);
const startAtValue = +utcStart;
const endAtValue = +utcEnd;
const isStartValid = Number.isFinite(startAtValue);
+5 -7
View File
@@ -4,26 +4,24 @@ import { useNavigation } from '@/components/hooks/useNavigation';
import { DATE_RANGE_CONFIG, DEFAULT_DATE_RANGE_VALUE } from '@/lib/constants';
import { getCompareDate, getOffsetDateRange, parseDateRange } from '@/lib/date';
import { getItem } from '@/lib/storage';
import { useTimezone } from './useTimezone';
export function useDateRange(options: { ignoreOffset?: boolean; timezone?: string } = {}) {
const {
query: { date = '', unit = '', offset = 0, compare = 'prev' },
} = useNavigation();
const { locale } = useLocale();
const { timezone: defaultTimezone } = useTimezone();
const timezone = options.timezone ?? defaultTimezone;
const ignoreOffset = options.ignoreOffset;
const dateRange = useMemo(() => {
const dateRangeObject = parseDateRange(
date || getItem(DATE_RANGE_CONFIG) || DEFAULT_DATE_RANGE_VALUE,
unit,
locale,
timezone,
options.timezone,
);
return !ignoreOffset && offset ? getOffsetDateRange(dateRangeObject, +offset) : dateRangeObject;
}, [date, unit, offset, locale, timezone, ignoreOffset]);
return !options.ignoreOffset && offset
? getOffsetDateRange(dateRangeObject, +offset)
: dateRangeObject;
}, [date, unit, offset, options]);
const dateCompare = getCompareDate(compare, dateRange.startDate, dateRange.endDate);
+17 -25
View File
@@ -1,35 +1,22 @@
import { formatInTimeZone, fromZonedTime, toZonedTime } from 'date-fns-tz';
import { useSyncExternalStore } from 'react';
import { TIMEZONE_LEGACY } from '@/lib/constants';
import { getTimezone, isValidTimezone, normalizeTimezone } from '@/lib/date';
import { TIMEZONE_CONFIG, TIMEZONE_LEGACY } from '@/lib/constants';
import { getTimezone } from '@/lib/date';
import { setItem } from '@/lib/storage';
import { setTimezone, useApp } from '@/store/app';
import { useLocale } from './useLocale';
import { useNavigation } from './useNavigation';
function canonicalizeTimezone(timezone: string): string {
return TIMEZONE_LEGACY[timezone] ?? normalizeTimezone(timezone);
}
function subscribe() {
return () => {};
}
function getServerTimezone() {
return 'UTC';
}
const selector = (state: { timezone: string }) => state.timezone;
export function useTimezone() {
const {
query: { timezone: queryTimezone },
} = useNavigation();
const localTimeZone = canonicalizeTimezone(
useSyncExternalStore(subscribe, getTimezone, getServerTimezone),
);
const requestedTimezone =
typeof queryTimezone === 'string' ? canonicalizeTimezone(queryTimezone) : undefined;
const timezone =
requestedTimezone && isValidTimezone(requestedTimezone) ? requestedTimezone : localTimeZone;
const timezone = useApp(selector);
const localTimeZone = getTimezone();
const { dateLocale } = useLocale();
const saveTimezone = (value: string) => {
setItem(TIMEZONE_CONFIG, value);
setTimezone(value);
};
const formatTimezoneDate = (date: string, pattern: string) => {
return formatInTimeZone(
/^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(\.[0-9]{3})?Z$/.test(date)
@@ -89,6 +76,10 @@ export function useTimezone() {
return toZonedTime(date, localTimeZone);
};
const canonicalizeTimezone = (timezone: string): string => {
return TIMEZONE_LEGACY[timezone] ?? timezone;
};
return {
timezone,
localTimeZone,
@@ -96,6 +87,7 @@ export function useTimezone() {
fromUtc,
localToUtc,
localFromUtc,
saveTimezone,
formatTimezoneDate,
formatSeriesTimezone,
canonicalizeTimezone,
+3 -4
View File
@@ -1,7 +1,7 @@
import { Dialog, ListItem, ListSeparator, Modal, Select, type SelectProps } from '@umami/react-zen';
import { Fragment, type Key, useState } from 'react';
import { DateDisplay } from '@/components/common/DateDisplay';
import { useMessages, useMobile, useTimezone } from '@/components/hooks';
import { useMessages, useMobile } from '@/components/hooks';
import { DatePickerForm } from '@/components/metrics/DatePickerForm';
import { getMaxSelectableDate, parseDateRange } from '@/lib/date';
@@ -23,8 +23,7 @@ export function DateFilter({
}: DateFilterProps) {
const { t, labels } = useMessages();
const [showPicker, setShowPicker] = useState(false);
const { timezone, fromUtc } = useTimezone();
const { startDate, endDate } = parseDateRange(value, undefined, undefined, timezone) || {};
const { startDate, endDate } = parseDateRange(value) || {};
const { isMobile } = useMobile();
const options = [
@@ -129,7 +128,7 @@ export function DateFilter({
startDate={startDate}
endDate={endDate}
minDate={new Date(2000, 0, 1)}
maxDate={getMaxSelectableDate(fromUtc(new Date()))}
maxDate={getMaxSelectableDate()}
onChange={handlePickerChange}
onClose={() => setShowPicker(false)}
/>
@@ -1,5 +1,6 @@
import { Button, Column, DialogTrigger, Icon, Label, Popover } from '@umami/react-zen';
import { DateRangeSetting } from '@/app/(main)/settings/preferences/DateRangeSetting';
import { TimezoneSetting } from '@/app/(main)/settings/preferences/TimezoneSetting';
import { Panel } from '@/components/common/Panel';
import { useMessages } from '@/components/hooks';
import { Settings } from '@/components/icons';
@@ -16,6 +17,10 @@ export function PreferencesButton() {
</Button>
<Popover placement="bottom end">
<Panel gap="3">
<Column>
<Label>{t(labels.timezone)}</Label>
<TimezoneSetting />
</Column>
<Column>
<Label>{t(labels.defaultDateRange)}</Label>
<DateRangeSetting />
-62
View File
@@ -1,62 +0,0 @@
import { ListItem, ListSeparator, Select } from '@umami/react-zen';
import { type Key, useState } from 'react';
import { useMessages, useNavigation, useTimezone } from '@/components/hooks';
import { isValidTimezone } from '@/lib/date';
const CURRENT_TIMEZONE = 'current';
const timezones = [
'UTC',
...(Intl.supportedValuesOf?.('timeZone') ?? []).filter(value => value !== 'UTC'),
];
export function TimezoneFilter() {
const [search, setSearch] = useState('');
const { t, labels } = useMessages();
const { router, query, updateParams } = useNavigation();
const { timezone, localTimeZone, canonicalizeTimezone } = useTimezone();
const hasOverride =
typeof query.timezone === 'string' && isValidTimezone(canonicalizeTimezone(query.timezone));
const selectedTimezone = hasOverride ? timezone : CURRENT_TIMEZONE;
const normalizedSearch = search.trim().toLowerCase();
const items = normalizedSearch
? timezones.filter(value => value.toLowerCase().includes(normalizedSearch))
: timezones;
const handleChange = (value: Key) => {
router.push(
updateParams({
timezone: value === CURRENT_TIMEZONE ? undefined : value.toString(),
page: 1,
}),
);
};
const handleOpen = (isOpen: boolean) => {
if (isOpen) {
setSearch('');
}
};
return (
<Select
value={selectedTimezone}
onChange={handleChange}
allowSearch
onSearch={setSearch}
onOpenChange={handleOpen}
maxHeight={480}
style={{ minWidth: 220 }}
>
<ListItem id={CURRENT_TIMEZONE}>
{t(labels.current)} ({localTimeZone})
</ListItem>
<ListSeparator />
{items.map(item => (
<ListItem key={item} id={item}>
{item}
</ListItem>
))}
{!items.length && <ListItem />}
</Select>
);
}
+9 -7
View File
@@ -4,7 +4,6 @@ import { useFilters, useMessages, useNavigation } from '@/components/hooks';
import { ListFilter } from '@/components/icons';
import { DialogButton } from '@/components/input/DialogButton';
import { FilterEditForm } from '@/components/input/FilterEditForm';
import { TimezoneFilter } from '@/components/input/TimezoneFilter';
import {
filtersArrayToObject,
serializeSessionPropertyFilters,
@@ -79,14 +78,17 @@ export function WebsiteFilterButton({
{({ close }) => {
return (
<>
<Row position="absolute" top="30px" right="30px" alignItems="center" gap>
{(isOverview || allowBounceFilter) && (
<Checkbox value={excludeBounce ? 'true' : ''} onChange={setExcludeBounce}>
{(isOverview || allowBounceFilter) && (
<Row position="absolute" top="30px" right="30px">
<Checkbox
value={excludeBounce ? 'true' : ''}
onChange={setExcludeBounce}
style={{ marginTop: '3px' }}
>
{t(labels.excludeBounce)}
</Checkbox>
)}
<TimezoneFilter />
</Row>
</Row>
)}
<FilterEditForm websiteId={websiteId} onChange={handleChange} onClose={close} />
</>
);
+8 -2
View File
@@ -2,7 +2,12 @@ import { colord } from 'colord';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { BarChart, type BarChartProps } from '@/components/charts/BarChart';
import { LoadingPanel } from '@/components/common/LoadingPanel';
import { useDateRange, useLocale, useWebsiteEventsSeriesQuery } from '@/components/hooks';
import {
useDateRange,
useLocale,
useTimezone,
useWebsiteEventsSeriesQuery,
} from '@/components/hooks';
import { renderDateLabels } from '@/lib/charts';
import { hex6 } from '@/lib/colors';
import { CHART_COLORS } from '@/lib/constants';
@@ -15,9 +20,10 @@ export interface EventsChartProps extends BarChartProps {
}
export function EventsChart({ websiteId, focusLabel, limit }: EventsChartProps) {
const { timezone } = useTimezone();
const {
dateRange: { startDate, endDate, unit },
} = useDateRange();
} = useDateRange({ timezone: timezone });
const { locale, dateLocale } = useLocale();
const { data, isLoading, error } = useWebsiteEventsSeriesQuery(websiteId, { limit });
const [label, setLabel] = useState<string>(focusLabel);
@@ -11,6 +11,7 @@ import {
useMessages,
usePropertyArraySeriesQuery,
usePropertySeriesQuery,
useTimezone,
} from '@/components/hooks';
import type { PropertyDataSource } from '@/components/hooks/queries/usePropertyFieldsQuery';
import { ListTable } from '@/components/metrics/ListTable';
@@ -35,9 +36,10 @@ export function PropertyChart({
seriesType?: 'property' | 'array';
}) {
const { t, labels } = useMessages();
const { timezone } = useTimezone();
const {
dateRange: { startDate, endDate, unit },
} = useDateRange();
} = useDateRange({ timezone });
const { locale, dateLocale } = useLocale();
const propertySeriesQuery = usePropertySeriesQuery(
source,
@@ -9,6 +9,7 @@ import {
useMessages,
usePropertyNumericSeriesQuery,
usePropertyNumericStatsQuery,
useTimezone,
} from '@/components/hooks';
import type { PropertyDataSource } from '@/components/hooks/queries/usePropertyFieldsQuery';
import { MetricCard } from '@/components/metrics/MetricCard';
@@ -34,9 +35,10 @@ export function PropertyNumericChart({
}) {
const { t, labels } = useMessages();
const { theme } = useTheme();
const { timezone } = useTimezone();
const {
dateRange: { startDate, endDate, unit },
} = useDateRange();
} = useDateRange({ timezone });
const { locale, dateLocale } = useLocale();
const { colors } = useMemo(() => getThemeColors(theme), [theme]);