import { Fragment } from "react"; import { Box, Group, Stack, Text } from "@mantine/core"; import { Plural, useLingui } from "@lingui/react/macro"; import { useSessionTimeline } from "../../queries"; import type { TimelineEntry } from "../../types"; const GAP_MARKER_MS = 5 * 60 * 1000; const parseTs = (ts: string) => Date.parse(ts.replace(" ", "T")); const dotColor = (entry: TimelineEntry): string => { if (entry.kind === "error" || entry.success === false) return "var(--mantine-color-red-6)"; if (entry.kind === "server_fn") return "var(--mantine-color-green-6)"; return "var(--mantine-color-dimmed)"; }; const Dot = ({ color }: { color: string }) => ( ); export const SessionTimelineView = ({ sessionId }: { sessionId: string }) => { const { i18n } = useLingui(); const { data } = useSessionTimeline(sessionId); const clock = (ts: string) => i18n.date(new Date(parseTs(ts)), { hour: "2-digit", minute: "2-digit", second: "2-digit" }); return ( {i18n.date(new Date(parseTs(data.session.started)), { month: "short", day: "numeric", hour: "2-digit", minute: "2-digit", })} {data.session.errors > 0 && ( )} {data.entries.map((entry, index) => { const prev = data.entries[index - 1]; const gapMs = prev ? parseTs(entry.ts) - parseTs(prev.ts) : 0; return ( {gapMs > GAP_MARKER_MS && ( +{Math.round(gapMs / 60000)}m )} {clock(entry.ts)} {entry.label} {entry.detail && entry.kind !== "error" && ( {" "} · {entry.detail} )} {entry.kind === "server_fn" && entry.success === false && ( ✗ )} {entry.kind === "server_fn" && typeof entry.duration === "number" && entry.duration > 0 && ( {entry.duration}ms )} ); })} ); }; export default SessionTimelineView;