Admins and supervisors without 2FA are sent a code at their next login, and verifying it turns 2FA on. They can change the channel but not turn it off. Older sessions without 2FA are rejected with TWO_FACTOR_REQUIRED, and promoting a user ends their sessions so they enrol at next login. The requirement is skipped when no email or WhatsApp channel can deliver a code, and TWO_FACTOR_REQUIRED_ROLES=false switches it off. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
20 KiB
Hope Events — Feature Overview
A complete map of what the platform does, grouped by who uses it. For setup and deployment see README.md; for the API see backend/API_DOCUMENTATION.md; for setting defaults see SETTINGS.md; for history see CHANGELOG.md.
Stack: Next.js (React, Tailwind, shadcn/ui) frontend · Express + Prisma + PostgreSQL backend · Yoco for card payments · SMTP for email · WAWP for WhatsApp · PM2 for process management · optional Sentry for error monitoring.
Contents
- Roles
- Public site
- Events
- Registration
- Payments, donations & refunds
- Tickets & check-in
- Attendee forms
- Messaging & notifications
- User dashboard (My Events)
- Staff tools
- Supervisor tools
- Admin tools
- Cashup & event costs
- Reports
- Site settings & setup wizard
- Accounts & security
- Help system
- Background jobs
- Operations
- Page map
Roles
Four roles, each including everything the roles below it can do:
| Role | Can do |
|---|---|
| User | Browse events, register, pay, donate, view tickets, fill in forms, manage their own profile |
| Staff | Scan tickets, view/print event tickets, view registrations and form responses |
| Supervisor | Create and manage events, record payments/refunds, assign donations, at-the-door desk, manual registration, bulk email/WhatsApp, reports, site banner, self-service kiosk |
| Admin | Manage users and roles, all registrations, event costs and cashup (close/reopen events), audit log, site settings, WhatsApp connection, backups |
Public site
- Home page — hero section with the organisation's name and branding, upcoming and in-progress events, and a "Join Us" button (becomes "My Dashboard" when logged in).
- Events list (
/events) — event cards with picture, dates, location, price, and a Register / Sold Out / Contact us button. - Event detail (
/events/[id]) — description, ticket options with variant and early-bird pricing, low-stock and sold-out badges, downloadable attachments, embedded Google Map with "Directions" link, and an Add to calendar (.ics) button. - Short links — events can have an alias (e.g.
/camp-2025) that redirects to the event. Only plausible aliases hit the database, so bot probes are rejected cheaply. - Contact page (
/contact) — organisation email, phone, address and map. - Legal pages — Terms of Use and Privacy Policy, filled from Site Settings.
- Site banner — a site-wide announcement bar set by supervisors.
- Lockdown rules (
/lockdown-rules) — static rules page for the youth lockdown event. - SEO —
sitemap.xml(every public page and event),robots.txt, and Open Graph/Twitter link-preview metadata. - Theming — brand colours, logo and favicon come from Site Settings.
Events
Created and edited by supervisors through a 6-step wizard: Basic Details → Items & Pricing → Sections → Form → Visibility → Attachments.
- Basic details — title, description, start/end dates, registration deadline, picture (upload or URL), location (defaults to the org's address), contact person.
- Contact-only events — for things like baptisms: shown publicly with a "Contact us" popup instead of registration, with no tickets.
- Ticket options — multiple options per event (e.g. Adult, Child, Dinner, T-shirt), each with a price and optional stock limit. One option is marked as the main ticket, which is what gets checked in at the door.
- Variants — sub-types of an option (sizes, colours, Adult/Child/VIP), each with an optional price override and its own stock limit.
- Early-bird tiers — deadline- and/or stock-limited cheaper prices per option or variant. Prices are locked in per purchase ("tranches"), so buying more later doesn't re-price what was already bought.
- Sections — named scan points (e.g. "Main hall", "Dinner") limited to certain ticket options, so a scanner at that point only accepts those tickets.
- Visibility — active/inactive, hidden (reachable only by link), scheduled go-live date, require login vs. allow guest registration, or no registration at all.
- Attachments — downloadable files (maps, schedules, waivers) shown on the event page.
- Notify recipients — choose which staff get registration/payment alerts and the daily summary for the event.
- Sold-out handling — an event is sold out when every stock-limited option is exhausted. "X remaining" badges appear at a threshold that scales with capacity.
Registration
- Online registration (
/register/[eventId]) — pick options, variants and quantities; see early-bird prices and stock warnings; fill in the event form. - Guest registration — when an event doesn't require login, an inactive account is created and an activation link is sent by email or WhatsApp.
- Statuses — Pending, Confirmed, Partially Paid, Paid, Cancelled.
- Edit options — users can add or change items on their registration (not for past or closed events).
- Self-cancellation — allowed only before any payment; admins can cancel any time.
- Pricing refresh — when an unpaid registration is viewed or paid, expired early-bird prices are re-evaluated, and the user is warned if the total changed.
- Manual registration (supervisor) — register someone on their behalf, creating an account if needed. Includes a "Record Payment" tab.
- Duplicate-account protection — if an email and phone belong to two different accounts, the registration is blocked instead of merging them.
Payments, donations & refunds
- Online card payments via Yoco — checkout for the full outstanding balance, confirmed by a signed webhook.
- Payment links — supervisors can generate and send a Yoco link for any amount (including partial).
- Manual payments — cash, card, EFT, voucher or other, recorded by staff. Each payment records who recorded it as well as who it's for.
- Refunds — negative payments against an original payment, netted against the original method.
- Donations — users can donate from their dashboard (
/dashboard/user/donate). Supervisors can assign a donation (fully or partly) to someone's registration and unassign it later. Assignments are kept as separate records, so the original donation never changes. - Yoco reconciliation — imported Yoco transactions can be matched to a registration, recorded as a donation (optionally choosing the donor), or ignored.
- Invoices & receipts — branded PDFs: an invoice (itemised, early-bird discount, balance due, pay-now link and QR code) and a payment receipt. Sent automatically and re-sendable on demand by email or WhatsApp.
- Method normalisation — Apple Pay / Google Pay / checkout payments count as "Card" in reports and user views.
Tickets & check-in
- Automatic tickets — QR-coded tickets are generated when a registration is fully paid (or free), and sent as a PDF by email and/or WhatsApp.
- Quantity tickets — one ticket can cover several people and be redeemed partially (e.g. check in 2 of 3).
- QR scanner — works in any mobile browser. A preview checks the ticket is for the selected event/section, then a confirmation screen shows holder, type and remaining quantity.
- Check-in confirmation — holders get an email/WhatsApp saying how many were checked in and how many remain.
- Scan stats — recent scans and per-event scan counts.
- Printing — A4 sheets (8 tickets per page) and A6 single tickets.
- Resend — send tickets to a different email/phone at the door.
Attendee forms
- Form builder per event — field types: Yes/No, Text, Paragraph, Number, Date, plus Statement and Heading blocks; required/optional, help text, reordering. The whole form can be marked required.
- Drafts — partially filled forms are auto-saved.
- Responses — view, filter (event, user, past/inactive) and print one response per A4 page. Supervisors can edit responses.
Messaging & notifications
Every user picks a notification preference: Email, WhatsApp, or Both.
Automatic notifications
| Trigger | Goes to |
|---|---|
| Registration created / updated | Registrant (with invoice if unpaid) + event notify recipients |
| Payment recorded / donation applied / unassigned | Registrant (with receipt) + admins |
| Refund processed | Registrant + admins |
| Tickets generated | Registrant (ticket PDF) |
| Ticket checked in | Ticket holder |
| Daily summary, 07:00, for each live event that hasn't started yet | Registrations inbox + event notify recipients |
| Account registered, activation, login, password changed/reset, account closed | The account holder |
Bulk messaging (supervisor)
- Email attendees and WhatsApp attendees — filter by status or pick specific attendees, preview, send now or schedule for later.
- Placeholders —
{{name}},{{event.title}},{{event.link}},{{event.start}},{{balance}}, and{{payment.link}}(a live Yoco link for each recipient's outstanding balance). - Broadcasts — ad-hoc email/WhatsApp to any users or addresses, not tied to one event.
- Automations — schedule a sequence of lifecycle emails for an event (pre-event, final reminder, thank-you, promo for another event).
- Manage scheduled — view, edit or delete queued jobs per channel. Sends run in the background and are purged 24 h after sending.
WhatsApp is connected through the WAWP API: an admin links a number in Site Settings → WhatsApp (QR code or pairing code), with status, restart and logout controls. Disconnected sessions are recovered automatically.
User dashboard (My Events)
- Registrations with status badges, event thumbnails, and Closed/Past/Inactive tags. Past and closed events are hidden by default.
- Registration detail: itemised bill (early-bird lines grouped), payments, Invoice and Receipt buttons, edit, cancel, Make payment.
- Upcoming events and tickets side by side, with bulk ticket email/WhatsApp.
- Payment history (
/dashboard/user/payments) — paginated, with filters by date, method and payment/refund. - Donate page.
- Forms — complete outstanding attendee forms.
- Profile & Security — edit details and notification preference, change password, see recent account activity (logins, password changes, device), sign out everywhere, deactivate or delete my data.
Staff tools
- Staff dashboard — scan stats and quick actions.
- Ticket scanning (
/dashboard/staff/ticket-scanning) — camera QR scanner with event and section filters. - Event tickets & printing (
/dashboard/staff/event-tickets) — list, search and print an event's tickets.
Supervisor tools
- Dashboard — KPIs (active events, revenue, donations, registrations, tickets sold, each vs. the previous month), revenue trend chart, top events, and recent scans. Tiles link to the matching report.
- Manage events — the 6-step event wizard, ticket options, variants, sections, form and attachments.
- At the door — search by name/phone/ticket ID, walk-in registration, payments, a Check-In tab for redeeming main tickets by quantity, and ticket printing/resending.
- Self-service kiosk (
/self-service) — a touchscreen page a supervisor unlocks for an event, where visitors register themselves. Existing accounts are matched by email/phone, and any change to their details needs confirmation. - Manual registration — register someone on their behalf and record their payment.
- Payments — tabs for Payment, Refund, Donations (assign/unassign), Reconcile (Yoco transactions), and Payment Link.
- Email attendees / WhatsApp attendees — bulk and scheduled messaging.
- Forms — view, filter, edit and print form responses.
- Reports — see Reports.
- Site banner (
/set-banner) — set the announcement bar.
Admin tools
- Admin dashboard — same KPIs as supervisor, plus admin quick actions.
- Manage users — server-side search and filters (role, active), paging, edit role and notification preference, revoke sessions, delete or anonymise personal data.
- Registrations — all registrations, with filters (event, status, text), status counts, revenue and outstanding totals, and per-registration options, form answers and payments (with who recorded each one).
- Cashup — see below.
- Audit log (
/dashboard/admin/audit-log) — who did what and when for refunds, donation assign/unassign, manual registrations, staff cancellations, event create/update/delete, and settings changes. - Site settings — see below.
Cashup & event costs
- Event costs — once-off or per-item costs (optionally linked to a ticket option), with a "paid from takings" method so cash on hand is correct.
- Expected vs. actual — expected takings per method (cash/card/EFT/other) compared with actual amounts, with variance shown.
- Cash denomination counts — entered per staff member, at any time. The event's actual cash is the sum of these counts.
- Payment accountability by staff member — what each person recorded, by method, with live cash variance.
- Close / quick close / reopen an event — closing locks it from further edits and hides it from the kiosk and user actions. Drafts are saved as you go.
- Report tab — a read-only cashup summary, shown automatically once the event is closed.
- Cashup audit trail — every close/reopen with totals and who did it.
Reports
A full-width workspace with shared filters (events, date range with presets, include past/inactive/closed) and a searchable grid of reports, each opening in a viewer with its own filters, stat tiles and bar charts.
| Category | Reports |
|---|---|
| Financial | Payments between dates · Revenue summary (by method) · Revenue detailed · Finance report (revenue & costs) · Profit report |
| Registration | Attendees per event (grouped by option) · Registration type counts · Master orders breakdown · Registration status breakdown (count by registration or ticket quantity) |
| Ticketing | Ticket usage summary |
| Donations | Donations breakdown (used / unused per event) |
| Cashup | Cashup reconciliation · Cashup audit trail |
Export: Print, branded PDF download, styled Excel (.xlsx) workbook,
Email (to yourself), or WhatsApp (the PDF to your own number). Dashboard tiles
deep-link into reports with ?report=&range=.
Site settings & setup wizard
Setup wizard (/setup) — on a fresh database, walks through organisation
details, the first admin account and branding. No manual seeding needed.
Site Settings (/dashboard/admin/settings) — stored in the database and
applied without a restart. Secrets are encrypted with AES-256-GCM.
| Tab | Contains |
|---|---|
| Organisation | Name, tagline, contact email/phone, address (with map link) |
| Branding | Primary/secondary/accent colours, logo, favicon, colours suggested from the logo, live website/email preview on desktop and mobile |
| Notifications | Registrations inbox and admin notification emails |
| SMTP host/port/TLS/user/password/from, with a "Test connection" button | |
| Legal | Operator, Information Officer, website, effective date for the Terms/Privacy pages |
| WAWP credentials, connect a number, status/restart/logout | |
| Backups | List, run now, download database backups |
See SETTINGS.md for the default of every setting.
Accounts & security
- Register, log in, forgot/reset password, activate account (for accounts created on someone's behalf).
- JWT authentication (HS256 pinned) with session revocation (token versioning) — users can sign out everywhere, and admins can do it for any user.
- Two-factor authentication — a 6-digit login code by email or WhatsApp
(10-minute expiry, 5 attempts, resend), with "Trust this device for 30 days".
Required for Admins and Supervisors: their next login sends a code that turns
it on, and they can change the channel but not turn it off (skipped only when no
email or WhatsApp channel can deliver a code;
TWO_FACTOR_REQUIRED_ROLES=falseis the emergency switch). Optional for everyone else from Profile & Security. Admins can reset it for a locked-out user, and the kiosk asks for the code at setup. - Login lockout after repeated failed attempts, plus login rate limiting
(
TRUST_PROXYto read real IPs behind a proxy). - Security event log — logins, password changes and resets, with device and IP.
- Account closure — deactivate, or anonymise personal data while keeping financial records.
- Upload hardening — server-generated filenames, and uploads served with a
sandbox CSP and
nosniff.
Help system
A floating "Need help?" button on every page opens a page-specific guide
(tabs, quick links). Every page has dedicated content (frontend/src/content/help/),
falling back to a general guide.
Background jobs
| Job | Schedule |
|---|---|
| Scheduled email/WhatsApp worker | Polls every 30 s (SCHEDULED_EMAILS_INTERVAL_MS) and purges sent jobs after 24 h |
| Daily event summary email | 07:00 daily |
Database backup (pg_dump, gzipped, 14-day retention) |
02:00 daily (BACKUP_ENABLED=false to disable) |
Temp file cleanup (files older than 7 days in /temp) |
Daily |
Operations
- PM2 (
ecosystem.config.js) runs backend and frontend, restarting either one above 500 MB of memory. - Sentry error monitoring when
SENTRY_DSN/NEXT_PUBLIC_SENTRY_DSNare set. - Unhandled promise rejections are logged and don't crash the server.
- Backend status page (
/) and built-in API docs page (/docs). - Jest tests for early-bird tranche pricing and donation balances.
- Versioning via git tags and CHANGELOG.md.
Page map
| Path | Who | What |
|---|---|---|
/ |
Public | Home |
/events, /events/[id] |
Public | Event list and detail |
/[alias] |
Public | Event short link |
/register/[eventId] |
Public / User | Registration |
/registration/success |
Public / User | Confirmation, form and pay |
/payment/success, /payment/failure, /payment/cancel |
Public | Yoco return pages |
/forms |
Public / User | Attendee forms |
/contact, /legal/terms, /legal/privacy, /lockdown-rules |
Public | Info pages |
/login, /register, /forgot-password, /reset-password, /activate-account |
Public | Accounts |
/setup |
First run | Setup wizard |
/self-service |
Supervisor-unlocked | Kiosk |
/set-banner |
Supervisor | Site banner |
/dashboard/user (+ /payments, /donate, /forms, /profile, /reset-password) |
User | My Events |
/dashboard/staff (+ /ticket-scanning, /event-tickets) |
Staff | Staff tools |
/dashboard/supervisor (+ /events, /event-options, /at-the-door, /manual, /manual-registration, /payments, /email-attendees, /whatsapp-attendees, /forms, /reports) |
Supervisor | Supervisor tools |
/dashboard/admin (+ /users, /registrations, /forms, /cashup, /cashup/[id], /audit-log, /settings) |
Admin | Admin tools |