- {ADS_728X90.length > 0 && (
+ {pickAds("728x90", 1).length > 0 && (
)}
diff --git a/src/components/Header.tsx b/src/components/Header.tsx
index 7429cc0..946ae24 100644
--- a/src/components/Header.tsx
+++ b/src/components/Header.tsx
@@ -19,7 +19,7 @@ import AdImage from "@/components/AdImage";
import EventsDropdown from "@/components/EventsDropdown";
import AboutDropdown from "@/components/AboutDropdown";
import DualSpeedTicker from "@/components/DualSpeedTicker";
-import { ADS_728X90, shuffle } from "@/lib/ads";
+import { pickAds } from "@/lib/ads";
interface NavItem {
label: string;
@@ -273,9 +273,9 @@ export default function Header() {
{/* Header 728x90 banner — centered between logo and right side */}
- {ADS_728X90.length > 0 && (
+ {pickAds("728x90", 1).length > 0 && (
)}
diff --git a/src/components/SidebarAdStack.tsx b/src/components/SidebarAdStack.tsx
index ea9998c..c0e7a0d 100644
--- a/src/components/SidebarAdStack.tsx
+++ b/src/components/SidebarAdStack.tsx
@@ -1,11 +1,14 @@
"use client";
import { useMemo } from "react";
import AdImage from "./AdImage";
-import { FIXED_300X600, rotateAll, type Ad } from "@/lib/ads";
+import { pickAds, rotateAll, type Ad } from "@/lib/ads";
-// Renders the fixed 300x600 at the top (no rotation) followed by EVERY
-// 300x250 banner stacked vertically in a shuffled order. Adding more
-// 300x250 entries to ads.ts automatically grows this stack.
+// Renders the 300x600 at the top followed by EVERY 300x250 banner stacked
+// vertically in a shuffled order. Both reads happen at render time so the
+// centralized RMP cache (which populates async after module import) is
+// picked up — using the module-level FIXED_300X600 constant captured the
+// FALLBACK ad with its direct bmd.link click_url and bypassed the click
+// logger entirely.
export default function SidebarAdStack({
excludeSrcs = [],
className = "",
@@ -13,6 +16,10 @@ export default function SidebarAdStack({
excludeSrcs?: string[];
className?: string;
}) {
+ const top600 = useMemo
(
+ () => pickAds("300x600", 1)[0] ?? null,
+ [excludeSrcs.join("|")],
+ );
const rotating = useMemo(
() => rotateAll("300x250", new Set(excludeSrcs)),
[excludeSrcs.join("|")],
@@ -20,7 +27,7 @@ export default function SidebarAdStack({
return (
- {FIXED_300X600 &&
}
+ {top600 &&
}
{rotating.map((ad) => (
))}
diff --git a/src/lib/ads.ts b/src/lib/ads.ts
index 39a8372..0b8ae32 100644
--- a/src/lib/ads.ts
+++ b/src/lib/ads.ts
@@ -99,15 +99,18 @@ async function fetchPlacement(size: Ad["size"]): Promise
{
if (!res.ok) return [];
const data = await res.json() as { banners?: RmpBanner[] };
return (data.banners || []).map((b): Ad => ({
- // Prefer the human-readable slug for the centralized click URL; fall back
- // to the campaign UUID if the slug column ever ends up empty.
slug: b.slug || b.campaign_id,
campaign_id: b.campaign_id,
label: b.name,
size: b.placement,
src: b.image_url,
alt: b.name,
- click_url: `https://www.relevantmediaproperties.com/${PROPERTY}/${b.slug || b.campaign_id}`,
+ // All clicks route through advertising.RMP.com/r/ so they
+ // land in bb.ad_clicks — the single table the RMP analytics dashboard
+ // reads. Do not change to a different host or path; the legacy
+ // www.RMP.com// path writes to adv.banner_clicks which
+ // the dashboard does not query.
+ click_url: `${RMP_API}/r/${b.campaign_id}`,
}));
} catch {
return [];
@@ -207,11 +210,8 @@ export function pickAds(
return all.slice(0, Math.min(n, all.length));
}
-// Sync exports for callers that still want the bare arrays. These also
-// read the cache (so live edits surface) but fall back to FALLBACK.
-export const ADS_728X90: Ad[] = (() => readCachedAds().filter((a) => a.size === "728x90"))();
-export const ADS_300X250: Ad[] = (() => readCachedAds().filter((a) => a.size === "300x250"))();
-export const FIXED_300X600: Ad | null = (() => readCachedAds().find((a) => a.size === "300x600") || null)();
-
-// Prime the cache once at module load.
+// Prime the cache once at module load. Callers must use rotateAll() or
+// pickAds() so the live cache (which fills async after import) is read at
+// render time — the previous module-level IIFE constants captured the
+// FALLBACK array forever and silently bypassed the centralized click logger.
triggerRefresh();