Add invite link to Admin panel

The Admin panel now shows the crew's join link (origin/#/game/{id}/join)
with Open and Copy buttons, so an admin can invite new players without
returning to the lobby. Matches the existing rejoin-link copy pattern.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-06-12 21:07:28 -07:00
parent e502155af7
commit fb17c632b0
2 changed files with 23 additions and 1 deletions

View File

@@ -5,7 +5,6 @@
## Minor Gameplay Polish ## Minor Gameplay Polish
- [ ] **Add invite link to Admin panel**
- [ ] **Add crew/player/rat name to page title, if available**. This will make it easier to distinguish tabs/browser history entries - [ ] **Add crew/player/rat name to page title, if available**. This will make it easier to distinguish tabs/browser history entries
- [ ] **Save active sessions in browser local storage**. Whenever you create or join a game, your browser local storage should be updated with the game ID, player ID, crew name, and player/rat names (if available). Going to the home page with saved sessions should provide a menu of games to rejoin in addition to the ability to start a new game. It should be possible to delete entries from this list, which only affects the browser local storage, not the backend database, and is undoable (the entry doesn't disappear immediately, it's just marked as deleted until page refresh). - [ ] **Save active sessions in browser local storage**. Whenever you create or join a game, your browser local storage should be updated with the game ID, player ID, crew name, and player/rat names (if available). Going to the home page with saved sessions should provide a menu of games to rejoin in addition to the ability to start a new game. It should be possible to delete entries from this list, which only affects the browser local storage, not the backend database, and is undoable (the entry doesn't disappear immediately, it's just marked as deleted until page refresh).
- [ ] **Dead Pi-Rat rank voting streamlined**. Don't allow voting for dead Pi-Rats. If a player's only vote options are dead, then they skip voting entirely. - [ ] **Dead Pi-Rat rank voting streamlined**. Don't allow voting for dead Pi-Rats. If a player's only vote options are dead, then they skip voting entirely.

View File

@@ -10,11 +10,17 @@
let error = ''; let error = '';
let copiedPlayerId = null; let copiedPlayerId = null;
let copiedTimer = null; let copiedTimer = null;
let copiedInvite = false;
let copiedInviteTimer = null;
function rejoinLink(playerId) { function rejoinLink(playerId) {
return `${window.location.origin}/#/game/${gameId}/player/${playerId}`; return `${window.location.origin}/#/game/${gameId}/player/${playerId}`;
} }
function inviteLink() {
return `${window.location.origin}/#/game/${gameId}/join`;
}
function copyRejoinLink(playerId) { function copyRejoinLink(playerId) {
navigator.clipboard.writeText(rejoinLink(playerId)); navigator.clipboard.writeText(rejoinLink(playerId));
copiedPlayerId = playerId; copiedPlayerId = playerId;
@@ -22,6 +28,13 @@
copiedTimer = setTimeout(() => { copiedPlayerId = null; }, 2000); copiedTimer = setTimeout(() => { copiedPlayerId = null; }, 2000);
} }
function copyInviteLink() {
navigator.clipboard.writeText(inviteLink());
copiedInvite = true;
clearTimeout(copiedInviteTimer);
copiedInviteTimer = setTimeout(() => { copiedInvite = false; }, 2000);
}
onMount(() => { onMount(() => {
// Try to load admin_key from local storage // Try to load admin_key from local storage
adminKey = localStorage.getItem(`admin_key_${gameId}`) || ''; adminKey = localStorage.getItem(`admin_key_${gameId}`) || '';
@@ -75,6 +88,16 @@
<p><strong>Phase:</strong> {data.game.phase}</p> <p><strong>Phase:</strong> {data.game.phase}</p>
<p><strong>Admin Key:</strong> <span class="admin-key-chip">{data.game.admin_key}</span></p> <p><strong>Admin Key:</strong> <span class="admin-key-chip">{data.game.admin_key}</span></p>
<h3 class="mt-8 mb-4">Invite Link</h3>
<p class="info-text">Share this link to let new players join the crew.</p>
<div class="link-copy-action">
<input type="text" readonly value={inviteLink()} class="input-field flex-grow" />
<a href={inviteLink()} class="btn btn-secondary btn-small">Open</a>
<button on:click={copyInviteLink} class="btn btn-secondary btn-small">
{copiedInvite ? '✓ Copied' : 'Copy'}
</button>
</div>
<h3 class="mt-8 mb-4">Players</h3> <h3 class="mt-8 mb-4">Players</h3>
<table class="admin-table"> <table class="admin-table">
<thead> <thead>