import { useEffect, useMemo, useRef, useState } from 'react';
import { useCollection, exportCSV, renderChart, share } from '@deplixo/sdk';
import { ProgressBar } from './components/ProgressBar.jsx';
import { Stats } from './components/Stats.jsx';
import { DonationForm } from './components/DonationForm.jsx';
import { DonorWall } from './components/DonorWall.jsx';
const FUNDRAISING_GOAL = 10000;
function App() {
const { items, loading, add } = useCollection('donations', { personal: true });
const [showForm, setShowForm] = useState(false);
const [activeTab, setActiveTab] = useState('overview');
const [shareOpen, setShareOpen] = useState(false);
const [copyStatus, setCopyStatus] = useState('idle');
const totalRaised = items.reduce((sum, item) => sum + (Number(item.value.amount) || 0), 0);
const percentage = Math.min((totalRaised / FUNDRAISING_GOAL) * 100, 100);
const campaignUrl = useMemo(() => {
if (typeof window === 'undefined') return '';
return window.location.href;
}, []);
const shareText = useMemo(() => {
return `Support our fundraising campaign! Every contribution helps us reach our goal. ${campaignUrl}`;
}, [campaignUrl]);
const handleDonate = async (donation) => {
await add({
name: donation.name,
amount: Number(donation.amount),
message: donation.message || '',
date: new Date().toISOString()
});
setShowForm(false);
};
const handleCopyLink = async () => {
try {
await navigator.clipboard.writeText(campaignUrl);
setCopyStatus('copied');
setTimeout(() => setCopyStatus('idle'), 2000);
} catch (error) {
setCopyStatus('failed');
setTimeout(() => setCopyStatus('idle'), 2000);
}
};
const handleNativeShare = async () => {
if (navigator.share) {
try {
await navigator.share({
title: 'Donation Tracker Campaign',
text: shareText,
url: campaignUrl
});
setShareOpen(false);
} catch (error) {
// User cancelled or share failed; keep menu open for other options.
}
}
};
const handleExportCSV = () => {
exportCSV(
items.map((i) => i.value),
{
filename: 'donations.csv',
columns: ['name', 'amount', 'message', 'date']
}
);
};
return (
Share the campaign
Copy the link or use a share option to invite more supporters.
{shareOpen && (
{navigator.share && (
)}
Share by email
Share on X
)}
{showForm && setShowForm(false)} />}
{activeTab === 'overview' && (
)}
{activeTab === 'charts' && (
)}
{activeTab === 'donors' && (
)}
);
}
function FundraisingCharts({ items, loading }) {
const lineData = [];
const dailyTotals = {};
items.forEach((item) => {
const rawDate = item && item.value ? item.value.date : null;
const amount = Number(item && item.value ? item.value.amount : 0) || 0;
const dateKey = rawDate ? new Date(rawDate).toISOString().slice(0, 10) : null;
if (!dateKey) return;
dailyTotals[dateKey] = (dailyTotals[dateKey] || 0) + amount;
});
Object.keys(dailyTotals)
.sort()
.forEach((dateKey) => {
lineData.push({ label: dateKey, value: dailyTotals[dateKey] });
});
const donorTotals = {};
items.forEach((item) => {
const donorName = item && item.value ? item.value.name || 'Anonymous' : 'Anonymous';
const amount = Number(item && item.value ? item.value.amount : 0) || 0;
donorTotals[donorName] = (donorTotals[donorName] || 0) + amount;
});
const topDonors = Object.keys(donorTotals)
.map((name) => ({ label: name, value: donorTotals[name] }))
.sort((a, b) => b.value - a.value)
.slice(0, 5);
return (
Donations Over Time
Daily totals update automatically as new donations arrive.
{loading ? (
Loading chart data...
) : lineData.length > 0 ? (
) : (
No donation history yet.
)}
Top Donors
Ranked by total contribution amount.
{loading ? (
Loading chart data...
) : topDonors.length > 0 ? (
) : (
No donors to display yet.
)}
);
}
function SalesChart({ data }) {
const canvasRef = useRef(null);
const chartType = data && data.length > 0 && data[0] && typeof data[0].label === 'string' && data[0].label.length === 10 ? 'line' : 'bar';
useEffect(() => {
if (canvasRef.current && data.length > 0) {
renderChart(canvasRef.current, {
type: chartType,
data: {
labels: data.map((d) => d.label),
datasets: [{ label: chartType === 'line' ? 'Donations' : 'Top Donors', data: data.map((d) => d.value) }]
}
});
}
}, [data, chartType]);
return ;
}
ReactDOM.createRoot(document.getElementById('root')).render();