Fix strategy timeline timestamp handling
This commit is contained in:
parent
5577de7b9e
commit
a22e8d7ee8
@ -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>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user