// DEPLOY_CONFIG: {"triggers": [{"name": "cause_reached_goal_notify_creator_and_donors", "on": "cause.goal_reached", "collection": "causes", "actions": [{"type": "email", "to": "{{creator_email}}", "subject": "Your cause has reached its goal!", "body": "Congratulations! Your cause \"{{cause_name}}\" has reached its fundraising goal."}, {"type": "email", "to": "{{donor_emails}}", "subject": "A cause you supported has reached its goal", "body": "Great news! The cause \"{{cause_name}}\" you donated to has reached its fundraising goal."}]}]} import { useEffect, useMemo, useRef, useState } from 'react'; import { useBroadcast, useCollection, generatePDF, playSound, renderChart } from '@deplixo/sdk'; import { CausesList } from './components/CausesList.jsx'; import { AddCause } from './components/AddCause.jsx'; import { DonationFeed } from './components/DonationFeed.jsx'; import { Stats } from './components/Stats.jsx'; function App() { const [activeTab, setActiveTab] = useState('causes'); const [donationTick, setDonationTick] = useState(0); const [lastDonationAt, setLastDonationAt] = useState(0); const [feedTick, setFeedTick] = useState(0); const soundCooldownRef = useRef(0); const chartsVersionRef = useRef(0); const chartCanvasRefs = useRef({ overTime: null, topCauses: null }); const reportRefs = useRef({}); const { items: causes = [], loading: causesLoading } = useCollection('causes', { personal: true }); const { items: donations = [], loading: donationsLoading } = useCollection('donations', { personal: true }); const tabs = useMemo(() => [ { id: 'causes', label: 'Causes', icon: '๐Ÿ’›' }, { id: 'add', label: 'New Cause', icon: '๏ผ‹' }, { id: 'activity', label: 'Activity', icon: '๐Ÿ“‹' }, { id: 'stats', label: 'Stats', icon: '๐Ÿ“Š' } ], []); const triggerDonationSound = (data = {}) => { const now = Date.now(); if (now - soundCooldownRef.current < 350) return; soundCooldownRef.current = now; const amount = Number(data.amount ?? data.donationAmount ?? data.value ?? 0); if (amount >= 5) { playSound('@success'); } else if (amount >= 3) { playSound('@ding'); } else { playSound('@click'); } setDonationTick(t => t + 1); setLastDonationAt(now); setFeedTick(t => t + 1); }; const normalizeTime = (value) => { if (!value) return null; const date = new Date(value); return Number.isNaN(date.getTime()) ? null : date; }; const donationAnalytics = useMemo(() => { const byDate = new Map(); const byCause = new Map(); donations.forEach((donation) => { const amount = Number(donation.amount ?? donation.donationAmount ?? donation.value ?? 0); const createdAt = normalizeTime(donation.createdAt ?? donation.created_at ?? donation.timestamp ?? donation.date); const causeId = String(donation.causeId ?? donation.cause_id ?? donation.cause ?? 'unknown'); const causeName = donation.causeName ?? donation.causeTitle ?? donation.title ?? donation.cause ?? `Cause ${causeId}`; if (createdAt) { const key = createdAt.toISOString().slice(0, 10); byDate.set(key, (byDate.get(key) || 0) + amount); } const existing = byCause.get(causeId) || { label: causeName, value: 0 }; byCause.set(causeId, { label: existing.label, value: existing.value + amount }); }); const timeSeries = Array.from(byDate.entries()) .sort((a, b) => a[0].localeCompare(b[0])) .map(([label, value]) => ({ label, value })); const topCauses = Array.from(byCause.values()) .sort((a, b) => b.value - a.value) .slice(0, 6); return { timeSeries, topCauses }; }, [donations]); useEffect(() => { if (activeTab !== 'stats') return; const overTimeCanvas = chartCanvasRefs.current.overTime; const topCausesCanvas = chartCanvasRefs.current.topCauses; if (overTimeCanvas && donationAnalytics.timeSeries.length > 0) { renderChart(overTimeCanvas, { type: 'line', data: { labels: donationAnalytics.timeSeries.map(d => d.label), datasets: [{ label: 'Donations over time', data: donationAnalytics.timeSeries.map(d => d.value) }] } }); } if (topCausesCanvas && donationAnalytics.topCauses.length > 0) { renderChart(topCausesCanvas, { type: 'bar', data: { labels: donationAnalytics.topCauses.map(d => d.label), datasets: [{ label: 'Top causes', data: donationAnalytics.topCauses.map(d => d.value) }] } }); } chartsVersionRef.current += 1; }, [activeTab, donationAnalytics]); useBroadcast('donation-created', (data) => { triggerDonationSound(data); }); useBroadcast('donation-feed-refresh', () => { setFeedTick(t => t + 1); }); useEffect(() => { const originalWindow = typeof window !== 'undefined' ? window : null; if (!originalWindow) return; const existing = originalWindow.__MICROGIVE_DONATION_SOUND__; originalWindow.__MICROGIVE_DONATION_SOUND__ = (data) => { triggerDonationSound(data); if (typeof existing === 'function') existing(data); }; return () => { if (originalWindow.__MICROGIVE_DONATION_SOUND__ === triggerDonationSound) { delete originalWindow.__MICROGIVE_DONATION_SOUND__; } }; }, []); const buildCauseImpact = (cause) => { const causeId = String(cause?.id ?? cause?.causeId ?? cause?.cause_id ?? 'unknown'); const causeName = cause?.title ?? cause?.name ?? cause?.causeName ?? `Cause ${causeId}`; const causeGoal = Number(cause?.goal ?? cause?.goalAmount ?? cause?.fundingGoal ?? 0); const causeRaised = Number(cause?.raised ?? cause?.raisedAmount ?? cause?.amountRaised ?? 0); const matchingDonations = donations.filter((donation) => { const donationCauseId = String(donation.causeId ?? donation.cause_id ?? donation.cause ?? 'unknown'); const donationCauseName = donation.causeName ?? donation.causeTitle ?? donation.title ?? donation.cause ?? ''; return donationCauseId === causeId || donationCauseName === causeName; }); const totalDonated = matchingDonations.reduce((sum, donation) => sum + Number(donation.amount ?? donation.donationAmount ?? donation.value ?? 0), 0); const donationCount = matchingDonations.length; const uniqueDays = new Set(); matchingDonations.forEach((donation) => { const date = normalizeTime(donation.createdAt ?? donation.created_at ?? donation.timestamp ?? donation.date); if (date) uniqueDays.add(date.toISOString().slice(0, 10)); }); const percentOfGoal = causeGoal > 0 ? Math.min(100, Math.round((causeRaised / causeGoal) * 100)) : 0; const funded = causeGoal > 0 ? causeRaised >= causeGoal : false; return { causeName, category: cause?.category ?? cause?.type ?? 'General', description: cause?.description ?? 'No description provided.', goal: causeGoal, raised: causeRaised, percentOfGoal, funded, totalDonated, donationCount, activeDays: uniqueDays.size, lastDonationAt: matchingDonations.length > 0 ? matchingDonations .map(d => normalizeTime(d.createdAt ?? d.created_at ?? d.timestamp ?? d.date)) .filter(Boolean) .sort((a, b) => b - a)[0] : null }; }; const handleExportCausePdf = async (cause) => { const reportEl = reportRefs.current[String(cause?.id ?? cause?.causeId ?? cause?.cause_id ?? 'unknown')]; if (!reportEl) return; await generatePDF(reportEl, { filename: `${String(cause?.title ?? cause?.name ?? 'impact-report').replace(/[^a-z0-9-_]+/gi, '-').toLowerCase()}.pdf`, margin: 10 }); }; const statsView = (

