# RSVP System — Full Documentation

A complete multi-tenant event RSVP, ticketing & check-in management system built in PHP/MySQL.

---

## Features

| Area | Feature |
|---|---|
| **Accounts** | Super-admin + admin roles, per-event **door-staff** logins |
| **Navigation** | Event-centric **workspace**: pick an event, then work through its tabs (Overview · Guests · Tickets · WhatsApp · Email · Door Staff · Reports). Advanced/technical controls stay off the everyday path |
| **Admin provisioning** | Super admin creates admins with plan limits (max events / max guests), email activation tokens |
| **Auth security** | Bcrypt hashing, CSRF protection, login rate-limiting, session-fixation protection, self-service password change + forgot-password reset |
| **Events** | Create/edit events with unique slugs, date, venue, status, per-event timezone; first-run guided setup |
| **Guests** | CSV bulk import (with de-dup + validation options), single add, **auto-saving** inline editor, pagination, bulk actions (delete / set category / mark sent), one-click Export menu |
| **Tickets** | QR ticket generation (phpqrcode) + drag-and-drop **Ticket Designer** with saved layouts and custom fonts |
| **WhatsApp** | Manual (wa.me links) **or** automatic sending via Meta WhatsApp Cloud API / Twilio; per-guest & batch "sent" tracking |
| **Email** | SMTP (PHPMailer) or `mail()` fallback, inline-embedded ticket image, per-event templates, test send |
| **Check-in (door)** | Camera QR scanner + manual search + **walk-in** registration; **audio + haptic** feedback; **offline queue** that never loses a scan; persistent result with reachable Undo; distinct success / duplicate / not-found / closed states; torch toggle; large one-handed tap targets |
| **Anti-replay** | Optional per-event check-in window (opens/closes) and single-use QR mode |
| **Dashboard & reports** | Live stats, feeds, per-method/checker/hour analytics, printable/PDF report, JSON analytics export |
| **Webhooks** | Per-event webhook POSTed on every check-in |
| **Audit log** | Every admin action recorded and viewable by super admins |
| **Exports** | CSV / Excel guest exports via a single scope + format menu |
| **White-label** | Guest pages & emails carry the organizer's `org_name` (not "RSVP System") |
| **Accessibility & mobile** | AA-contrast text, self-hosted brand serif (no CDN flash), responsive admin with a mobile drawer nav |
| **Public pages** | Guest self-lookup at `/event/{slug}`, ticket confirmation pages |

---

## Directory Structure

```
premium_rsvp/
├── config.php                ← config loader, DB, helpers, CSRF, throttling, webhooks, timezone, WhatsApp/email
├── config.local.php          ← LOCAL secrets (git-ignored, web-blocked) — create from .example
├── config.local.php.example  ← template for the above
├── schema.sql                ← full MySQL schema
├── migration_v3.sql          ← run once on existing installs (email/timezone/anti-replay/audit/throttle columns)
├── migrate_v2.sql, migration_ticket.sql, migration_whatsapp.sql  ← earlier migrations
├── .htaccess                 ← slug routing + hardened file protections
│
├── bin/
│   └── create_admin.php       ← CLI-only: create/reset a super admin securely
│
├── admin/
│   ├── login.php, logout.php, forgot_password.php, reset_password.php, activate.php
│   ├── dashboard.php, events.php, guests.php, guests_edit.php
│   ├── checkers.php, whatsapp.php, email.php, ticket_designer.php
│   ├── reports.php, report_print.php, audit.php
│   ├── account.php, manage_admins.php
│   └── _layout.php            ← sidebar, event-workspace tabs, CSRF wiring, toast/loader,
│                                 themed confirm modal, mobile drawer
│
├── checker/  (login, scan [QR + manual + walk-in + offline queue + torch], confirmed, logout)
├── event/    (index [public lookup], confirmed, checked, not_found)
│
├── api/
│   ├── checkin.php            ← check-in (POST authenticated, GET QR confirm), walk-in, window/anti-replay, webhook
│   ├── wa_send.php            ← send invite via WhatsApp Cloud API
│   ├── analytics.php          ← JSON analytics export per event
│   ├── search_guest.php, stats.php, live_feed.php, report_data.php
│   ├── export.php, guests_export.php, csv_template.php
│   ├── preview_ticket.php, fonts.php
│
├── assets/css/main.css
├── lib/  (qr_invite.php, phpqrcode/, fonts/, vendor/ [PHPMailer])
├── tests/  (bootstrap.php, HelpersTest.php, run.php)  ← unit tests
└── uploads/  (invites/, qrcodes/, templates/)
```

---

## Installation

### 1. Requirements
- PHP 8.0+ (GD extension; cURL for WhatsApp/webhooks)
- MySQL 5.7+ / MariaDB 10.4+ (for `IF NOT EXISTS` in migrations)
- Apache with `mod_rewrite`

### 2. Configure secrets
Copy the template and fill in real values (this file is git-ignored and blocked from web access):
```bash
cp config.local.php.example config.local.php
```
Credentials may also be supplied via environment variables (`DB_HOST`, `DB_NAME`, `DB_USER`, `DB_PASS`, `BASE_URL`) — these take priority over `config.local.php`.

### 3. Database
```bash
mysql -u root -p your_db < schema.sql
# Existing install upgrading? also run:
mysql -u root -p your_db < migration_v3.sql
```

### 4. Create the first super admin (CLI only — no default password ships)
```bash
php bin/create_admin.php admin@yourdomain.com "Super Admin"
# prompts for a password (min 8 chars)
```

