/* global React */ const { useState: useStateI, useEffect: useEffectI } = React; // ===== FAQ ===== function FAQ() { const [open, setOpen] = useStateI(0); const items = [ ['Can I bring a plus-one?', "We'd love to keep things intimate. If your invitation lists a plus-one, you'll see their name on the RSVP. Please ask us if you're unsure!"], ['What time should I arrive?', "Please arrive by 1:30 PM for seating. The ceremony begins at 2:00 PM, and we'd hate for you to miss the procession."], ['Will it be outdoors?', "Yes and no — the ceremony will be held indoors at the Kidapawan City Seventh-day Adventist Church, while the reception will be held outdoors at The Cascades by Amador."], ['What about parking?', "Parking is available at both the church and the reception venue."], ['Is there a gift registry?', "Your presence is the only present we need. If you'd like to give a gift, an envelope at the reception is most welcome — we're saving for a home of our own."]]; return (
VIII. Questions

A few things worth knowing in advance.

{items.map(([q, a], i) =>
{a}
)}
); } // ===== RSVP (multi-step) ===== function RSVP() { const [step, setStep] = useStateI(0); const [data, setData] = useStateI({ name: '', email: '', attending: null, guests: [{ name: '' }] }); const [errors, setErrors] = useStateI({}); const [submitting, setSubmitting] = useStateI(false); const [submitError, setSubmitError] = useStateI(''); const RSVP_ENDPOINT = 'https://script.google.com/macros/s/AKfycbw9tRkrQ1ExPl2mwl_GgRAXcvjw9V53uKnn9ABQzwEd9AqsyekITomv1f1hF3ptyZuUkw/exec'; async function submitRSVP() { setSubmitError(''); setSubmitting(true); const guestNames = data.attending === 'yes' ? data.guests.map((g) => g.name.trim()).filter(Boolean) : []; const payload = { name: data.name.trim(), email: data.email.trim(), attendance: data.attending === 'yes' ? 'Attending' : 'Not attending', // First companion goes in guestName; any beyond that go in additionalGuests. guestName: guestNames[0] || '', additionalGuests: guestNames.slice(1) }; try { const res = await fetch(RSVP_ENDPOINT, { method: 'POST', // text/plain keeps this a "simple" request so the browser skips the // CORS preflight that Apps Script web apps don't answer; the body is // still JSON for the script to parse server-side. headers: { 'Content-Type': 'text/plain;charset=utf-8' }, body: JSON.stringify(payload), redirect: 'follow' }); if (!res.ok) throw new Error(`HTTP ${res.status}`); setSubmitting(false); setStep(3); } catch (err) { setSubmitting(false); setSubmitError("We couldn't send your reply just now. Please check your connection and try again."); } } function set(k, v) {setData((d) => ({ ...d, [k]: v }));} function setGuest(i, k, v) { setData((d) => ({ ...d, guests: d.guests.map((g, gi) => gi === i ? { ...g, [k]: v } : g) })); } function addGuest() {setData((d) => ({ ...d, guests: [...d.guests, { name: '' }] }));} function removeGuest(i) {setData((d) => ({ ...d, guests: d.guests.filter((_, gi) => gi !== i) }));} function next() { const e = {}; if (step === 0) { if (!data.name.trim()) e.name = 'Required'; if (!data.email.includes('@')) e.email = 'Valid email'; } if (step === 1 && data.attending === null) e.attending = 'Pick one'; if (step === 2 && data.attending === 'yes') { data.guests.forEach((g, i) => { if (!g.name.trim()) e[`g${i}`] = 'Name required'; }); } setErrors(e); if (Object.keys(e).length === 0) { setSubmitError(''); // A decline submits straight from step 1; an acceptance submits from // step 2 (after collecting guests). Both paths POST before showing "Done". if (data.attending === 'no' && step === 1) { submitRSVP(); } else if (step === 2) { submitRSVP(); } else { setStep((s) => s + 1); } } } function back() {setStep((s) => Math.max(0, s - 1));} const steps = ['You', 'Attending', 'Guests', 'Done']; return (
IX. Kindly Reply

Please let us know by June 12, 2026.

