diff --git a/src/components/AdImage.tsx b/src/components/AdImage.tsx index 9c9f43a..fc98b03 100644 --- a/src/components/AdImage.tsx +++ b/src/components/AdImage.tsx @@ -1,103 +1,88 @@ -"use client"; +'use client'; +import { useEffect, useRef } from 'react'; +import AppImage from '@/components/ui/AppImage'; +import { createClient } from '@/lib/supabase/client'; -import { useEffect, useRef } from "react"; -import { usePathname } from "next/navigation"; -import AppImage from "@/components/ui/AppImage"; -import { createClient } from "@/lib/supabase/client"; -import type { Ad } from "@/lib/ads"; +interface Ad { + src: string; + label: string; + size: string; + click_url?: string; + adsanity_id?: string; +} -const SIZE_DIM: Record = { - "300x250": { w: 300, h: 250 }, - "300x600": { w: 300, h: 600 }, - "728x90": { w: 728, h: 90 }, +const ADSANITY_CAMPAIGN_MAP: Record = { + 'riedel': 'ad-210571', + 'look-solutions': 'ad-208185', + 'opengear': 'ad-200174', + 'telycam': 'ad-191740', + 'livu-728': 'ad-189081', + 'aja': 'ad-164285', + 'studio-suite': 'ad-162714', + 'zixi': 'ad-156737', + 'lectrosonics': 'ad-156727', + 'livu-300': 'ad-156486', + 'magewell': 'ad-154296', + 'blackmagic': 'ad-210571', + 'magewell-300': 'ad-154296', + 'studio-suite-300': 'ad-162714', + 'zixi-300': 'ad-156737', + 'lectrosonics-300': 'ad-156727', + 'aja-300': 'ad-164285', + 'livu-300-2': 'ad-156486', + 'opengear-300': 'ad-200174', + 'look-solutions-300': 'ad-208185', + 'riedel-300': 'ad-210571', + 'telycam-300': 'ad-191740' }; -function getSessionId(): string { - if (typeof window === "undefined") return ""; - let sid = sessionStorage.getItem("bb_session_id"); - if (!sid) { - sid = crypto.randomUUID(); - sessionStorage.setItem("bb_session_id", sid); - } - return sid; -} - -export default function AdImage({ - ad, - className = "", - priority = false, -}: { - ad: Ad; - className?: string; - priority?: boolean; -}) { - const { w, h } = SIZE_DIM[ad.size]; - const anchorRef = useRef(null); - const impressionFiredRef = useRef(false); - const pathname = usePathname(); +export default function AdImage({ ad, page, priority }: { ad: Ad; page?: string; priority?: boolean }) { + const containerRef = useRef(null); + const trackedRef = useRef(false); + const supabase = createClient(); + const adsanityId = ad.adsanity_id || ADSANITY_CAMPAIGN_MAP[ad.label.toLowerCase().replace(/\s+/g, '-')] || ''; useEffect(() => { - const el = anchorRef.current; - if (!el || impressionFiredRef.current) return; - - const observer = new IntersectionObserver( - (entries) => { - for (const entry of entries) { - if (entry.isIntersecting && !impressionFiredRef.current) { - impressionFiredRef.current = true; - observer.disconnect(); - void recordEvent("impression"); - break; - } - } - }, - { threshold: 0.5 } - ); - observer.observe(el); + const observer = new IntersectionObserver(([entry]) => { + if (entry.isIntersecting && !trackedRef.current) { + trackedRef.current = true; + supabase.from('banner_analytics').insert({ + ad_src: ad.src, + ad_label: ad.label, + ad_size: ad.size, + event_type: 'impression', + page: page || typeof window !== 'undefined' ? window.location.pathname : null, + session_id: typeof window !== 'undefined' ? (window as any).__session_id : null, + referrer: typeof document !== 'undefined' ? document.referrer : null + }).catch(err => console.error('Impression:', err)); + } + }, { threshold: 0.5 }); + if (containerRef.current) observer.observe(containerRef.current); return () => observer.disconnect(); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [ad.src]); + }, [ad, page, supabase]); - async function recordEvent(eventType: "impression" | "click") { - try { - const supabase = createClient(); - await supabase.from("banner_analytics").insert({ - ad_src: ad.src, - ad_label: ad.label, - ad_size: ad.size, - event_type: eventType, - page: pathname || (typeof window !== "undefined" ? window.location.pathname : null), - session_id: getSessionId(), - referrer: typeof document !== "undefined" ? document.referrer || null : null, - }); - } catch { - /* tracking is best-effort; never block the user */ - } - } - - function handleClick() { - void recordEvent("click"); - } + const handleClick = () => { + supabase.from('banner_analytics').insert({ + ad_src: ad.src, + ad_label: ad.label, + ad_size: ad.size, + event_type: 'click', + page: page || typeof window !== 'undefined' ? window.location.pathname : null, + session_id: typeof window !== 'undefined' ? (window as any).__session_id : null, + referrer: typeof document !== 'undefined' ? document.referrer : null + }).catch(err => console.error('Click:', err)); + }; return ( - - - +
+ {adsanityId && ( +