# VGM Queue Admin Guide

This guide explains how to run the Video Game Ministries queue, brackets, and booth signup page during a live event.

## What The System Does

The queue has two public-facing lanes:

- `Ready Up`: players who have been pulled into the current game
- `Live Queue`: players still waiting

The admin page lets you:

- unlock with the admin PIN
- allow, block, remove, or search a live gamertag rule from the admin page
- create and switch between queue and bracket events
- manage the global member and Jr Member roster
- move players into the current game
- pause or resume new queue joins
- move a waiting player to the front
- enable one per-event `Event Boost` for a Member
- ban a waiting player inline
- remove a waiting player
- remove a checked-in player
- clear the current game
- delete an event

The booth page gives table volunteers one public touch screen for:

- the Video Game Missions giveaway signup
- Super Smash Bros tournament signup
- Mario Kart tournament signup
- FORTNITE tournament signup

The booth admin page lets you:

- update the giveaway form destination
- update each tournament signup link
- update each tournament date and time
- save booth changes without editing code or pushing Git

For bracket events, the admin page also lets you:

- collect player signups from the public random bracket URL
- collect first name, last name, phone, and email for admin follow-up
- paste Google Forms/Sheets signup exports
- build a bracket from public signups
- export bracket signups to CSV for Google Sheets
- build a single-elimination bracket
- randomize first-round assignments
- add late players into open first-round bye/TBD slots
- advance winners live
- share a public bracket link with viewers
- open a TV popout bracket display

## Public Vs Admin Pages

- Public queue: `https://queue.vgm.life/index.html?event=EVENTCODE`
- Public bracket: `https://bracket.vgm.life/bracket.html?event=EVENTCODE`
- Booth page: `https://queue.vgm.life/booth.html`
- Booth admin: `https://queue.vgm.life/boothadmin.html`
- Admin page: `https://queue.vgm.life/admin.html`

Players should only use the public event link.

Admins should only use the admin page.

On the public page, the admin link is labeled `Organizer`.

## Admin Login

1. Open the admin page.
2. Enter the admin PIN.
3. Click `Unlock`.

Notes:

- The PIN is stored only for the current browser tab session.
- `Clear PIN` logs the tab out of admin mode.
- There is no separate username/password flow right now.
- All event, roster, and queue controls stay hidden until the PIN is accepted.

### Manage Gamertag Rules Live

Use the banned-word box under `Admin Access` when you need to allow a known false positive exactly, block a new abusive pattern, remove one from the live database, or quickly check whether a term is currently allowed.

What happens:

- the term is normalized to letters and numbers only
- `Allow` writes an exact-match override into the live allow-list table
- `Block` writes it directly into the live blocked-term database table
- `Block` also removes the same exact term from the allow list so a manual block always wins
- `Remove` deletes the exact term from the live allow list, the live blocked list, or both
- `Search` checks the live allow/block matcher, so partial matches such as `yousuck` still return blocked if `suck` is already banned
- when a search is blocked, the result also tells you which blocked term matched
- when a search is allowed by exact override, the result tells you which exact allow term matched
- blocked terms take effect on new join attempts right away

Important behavior:

- the allow list is exact-match only
- allow-list matches are case-insensitive after normalization
- the allow list is checked before the blocked matcher
- an exact allowed name such as `Ashterinn` can pass while another name such as `eshterinn` is still blocked by `sht`

This is the fastest way to allow, block, or unblock a term during a live stream without rerunning the seed SQL.

## Event Flow

### Event Boost

`Event Boost` is a per-event admin override for `Member` only.

What it does:

- only one boosted Member is allowed per event
- the star is clickable only on the live queue/current-game cards, not in the roster
- clicking the outlined Member star fills it and enables `Event Boost`
- if that Member is lower than `#1`, they move to waiting `#1` immediately
- while boosted, that Member stays pinned as the event's `#1`
- `Clear Current Game` does not remove the boosted Member; they stay checked in until the star is turned off
- turning on `Event Boost` also consumes that Member's normal event priority for the rest of the event
- clicking the filled star again removes the boost, but it does not restore the consumed event priority
- if another Member is already boosted, the admin must unstar them before boosting someone else

`Jr Member` does not support `Event Boost`.

### Create A New Event

