import { createClient } from "@supabase/supabase-js"; import Header from "@/components/Header"; import Footer from "@/components/Footer"; import SidebarAdStack from "@/components/SidebarAdStack"; import ObfuscatedEmail from "@/components/ObfuscatedEmail"; export const dynamic = "force-dynamic"; export const revalidate = 600; export const metadata = { title: "Our team — AV Beat", description: "Meet the AV Beat team — editorial staff plus accounts and support, covering pro AV, live production, and display tech.", }; interface Persona { slug: string; name: string; bio: string | null; beat: string | null; avatar_url: string | null; } interface OpsPersona { id: string; display_name: string; role: string; email: string | null; phone: string | null; extension: string | null; avatar_url: string | null; } export default async function TeamPage() { const sb = createClient( process.env.NEXT_PUBLIC_SUPABASE_URL!, process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!, { db: { schema: (process.env.NEXT_PUBLIC_SUPABASE_SCHEMA || "bb") as "public" }, auth: { persistSession: false }, } ); const { data } = await sb .from("ai_personas") .select("slug, name, bio, beat, avatar_url") .eq("active", true); const raw = (data || []) as Persona[]; const team = [ ...raw.filter((p) => p.slug === "ryan-salazar"), ...raw .filter((p) => p.slug !== "ryan-salazar") .sort((a, b) => a.name.localeCompare(b.name)), ]; const sbOps = createClient( process.env.NEXT_PUBLIC_SUPABASE_URL!, process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!, { db: { schema: "pulsedesk" as "public" }, auth: { persistSession: false } } ); const { data: opsData } = await sbOps .from("personas") .select("id, display_name, role, email, phone, extension, avatar_url") .eq("active", true) .in("id", ["chloe", "jeff", "riley", "brian", "avery"]); const ops = (opsData || []) as OpsPersona[]; const displayBeat = (p: Persona) => p.slug === "ryan-salazar" ? "Founder / Editor-in-Chief" : p.beat?.replace(/-/g, " ") ?? ""; const formatPhone = (raw: string): string => { const digits = raw.replace(/[^0-9]/g, ""); const local = digits.startsWith("1") && digits.length === 11 ? digits.slice(1) : digits; if (local.length !== 10) return raw; return `(${local.slice(0, 3)}) ${local.slice(3, 6)}-${local.slice(6)}`; }; const editorialEmail = (p: Persona): { user: string; domain: string } | null => { if (!p.slug) return null; return { user: p.slug.replace(/-/g, "."), domain: "avbeat.com" }; }; const splitEmail = (email: string): { user: string; domain: string } | null => { const at = email.indexOf("@"); if (at <= 0) return null; return { user: email.slice(0, at), domain: email.slice(at + 1) }; }; const tableWrap = "overflow-x-auto rounded-xl border border-[#1a1f2a] bg-[#0b0f17]"; const tableClass = "w-full min-w-[640px] text-sm text-left border-collapse"; const thClass = "px-4 py-3 text-[10px] font-mono uppercase tracking-[0.14em] text-[#93C5FD] bg-[#141a25] border-b border-[#1a1f2a] whitespace-nowrap"; const tdClass = "px-4 py-4 align-top border-b border-[#1a1f2a] text-[#aab1bd]"; const trHover = "hover:bg-[#111827]/60 transition-colors"; return (
AV Beat · editorial

The team behind every byline

The writers, editors, and operators behind the AV Beat newsroom — covering pro AV, live production, and display tech every day.

Editorial

{team.length} {team.length === 1 ? "writer" : "writers"}
{team.map((p) => { const e = editorialEmail(p); return ( ); })}
Name Role Focus Contact
{p.name} {(p.beat || p.slug === "ryan-salazar") && ( {displayBeat(p)} )} {p.bio ? (

{p.bio}

) : ( )}
{e && ( )} Profile →
{ops.length > 0 && (

Accounts & support

{ops.length} {ops.length === 1 ? "contact" : "contacts"}

For billing, accounts receivable, vendor onboarding, sales, and general support questions — these are the people to reach.

{ops.map((p) => ( ))}
Name Role Email Phone
{p.display_name} {p.role} {p.email ? ( (() => { const e = splitEmail(p.email!); if (!e) return null; return ( ); })() ) : ( )} {p.phone ? ( {formatPhone(p.phone)} {p.extension ? `, Ext. ${p.extension}` : ""} ) : ( )}
)}
); }