← back to Homesonspec

apps/web/src/components/AdSlot.tsx

80 lines

"use client";

import Script from "next/script";
import { useEffect } from "react";

/**
 * Google AdSense banner slot — GATED.
 *
 * Renders NOTHING until NEXT_PUBLIC_ADSENSE_CLIENT is set to a real
 * `ca-pub-…` publisher id. No id → no script, no <ins>, no network call,
 * so the site ships ad-free by default and Steve flips it live with one
 * env var once the AdSense account is approved.
 *
 * Placement rule (honors the footer promise "Organic results are never
 * mixed with paid placement"): banners live in their OWN labeled zones,
 * never interleaved into the listings grid, always tagged "Advertisement".
 */

const CLIENT = process.env.NEXT_PUBLIC_ADSENSE_CLIENT || "ca-pub-5278231299883833"; // TK-11341 default pub id

declare global {
  interface Window {
    adsbygoogle?: unknown[];
  }
}

/** Loads the AdSense library once, site-wide. No-op until the id is set. Mount in layout. */
export function AdSenseLoader() {
  if (!CLIENT) return null;
  return (
    <Script
      id="adsbygoogle-init"
      async
      strategy="afterInteractive"
      crossOrigin="anonymous"
      src={`https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=${CLIENT}`}
    />
  );
}

export default function AdSlot({
  slot,
  format = "auto",
  className,
}: {
  slot?: string;
  format?: string;
  className?: string;
}) {
  useEffect(() => {
    if (!CLIENT || !slot) return;
    try {
      (window.adsbygoogle = window.adsbygoogle || []).push({});
    } catch {
      /* AdSense not yet loaded — the loader retries the queue */
    }
  }, []);

  if (!CLIENT || !slot) return null; // never emit an unidentifiable manual unit

  return (
    <aside
      className={`mx-auto w-full max-w-7xl px-4 ${className ?? "my-6"}`}
      aria-label="Advertisement"
    >
      <p className="mb-1 text-center text-[10px] uppercase tracking-wide text-neutral-400">
        Advertisement
      </p>
      <ins
        className="adsbygoogle block"
        style={{ display: "block" }}
        data-ad-client={CLIENT}
        data-ad-slot={slot}
        data-ad-format={format}
        data-full-width-responsive="true"
      />
    </aside>
  );
}