/* ============================================================= * Action pages: Donate, Get Involved, Contact, Events, Team Login * ============================================================= */ /* ---------- Donate ---------- */ function DonatePage() { const [path, setPath] = useState("momo"); // momo | card const [amount, setAmount] = useState(30000); const [custom, setCustom] = useState(""); const [freq, setFreq] = useState("monthly"); const [form, setForm] = useState({ name: "", email: "", phone: "", network: "MTN", cardNumber: "", cardExpiry: "", cardCVV: "", cardType: "visa", anon: false }); const [errors, setErrors] = useState({}); const [stage, setStage] = useState("form"); // form | processing | done const presets = path === "momo" ? [{ v: 30000, l: "USh 30,000", h: "1 father · 1 month" }, { v: 90000, l: "USh 90,000", h: "1 father · 3 months" }, { v: 360000, l: "USh 360,000", h: "1 Forge · 1 month" }] : [{ v: 25, l: "$25", h: "1 father · 1 month" }, { v: 75, l: "$75", h: "1 father · 3 months" }, { v: 250, l: "$250", h: "1 Forge · 1 month" }]; const total = custom ? Number(custom) : amount; const detectCardType = (number) => { const cleaned = number.replace(/\s/g, ''); if (/^4/.test(cleaned)) return 'visa'; if (/^5[1-5]/.test(cleaned)) return 'mastercard'; if (/^3[47]/.test(cleaned)) return 'amex'; if (/^6(?:011|5)/.test(cleaned)) return 'discover'; return 'visa'; }; const formatCardNumber = (value) => { const cleaned = value.replace(/\s/g, ''); const type = detectCardType(cleaned); setForm({ ...form, cardType: type }); // Format with spaces every 4 digits const formatted = cleaned.match(/.{1,4}/g)?.join(' ') || cleaned; return formatted.slice(0, 19); // Max 16 digits + 3 spaces }; const formatExpiry = (value) => { const cleaned = value.replace(/\D/g, ''); if (cleaned.length >= 2) { return cleaned.slice(0, 2) + '/' + cleaned.slice(2, 4); } return cleaned; }; const validate = () => { const e = {}; if (!form.name.trim() && !form.anon) e.name = "Required"; if (!form.email.trim() || !/^[^@]+@[^@]+\.[^@]+$/.test(form.email)) e.email = "Valid email required"; if (path === "momo") { if (!/^[+0-9 -]{7,}$/.test(form.phone)) e.phone = "Mobile number required"; } else { // Card validation const cardNum = form.cardNumber.replace(/\s/g, ''); if (cardNum.length < 13 || cardNum.length > 19) e.cardNumber = "Invalid card number"; if (!/^\d{2}\/\d{2}$/.test(form.cardExpiry)) e.cardExpiry = "MM/YY format required"; if (!/^\d{3,4}$/.test(form.cardCVV)) e.cardCVV = "Invalid CVV"; } if (!total || total <= 0) e.amount = "Pick or enter an amount"; setErrors(e); return Object.keys(e).length === 0; }; // Phase 3.9: store receipt details after successful payment for download const [receipt, setReceipt] = useState(null); // Save donation to Supabase + build a receipt object for the success screen. const recordDonation = async (txResponse) => { const txRef = (txResponse && (txResponse.tx_ref || txResponse.transaction_id)) || ('FA-' + Date.now()); const currency = path === "momo" ? "UGX" : "USD"; const donor = form.anon ? "Anonymous" : form.name; const rec = { tx_ref: txRef, donor_name: donor, donor_email: form.email, donor_phone: form.phone || null, amount: total, currency: currency, frequency: freq, payment_method: path === "momo" ? "Mobile Money" : "Card", anonymous: !!form.anon, date: new Date().toISOString() }; setReceipt(rec); // Best-effort Supabase insert into existing 'donations' table. // Extra fields (tx_ref, email, phone, payment_method, anon flag) are folded into `notes` // as a pipe-separated string so the OS can parse them back when displaying donation history. try { if (window.sb) { const noteParts = [ 'tx_ref:' + rec.tx_ref, 'method:' + rec.payment_method, rec.donor_email ? 'email:' + rec.donor_email : null, rec.donor_phone ? 'phone:' + rec.donor_phone : null, rec.anonymous ? 'anon:true' : null ].filter(Boolean); await window.sb.from('donations').insert({ donor_name: rec.donor_name, amount: rec.amount, amount_ugx: rec.currency === 'UGX' ? rec.amount : null, donation_date: rec.date.slice(0, 10), is_recurring: rec.frequency === 'monthly', frequency: rec.frequency, notes: noteParts.join(' | ') }); } } catch (e) { console.warn('Donation record save failed:', e && e.message); } }; // Build the receipt as a printable HTML window — user can save as PDF via browser print. const downloadReceipt = () => { if (!receipt) return; const dateLabel = new Date(receipt.date).toLocaleDateString(undefined, { year:'numeric', month:'long', day:'numeric' }); const amountFmt = receipt.currency === 'UGX' ? 'USh ' + Number(receipt.amount).toLocaleString() : '$' + Number(receipt.amount).toLocaleString(); const html = `Donation Receipt — Fathers Arise
Fathers Arise

Restoring fatherhood. Building nations.

Reg. NGO Uganda
fathersarize.org
Donation Receipt
Receipt no.${receipt.tx_ref}
Date${dateLabel}
Donor${receipt.donor_name || '—'}
Email${receipt.donor_email || '—'}
Payment method${receipt.payment_method}
Frequency${receipt.frequency === 'monthly' ? 'Monthly recurring' : 'One-time'}
Amount${amountFmt}

"Asante! Every shilling you give places a father into a Forge group — a circle of brothers who keep him present, honest, and in his children's lives."

`; const w = window.open('', '_blank'); if (!w) { alert('Please allow popups to view your receipt'); return; } w.document.write(html); w.document.close(); }; const initFlutterwavePayment = async () => { setStage("processing"); try { // Flutterwave payment configuration const config = { public_key: window.FLUTTERWAVE_PUBLIC_KEY || "FLWPUBK-YOUR-PUBLIC-KEY-HERE", tx_ref: 'FA-' + Date.now(), amount: total, currency: path === "momo" ? "UGX" : "USD", payment_options: path === "momo" ? "mobilemoneyuganda" : "card", customer: { email: form.email, phone_number: form.phone, name: form.anon ? "Anonymous" : form.name, }, customizations: { title: "Fathers Arise", description: `${freq === 'monthly' ? 'Monthly' : 'One-time'} donation`, logo: window.location.origin + "/uploads/logo.png", }, callback: async function(response) { console.log(response); if (response && response.status === "successful") { await recordDonation(response); setStage("done"); } else { setStage("form"); alert("Payment failed. Please try again."); } }, onclose: function() { if (stage !== "done") setStage("form"); } }; // Initialize Flutterwave if (window.FlutterwaveCheckout) { window.FlutterwaveCheckout(config); } else { // Fallback: load script and retry const script = document.createElement('script'); script.src = 'https://checkout.flutterwave.com/v3.js'; script.onload = () => window.FlutterwaveCheckout(config); document.body.appendChild(script); } } catch (error) { console.error('Payment error:', error); setStage("form"); alert("Payment initialization failed. Please try again."); } }; const submit = (e) => { e.preventDefault(); if (!validate()) return; initFlutterwavePayment(); }; // Phase 3.7: editable site content const [cmsDonate, setCmsDonate] = useState({}); useEffect(() => { if (window.cms && typeof window.cms.getSiteContent === "function") { window.cms.getSiteContent("donate").then(setCmsDonate); } }, []); const dHero = cmsDonate.hero || {}; return (
{[["momo", "Mobile Money"], ["card", "Card Payment"]].map(([k, l]) => ( ))}
{stage === "done" ? (

Asante! You just helped restore a father.

{receipt && (
Receipt
No. {receipt.tx_ref}
Amount: {receipt.currency === 'UGX' ? 'USh ' + Number(receipt.amount).toLocaleString() : '$' + Number(receipt.amount).toLocaleString()}
Donor: {receipt.donor_name}
)}
{receipt && ( )}

A copy has also been saved in our records.

) : stage === "processing" ? (

Processing payment...

Please complete the payment in the popup window

) : (
{/* Frequency toggle */}
Frequency
{[["once", "Once"], ["monthly", "Monthly · most impact"]].map(([k, l]) => ( ))}
{/* Amount */}
Amount
{presets.map((p) => ( ))}
{path === "momo" ? "USh" : "$"} { setCustom(e.target.value.replace(/[^0-9]/g, "")); setAmount(0); }} placeholder="Or enter a custom amount" className="w-full pl-14 pr-4 py-3 rounded-xl bg-white border border-navy/15 focus:border-sky focus:ring-2 focus:ring-sky/20 outline-none transition" />
{errors.amount &&
{errors.amount}
}
{/* Identity & Contact */}
setForm({ ...form, name: e.target.value })} placeholder="Full name" /> setForm({ ...form, email: e.target.value })} placeholder="you@example.com" />
{path === "momo" ? ( <> setForm({ ...form, phone: e.target.value })} placeholder="+256 700 245 880" />
{["MTN", "Airtel"].map((n) => ( ))}
) : ( <> {/* Card Information */}
setForm({ ...form, cardNumber: formatCardNumber(e.target.value) })} placeholder="1234 5678 9012 3456" maxLength="19" />
{form.cardType === 'visa' && VISA} {form.cardType === 'mastercard' && } {form.cardType === 'amex' && AMEX}
setForm({ ...form, cardExpiry: formatExpiry(e.target.value) })} placeholder="MM/YY" maxLength="5" /> setForm({ ...form, cardCVV: e.target.value.replace(/\D/g, '').slice(0, 4) })} placeholder="123" maxLength="4" />
Payments secured by Flutterwave. We never store your card details.
)}
Secured · 100% to programs · Tax-deductible (US/UK/UG)
)}
); } /* ---------- Get Involved (brief: 4 pathways + Donation) ---------- */ const PATHWAYS = [ { k: "register", color: "sky", title: "Register as a Father", desc: "Join a Forge group and begin the 12-week journey. Fill in a short registration form and we will connect you to a group.", cta: "Register Now", img: "uploads/TRG_Father_sBreakfast125.jpg" }, { k: "chapter", color: "navy", title: "Start a Be A Man Chapter", desc: "Are you a teacher, chaplain, or youth leader? Bring Be A Man to your school or community. We provide the curriculum and training.", cta: "Apply to Start a Chapter", img: "uploads/FA-09310.jpg" }, { k: "volunteer", color: "gold", title: "Become a Volunteer Mentor", desc: "If you are a stable, mature man willing to invest in the next generation, we need you. Training provided.", cta: "Volunteer Application", img: "uploads/FATHERS ARISE MEETING-66.jpg" }, { k: "partner", color: "forest", title: "Partner or Donate", desc: "Churches, NGOs, corporates, and individuals — partner with the movement. Your support fuels school chapters, Forge groups, retreats, and media production.", cta: "Give / Partner", img: "uploads/FATHERS ARISE-135-9f499005.jpg" }, ]; function GetInvolvedPage() { const [active, setActive] = useState(null); // Phase 3.7: editable site content const [cms, setCms] = useState({}); useEffect(() => { if (window.cms && typeof window.cms.getSiteContent === "function") { window.cms.getSiteContent("getinvolved").then(setCms); } }, []); const hero = cms.hero || {}; return (
Get Involved

{hero.headline || <>There Is a Place for You}

{hero.subhead || "Whether you want to register, volunteer, partner, or give — the movement needs you."}

{PATHWAYS.map((p, i) => { const accent = p.color === "sky" ? "border-sky" : p.color === "gold" ? "border-gold" : p.color === "forest" ? "border-forest" : "border-navy"; const txt = p.color === "sky" ? "text-sky" : p.color === "gold" ? "text-[#A07700]" : p.color === "forest" ? "text-forest" : "text-navy"; return (
{p.title}
{String(i + 1).padStart(2, "0")}

{p.title}

{p.desc}

); })}
{/* Impact Report — downloadable */}
Fathers Arise Impact Report
PDF · 2025 Annual Report
Transparency

See what your support builds.

A year of fathers restored, families rebuilt, and Forge groups planted across Uganda — in the numbers, the photos, and the testimonies. Download the full report and see exactly where every shilling goes.

547+
Fathers
82
Forge Groups
25
Communities

Last updated: 2025 · 20 pages · Free to share with churches, partners, and donors.

{/* Donation block */}
Donate

Your gift fuels the movement.

Mobile Money support is critical for Uganda. We also accept bank transfer and international cards.

{[ { name: "Mobile Money", desc: "MTN MoMo / Airtel Money", detail: "Quick local giving", color: "#FFCC00" }, { name: "Bank Transfer", desc: "UG / UK / US accounts", detail: "Details on request", color: "#2E96D4" }, { name: "PayPal / Card", desc: "International donors", detail: "Visa, Mastercard, Amex", color: "#2ECC71" }, ].map((m) => (

{m.name}

{m.desc}

{m.detail}
))}

Digital receipt provided for every gift.

{active && setActive(null)} />}
); } function PathwayModal({ pathway, onClose }) { const p = PATHWAYS.find((x) => x.k === pathway); const fields = { register: ["Full name", "Phone (WhatsApp)", "Email", "City / location", "Number of children"], chapter: ["School / institution name", "Your name (role)", "Phone (WhatsApp)", "Email", "Number of students"], volunteer: ["Your name", "Phone (WhatsApp)", "Email", "City / region", "Skills / availability"], partner: ["Organisation name", "Contact person", "Email", "Type of partnership", "Brief about your org"], }; const [vals, setVals] = useState(Array(5).fill("")); const [done, setDone] = useState(false); const [submitting, setSubmitting] = useState(false); const [error, setError] = useState(null); // Map pathway → Supabase table + row shape (only volunteer & partner use the modal now) const submitPathway = async (e) => { e.preventDefault(); if (!window.sb) { setError("Connection error. Please reload."); return; } setSubmitting(true); setError(null); try { let table = null, row = null; if (pathway === "volunteer") { table = "fa_volunteer_applications"; row = { full_name: vals[0], phone: vals[1], email: vals[2], city: vals[3], skills: vals[4], source: "website" }; } else if (pathway === "partner") { table = "fa_partner_inquiries"; row = { organisation: vals[0], contact_name: vals[1], email: vals[2], partnership_type: vals[3], about: vals[4], source: "website" }; } else { // Fallback for any future pathway — generic registrations table table = "registrations"; row = { pathway, name: vals[0], phone: vals[1], email: vals[2], city: vals[3], data: { extra: vals[4] }, source: "website" }; } const { error: insertError } = await window.sb.from(table).insert(row); if (insertError) throw insertError; setDone(true); } catch (err) { console.error("Pathway submit failed:", err); setError("We couldn't send your request. Please try again or WhatsApp +256 700 669 586."); } finally { setSubmitting(false); } }; return (
e.stopPropagation()} className="w-full max-w-2xl bg-white rounded-3xl shadow-2xl overflow-hidden max-h-[90vh] overflow-y-auto">
{p.cta}

{p.title}

{done ? (

Asante! We'll be in touch.

A team member will reach out via WhatsApp or email within five working days.

) : (

{p.desc}

{fields[pathway].map((f, i) => ( { const v = [...vals]; v[i] = e.target.value; setVals(v); }} /> ))}
{error &&
{error}
}
)}
); } function PathwayIcon({ name }) { const p = { width: 22, height: 22, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round" }; switch (name) { case "church": return ; case "handshake": return ; case "user": return ; default: return null; } } function PathwayForm({ pathway }) { const titles = { church: "Plant Forge groups in your church", partner: "Build something with us", volunteer: "Show up where it counts", }; const fields = { church: ["Church name", "Your name (Pastor/Elder)", "Phone (WhatsApp)", "Approx. men in congregation"], partner: ["Organisation name", "Your name", "Email", "How would you like to partner?"], volunteer: ["Your name", "Phone (WhatsApp)", "City / region", "Skills you'd bring"], }; const [vals, setVals] = useState(["", "", "", ""]); const [done, setDone] = useState(false); if (done) return (

Got it. We'll be in touch this week.

A team member will reach out via WhatsApp or email within five working days.

); return (
{ e.preventDefault(); setDone(true); }}>
{pathway === "volunteer" ? "Volunteer" : pathway === "partner" ? "Partner" : "Church"}

{titles[pathway]}

Five fields. Five days to respond. That's our promise.

{fields[pathway].map((f, i) => ( { const v = [...vals]; v[i] = e.target.value; setVals(v); }} /> ))}
); } /* ---------- Contact (per brief) ---------- */ function ContactPage() { const [form, setForm] = useState({ name: "", email: "", phone: "", subject: "General", msg: "" }); const [done, setDone] = useState(false); const [submitting, setSubmitting] = useState(false); const [error, setError] = useState(null); const submitContact = async (e) => { e.preventDefault(); if (!window.sb) { setError("Connection error. Please reload the page."); return; } setSubmitting(true); setError(null); try { const { error: insertError } = await window.sb.from("fa_contact_messages").insert({ name: form.name, email: form.email, phone: form.phone, subject: form.subject, message: form.msg, language: (window.getCurrentLang && window.getCurrentLang()) || "en" }); if (insertError) throw insertError; setDone(true); } catch (err) { console.error("Contact submit failed:", err); setError("We couldn't send your message. Please try again or WhatsApp +256 700 669 586."); } finally { setSubmitting(false); } }; // Phase 3.7: editable site content const [cmsContact, setCmsContact] = useState({}); useEffect(() => { if (window.cms && typeof window.cms.getSiteContent === "function") { window.cms.getSiteContent("contact").then(setCmsContact); } }, []); const hero = cmsContact.hero || {}; return (
Contact

{hero.headline || <>Get in Touch}

{hero.subhead || "A real person will reply. WhatsApp is fastest."}

{/* Form */}
{done ? (

Asante! Message received.

A real person will reply within two business days.

) : (

Send us a message

setForm({ ...form, name: e.target.value })} /> setForm({ ...form, email: e.target.value })} />
setForm({ ...form, phone: e.target.value })} placeholder="+256 …" />