diff --git a/frontend/src/__tests__/components/automations/detail/activity-log-item.test.tsx b/frontend/src/__tests__/components/automations/detail/activity-log-item.test.tsx
index a11fd46..9809fd5 100644
--- a/frontend/src/__tests__/components/automations/detail/activity-log-item.test.tsx
+++ b/frontend/src/__tests__/components/automations/detail/activity-log-item.test.tsx
@@ -37,6 +37,17 @@ describe("ActivityLogItem", () => {
).toBeInTheDocument();
});
+ it("formats the run timestamp in the automation timezone", () => {
+ const run = createRun();
+ render();
+
+ expect(
+ screen.getByText(
+ (content) => content.includes("2:00 AM") && content.includes("PDT"),
+ ),
+ ).toBeInTheDocument();
+ });
+
it("renders as a link when conversation_id exists", () => {
const run = createRun({ conversation_id: "conv-abc123" });
render();
diff --git a/frontend/src/components/automations/detail/activity-log-item.tsx b/frontend/src/components/automations/detail/activity-log-item.tsx
index 5a92259..e30d68a 100644
--- a/frontend/src/components/automations/detail/activity-log-item.tsx
+++ b/frontend/src/components/automations/detail/activity-log-item.tsx
@@ -5,18 +5,36 @@ import { RunStatusBadge } from "./run-status-badge";
interface ActivityLogItemProps {
run: AutomationRun;
+ timeZone?: string;
}
-function formatRunTimestamp(dateStr: string, locale: string): string {
+const RUN_TIMESTAMP_OPTIONS: Intl.DateTimeFormatOptions = {
+ weekday: "long",
+ year: "numeric",
+ month: "long",
+ day: "numeric",
+ hour: "numeric",
+ minute: "2-digit",
+};
+
+function formatRunTimestamp(
+ dateStr: string,
+ locale: string,
+ timeZone?: string,
+): string {
const date = new Date(dateStr);
- return date.toLocaleDateString(locale, {
- weekday: "long",
- year: "numeric",
- month: "long",
- day: "numeric",
- hour: "numeric",
- minute: "2-digit",
- });
+ const options: Intl.DateTimeFormatOptions = { ...RUN_TIMESTAMP_OPTIONS };
+
+ if (timeZone) {
+ options.timeZone = timeZone;
+ options.timeZoneName = "short";
+ }
+
+ try {
+ return date.toLocaleDateString(locale, options);
+ } catch {
+ return date.toLocaleDateString(locale, RUN_TIMESTAMP_OPTIONS);
+ }
}
function getConversationUrl(conversationId: string): string {
@@ -27,16 +45,19 @@ function getConversationUrl(conversationId: string): string {
return `https://${baseHost}/conversations/${conversationId}`;
}
-export function ActivityLogItem({ run }: ActivityLogItemProps) {
+export function ActivityLogItem({ run, timeZone }: ActivityLogItemProps) {
const { t, i18n } = useTranslation();
const hasConversation = !!run.conversation_id;
+ const runTimestamp = formatRunTimestamp(
+ run.started_at,
+ i18n.language,
+ timeZone,
+ );
const content = (
<>
-
- {formatRunTimestamp(run.started_at, i18n.language)}
-
+
{runTimestamp}
{!hasConversation && (
({t(I18nKey.AUTOMATIONS$DETAIL$NO_CONVERSATION)})
@@ -54,7 +75,7 @@ export function ActivityLogItem({ run }: ActivityLogItemProps) {
target="_blank"
rel="noopener noreferrer"
className="flex items-center justify-between px-5 py-3 transition-colors cursor-pointer hover:bg-surface-elevated focus:bg-surface-elevated focus:outline-none"
- aria-label={`View conversation for run at ${formatRunTimestamp(run.started_at, i18n.language)}`}
+ aria-label={`View conversation for run at ${runTimestamp}`}
>
{content}
diff --git a/frontend/src/components/automations/detail/activity-log-section.tsx b/frontend/src/components/automations/detail/activity-log-section.tsx
index d2cc801..6b092e2 100644
--- a/frontend/src/components/automations/detail/activity-log-section.tsx
+++ b/frontend/src/components/automations/detail/activity-log-section.tsx
@@ -7,11 +7,15 @@ import { ActivityLogItem } from "./activity-log-item";
interface ActivityLogSectionProps {
automationId: string;
+ timeZone?: string;
}
const PAGE_SIZE = 20;
-export function ActivityLogSection({ automationId }: ActivityLogSectionProps) {
+export function ActivityLogSection({
+ automationId,
+ timeZone,
+}: ActivityLogSectionProps) {
const { t } = useTranslation();
const [limit, setLimit] = useState(PAGE_SIZE);
const { data, isLoading } = useAutomationRuns(automationId, limit, 0);
@@ -56,7 +60,7 @@ export function ActivityLogSection({ automationId }: ActivityLogSectionProps) {
key={run.id}
className={index > 0 ? "border-t border-border" : ""}
>
-
+
))}
diff --git a/frontend/src/components/automations/detail/configuration-section.tsx b/frontend/src/components/automations/detail/configuration-section.tsx
index 01081ad..cdd4c76 100644
--- a/frontend/src/components/automations/detail/configuration-section.tsx
+++ b/frontend/src/components/automations/detail/configuration-section.tsx
@@ -20,10 +20,11 @@ export function ConfigurationSection({
}: ConfigurationSectionProps) {
const { t } = useTranslation();
+ const automationTimeZone = automation.trigger.timezone ?? automation.timezone;
let scheduleDisplay = automation.trigger.schedule ?? "";
if (automation.trigger.schedule_human) {
- scheduleDisplay = automation.timezone
- ? `${automation.trigger.schedule_human} (${automation.timezone})`
+ scheduleDisplay = automationTimeZone
+ ? `${automation.trigger.schedule_human} (${automationTimeZone})`
: automation.trigger.schedule_human;
}
diff --git a/frontend/src/routes/automation-detail.tsx b/frontend/src/routes/automation-detail.tsx
index 6db6202..f1ad3e7 100644
--- a/frontend/src/routes/automation-detail.tsx
+++ b/frontend/src/routes/automation-detail.tsx
@@ -64,6 +64,8 @@ export default function AutomationDetail() {
});
};
+ const automationTimeZone = automation.trigger.timezone ?? automation.timezone;
+
return (
@@ -81,7 +83,10 @@ export default function AutomationDetail() {
createdAt={automation.created_at}
lastRunAt={automation.last_triggered_at}
/>
-
+