'use client'; import { useEffect, useState } from 'react'; import { useParams } from 'next/navigation'; import DOMPurify from 'dompurify'; import { authApi, wishlistsApi, itemsApi, claimingApi, settingsApi, type Wishlist, type Item, type Settings } from '@/lib/api'; import Header from '@/components/header'; import GuestGuard from '@/components/guest-guard'; export default function PublicWishlistPage() { return ( ); } function PublicWishlistContent() { const params = useParams(); const [wishlist, setWishlist] = useState(null); const [items, setItems] = useState([]); const [siteSettings, setSiteSettings] = useState({ siteTitle: '', homepageSubtext: '', claimingEnabled: true, showQuantity: true }); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(''); // Current viewer const [currentGuestId, setCurrentGuestId] = useState(null); const [isAdmin, setIsAdmin] = useState(false); // Claim form state const [claimingItemId, setClaimingItemId] = useState(null); const [claimNote, setClaimNote] = useState(''); const [claimQty, setClaimQty] = useState(1); const [isClaiming, setIsClaiming] = useState(false); const [claimError, setClaimError] = useState(''); const [justClaimedItemId, setJustClaimedItemId] = useState(null); // Unclaim state const [isUnclaiming, setIsUnclaiming] = useState(false); useEffect(() => { (async () => { try { const who = await authApi.whoami(); if (who.role === 'admin') { setIsAdmin(true); if (who.guest) setCurrentGuestId(who.guest.id); } else if (who.role === 'guest') { setCurrentGuestId(who.guest.id); } } catch { /* ignore */ } })(); }, []); useEffect(() => { settingsApi.getSettings().then(setSiteSettings).catch(() => {}); fetchWishlist(); }, [params.slug]); const fetchWishlist = async () => { if (!params.slug) return; try { const wishlistData = await wishlistsApi.getBySlug(params.slug as string); setWishlist(wishlistData); const itemsData = await itemsApi.getAll(wishlistData.id); setItems(itemsData.sort((a, b) => a.sortOrder - b.sortOrder)); } catch (err: any) { setError(err.message || 'Wishlist not found'); } finally { setIsLoading(false); } }; const myClaimFor = (item: Item) => item.claims.find((c) => c.guest.id === currentGuestId); const handleStartClaim = (item: Item) => { const my = myClaimFor(item); setClaimingItemId(item.id); setClaimError(''); setClaimNote(my?.note ?? ''); setClaimQty(my?.quantity ?? 1); setJustClaimedItemId(null); }; const handleSubmitClaim = async (e: React.FormEvent, itemId: string) => { e.preventDefault(); setIsClaiming(true); setClaimError(''); try { await claimingApi.claim(itemId, { quantity: claimQty, note: claimNote }); setJustClaimedItemId(itemId); setClaimingItemId(null); setClaimNote(''); setClaimQty(1); fetchWishlist(); } catch (err: any) { setClaimError(err.message || 'Erro ao reservar item'); } finally { setIsClaiming(false); } }; const handleUnclaim = async (itemId: string, guestId?: string) => { if (!confirm('Cancelar a reserva?')) return; setIsUnclaiming(true); try { await claimingApi.unclaim(itemId, guestId ? { guestId } : {}); fetchWishlist(); } catch (err: any) { alert(err.message || 'Erro ao cancelar reserva'); } finally { setIsUnclaiming(false); } }; const filteredItems = items.filter((item) => { const my = myClaimFor(item); return item.remainingQuantity > 0 || my || item.id === justClaimedItemId; }); if (isLoading) { return (

Carregando...

); } if (error || !wishlist) { return (

Lista não encontrada

{error || 'Esta lista não existe ou não é pública.'}

); } return (
{/* Main Content */}
{/* Preferences Section */} {wishlist.preferences && (

Interesses e Preferências

{ const target = e.target as HTMLElement; if (target.tagName === 'A') { e.preventDefault(); window.open((target as HTMLAnchorElement).href, '_blank', 'noopener,noreferrer'); } }} />
)} {/* Controls */}
{filteredItems.length} de {items.length} itens
{/* Items List */} {filteredItems.length === 0 ? (

Todos os itens já foram reservados!

) : (
{filteredItems.map((item) => { const myClaim = myClaimFor(item); const maxForMe = item.remainingQuantity + (myClaim?.quantity ?? 0); const showQuantitySummary = item.quantity > 1 && siteSettings.showQuantity; const sold = item.remainingQuantity === 0 && !myClaim; const canClaim = siteSettings.claimingEnabled; return (
{/* Top: Image */} {item.imageUrl && (
{item.name}
)} {/* Middle: Item Details */}

{item.name}

{item.price != null && (

{new Intl.NumberFormat('pt-BR', { style: 'currency', currency: item.currency || 'BRL' }).format(item.price)}

)} {showQuantitySummary && (

{item.claimedQuantity} de {item.quantity} reservados

)} {item.description && (

{item.description}

)} {/* Existing claims list */} {item.claims.length > 0 && (
    {item.claims.map((c) => { const isMine = c.guest.id === currentGuestId; const canCancel = isMine || isAdmin; return (
  • {isMine ? 'Você' : 'Reservado'} {item.quantity > 1 && ( · {c.quantity} un. )} {c.note && isMine && ( "{c.note}" )}
    {canCancel && ( )}
  • ); })}
)}
{/* Bottom: Action Area */}
{item.purchaseUrls && item.purchaseUrls.length > 0 && (
{item.purchaseUrls.map((url, idx) => ( {url.label} ))}
)} {!canClaim ? (
Reservas desativadas
) : sold ? (
Esgotado
) : claimingItemId === item.id ? (
handleSubmitClaim(e, item.id)} className="space-y-3"> {claimError && (
{claimError}
)} {item.quantity > 1 && siteSettings.showQuantity && (
setClaimQty(Math.max(1, Math.min(maxForMe, Number(e.target.value) || 1)))} className="w-full px-3 py-2 text-sm border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-green-500 dark:bg-gray-700 dark:text-white" />
)}