# 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 %; 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 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 |