technology: dynamic header + filter for ?category=ai/cloud/ip-workflows/streaming/atsc-3; add sub-category chip row
This commit is contained in:
@@ -10,22 +10,71 @@ import { getLegacyArticlesBySection } from "@/lib/articles/legacy-source";
|
||||
|
||||
export const revalidate = 1800;
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Production Technology Deep Dives — Broadcast Beat",
|
||||
description:
|
||||
"Deep-dive analysis of broadcast technology trends: IP workflows, cloud production, AI automation, streaming infrastructure, and ATSC 3.0.",
|
||||
alternates: { canonical: "/technology" },
|
||||
openGraph: {
|
||||
title: "Production Technology Deep Dives — Broadcast Beat",
|
||||
description:
|
||||
"Deep-dive analysis of broadcast technology trends: IP workflows, cloud production, AI automation, streaming infrastructure, and ATSC 3.0.",
|
||||
url: "/technology",
|
||||
type: "website",
|
||||
// Category sub-filters keyed off the ?category= query string. Each one
|
||||
// pairs a display title with the keywords used to match an article's
|
||||
// category, tags, or title (substring, case-insensitive).
|
||||
const CATEGORY_FILTERS: Record<string, { title: string; keywords: string[]; description: string }> = {
|
||||
ai: {
|
||||
title: "AI & Automation",
|
||||
keywords: ["ai", "artificial intelligence", "automation", "machine learning", "ml"],
|
||||
description: "Coverage of AI and automation in broadcast — generative tools, ML-driven workflows, automated playout, and editorial-grade AI.",
|
||||
},
|
||||
cloud: {
|
||||
title: "Cloud Production",
|
||||
keywords: ["cloud"],
|
||||
description: "Cloud-native production, remote workflows, hybrid pipelines, and SaaS-delivered broadcast infrastructure.",
|
||||
},
|
||||
"ip-workflows": {
|
||||
title: "IP Workflows",
|
||||
keywords: ["ip", "st 2110", "smpte 2110", "nmos"],
|
||||
description: "ST 2110, NMOS, IP infrastructure, and the migration off SDI.",
|
||||
},
|
||||
streaming: {
|
||||
title: "Streaming",
|
||||
keywords: ["streaming", "ott", "fast", "vod"],
|
||||
description: "OTT, FAST, VOD, low-latency streaming, and direct-to-consumer delivery.",
|
||||
},
|
||||
"atsc-3": {
|
||||
title: "ATSC 3.0 / NextGen TV",
|
||||
keywords: ["atsc", "nextgen"],
|
||||
description: "ATSC 3.0 deployments, 5G Broadcast, datacasting, and the U.S. terrestrial transition.",
|
||||
},
|
||||
};
|
||||
|
||||
export default async function TechnologyPage() {
|
||||
const articles = await getLegacyArticlesBySection("technology");
|
||||
interface PageProps {
|
||||
searchParams?: Promise<{ category?: string }>;
|
||||
}
|
||||
|
||||
export async function generateMetadata({ searchParams }: PageProps): Promise<Metadata> {
|
||||
const sp = await searchParams;
|
||||
const cat = sp?.category && CATEGORY_FILTERS[sp.category];
|
||||
const title = cat ? `${cat.title} — Broadcast Beat` : "Production Technology Deep Dives — Broadcast Beat";
|
||||
const desc = cat?.description || "Deep-dive analysis of broadcast technology trends: IP workflows, cloud production, AI automation, streaming infrastructure, and ATSC 3.0.";
|
||||
return {
|
||||
title,
|
||||
description: desc,
|
||||
alternates: { canonical: sp?.category ? `/technology?category=${sp.category}` : "/technology" },
|
||||
openGraph: { title, description: desc, url: "/technology", type: "website" },
|
||||
};
|
||||
}
|
||||
|
||||
function articleMatches(a: { title?: string | null; category?: string | null; tags?: string[] | null }, keywords: string[]): boolean {
|
||||
const hay = `${a.title || ""} ${a.category || ""} ${(a.tags || []).join(" ")}`.toLowerCase();
|
||||
return keywords.some((k) => hay.includes(k.toLowerCase()));
|
||||
}
|
||||
|
||||
export default async function TechnologyPage({ searchParams }: PageProps) {
|
||||
const sp = await searchParams;
|
||||
const filterSlug = sp?.category && CATEGORY_FILTERS[sp.category] ? sp.category : null;
|
||||
const filter = filterSlug ? CATEGORY_FILTERS[filterSlug] : null;
|
||||
|
||||
const baseArticles = await getLegacyArticlesBySection("technology");
|
||||
const articles = filter ? baseArticles.filter((a) => articleMatches(a, filter.keywords)) : baseArticles;
|
||||
|
||||
const h1 = filter ? filter.title : "Production Technology";
|
||||
const subtitle = filter
|
||||
? filter.description
|
||||
: "Deep-dive coverage of IP infrastructure, AI, cloud production, and the standards shaping broadcast's future";
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
@@ -36,13 +85,59 @@ export default async function TechnologyPage() {
|
||||
<div className="flex items-center gap-3 mb-2">
|
||||
<span className="section-label">Technology</span>
|
||||
<div className="flex-1 h-px bg-[#2a2a2a]" />
|
||||
{filter && (
|
||||
<Link href="/technology" className="text-[#666] hover:text-[#3b82f6] font-body text-[11px] underline">
|
||||
clear filter
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
<h1 className="font-heading text-white text-3xl font-bold">{h1}</h1>
|
||||
<p className="text-[#777] font-body text-sm mt-2">{subtitle}</p>
|
||||
|
||||
{/* Sub-category chips */}
|
||||
<div className="flex items-center gap-2 mt-5 flex-wrap">
|
||||
<Link
|
||||
href="/technology"
|
||||
className={`px-3 py-1.5 rounded-sm font-body text-[11px] font-semibold uppercase tracking-wide transition-all ${
|
||||
!filterSlug
|
||||
? "bg-[#3b82f6]/20 text-[#3b82f6] border border-[#3b82f6]/50"
|
||||
: "bg-[#111] text-[#888] border border-[#2a2a2a] hover:text-[#ccc]"
|
||||
}`}>
|
||||
All
|
||||
</Link>
|
||||
{Object.entries(CATEGORY_FILTERS).map(([slug, def]) => {
|
||||
const isActive = filterSlug === slug;
|
||||
return (
|
||||
<Link
|
||||
key={slug}
|
||||
href={`/technology?category=${slug}`}
|
||||
className={`px-3 py-1.5 rounded-sm font-body text-[11px] font-semibold uppercase tracking-wide transition-all ${
|
||||
isActive
|
||||
? "bg-[#3b82f6]/20 text-[#3b82f6] border border-[#3b82f6]/50"
|
||||
: "bg-[#111] text-[#888] border border-[#2a2a2a] hover:text-[#ccc]"
|
||||
}`}>
|
||||
{def.title}
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<h1 className="font-heading text-white text-3xl font-bold">Production Technology</h1>
|
||||
<p className="text-[#777] font-body text-sm mt-2">Deep-dive coverage of IP infrastructure, AI, cloud production, and the standards shaping broadcast's future</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<main className="max-w-container mx-auto px-4 py-8">
|
||||
{articles.length === 0 && (
|
||||
<div className="text-center py-16">
|
||||
<p className="font-body text-[#888] text-base">
|
||||
No articles found{filter ? ` for "${filter.title}"` : ""}.
|
||||
</p>
|
||||
{filter && (
|
||||
<Link href="/technology" className="inline-block mt-3 text-[#3b82f6] hover:underline font-body text-sm">
|
||||
← Back to all technology coverage
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Featured technology article */}
|
||||
{articles[0] && (
|
||||
<div className="mb-10">
|
||||
|
||||
Reference in New Issue
Block a user