1. Unlock the admin page.
2. In `New Event`, enter a title such as `Friday Night Smash`.
3. Choose `Live Queue` or `Bracket`.
4. Click `New Event`.
5. Copy or open the generated public event link.
6. Share that public link with players.

Each event gets a random event code in the URL. This keeps queues separate.

### Run A Queue Event

Use a `Live Queue` event when players should join a waiting line and be called into the current game.

1. Unlock the admin page.
2. Create or select a `Live Queue` event.
3. Share the generated public queue link with players.
4. Watch new players appear under `Live Queue`.
5. Click `Ready Up` when a player should enter the current game.
6. Click `Clear Current Game` after the current group is done.
7. Click `Pause Queue` if signups should temporarily stop.
8. Click `Resume Queue` when signups can continue.
9. Click `Delete Event` when the event should disappear from public/admin event lists.

Queue notes:

- paused queues keep current players visible, but block new joins
- `Ready Up` removes a player from the waiting line and shows them in the public `Ready Up` lane
- `Move To Front` is a manual organizer override
- `Ban` blocks the waiting player's gamertag and removes them from the selected queue
- `Event Boost` is available for one Member per event
- deleted queue events remain available to the stats dashboard as historical reporting data

### Import A Bracket

1. Create or select a `Bracket` event.
2. Share the generated public bracket link with players, or open it on an iPad for walk-up signups.
3. Leave `Randomize first-round assignments` checked unless you need signup order to become seed order.
4. Click `Build From Signups` when registration is ready.

### Import A Bracket From Google Forms

1. Create or select a `Bracket` event.
2. Copy names from Google Forms/Sheets, or paste CSV rows into `Signup export`.
3. Leave `Randomize first-round assignments` checked unless you need pasted order to become seed order.
4. Click `Import Bracket`.
5. Confirm that the import should add names to current signups and rebuild bracket matches.
6. Use the live bracket cards to advance winners.

Notes:

- bracket v1 is single-elimination
- random assignment is enabled by default
- when random assignment is off, pasted order becomes seed order
- duplicate imported public names are ignored
- imported names are merged with public signups instead of erasing them
- the current bracket supports up to 128 players
- public viewers use the generated bracket link, while admins advance winners from the admin page
- public signups close automatically once the bracket has been built
- phone and email are never shown on the public bracket
- admins can export first name, last name, gamertag, phone, and email as CSV for Google Sheets
- bracket events do not appear in the stats dashboard

### Add A Late Player

Use `Add late player` when someone signs up after the bracket has been imported.

What happens:

- the system looks for an open first-round bye/TBD slot
- if the bye had auto-advanced, the auto-advance is pulled back as long as the next match has not already been completed
- if there are no open slots, or the affected next match is already complete, the add is blocked

For late additions to work during a live first round, leave enough bye/TBD room when possible. If the bracket is already full, reimport/reset before play starts.

### TV Popout

Use `TV popout` from the bracket controls to open a clean display URL for a monitor or TV.

The TV view uses the same live bracket data, hides organizer chrome, and refreshes automatically.

## Booth Guide

Use the booth page when a shared iPad, kiosk, QR code, or conference-table screen needs one simple place for visitors to choose what they want to sign up for.

What the booth is for:

- visitors do not need to remember separate giveaway and tournament links
- tournament signups can return to the booth after completion
- volunteers can update links and schedules from `boothadmin.html`
- the public booth page can stay open during a conference or outreach event

### Set Up Booth Links

1. Create the tournament bracket events from the main admin page first.
2. Copy each generated public bracket link.
3. Open `https://queue.vgm.life/boothadmin.html`.
4. Unlock with the same admin PIN used by the main admin page.
5. Paste the Video Game Missions Google Form embed code or iframe `src` URL.
6. Paste the Super Smash Bros, Mario Kart, and FORTNITE bracket links.
7. Set each tournament date and time.
8. Click `Save Booth Setup`.
9. Open or refresh `https://queue.vgm.life/booth.html` on the booth device.

Booth notes:

- booth admin changes are saved in Supabase
- `booth.html` uses saved links first and falls back to the defaults in `js/booth-links.js`
- booth admin does not create queue or bracket events
- create bracket events in the main admin page before pasting links into booth admin
- if a booth device was already open, refresh it after saving new booth setup
- tournament booth links should point to `https://bracket.vgm.life/bracket.html?event=EVENTCODE`

### Switch Between Events

