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;
};
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 {
if (typeof log === "string") {
try {
@ -49,25 +57,28 @@ function normalizeLog(log: unknown): StrategyEvent {
return { message: "Unknown log entry" };
}
function formatTimestamp(value?: string) {
function formatTimestamp(value?: string | null) {
if (!value) return "Unknown time";
const hasTimezone = /Z|[+-]\d{2}:?\d{2}$/.test(value);
const normalized = hasTimezone ? value : `${value}Z`;
const parsed = new Date(normalized);
if (Number.isNaN(parsed.getTime())) return value;
const parsed = parseTimestamp(value);
if (!parsed) return value;
return parsed.toLocaleString();
}
function formatCompactTimestamp(value?: string | null) {
if (!value) return null;
const parsed = new Date(value);
if (Number.isNaN(parsed.getTime())) return null;
return parsed.toLocaleTimeString([], {
const parsed = parseTimestamp(value);
if (!parsed) return null;
return parsed.toLocaleString([], {
month: "short",
day: "numeric",
hour: "numeric",
minute: "2-digit",
});
}
function getTimestampMs(value?: string | null) {
return parseTimestamp(value)?.getTime() ?? 0;
}
function getRunId(entry: StrategyEvent) {
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="flex flex-col gap-1 md:flex-row md:items-center md:justify-between">
<span>{message}</span>
{updatedAt ? (
<span className="text-xs opacity-80">Updated {updatedAt}</span>
) : null}
{updatedAt ? <span className="text-xs opacity-80">Updated {updatedAt}</span> : null}
</div>
</div>
);
@ -228,25 +237,34 @@ function VerboseStrategyTimeline() {
}, []);
const groupedRuns = useMemo<StrategyRun[]>(() => {
const indexed = logs.map((entry, index) => ({ entry, index }));
indexed.sort((a, b) => {
const runCompare = getRunId(b.entry).localeCompare(getRunId(a.entry));
if (runCompare !== 0) return runCompare;
const seqCompare = getSeq(a.entry) - getSeq(b.entry);
const grouped = new Map<string, StrategyEvent[]>();
for (const entry of logs) {
const runId = getRunId(entry);
const events = grouped.get(runId) ?? [];
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 a.index - b.index;
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;
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;
}, [logs]);
@ -316,7 +334,7 @@ function VerboseStrategyTimeline() {
</span>
<div className="flex-1">
<div className="text-xs text-green-300/80">
{formatTimestamp(timestamp)} {entry.event ?? "UNKNOWN"}
{formatTimestamp(timestamp)} - {entry.event ?? "UNKNOWN"}
</div>
{message && message !== entry.event ? (
<div className="text-sm text-green-200">{message}</div>