enhance: convert to local time on client

This commit is contained in:
a7m-1st 2026-01-13 23:27:18 +03:00
parent eb0cba12c6
commit d771c29b0a
6 changed files with 72 additions and 38 deletions

View file

@ -61,6 +61,7 @@
"clsx": "^2.1.1",
"cmdk": "^1.1.1",
"csv-parser": "^3.2.0",
"date-fns": "^3.6.0",
"dompurify": "^3.2.7",
"electron-log": "^5.4.0",
"electron-updater": "^6.3.9",

View file

@ -12,6 +12,7 @@ import {
} from "@/components/ui/popover";
import { useTranslation } from "react-i18next";
import folderIcon from "@/assets/Folder-1.svg";
import { formatDateTime } from "@/lib/utils";
interface TaskItemProps {
task: HistoryTask;
@ -72,8 +73,7 @@ export default function TaskItem({
const formatDate = (dateString?: string) => {
if (!dateString) return "";
const date = new Date(dateString);
return date.toLocaleDateString() + " " + date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
return formatDateTime(dateString, "MMM dd, yyyy HH:mm");
};
return (

View file

@ -3,6 +3,7 @@ import { useEffect, useState } from "react";
import { proxyFetchTrigger, proxyFetchTriggerExecutions } from "@/service/triggerApi";
import { useActivityLogStore, ActivityType } from "@/store/activityLogStore";
import { Trigger, TriggerExecution, ExecutionStatus } from "@/types";
import { formatTime, formatRelativeTime } from "@/lib/utils";
export interface ExecutionLogEntry {
id: number;
@ -52,37 +53,6 @@ const formatDuration = (seconds?: number): string | undefined => {
return `${minutes}m ${remainingSeconds.toFixed(0)}s`;
};
// Helper function to format relative time
const formatRelativeTime = (dateString?: string): string => {
if (!dateString) return "Never";
const date = new Date(dateString);
const now = new Date();
const diffMs = now.getTime() - date.getTime();
const diffMins = Math.floor(diffMs / 60000);
const diffHours = Math.floor(diffMs / 3600000);
const diffDays = Math.floor(diffMs / 86400000);
if (diffMins < 1) return "Just now";
if (diffMins < 60) return `${diffMins} minute${diffMins > 1 ? 's' : ''} ago`;
if (diffHours < 24) return `${diffHours} hour${diffHours > 1 ? 's' : ''} ago`;
if (diffDays === 1) return "Yesterday";
if (diffDays < 7) return `${diffDays} days ago`;
return date.toLocaleDateString();
};
// Helper function to format timestamp
const formatTimestamp = (dateString: string): string => {
const date = new Date(dateString);
return date.toLocaleTimeString('en-US', {
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false
});
};
// Helper function to transform TriggerExecution to ExecutionLogEntry
const transformToLogEntry = (execution: TriggerExecution): ExecutionLogEntry => {
const status = mapExecutionStatus(execution.status);
@ -118,7 +88,7 @@ const transformToLogEntry = (execution: TriggerExecution): ExecutionLogEntry =>
return {
id: execution.id,
timestamp: formatTimestamp(execution.started_at || execution.created_at || new Date().toISOString()),
timestamp: formatTime(execution.started_at || execution.created_at),
status,
message,
duration,

View file

@ -9,6 +9,7 @@ import {
} from "@/components/ui/dropdown-menu";
import { Zap, Clock, MoreHorizontal, Edit, Copy, Trash2, Globe, MessageSquare } from "lucide-react";
import { useTranslation } from "react-i18next";
import { formatDateTime } from "@/lib/utils";
type TriggerListItemProps = {
trigger: Trigger;
@ -60,8 +61,7 @@ export const TriggerListItem: React.FC<TriggerListItemProps> = ({
const formatLastExecution = (dateString?: string) => {
if (!dateString) return t("triggers.never");
const date = new Date(dateString);
return `${date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })} ${date.toLocaleDateString([], { month: 'short', day: 'numeric' })}`;
return formatDateTime(dateString, "HH:mm MMM dd");
};
return (

View file

@ -1,6 +1,69 @@
import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"
import { formatDistanceToNow, format, parseISO } from "date-fns"
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
/**
* Date/Time Utilities
* All functions expect UTC ISO strings from API and convert to local timezone
*/
/**
* Format UTC timestamp to local time (HH:mm:ss)
* @param utcString - ISO 8601 UTC timestamp from API
*/
export function formatTime(utcString: string | null | undefined): string {
if (!utcString) return "N/A";
try {
const date = parseISO(utcString.endsWith('Z') ? utcString : utcString + 'Z');
return format(date, "HH:mm:ss");
} catch {
return "Invalid time";
}
}
/**
* Format UTC timestamp to local date and time
* @param utcString - ISO 8601 UTC timestamp from API
* @param formatStr - date-fns format string (default: "MMM dd, yyyy HH:mm")
*/
export function formatDateTime(utcString: string | null | undefined, formatStr: string = "MMM dd, yyyy HH:mm"): string {
if (!utcString) return "N/A";
try {
const date = parseISO(utcString.endsWith('Z') ? utcString : utcString + 'Z');
return format(date, formatStr);
} catch {
return "Invalid date";
}
}
/**
* Format UTC timestamp to local date only
* @param utcString - ISO 8601 UTC timestamp from API
*/
export function formatDate(utcString: string | null | undefined): string {
if (!utcString) return "N/A";
try {
const date = parseISO(utcString.endsWith('Z') ? utcString : utcString + 'Z');
return format(date, "MMM dd, yyyy");
} catch {
return "Invalid date";
}
}
/**
* Format UTC timestamp as relative time (e.g., "2 hours ago")
* @param utcString - ISO 8601 UTC timestamp from API
*/
export function formatRelativeTime(utcString: string | null | undefined): string {
if (!utcString) return "N/A";
try {
const date = parseISO(utcString.endsWith('Z') ? utcString : utcString + 'Z');
return formatDistanceToNow(date, { addSuffix: true });
} catch {
return "Invalid date";
}
}

View file

@ -18,7 +18,7 @@ import { useTriggerStore } from "@/store/triggerStore";
import { useActivityLogStore, ActivityType } from "@/store/activityLogStore";
import { Trigger, TriggerInput, TriggerStatus } from "@/types";
import { toast } from "sonner";
import { formatDistanceToNow } from "date-fns";
import { formatRelativeTime } from "@/lib/utils";
import useChatStoreAdapter from "@/hooks/useChatStoreAdapter";
import { AnimatePresence, motion } from "framer-motion";
@ -331,7 +331,7 @@ export default function Overview() {
{activityLogs.slice(0, 50).map((log) => {
const Icon = getActivityIcon(log.type);
const notificationType = getActivityNotificationType(log.type);
const timeAgo = formatDistanceToNow(log.timestamp, { addSuffix: true });
const timeAgo = formatRelativeTime(log.timestamp.toISOString());
return (
<motion.div