Use timezone dropdown for company form
continuous-integration/drone/push Build is passing

This commit is contained in:
AmirReza Jamali
2026-07-04 13:52:43 +03:30
parent 3933efeddb
commit 98fac2c867
@@ -79,6 +79,161 @@ const currencyItems: ILabelValue[] = [
{ label: "BRL", value: "BRL" },
];
const allTimezones: Record<string, string> = {
"Pacific/Midway": "Midway Island, Samoa",
"Pacific/Honolulu": "Hawaii",
"America/Juneau": "Alaska",
"America/Boise": "Mountain Time",
"America/Dawson": "Dawson, Yukon",
"America/Chihuahua": "Chihuahua, La Paz, Mazatlan",
"America/Phoenix": "Arizona",
"America/Los_Angeles": "Pacific Time",
"America/Chicago": "Central Time",
"America/Regina": "Saskatchewan",
"America/Mexico_City": "Guadalajara, Mexico City, Monterrey",
"America/Belize": "Central America",
"America/Detroit": "Eastern Time",
"America/Bogota": "Bogota, Lima, Quito",
"America/Caracas": "Caracas, La Paz",
"America/Santiago": "Santiago",
"America/St_Johns": "Newfoundland and Labrador",
"America/Sao_Paulo": "Brasilia",
"America/Tijuana": "Tijuana",
"America/Montevideo": "Montevideo",
"America/Argentina/Buenos_Aires": "Buenos Aires, Georgetown",
"America/Godthab": "Greenland",
"Atlantic/Azores": "Azores",
"Atlantic/Cape_Verde": "Cape Verde Islands",
GMT: "UTC",
"Europe/London": "Edinburgh, London",
"Europe/Dublin": "Dublin",
"Europe/Lisbon": "Lisbon",
"Africa/Casablanca": "Casablanca, Monrovia",
"Atlantic/Canary": "Canary Islands",
"Europe/Belgrade": "Belgrade, Bratislava, Budapest, Ljubljana, Prague",
"Europe/Sarajevo": "Sarajevo, Skopje, Warsaw, Zagreb",
"Europe/Brussels": "Brussels, Copenhagen, Madrid, Paris",
"Europe/Amsterdam": "Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna",
"Africa/Algiers": "West Central Africa",
"Europe/Bucharest": "Bucharest",
"Africa/Cairo": "Cairo",
"Europe/Helsinki": "Helsinki, Kyiv, Riga, Sofia, Tallinn, Vilnius",
"Europe/Athens": "Athens",
"Asia/Jerusalem": "Jerusalem",
"Africa/Harare": "Harare, Pretoria",
"Europe/Moscow": "Istanbul, Minsk, Moscow, St. Petersburg, Volgograd",
"Asia/Kuwait": "Kuwait, Riyadh",
"Africa/Nairobi": "Nairobi",
"Asia/Baghdad": "Baghdad",
"Asia/Tehran": "Tehran",
"Asia/Dubai": "Abu Dhabi, Muscat",
"Asia/Baku": "Baku, Tbilisi, Yerevan",
"Asia/Kabul": "Kabul",
"Asia/Yekaterinburg": "Ekaterinburg",
"Asia/Karachi": "Islamabad, Karachi, Tashkent",
"Asia/Kolkata": "Chennai, Kolkata, Mumbai, New Delhi",
"Asia/Kathmandu": "Kathmandu",
"Asia/Dhaka": "Astana, Dhaka",
"Asia/Colombo": "Sri Jayawardenepura",
"Asia/Almaty": "Almaty, Novosibirsk",
"Asia/Rangoon": "Yangon Rangoon",
"Asia/Bangkok": "Bangkok, Hanoi, Jakarta",
"Asia/Krasnoyarsk": "Krasnoyarsk",
"Asia/Shanghai": "Beijing, Chongqing, Hong Kong SAR, Urumqi",
"Asia/Kuala_Lumpur": "Kuala Lumpur, Singapore",
"Asia/Taipei": "Taipei",
"Australia/Perth": "Perth",
"Asia/Irkutsk": "Irkutsk, Ulaanbaatar",
"Asia/Seoul": "Seoul",
"Asia/Tokyo": "Osaka, Sapporo, Tokyo",
"Asia/Yakutsk": "Yakutsk",
"Australia/Darwin": "Darwin",
"Australia/Adelaide": "Adelaide",
"Australia/Sydney": "Canberra, Melbourne, Sydney",
"Australia/Brisbane": "Brisbane",
"Australia/Hobart": "Hobart",
"Asia/Vladivostok": "Vladivostok",
"Pacific/Guam": "Guam, Port Moresby",
"Asia/Magadan": "Magadan, Solomon Islands, New Caledonia",
"Asia/Kamchatka": "Kamchatka, Marshall Islands",
"Pacific/Fiji": "Fiji Islands",
"Pacific/Auckland": "Auckland, Wellington",
"Pacific/Tongatapu": "Nuku'alofa",
};
const getTimezoneOffsetMinutes = (timezone: string, date = new Date()) => {
try {
const formatter = new Intl.DateTimeFormat("en", {
timeZone: timezone,
timeZoneName: "shortOffset",
});
const offset =
formatter
.formatToParts(date)
.find((part) => part.type === "timeZoneName")?.value ?? "";
const label = offset.replace(/^GMT/, "") || "+0";
const match = label.match(/^([+-])(\d{1,2})(?::(\d{2}))?$/);
if (!match) return 0;
const sign = match[1] === "-" ? -1 : 1;
const hours = Number(match[2]);
const minutes = Number(match[3] ?? 0);
return sign * (hours * 60 + minutes);
} catch {
return 0;
}
};
const formatTimezoneOffset = (offsetMinutes: number) => {
const sign = offsetMinutes < 0 ? "-" : "+";
const absoluteMinutes = Math.abs(offsetMinutes);
const hours = Math.trunc(absoluteMinutes / 60);
const minutes = absoluteMinutes % 60;
return `${sign}${hours}:${minutes.toString().padStart(2, "0")}`;
};
const hasTimezoneDst = (timezone: string) => {
const januaryOffset = getTimezoneOffsetMinutes(
timezone,
new Date(new Date().getFullYear(), 0, 1),
);
const julyOffset = getTimezoneOffsetMinutes(
timezone,
new Date(new Date().getFullYear(), 6, 1),
);
return januaryOffset !== julyOffset;
};
const timezoneItems: ILabelValue[] = Object.entries(allTimezones)
.map(([timezone, name]) => {
const offsetMinutes = getTimezoneOffsetMinutes(timezone);
return {
timezone,
name,
hasDst: hasTimezoneDst(timezone),
offsetMinutes,
};
})
.sort((a, b) => a.offsetMinutes - b.offsetMinutes)
.filter((item, index, items) => {
return (
items.findIndex(
(timezone) =>
timezone.offsetMinutes === item.offsetMinutes &&
timezone.hasDst === item.hasDst,
) === index
);
})
.map(({ timezone, name, offsetMinutes }) => ({
label: `(GMT${formatTimezoneOffset(offsetMinutes)}) ${name}`,
value: timezone,
}));
export const createCompanyFormSections = ({
editMode,
id,
@@ -315,15 +470,24 @@ export const createCompanyFormSections = ({
),
},
{
kind: "input",
kind: "custom",
name: "timezone",
label: "Timezone",
type: "text",
className: "sm:col-span-2",
rules: {
minLength: { value: 3, message: FormErrorMessages.minLength(3) },
maxLength: { value: 50, message: FormErrorMessages.maxLength(50) },
},
render: ({ register, errors }) => (
<Select<ICreateCompanyCredentials>
{...register("timezone")}
errors={errors}
name="timezone"
label="Timezone"
items={timezoneItems}
value={watch("timezone") ?? ""}
placeholder="Select timezone"
searchable
searchPlaceholder="Search timezone..."
clearable
onClear={() => setValue("timezone", "")}
/>
),
},
],
},