Initial commit

Next.js + Express event management app for Hope Family Church.
This commit is contained in:
2026-07-23 15:26:47 +02:00
commit 3d381944d2
246 changed files with 57565 additions and 0 deletions
+318
View File
@@ -0,0 +1,318 @@
# 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
1. [Overview](#overview)
2. [Tech Stack](#tech-stack)
3. [Prerequisites](#prerequisites)
4. [Installation](#installation)
5. [Environment Variables](#environment-variables)
6. [Running the App](#running-the-app)
7. [Project Structure](#project-structure)
8. [Pages & Routes](#pages--routes)
9. [Authentication & Roles](#authentication--roles)
10. [Payment Flow](#payment-flow)
11. [Notifications](#notifications)
12. [Deployment](#deployment)
13. [Troubleshooting](#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
```bash
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`, and `NEXT_PUBLIC_CONTACT_EMAIL` are no longer needed and can be removed from your `.env.local`.
---
## Running the App
```bash
# 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 %; 51200: 15 %; 2011 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 only
- `whatsapp` — WhatsApp notifications only
- `both` — 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
1. User registers for a paid event → registration created with `status: pending`
2. User clicks "Pay" → calls `POST /api/payments/yoco-checkout`
3. Backend returns a Yoco `redirectUrl` → frontend opens it in a new tab
4. User pays on Yoco's hosted page → Yoco sends webhook to backend
5. Backend reconciles payment, updates registration status, generates and emails tickets
6. User sees `/payment/success` and 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)
1. Push the `frontend/` directory to GitHub
2. Create a Vercel project pointing to `frontend/`
3. Set env var: `NEXT_PUBLIC_API_URL=https://your-api.domain.com`
4. Deploy
### Self-hosted (Node.js)
```bash
npm run build
npm start # port 3000 by default
```
Use nginx/Caddy as a reverse proxy.
### PM2
```bash
pm2 start npm --name hope-events-frontend -- start
pm2 save && pm2 startup
```
### Checklist
- [ ] `NEXT_PUBLIC_API_URL` set to production backend URL
- [ ] Backend `FRONTEND_URL` includes this frontend's origin
- [ ] Yoco webhook URL reachable over HTTPS
- [ ] `npm run build` completes 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 |