From 90e9e4bee6dd5ed81acc8db7acf6b02fbb2edf8d Mon Sep 17 00:00:00 2001 From: Paulo Castellano Date: Sat, 13 Jun 2026 16:46:34 -0300 Subject: [PATCH] Centralize timezone display in the date helper Add date.getTimezoneAbbr() and route every "GMT-x" abbreviation through it, removing the three inline dayjs().format('z') copies (schedule-summary, TriggerNodeConfig, PickTimePopover) and the duplicate userTimezone helper. Drop the pointless computed wrapper for a value that never changes, and rename snap5 to snapToFiveMinutes. --- .../automations/config/TriggerNodeConfig.vue | 15 +++++++-------- .../js/components/automations/schedule-summary.ts | 5 ----- resources/js/components/posts/PickTimePopover.vue | 7 ++++--- resources/js/date.ts | 8 ++++++++ 4 files changed, 19 insertions(+), 16 deletions(-) diff --git a/resources/js/components/automations/config/TriggerNodeConfig.vue b/resources/js/components/automations/config/TriggerNodeConfig.vue index 5255d8bd..0d64e69c 100644 --- a/resources/js/components/automations/config/TriggerNodeConfig.vue +++ b/resources/js/components/automations/config/TriggerNodeConfig.vue @@ -5,8 +5,6 @@ import { generateScheduleCron, humanSchedule as scheduleSummary, normalizeScheduleData, - timezoneAbbr as getTimezoneAbbr, - userTimezone as getUserTimezone, } from '@/components/automations/schedule-summary'; import InputError from '@/components/InputError.vue'; import { Input } from '@/components/ui/input'; @@ -18,6 +16,7 @@ import { SelectTrigger, SelectValue, } from '@/components/ui/select'; +import date from '@/date'; import type { ScheduleData } from '@/types/automation/schedule-data'; import { ScheduleField } from '@/types/automation/schedule-field'; import { TriggerType, type TriggerTypeValue } from '@/types/automation/trigger-type'; @@ -30,9 +29,9 @@ const emit = defineEmits<{ update: [Record] }>(); const pad2 = (n: number) => String(n).padStart(2, '0'); const clamp = (n: number, min: number, max: number) => Math.min(Math.max(n, min), max); -const snap5 = (n: number) => (Math.round(n / 5) * 5) % 60; +const snapToFiveMinutes = (n: number) => (Math.round(n / 5) * 5) % 60; -const timezoneAbbr = computed(() => getTimezoneAbbr()); +const timezoneAbbr = date.getTimezoneAbbr(); // Single source of truth for default + inferred field values, shared with the // Trigger card via `triggerSummary`. Anything beyond `normalizeScheduleData`'s @@ -41,8 +40,8 @@ const local = ref @@ -97,14 +96,14 @@ const scheduleSummaryLine = computed(() => { || local.value.schedule_field === ScheduleField.Months; return pinsClockTime - ? `${humanSchedule.value} (${timezoneAbbr.value})` + ? `${humanSchedule.value} (${timezoneAbbr})` : humanSchedule.value; }); watch(generatedCron, (cron) => { if (local.value.trigger_type === TriggerType.Schedule) { local.value.cron = cron; - local.value.schedule_timezone = getUserTimezone(); + local.value.schedule_timezone = date.getUserTimezone(); } }, { immediate: true }); diff --git a/resources/js/components/automations/schedule-summary.ts b/resources/js/components/automations/schedule-summary.ts index 2d75dd4e..916401d9 100644 --- a/resources/js/components/automations/schedule-summary.ts +++ b/resources/js/components/automations/schedule-summary.ts @@ -1,6 +1,5 @@ import { trans, transChoice } from 'laravel-vue-i18n'; -import dayjs from '@/dayjs'; import type { ScheduleData } from '@/types/automation/schedule-data'; import { ScheduleField } from '@/types/automation/schedule-field'; import { TriggerType } from '@/types/automation/trigger-type'; @@ -155,7 +154,3 @@ export const triggerSummary = (data: ScheduleData): string => { } return ''; }; - -export const userTimezone = (): string => Intl.DateTimeFormat().resolvedOptions().timeZone; - -export const timezoneAbbr = (): string => dayjs().format('z'); diff --git a/resources/js/components/posts/PickTimePopover.vue b/resources/js/components/posts/PickTimePopover.vue index a4bf3357..970f946e 100644 --- a/resources/js/components/posts/PickTimePopover.vue +++ b/resources/js/components/posts/PickTimePopover.vue @@ -6,6 +6,7 @@ import { Button } from '@/components/ui/button'; import { Calendar } from '@/components/ui/calendar'; import { Dialog, DialogContent, DialogTitle, DialogTrigger } from '@/components/ui/dialog'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; +import date from '@/date'; import dayjs from '@/dayjs'; const props = defineProps<{ @@ -14,7 +15,7 @@ const props = defineProps<{ showRemove?: boolean; }>(); -const timezoneAbbr = computed(() => dayjs().format('z')); +const timezoneAbbr = date.getTimezoneAbbr(); const emit = defineEmits<{ 'update:modelValue': [value: string]; @@ -27,8 +28,8 @@ const open = ref(false); const parseInput = (value: string) => { if (!value) return undefined; try { - const date = dayjs(value); - if (date.isValid()) return parseDate(date.format('YYYY-MM-DD')); + const parsed = dayjs(value); + if (parsed.isValid()) return parseDate(parsed.format('YYYY-MM-DD')); } catch { return undefined; } diff --git a/resources/js/date.ts b/resources/js/date.ts index 913d4974..09095988 100644 --- a/resources/js/date.ts +++ b/resources/js/date.ts @@ -155,6 +155,14 @@ export default { */ getUserTimezone, + /** + * Abreviação do timezone do usuário para exibição (ex.: "GMT-3") + * @returns Abreviação do timezone + */ + getTimezoneAbbr(): string { + return dayjs().format('z'); + }, + /** * Formata uma data para o formato YYYY-MM-DD (usado em DatePicker) * Evita problemas de timezone ao não criar objeto Date