import Link from "next/link"; import type { Cluster, EventItem, TrendingItem } from "@/lib/api"; import { fmtOffset, fmtScore, relTime } from "@/lib/format"; import { Badge, Chip, Empty, Mark, Panel, Score, StateLabelText } from "./ui"; export function TrendingPanel({ items, title = "Trending entities", hours = 24 }: { items: TrendingItem[]; title?: string; hours?: number }) { return ( all →}> {items.length === 0 ? ( Trending is computed from the last {hours} h of events. ) : (
    {items.map((t, i) => (
  1. {i + 1}
    {t.name}
    {t.events} signals · {t.sources} source{t.sources === 1 ? "" : "s"}{t.first_party ? ` · ${t.first_party} first-party` : ""}{t.silent ? ` · ${t.silent} silent` : ""}
    {t.direction === "up" ? "↑" : t.direction === "down" ? "↓" : "→"} {fmtScore(t.score)}
  2. ))}
)}
); } export function ClustersPanel({ items, title = "Event clusters" }: { items: Cluster[]; title?: string }) { const multi = items.filter((c) => c.event_count > 1).slice(0, 8); return ( all →}> {multi.length === 0 ? ( Related observations are grouped into clusters as they arrive. ) : ( )} ); } /** Breaking now rail (spec §94): primary event of each breaking / developing cluster. */ export function BreakingRail({ items, developing }: { items: Cluster[]; developing?: Cluster[] }) { const all = [...items.map((c) => ({ c, state: "breaking" })), ...(developing ?? []).map((c) => ({ c, state: "developing" }))].slice(0, 8); return ( Breaking now} dense action={desk →}> {all.length === 0 ? ( Nothing is breaking right now. Breaking requires strong signal, freshness and confirmation — not just recency. ) : ( )} ); } export function AnomalyRail({ items }: { items: { id: string; name: string; domain: string; changes_2h: number; baseline_per_day: number; activity_score: number; pct_vs_baseline?: number | null }[] }) { const top = items.filter((s) => s.activity_score >= 45).slice(0, 6); return ( radar →}> {top.length === 0 ? ( No source is far above its baseline right now. ) : ( )} ); } export function SilentRail({ items }: { items: EventItem[] }) { return ( Silent changes} dense action={all →}> {items.length === 0 ? ( No silent change in this window. ) : ( )} ); }