Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions console/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions console/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"@dnd-kit/core": "^6.3.1",
"@dnd-kit/sortable": "^10.0.0",
"@dnd-kit/utilities": "^3.2.2",
"@vvo/tzdb": "^6.198.0",
"ahooks": "^3.9.6",
"antd": "^5.29.1",
"antd-style": "^3.7.1",
Expand Down
81 changes: 60 additions & 21 deletions console/src/constants/timezone.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,60 @@
export const TIMEZONE_OPTIONS = [
{ value: "America/Los_Angeles", label: "America/Los_Angeles (UTC-8)" },
{ value: "America/Denver", label: "America/Denver (UTC-7)" },
{ value: "America/Chicago", label: "America/Chicago (UTC-6)" },
{ value: "America/New_York", label: "America/New_York (UTC-5)" },
{ value: "America/Toronto", label: "America/Toronto (UTC-5)" },
{ value: "UTC", label: "UTC" },
{ value: "Europe/London", label: "Europe/London (UTC+0)" },
{ value: "Europe/Paris", label: "Europe/Paris (UTC+1)" },
{ value: "Europe/Berlin", label: "Europe/Berlin (UTC+1)" },
{ value: "Europe/Moscow", label: "Europe/Moscow (UTC+3)" },
{ value: "Asia/Dubai", label: "Asia/Dubai (UTC+4)" },
{ value: "Asia/Shanghai", label: "Asia/Shanghai (UTC+8)" },
{ value: "Asia/Hong_Kong", label: "Asia/Hong_Kong (UTC+8)" },
{ value: "Asia/Singapore", label: "Asia/Singapore (UTC+8)" },
{ value: "Asia/Tokyo", label: "Asia/Tokyo (UTC+9)" },
{ value: "Asia/Seoul", label: "Asia/Seoul (UTC+9)" },
{ value: "Australia/Sydney", label: "Australia/Sydney (UTC+10)" },
{ value: "Australia/Melbourne", label: "Australia/Melbourne (UTC+10)" },
{ value: "Pacific/Auckland", label: "Pacific/Auckland (UTC+12)" },
];
import { getTimeZones } from "@vvo/tzdb";

const TIMEZONE_ID_SET = new Set([
"America/Los_Angeles",
"America/Denver",
"America/Chicago",
"America/New_York",
"America/Toronto",
"UTC",
"Europe/London",
"Europe/Paris",
"Europe/Berlin",
"Europe/Moscow",
"Asia/Dubai",
"Asia/Shanghai",
"Asia/Hong_Kong",
"Asia/Singapore",
"Asia/Tokyo",
"Asia/Seoul",
"Australia/Sydney",
"Australia/Melbourne",
"Pacific/Auckland",
]);

export interface TimezoneOption {
value: string; // for timezone id
label: string; // for display text
}

function getLocalizedName(tzName: string, lang: string): string {
const locale = { zh: "zh-CN", en: "en", ru: "ru", ja: "ja" }[lang] || "en";
try {
const parts = new Intl.DateTimeFormat(locale, {
timeZone: tzName,
timeZoneName: "long",
}).formatToParts(new Date());
return parts.find((p) => p.type === "timeZoneName")?.value || tzName;
} catch {
return tzName;
}
}

export function getTimezoneOptions(lang: string = "en"): TimezoneOption[] {
return getTimeZones({ includeUtc: true })
.filter(
(tz) =>
TIMEZONE_ID_SET.has(tz.name) ||
(tz.name === "Etc/UTC" && TIMEZONE_ID_SET.has("UTC")),
)
.sort((a, b) => a.currentTimeOffsetInMinutes - b.currentTimeOffsetInMinutes)
.map((tz) => {
const value = tz.name === "Etc/UTC" ? "UTC" : tz.name;
return {
value,
label: `${getLocalizedName(tz.name, lang)} (${
tz.currentTimeFormat.split(" ")[0]
}, ${value})`,
Comment on lines +50 to +57
Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The offset portion of the label is derived from tz.currentTimeFormat.split(" ")[0], which is brittle and depends on the library’s string format (and may include a UTC prefix, differing from the intended +08:00 display). Prefer formatting tz.currentTimeOffsetInMinutes into a ±HH:MM string directly so the UI output is stable and matches the desired format.

Copilot uses AI. Check for mistakes.
};
});
}
8 changes: 8 additions & 0 deletions console/src/hooks/useTimezoneOptions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { useMemo } from "react";
import { useTranslation } from "react-i18next";
import { getTimezoneOptions, type TimezoneOption } from "../constants/timezone";

