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:
@@ -1,3 +1,6 @@
|
|||||||
|
import Header from "@/components/Header";
|
||||||
|
import Footer from "@/components/Footer";
|
||||||
|
|
||||||
export const metadata = {
|
export const metadata = {
|
||||||
title: "About BroadcastBeat",
|
title: "About BroadcastBeat",
|
||||||
description: "BroadcastBeat is an independent industry publication covering broadcast, production, and post-production technology.",
|
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() {
|
export default function AboutPage() {
|
||||||
return (
|
return (
|
||||||
|
<div className="min-h-screen bg-background">
|
||||||
|
<Header />
|
||||||
<main className="mx-auto max-w-3xl px-6 py-12 text-[#e5e7eb]">
|
<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>
|
<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">
|
<div className="space-y-5 text-[#d1d5db] text-[15px] leading-relaxed">
|
||||||
@@ -29,5 +34,7 @@ export default function AboutPage() {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
<Footer />
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
|
import Header from "@/components/Header";
|
||||||
|
import Footer from "@/components/Footer";
|
||||||
|
|
||||||
export const metadata = { title: "Press kit — BroadcastBeat" };
|
export const metadata = { title: "Press kit — BroadcastBeat" };
|
||||||
|
|
||||||
export default function PressKitPage() {
|
export default function PressKitPage() {
|
||||||
return (
|
return (
|
||||||
|
<div className="min-h-screen bg-background">
|
||||||
|
<Header />
|
||||||
<main className="mx-auto max-w-3xl px-6 py-12 text-[#e5e7eb]">
|
<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>
|
<h1 className="font-serif text-4xl font-bold tracking-tight mb-6">Press kit</h1>
|
||||||
<div className="space-y-6 text-[15px]">
|
<div className="space-y-6 text-[15px]">
|
||||||
@@ -29,5 +34,7 @@ export default function PressKitPage() {
|
|||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
<Footer />
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import { createClient } from "@supabase/supabase-js";
|
import { createClient } from "@supabase/supabase-js";
|
||||||
|
import Header from "@/components/Header";
|
||||||
|
import Footer from "@/components/Footer";
|
||||||
|
|
||||||
export const dynamic = "force-dynamic";
|
export const dynamic = "force-dynamic";
|
||||||
export const revalidate = 600;
|
export const revalidate = 600;
|
||||||
@@ -32,6 +34,8 @@ export default async function TeamPage() {
|
|||||||
const team = (data || []) as Persona[];
|
const team = (data || []) as Persona[];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<div className="min-h-screen bg-background">
|
||||||
|
<Header />
|
||||||
<main className="mx-auto max-w-5xl px-6 py-12 text-[#e5e7eb]">
|
<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>
|
<h1 className="font-serif text-4xl font-bold tracking-tight mb-2">Editorial team</h1>
|
||||||
<p className="text-sm text-[#9ca3af] mb-10">
|
<p className="text-sm text-[#9ca3af] mb-10">
|
||||||
@@ -63,5 +67,7 @@ export default async function TeamPage() {
|
|||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
</main>
|
</main>
|
||||||
|
<Footer />
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
11
src/app/newsletter/page.tsx
Normal file
11
src/app/newsletter/page.tsx
Normal 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
60
src/app/rss/route.ts
Normal 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, "&")
|
||||||
|
.replace(/</g, "<")
|
||||||
|
.replace(/>/g, ">")
|
||||||
|
.replace(/"/g, """)
|
||||||
|
.replace(/'/g, "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
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 & Intelligence for Broadcast, Post-Production & 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",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -8,7 +8,6 @@ const ITEMS = [
|
|||||||
{ label: "Editorial team", href: "/about/team" },
|
{ label: "Editorial team", href: "/about/team" },
|
||||||
{ label: "Contact", href: "/about/contact" },
|
{ label: "Contact", href: "/about/contact" },
|
||||||
{ label: "Advertise", href: "/about/advertise" },
|
{ label: "Advertise", href: "/about/advertise" },
|
||||||
{ label: "Press kit", href: "/about/press-kit" },
|
|
||||||
];
|
];
|
||||||
|
|
||||||
export default function AboutDropdown() {
|
export default function AboutDropdown() {
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import AppImage from "@/components/ui/AppImage";
|
|||||||
import {
|
import {
|
||||||
LinkedInIcon,
|
LinkedInIcon,
|
||||||
InstagramIcon,
|
InstagramIcon,
|
||||||
TwitterXIcon,
|
|
||||||
FacebookIcon,
|
FacebookIcon,
|
||||||
RssIcon,
|
RssIcon,
|
||||||
MenuIcon,
|
MenuIcon,
|
||||||
@@ -215,19 +214,15 @@ export default function Header() {
|
|||||||
News & Intelligence for Broadcast, Post-Production & Streaming Technology
|
News & Intelligence for Broadcast, Post-Production & Streaming Technology
|
||||||
</p>
|
</p>
|
||||||
<div className="flex items-center gap-3 ml-auto">
|
<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">
|
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} />
|
<LinkedInIcon size={14} />
|
||||||
</a>
|
</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">
|
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} />
|
<InstagramIcon size={14} />
|
||||||
</a>
|
</a>
|
||||||
<a href="https://twitter.com" target="_blank" rel="noopener noreferrer" aria-label="BroadcastBeat on Twitter/X"
|
<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">
|
|
||||||
<TwitterXIcon size={14} />
|
|
||||||
</a>
|
|
||||||
<a href="https://facebook.com" 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">
|
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} />
|
<FacebookIcon size={14} />
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -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="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">
|
<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.articles.toLocaleString()} articles indexed</span>
|
||||||
<span>{stats.sources.toLocaleString()} wire sources</span>
|
|
||||||
<span>{stats.events} events tracked</span>
|
<span>{stats.events} events tracked</span>
|
||||||
</div>
|
</div>
|
||||||
<span className="text-[var(--color-text-info,#60a5fa)]">[{BUILD_VERSION}]</span>
|
<span className="text-[var(--color-text-info,#60a5fa)]">[{BUILD_VERSION}]</span>
|
||||||
|
|||||||
Reference in New Issue
Block a user