/**
* AdvertisementCard Component
*
* Inline advertisement card that matches the existing ActivityCard styling.
* Displays sponsored content with clear "Sponsored" label.
*/
'use client';
import { useEffect, useRef } from 'react';
import { ExternalLink, Megaphone } from 'lucide-react';
import { cn } from '@canadagpt/design-system';
import type { TargetedAd } from '@/types/ads';
interface AdvertisementCardProps {
ad: TargetedAd;
locale?: string;
onImpression?: (adId: string) => void;
onClick?: (adId: string) => void;
}
/**
* Track ad impression when card comes into view
*/
function useImpressionTracking(
adId: string,
onImpression?: (adId: string) => void
) {
const hasTracked = useRef(false);
const cardRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (!onImpression || hasTracked.current) return;
const observer = new IntersectionObserver(
(entries) => {
if (entries[0].isIntersecting && !hasTracked.current) {
hasTracked.current = true;
onImpression(adId);
}
},
{ threshold: 0.5 } // Track when 50% visible
);
if (cardRef.current) {
observer.observe(cardRef.current);
}
return () => observer.disconnect();
}, [adId, onImpression]);
return cardRef;
}
export function AdvertisementCard({
ad,
locale = 'en',
onImpression,
onClick,
}: AdvertisementCardProps) {
const cardRef = useImpressionTracking(ad.id, onImpression);
const handleClick = () => {
if (onClick) {
onClick(ad.id);
}
};
const sponsoredLabel = locale === 'fr' ? 'Commandite' : 'Sponsored';
const ctaText = ad.cta_text || (locale === 'fr' ? 'En savoir plus' : 'Learn More');
return (
<div ref={cardRef}>
<a
href={ad.cta_url}
target="_blank"
rel="noopener noreferrer sponsored"
onClick={handleClick}
className="block group"
>
<article className="rounded-lg bg-bg-elevated border border-border-subtle sm:hover:border-amber-400 sm:hover:shadow-md transition-all overflow-hidden">
<div className="flex">
{/* Thumbnail - Left side, only if exists */}
{ad.image_url && (
<div className="flex-shrink-0 w-[120px] sm:w-[140px] relative overflow-hidden bg-bg-overlay">
<img
src={ad.image_url}
alt=""
className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-300"
onError={(e) => {
const target = e.target as HTMLImageElement;
const parent = target.parentElement;
if (parent) {
parent.style.display = 'none';
}
}}
/>
</div>
)}
{/* Content - Flex grows */}
<div className="flex-1 p-4 min-w-0">
{/* Header row with Sponsored badge */}
<div className="flex items-center gap-2 mb-2 flex-wrap">
<span className="inline-flex items-center gap-1 px-2 py-0.5 text-xs font-medium rounded-full bg-amber-100 dark:bg-amber-900/30 text-amber-700 dark:text-amber-300 border border-amber-200 dark:border-amber-700">
<Megaphone size={10} />
{sponsoredLabel}
</span>
<span className="text-xs text-text-tertiary truncate">
{ad.advertiser_name}
</span>
</div>
{/* Headline */}
<h3 className="text-sm font-semibold text-text-primary sm:group-hover:text-amber-600 dark:sm:group-hover:text-amber-400 transition-colors line-clamp-2 mb-1">
{ad.title}
</h3>
{/* Description */}
{ad.description && (
<p className="text-xs text-text-secondary line-clamp-2 mb-2">
{ad.description}
</p>
)}
{/* CTA Button */}
<div className="flex items-center gap-1 text-xs font-medium text-amber-600 dark:text-amber-400 sm:group-hover:underline">
{ctaText}
<ExternalLink size={12} />
</div>
</div>
</div>
</article>
</a>
</div>
);
}
export default AdvertisementCard;