import Link from "next/link"; import { createClient } from "@supabase/supabase-js"; export const dynamic = "force-dynamic"; export const revalidate = 600; interface Row { id: string; slug: string; name: string; short_name: string | null; start_date: string; end_date: string; venue: string | null; city: string | null; country: string | null; url: string | null; status: string; category: string | null; } function fmtRange(s: string, e: string): string { const a = new Date(s); const b = new Date(e); const sameYear = a.getUTCFullYear() === b.getUTCFullYear(); const sameMo = a.getUTCMonth() === b.getUTCMonth() && sameYear; const f = (d: Date) => d.toLocaleDateString("en-US", { month: "short", day: "numeric", timeZone: "UTC" }); if (sameMo) return `${f(a)}–${b.getUTCDate()}, ${b.getUTCFullYear()}`; if (sameYear) return `${f(a)} – ${f(b)}, ${b.getUTCFullYear()}`; return `${f(a)} ${a.getUTCFullYear()} – ${f(b)} ${b.getUTCFullYear()}`; } function pillStatus(s: string) { const map: Record = { confirmed: "bg-emerald-500/10 text-emerald-400 border-emerald-500/30", tentative: "bg-amber-500/10 text-amber-400 border-amber-500/30", cancelled: "bg-zinc-500/10 text-zinc-400 border-zinc-500/30", }; return `inline-flex items-center text-[10px] font-mono uppercase tracking-wider px-2 py-0.5 rounded-full border ${map[s] || map.tentative}`; } export default async function ShowCoverageIndex() { const url = process.env.NEXT_PUBLIC_SUPABASE_URL!; const key = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const schema = process.env.NEXT_PUBLIC_SUPABASE_SCHEMA || "bb"; const sb = createClient(url, key, { db: { schema: schema as "public" }, auth: { persistSession: false }, }); const { data } = await sb .from("events") .select("id,slug,name,short_name,start_date,end_date,venue,city,country,url,status,category") .in("status", ["confirmed", "tentative"]) .order("start_date", { ascending: true }); const events = (data || []) as Row[]; const today = new Date().toISOString().slice(0, 10); const upcoming = events.filter((e) => e.end_date >= today); const past = events.filter((e) => e.end_date < today); return (

Show coverage

{upcoming.length} upcoming · {past.length} past · {events.length} total

Upcoming

{upcoming.length === 0 ? (

No upcoming events.

) : (
    {upcoming.map((e) => ( ))}
)}
{past.length > 0 && (

Past

    {past.map((e) => ( ))}
)}
); } function EventCard({ e }: { e: Row }) { return (
  • {e.name}

    {e.status}
    {[e.venue, e.city, e.country].filter(Boolean).join(" · ")}
    {fmtRange(e.start_date, e.end_date)} {e.url && ( <> {" · "} {new URL(e.url).hostname.replace(/^www\./, "")} )}
  • ); } export const metadata = { title: "Show coverage — industry events tracked by BroadcastBeat", description: "All upcoming and past broadcast / production industry events tracked by BroadcastBeat, with linked coverage.", };