Extends the earlier user-dashboard fix: canEditActive is renamed canModifyActive and now also gates the "Make payment" and "Cancel registration" actions, not just editing. Backend enforcement added to cancelRegistration and createYocoCheckout to block past-event self-service payment/cancellation server-side (cashup-closed events were already blocked via assertEventOpen; admins/supervisors are exempt from the past-date check, consistent with existing overrides). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Hope Events — Frontend
Next.js 15 (App Router) frontend for the Hope Events platform used by Hope Family Church (events.hopehenley.co.za).
Table of Contents
- Overview
- Tech Stack
- Prerequisites
- Installation
- Environment Variables
- Running the App
- Project Structure
- Pages & Routes
- Authentication & Roles
- Payment Flow
- Notifications
- Deployment
- Troubleshooting
Overview
The frontend handles:
- Public event listings and event detail pages
- User registration, login, and account management
- Attendee registration and Yoco checkout
- Ticket viewing and delivery
- Staff ticket scanning (QR scanner)
- Supervisor tools: event management, payments, bulk messaging (email + WhatsApp), reports, at-the-door check-in
- Admin tools: user management, WhatsApp instance management, form builder
Tech Stack
| Layer | Technology |
|---|---|
| Framework | Next.js 15 (App Router, React 19) |
| Language | TypeScript |
| Styling | Tailwind CSS 3 |
| UI Primitives | Radix UI |
| Forms | React Hook Form + Zod |
| Icons | Lucide React |
| QR Scanning | @zxing/browser, react-webcam |
| QR Generation | qrcode |
| Date handling | date-fns, react-day-picker |
Prerequisites
- Node.js 18+
- npm
- A running backend API (see
../backend/README.md)
Installation
cd frontend
npm install
Environment Variables
Create frontend/.env.local:
| Variable | Required | Description |
|---|---|---|
NEXT_PUBLIC_API_URL |
Yes | Base URL of the backend API, e.g. https://api.yourdomain.com or http://localhost:5000 for local dev |
Organisation name, brand colour, logo, and contact email are now stored in the database and managed via Admin → Site Settings.
NEXT_PUBLIC_APP_NAME,NEXT_PUBLIC_ORG_NAME,NEXT_PUBLIC_BRAND_COLOR, andNEXT_PUBLIC_CONTACT_EMAILare no longer needed and can be removed from your.env.local.
Running the App
# Development (Turbopack)
npm run dev
# Production build + start
npm run build
npm start
# Lint
npm run lint
Project Structure
frontend/src/
├── app/ # Next.js App Router pages
│ ├── (auth)/ # Login, register, forgot/reset password
│ ├── [redirectUrl]/ # Event alias redirects
│ ├── activate-account/ # Account activation from email link
│ ├── dashboard/
│ │ ├── layout.tsx # Shared dashboard shell + nav
│ │ ├── admin/ # Admin-only pages
│ │ ├── supervisor/ # Supervisor + admin pages
│ │ ├── staff/ # Staff + above pages
│ │ └── user/ # All authenticated users
│ ├── events/ # Public event listing + detail
│ ├── forms/ # Public form submission
│ ├── legal/ # Privacy policy, terms
│ ├── payment/ # Payment success/failure/cancel
│ ├── register/[eventId]/ # Event registration flow
│ ├── registration/ # Post-registration success
│ ├── reset-password/ # Password reset (token flow)
│ ├── self-service/ # Guest self-service portal
│ ├── set-banner/ # Quick banner editor (supervisor)
│ └── layout.tsx # Root layout
├── components/ # Shared UI components
├── hooks/ # Custom React hooks (useAuth, etc.)
└── lib/ # Utilities (api.ts fetch wrapper, etc.)
Pages & Routes
Public
| Route | Description |
|---|---|
/ |
Landing page |
/events |
Public event listing — shows "Sold Out" button when all limited-stock options are exhausted; free events show "Register" without a price suffix |
/events/[id] |
Event detail page — sold-out/registration-closed/not-yet-open states; zero-price options display "Free"; tiered low-stock badges (≤ 50 tickets: 20 %; 51–200: 15 %; 201–1 000: 10 %; 1 000+: 5 %) |
/[redirectUrl] |
Event alias redirect |
/register/[eventId] |
Registration flow — sold-out banner + disabled button when event is fully booked; tiered stock badges; variant qty controls; early-bird notice |
/forms |
Public form submission |
/legal/privacy |
Privacy policy |
/legal/terms |
Terms of service |
/lockdown-rules |
House rules page |
/payment/success |
Yoco checkout success |
/payment/failure |
Yoco checkout failure |
/payment/cancel |
Yoco checkout cancelled |
/registration/success |
Post-registration confirmation |
/self-service |
Guest self-service (find tickets by email) |
/activate-account |
Account activation via email link |
/reset-password |
Password reset (token from email) |
Auth
| Route | Description |
|---|---|
/login |
Login form |
/register |
Sign-up form |
/forgot-password |
Request password reset |
User Dashboard — /dashboard/user/
| Route | Description |
|---|---|
/dashboard/user |
My registrations + tickets overview |
/dashboard/user/profile |
Edit profile, notification preferences |
/dashboard/user/pay |
Pay outstanding balance |
/dashboard/user/donate |
Make a donation |
/dashboard/user/forms |
Complete registration forms |
/dashboard/user/reset-password |
Change password (authenticated) |
Staff Dashboard — /dashboard/staff/
| Route | Description |
|---|---|
/dashboard/staff |
Staff overview |
/dashboard/staff/ticket-scanning |
QR code scanner for check-in |
/dashboard/staff/event-tickets |
All tickets for an event |
Supervisor Dashboard — /dashboard/supervisor/
| Route | Description |
|---|---|
/dashboard/supervisor |
Supervisor overview + quick links |
/dashboard/supervisor/events |
Create/edit/manage events — 6-step wizard (Basic Details, Items & Pricing, Sections, Form, Visibility, Attachments); form builder with up/down reorder, type badges, separate shortcut buttons for statement/heading fields; early-bird tiers with labelled deadline/price inputs; event image file upload with preview; attachments can be staged during creation (uploaded after save) |
/dashboard/supervisor/event-options |
Manage ticket types / options |
/dashboard/supervisor/payments |
View and manage payments |
/dashboard/supervisor/manual |
Manual payment entry |
/dashboard/supervisor/manual-registration |
Register an attendee manually |
/dashboard/supervisor/at-the-door |
At-the-door check-in management |
/dashboard/supervisor/email-attendees |
Bulk email + automations + broadcasts + scheduled |
/dashboard/supervisor/whatsapp-attendees |
Bulk WhatsApp + automations + broadcasts + scheduled |
/dashboard/supervisor/reports |
Generate/download/email reports |
/dashboard/supervisor/forms |
Event form builder and response viewer — three modes: view responses (printable, page-break per submission), fill/edit responses, edit form structure; improved form builder with reorder buttons and type badges |
/dashboard/supervisor/sections |
Manage event sections |
/set-banner |
Quick banner editor |
Admin Dashboard — /dashboard/admin/
| Route | Description |
|---|---|
/dashboard/admin |
Admin overview |
/dashboard/admin/users |
User management — list, inline edit, deactivate, role assignment, per-page selector (10/25/50/100), server-side search/filter by name/email/phone/role/status, "Delete data" button to anonymise a user's personal information |
/dashboard/admin/registrations |
All registrations — filter by event, status, and fuzzy text search; expandable rows showing ticket options and form responses |
/dashboard/admin/whatsapp |
WhatsApp instance management (WAWP) |
/dashboard/admin/forms |
View all form responses (inherits supervisor/forms) |
/dashboard/admin/settings |
Site Settings — organisation details, logo, brand colour, notification emails, SMTP delivery (with test connection; friendly errors + collapsible raw details on failure), legal page content |
Setup — /setup
| Route | Description |
|---|---|
/setup |
First-time setup wizard — shown automatically on a fresh deployment (empty database). Creates the first admin account and initial site settings in three steps: organisation details, admin account, branding. After completion, redirected to login. |
Authentication & Roles
Auth state is managed via the useAuth hook (src/hooks/useAuth.ts). The JWT is stored in localStorage and sent as a Bearer token on every API call via src/lib/api.ts (apiFetch).
Role access matrix
| Page group | user |
staff |
supervisor |
admin |
|---|---|---|---|---|
| Public pages | Yes | Yes | Yes | Yes |
/dashboard/user/* |
Yes | Yes | Yes | Yes |
/dashboard/staff/* |
— | Yes | Yes | Yes |
/dashboard/supervisor/* |
— | — | Yes | Yes |
/dashboard/admin/* |
— | — | — | Yes |
Unauthenticated users are redirected to /login. Insufficient-role users see an in-page warning.
Notification preferences
Users set their preference on the profile page:
email— email notifications onlywhatsapp— WhatsApp notifications onlyboth— both channels
The supervisor bulk-messaging pages show preference indicators in user/attendee dropdowns:
- Indigo badge — email preference matches (email/both) on the email page
- Green badge — WhatsApp preference matches (whatsapp/both) on the WA page
- Amber badge — preference mismatch (will still receive message)
A "Select Email/both" or "Select WhatsApp/both" quick-select button is available in each dropdown.
Payment Flow
- User registers for a paid event → registration created with
status: pending - User clicks "Pay" → calls
POST /api/payments/yoco-checkout - Backend returns a Yoco
redirectUrl→ frontend opens it in a new tab - User pays on Yoco's hosted page → Yoco sends webhook to backend
- Backend reconciles payment, updates registration status, generates and emails tickets
- User sees
/payment/successand tickets appear in their dashboard
Outstanding balances can be paid at any time from /dashboard/user/pay.
Notifications
Bulk Email / WhatsApp (email-attendees, whatsapp-attendees)
Both pages share the same feature set:
- Attendees tab — send or schedule a message to attendees of a specific event; filter by payment status; templates: custom, payment reminder, event reminder, ticket delivery
- Automations tab — schedule pre-event (1 week), final reminder (24/48 h), thank-you, and promo messages with smart default timing based on the event's dates
- Broadcasts tab — send to a selected user list plus ad-hoc email addresses or phone numbers
- Scheduled tab — view, edit (reschedule / update content), or cancel queued jobs
Mismatch warnings appear when selected users prefer a different channel. Falling back to all attendees if no matching-preference attendees exist.
The attendee selection dropdown is constrained to a fixed width (max-w-xs / w-64 panel) so it does not grow to fill the page on wide screens.
Deployment
Vercel (recommended)
- Push the
frontend/directory to GitHub - Create a Vercel project pointing to
frontend/ - Set env var:
NEXT_PUBLIC_API_URL=https://your-api.domain.com - Deploy
Self-hosted (Node.js)
npm run build
npm start # port 3000 by default
Use nginx/Caddy as a reverse proxy.
PM2
pm2 start npm --name hope-events-frontend -- start
pm2 save && pm2 startup
Checklist
NEXT_PUBLIC_API_URLset to production backend URL- Backend
FRONTEND_URLincludes this frontend's origin - Yoco webhook URL reachable over HTTPS
npm run buildcompletes without errors
Troubleshooting
| Symptom | Fix |
|---|---|
| 4xx/5xx API calls | Check NEXT_PUBLIC_API_URL is correct and reachable from the browser |
| CORS errors | Confirm backend FRONTEND_URL includes your frontend origin |
| Port conflict in dev | Next.js auto-selects 3001+ if 3000 is taken — keep NEXT_PUBLIC_API_URL pointing at the backend port |
| Images not showing | Backend must serve /uploads and public/uploads must be writable |
| Login loop | Clear localStorage and re-login; check JWT_SECRET on backend hasn't changed |
| QR scanner not working | Browser requires camera permission; @zxing/browser requires HTTPS in production |