Add vote finalize UX and missing-vote warning placement

This commit is contained in:
2026-02-04 23:02:09 +01:00
parent 0571877f3a
commit 58159119cf
5 changed files with 31 additions and 0 deletions

View File

@@ -118,10 +118,12 @@
<div class="card subcard phase-nav" id="nav-vote">
<div class="nav-text">
<p data-i18n="nav.voteHint">Cast votes for every game to unlock results.</p>
<span class="badge warning hidden" id="vote-missing" data-i18n="vote.missingWarn">You havent voted yet for all games.</span>
<span class="badge warning hidden" id="results-lock" data-i18n="admin.resultsLocked">Results locked by admin</span>
</div>
<div class="nav-actions">
<button id="nav-vote-prev" class="ghost" data-i18n="nav.prev">Back</button>
<button id="finalize-votes" class="secondary" type="button" data-i18n="vote.finalize">Finalize votes</button>
<button id="nav-vote-next" class="primary" data-i18n="nav.next">Next</button>
</div>
</div>

View File

@@ -9,6 +9,7 @@ export async function loadState() {
state.prevPhase = state.phase;
state.phase = stateData.currentPhase;
state.resultsOpen = stateData.resultsOpen;
state.votesFinal = stateData.votesFinal ?? me?.votesFinal ?? false;
state.counts = stateData;
if (state.prevPhase !== state.phase && state.phase === "Vote") {
state.votesRendered = false;

View File

@@ -78,6 +78,9 @@ const translations = {
"vote.saved": "Saved vote",
"vote.missing": "Missing",
"vote.missingWarn": "You havent voted yet. Slide to set a score.",
"vote.finalize": "Finalize votes",
"vote.unfinalize": "Edit votes",
"vote.finalHint": "Finalize when youre done. You can unfinalize to change scores.",
"results.rank": "Rank",
"results.game": "Game",
@@ -197,6 +200,9 @@ const translations = {
"vote.saved": "Stimme gespeichert",
"vote.missing": "Fehlt",
"vote.missingWarn": "Du hast hier noch nicht abgestimmt. Schiebe den Regler.",
"vote.finalize": "Abstimmung abschließen",
"vote.unfinalize": "Abstimmung bearbeiten",
"vote.finalHint": "Schließe ab, wenn du fertig bist. Zum Ändern wieder öffnen.",
"results.rank": "Rang",
"results.game": "Spiel",

View File

@@ -5,6 +5,7 @@ export const state = {
phase: null,
prevPhase: null,
resultsOpen: false,
votesFinal: false,
counts: null,
mySuggestions: [],
allSuggestions: [],
@@ -19,6 +20,7 @@ export function clearUserState() {
state.phase = null;
state.prevPhase = null;
state.resultsOpen = false;
state.votesFinal = false;
state.counts = null;
state.mySuggestions = [];
state.allSuggestions = [];

View File

@@ -642,6 +642,14 @@ function formatMyVote(score) {
return `${score} ${scoreToEmoji(score)}`;
}
function missingVotesCount() {
const total = state.allSuggestions?.length ?? 0;
const mine = state.myVotes?.length ?? 0;
const votedIds = new Set(state.myVotes?.map((v) => v.suggestionId));
const missing = total - votedIds.size;
return missing < 0 ? 0 : missing;
}
function openDeleteConfirmModal(s) {
const overlay = document.createElement("div");
overlay.className = "edit-modal";
@@ -733,6 +741,18 @@ export function updatePhaseNav() {
lockBadge.textContent = t("admin.resultsLocked");
}
const finalizeBtn = $("finalize-votes");
if (finalizeBtn) {
finalizeBtn.textContent = state.votesFinal ? t("vote.unfinalize") : t("vote.finalize");
}
const voteMissingBadge = $("vote-missing");
if (voteMissingBadge) {
const missing = missingVotesCount();
voteMissingBadge.classList.toggle("hidden", missing === 0);
voteMissingBadge.textContent = missing > 0 ? t("vote.missingWarn") : "";
}
// Toggle admin-only back buttons
const backButtons = ["nav-vote-prev"];
backButtons.forEach((id) => {