Skip to content

Investigate & implement walk-in queue #77

@balebbae

Description

@balebbae

Goal

Add a walk-in queue for at-event registration. When a walk-in's QR is scanned, they join a FIFO queue and receive a "you're queued" email. Super admin can later select N walk-ins to promote — those users get their application status flipped to accepted and receive an acceptance email.

Approach — Dedicated walk_ins table

Keep the queue as a first-class entity separate from application_status. Status represents review outcome; the queue represents arrival order + promotion audit trail. Overloading the status enum (Option A) fights the grading flow, and encoding the queue as scan records (Option C) fights the scan model.

Phase 1 — Investigation (do this first)

Before writing code, confirm the following and write up findings:

  • Do walk-ins always have a users row? A walk-in who previously applied through the portal does. A true drop-in who never registered does not, and scans.user_id is a FK. Decide: require prior registration, or build a "create user from scan" path. This choice drives most of the remaining scope.
  • Queue semantics on re-scan. If the same person is scanned twice, is it a no-op, an error, or does it update queued_at? Recommend no-op (unique on user_id).
  • Promotion on users who were previously rejected/waitlisted. Confirm with product whether promotion should override any prior final status.
  • What does "accepted via walk-in" imply for applications without a submitted application record? Do we stub an application row, or gate promotion on having one?
  • Email copy for both walk_in_queued and walk_in_accepted — get from marketing/ops.

Phase 2 — Backend

  • Migration 000015_add_walk_ins.up.sql: table walk_ins (id uuid pk, user_id uuid unique fk → users, queued_at timestamptz, promoted_at timestamptz null, promoted_by uuid null fk → users). Index on (promoted_at nulls first, queued_at) for FIFO.
  • Migration 000016_add_walkin_scan_type.up.sql: upsert {"name":"walk_in","display_name":"Walk-In","category":"walk_in","is_active":true} into the scan_types settings JSONB so existing deployments pick it up. Also update the seed in 000006_seed_settings.up.sql for fresh installs.
  • Add ScanCategoryWalkIn to internal/store/scans.go and extend the validate:"oneof=..." tag on ScanType.Category.
  • In cmd/api/scans.go createScanHandler: when the scanned category is walk_in, skip the check-in prerequisite (walk-ins haven't checked in by definition), upsert the user into walk_ins, and fire the queued email async.
  • In updateScanTypesHandler: mirror the "exactly one check_in" validation for walk_in so super admins can't delete it by accident.
  • New store: internal/store/walkins.go with Enqueue(userID), PromoteNext(count, promotedBy) -> []User, QueueDepth().
  • New handler + route: POST /v1/superadmin/walk-ins/promote with {count: N}. Single transaction: select N oldest un-promoted rows, stamp promotion, update applications.status = 'accepted' for each user (decide Phase 1 Q4 first), return the promoted users. Fire acceptance emails after commit.
  • New handler + route: GET /v1/superadmin/walk-ins returning queue depth + the pending list for the dashboard.
  • Mailer: add SendWalkInQueuedEmail and SendWalkInAcceptedEmail to the Client interface, plus mock implementations. Two new templates in internal/mailer/template/.
  • Handler tests in cmd/api/ following the existing pattern (newTestApplication, MockStore, setUserContext).

Phase 3 — Frontend

  • Scanner: walk-in category will appear automatically once seeded — verify the existing ScannerDialog handles it without code changes, or branch on category for any UX differences.
  • New super admin page (or card on an existing settings page): shows queue depth + "Promote next N walk-ins" input with confirm dialog. Uses postRequest from shared/lib/api.ts.
  • Co-locate page-specific store/api/types/components per the convention.

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions