Files
joshua 3d381944d2 Initial commit
Next.js + Express event management app for Hope Family Church.
2026-07-23 15:26:47 +02:00

318 lines
13 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 |