'use client' import { useState, useRef, useEffect } from 'react' import Image from 'next/image' import { useSearchParams } from 'next/navigation' import { Suspense } from 'react' import { Ruler, ChevronDown, Check, AlertCircle, Loader2 } from 'lucide-react' import Header from '@/components/Header' import Footer from '@/components/Footer' import CountdownBanner from '@/components/CountdownBanner' import TrustSection from '@/components/TrustSection' import SizeGuideModal from '@/components/SizeGuideModal' import clsx from 'clsx' // ─── Config ─────────────────────────────────────────────────────────────────── const PRODUCTS = { samarreta: { id: 'samarreta', name: 'Samarreta', fullName: 'Samarreta Cursa de la Cirera 2026', price: 10.0, image: '/assets/Samarreta.jpeg', features: ['Teixit tècnic Dry Fit', 'Running Mesh Yarn', 'Samarreta Fullprint', 'Mànica recta'], hasTshirt: true, hasSocks: false, badge: null, }, mitjons: { id: 'mitjons', name: 'Mitjons', fullName: 'Mitjons Cursa de la Cirera 2026', price: 10.0, image: '/assets/Mitjons.jpeg', features: ['Blood Bros Socks', 'Alta qualitat', 'Disseny exclusiu', 'Talles XXS–XL'], hasTshirt: false, hasSocks: true, badge: null, }, pack: { id: 'pack', name: 'Pack Complet', fullName: 'Pack Samarreta + Mitjons', price: 18.5, image: '/assets/Pack Samarreta+Mitjons.jpeg', features: ['Samarreta + Mitjons', 'Estalvia 1,50€', 'Tot l\'equipament', 'Millor preu'], hasTshirt: true, hasSocks: true, badge: 'OFERTA', }, } as const type ProductId = keyof typeof PRODUCTS const TSHIRT_SIZES = ['S', 'M', 'L', 'XL', 'XXL', '3XL'] const SOCK_SIZES = [ { label: 'XXS', range: '32-34' }, { label: 'XS', range: '35-37' }, { label: 'S', range: '38-39' }, { label: 'M', range: '40-42' }, { label: 'L', range: '43-44' }, { label: 'XL', range: '45-47' }, ] const PROVINCES = [ 'Lleida', 'Barcelona', 'Girona', 'Tarragona', 'Aragó', 'Madrid', 'Valencia', 'Altres', ] interface FormData { nom: string cognoms: string telefon: string email: string adreca: string codiPostal: string poblacio: string provincia: string } const EMPTY_FORM: FormData = { nom: '', cognoms: '', telefon: '', email: '', adreca: '', codiPostal: '', poblacio: '', provincia: '', } // ─── Cancelled Banner ───────────────────────────────────────────────────────── function CancelledBanner() { const params = useSearchParams() const [visible, setVisible] = useState(false) useEffect(() => { if (params.get('cancelled') === 'true') setVisible(true) }, [params]) if (!visible) return null return (

Pagament cancel·lat

