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
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions