/* * Copyright (C) 2025 Red Hat, Inc. * SPDX-License-Identifier: LGPL-2.1-or-later */ import cockpit from "cockpit"; import { DateTime } from "luxon"; import React, { useContext, useEffect, useState } from "react"; import { Checkbox } from "@patternfly/react-core/dist/esm/components/Checkbox/index.js"; import { FormGroup } from "@patternfly/react-core/dist/esm/components/Form/index.js"; import { MenuToggle } from "@patternfly/react-core/dist/esm/components/MenuToggle/index.js"; import { Select, SelectList, SelectOption } from "@patternfly/react-core/dist/esm/components/Select/index.js"; import { Title } from "@patternfly/react-core/dist/esm/components/Title/index.js"; import { Flex, FlexItem } from "@patternfly/react-core/dist/esm/layouts/Flex/index.js"; import { Stack, StackItem } from "@patternfly/react-core/dist/esm/layouts/Stack/index.js"; import { setTimezone, } from "../../apis/timezone.js"; import { TimezoneContext, } from "../../contexts/Common.jsx"; const _ = cockpit.gettext; const SCREEN_ID = "anaconda-screen-date-time"; export const TimezoneSection = ({ locale, setSectionValid, setTimezoneLabel }) => { const [autoTimezone, setAutoTimezone] = useState(true); const [regionSelectOpen, setRegionSelectOpen] = useState(false); const [citySelectOpen, setCitySelectOpen] = useState(false); const [regions, setRegions] = useState([]); const [citiesByRegion, setCitiesByRegion] = useState({}); const [region, setRegion] = useState(""); const [city, setCity] = useState(""); const [shownTimezoneLabel, setShownTimezoneLabel] = useState(""); const timezoneData = useContext(TimezoneContext); const timezone = timezoneData?.timezone; const allValidTimezones = timezoneData?.allValidTimezones; useEffect(() => { const keys = Object.keys(allValidTimezones || {}); setRegions(keys); setCitiesByRegion(allValidTimezones || {}); if (keys.length > 0 && !timezone) { setRegion(keys[0]); setCity(allValidTimezones[keys[0]][0]); } }, [timezone, allValidTimezones]); useEffect(() => { if (timezone && typeof timezone === "string" && timezone.includes("/")) { const [reg, ...cty] = timezone.split("/"); setRegion(reg); setCity(cty.join("/")); } }, [timezone]); useEffect(() => { setSectionValid(autoTimezone || (!!region && !!city)); }, [autoTimezone, region, city, setSectionValid]); useEffect(() => { if (region && city) { const zone = `${region}/${city}`; const dt = DateTime.now().setZone(zone); const formatter = new Intl.DateTimeFormat(locale, { hour: "2-digit", minute: "2-digit", timeZone: zone, timeZoneName: "short" }); const parts = formatter.formatToParts(new Date()); const tzPart = parts.find(p => p.type === "timeZoneName"); const abbreviation = tzPart?.value; const offsetHours = dt.offset / 60; const offsetStr = `UTC${offsetHours >= 0 ? "+" : ""}${offsetHours}`; const label = (abbreviation && !abbreviation.includes("/")) ? `${abbreviation}, ${offsetStr}` : offsetStr; setTimezoneLabel(zone); setShownTimezoneLabel(label); } }, [region, city, setTimezoneLabel, locale]); const handleRegionSelect = (_ev, value) => { setRegion(value); setCity(citiesByRegion[value][0]); setRegionSelectOpen(false); setTimezone({ timezone: `${value}/${citiesByRegion[value][0]}` }); }; const handleCitySelect = (_ev, value) => { setCity(value); setCitySelectOpen(false); setTimezone({ timezone: `${region}/${value}` }); }; const regionToggle = toggleRef => ( setRegionSelectOpen(!regionSelectOpen)} isExpanded={regionSelectOpen} isDisabled={autoTimezone} className={`${SCREEN_ID}__region-toggle`} > {region} ); const cityToggle = toggleRef => ( setCitySelectOpen(!citySelectOpen)} isExpanded={citySelectOpen} isDisabled={autoTimezone} className={`${SCREEN_ID}__city-toggle`} > {city} ); return ( <> {_("Timezone")} setAutoTimezone(!autoTimezone)} label={_("Automatically set timezone")} aria-label={_("Automatically set timezone")} /> {shownTimezoneLabel} ); };