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

198 lines
13 KiB
Markdown
Raw 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 Platform
Hope Events is a full-stack event management platform that allows users to browse events, register for them, make payments, and manage their tickets.
## Project Structure
The project is divided into two main parts:
- Backend: Node.js + Express + Prisma + PostgreSQL
- Frontend: Next.js (React) + Tailwind CSS
### Backend
The backend provides a RESTful API for managing users, events, registrations, payments, uploads, and tickets. It includes:
- JWT-based authentication and role-based authorization
- Event and registration management
- Payment integration with Yoco (checkout + webhooks)
- Ticket generation (PDF + QR) and email delivery
- Static uploads for event images
Documentation: see Backend API Documentation and Backend README for environment setup and endpoints.
- Backend API Documentation: ./backend/API_DOCUMENTATION.md
- Backend README: ./backend/README.md
### Frontend
The frontend is a Next.js app that provides:
- User authentication (login, register)
- Browse/search events and view details
- Register for events and pay via Yoco
- Manage and view tickets
Documentation: see Frontend README for environment and scripts.
- Frontend README: ./frontend/README.md
## Getting Started (Local Development)
### Prerequisites
- Node.js 18+ (recommended)
- npm or yarn
- PostgreSQL database
### 1) Install dependencies
- Backend
- cd backend
- npm install
- Frontend
- cd ../frontend
- npm install
### 2) Configure environment variables
- Backend: create backend/.env and set required variables (see backend/README.md). At minimum:
- DATABASE_URL
- JWT_SECRET
- PORT (optional; defaults to 3000 if not set)
- YOCO_SECRET_KEY (for payments)
- YOCO_WEBHOOK_SECRET (to verify webhooks)
- EMAIL_HOST, EMAIL_PORT, EMAIL_USER, EMAIL_PASS, EMAIL_FROM (for ticket emails)
- FRONTEND_URL or APP_BASE_URL (used in email links)
- Frontend: create frontend/.env.local and set
- NEXT_PUBLIC_API_URL=http://localhost:<backend-port>
Example for default backend: NEXT_PUBLIC_API_URL=http://localhost:5000
- Organisation name, logo, brand colour, and notification emails are configured via the admin panel (Admin → Site Settings) and do not need to be set in .env files.
### 3) Run the apps
- Start backend (development):
- cd backend
- npm run dev
- By default runs at http://localhost:3000 (or the PORT you set)
- Start frontend (development):
- cd ../frontend
- npm run dev
- Next.js dev server runs at http://localhost:3000 by default; if it conflicts with backend, it will pick another port (e.g., 3001). Ensure NEXT_PUBLIC_API_URL points to the backend port.
## Deployment
### Backend Deployment
1. Provision a PostgreSQL database and set DATABASE_URL.
2. Set all required environment variables (see backend/README.md).
3. Run Prisma migrations (if applicable).
4. Start the server with: npm start (or a process manager like PM2).
5. Expose the webhook endpoint /api/webhooks/yoco publicly and configure the YOCO webhook to point to it.
6. Ensure the uploads directory public/uploads exists and is writable.
### Frontend Deployment
1. Build the frontend:
- cd frontend
- npm run build
2. Run with npm start (Next.js) on your server, or deploy to a platform like Vercel.
3. Ensure NEXT_PUBLIC_API_URL points to the public URL of your backend (including port if not 80/443).
### Ports and URLs
- Backend default port is 3000 unless overridden by PORT.
- Frontend dev server commonly uses 3000; set NEXT_PUBLIC_API_URL to avoid clashes (e.g., backend 3000, frontend 3001).
## Useful Links
- Backend API Documentation: ./backend/API_DOCUMENTATION.md
- Backend README: ./backend/README.md
- Frontend README: ./frontend/README.md
## Recent Changes
### Sold-out handling, tiered stock warnings & UI polish
**Backend**
- `GET /api/events` now computes and returns `isSoldOut: boolean` per event — true when every option with a stock limit (`stockLimit > 0`) is fully sold out. Events with only unlimited options are never sold out.
- `POST /api/settings/test-smtp` error responses now include a `raw` field (the original error message) alongside the human-readable `message`. Error code 530 (Microsoft "Client not authenticated to send mail") is now correctly mapped to the authentication-failure message.
**Frontend**
- **Event listing** (`/events`): `EventCard` shows a disabled "Sold Out" button (red tint) when `isSoldOut` is true, replacing the Register button. Free events now show "Register" without the "- R0.00" suffix.
- **Event detail page** (`/events/[id]`): computes sold-out state from returned option `availableCount`; shows a disabled "Sold Out" button instead of the Register link. Options with price `0` display "Free" instead of "R0.00".
- **Registration page** (`/register/[eventId]`): shows a red "This event is sold out" banner and replaces the Register button with a disabled "Sold Out" button when all limited options are exhausted.
- **Tiered low-stock threshold**: the "X remaining" badge now uses capacity-based percentages — ≤ 50 tickets: 20 %; 51200: 15 %; 2011,000: 10 %; 1,000+: 5 %. Changed from `Math.ceil` to `Math.round` to prevent rounding-up inflation of the threshold. Fixed a React rendering bug where `stockLimit && stockLimit > 0 && ...` evaluated to the number `0` (which React renders as visible text) instead of a boolean — this caused "00" to appear next to options with unlimited stock (`stockLimit = 0`).
- **Event image upload**: the EventForm (used in the create modal) now shows a file upload button with inline preview alongside the URL input field. Files are uploaded to `POST /api/uploads/event-image` and the returned URL is set automatically.
- **Attachments during event creation**: step 6 ("Attachments") of the supervisor create-event wizard now lets you stage files. They are uploaded to `POST /api/events/:id/attachments` immediately after the event record is created, so attachments no longer require a separate edit session.
- **SMTP test details**: on failure the "Test connection" result now shows a collapsible "Show technical details" section exposing the raw SMTP error alongside the friendly message. `ApiError` in `src/lib/api.ts` now carries a `data` field with the full JSON response body.
### Variants, stock limits, early-bird pricing & cancellation guard
**Backend**
- **`OptionVariant` model**: event options can now have sub-variants (sizes, colours, ticket types — e.g. Adult/Child/VIP under a single "Ticket" option). Variants have an optional `price` override and their own `stockLimit`.
- **Stock limits**: `stockLimit` added to `EventOption` and `EarlyBirdTier`. A value of `0` means unlimited. All stock counts exclude cancelled registrations.
- **Early-bird concurrency handling**: once a Yoco checkout has been issued at an early-bird price, that price is honoured even if another user simultaneously exhausts the tier's stock. Stock-based forfeiture only applies at registration time (via `resolveOptionPrice`). Deadline expiry at payment time forfeits the price; stock races after the checkout is issued do not.
- **Early-bird stock forfeiture**: if a tier's deadline passes after registration but before payment, `refreshPricingForRegistration` (called before every Yoco checkout) re-evaluates the tier and updates `priceSnapshot`. If prices changed, `POST /api/payments/yoco-checkout` returns `{ priceUpdated: true, newTotal }` (HTTP 200) so the frontend can show a warning before retrying.
- **`priceSnapshot` / `appliedTierId`** on `RegistrationOption`: price and tier are snapshotted at registration creation. `computeRegistrationTotalDue` uses `priceSnapshot` as authoritative when present (legacy rows without a snapshot fall back to deadline-only re-evaluation).
- **Cancelled registration tickets**: `GET /api/tickets/mytickets` now excludes tickets from cancelled registrations at the database level.
- **Cancellation payment guard**: `DELETE /api/registrations/:id` blocks non-admins from cancelling a registration that has positive payments. Admins can cancel at any time.
- **Variant CRUD**: `POST /api/events/options/:id/variants`, `PUT /api/events/variants/:id`, `DELETE /api/events/variants/:id`.
- **Testing mode** (`NODE_ENV=testing`): behaves like development (permissive CORS, all debug routes) but validates required env vars (`DATABASE_URL`, `JWT_SECRET`) at startup and enables rate limiting.
**Frontend**
- **Register page** (`/register/[eventId]`): variant picker — when an option has variants, shows each variant with its own qty control, stock badge (sold out / X remaining at ≤ 15 %), and price. Submission payload includes `variantId`. Early-bird notice shown when any active tier is present.
- **Event detail page** (`/events/[id]`): ticket section shows variant breakdown with "From R…" pricing, early-bird strikethrough, and sold-out/nearly-out badges.
- **User dashboard**: "Cancel registration" button appears in the registration modal when no payments have been made. Inline confirmation before the API call.
- **Pay page**: if the backend returns `priceUpdated: true`, an amber warning banner shows the new total and a dismiss button before the user can retry.
- **Supervisor events page** (`/dashboard/supervisor/events`): 6-step create/edit modal — "Basic Details", "Items & Pricing", "Sections", "Form", "Visibility", "Attachments". In create mode, step tabs are not clickable (linear navigation only); in edit mode any step can be jumped to. Options in "Items & Pricing" are collapsible cards; Variants and Early-bird tiers are expandable sub-sections within each option. `/dashboard/supervisor/sections` now redirects to the Events page.
- **Legal pages**: Terms of Use updated with early-bird pricing terms, partial payment terms, and self-cancellation policy. Privacy Policy updated with data anonymisation details.
### Site settings & first-time setup wizard
- **Admin → Site Settings** (`/dashboard/admin/settings`): organisation details, branding (colour + logo), notification emails, **SMTP email delivery**, and **legal page content** are stored in the database and editable at runtime.
- **Setup wizard** (`/setup`): on a fresh deployment (empty database) the platform automatically shows a wizard — organisation details, admin account creation, and branding. No manual DB seeding required.
- **`/api/settings`** (public): exposes public settings (org details, legal keys) for navbar and legal pages.
- **`/api/setup`** (public, one-time): creates the first admin account; blocked once any user exists.
- **Settings cache** (`settingsCache.js`): in-process 60-second cache with transparent AES-256-GCM decryption for sensitive keys.
- **SMTP via admin panel**: host, port, TLS, from address, username, and password are all configurable without touching `.env`. Password is stored AES-256-GCM encrypted (key derived from `JWT_SECRET`).
- **Legal pages dynamic**: Terms of Use and Privacy Policy populate org name, contact email, website URL, operator name, Information Officer details, and effective date from the settings DB.
- **`ORG_NAME`, `ORG_TAGLINE`, `EMAIL_HEADER_COLOR`, `REGISTRATIONS_EMAIL`, SMTP vars** no longer required in `.env` — all managed via the admin panel (env vars still work as fallbacks).
### Admin dashboard improvements
- **User management** (`/dashboard/admin/users`): filtering (role, active status, text search) is now server-side so page size is always consistent; per-page selector (10/25/50/100); "Delete data" button anonymises a user's personal information via `POST /api/users/:id/anonymize`.
- **Registrations** (`/dashboard/admin/registrations`): event dropdown, status, and fuzzy text filters; expandable rows showing ticket options and form responses loaded on demand.
### Forms page (`/dashboard/supervisor/forms`, `/dashboard/admin/forms`)
- **Print** opens a clean popup window (no site chrome) with one form response per A4 page — event name, attendee details, registration ID, and answers in a two-column grid.
- **User filter** replaced raw "User ID" text input with a searchable name/email/phone dropdown.
- **Event filter** includes "Include past events" and "Include inactive events" checkboxes.
- Filters in a responsive grid; dropdowns constrained to `max-w-xs`.
### Ticket printing
- **At-the-door individual print**: individual ticket print now generates a proper A6 ticket with QR code, event title, date, type, holder name, and ID — no longer just a UUID.
- **Staff event-tickets page**: A4 print layout matches at-the-door (2 columns × 4 rows, 8 tickets per page).
### Form builder & events
- **Form builder**: up/down reorder buttons; type badge (Text/Number/Date/etc.); separate quick-add buttons for Statement and Heading fields.
- **Early-bird tiers**: no manual order number — tiers ordered by list position; labelled Deadline and Price inputs.
### Bulk messaging dropdowns
- Attendee selection dropdown constrained to `max-w-xs` / `w-64` panel on email-attendees and whatsapp-attendees pages.
### Backend
- `GET /api/users` supports server-side `?search=`, `?role=`, `?isActive=` filtering.
- `POST /api/users/:id/anonymize` — erases personal data (admin only).
- `GET|PUT /api/settings`, `GET /api/settings/needs-setup`, `POST /api/setup` — settings and setup endpoints.
- `POST /api/uploads/logo` — logo upload (admin, or unauthenticated during first-time setup).
- `backend/src/utils/encryption.js` — AES-256-GCM encrypt/decrypt; key derived from `JWT_SECRET` via scrypt.
- SMTP transporter rebuilt dynamically when settings cache detects a config change — no restart required after updating SMTP settings.
## License
This project is licensed under the MIT License.