'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 */}
{/* ─── 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}
{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 && (
)}
{/* Size guide modal */}
{sizeGuide && (
setSizeGuide(null)} />
)}
>
)
}