import { useState } from 'react'; import type { ShopOffer } from '../data/mock'; import { trackClick, getPageSource } from '../lib/tracking'; interface Props { offers: ShopOffer[]; lowestPrice: number; selectedSize?: string | null; productId?: string; sneakerData?: { item_id: string; item_name: string; item_brand: string; price: number; }; } function SizeChips({ sizes }: { sizes: string[] }) { const [open, setOpen] = useState(false); const sorted = [...sizes].sort((a, b) => parseFloat(a) - parseFloat(b)); const maxShow = 10; const hasMore = sorted.length > maxShow; const visible = open ? sorted : sorted.slice(0, maxShow); return (
{open && (
{visible.map((size) => ( {size} ))} {hasMore && !open && ( en {sorted.length - maxShow} meer )}
)}
); } export default function PriceComparisonTable({ offers, lowestPrice, selectedSize, productId, sneakerData }: Props) { const filtered = selectedSize ? offers.filter((o) => o.sizes.includes(selectedSize)) : offers; // When a size is selected, use the size-specific price and URL from sizeDetails const withEffectivePricing = filtered.map((o) => { if (!selectedSize) return o; const detail = o.sizeDetails?.find((d) => d.size === selectedSize); if (!detail) return o; return { ...o, price: detail.price, url: detail.url ?? o.url, }; }); const sorted = [...withEffectivePricing].sort((a, b) => a.price - b.price); const handleOutboundClick = async (e: React.MouseEvent, offer: ShopOffer, position: number) => { e.preventDefault(); // GA4 dataLayer events if (sneakerData) { window.dataLayer = window.dataLayer || []; window.dataLayer.push({ ecommerce: null }); window.dataLayer.push({ event: 'select_promotion', ecommerce: { promotion_id: offer.shop, promotion_name: offer.shop, items: [sneakerData], }, }); window.dataLayer.push({ event: 'outbound_click', destination_url: offer.url, shop_name: offer.shop, }); } // Track click server-side and get redirect URL with clickRef const result = await trackClick({ product_id: productId || sneakerData?.item_id || '0', shop_id: offer.shop, url: offer.url, source: getPageSource(), source_detail: offer.shop, position, }); // Klaviyo affiliate_click event (clickRef comes from redirect URL) const redirectUrl = result?.redirect_url || offer.url; let clickRef = ''; try { clickRef = new URL(redirectUrl).searchParams.get('clickRef') || ''; } catch (_) {} window.dataLayer = window.dataLayer || []; window.dataLayer.push({ event: 'affiliate_click', product_id: sneakerData?.item_id || productId || '', product_name: sneakerData?.item_name || '', brand: sneakerData?.item_brand || '', shop: offer.shop, price: offer.price, click_ref: clickRef, affiliate_url: redirectUrl, }); // Redirect to affiliate URL (with clickRef appended) window.open(redirectUrl, '_blank', 'noopener,noreferrer'); }; return (

Prijsvergelijking {selectedSize && · Maat {selectedSize}}

{sorted.length === 0 ? (
Geen shops gevonden met maat {selectedSize}
) : (
{sorted.map((offer, i) => { const isLowest = offer.price === Math.min(...sorted.map(o => o.price)); return (
{offer.shop} {isLowest && ( LAAGSTE )}
{offer.inStock ? ( ✓ Op voorraad ) : ( ✗ Uitverkocht )} {offer.sizes.length > 0 && }
{offer.originalPrice && offer.originalPrice > offer.price && ( €{offer.originalPrice.toFixed(2).replace('.', ',')} )} €{offer.price.toFixed(2).replace('.', ',')}
handleOutboundClick(e, offer, i)} className={`rounded-xl px-4 py-2 text-sm font-bold transition-all ${ offer.inStock ? 'bg-accent text-bg hover:bg-accent-dim hover:shadow-lg hover:shadow-accent/20' : 'cursor-not-allowed bg-surface text-text-muted' }`} > Bekijk
); })}
)}
); }