Files
avbeat-com/src/app/nab-2026/page.tsx
Ryan Salazar d67a2bd48d AV Beat rebrand: theme swap (orange #E67E22 / near-black), logo, schema default → avb
- public/assets/images/logo.png + public/brand/logo*.png replaced with the 1320x310 AV Beat logo
- public/assets/logos/avbeat.png added as the canonical source
- tailwind.config.js + src/styles/tailwind.css + src/styles/redesign-tokens.css palette swapped (primary #0F0E0E, secondary #1D1A1A, accent #E67E22 orange, foreground #F0EBE6 warm white)
- Layout / robots / sitemap / home-page / about / contact / press-kit / team / global-error metadata switched to AV Beat (Where AV Meets IT)
- Bulk replaced 'Broadcast Beat'/'BroadcastBeat.com'/'broadcastbeat.com' across user-facing news/forum/marketplace/advertise/search/newsletter pages
- src/lib/supabase/{admin,server,client} now default to schema 'avb' (was 'bb') — overridable via NEXT_PUBLIC_SUPABASE_SCHEMA
- package.json renamed to 'avbeat'
- Single d60701 reference in about/team swapped to E67E22

Design-only replica — no article content migrated.
2026-06-01 15:30:16 +00:00

235 lines
9.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { Metadata } from "next";
import Link from "next/link";
import Header from "@/components/Header";
import Footer from "@/components/Footer";
import { createClient } from "@/lib/supabase/server";
import { createAdminClient } from "@/lib/supabase/admin";
import CompanyMentionsHover from "@/components/CompanyMentionsHover";
export const revalidate = 600;
export const metadata: Metadata = {
title: "2026 NAB Show — Live Coverage Hub | AV Beat",
description:
"AV Beat's live coverage of 2026 NAB Show: 1,100+ exhibitors, the latest product launches, booth-by-booth news, and editorial pick stories from the show floor.",
alternates: { canonical: "/nab-2026" },
openGraph: {
title: "2026 NAB Show — Live Coverage Hub",
description:
"Live coverage, exhibitor list, and breaking product news from 2026 NAB Show.",
type: "website",
url: "https://avbeat.com/nab-2026",
},
};
type ExhibitorRow = {
slug: string;
company_name: string;
logo_url: string | null;
featured: boolean;
mention_count: number;
categories: string[] | null;
};
type ArticleRow = {
wp_slug: string;
title: string;
excerpt: string | null;
featured_image: string | null;
wp_published_at: string | null;
};
export default async function NabHub() {
const supabase = await createClient();
const { data: rawExhibitors } = await supabase
.from("tracked_companies")
.select("slug, company_name, logo_url, featured, mention_count, categories")
.eq("directory_visible", true)
.eq("exhibits_nab", true)
.not("slug", "is", null)
.order("featured", { ascending: false })
.order("mention_count", { ascending: false })
.order("company_name", { ascending: true })
.limit(2000);
const exhibitors = (rawExhibitors || []) as ExhibitorRow[];
// Sponsor lookup so the hub can bubble paying advertisers to the top.
let sponsors = new Set<string>();
try {
const adv = createAdminClient("adv");
const { data: ads } = await adv
.from("active_advertisers_by_property")
.select("company_name_lower")
.eq("property", "broadcastbeat");
sponsors = new Set((ads || []).map((r: any) => String(r.company_name_lower)));
} catch { /* tolerate */ }
// Group exhibitors: sponsors first, then everything else alphabetically.
const sponsorRows = exhibitors.filter((e) => sponsors.has(e.company_name.toLowerCase()));
const otherRows = exhibitors
.filter((e) => !sponsors.has(e.company_name.toLowerCase()))
.sort((a, b) => a.company_name.localeCompare(b.company_name));
// Recent NAB stories — anything published in the last 60 days with NAB in
// the title or tags. Cap at 30.
const sixtyDaysAgo = new Date(Date.now() - 60 * 24 * 3600 * 1000).toISOString();
const { data: rawArticles } = await supabase
.from("wp_imported_posts")
.select("wp_slug, title, excerpt, featured_image, wp_published_at")
.eq("status", "published")
.or("title.ilike.%NAB%,title.ilike.%NAB 2026%")
.gte("wp_published_at", sixtyDaysAgo)
.order("wp_published_at", { ascending: false })
.limit(30);
const articles = (rawArticles || []) as ArticleRow[];
return (
<div className="min-h-screen bg-[#0d0d0d]">
<Header />
<CompanyMentionsHover />
{/* Hero */}
<section className="bg-gradient-to-br from-amber-500/10 to-[#0d0d0d] border-b border-[#252525]">
<div className="max-w-6xl mx-auto px-4 py-10">
<div className="flex items-baseline gap-3 mb-2">
<span className="text-[10px] font-bold uppercase tracking-widest bg-amber-500/20 text-amber-300 px-2 py-0.5 rounded">
Live coverage hub
</span>
<span className="text-xs text-[#888]">April 1116, 2026 · Las Vegas</span>
</div>
<h1 className="text-3xl md:text-5xl font-display font-bold text-[#e0e0e0] mb-3">
2026 NAB Show
</h1>
<p className="text-[#aaa] text-base max-w-3xl">
{exhibitors.length.toLocaleString()} exhibitors confirmed.
{sponsorRows.length > 0 && <> {sponsorRows.length} are AV Beat content partners.</>}
{" "}Browse the directory, read the latest product news, and find every booth.
</p>
</div>
</section>
<div className="max-w-6xl mx-auto px-4 py-10 space-y-12">
{/* Featured / Content Partners */}
{sponsorRows.length > 0 && (
<section>
<header className="flex items-baseline justify-between mb-4">
<h2 className="text-xl font-display font-bold text-[#e0e0e0]">
Content Partners at the 2026 NAB Show
</h2>
<span className="text-xs text-[#888]">{sponsorRows.length} exhibiting partner{sponsorRows.length === 1 ? "" : "s"}</span>
</header>
<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-6 gap-3">
{sponsorRows.map((e) => (
<Link
key={e.slug}
href={`/manufacturers/${e.slug}`}
data-company-slug={e.slug}
className="bg-[#0d0d0d] border-2 border-emerald-500/40 rounded-lg p-3 hover:border-emerald-500/70 hover:shadow-md transition-all flex flex-col items-center text-center gap-2"
>
<div className="w-full h-16 flex items-center justify-center bg-[#1a1a1a] rounded">
{e.logo_url ? (
/* eslint-disable-next-line @next/next/no-img-element */
<img src={e.logo_url} alt={`${e.company_name} logo`} className="max-h-12 max-w-full object-contain" loading="lazy" />
) : (
<span className="text-2xl font-bold text-[#666]">{e.company_name.charAt(0).toUpperCase()}</span>
)}
</div>
<span className="font-semibold text-[#e0e0e0] text-xs truncate w-full">{e.company_name}</span>
<span className="text-[9px] font-bold uppercase tracking-wider bg-emerald-100 text-emerald-300 px-1.5 py-0.5 rounded">Sponsor</span>
</Link>
))}
</div>
</section>
)}
{/* Recent NAB-related stories */}
{articles.length > 0 && (
<section>
<header className="flex items-baseline justify-between mb-4">
<h2 className="text-xl font-display font-bold text-[#e0e0e0]">
Latest from the show floor
</h2>
<Link href="/news?search=NAB+2026" className="text-xs text-[#E67E22] hover:underline">
See all NAB coverage
</Link>
</header>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{articles.slice(0, 9).map((a) => (
<Link
key={a.wp_slug}
href={`/news/${a.wp_slug}`}
className="group bg-[#0d0d0d] border border-[#252525] rounded-lg overflow-hidden hover:border-[#E67E22] hover:shadow-sm transition-all"
>
{a.featured_image && (
/* eslint-disable-next-line @next/next/no-img-element */
<img
src={a.featured_image}
alt=""
className="w-full aspect-video object-cover group-hover:scale-[1.02] transition-transform"
loading="lazy"
/>
)}
<div className="p-3">
<h3 className="font-semibold text-[#e0e0e0] text-sm leading-snug group-hover:text-[#E67E22] line-clamp-3 mb-1">
{a.title}
</h3>
{a.wp_published_at && (
<p className="text-[10px] text-[#888]">
{new Date(a.wp_published_at).toLocaleDateString("en-US", { month: "short", day: "numeric" })}
</p>
)}
</div>
</Link>
))}
</div>
</section>
)}
{/* Full exhibitor list */}
<section>
<header className="flex items-baseline justify-between mb-4">
<h2 className="text-xl font-display font-bold text-[#e0e0e0]">
All 2026 NAB Show exhibitors
</h2>
<Link href="/manufacturers" className="text-xs text-[#E67E22] hover:underline">
Full directory
</Link>
</header>
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-3">
{otherRows.slice(0, 200).map((e) => (
<Link
key={e.slug}
href={`/manufacturers/${e.slug}`}
data-company-slug={e.slug}
className="bg-[#0d0d0d] border border-[#252525] rounded-md p-2.5 hover:border-[#E67E22] transition-all flex items-center gap-2.5"
>
{e.logo_url ? (
/* eslint-disable-next-line @next/next/no-img-element */
<img src={e.logo_url} alt="" className="w-10 h-10 rounded bg-[#1a1a1a] object-contain flex-shrink-0" loading="lazy" />
) : (
<div className="w-10 h-10 rounded bg-[#1a1a1a] flex items-center justify-center text-[#666] font-bold text-sm flex-shrink-0">
{e.company_name.charAt(0).toUpperCase()}
</div>
)}
<span className="font-medium text-[#e0e0e0] text-xs truncate">{e.company_name}</span>
</Link>
))}
</div>
{otherRows.length > 200 && (
<p className="text-center text-xs text-[#888] mt-4">
Showing 200 of {otherRows.length.toLocaleString()} exhibitors.{" "}
<Link href="/manufacturers" className="text-[#E67E22] hover:underline">Browse the full directory </Link>
</p>
)}
</section>
</div>
<Footer />
</div>
);
}