1. Unlock the admin page.
2. Use the `Events` list.
3. Click the event you want to manage.

The admin page stays the same. Only the selected event changes.

### Delete An Event

1. Select the event.
2. Click `Delete Event`.
3. Confirm the warning.

Deleting an event hides it from active admin/public event flows and closes out:

- waiting players for that event
- checked-in players for that event

Queue event history remains available to the stats dashboard. This cannot be undone from the UI.

### Pause Queue / Resume Queue

`Pause Queue` temporarily blocks new sign-ups for the selected event without disturbing the current line.

What happens:

- waiting players stay in order
- checked-in players stay in `Ready Up`
- the public page keeps showing the event board
- new players cannot join until `Resume Queue` is clicked

Suggested use cases:

- a bad actor joined and the host needs a moment to clean up the line
- the event is close to ending and you only want to finish the current players
- you need a short hold without deleting the event

The button changes to `Resume Queue` while the event is paused.

## Queue Management

### Ready Up

`Ready Up` moves a waiting player into the current game.

What happens:

- the player leaves the waiting line
- the player appears in `Checked In` on admin
- the player appears in `Ready Up` on public
- everyone behind them moves up naturally

Use this when a player is called into the current match.

### Move To Front

`Move To Front` is a manual override for any waiting player.

What happens:

- that player becomes `#1`
- everyone who was ahead of them shifts down
- no badge or special message is shown publicly

Suggested use cases:

- brand new first-time player
- birthday guest
- contest winner
- organizer discretion

### Ban Waiting Player

Waiting players include a small inline `Ban` pill beside the joined time.

What happens:

- the player's current gamertag is normalized and added to the live blocked-term table
- that waiting player is removed from the selected event queue immediately
- the waiting line closes the gap naturally

Suggested use case:

- obvious troll or abusive gamertag during a live event

This is a fast moderation shortcut. If you later want broader coverage than the exact gamertag, you can still use the banned-word tools under `Admin Access`.

### Remove

`Remove` deletes a waiting player from the current event queue.

Use this for:

- no-shows
- duplicate entries that slipped through
- typo entries that should be removed instead of played

This only applies to waiting players, not players already in `Checked In`.

### Remove From Checked In

Checked-in players also have a `Remove` button.

What happens:

- the player is removed from `Checked In`
- they are removed from the event entirely
- they must join the queue again if they still want to play

Suggested use case:

- the player has an internet issue or disconnects right as they are pulled into the game

This does not place them back into the waiting line automatically.
This also does not advance the game. Only `Clear Current Game` moves the event into the next game state.

### Clear Current Game

`Clear Current Game` ends the current game round.

What happens:

- players in `Checked In` are cleared out
- a boosted `Event Boost` Member is preserved in `Checked In` until the star is turned off
- waiting players remain in the queue
- waiting players move up naturally because the checked-in group is gone

Example:

- Checked In: Sue, Tim, Alex
- Waiting: Bob `#1`, Joe `#2`
- Click `Clear Current Game`
- Result: Sue, Tim, Alex are removed from the current game view and Bob/Joe remain waiting at the front

This is the main button to use between games.

## Member And Jr Member Roster

The member roster is global across all events.

That means:

- add a Member once
- add a Jr Member once
- their badge applies in every event automatically
- the live member roster survives normal `supabase/schema.sql` reruns

### Add A Member

1. Unlock admin.
2. In `Member Roster`, enter the gamertag exactly.
3. Choose `Member` or `Jr Member`.
4. Click `Save Member`.

### Delete A Member

1. Unlock admin.
2. Find the name in `Member Roster`.
3. Click `Delete`.

### Important Matching Rule

Roster matching is case-insensitive.

Examples:

- `Sue`
- `sue`
- `SUE`

all count as the same roster name.

But the queue only matches if the entered gamertag itself is valid and uses the same letters/numbers. If someone joins under a different spelling, they will not get the badge.

## Member Priority Rules

Members and Jr Members always keep their badge.

Priority is:

- global badge
- per-event one-time priority
- consumed on `Ready Up`
- not consumed on queue join
- consumed only when it actually gives the player a bump
- waiting players continue to rise naturally after clears

### Priority Order

- `Member` outranks `Jr Member`
- `Jr Member` outranks normal players
- within the same tier, earlier join time stays ahead

### Example

If Bob is `#1` and Joe is `#2`, and Member Sue joins:

