Advance after final rank vote and allow ballot changes
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
/* --- Between-scenes upkeep --- */
|
||||
.between-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
gap: 2rem;
|
||||
margin: 2rem 0;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
export let state;
|
||||
|
||||
let voting = false;
|
||||
let readying = false;
|
||||
let voteError = "";
|
||||
let confirming = false;
|
||||
|
||||
let selectedVoteId = '';
|
||||
@@ -134,28 +134,18 @@
|
||||
async function submitVote() {
|
||||
if (!selectedVoteId) return;
|
||||
voting = true;
|
||||
voteError = "";
|
||||
try {
|
||||
await apiRequest(`/game/${state.game.id}/player/${state.player.id}/submit-vote`, 'POST', {
|
||||
nominated_id: selectedVoteId
|
||||
});
|
||||
} catch(e) {
|
||||
console.error(e);
|
||||
voteError = e.message;
|
||||
} finally {
|
||||
voting = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function setReady() {
|
||||
readying = true;
|
||||
try {
|
||||
await apiRequest(`/game/${state.game.id}/player/${state.player.id}/ready-next`, 'POST');
|
||||
} catch(e) {
|
||||
console.error(e);
|
||||
} finally {
|
||||
readying = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function confirmRefresh() {
|
||||
confirming = true;
|
||||
try {
|
||||
@@ -173,7 +163,9 @@
|
||||
|
||||
<div class="between-scenes-view text-center">
|
||||
<h2>Between Scenes · Scene {state.game.current_scene_number}</h2>
|
||||
<p class="description">Upkeep and tallying. Nominate a crewmate to Rank Up, redraw hand cards for resting Deep players, and ready up for the next scene.</p>
|
||||
<p class="description">{state.game.phase === 'between_scenes'
|
||||
? 'Vote for a crewmate to rank up. You can change your vote until everyone has voted.'
|
||||
: 'Voting complete. The resting Deep refreshes their hand before the next scene.'}</p>
|
||||
|
||||
{#if state.player.is_dead && !state.player.is_ghost && !state.player.needs_reroll}
|
||||
<!-- Death Fate Panel -->
|
||||
@@ -204,7 +196,9 @@
|
||||
<!-- Ranks and Voting Card -->
|
||||
<div class="card glass-panel voting-card">
|
||||
<h3>Rank Up</h3>
|
||||
<p class="section-desc">Every player (including the Deep) nominates one crewmate who best exemplified pirate qualities in the previous scene. Previous Deep players are ineligible to receive votes.</p>
|
||||
{#if state.game.phase === 'between_scenes'}
|
||||
<p class="section-desc">Nominate another Pi-Rat who best showed their pirate spirit this scene. The Deep cannot receive votes.</p>
|
||||
{/if}
|
||||
|
||||
{#if state.game.phase === 'deep_upkeep'}
|
||||
<div class="voted-confirmation-box glass-panel">
|
||||
@@ -214,26 +208,30 @@
|
||||
<p class="success-text">✔️ Voting complete! No one ranked up this time.</p>
|
||||
{/if}
|
||||
</div>
|
||||
{:else if hasVoted}
|
||||
{:else}
|
||||
{#if hasVoted}
|
||||
<div class="voted-confirmation-box glass-panel">
|
||||
<p class="success-text">✓ Your vote: <strong>{votedNominee ? displayName(votedNominee) : "Unavailable crewmate"}</strong></p>
|
||||
</div>
|
||||
{:else if !mustVote}
|
||||
{/if}
|
||||
{#if !mustVote}
|
||||
<div class="voted-confirmation-box glass-panel">
|
||||
<p class="info-text">No living crewmates left to nominate — you skip voting this round. Go ahead and ready up.</p>
|
||||
<p class="info-text">No eligible crewmates — voting skipped.</p>
|
||||
</div>
|
||||
{:else}
|
||||
<form on:submit|preventDefault={submitVote} class="vote-form inline-form">
|
||||
<div class="form-group inline-group">
|
||||
<select class="select-field" bind:value={selectedVoteId} required>
|
||||
<select aria-label="Nominee" class="select-field" bind:value={selectedVoteId} required>
|
||||
<option value="">Nominate crewmate...</option>
|
||||
{#each myNominees as p}
|
||||
<option value={p.id}>{displayName(p)} (Rank {p.rank})</option>
|
||||
{/each}
|
||||
</select>
|
||||
<button type="submit" class="btn btn-primary" disabled={voting || !selectedVoteId}>Cast Nomination</button>
|
||||
<button type="submit" class="btn btn-primary" disabled={voting || !selectedVoteId || selectedVoteId === myVote?.nominated_player_id}>{hasVoted ? "Change Vote" : "Vote"}</button>
|
||||
</div>
|
||||
</form>
|
||||
{/if}
|
||||
{#if voteError}<p class="alert alert-danger" role="alert">{voteError}</p>{/if}
|
||||
{/if}
|
||||
|
||||
</div>
|
||||
@@ -320,7 +318,7 @@
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<!-- Next Scene Ready Up -->
|
||||
<!-- Upkeep progress and end-story action -->
|
||||
<div class="action-box margin-top">
|
||||
{#if state.game.phase === 'deep_upkeep'}
|
||||
{#if state.player.role === 'deep' && !state.player.is_ready}
|
||||
@@ -331,24 +329,6 @@
|
||||
<div class="spinner-small"></div>
|
||||
</div>
|
||||
{/if}
|
||||
{:else}
|
||||
{#if mustVote && !hasVoted}
|
||||
<p class="info-text gold-text" style="margin-bottom:0.5rem;">⚠️ You must nominate a crewmate above before you can ready up.</p>
|
||||
<button class="btn btn-primary btn-large" disabled>Ready for Next Scene</button>
|
||||
{:else}
|
||||
{#if state.player.is_ready}
|
||||
<div class="waiting-box">
|
||||
<p>Ready! Waiting for other players to ready up...</p>
|
||||
<div class="spinner-small"></div>
|
||||
</div>
|
||||
{:else}
|
||||
<button on:click={setReady}
|
||||
disabled={readying}
|
||||
class="btn btn-primary btn-large glow-effect">
|
||||
Ready for Next Scene
|
||||
</button>
|
||||
{/if}
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
{#if state.player.is_admin && state.game.phase === 'between_scenes'}
|
||||
@@ -360,3 +340,12 @@
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.vote-form { margin-top: 1rem; }
|
||||
.vote-form button { white-space: nowrap; flex-shrink: 0; }
|
||||
.vote-form select { min-width: 0; }
|
||||
@media (max-width: 600px) {
|
||||
.vote-form .inline-group { flex-direction: column; align-items: stretch; }
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -6,10 +6,11 @@
|
||||
// - Add a CHANGELOG entry only when a commit changes something players can
|
||||
// see. Skip refactors, tests, and tooling. Keep wording player-facing.
|
||||
|
||||
export const VERSION = 30;
|
||||
export const VERSION = 31;
|
||||
|
||||
// Newest first. Each entry: { version, date: 'YYYY-MM-DD', changes: [string, ...] }.
|
||||
export const CHANGELOG = [
|
||||
{ version: 31, date: '2026-09-04', changes: ['Change your rank-up vote until everyone has voted. The final vote now reveals the result and advances automatically, without a Ready button.', 'The vote result stays visible through upkeep and next-scene setup.'] },
|
||||
{ version: 30, date: '2026-09-04', changes: ['The crew roster shows voting progress with icons.', 'Your submitted vote now shows the crewmate you nominated.'] },
|
||||
{ version: 29, date: '2026-09-04', changes: ['Removed the remaining deck count from the table.'] },
|
||||
{ version: 28, date: '2026-09-04', changes: ['The Event Log toggle keeps the same size when opened or closed.', 'Dev Mode changes no longer clutter the Event Log.'] },
|
||||
|
||||
@@ -229,6 +229,15 @@
|
||||
<div class="phase-view-layout" class:log-collapsed={!phaseLogOpen}>
|
||||
<CrewSidebar {state} />
|
||||
<main class="phase-main-column">
|
||||
{#if ['scene_setup', 'recruit_creation'].includes(state.game.phase) && (state.game.last_rank_up_player_id || state.game.current_scene_number > 1)}
|
||||
<div class="notice-banner" role="status">
|
||||
{#if state.game.last_rank_up_player_id}
|
||||
🏆 {displayName(state.players.find(p => p.id === state.game.last_rank_up_player_id))} won the rank-up vote!
|
||||
{:else}
|
||||
Voting complete. No one ranked up this time.
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
{#if state.game.phase === 'lobby'}
|
||||
<LobbyPhase {state} />
|
||||
{:else if state.game.phase === 'character_creation' || state.game.phase === 'swap_techniques' || state.game.phase === 'assign_techniques'}
|
||||
|
||||
Reference in New Issue
Block a user