Add per-user phase navigation with results toggle

This commit is contained in:
2026-02-04 21:43:12 +01:00
parent b64a33d833
commit e5e27af0af
24 changed files with 507 additions and 88 deletions

View File

@@ -46,10 +46,12 @@ export const api = {
vote: (suggestionId, score) => request("/api/votes", { method: "POST", body: { suggestionId, score } }),
results: () => request("/api/results"),
nextPhase: () => request("/api/me/phase/next", { method: "POST" }),
prevPhase: () => request("/api/me/phase/prev", { method: "POST" }),
};
export const adminApi = {
setPhase: (phase) => request("/api/admin/phase", { method: "POST", body: { phase } }),
setResultsOpen: (resultsOpen) => request("/api/admin/results", { method: "POST", body: { resultsOpen } }),
reset: () => request("/api/admin/reset", { method: "POST" }),
factoryReset: () => request("/api/admin/factory-reset", { method: "POST" }),
};

View File

@@ -8,6 +8,7 @@ export async function loadState() {
state.me = me;
state.prevPhase = state.phase;
state.phase = stateData.currentPhase;
state.resultsOpen = stateData.resultsOpen;
state.counts = stateData;
if (state.prevPhase !== state.phase && state.phase === "Vote") {
state.votesRendered = false;
@@ -52,7 +53,7 @@ export async function loadVoteData() {
}
export async function loadResults() {
if (state.phase !== "Results") return;
if (state.phase !== "Results" || !state.resultsOpen) return;
state.results = await api.results();
renderResults();
}

View File

@@ -31,6 +31,10 @@ const translations = {
"counts.format": "Players: {players} • Suggestions: {suggestions} • Votes: {votes}",
"nav.prev": "Back",
"nav.next": "Next",
"nav.waitingForResults": "Waiting…",
"suggest.title": "Suggest games (up to 5)",
"suggest.new": "Add new suggestion",
"suggest.addButton": "Suggest a game",
@@ -79,10 +83,11 @@ const translations = {
"admin.title": "Admin",
"admin.tools": "Admin tools",
"admin.setPhase": "Set phase",
"admin.resultsOpenToggle": "Allow results phase",
"admin.resultsLocked": "Results locked by admin",
"admin.resultsUpdated": "Results availability updated",
"admin.reset": "Reset (keep players)",
"admin.factoryReset": "Factory reset",
"admin.phaseUpdated": "Phase updated",
"admin.resetDone": "Reset complete",
"admin.factoryResetDone": "Factory reset complete",
@@ -138,6 +143,10 @@ const translations = {
"counts.format": "Spieler: {players} • Vorschläge: {suggestions} • Stimmen: {votes}",
"nav.prev": "Zurück",
"nav.next": "Weiter",
"nav.waitingForResults": "Warten…",
"suggest.title": "Schlage Spiele vor (bis zu 5)",
"suggest.new": "Neuen Vorschlag hinzufügen",
"suggest.addButton": "Spiel vorschlagen",
@@ -186,10 +195,11 @@ const translations = {
"admin.title": "Admin",
"admin.tools": "Admin-Werkzeuge",
"admin.setPhase": "Phase setzen",
"admin.resultsOpenToggle": "Ergebnisse freigeben",
"admin.resultsLocked": "Ergebnisse vom Admin gesperrt",
"admin.resultsUpdated": "Ergebnisfreigabe aktualisiert",
"admin.reset": "Zurücksetzen (Spieler behalten)",
"admin.factoryReset": "Werkseinstellung",
"admin.phaseUpdated": "Phase aktualisiert",
"admin.resetDone": "Zurücksetzen abgeschlossen",
"admin.factoryResetDone": "Werkseinstellung abgeschlossen",

View File

@@ -4,6 +4,7 @@ export const state = {
me: null,
phase: null,
prevPhase: null,
resultsOpen: false,
counts: null,
mySuggestions: [],
allSuggestions: [],
@@ -17,6 +18,7 @@ export function clearUserState() {
state.me = null;
state.phase = null;
state.prevPhase = null;
state.resultsOpen = false;
state.counts = null;
state.mySuggestions = [];
state.allSuggestions = [];

View File

@@ -65,7 +65,8 @@ export function handleAuthError(err, clearUserState) {
export function renderPhasePill() {
const phaseKey = typeof state.phase === "string" ? state.phase.toLowerCase() : null;
$("phase-pill").textContent = phaseKey ? "" : t("phase.loading");
const pill = $("phase-pill");
if (pill) pill.textContent = phaseKey ? t(`phase.${phaseKey}`) : t("phase.loading");
document.querySelectorAll(".phase-view").forEach((el) =>
el.classList.add("hidden"),
);
@@ -77,9 +78,27 @@ export function renderPhasePill() {
};
const id = viewMap[state.phase];
if (id) $(id).classList.remove("hidden");
const phaseSelect = $("phase-select");
if (phaseSelect && !phaseSelect.dataset.userEditing) {
phaseSelect.value = state.phase || "Suggest";
const prevBtn = $("prev-phase");
if (prevBtn) prevBtn.disabled = state.phase === "Suggest";
const nextBtn = $("next-phase");
if (nextBtn) {
const atResults = state.phase === "Results";
const locked = !state.resultsOpen && state.phase === "Vote";
nextBtn.disabled = atResults || locked;
nextBtn.textContent = locked ? t("nav.waitingForResults") : t("nav.next");
}
const resultsLock = $("results-lock");
if (resultsLock) {
resultsLock.classList.toggle("hidden", state.resultsOpen);
resultsLock.textContent = t("admin.resultsLocked");
}
const adminResultsToggle = $("results-open");
if (adminResultsToggle) {
adminResultsToggle.checked = !!state.resultsOpen;
}
}