import Link from "next/link"; import type { Metadata } from "next"; import { EventRow } from "@/components/event-row"; import { Badge, Bar, Chip, Empty, Flag, PageHeader, Panel, Score, StateBadge, Table, Td, Tabs, TierBadge } from "@/components/ui"; import { api, type Cluster, type EventItem } from "@/lib/api"; import { CHANNELS, SAVED_VIEWS, feedHref, fmtInt, fmtOffset, fmtScore, relTime, typeLabel } from "@/lib/format"; export const dynamic = "force-dynamic"; const TABS = [ { key: "trending", label: "Trending" }, { key: "breaking", label: "Breaking" }, { key: "silent", label: "Silent" }, { key: "newly", label: "Newly detected" }, { key: "active", label: "Active sources" }, { key: "unusual", label: "Unusual activity" }, { key: "clusters", label: "Clusters" }, { key: "entities", label: "Entities" }, { key: "sources", label: "Sources" }, { key: "categories", label: "Categories" }, { key: "countries", label: "Countries" }, ] as const; type TabKey = (typeof TABS)[number]["key"]; function tabOf(v: string | undefined): TabKey { return (TABS.find((t) => t.key === v)?.key ?? "trending") as TabKey; } export async function generateMetadata({ searchParams }: { searchParams: Promise<{ tab?: string }> }): Promise { const { tab } = await searchParams; const t = TABS.find((x) => x.key === tabOf(tab))!; return { title: `Discover · ${t.label}`, description: "Trending entities, breaking clusters, silent changes, newly detected signals, active and anomalous sources, clusters, entities, sources, categories and countries.", alternates: { canonical: t.key === "trending" ? "/explore" : `/explore?tab=${t.key}` } }; } /** Discover page (spec §28): URL-driven tabs over the derived views of the event store. */ export default async function ExplorePage({ searchParams }: { searchParams: Promise<{ tab?: string }> }) { const sp = await searchParams; const tab = tabOf(sp.tab); const [x, trending, desk, rank, sources, countries] = await Promise.all([ api.explore(), tab === "trending" ? api.trending(24, 30) : Promise.resolve({ items: [] }), tab === "breaking" ? api.breakingDesk() : Promise.resolve(null), tab === "entities" ? api.rank(50) : Promise.resolve({ items: [] }), tab === "sources" ? api.sources({}) : Promise.resolve({ items: [] }), tab === "countries" ? api.countries() : Promise.resolve({ items: [] }), ]); const counts: Partial> = { trending: tab === "trending" ? trending.items.length : undefined, breaking: desk ? desk.breaking_now.length + desk.developing.length + desk.recently_confirmed.length : undefined, silent: x?.silent_changes?.length, newly: x?.newly_detected?.length, active: x?.most_active_sources?.length, unusual: x?.unusual_activity?.length, clusters: x?.clusters?.length, entities: tab === "entities" ? rank.items.length : undefined, sources: tab === "sources" ? sources.items.length : undefined, categories: x?.by_category?.length, countries: tab === "countries" ? countries.items.length : undefined, }; return ( <> ({ key: t.key, label: t.label, href: t.key === "trending" ? "/explore" : `/explore?tab=${t.key}`, count: counts[t.key] }))} />
Saved views {SAVED_VIEWS.map((v) => ( {v.label} ))}
{tab === "trending" && } {tab === "breaking" && } {tab === "silent" && ( all silent →}> {x?.silent_changes?.length ? x.silent_changes.map((e) => ) : No silent change detected in this window.} )} {tab === "newly" && ( live →}> {x?.newly_detected?.length ? x.newly_detected.map((e) => ) : Nothing newly detected in this window.} )} {tab === "active" && } {tab === "unusual" && } {tab === "clusters" && } {tab === "entities" && } {tab === "sources" && } {tab === "categories" && } {tab === "countries" && } ); } // --------------------------------------------------------------------------------------- function Dir({ d }: { d?: "up" | "down" | "flat" }) { const c = d === "up" ? "text-signal" : d === "down" ? "text-fg-subtle" : "text-fg-muted"; return {d === "up" ? "↑" : d === "down" ? "↓" : "→"}; } function TrendingTab({ items }: { items: Awaited>["items"] }) { return ( score = volume × acceleration × signal, vs. the previous 24 h}> {items.length === 0 ? ( Trending is computed from the last 24 h of events. ) : ( {items.map((t, i) => ( ))}
{i + 1} {t.name}
{t.type}{t.domain ? ` · ${t.domain}` : ""}
{fmtInt(t.events)} / {fmtInt(t.prev_events)} {t.sources} {t.first_party ?? 0} {t.silent} {fmtScore(t.avg_signal)}
= 80 ? "hot" : t.score >= 60 ? "high" : "signal"} /> {fmtScore(t.score)}
)}
); } function ClusterRows({ items, empty }: { items: Cluster[]; empty: string }) { if (!items.length) return {empty}; return (
    {items.map((c) => { const e = c.event as EventItem | null | undefined; return (
  • {(e?.source ?? c.source) && {(e?.source ?? c.source)?.name}} {e?.country && } {relTime(c.last_at)}
    {c.title}
    {c.event_count} signal{c.event_count === 1 ? "" : "s"} · {c.source_count ?? 1} source{(c.source_count ?? 1) === 1 ? "" : "s"} {(c.first_party_count ?? 0) > 0 && {c.first_party_count} first-party} {(c.external_count ?? 0) > 0 && {c.external_count} external} {c.lead_time_ms && c.lead_time_ms > 0 ? lead {fmtOffset(c.lead_time_ms).replace("+", "")} : null} {c.categories?.slice(0, 2).map((k) => {k})}
  • ); })}
); } function BreakingTab({ desk }: { desk: Awaited> }) { const d = desk ?? { breaking_now: [], developing: [], recently_confirmed: [], watching: [] }; return (
Breaking now {d.breaking_now.length}} dense action={desk →}> Developing {d.developing.length}} dense> Recently confirmed {d.recently_confirmed.length}} dense> {d.watching.length ? d.watching.slice(0, 12).map((e) => ) : Nothing to watch.}
); } function ActiveTab({ items }: { items: NonNullable>>["most_active_sources"] }) { const max = Math.max(1, ...items.map((s) => s.events_24h)); return ( all sources →}> {items.length === 0 ? ( ) : ( {items.map((s, i) => ( ))}
{i + 1} {s.name} {s.domain} {(s as { first_party?: boolean }).first_party === false ? : } {fmtInt(s.events_24h)}
)}
); } function UnusualTab({ items }: { items: NonNullable>>["unusual_activity"] }) { return ( radar →}> {items.length === 0 ? ( Activity anomalies compare the last 2 h of raw changes with a 14-day baseline. Nothing is far above its baseline right now. ) : ( {items.map((s) => { const tone = s.activity_score >= 70 ? "hot" : s.activity_score >= 45 ? "high" : "signal"; return ( ); })}
{s.name} {s.domain} {s.changes_2h} {s.baseline_per_day} {s.pct_vs_baseline !== null && s.pct_vs_baseline !== undefined ? `+${Math.min(9999, Math.round(s.pct_vs_baseline)).toLocaleString("en-US")}%` : "—"}
{fmtScore(s.activity_score)}
)}
); } function ClustersTab({ items }: { items: Cluster[] }) { return ( breaking desk →}> ); } function EntitiesTab({ items }: { items: Awaited>["items"] }) { return ( all entities →}> {items.length === 0 ? ( Rankings appear once entities accumulate events. ) : ( {items.map((e) => ( ))}
{e.rank} {e.name} {e.domain &&
{e.domain}
}
{e.type.replace(/_/g, " ")} {fmtInt(e.events_24h)} {fmtInt(e.events_7d)} {e.baseline_per_day.toFixed(1)} {e.sources} {e.silent_24h} {e.breaking_24h} {fmtScore(e.avg_signal)} {Math.round(e.confirmed_ratio * 100)}% {relTime(e.last_at)}
= 80 ? "hot" : e.rank_score >= 60 ? "high" : "signal"} /> {fmtScore(e.rank_score)}
)}
); } function SourcesTab({ items }: { items: Awaited>["items"] }) { const top = [...items].sort((a, b) => (b.events_24h ?? 0) - (a.events_24h ?? 0)).slice(0, 50); const max = Math.max(1, ...top.map((s) => s.events_24h ?? 0)); return ( all sources →}> {top.length === 0 ? ( ) : ( {top.map((s) => ( ))}
{s.name}
{s.domain}
{s.first_party === false ? : }
{s.categories.slice(0, 3).map((c) => {c})}
{s.sensor_count ?? 0} {fmtInt(s.events_24h ?? 0)}
{fmtInt(s.event_count)} {s.last_event_at ? relTime(s.last_event_at) : "—"}
)}
); } function CategoriesTab({ byCategory, byType, labels }: { byCategory: { category: string; n: number }[]; byType: { event_type: string; n: number }[]; labels: Record }) { const maxCat = Math.max(1, ...byCategory.map((t) => t.n)); const maxType = Math.max(1, ...byType.map((t) => t.n)); const channelOf = (c: string): string | undefined => CHANNELS.find((ch) => ch.query.category === c)?.label; return (
{byCategory.length ? (
    {byCategory.slice(0, 30).map((t) => (
  • {t.category} {fmtInt(t.n)}
  • ))}
) : ( )}
{byType.length ? (
    {byType.slice(0, 30).map((t) => (
  • {typeLabel(t.event_type)} {fmtInt(t.n)}
  • ))}
) : ( )}
); } function CountriesTab({ items }: { items: Awaited>["items"] }) { return ( country desks →}> {items.length === 0 ? ( Country desks appear once sources carry a country. ) : (
    {items.map((c) => (
  • {c.flag || } {c.name} {c.country} · {c.sources} source{c.sources === 1 ? "" : "s"} {fmtInt(c.events_24h)} 24 h {fmtInt(c.breaking_24h)} brk
  • ))}
)}
); }