Has cancel·lat el pagament. Torna a omplir el formulari quan vulguis per completar la teva comanda.

) } // ─── Main Page ──────────────────────────────────────────────────────────────── export default function HomePage() { const [selectedProduct, setSelectedProduct] = useState(null) const [sizeTshirt, setSizeTshirt] = useState('') const [sizeSocks, setSizeSocks] = useState('') const [formData, setFormData] = useState(EMPTY_FORM) const [shipping, setShipping] = useState<'correos' | 'recollida' | null>(null) const [sizeGuide, setSizeGuide] = useState<'samarreta' | 'mitjons' | null>(null) const [loading, setLoading] = useState(false) const [errors, setErrors] = useState>>({}) const sizesRef = useRef(null) const formRef = useRef(null) const shippingRef = useRef(null) const summaryRef = useRef(null) const product = selectedProduct ? PRODUCTS[selectedProduct] : null const shippingCost = shipping === 'correos' ? 7.99 : 0 const total = product ? product.price + shippingCost : 0 function scrollTo(ref: React.RefObject) { setTimeout(() => ref.current?.scrollIntoView({ behavior: 'smooth', block: 'start' }), 100) } function handleSelectProduct(id: ProductId) { setSelectedProduct(id) setSizeTshirt('') setSizeSocks('') setShipping(null) setErrors({}) scrollTo(sizesRef) } function handleSizeTshirt(s: string) { setSizeTshirt(s) if (!product?.hasSocks) scrollTo(formRef) else if (sizeSocks) scrollTo(formRef) } function handleSizeSocks(s: string) { setSizeSocks(s) if (!product?.hasTshirt) scrollTo(formRef) else if (sizeTshirt) scrollTo(formRef) } function handleField(key: keyof FormData, value: string) { setFormData(prev => ({ ...prev, [key]: value })) if (errors[key]) setErrors(prev => ({ ...prev, [key]: '' })) } function handleShipping(v: 'correos' | 'recollida') { setShipping(v) scrollTo(summaryRef) } function validate(): boolean { const newErrors: typeof errors = {} if (!selectedProduct) newErrors.product = 'Selecciona un producte' if (product?.hasTshirt && !sizeTshirt) newErrors.sizes = 'Selecciona la talla de la samarreta' if (product?.hasSocks && !sizeSocks) newErrors.sizes = (newErrors.sizes ? newErrors.sizes + ' i els ' : '') + 'Selecciona la talla dels mitjons' if (!formData.nom.trim()) newErrors.nom = 'El nom és obligatori' if (!formData.cognoms.trim()) newErrors.cognoms = 'Els cognoms són obligatoris' if (!formData.telefon.trim()) newErrors.telefon = 'El telèfon és obligatori' if (!formData.email.trim() || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.email)) newErrors.email = 'Introdueix un email vàlid' if (!shipping) newErrors.shipping = 'Selecciona una opció de lliurament' if (shipping === 'correos') { if (!formData.adreca.trim()) newErrors.adreca = "L'adreça és obligatòria per a enviament" if (!formData.codiPostal.trim()) newErrors.codiPostal = 'El codi postal és obligatori' if (!formData.poblacio.trim()) newErrors.poblacio = 'La població és obligatòria' if (!formData.provincia.trim()) newErrors.provincia = 'La província és obligatòria' } setErrors(newErrors) return Object.keys(newErrors).length === 0 } async function handlePay() { if (!validate()) { const firstError = document.querySelector('[data-error]') firstError?.scrollIntoView({ behavior: 'smooth', block: 'center' }) return } setLoading(true) try { const res = await fetch('/api/checkout', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ product: selectedProduct, sizeTshirt: sizeTshirt || null, sizeSocks: sizeSocks || null, shipping, ...formData, }), }) const data = await res.json() if (!res.ok) throw new Error(data.error ?? 'Error desconegut') window.location.href = data.url } catch (err: unknown) { const message = err instanceof Error ? err.message : 'Error inesperat. Torna-ho a intentar.' setErrors({ product: message }) setLoading(false) } } const needsTshirtSize = product?.hasTshirt ?? false const needsSockSize = product?.hasSocks ?? false const sizesComplete = (!needsTshirtSize || sizeTshirt) && (!needsSockSize || sizeSocks) const formComplete = formData.nom && formData.cognoms && formData.telefon && formData.email && (shipping !== 'correos' || (formData.adreca && formData.codiPostal && formData.poblacio && formData.provincia)) const canPay = selectedProduct && sizesComplete && formComplete && shipping return ( <>
{/* ─── Hero ─────────────────────────────────────────────── */}
{/* Text side */}

Blood Bros Sport presenta

Cursa
de la
Cirera

Corbins 2026

L'equipament oficial de la Cursa. Samarreta tècnica Fullprint i mitjons exclusius Blood Bros Socks. Edició limitada. Comandes fins al 25 de juny.

