import { useEffect } from 'react' import type { BannerPlacement, PublicBanner } from '../../api/types' import { Icon } from '../../components/Icon' import { useRotation } from '../../lib/useBanners' /** * Miejsce na baner w sali. * * ROTACJA JEST NA MIEJSCE, NIE NA CALA SALE * ------------------------------------------------------------------------ * Kazde miejsce rotuje niezaleznie: baner boczny i pas pod wideo nie musza * zmieniac sie w tej samej chwili, a wspolny licznik wiazalby je bez powodu. * * KLIKNIECIE MA DWIE DROGI * ------------------------------------------------------------------------ * Zwykly baner otwiera `targetUrl` w nowej karcie. Baner z tematem oferty * otwiera **formularz zapytania** w oknie, bez opuszczania sali: to domyka * droge od reklamy do kontaktu handlowego. Uczestnik, ktory wyszedl na obcy * serwis w trakcie prezentacji, zwykle juz nie wraca. * * `rel="noopener noreferrer"` odcina nowej karcie dostep do `window.opener` * i nie zdradza obcemu serwisowi, z jakiego adresu przyszedl uczestnik. */ export function BannerSlot({ placement, banners, rotationSeconds, onImpression, onClick, onDismiss, onOfferTopic, className = '', }: { placement: BannerPlacement banners: PublicBanner[] rotationSeconds: number onImpression: (bannerId: number) => void onClick: (bannerId: number) => void /** Zamkniecie, wylacznie przy OVERLAY. */ onDismiss?: (bannerId: number) => void /** Otwarcie formularza oferty z wybranym z gory tematem. */ onOfferTopic?: (topic: string) => void className?: string }) { const index = useRotation(banners.length, rotationSeconds) const banner = banners[index] /* * Wyswietlenie liczymy, gdy baner faktycznie wchodzi na ekran, a nie gdy * przychodzi z serwera: baner z rotacji, ktorego uczestnik nigdy nie * zobaczyl, nie zostal wyswietlony. */ useEffect(() => { if (banner) onImpression(banner.id) }, [banner, onImpression]) if (!banner) return null const isOffer = Boolean(banner.offerTopicName) const clickable = isOffer || Boolean(banner.targetUrl) function handleClick() { onClick(banner.id) if (isOffer && banner.offerTopicName && onOfferTopic) { onOfferTopic(banner.offerTopicName) } } const content = ( <> {banner.mediaUrl ? ( {banner.title} ) : ( /* Tresc jest oczyszczona sanitizerem na backendzie, tym samym co tresc strony zapisu, wiec nie czyscimy jej drugi raz w przegladarce. */
)} ) const frame = placement === 'OVERLAY' ? 'relative overflow-hidden rounded-lg border border-white/20 bg-white shadow-xl' : 'relative overflow-hidden rounded-lg border border-slate-200 bg-white' return (
{/* Baner z adresem zewnetrznym jest prawdziwym linkiem, a nie przyciskiem: tylko wtedy dziala srodkowy przycisk myszy i menu kontekstowe. Baner otwierajacy formularz oferty musi byc przyciskiem, bo nigdzie nie prowadzi. */} {isOffer ? ( ) : banner.targetUrl ? ( {content} ) : ( content )} {isOffer && (

Kliknij, żeby zapytać o ofertę

)} {/* OVERLAY zaslania czesc obrazu, wiec musi dac sie zamknac: baner nie do usuniecia w trakcie prezentacji jest gorszy niz brak banera. */} {placement === 'OVERLAY' && onDismiss && ( )} {/* wskaznik rotacji: uczestnik widzi, ze jest wiecej niz jeden baner */} {banners.length > 1 && (
{banners.map((item, i) => ( ))}
)} {!clickable && {banner.title}}
) }