TypeScript 55.4%
Python 43.2%
SQL 1.2%
1import Link from "next/link";2import type { EventItem } from "@/lib/api";3import { dayHeader, utcDate, utcTime, typeLabel } from "@/lib/format";4import { Empty, Score, SilentBadge } from "./ui";56/** Git-log style timeline grouped by UTC day (spec §21). */7export function Timeline({ events, showSource = false }: { events: EventItem[]; showSource?: boolean }) {8 if (!events.length) return <Empty>No events recorded yet.</Empty>;9 const groups = new Map<string, EventItem[]>();10 for (const e of events) {11 const d = utcDate(e.detected_at);12 if (!groups.has(d)) groups.set(d, []);13 groups.get(d)!.push(e);14 }15 return (16 <div>17 {[...groups.entries()].map(([day, evs]) => (18 <div key={day}>19 <div className="sticky top-12 z-10 border-y border-line bg-panel-2/95 px-3 py-1 font-mono text-[11px] font-semibold tracking-wider text-fg-muted backdrop-blur">{dayHeader(evs[0]!.detected_at)}</div>20 {evs.map((e) => (21 <div key={e.id} className="grid grid-cols-[3.2rem_1fr_auto] items-start gap-x-3 px-3 py-1.5 hairline text-[13px]">22 <span className="font-mono text-[12px] text-fg-subtle tabular">{utcTime(e.detected_at, false)}</span>23 <div className="min-w-0">24 <div className="flex flex-wrap items-center gap-x-2 text-[11px] text-fg-subtle">25 {showSource && <span className="font-mono uppercase text-fg-muted">{e.source?.name}</span>}26 <span>{typeLabel(e.event_type)}</span>27 {e.silent_change && <SilentBadge compact />}28 </div>29 <Link href={`/event/${e.slug}`} className="block truncate font-medium hover:underline">{e.title}</Link>30 </div>31 <Score value={e.importance} size="sm" />32 </div>33 ))}34 </div>35 ))}36 </div>37 );38}39