'use client'; import Link from 'next/link'; import Image from 'next/image'; import { useEffect, useMemo, useState } from 'react'; import { useAuth } from '@/contexts/AuthContext'; import { useCart } from '@/contexts/CartContext'; function formatPrice(cents: number) { return `€${(cents / 100).toFixed(2)}`; } interface ShippingMethod { id: string; zoneId?: string; name: string; baseCostCents: number; freeShippingThresholdCents: number | null; description: string | null; } interface SavedAddress { id: string; label: string | null; recipientName: string; street: string; city: string; postalCode: string; country: string; isDefault: boolean; } function splitName(fullName: string): { firstName: string; lastName: string } { const trimmed = fullName.trim().replace(/\s+/g, ' '); if (!trimmed) return { firstName: '', lastName: '' }; const parts = trimmed.split(' '); if (parts.length === 1) return { firstName: parts[0], lastName: '' }; return { firstName: parts.slice(0, -1).join(' '), lastName: parts[parts.length - 1] }; } function addressToForm(addr: SavedAddress) { const { firstName, lastName } = splitName(addr.recipientName); return { firstName, lastName, phone: '', address: addr.street, city: addr.city, postalCode: addr.postalCode ?? '', country: addr.country ?? '', }; } export default function CheckoutClient() { const { user, loading: authLoading } = useAuth(); const { items, subtotalCents, itemCount, clearCart } = useCart(); const [submitting, setSubmitting] = useState(false); const [error, setError] = useState(''); const [addresses, setAddresses] = useState([]); const [selectedAddressId, setSelectedAddressId] = useState(null); const [shippingMethods, setShippingMethods] = useState([]); const [shippingMethodId, setShippingMethodId] = useState(null); // Form state const [form, setForm] = useState({ firstName: '', lastName: '', email: user?.email ?? '', phone: '', address: '', city: '', postalCode: '', notes: '', shippingMethod: 'standard', }); // Fetch saved addresses when the customer is logged in. useEffect(() => { if (!user || user.role !== 'customer') { setAddresses([]); setSelectedAddressId(null); return; } let cancelled = false; (async () => { try { const res = await fetch(`/api/users/${user.id}/addresses`, { credentials: 'include', cache: 'no-store' }); if (!res.ok) return; const data = (await res.json()) as { items?: SavedAddress[] }; if (cancelled) return; const list = data.items ?? []; setAddresses(list); const def = list.find((a) => a.isDefault) ?? list[0] ?? null; if (def) { setSelectedAddressId(def.id); setForm((f) => ({ ...f, ...addressToForm(def) })); } } catch { /* ignore */ } })(); return () => { cancelled = true; }; }, [user]); // Fetch shipping methods from the admin-managed catalogue. useEffect(() => { let cancelled = false; (async () => { try { const res = await fetch('/api/shipping/methods', { cache: 'no-store' }); if (!res.ok) return; const data = (await res.json()) as { items?: ShippingMethod[] }; if (cancelled) return; const list = data.items ?? []; setShippingMethods(list); const def = list[0]; if (def) { setShippingMethodId(def.id); setForm((f) => ({ ...f, shippingMethod: def.id })); } } catch { /* ignore */ } })(); return () => { cancelled = true; }; }, []); const selectedAddress = useMemo( () => addresses.find((a) => a.id === selectedAddressId) ?? null, [addresses, selectedAddressId], ); const handleSelectAddress = (id: string) => { setSelectedAddressId(id); const addr = addresses.find((a) => a.id === id); if (addr) setForm((f) => ({ ...f, ...addressToForm(addr) })); }; const selectedShippingMethod = useMemo( () => shippingMethods.find((m) => m.id === shippingMethodId) ?? null, [shippingMethods, shippingMethodId], ); const shippingCost = selectedShippingMethod?.baseCostCents ?? 0; const totalCents = subtotalCents + shippingCost; if (authLoading) { return (
Cargando...
); } if (items.length === 0) { return (
🛒

Tu carrito está vacío

Añade productos antes de hacer el pedido.

Ver productos
); } const handlePlaceOrder = async () => { setSubmitting(true); setError(''); try { const res = await fetch('/api/checkout', { method: 'POST', headers: { 'Content-Type': 'application/json' }, credentials: 'include', body: JSON.stringify({ shippingAddress: { firstName: form.firstName, lastName: form.lastName, email: form.email, phone: form.phone, line1: form.address, city: form.city, postalCode: form.postalCode, country: selectedAddress?.country || form.shippingMethod ? 'ES' : 'ES', }, items: items.map((i) => ({ productId: i.productId, variantId: i.variantId, quantity: i.quantity, })), shippingMethod: form.shippingMethod, notes: form.notes, shippingMethodId: selectedShippingMethod?.id ?? null, }), }); if (res.status === 401) { setError('Debes iniciar sesión para finalizar el pedido.'); return; } if (!res.ok) { const data = await res.json().catch(() => ({})); throw new Error(data.error?.message || 'Error al procesar el pedido'); } const { orderId } = await res.json(); clearCart(); window.location.href = `/order-confirmation?orderId=${orderId}`; } catch (err) { setError(err instanceof Error ? err.message : 'Error al procesar el pedido'); } finally { setSubmitting(false); } }; return (

Finalizar pedido

{/* Form */}
{/* Login prompt — solo si NO está autenticado */} {!user && (

¿Ya tienes cuenta?

Inicia sesión para una experiencia más rápida.

Iniciar sesión Crear cuenta
)} {/* Saved addresses selector — only when logged in */} {user && addresses.length > 0 && (

Direcciones guardadas

{addresses.map((a) => { const active = selectedAddressId === a.id; return ( ); })}

También puedes editar los campos manualmente debajo; se aplicarán al pedido sin guardar nada en tu cuenta.

)} {/* Shipping form */}

Datos de envío

setForm(f => ({ ...f, firstName: e.target.value }))} className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#70ad47] focus:border-transparent outline-none" />
setForm(f => ({ ...f, lastName: e.target.value }))} className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#70ad47] focus:border-transparent outline-none" />
setForm(f => ({ ...f, email: e.target.value }))} className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#70ad47] focus:border-transparent outline-none" />
setForm(f => ({ ...f, phone: e.target.value }))} className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#70ad47] focus:border-transparent outline-none" />
setForm(f => ({ ...f, address: e.target.value }))} className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#70ad47] focus:border-transparent outline-none" />
setForm(f => ({ ...f, city: e.target.value }))} className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#70ad47] focus:border-transparent outline-none" />
setForm(f => ({ ...f, postalCode: e.target.value }))} className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#70ad47] focus:border-transparent outline-none" />

Método de envío

{shippingMethods.length === 0 ? (

No hay métodos de envío configurados.

) : (
{shippingMethods.map(opt => { const active = form.shippingMethod === opt.id; return ( ); })}
)}

Notas del pedido