Pagament segur via Stripe
{/* Image side */}
Pack Samarreta + Mitjons Cursa de la Cirera 2026
{/* ─── STEP 1: PRODUCTES ────────────────────────────────── */}

Pas 1 de 4 · Producte

Quin equipament vols?

{errors.product && (

{errors.product}

)}
{(Object.values(PRODUCTS) as typeof PRODUCTS[keyof typeof PRODUCTS][]).map((p) => ( ))}
{/* ─── STEP 2: TALLES ───────────────────────────────────── */}
{selectedProduct && (

Pas 2 de 4 · Talles

La teva talla

{errors.sizes && (

{errors.sizes}

)}
{/* Tshirt sizes */} {product?.hasTshirt && (

Samarreta

{TSHIRT_SIZES.map((s) => ( ))}
{sizeTshirt && (

Talla {sizeTshirt} seleccionada

)}
)} {/* Socks sizes */} {product?.hasSocks && (

Mitjons

{SOCK_SIZES.map(({ label, range }) => ( ))}
{sizeSocks && (

Talla {sizeSocks} seleccionada

)}
)}
)} {/* ─── STEP 3: DADES ────────────────────────────────────── */}
{selectedProduct && sizesComplete && (

Pas 3 de 4 · Les teves dades

On t'enviem la comanda?

{/* Nom */}
handleField('nom', e.target.value)} autoComplete="given-name" /> {errors.nom && (

{errors.nom}

)}
{/* Cognoms */}
handleField('cognoms', e.target.value)} autoComplete="family-name" /> {errors.cognoms && (

{errors.cognoms}

)}
{/* Telèfon */}
handleField('telefon', e.target.value)} autoComplete="tel" /> {errors.telefon && (

{errors.telefon}

)}
{/* Email */}
handleField('email', e.target.value)} autoComplete="email" /> {errors.email && (

{errors.email}

)}
{/* Adreça */}
handleField('adreca', e.target.value)} autoComplete="street-address" /> {errors.adreca && (

{errors.adreca}

)}
{/* CP */}
handleField('codiPostal', e.target.value)} maxLength={5} autoComplete="postal-code" /> {errors.codiPostal && (

{errors.codiPostal}

)}
{/* Població */}
handleField('poblacio', e.target.value)} autoComplete="address-level2" /> {errors.poblacio && (

{errors.poblacio}

)}
{/* Província */}
{errors.provincia && (

{errors.provincia}

)}
{formComplete && (
Dades correctes. Ara selecciona com vols rebre la comanda.
)}
)} {/* ─── STEP 4: ENVIAMENT ────────────────────────────────── */}
{selectedProduct && sizesComplete && formData.nom && (

Pas 4 de 4 · Lliurament

Com vols rebre-ho?

{errors.shipping && (

{errors.shipping}

)}
{/* Correos Express */} {/* Recollida */}
)} {/* ─── RESUM I PAGAMENT ─────────────────────────────────── */}
{selectedProduct && sizesComplete && formData.nom && shipping && (

Resum de la comanda

Llest per pagar?

{/* Summary items */}
{product!.fullName}
{product!.fullName}
{sizeTshirt && `Samarreta: ${sizeTshirt}`} {sizeTshirt && sizeSocks && ' · '} {sizeSocks && `Mitjons: ${sizeSocks}`}
{product!.price.toFixed(2).replace('.', ',')}€
{shipping === 'correos' ? 'Correos Express' : 'Recollida Corbins/Lleida'} {shipping === 'correos' ? '7,99€' : 'Gratuït'}
TOTAL {total.toFixed(2).replace('.', ',')}€
{/* Client summary */}

Comanda per a

{formData.nom} {formData.cognoms}

{formData.email} · {formData.telefon}

{shipping === 'correos' && formData.adreca && (

{formData.adreca}, {formData.codiPostal} {formData.poblacio}

)}
{/* Pay button */}

Seràs redirigit a Stripe per completar el pagament de forma segura

)} {/* CTA if nothing selected yet */} {!selectedProduct && (
)}