/* @component-map * App — Main container, loads fonts and renders layout * RSVPForm — RSVP form with name, count, dish selection, note, submit/edit * GuestList — Displays list of RSVPed guests with edit/remove actions * ConfettiOverlay — Confetti animation overlay * @end-component-map */ import { useCollection } from '@deplixo/sdk'; import { RSVPForm } from './components/RSVPForm.jsx'; import { GuestList } from './components/GuestList.jsx'; import { useEffect, useState } from 'react'; import { ConfettiOverlay } from './components/ConfettiOverlay.jsx'; const FONTS_URL = "https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700;800;900&family=Fraunces:opsz,wght@9..144,300;9..144,500;9..144,700;9..144,900&display=swap"; function App() { const { items: guests, loading, add, update, remove } = useCollection("block-party-rsvps", { personal: true }); const [editingGuest, setEditingGuest] = useState(null); const [showConfetti, setShowConfetti] = useState(false); const [submitted, setSubmitted] = useState(false); useEffect(() => { const link = document.createElement("link"); link.rel = "stylesheet"; link.href = FONTS_URL; document.head.appendChild(link); }, []); const totalHeads = guests.reduce((s, g) => s + (g.value?.count || 0), 0); const handleSubmitted = () => { setSubmitted(true); setShowConfetti(true); setEditingGuest(null); setTimeout(() => setShowConfetti(false), 2400); setTimeout(() => setSubmitted(false), 3000); }; const handleEdit = (guest) => { setEditingGuest(guest); setSubmitted(false); window.scrollTo({ top: 0, behavior: "smooth" }); }; if (loading) { return
; } return (
{showConfetti && }
SAT · JUL 19 · 4 PM

Block Party

You're invited to the neighborhood cookout!

{guests.length} {guests.length === 1 ? "household" : "households"}
{totalHeads} {totalHeads === 1 ? "person" : "people"}
{guests.length > 0 && }

📍 Meet at the cul-de-sac · Bring lawn chairs!

); } ReactDOM.createRoot(document.getElementById("root")).render();