You need supervisor or admin access to view reports.
Filters
{report === "payments" && (
)}
{report === "attendees" && (
)}
{report === "regTypes" && (
({ value: ev.id, label: ev.title }))} value={regTypeEvents} onChange={setRegTypeEvents} />
)}
{report === "usage" && (
({ value: ev.id, label: ev.title }))} value={usageEvents} onChange={setUsageEvents} />
)}
{report === "revenue" && (
)}
{report === "revenueDetailed" && (
)}
{report === "regStatus" && (
)}
Results
{report === "payments" && (
{paymentRows.length === 0 ? (
No payments match the selected filters.
) : (
{Object.entries(paymentRows.reduce((acc: any, r: any) => {
acc[r.eventTitle] = acc[r.eventTitle] || [];
acc[r.eventTitle].push(r);
return acc;
}, {})).map(([eventTitle, rows]: any) => {
// group by user
const byUser: Record
= {};
rows.forEach((r: any) => {
const key = r.userName || 'User';
byUser[key] = byUser[key] || [];
byUser[key].push(r);
});
const eventTotal = rows.reduce((s: number, r: any) => s + (r.amount || 0), 0);
return (
{eventTitle}
Total: R {eventTotal.toFixed(2)}
{Object.keys(byUser).sort().map(userName => {
const list = byUser[userName];
const userTotal = list.reduce((s: number, r: any) => s + (r.amount || 0), 0);
const email = list.find((r: any) => r.userEmail)?.userEmail || '';
return (
{userName} {email ? ({email}) : null}
Subtotal: R {userTotal.toFixed(2)}
{list.map((r: any, i: number) => (
-
{r.method}{r.isDonation ? ' • Donation' : ''}
R {Number(r.amount).toFixed(2)} • {new Date(r.createdAt).toLocaleString()}
))}
);
})}
);
})}
)}
)}
{report === "attendees" && (
Event: {attendeesLayer.eventTitle}
{attendeesLayer.options.length === 0 ? (
No attendees for selected filters.
) : (
{attendeesLayer.options.map(opt => (
{opt.optionName}
{opt.users.map((u: any, i: number) => (
-
{u.name}
{u.email && ({u.email})}
— {u.status}
• Qty {u.qty}
))}
))}
)}
)}
{report === "regTypes" && (
{regTypeRows.length === 0 ? (
No data available. Select events and view.
) : (
| Event |
Registration Type |
Quantity |
{regTypeRows.map((r, idx) => (
| {r.eventTitle} |
{r.type} |
{r.qty} |
))}
)}
)}
{report === "usage" && (
{Object.keys(ticketUsage).length === 0 ? (
No data available. Select events and view.
) : (
| Event |
Used |
Unused |
{Object.keys(ticketUsage).map(evId => {
const ev = events.find(e => e.id === evId);
const tu = ticketUsage[evId];
return (
| {ev?.title || evId} |
{tu.used} |
{tu.unused} |
);
})}
)}
)}
{report === "revenue" && (
{Object.keys(paymentsByEvent).length === 0 ? (
No data available. Select events and view.
) : (
Object.keys(paymentsByEvent).map(evId => {
const ev = events.find(e => e.id === evId);
const df = payFrom ? new Date(payFrom).getTime() : null;
const dt = payTo ? new Date(payTo).getTime() : null;
const list = (paymentsByEvent[evId] || []).filter((p: any) => {
const t = new Date(p.createdAt).getTime();
return !(df && t < df) && !(dt && t > dt);
});
const byMethod: Record
= {};
let total = 0;
list.forEach((p: any) => { const m = getPaymentMethod(p); byMethod[m] = (byMethod[m] || 0) + (p.amount || 0); total += (p.amount || 0); });
return (
{ev?.title || evId}
Total: R {total.toFixed(2)}
{Object.keys(byMethod).length === 0 ? (
No payments in date range.
) : (
{Object.keys(byMethod).sort().map(m => (
- {m}R {byMethod[m].toFixed(2)}
))}
)}
);
})
)}
)}
{report === "revenueDetailed" && (
{Object.keys(registrationsByEvent).length === 0 ? (
No data available. Select events and view.
) : (
Object.keys(registrationsByEvent).map(evId => {
const ev = events.find(e => e.id === evId);
const rows = (revDetRows as any[]).filter(r => r.eventId === evId);
const total = rows.reduce((s, r) => s + (r.totalPaid || 0), 0);
const regIds = rows.map(r => r.registrationId);
const outstandingTotal = regIds.reduce((s, id) => s + (outstandingByReg.get(id) || 0), 0);
return (
{ev?.title || evId}
Total: R {total.toFixed(2)}{` • Outstanding: R ${outstandingTotal.toFixed(2)}`}
{rows.length === 0 ? (
No registrations in date range.
) : (
| Name |
Email |
Status |
Total paid |
Outstanding |
Registration |
{rows.map((r, idx) => (
| {r.userName} |
{r.userEmail || ''} |
{r.status || ''} |
R {Number(r.totalPaid || 0).toFixed(2)} |
R {Number(r.outstanding || 0).toFixed(2)} |
{r.registrationId || ''} |
))}
)}
);
})
)}
)}
{report === "regStatus" && (
{Object.keys(registrationsByEvent).length === 0 ? (
No data available. Select events and view.
) : (
| Event |
Pending |
Partial |
Paid |
Cancelled |
{Object.keys(registrationsByEvent).map(evId => {
const ev = events.find(e => e.id === evId);
const regs = registrationsByEvent[evId] || [];
const counts: Record = { pending: 0, partial_paid: 0, paid: 0, cancelled: 0 };
regs.forEach((r: any) => { if (!statusIncludeCancelled && r.status === 'cancelled') return; counts[r.status] = (counts[r.status] || 0) + 1; });
return (
| {ev?.title || evId} |
{counts.pending || 0} |
{counts.partial_paid || 0} |
{counts.paid || 0} |
{counts.cancelled || 0} |
);
})}
)}
)}