{filtered.length === allArticles.length ? `${allArticles.length} articles` : `${filtered.length} of ${allArticles.length} articles`}
No articles match your filters.
{article.title || "Untitled"}
{article.excerpt || ""}
"use client"; import React, { useState, useMemo, useEffect } from "react"; import Link from "next/link"; import { useSearchParams } from "next/navigation"; import Header from "@/components/Header"; import Footer from "@/components/Footer"; import AppImage from "@/components/ui/AppImage"; import type { Article } from "@/lib/articles/types"; import AISuggestedArticles from "@/components/AISuggestedArticles"; const CATEGORIES = ["All", "Cloud", "AI", "IP Workflows", "Live Production", "Streaming", "Audio", "Cameras", "NAB 2026", "EAS", "Sports Broadcasting", "Ad Tech"]; // Map URL category slugs (?category=live-production) to a matching CATEGORIES label. function categoryFromSlug(slug: string | null): string { if (!slug) return "All"; const norm = slug.toLowerCase().replace(/[-_]+/g, " ").trim(); const hit = CATEGORIES.find((c) => c.toLowerCase() === norm); return hit || "All"; } const SORT_OPTIONS = [ { value: "newest", label: "Newest First" }, { value: "oldest", label: "Oldest First" }, { value: "az", label: "A → Z" }, { value: "za", label: "Z → A" }, ]; function parseArticleDate(dateStr: string): Date { return new Date(dateStr); } export default function NewsPage({ articles }: { articles: Article[] }) { const allArticles = Array.isArray(articles) ? articles : []; const searchParams = useSearchParams(); const [search, setSearch] = useState(""); const [activeCategory, setActiveCategory] = useState("All"); const [dateFrom, setDateFrom] = useState(""); const [dateTo, setDateTo] = useState(""); const [sortBy, setSortBy] = useState("newest"); // Hydrate filter state from URL (?category=live-production&search=foo) useEffect(() => { const cat = categoryFromSlug(searchParams?.get("category") ?? null); if (cat !== "All") setActiveCategory(cat); const q = searchParams?.get("search") ?? searchParams?.get("q"); if (q) setSearch(q); }, [searchParams]); const filtered = useMemo(() => { let result = [...allArticles]; // Search filter if (search.trim()) { const q = search.toLowerCase(); result = result.filter((a) => { const title = (a?.title ?? "").toLowerCase(); const excerpt = (a?.excerpt ?? "").toLowerCase(); const author = (a?.author ?? "").toLowerCase(); const tags = Array.isArray(a?.tags) ? a.tags : []; return ( title.includes(q) || excerpt.includes(q) || author.includes(q) || tags.some((t) => (t ?? "").toLowerCase().includes(q)) ); }); } // Category filter if (activeCategory !== "All") { const cat = activeCategory.toLowerCase(); result = result.filter((a) => { const tags = Array.isArray(a?.tags) ? a.tags : []; const category = (a?.category ?? "").toLowerCase(); return ( tags.some((t) => (t ?? "").toLowerCase() === cat) || category.includes(cat) ); }); } // Date range filter if (dateFrom) { const from = new Date(dateFrom); result = result.filter((a) => parseArticleDate(a?.date ?? "") >= from); } if (dateTo) { const to = new Date(dateTo); to.setHours(23, 59, 59, 999); result = result.filter((a) => parseArticleDate(a?.date ?? "") <= to); } // Sorting result.sort((a, b) => { const ta = (a?.title ?? ""); const tb = (b?.title ?? ""); if (sortBy === "newest") return parseArticleDate(b?.date ?? "").getTime() - parseArticleDate(a?.date ?? "").getTime(); if (sortBy === "oldest") return parseArticleDate(a?.date ?? "").getTime() - parseArticleDate(b?.date ?? "").getTime(); if (sortBy === "az") return ta.localeCompare(tb); if (sortBy === "za") return tb.localeCompare(ta); return 0; }); return result; }, [allArticles, search, activeCategory, dateFrom, dateTo, sortBy]); const hasActiveFilters = search || activeCategory !== "All" || dateFrom || dateTo || sortBy !== "newest"; function clearFilters() { setSearch(""); setActiveCategory("All"); setDateFrom(""); setDateTo(""); setSortBy("newest"); } return (
Breaking news and industry updates from the world of broadcast engineering
{filtered.length === allArticles.length ? `${allArticles.length} articles` : `${filtered.length} of ${allArticles.length} articles`}
No articles match your filters.
{article.excerpt || ""}