diff --git a/apps/studio/components/interfaces/Account/Preferences/TimezoneSettings.tsx b/apps/studio/components/interfaces/Account/Preferences/TimezoneSettings.tsx new file mode 100644 index 00000000000..24ebb78d7ea --- /dev/null +++ b/apps/studio/components/interfaces/Account/Preferences/TimezoneSettings.tsx @@ -0,0 +1,166 @@ +import { useFlag } from 'common' +import { CheckIcon, ChevronsUpDown, Globe } from 'lucide-react' +import { useId, useMemo, useState } from 'react' +import { + Button, + Card, + CardContent, + cn, + Command_Shadcn_, + CommandEmpty_Shadcn_, + CommandGroup_Shadcn_, + CommandInput_Shadcn_, + CommandItem_Shadcn_, + CommandList_Shadcn_, + Popover_Shadcn_, + PopoverContent_Shadcn_, + PopoverTrigger_Shadcn_, + ScrollArea, +} from 'ui' +import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout' +import { + PageSection, + PageSectionContent, + PageSectionDescription, + PageSectionMeta, + PageSectionSummary, + PageSectionTitle, +} from 'ui-patterns/PageSection' + +import { findTimezoneByIana, TIMEZONES_BY_IANA } from '@/lib/constants/timezones' +import { useTimezone } from '@/lib/datetime' +import { guessLocalTimezone } from '@/lib/dayjs' +import { useTrack } from '@/lib/telemetry/track' + +const AUTO_OPTION_VALUE = '__auto__' + +export const TimezoneSettings = () => { + const timezonePickerEnabled = useFlag('timezonePicker') + const { timezone, storedTimezone, setTimezone, isAutoDetected } = useTimezone() + const track = useTrack() + const [open, setOpen] = useState(false) + const listboxId = useId() + + // Browser timezone is captured once and stays stable even when the user has + // overridden the dashboard timezone — that's the value the "Auto detect" + // option will revert to. + const browserTimezone = useMemo(() => guessLocalTimezone(), []) + + const triggerLabel = useMemo(() => findTimezoneByIana(timezone)?.text ?? timezone, [timezone]) + + if (!timezonePickerEnabled) return null + + const handleSelect = (nextStored: string) => { + setTimezone(nextStored) + const resolvedNext = nextStored || guessLocalTimezone() + track('timezone_picker_clicked', { + previousTimezone: timezone, + nextTimezone: resolvedNext, + isAutoDetected: nextStored === '', + source: 'account_preferences', + }) + setOpen(false) + } + + return ( + + + + Timezone + + Choose how dates and times in logs and other dashboard surfaces are displayed. + + + + + + + + + + } + iconRight={} + > + + {isAutoDetected ? `Auto detect (${timezone})` : triggerLabel} + + + + + + + + No timezones found + + + handleSelect('')} + > + + Auto detect + + {browserTimezone} + + + + + {TIMEZONES_BY_IANA.map((entry) => { + const ianaName = entry.utc[0] + const isSelected = !isAutoDetected && storedTimezone === ianaName + return ( + handleSelect(ianaName)} + > + {entry.text} + + + ) + })} + + + + + + + + + + + + ) +} diff --git a/apps/studio/components/interfaces/UserDropdown/TimezoneDropdown.tsx b/apps/studio/components/interfaces/UserDropdown/TimezoneDropdown.tsx index 5f63c6c3c54..f6e7cf989be 100644 --- a/apps/studio/components/interfaces/UserDropdown/TimezoneDropdown.tsx +++ b/apps/studio/components/interfaces/UserDropdown/TimezoneDropdown.tsx @@ -42,6 +42,7 @@ export const TimezoneDropdown = () => { previousTimezone: timezone, nextTimezone: resolvedNext, isAutoDetected: nextStored === '', + source: 'user_dropdown', }) setOpen(false) } diff --git a/apps/studio/pages/account/me.tsx b/apps/studio/pages/account/me.tsx index 9dbe1bbf222..f069bdee82c 100644 --- a/apps/studio/pages/account/me.tsx +++ b/apps/studio/pages/account/me.tsx @@ -25,6 +25,7 @@ import { DashboardSettings } from '@/components/interfaces/Account/Preferences/D import { HotkeySettings } from '@/components/interfaces/Account/Preferences/HotkeySettings' import { ProfileInformation } from '@/components/interfaces/Account/Preferences/ProfileInformation' import { ThemeSettings } from '@/components/interfaces/Account/Preferences/ThemeSettings' +import { TimezoneSettings } from '@/components/interfaces/Account/Preferences/TimezoneSettings' import AccountLayout from '@/components/layouts/AccountLayout/AccountLayout' import { AppLayout } from '@/components/layouts/AppLayout/AppLayout' import { DefaultLayout } from '@/components/layouts/DefaultLayout' @@ -95,6 +96,8 @@ const PlatformPreferences = () => { + + @@ -191,6 +194,8 @@ const SelfHostedPreferences = () => { + + diff --git a/packages/common/telemetry-constants.ts b/packages/common/telemetry-constants.ts index e0da300f829..19b46b65b15 100644 --- a/packages/common/telemetry-constants.ts +++ b/packages/common/telemetry-constants.ts @@ -281,6 +281,8 @@ export interface TimezonePickerClickedEvent { nextTimezone: string /** True when the user opted back into the browser-detected default. */ isAutoDetected: boolean + /** Where the picker was rendered. */ + source: 'user_dropdown' | 'account_preferences' } groups: TelemetryGroups }