slice 1+2: header social URLs, drop TwitterX/Press Kit, wrap About/Team/PressKit, add /newsletter + /rss

- Header.tsx: bare linkedin/instagram/facebook URLs → /broadcastbeat handles; remove TwitterXIcon from social bar + import (Footer retains TwitterXIcon per "DO NOT OVERRIDE" marker)
- AboutDropdown.tsx: drop "Press kit" item (the page itself stays at /about/press-kit, just delisted from menu)
- SystemStatusBar.tsx: remove "Index online" pulse-dot and "N wire sources" span (keep articles count + events count + build version)
- /about, /about/team, /about/press-kit: wrap in Header+Footer (previously rendered bare <main>)
- /newsletter/page.tsx (new): redirect to /newsletter/archive — Header was 404'ing on /newsletter
- /rss/route.ts (new): dynamic RSS feed reading from legacy-source news section; replaces 404 at /rss

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-15 21:49:34 +00:00
parent 09a24be3de
commit a240133ec7
8 changed files with 175 additions and 95 deletions

View File

@@ -1,3 +1,6 @@
import Header from "@/components/Header";
import Footer from "@/components/Footer";
export const metadata = {
title: "About BroadcastBeat",
description: "BroadcastBeat is an independent industry publication covering broadcast, production, and post-production technology.",
@@ -5,6 +8,8 @@ export const metadata = {
export default function AboutPage() {
return (
<div className="min-h-screen bg-background">
<Header />
<main className="mx-auto max-w-3xl px-6 py-12 text-[#e5e7eb]">
<h1 className="font-serif text-4xl font-bold tracking-tight mb-6">About BroadcastBeat</h1>
<div className="space-y-5 text-[#d1d5db] text-[15px] leading-relaxed">
@@ -29,5 +34,7 @@ export default function AboutPage() {
</p>
</div>
</main>
<Footer />
</div>
);
}

View File

@@ -1,7 +1,12 @@
import Header from "@/components/Header";
import Footer from "@/components/Footer";
export const metadata = { title: "Press kit — BroadcastBeat" };
export default function PressKitPage() {
return (
<div className="min-h-screen bg-background">
<Header />
<main className="mx-auto max-w-3xl px-6 py-12 text-[#e5e7eb]">
<h1 className="font-serif text-4xl font-bold tracking-tight mb-6">Press kit</h1>
<div className="space-y-6 text-[15px]">
@@ -29,5 +34,7 @@ export default function PressKitPage() {
</section>
</div>
</main>
<Footer />
</div>
);
}

View File

@@ -1,4 +1,6 @@
import { createClient } from "@supabase/supabase-js";
import Header from "@/components/Header";
import Footer from "@/components/Footer";
export const dynamic = "force-dynamic";
export const revalidate = 600;
@@ -32,6 +34,8 @@ export default async function TeamPage() {
const team = (data || []) as Persona[];
return (
<div className="min-h-screen bg-background">
<Header />
<main className="mx-auto max-w-5xl px-6 py-12 text-[#e5e7eb]">
<h1 className="font-serif text-4xl font-bold tracking-tight mb-2">Editorial team</h1>
<p className="text-sm text-[#9ca3af] mb-10">
@@ -63,5 +67,7 @@ export default async function TeamPage() {
))}
</ul>
</main>
<Footer />
</div>
);
}

View File

@@ -0,0 +1,11 @@
import { redirect } from "next/navigation";
export const metadata = {
title: "Newsletter — BroadcastBeat",
description: "BroadcastBeat newsletter archive.",
alternates: { canonical: "/newsletter/archive" },
};
export default function NewsletterIndex() {
redirect("/newsletter/archive");
}

60
src/app/rss/route.ts Normal file
View File

@@ -0,0 +1,60 @@
import { getLegacyArticlesBySection } from "@/lib/articles/legacy-source";
export const dynamic = "force-dynamic";
export const revalidate = 1800;
const SITE_URL = process.env.NEXT_PUBLIC_SITE_URL || "https://broadcastbeat.com";
function escapeXml(s: string): string {
return s
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&apos;");
}
export async function GET() {
let items = "";
try {
const articles = await getLegacyArticlesBySection("news", 50);
items = articles
.map((a) => {
const url = `${SITE_URL}/news/${a.slug}`;
const pubDate = a.date ? new Date(a.date).toUTCString() : new Date().toUTCString();
const title = escapeXml(a.title || "Untitled");
const description = escapeXml(a.excerpt || "");
return ` <item>
<title>${title}</title>
<link>${url}</link>
<guid isPermaLink="true">${url}</guid>
<pubDate>${pubDate}</pubDate>
<description>${description}</description>
</item>`;
})
.join("\n");
} catch {
// serve an empty but valid feed on failure
items = "";
}
const body = `<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>BroadcastBeat</title>
<link>${SITE_URL}</link>
<atom:link href="${SITE_URL}/rss" rel="self" type="application/rss+xml" />
<description>News &amp; Intelligence for Broadcast, Post-Production &amp; Streaming Technology</description>
<language>en-us</language>
<lastBuildDate>${new Date().toUTCString()}</lastBuildDate>
${items}
</channel>
</rss>`;
return new Response(body, {
headers: {
"Content-Type": "application/rss+xml; charset=utf-8",
"Cache-Control": "public, max-age=1800, s-maxage=1800, stale-while-revalidate=3600",
},
});
}

View File

@@ -8,7 +8,6 @@ const ITEMS = [
{ label: "Editorial team", href: "/about/team" },
{ label: "Contact", href: "/about/contact" },
{ label: "Advertise", href: "/about/advertise" },
{ label: "Press kit", href: "/about/press-kit" },
];
export default function AboutDropdown() {

View File

@@ -6,7 +6,6 @@ import AppImage from "@/components/ui/AppImage";
import {
LinkedInIcon,
InstagramIcon,
TwitterXIcon,
FacebookIcon,
RssIcon,
MenuIcon,
@@ -215,19 +214,15 @@ export default function Header() {
News & Intelligence for Broadcast, Post-Production & Streaming Technology
</p>
<div className="flex items-center gap-3 ml-auto">
<a href="https://linkedin.com" target="_blank" rel="noopener noreferrer" aria-label="BroadcastBeat on LinkedIn"
<a href="https://linkedin.com/company/broadcastbeat" target="_blank" rel="noopener noreferrer" aria-label="BroadcastBeat on LinkedIn"
className="text-[#999] hover:text-[#3b82f6] focus:text-[#3b82f6] focus:outline-none focus-visible:ring-1 focus-visible:ring-[#3b82f6] transition-colors social-icon">
<LinkedInIcon size={14} />
</a>
<a href="https://instagram.com" target="_blank" rel="noopener noreferrer" aria-label="BroadcastBeat on Instagram"
<a href="https://instagram.com/broadcastbeat" target="_blank" rel="noopener noreferrer" aria-label="BroadcastBeat on Instagram"
className="text-[#999] hover:text-[#3b82f6] focus:text-[#3b82f6] focus:outline-none focus-visible:ring-1 focus-visible:ring-[#3b82f6] transition-colors social-icon">
<InstagramIcon size={14} />
</a>
<a href="https://twitter.com" target="_blank" rel="noopener noreferrer" aria-label="BroadcastBeat on Twitter/X"
className="text-[#999] hover:text-[#3b82f6] focus:text-[#3b82f6] focus:outline-none focus-visible:ring-1 focus-visible:ring-[#3b82f6] transition-colors social-icon">
<TwitterXIcon size={14} />
</a>
<a href="https://facebook.com" target="_blank" rel="noopener noreferrer" aria-label="BroadcastBeat on Facebook"
<a href="https://facebook.com/broadcastbeat" target="_blank" rel="noopener noreferrer" aria-label="BroadcastBeat on Facebook"
className="text-[#999] hover:text-[#3b82f6] focus:text-[#3b82f6] focus:outline-none focus-visible:ring-1 focus-visible:ring-[#3b82f6] transition-colors social-icon">
<FacebookIcon size={14} />
</a>

View File

@@ -50,12 +50,7 @@ export default async function SystemStatusBar() {
>
<div className="max-w-container mx-auto px-4 h-[28px] flex items-center justify-between text-[10px] font-mono uppercase tracking-wider text-[var(--color-text-tertiary,#6b7280)]">
<div className="flex items-center gap-5">
<span className="inline-flex items-center gap-1.5">
<span className="bb-pulse-dot bb-pulse-dot--green" />
Index online
</span>
<span>{stats.articles.toLocaleString()} articles indexed</span>
<span>{stats.sources.toLocaleString()} wire sources</span>
<span>{stats.events} events tracked</span>
</div>
<span className="text-[var(--color-text-info,#60a5fa)]">[{BUILD_VERSION}]</span>