Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ import { methodName, type TickerNotificationHubType } from '@/hub/tickerNotifica
import type { GetCronTickerOccurrenceResponse } from '@/http/services/types/cronTickerOccurrenceService.types'
import { ConfirmDialogProps } from '@/components/common/ConfirmDialog.vue'
import PaginationFooter from '@/components/PaginationFooter.vue'
import { formatTime } from '@/utilities/dateTimeParser'
import { format } from 'timeago.js'
import { formatTime, formatTimeAgo } from '@/utilities/dateTimeParser'

const confirmDialog = useDialog<{ data: string }>().withComponent(
() => import('@/components/common/ConfirmDialog.vue'),
Expand Down Expand Up @@ -100,7 +99,7 @@ const addHubListeners = async () => {
...currentItem,
...val,
status: Status[val.status as any],
executedAt: `${format(val.executedAt)} (took ${formatTime(val.elapsedTime as number, true)})`,
executedAt: `${formatTimeAgo(val.executedAt)} (took ${formatTime(val.elapsedTime as number, true)})`,
retryIntervals: currentItem.retryIntervals,
lockedAt: currentItem.lockedAt, // Preserve existing lockedAt
lockHolder: currentItem.lockHolder,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@

import { formatDate, formatTime } from '@/utilities/dateTimeParser';
import { formatDate, formatTime, formatTimeAgo } from '@/utilities/dateTimeParser';
import { useBaseHttpService } from '../base/baseHttpService';
import { Status } from './types/base/baseHttpResponse.types';
import { GetCronTickerOccurrenceGraphDataRequest, GetCronTickerOccurrenceGraphDataResponse, GetCronTickerOccurrenceRequest, GetCronTickerOccurrenceResponse } from './types/cronTickerOccurrenceService.types';
import { format} from 'timeago.js';
import { nameof } from '@/utilities/nameof';
import { useTimeZoneStore } from '@/stores/timeZoneStore';

Expand All @@ -23,14 +22,12 @@ const getByCronTickerId = () => {
}

// Safely set status with null check
if (response.status !== undefined && response.status !== null) {
if (response.status != null) {
response.status = Status[response.status as any];
}

if (response.executedAt != null || response.executedAt != undefined) {
// Ensure the datetime is treated as UTC by adding 'Z' if missing
const utcExecutedAt = response.executedAt.endsWith('Z') ? response.executedAt : response.executedAt + 'Z';
response.executedAt = `${format(utcExecutedAt)} (took ${formatTime(response.elapsedTime as number, true)})`;
if (response.executedAt != null) {
response.executedAt = `${formatTimeAgo(response.executedAt)} (took ${formatTime(response.elapsedTime as number, true)})`;
}

const utcExecutionTime = response.executionTime.endsWith('Z') ? response.executionTime : response.executionTime + 'Z';
Expand Down Expand Up @@ -75,17 +72,15 @@ const getByCronTickerIdPaginated = () => {
if (!item) return item;

// Safely set status with null check and ensure it's always a string
if (item.status !== undefined && item.status !== null) {
if (item.status != null) {
const statusValue = Status[item.status as any];
item.status = statusValue !== undefined ? statusValue : String(item.status);
} else {
item.status = 'Unknown';
}

if (item.executedAt != null || item.executedAt != undefined) {
// Ensure the datetime is treated as UTC by adding 'Z' if missing
const utcExecutedAt = item.executedAt.endsWith('Z') ? item.executedAt : item.executedAt + 'Z';
item.executedAt = `${format(utcExecutedAt)} (took ${formatTime(item.elapsedTime as number, true)})`;
if (item.executedAt != null) {
item.executedAt = `${formatTimeAgo(item.executedAt)} (took ${formatTime(item.elapsedTime as number, true)})`;
}

const utcExecutionTime = item.executionTime.endsWith('Z') ? item.executionTime : item.executionTime + 'Z';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

import { formatDate, formatTime } from '@/utilities/dateTimeParser';
import { formatDate, formatTime, formatTimeAgo } from '@/utilities/dateTimeParser';
import { useBaseHttpService } from '../base/baseHttpService';
import { Status } from './types/base/baseHttpResponse.types';
import {
Expand All @@ -11,7 +11,6 @@ import {
UpdateTimeTickerRequest
} from './types/timeTickerService.types'
import { nameof } from '@/utilities/nameof';
import { format} from 'timeago.js';
import { useFunctionNameStore } from '@/stores/functionNames';
import { useTimeZoneStore } from '@/stores/timeZoneStore';

Expand All @@ -36,12 +35,12 @@ const getTimeTickers = () => {
// Recursive function to process item and its children
const processItem = (item: GetTimeTickerResponse): GetTimeTickerResponse => {
// Safely set status with null check
if (item.status !== undefined && item.status !== null) {
if (item.status != null) {
item.status = Status[item.status as any];
}

if (item.executedAt != null || item.executedAt != undefined)
item.executedAt = `${format(item.executedAt)} (took ${formatTime(item.elapsedTime as number, true)})`;
if (item.executedAt != null)
item.executedAt = `${formatTimeAgo(item.executedAt)} (took ${formatTime(item.elapsedTime as number, true)})`;

item.executionTimeFormatted = formatDate(item.executionTime, true, timeZoneStore.effectiveTimeZone);
item.requestType = functionNamesStore.getNamespaceOrNull(item.function) ?? '';
Expand Down Expand Up @@ -108,12 +107,12 @@ const getTimeTickersPaginated = () => {
if (response && response.items && Array.isArray(response.items)) {
response.items = response.items.map((item: GetTimeTickerResponse) => {
const processItem = (item: GetTimeTickerResponse): GetTimeTickerResponse => {
if (item.status !== undefined && item.status !== null) {
if (item.status != null) {
item.status = Status[item.status as any];
}

if (item.executedAt != null || item.executedAt != undefined)
item.executedAt = `${format(item.executedAt)} (took ${formatTime(item.elapsedTime as number, true)})`;
if (item.executedAt != null)
item.executedAt = `${formatTimeAgo(item.executedAt)} (took ${formatTime(item.elapsedTime as number, true)})`;

item.executionTimeFormatted = formatDate(item.executionTime, true, timeZoneStore.effectiveTimeZone);
item.requestType = functionNamesStore.getNamespaceOrNull(item.function) ?? '';
Expand Down
12 changes: 12 additions & 0 deletions src/TickerQ.Dashboard/wwwroot/src/utilities/dateTimeParser.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { format as timeago } from 'timeago.js';

export function formatDate(
utcDateString: string,
Expand Down Expand Up @@ -126,3 +127,14 @@ export function formatFromUtcToLocal(utcDateString: string): string {

return `${year}-${month}-${day}T${hours}:${minutes}:${seconds}`
}

export function formatTimeAgo(date: string | Date): string {
// Front-end often passes dates as strings straight up from JSON payloads.
// All dates on back-end are UTC but dates loaded by EF have DateTimeKind.Unspecified by default,
// which is serialized to JSON without any offset suffix.
// We have to specify them as UTC so that they're not parsed as local time by JS.
if (typeof date === 'string' && !date.endsWith('Z')) {
date = date + 'Z'
}
return timeago(date)
}
Loading