From 2e787262918366b127fd676679d541afe41abbae Mon Sep 17 00:00:00 2001 From: Ryan Salazar Date: Wed, 27 May 2026 12:55:25 +0000 Subject: [PATCH] cross-link: forum auto-link, NAB hub, sponsor strip, recent stories MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Feature 3 — forum auto-linking Thread + reply bodies in /forum/thread/[id] now wrap company-name mentions with /manufacturers/ links via a new client-side LinkifyPlainText component. Loads the directory list once per page (module-level promise cache), regex-scans each body, preserves whitespace, caps at 10 links per body to avoid clutter. CompanyMentionsHover mounted on the page so the same preview card pops on hover. Adds /api/public/companies/list — minimal { name, slug } directory feed, cached 10min. Feature 4 — /nab-2026 live coverage hub New page aggregating every directory company with exhibits_nab=true. Three sections: - Coverage Partners — active advertisers exhibiting at NAB, larger tiles with emerald Sponsor border - Latest from the show floor — last 60 days of articles with NAB in the title, cinematic grid - All NAB 2026 exhibitors — alphabetical, capped at 200 with link to full /manufacturers directory Every company tile carries data-company-slug for hover previews. Feature 5b — Sponsor logo strip on homepage New SponsorLogoStrip server component renders just above the footer on the homepage, showing logos of every current banner advertiser with a directory entry + logo. Each links to the manufacturer profile with the hover-card slug attribute. Feature 5c — Recent coverage on manufacturer profiles /manufacturers/[slug] now includes a grid of the 6 most recent articles matching the company name in the title, with thumbnails. Renames the existing manufacturer_id-linked block to "Press releases" since it's a separate data source. --- src/app/api/public/companies/list/route.ts | 29 +++ src/app/forum/thread/[id]/page.tsx | 11 +- src/app/home-page/page.tsx | 10 + src/app/manufacturers/[slug]/page.tsx | 58 ++++- src/app/nab-2026/page.tsx | 234 +++++++++++++++++++++ src/components/LinkifyPlainText.tsx | 125 +++++++++++ src/components/SponsorLogoStrip.tsx | 89 ++++++++ 7 files changed, 552 insertions(+), 4 deletions(-) create mode 100644 src/app/api/public/companies/list/route.ts create mode 100644 src/app/nab-2026/page.tsx create mode 100644 src/components/LinkifyPlainText.tsx create mode 100644 src/components/SponsorLogoStrip.tsx diff --git a/src/app/api/public/companies/list/route.ts b/src/app/api/public/companies/list/route.ts new file mode 100644 index 0000000..fafc769 --- /dev/null +++ b/src/app/api/public/companies/list/route.ts @@ -0,0 +1,29 @@ +import { NextResponse } from "next/server"; +import { createAdminClient } from "@/lib/supabase/admin"; + +// Cache aggressively — directory rarely changes, and 1k+ companies is ~50KB. +export const revalidate = 600; + +export async function GET() { + try { + const svc = createAdminClient(); + const { data } = await svc + .from("tracked_companies") + .select("company_name, slug") + .eq("directory_visible", true) + .not("slug", "is", null) + .not("company_name", "is", null); + + const rows = (data || []) + .filter((r: any) => r.company_name && r.slug) + .map((r: any) => ({ name: String(r.company_name).trim(), slug: String(r.slug) })) + .filter((r) => r.name.length >= 3); + + return NextResponse.json( + { companies: rows }, + { headers: { "Cache-Control": "public, s-maxage=600, stale-while-revalidate=1200" } }, + ); + } catch (e: any) { + return NextResponse.json({ companies: [], error: e?.message || "error" }, { status: 500 }); + } +} diff --git a/src/app/forum/thread/[id]/page.tsx b/src/app/forum/thread/[id]/page.tsx index bec39c6..32798aa 100644 --- a/src/app/forum/thread/[id]/page.tsx +++ b/src/app/forum/thread/[id]/page.tsx @@ -8,6 +8,8 @@ import AdImage from '@/components/AdImage'; import { ADS_300X250, rotateAll } from '@/lib/ads'; import { createClient } from '@/lib/supabase/client'; import SidebarAdStack from "@/components/SidebarAdStack"; +import LinkifyPlainText from "@/components/LinkifyPlainText"; +import CompanyMentionsHover from "@/components/CompanyMentionsHover"; // Inline ad slot inserted between forum replies every N posts. function InThreadAd({ adIndex }: { adIndex: number }) { @@ -368,7 +370,9 @@ export default function ForumThreadPage() { · {thread.view_count} views -

{thread.body}

+

+ +

· {timeAgo(reply.created_at)} -

{reply.body}

+

+ +