diff --git a/README.md b/README.md index 88b09ef..80a43e5 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,21 @@ # Turnpike -Self-hosted event attendee and volunteer management. One instance, one event. +Self-hosted event ticketing and volunteer management. One instance, one event. Turnpike handles gate check-in, volunteer scheduling, and department coordination for events ranging from a single evening to a multi-day festival. It works offline — gate volunteers can check people in without a network connection and sync when connectivity returns. ## Features -- **Attendee management** — CSV import (CrowdWork/Zeffy auto-detected), party-size tracking, search, check-in +- **Participant & ticket management** — CSV import (CrowdWork/Zeffy auto-detected), search, check-in - **Volunteer scheduling** — departments, shifts with capacity, conflict detection, reordering -- **Public volunteer signup** — self-registration form with email confirmation, auto-attendee linking -- **Volunteer kiosk** — token-authenticated self-service shift signup, no login required -- **Gate check-in** — full-screen UI with QR scanner, party check-in ("2/3 checked in"), volunteer dual check-in +- **Public volunteer signup** — self-registration form with email confirmation, auto-participant linking +- **Volunteer kiosk** — code-authenticated self-service shift signup, no login required +- **Gate check-in** — full-screen UI with QR scanner, volunteer dual check-in - **Schedule** — create shifts, assign volunteers, manage assignments with conflict awareness -- **Role-based access** — admin, coordinator, volunteer lead (department-scoped), gate +- **Role-based access** — admin, ticketing, staffing, colead (department-scoped), gatekeeper - **Offline-first PWA** — installs on phones/tablets, full offline check-in with background sync - **Real-time** — check-ins and changes broadcast live via SSE -- **SMTP email** — send volunteer token links directly or export CSV for bulk email platforms +- **SMTP email** — volunteer confirmation emails, kiosk link distribution when shift signups open - **Single binary** — Go backend embeds the frontend; no runtime dependencies ## Tech Stack @@ -60,10 +60,11 @@ See [docs/INSTALLATION.md](docs/INSTALLATION.md) for systemd, Docker, NixOS, and | Role | Access | |------|--------| -| `admin` | Full access: attendee import, user management, SMTP settings, all departments and shifts | -| `coordinator` | All departments: volunteers, shifts, schedule. No user management or settings | -| `volunteer_lead` | Own department only: volunteers and shifts scoped to assigned department | -| `gate` | Full-screen check-in UI with QR scanner. No access to other pages | +| `admin` | Full access: participant import, user management, SMTP settings, all departments and shifts | +| `ticketing` | Participants, tickets, import. No user management | +| `staffing` | All departments: volunteers, shifts, schedule. No user management or settings | +| `colead` | Own department only: volunteers and shifts scoped to assigned department(s) | +| `gatekeeper` | Full-screen check-in UI with QR scanner. No access to other pages | See [docs/USAGE.md](docs/USAGE.md) for detailed workflow documentation. @@ -91,7 +92,7 @@ The Vite dev server runs on `:5173` and proxies `/api` requests to the Go server ## Documentation -- [Usage Guide](docs/USAGE.md) — event setup, attendee import, volunteer signup, volunteer kiosk, gate check-in, schedule +- [Usage Guide](docs/USAGE.md) — event setup, participant import, volunteer signup, volunteer kiosk, gate check-in, schedule - [Installation Guide](docs/INSTALLATION.md) — building, deploying, systemd, Docker, NixOS, reverse proxy, backup ## License diff --git a/docs/INSTALLATION.md b/docs/INSTALLATION.md index 1f9a967..9bc0dbc 100644 --- a/docs/INSTALLATION.md +++ b/docs/INSTALLATION.md @@ -105,23 +105,27 @@ docker run -p 8180:8180 \ ## NixOS -Turnpike builds with `buildGoModule` using the pure-Go SQLite driver (no CGO): +Turnpike builds with `buildGoModule` + `buildNpmPackage` (pure-Go SQLite, no CGO). The frontend is built separately and copied into the Go build: ```nix +frontendDist = pkgs.buildNpmPackage { + pname = "turnpike-frontend"; + src = "${src}/frontend"; + npmDepsHash = "sha256-..."; + buildPhase = "npm run build"; + installPhase = "cp -r dist $out"; +}; + turnpike = pkgs.buildGoModule { pname = "turnpike"; - version = "0.1.0"; - src = ./path/to/turnpike; # must include vendor/ and frontend/dist/ - vendorHash = null; + src = fetchgit { url = "..."; rev = "v2.0.0"; hash = "sha256-..."; }; + vendorHash = "sha256-..."; env.CGO_ENABLED = 0; + preBuild = "cp -r ${frontendDist} frontend/dist"; }; ``` -The source directory must contain: -- Go source files and `vendor/` (run `go mod vendor`) -- Pre-built frontend at `frontend/dist/` (run `cd frontend && npm run build`) - -A complete NixOS module example with `DynamicUser`, `StateDirectory`, and agenix secrets is in the project's `homelab/turnpike.nix`. +A complete NixOS module with `DynamicUser`, `StateDirectory`, and secrets is in the project's `homelab/turnpike.nix`. ## Reverse Proxy diff --git a/docs/USAGE.md b/docs/USAGE.md index de4e765..11684d4 100644 --- a/docs/USAGE.md +++ b/docs/USAGE.md @@ -12,23 +12,22 @@ After logging in, create accounts for your team under **Users**. Each user gets | Role | What they see | What they can do | |------|--------------|------------------| -| **admin** | All pages + Settings | Everything: attendee import, user management, SMTP config, departments, shifts, volunteers | -| **coordinator** | Dashboard, Schedule, Volunteers, Departments | Manage volunteers, departments, and shifts across all departments. Cannot manage users or settings | -| **volunteer_lead** | Schedule, Volunteers, Departments | Manage volunteers and shifts within their assigned department only | -| **gate** | Full-screen Gate UI | Check in attendees (search + QR scan). No access to other pages | +| **admin** | All pages + Settings | Everything: participant import, user management, SMTP config, departments, shifts, volunteers | +| **ticketing** | Participants, Tickets, Import | Manage participants and tickets, run CSV imports | +| **staffing** | Dashboard, Schedule, Volunteers, Departments | Manage volunteers, departments, and shifts across all departments. No user management or settings | +| **colead** | Dashboard, Schedule, Volunteers | Manage volunteers and shifts within their assigned department(s) only | +| **gatekeeper** | Full-screen Gate UI | Check in ticket holders (search + QR scan). No access to other pages | -Ticketing and ops staff should use the **admin** role. The `ticketing` role exists in the codebase but is effectively unused — admin covers all ticketing functions. - -Volunteer leads are scoped to a single department. When creating a volunteer_lead user, assign their department. +Coleads are scoped to one or more departments. When creating a colead user, assign their department(s). ## Event Setup -1. **Configure your event** — go to the Dashboard and set the event name and dates. +1. **Configure your event** — set the event name, venue, dates, and timezone via the API (`PUT /api/event`). These appear on the Dashboard. 2. **Create departments** — under Departments, add each department your event needs (e.g., Gate, Greeters, Rangers, Build, LNT). -3. **Import attendees** — see next section. +3. **Import participants** — see next section. 4. **Create shifts** — under Schedule, create shifts for each department with day, start/end time, and capacity. -## Importing Attendees +## Importing Participants Go to **Import** and upload a CSV file. Turnpike auto-detects two formats: @@ -36,7 +35,7 @@ Go to **Import** and upload a CSV file. Turnpike auto-detects two formats: | Column | Maps to | |--------|---------| -| `Patron Name` | Name | +| `Patron Name` | Ticket name | | `Patron Email` | Email | | `Order Number` | Ticket ID | | `Tier Name` | Ticket type | @@ -45,7 +44,7 @@ Go to **Import** and upload a CSV file. Turnpike auto-detects two formats: | Column | Maps to | |--------|---------| -| `name` (required) | Name | +| `name` (required) | Ticket name | | `email` | Email | | `ticket_id` | Ticket ID | | `ticket_type` | Ticket type | @@ -53,27 +52,21 @@ Go to **Import** and upload a CSV file. Turnpike auto-detects two formats: Column matching is case-insensitive. Extra columns are ignored. BOM-encoded files (Windows Excel exports) are handled automatically. -### Party-size dedup +### Participants and tickets -CrowdWork exports one row per ticket, even when the same person bought multiple tickets in one order. Turnpike handles this automatically: +Each row in the CSV creates one **ticket**. Participants are deduplicated by email — multiple tickets with the same email address are linked to a single participant record. The import result shows `inserted` (new tickets) and `skipped` (exact duplicates). -- First row for "Titania Fairweather" (order 1234) creates a record with `party_size=1` -- Subsequent rows with the same name + order number increment `party_size` (no duplicate record) -- Result: one attendee record, `party_size=3` if three tickets were purchased - -The import result shows `inserted` (new records), `grouped` (merged into existing party), and `skipped` (exact duplicates). - -Re-importing the same CSV is safe — existing records are skipped, not duplicated. +Re-importing the same CSV is safe — exact duplicates are skipped, not duplicated. ## Volunteer Signup -Turnpike provides a public signup form for volunteers at `/#/volunteer-signup`. No login is required. +Turnpike provides a public signup form for volunteers at `/volunteer-signup`. No login is required. ### Signup flow 1. Volunteer visits the signup form and fills in: preferred name (required), ticket name, email (required), pronouns, phone, department preference, and an optional note. -2. Turnpike creates a volunteer record and auto-links it to an existing attendee by email match, or creates a new attendee record. -3. A confirmation email is sent with a unique link (`/#/confirm/{token}`). +2. Turnpike creates a volunteer record and auto-links it to an existing participant by email match, or creates a new participant record. +3. A confirmation email is sent with a unique link (`/confirm/{token}`). 4. The volunteer clicks the link to confirm their email. 5. If shift signups are already open, the confirmation page includes a link to the kiosk for shift selection. @@ -90,7 +83,7 @@ In **Settings**, the "Volunteer Signup" card controls: In **Settings**, the "Shift Signups" card has an open/close toggle: -- **Opening** signups generates kiosk tokens for all confirmed volunteers and emails them their shift signup links. A confirmation dialog warns before sending. +- **Opening** signups generates kiosk codes for all confirmed volunteers and emails them their shift signup links. A confirmation dialog warns before sending. - **Closing** signups prevents new kiosk links from being issued on confirmation, but existing links continue to work. If a volunteer confirms their email while signups are already open, they receive their kiosk link immediately in the confirmation response and via email. @@ -100,11 +93,11 @@ If a volunteer confirms their email while signups are already open, they receive Under **Volunteers**, you can: - Create volunteers manually (name, email, department) -- Link a volunteer to an existing attendee record (for dual check-in at the gate) - Assign volunteers to departments +- Mark volunteers as co-leads - Check in volunteers -Volunteers are separate from attendees. A person can be both an attendee (ticket holder) and a volunteer (shift worker). Linking them enables the gate team to check in both records simultaneously. +Volunteers are separate from participants. A person can be both a ticket holder and a volunteer. When a volunteer signs up via the public form, they are automatically linked to their participant record by email. ## Shift Scheduling @@ -128,15 +121,17 @@ The kiosk lets volunteers self-select shifts without logging in. ### Setup -1. **Generate tokens** — on the Attendees page, click "Generate Tokens." This creates a unique 8-character code for every attendee that doesn't have one. -2. **Distribute tokens** — two options: - - **Export CSV** — downloads a file with columns `Email Address`, `First Name`, `Token`, `Signup Link`. Import this into MailChimp, Zeffy, or any email platform. - - **Email directly** — if SMTP is configured (see below), use "Email All" to send token links, or email individually per attendee. -3. **Set base URL** — in Settings, set the public base URL (e.g., `https://turnpike.example.com`). Token links use this URL. +Kiosk links are generated and distributed automatically through the volunteer signup flow: + +1. Volunteers sign up via the public signup form (`/volunteer-signup`) and confirm their email. +2. In **Settings**, open shift signups. This generates kiosk codes for all confirmed volunteers and emails them their links. A confirmation dialog warns before sending. +3. If a volunteer confirms their email while signups are already open, they receive their kiosk link immediately. + +**Set base URL** — in Settings, set the public base URL (e.g., `https://turnpike.example.com`). Kiosk links use this URL. ### Volunteer experience -Each volunteer receives a link like `https://turnpike.example.com/#/v/ABC12345`. This opens a mobile-friendly page showing: +Each volunteer receives a link like `https://turnpike.example.com/v/ABC12345`. This opens a mobile-friendly page showing: - Their name and department - Currently assigned shifts @@ -144,20 +139,19 @@ Each volunteer receives a link like `https://turnpike.example.com/#/v/ABC12345`. Claiming a shift checks for time conflicts. If a conflict exists, the volunteer sees which shifts overlap and can confirm to proceed anyway. -No login is required. The 8-character token authenticates the request. +No login is required. The kiosk code authenticates the request. -### Token format +### Code format -Tokens use the character set `A-Z, 2-9` (excluding 0/O, 1/I/L to avoid ambiguity when reading aloud or on printed badges). +Kiosk codes use the character set `A-Z, 2-9` (excluding 0/O, 1/I/L to avoid ambiguity when reading aloud or on printed badges). ## Gate Check-In -Users with the **gate** role see a dedicated full-screen UI: +Users with the **gatekeeper** role see a dedicated full-screen UI: - **QR scanner** — uses the device camera via the BarcodeDetector API. Scanned codes populate the search field. -- **Search** — type a name to filter attendees in real-time (searches local IndexedDB, works offline). -- **Party check-in** — for attendees with `party_size > 1`, the gate UI shows progress ("2/3 checked in") and offers "Check in 1" or "Check in all remaining." -- **Volunteer dual check-in** — if an attendee is linked to a volunteer record, the gate UI shows their volunteer status and offers to check in both simultaneously. +- **Search** — type a name to filter tickets in real-time (searches local IndexedDB, works offline). +- **Volunteer dual check-in** — if a ticket holder is also a volunteer, the gate UI shows their volunteer status and offers to check in both simultaneously. - **Recent check-ins** — the last 10 check-ins are shown for quick reference. Gate devices should install Turnpike as a PWA (Add to Home Screen) for the best experience. Check-ins are stored locally and sync when connectivity is available. @@ -170,7 +164,7 @@ The Schedule page is the primary UI for managing shifts and volunteer assignment - Each shift card shows: name, time, capacity (used/total), assigned volunteers - Conflict badges when a volunteer has overlapping shifts on the same day -**Coordinators and admins** see all departments. **Volunteer leads** see only their assigned department. +**Admins and staffing** see all departments. **Coleads** see only their assigned department(s). Actions available: - Create new shifts (+ Add shift button) @@ -182,7 +176,7 @@ Actions available: ## SMTP Configuration -SMTP enables token email distribution and test emails. Configure in **Settings** (admin only): +SMTP enables volunteer confirmation emails, kiosk link distribution, and test emails. Configure in **Settings** (admin only): | Field | Description | |-------|-------------| @@ -203,13 +197,13 @@ Turnpike is a Progressive Web App (PWA). After the first load, it works offline: - **Gate check-ins** are stored in the browser's IndexedDB and sync when connectivity returns. - **Real-time updates** use Server-Sent Events (SSE). When the connection drops, the client reconnects automatically. -- **Sync** pulls all changes from the server on startup and periodically thereafter. Local changes are queued in an outbox and flushed in order. +- **Sync** pulls all changes from the server on startup and periodically thereafter. Install Turnpike as a PWA (Add to Home Screen on mobile, or Install App in desktop Chrome) for the best offline experience. ## CSV Exports -Two CSV exports are available from the Attendees page: +CSV exports are available from the Participants page: -- **Attendee export** — all attendee records with check-in status -- **Token link export** — columns: `Email Address`, `First Name`, `Token`, `Signup Link`. Only includes attendees with tokens. Compatible with MailChimp and Zeffy for bulk email campaigns. +- **Participant export** — all participant records with check-in status +- **Ticket export** — all ticket records with codes and check-in status