### 5. phpqrcode & PHPMailer
- QR: place the phpqrcode library in `lib/phpqrcode/` (key file `qrlib.php`).
- Email: `cd lib && composer require phpmailer/phpmailer` (or drop the classes in `lib/phpmailer/`).

### 6. Permissions
```bash
chmod -R 755 uploads/
```

---

## Security

- **Secrets** live in `config.local.php` / env vars, never in version-controlled `config.php`.
- **CSRF**: all state-changing POSTs require a token (`csrfField()` / `requireCsrf()`); AJAX sends `X-CSRF-Token`.
- **Login throttling**: failed admin/checker logins are rate-limited per IP + username (`login_attempts` table).
- **Sessions**: `Secure` cookie flag auto-set on HTTPS; `HttpOnly`; `SameSite=Lax`; ID regenerated on login.
- **Passwords**: bcrypt; self-service change + emailed reset flow; no default password shipped.
- **Prepared statements** throughout; guest tokens are `random_bytes(16)`.
- **`.htaccess`** blocks `config*.php`, `*.sql`, `.env`, dotfiles, `bin/`, `tests/`, and `*_test.php`.
- **Audit log** records admin actions with actor, target, and IP.

---

## Interface & UX

**Event workspace (admin).** The app is organized around the *event*, not the tool. The
global sidebar is intentionally short — **Dashboard · Events · Reports** (plus **Manage
Admins · Audit Log** for super admins, and **Settings** in the footer). Opening an event
lands you on its **Overview** with a tab strip that carries the whole event:

```
← Events  ·  My Gala 2026
Overview | Guests | Tickets | WhatsApp | Email | Door Staff | Reports
```

Everyday work stays on the happy path — *create event → import guests → send invites →
check in → see who came*. Technical controls (timezone, single-use QR, check-in window,
webhook, SMTP, WhatsApp API tokens) are tucked behind **Advanced** disclosures so they
never clutter the common flow. A brand-new admin sees a guided "Create your first event"
hero instead of empty dashboards.

**Door / check-in (checker on a phone).** Built for a fast, noisy, low-signal doorway:

- **Audio + haptic** confirmation — distinct success chime vs. error buzz, with vibration.
- **Offline queue** — if the network drops, the scan is saved locally, shown as
  "N waiting to sync", and retried automatically when back online. A scan is never lost.
- **Persistent result** — the last outcome (and its **Undo**) stays on screen until the
  next scan, instead of vanishing on a timer.
- **Clear states** — success, already-checked-in (with who/when), not-found, and a
  separate amber **check-in closed** state.
- **Torch toggle** for dim venues (Android Chrome; hidden where unsupported), large
  full-width tap targets, QR / Search / Walk-in modes, and working Undo everywhere.

**Admin ergonomics.** The guest editor **auto-saves** each cell (no separate "save"
step); destructive actions use a themed confirm dialog; newly created door-staff
credentials appear in a "save these now" card with a copy button. Guest export is a single
**scope + CSV/Excel** menu rather than a wall of buttons.

**Look & feel.** Luxury dark theme (obsidian + gold). The brand serif (Cormorant Garamond)
is **self-hosted** so it never flashes/falls back on a slow CDN; UI/body fonts load
non-blocking with system fallbacks. Muted text meets WCAG AA contrast. The admin collapses
to a top bar + drawer under 760px.

**White-labeling.** Guest-facing pages and invite emails display the organizer's
`org_name` (set in Account settings), falling back to the event title — not the internal
product name.

---

## Per-event advanced settings (Events ▸ edit ▸ Advanced)

- **Timezone** — IANA name used to display event dates & check-in times.
- **Check-in opens / closes** — enforced window; scans outside it are rejected.
- **Single-use QR** — once checked in, a ticket cannot be undone.
- **Webhook URL** — receives a JSON POST on every check-in.

---

## WhatsApp delivery (Account ▸ WhatsApp)

Choose a send mode per admin account:

- **Manual** — opens WhatsApp with a pre-filled message (tap to send).
- **Meta Cloud API** — automatic; set the Phone Number ID + a permanent access token.
- **Twilio** — automatic; set the Account SID, Auth Token, and WhatsApp sender number.

For either automatic provider, use the ⚡ **API** button on the WhatsApp page (or wire batch sends). Every send is recorded in `wa_send_log` (visible in guest exports and the "WA failed" filter).

---

## Testing

Pure helper functions are covered by unit tests.

```bash
# No dependencies:
php tests/run.php

# With PHPUnit:
composer require --dev phpunit/phpunit
./vendor/bin/phpunit
```

---

## CSV Guest List Format

```csv
name,email,phone,table,seat,category
Olumide Fawehinmi,olumide@example.com,+2348012345678,1,A1,VIP
Adaeze Okonkwo,adaeze@example.com,+2348087654321,2,B3,Regular
Emeka Nwosu,,,3,,Press
```
- Only `name` is required. Invalid emails are cleared; phone is normalized.
- Toggle **Allow duplicates** per upload to permit or skip guests already in the list (matched on name + email).
- Template: `/api/csv_template.php`.

---

## Database Schema Summary

| Table | Purpose |
|---|---|
| `admins` | Admin accounts (+ SMTP & WhatsApp Cloud creds, plan limits) |
| `events` | Events (+ timezone, check-in window, single-use, webhook, email/wa templates) |
| `checkers` | Per-event door-staff accounts |
| `guests` | Guest records, tokens, invite/email sent tracking, check-in status |
| `checkin_log` | Full audit trail of check-in / undo actions |
| `login_attempts` | Login throttling state |
| `admin_log` | Admin action audit trail |