- Sue becomes `#1`
- Bob becomes `#2`
- Joe becomes `#3`

### Opening Priority Window

At the start of an event:

- the opening window follows the natural trio for the first two games
- natural event slots `1-3` can reorder for game 1 without consuming priority
- natural event slots `4-6` can also reorder immediately
- their bump stays free only when they remain in the second-game path
- if a natural `4-6` player breaks into the remaining game 1 slots, that bump is consumed
- later joiners can still use full priority strength, but if they bump their way into those protected trios, that bump is consumed
- if a member/jr member originally joins at `7+` and a bump moves them up, that bump can consume priority
- original `1-3` who rejoin after game 1 and before players are moved to `Ready Up` for game 2 can be no higher than `#4`
- after the second `Clear Current Game`, normal priority rules apply again

### Priority Repair

If a live event ever gets bad priority state for one player, there is now a backend-only repair action available:

- `admin_repair_event_priority`

It resets `priority_consumed` to `false` for one player in one event while preserving the original queue slot that event used for opening-window rules.

This is intended for rare live recovery situations, not routine queue management.

After a player has used their event priority:

- they still keep the badge
- they rejoin as a normal player for that event

## Gamertag Rules

Gamertags must be:

- 3 to 16 characters
- letters and numbers only
- no spaces
- no special characters

Examples that work:

- `Bob123`
- `Sue7`
- `AlexGamer`

Examples that do not work:

- `Bob_123`
- `Sue!`
- `Bob 123`
- `ab`

Players also cannot join the same event twice with the same active gamertag.

Allowed:

- same gamertag in two different events
- same gamertag rejoining after they were cleared from the current game

Not allowed:

- same gamertag joining the same event twice while already waiting or checked in

## Recommended Event Night Workflow

1. Unlock the admin page.
2. Create a new event.
3. Share the public event link in voice chat, stream chat, Discord, or on screen.
4. Watch players enter the queue.
5. Use `Ready Up` to pull players into the current game.
6. Use `Pause Queue` if you need to stop new joins temporarily.
7. Use `Move To Front` when you want to manually prioritize someone.
8. Use `Remove` for no-shows or mistakes.
9. When the game ends, click `Clear Current Game`.
10. Repeat until the event is done.
11. Delete the event when you no longer need it.

## Troubleshooting

### The Admin Page Says The PIN Did Not Work

- Make sure the correct PIN was entered
- click `Clear PIN`
- re-enter the PIN
- if needed, verify the PIN stored in Supabase

### A Player Says They Were A Member But Did Not Get A Badge

Check:

- the roster entry exists
- the roster gamertag matches the player name
- the player did not use a different spelling

### A Player Cannot Join

Common reasons:

- invalid gamertag format
- already active in the same event
- bad event link
- queue is paused
- Turnstile security check was not completed
- rate limit reached after too many attempts in one minute
- gamertag matched the blocked-term filter

Suggested checks:

- refresh the page and complete the security check again
- confirm the event is not paused
- verify the gamertag uses only letters and numbers and is 3 to 16 characters
- wait one minute and retry if several attempts were just made

### The Queue Feels Slow

A small amount of delay can happen on the free Supabase and Vercel tiers, especially during cold starts or first activity after idle time.

If it becomes noticeable:

- keep the admin page open during the event
- avoid opening many admin tabs at once
- use one active admin operator when possible

## Technical Notes For Maintainers

- The public page is a static HTML/CSS/JS app hosted on Vercel.
- Supabase stores queue data and runs the secure admin RPC functions.
- The frontend uses the Supabase anon key.
- Admin security is enforced by server-side PIN verification inside SQL functions.
- Public joins go through a Supabase Edge Function that verifies Turnstile before the queue insert.
- `supabase/blocked_terms_seed.sql` only needs to be rerun on first setup or when the blocked-term list changes.
- Admin-page banned-word changes update the live database immediately.
- New production database changes should go through `supabase/migrations/`.
- `supabase/patches.sql` is now a legacy fallback for older/manual environments.
- `supabase/schema.sql` is a destructive reset file and should not be the normal production path.

Main files:

- `index.html`
- `admin.html`
- `styles.css`
- `js/public.js`
- `js/admin.js`
- `supabase/migrations/`
- `supabase/schema.sql` (legacy full reset reference)
- `supabase/patches.sql` (legacy manual patch reference)