{steps.map((s, i) =>
{String(i + 1).padStart(2, '0')} · {s}
)}
{step === 0 &&
set('name', e.target.value)} placeholder="Maria Santos" /> {errors.name &&
{errors.name}
}
set('email', e.target.value)} placeholder="maria@example.com" /> {errors.email &&
{errors.email}
}
} {step === 1 &&
set('attending', 'yes')}>
Joyfully accept
I wouldn't miss it
set('attending', 'no')}>
Regretfully decline
Sending love from afar
{errors.attending &&
{errors.attending}
} {submitError &&
{submitError}
}
} {step === 2 && data.attending === 'yes' &&
{data.guests.map((g, i) =>
setGuest(i, 'name', e.target.value)} placeholder="Full name" />
{data.guests.length > 1 && }
)} {submitError &&
{submitError}
}
} {step === 3 &&
{data.attending === 'yes' ? '♥' : '✦'}

{data.attending === 'yes' ? 'See you in June.' : 'You will be missed.'}

Thank you, {data.name.split(' ')[0]}. {data.attending === 'yes' ? `We've saved a seat for ${data.guests.length} — and noted everything.` : "Thank you for letting us know. We'll raise a glass for you."}

A confirmation will follow with the final details.

}
); } // ===== PLAYLIST ===== function Playlist() { const [title, setTitle] = useStateI(''); const [artist, setArtist] = useStateI(''); const [name, setName] = useStateI(''); const [list, setList] = useStateI(() => { try {return JSON.parse(localStorage.getItem('jc-dj-playlist') || '[]') || seed;} catch {return seed;} }); const seed = [ { t: 'At Last', a: 'Etta James', by: 'Mom' }, { t: "Can't Help Falling in Love", a: 'Elvis Presley', by: 'Tito Bong' }, { t: 'September', a: 'Earth, Wind & Fire', by: 'Aira' }, { t: 'Pag-ibig na Kaya', a: 'Ben&Ben', by: 'Mika' }]; useEffectI(() => { if (list.length === 0) setList(seed); }, []); useEffectI(() => { try {localStorage.setItem('jc-dj-playlist', JSON.stringify(list));} catch {} }, [list]); function add() { if (!title.trim() || !artist.trim()) return; setList((l) => [{ t: title, a: artist, by: name || 'Anonymous' }, ...l]); setTitle('');setArtist(''); } return (
X. For the DJ

Help us fill the dance floor.

setTitle(e.target.value)} placeholder="At Last" />
setArtist(e.target.value)} placeholder="Etta James" />
setName(e.target.value)} placeholder="Optional" />
{list.map((s, i) =>
{String(i + 1).padStart(2, '0')}
{s.t}
{s.a}
— {s.by}
)}
); } // ===== GIFTS + WELL-WISHES (merged) ===== function Gifts() { const seed = []; const [list, setList] = useStateI(() => { try { const stored = JSON.parse(localStorage.getItem('jc-dj-guestbook-v2') || 'null'); return Array.isArray(stored) ? stored : seed; } catch {return seed;} }); const [m, setM] = useStateI(''); const [by, setBy] = useStateI(''); const [qrZoom, setQrZoom] = useStateI(false); useEffectI(() => { if (!qrZoom) return; const onKey = (e) => { if (e.key === 'Escape') setQrZoom(false); }; window.addEventListener('keydown', onKey); const prev = document.body.style.overflow; document.body.style.overflow = 'hidden'; return () => { window.removeEventListener('keydown', onKey); document.body.style.overflow = prev; }; }, [qrZoom]); useEffectI(() => { try {localStorage.setItem('jc-dj-guestbook-v2', JSON.stringify(list));} catch {} }, [list]); function post() { if (!m.trim() || !by.trim()) return; setList((l) => [{ m, by }, ...l]); setM('');setBy(''); } const RES_I = typeof window !== 'undefined' && window.__resources || {}; const qrSrc = RES_I.gcashQr || 'images/gcash-qr-enhanced.png'; return (
X. Gifts & Well-Wishes

The gift of your presence is more than enough.

Leave a Blessing

No gift expected — a few warm words are the loveliest thing you could leave us with.