diff --git a/src/TickerQ.Caching.StackExchangeRedis/Infrastructure/TickerRedisPersistenceProvider.cs b/src/TickerQ.Caching.StackExchangeRedis/Infrastructure/TickerRedisPersistenceProvider.cs index b047d416..b34b9e3b 100644 --- a/src/TickerQ.Caching.StackExchangeRedis/Infrastructure/TickerRedisPersistenceProvider.cs +++ b/src/TickerQ.Caching.StackExchangeRedis/Infrastructure/TickerRedisPersistenceProvider.cs @@ -570,7 +570,7 @@ public async Task GetAllCronTickerExpressions(CancellationTo cancellationToken.ThrowIfCancellationRequested(); if (!Guid.TryParse(redisValue.ToString(), out var id)) continue; var cron = await GetAsync(CronKey(id)).ConfigureAwait(false); - if (cron == null) continue; + if (cron == null || !cron.IsEnabled) continue; list.Add(new CronTickerEntity { Id = cron.Id, diff --git a/src/TickerQ.Dashboard/wwwroot/src/http/services/cronTickerService.ts b/src/TickerQ.Dashboard/wwwroot/src/http/services/cronTickerService.ts index af2228d1..92b6f2d0 100644 --- a/src/TickerQ.Dashboard/wwwroot/src/http/services/cronTickerService.ts +++ b/src/TickerQ.Dashboard/wwwroot/src/http/services/cronTickerService.ts @@ -132,11 +132,12 @@ const runCronTickerOnDemand = () => { } const getTimeTickersGraphDataRange = () => { + const timeZoneStore = useTimeZoneStore(); const baseHttp = useBaseHttpService('array') .FixToResponseModel(GetCronTickerGraphDataRangeResponse, (item) => { return { ...item, - date: formatDate(item.date, false), + date: formatDate(item.date, false, timeZoneStore.effectiveTimeZone), } }); @@ -149,11 +150,12 @@ const getTimeTickersGraphDataRange = () => { } const getTimeTickersGraphDataRangeById = () => { + const timeZoneStore = useTimeZoneStore(); const baseHttp = useBaseHttpService('array') .FixToResponseModel(GetCronTickerGraphDataRangeResponse, (item) => { return { ...item, - date: formatDate(item.date, false), + date: formatDate(item.date, false, timeZoneStore.effectiveTimeZone), } }); diff --git a/src/TickerQ.Dashboard/wwwroot/src/http/services/timeTickerService.ts b/src/TickerQ.Dashboard/wwwroot/src/http/services/timeTickerService.ts index 5a2ab9e6..7b4169ce 100644 --- a/src/TickerQ.Dashboard/wwwroot/src/http/services/timeTickerService.ts +++ b/src/TickerQ.Dashboard/wwwroot/src/http/services/timeTickerService.ts @@ -157,11 +157,12 @@ const getTimeTickersPaginated = () => { } const getTimeTickersGraphDataRange = () => { + const timeZoneStore = useTimeZoneStore(); const baseHttp = useBaseHttpService('array') .FixToResponseModel(GetTimeTickerGraphDataRangeResponse, (item) => { return { ...item, - date: formatDate(item.date, false), + date: formatDate(item.date, false, timeZoneStore.effectiveTimeZone), } }); diff --git a/src/TickerQ.Dashboard/wwwroot/src/main.ts b/src/TickerQ.Dashboard/wwwroot/src/main.ts index baf13156..508d96e1 100644 --- a/src/TickerQ.Dashboard/wwwroot/src/main.ts +++ b/src/TickerQ.Dashboard/wwwroot/src/main.ts @@ -23,6 +23,12 @@ import VueTheMask from 'vue-the-mask' // Import styles import './assets/main.css' +import { getDateFormatRegion, buildDatePart } from '@/utilities/dateTimeParser' +import { useTimeZoneStore } from './stores/timeZoneStore' + +// Create Pinia store (before Vuetify so the date format function can access the timezone store) +const pinia = createPinia() + // Create Vuetify instance const vuetify = createVuetify({ components: { @@ -31,6 +37,18 @@ const vuetify = createVuetify({ VPullToRefresh }, directives, + date: { + formats: { + keyboardDate: (date: Date) => { + const timeZoneStore = useTimeZoneStore(pinia) + const region = getDateFormatRegion(timeZoneStore.effectiveTimeZone) + const year = String(date.getFullYear()) + const month = String(date.getMonth() + 1).padStart(2, '0') + const day = String(date.getDate()).padStart(2, '0') + return buildDatePart(region, year, month, day) + }, + }, + }, icons: { defaultSet: 'mdi', aliases, @@ -43,9 +61,6 @@ const vuetify = createVuetify({ } }) -// Create Pinia store -const pinia = createPinia() - // Create Vue app const app = createApp(App) diff --git a/src/TickerQ.Dashboard/wwwroot/src/utilities/dateTimeParser.ts b/src/TickerQ.Dashboard/wwwroot/src/utilities/dateTimeParser.ts index 556433c7..d903df60 100644 --- a/src/TickerQ.Dashboard/wwwroot/src/utilities/dateTimeParser.ts +++ b/src/TickerQ.Dashboard/wwwroot/src/utilities/dateTimeParser.ts @@ -1,5 +1,32 @@ import { format as timeago } from 'timeago.js'; +type DateFormatRegion = 'eu' | 'us' | 'iso'; + +const europeanZonePrefixes = ['Europe/', 'Africa/']; +const americanZonePrefixes = ['America/', 'US/']; + +export function getDateFormatRegion(timeZone?: string): DateFormatRegion { + if (!timeZone) return 'iso'; + + if (europeanZonePrefixes.some(prefix => timeZone.startsWith(prefix))) { + return 'eu'; + } + + if (americanZonePrefixes.some(prefix => timeZone.startsWith(prefix))) { + return 'us'; + } + + return 'iso'; +} + +export function buildDatePart(region: DateFormatRegion, year: string, month: string, day: string): string { + switch (region) { + case 'eu': return `${day}/${month}/${year}`; + case 'us': return `${month}/${day}/${year}`; + default: return `${year}-${month}-${day}`; + } +} + export function formatDate( utcDateString: string, includeTime = true, @@ -31,12 +58,13 @@ export function formatDate( options.hour12 = false; } - // Use formatToParts for a consistent, locale-independent YYYY-MM-DD HH:mm:ss format const formatter = new Intl.DateTimeFormat('en-CA', options); const parts = formatter.formatToParts(dateObj); const get = (type: string) => parts.find(p => p.type === type)?.value ?? ''; - const datePart = `${get('year')}-${get('month')}-${get('day')}`; + const region = getDateFormatRegion(timeZone); + const datePart = buildDatePart(region, get('year'), get('month'), get('day')); + if (!includeTime) { return datePart; } diff --git a/src/TickerQ.Dashboard/wwwroot/src/views/CronTicker.vue b/src/TickerQ.Dashboard/wwwroot/src/views/CronTicker.vue index 67d5d7a1..6bc1f395 100644 --- a/src/TickerQ.Dashboard/wwwroot/src/views/CronTicker.vue +++ b/src/TickerQ.Dashboard/wwwroot/src/views/CronTicker.vue @@ -493,7 +493,7 @@ const onSubmitToggleConfirmDialog = async () => { const id = toggleConfirmDialog.propData?.id! const isEnabled = toggleConfirmDialog.propData?.isEnabled! toggleConfirmDialog.close() - await toggleCronTicker.requestAsync(id, isEnabled) + await toggleCronTicker.requestAsync(id, !isEnabled) await loadPageData() } catch (error: any) { if (error?.name === 'CanceledError' || error?.code === 'ERR_CANCELED') { @@ -1261,7 +1261,7 @@ const refreshData = async () => {