// Homepage: photo hero, real totals, programs, where the money went, how it works, stories. const KIND_LABEL = { masuk: 'Donasi', barang: 'Donasi barang', keluar: 'Pembelian' }; function Hero() { const [s, setS] = React.useState(null); React.useEffect(() => { getSettings().then(setS); }, []); const wa = s && waLink(s.whatsapp); const img = s && (s.hero_image || s.story_image1); return (

Dua Tangan, Satu Perubahan.

Organisasi sosial di Pekanbaru. Kami menyalurkan bantuan langsung ke yang membutuhkan, dan mencatat setiap donasi serta setiap pembelian secara terbuka.

Lihat program {wa ? WhatsApp kami : Hubungi kami}
{img ? Kegiatan Dua Tangan bersama anak-anak panti : Logo Dua Tangan} {img && Logo Dua Tangan}
); } function Totals() { const [t, setT] = React.useState(null); React.useEffect(() => { getJSON('/api/stats.php').then(setT).catch(() => {}); }, []); if (!t) return null; const stats = [ { label: 'Donasi terkumpul', value: t.total_raised, format: formatRpFull, note: `dari ${t.total_donors} donasi uang` }, { label: 'Sudah dibelanjakan', value: t.total_spent, format: formatRpFull, note: `${t.purchase_count} pembelian tercatat` }, { label: 'Program', value: t.causes_active + t.causes_done, format: n => n, note: `${t.causes_active} berjalan, ${t.causes_done} selesai` }, ]; return (
{stats.map(st => (
{st.label}
{st.note}
))}

Dihitung langsung dari catatan donasi dan pembelian{t.total_items > 0 ? `, belum termasuk donasi barang senilai sekitar ${formatRpFull(t.total_items)}` : ''}.

); } function Programs() { const [causes, setCauses] = React.useState(null); const [cat, setCat] = React.useState('all'); React.useEffect(() => { getJSON('/api/causes.php').then(d => setCauses(sortPrograms(Array.isArray(d) ? d : []))).catch(() => setCauses([])); }, []); if (causes && causes.length === 0) return null; const cats = [...new Set((causes || []).map(c => c.category))]; const shown = (causes || []).filter(c => cat === 'all' || c.category === cat); return (

Program kami

Setiap program punya halaman sendiri berisi donatur, pembelian, dan kabar terbaru.

Semua program
{cats.length > 1 && ( ({ value: c, label: CATEGORY_ID[c] || c, count: causes.filter(x => x.category === c).length }))]} /> )} {causes === null ?

Memuat program…

:
{shown.slice(0, 3).map(c => )}
}
); } function RecentLedger() { const [rows, setRows] = React.useState(null); const [kind, setKind] = React.useState('all'); React.useEffect(() => { getJSON('/api/ledger.php?limit=40').then(setRows).catch(() => setRows([])); }, []); if (!rows || rows.length === 0) return null; const isIn = r => r.kind !== 'keluar'; const shown = rows.filter(r => kind === 'all' || (kind === 'in' ? isIn(r) : !isIn(r))); return (

Ke mana uangnya pergi

Catatan terbaru dari semua program: donasi yang masuk dan barang yang kami beli.

!isIn(r)).length }, ]} />
{shown.map((r, i) => { const out = r.kind === 'keluar'; return ( ); })}
TanggalKeteranganMasukKeluar
{fmtDate(r.at)}
{KIND_LABEL[r.kind]}{r.label || 'Hamba Allah (anonim)'}
{out && r.vendor && <>{r.vendor}{r.receipt_id && <> · nota {r.receipt_id}} · } {r.cause_id ? {r.cause_title} : 'Kas umum'}
{!out ? (r.kind === 'barang' ? '≈ ' : '') + formatRpFull(r.amount) : ''} {out ? formatRpFull(r.amount) : ''}

{shown.length} catatan terbaru.{shown.length > 6 && ' Geser tabel ke bawah untuk melihat semuanya.'}

); } function HowItWorks() { const steps = [ { title: 'Anda menghubungi kami', body: 'Lewat WhatsApp, Instagram, atau formulir kontak. Pilih program yang ingin dibantu, dengan uang atau barang.' }, { title: 'Kami catat dan belanjakan', body: 'Setiap donasi dicatat per program. Setiap pembelian ditulis lengkap dengan toko, harga, dan nomor nota.' }, { title: 'Anda lihat hasilnya', body: 'Foto, penerima manfaat, dan kabar terbaru ditambahkan ke halaman program sampai program selesai.' }, ]; return (

Cara kerja

    {steps.map((st, i) => (
  1. {st.title}

    {st.body}

  2. ))}
); } function Stories() { const [posts, setPosts] = React.useState([]); React.useEffect(() => { getJSON('/api/blog.php').then(d => setPosts(d.posts || [])).catch(() => {}); }, []); if (!posts.length) return null; const list = posts.slice(0, 3); return (

Cerita dari lapangan

Semua cerita
{list.map(p => )}
); } function App() { return ( <>