Fix strategy timeline timestamp handling

This commit is contained in:
Thigazhezhilan J 2026-04-01 21:07:40 +05:30
parent 5577de7b9e
commit a22e8d7ee8

View File

@ -31,6 +31,14 @@ type StrategyTimelineProps = {
compact?: boolean; compact?: boolean;
}; };
function parseTimestamp(value?: string | null) {
if (!value) return null;
const hasTimezone = /Z|[+-]\d{2}:?\d{2}$/.test(value);
const normalized = hasTimezone ? value : `${value}Z`;
const parsed = new Date(normalized);
return Number.isNaN(parsed.getTime()) ? null : parsed;
}
function normalizeLog(log: unknown): StrategyEvent { function normalizeLog(log: unknown): StrategyEvent {
if (typeof log === "string") { if (typeof log === "string") {
try { try {
@ -49,25 +57,28 @@ function normalizeLog(log: unknown): StrategyEvent {
return { message: "Unknown log entry" }; return { message: "Unknown log entry" };
} }
function formatTimestamp(value?: string) { function formatTimestamp(value?: string | null) {
if (!value) return "Unknown time"; if (!value) return "Unknown time";
const hasTimezone = /Z|[+-]\d{2}:?\d{2}$/.test(value); const parsed = parseTimestamp(value);
const normalized = hasTimezone ? value : `${value}Z`; if (!parsed) return value;
const parsed = new Date(normalized);
if (Number.isNaN(parsed.getTime())) return value;
return parsed.toLocaleString(); return parsed.toLocaleString();
} }
function formatCompactTimestamp(value?: string | null) { function formatCompactTimestamp(value?: string | null) {
if (!value) return null; const parsed = parseTimestamp(value);
const parsed = new Date(value); if (!parsed) return null;
if (Number.isNaN(parsed.getTime())) return null; return parsed.toLocaleString([], {
return parsed.toLocaleTimeString([], { month: "short",
day: "numeric",
hour: "numeric", hour: "numeric",
minute: "2-digit", minute: "2-digit",
}); });
} }
function getTimestampMs(value?: string | null) {
return parseTimestamp(value)?.getTime() ?? 0;
}
function getRunId(entry: StrategyEvent) { function getRunId(entry: StrategyEvent) {
return entry.run_id ?? "unknown"; return entry.run_id ?? "unknown";
} }
@ -178,9 +189,7 @@ function CompactStrategySummary() {
<div className={`rounded-lg border px-4 py-3 text-sm ${className}`}> <div className={`rounded-lg border px-4 py-3 text-sm ${className}`}>
<div className="flex flex-col gap-1 md:flex-row md:items-center md:justify-between"> <div className="flex flex-col gap-1 md:flex-row md:items-center md:justify-between">
<span>{message}</span> <span>{message}</span>
{updatedAt ? ( {updatedAt ? <span className="text-xs opacity-80">Updated {updatedAt}</span> : null}
<span className="text-xs opacity-80">Updated {updatedAt}</span>
) : null}
</div> </div>
</div> </div>
); );
@ -228,25 +237,34 @@ function VerboseStrategyTimeline() {
}, []); }, []);
const groupedRuns = useMemo<StrategyRun[]>(() => { const groupedRuns = useMemo<StrategyRun[]>(() => {
const indexed = logs.map((entry, index) => ({ entry, index })); const grouped = new Map<string, StrategyEvent[]>();
indexed.sort((a, b) => { for (const entry of logs) {
const runCompare = getRunId(b.entry).localeCompare(getRunId(a.entry)); const runId = getRunId(entry);
if (runCompare !== 0) return runCompare; const events = grouped.get(runId) ?? [];
const seqCompare = getSeq(a.entry) - getSeq(b.entry); events.push(entry);
grouped.set(runId, events);
}
const groups = Array.from(grouped.entries()).map(([runId, events]) => ({
runId,
events: [...events].sort((a, b) => {
const seqCompare = getSeq(a) - getSeq(b);
if (seqCompare !== 0) return seqCompare;
return getTimestampMs(a.ts ?? a.timestamp) - getTimestampMs(b.ts ?? b.timestamp);
}),
}));
groups.sort((a, b) => {
const aLast = a.events[a.events.length - 1];
const bLast = b.events[b.events.length - 1];
const seqCompare = getSeq(bLast) - getSeq(aLast);
if (seqCompare !== 0) return seqCompare; if (seqCompare !== 0) return seqCompare;
return a.index - b.index; const timeCompare =
getTimestampMs(bLast.ts ?? bLast.timestamp) - getTimestampMs(aLast.ts ?? aLast.timestamp);
if (timeCompare !== 0) return timeCompare;
return b.runId.localeCompare(a.runId);
}); });
const groups: StrategyRun[] = [];
for (const { entry } of indexed) {
const runId = getRunId(entry);
const last = groups[groups.length - 1];
if (!last || last.runId !== runId) {
groups.push({ runId, events: [entry] });
} else {
last.events.push(entry);
}
}
return groups; return groups;
}, [logs]); }, [logs]);
@ -316,7 +334,7 @@ function VerboseStrategyTimeline() {
</span> </span>
<div className="flex-1"> <div className="flex-1">
<div className="text-xs text-green-300/80"> <div className="text-xs text-green-300/80">
{formatTimestamp(timestamp)} {entry.event ?? "UNKNOWN"} {formatTimestamp(timestamp)} - {entry.event ?? "UNKNOWN"}
</div> </div>
{message && message !== entry.event ? ( {message && message !== entry.event ? (
<div className="text-sm text-green-200">{message}</div> <div className="text-sm text-green-200">{message}</div>