Donations over time

{donationAnalytics.timeSeries.length > 0 ? ( { chartCanvasRefs.current.overTime = el; }} /> ) : (
No donation history yet.
)}

Top causes

{donationAnalytics.topCauses.length > 0 ? ( { chartCanvasRefs.current.topCauses = el; }} /> ) : (
No cause totals yet.
)}
} /> ); return (

๐ŸŒฑ MicroGive

Small donations, big impact

{activeTab === 'causes' && (

Impact reports

Generate a downloadable PDF summary for any cause.

{causesLoading ? (
) : causes.length > 0 ? ( causes.map((cause) => { const impact = buildCauseImpact(cause); const causeKey = String(cause?.id ?? cause?.causeId ?? cause?.cause_id ?? 'unknown'); return (

{impact.causeName}

{impact.category} ยท {impact.donationCount} donations

${impact.totalDonated.toFixed(2)} Total donated
{impact.percentOfGoal}% Goal reached
{impact.activeDays} Active days
{ reportRefs.current[causeKey] = el; }}>

MicroGive Impact Report

{impact.causeName}

Cause summary

{impact.description}

Category

{impact.category}

Goal

${impact.goal.toFixed(2)}

Raised

${impact.raised.toFixed(2)}

Donations

{impact.donationCount}

Active days

{impact.activeDays}

Status

{impact.funded ? 'Funded' : 'In progress'}

Impact highlights

  • {impact.totalDonated.toFixed(2)} total donated through MicroGive.
  • {impact.percentOfGoal}% of the funding goal reached.
  • {impact.donationCount} micro-donations contributed to this cause.

Generated by MicroGive

{impact.lastDonationAt ? impact.lastDonationAt.toLocaleString() : 'No donations yet'}

); }) ) : (
๐Ÿ“„

No causes yet

Create a cause first, then export a PDF impact report from this section.

)}
)} {activeTab === 'add' && setActiveTab('causes')} />} {activeTab === 'activity' && } {activeTab === 'stats' && statsView}
); } export default App; ReactDOM.createRoot(document.getElementById('root')).render();