export function useTimezoneOptions(): TimezoneOption[] {
const { i18n } = useTranslation();
return useMemo(() => getTimezoneOptions(i18n.language), [i18n.language]);
Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i18n.language can include region subtags (e.g. en-US). In this repo App.tsx normalizes language via lng.split("-")[0] when deriving locale. Consider normalizing here too (and/or using i18n.resolvedLanguage) before passing into getTimezoneOptions, otherwise the locale map in getLocalizedName will fall back to English unexpectedly.

Suggested change
return useMemo(() => getTimezoneOptions(i18n.language), [i18n.language]);
const language = i18n.resolvedLanguage ?? i18n.language;
return useMemo(
() => {
const locale = (language ?? "en").split("-")[0];
return getTimezoneOptions(locale);
},
[language],
);

Copilot uses AI. Check for mistakes.
}
4 changes: 2 additions & 2 deletions console/src/pages/Agent/Config/components/ReactAgentCard.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Form, InputNumber, Select, Card, Alert } from "@agentscope-ai/design";
import { useTranslation } from "react-i18next";
import { TIMEZONE_OPTIONS } from "../../../../constants/timezone";
import { useTimezoneOptions } from "../../../../hooks/useTimezoneOptions";
import styles from "../index.module.less";

const LANGUAGE_OPTIONS = [
Expand Down Expand Up @@ -63,7 +63,7 @@ export function ReactAgentCard({
.toLowerCase()
.includes(input.toLowerCase())
}
options={TIMEZONE_OPTIONS}
options={useTimezoneOptions()}
onChange={onTimezoneChange}
Comment on lines 63 to 67
Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

useTimezoneOptions() is a React hook; calling it inline in options={useTimezoneOptions()} violates react-hooks/rules-of-hooks (this repo enables the recommended react-hooks rules) and may fail linting. Invoke the hook once near the top of ReactAgentCard and pass the resulting array to the Select.

Copilot uses AI. Check for mistakes.
loading={savingTimezone}
disabled={savingTimezone}
Expand Down
5 changes: 3 additions & 2 deletions console/src/pages/Control/CronJobs/components/JobDrawer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ import { TimePicker } from "antd";
import { useTranslation } from "react-i18next";
import type { FormInstance } from "antd";
import type { CronJobSpecOutput } from "../../../../api/types";
import { TIMEZONE_OPTIONS, DEFAULT_FORM_VALUES } from "./constants";
import { DEFAULT_FORM_VALUES } from "./constants";
import { useTimezoneOptions } from "../../../../hooks/useTimezoneOptions";
import styles from "../index.module.less";

type CronJob = CronJobSpecOutput;
Expand Down Expand Up @@ -230,7 +231,7 @@ export function JobDrawer({
.toLowerCase()
.includes(input.toLowerCase())
}
options={TIMEZONE_OPTIONS}
options={useTimezoneOptions()}
/>
Comment on lines 231 to 235
Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

useTimezoneOptions() is a React hook; calling it directly inside JSX props (in the options={...} expression) will trip react-hooks/rules-of-hooks and can break hook ordering if this render path ever becomes conditional. Call the hook once at the top of the component (e.g., assign to a timezoneOptions const) and pass that variable to Select instead.

Copilot uses AI. Check for mistakes.
</Form.Item>

Expand Down
2 changes: 0 additions & 2 deletions console/src/pages/Control/CronJobs/components/constants.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import dayjs from "dayjs";

export { TIMEZONE_OPTIONS } from "../../../../constants/timezone";

export const DEFAULT_FORM_VALUES = {
enabled: false,
schedule: {
Expand Down
2 changes: 1 addition & 1 deletion console/src/pages/Control/CronJobs/components/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export { createColumns } from "./columns";
export { JobDrawer } from "./JobDrawer";
export { useCronJobs } from "../useCronJobs";
export { TIMEZONE_OPTIONS, DEFAULT_FORM_VALUES } from "./constants";
export { DEFAULT_FORM_VALUES } from "./constants";
Loading