Add help FAQ modal and i18n help content

This commit is contained in:
2026-02-06 18:25:55 +01:00
parent c0756ff2c6
commit d9288809c1
5 changed files with 220 additions and 0 deletions

View File

@@ -170,6 +170,11 @@ function setupHandlers() {
adminClose.addEventListener("click", () => togglePanel(false)); adminClose.addEventListener("click", () => togglePanel(false));
} }
const helpChip = $("help-chip");
if (helpChip) {
helpChip.addEventListener("click", () => openFaqModal());
}
const resultsToggle = $("results-open"); const resultsToggle = $("results-open");
if (resultsToggle) { if (resultsToggle) {
resultsToggle.addEventListener("change", async (e) => { resultsToggle.addEventListener("change", async (e) => {
@@ -378,3 +383,97 @@ function bindNavButtons() {
}); });
} }
} }
function faqTree() {
return [
{
title: t("help.cat.gettingStarted"),
items: [
{ q: t("help.q.join"), a: t("help.a.join") },
{ q: t("help.q.adminKey"), a: t("help.a.adminKey") },
],
},
{
title: t("help.cat.suggest"),
items: [
{ q: t("help.q.limit"), a: t("help.a.limit") },
{ q: t("help.q.editNames"), a: t("help.a.editNames") },
{ q: t("help.q.mediaRules"), a: t("help.a.mediaRules") },
],
},
{
title: t("help.cat.vote"),
items: [
{ q: t("help.q.howVote"), a: t("help.a.howVote") },
{ q: t("help.q.finalize"), a: t("help.a.finalize") },
{ q: t("help.q.linkedVotes"), a: t("help.a.linkedVotes") },
],
},
{
title: t("help.cat.results"),
items: [
{ q: t("help.q.resultsLocked"), a: t("help.a.resultsLocked") },
{ q: t("help.q.resultsContent"), a: t("help.a.resultsContent") },
],
},
{
title: t("help.cat.admin"),
items: [
{ q: t("help.q.openResults"), a: t("help.a.openResults") },
{ q: t("help.q.linkDuplicates"), a: t("help.a.linkDuplicates") },
{ q: t("help.q.grantJoker"), a: t("help.a.grantJoker") },
{ q: t("help.q.reset"), a: t("help.a.reset") },
],
},
];
}
function openFaqModal() {
const overlay = document.createElement("div");
overlay.className = "edit-modal";
const panel = document.createElement("div");
panel.className = "edit-panel faq-panel";
panel.innerHTML = `
<div class="edit-header">
<h3>${t("help.title")}</h3>
<button class="lightbox-close" aria-label="${t("modal.close")}">x</button>
</div>
<div class="edit-body">
<p class="faq-intro">${t("help.intro")}</p>
<div class="faq-list"></div>
</div>
`;
const list = panel.querySelector(".faq-list");
faqTree().forEach((section) => {
const sec = document.createElement("details");
sec.className = "faq-section";
const summary = document.createElement("summary");
summary.textContent = section.title;
sec.appendChild(summary);
const itemsWrap = document.createElement("div");
itemsWrap.className = "faq-items";
section.items.forEach((item) => {
const qd = document.createElement("details");
qd.className = "faq-item";
const qs = document.createElement("summary");
qs.textContent = item.q;
const ans = document.createElement("p");
ans.textContent = item.a;
qd.append(qs, ans);
itemsWrap.appendChild(qd);
});
sec.appendChild(itemsWrap);
list.appendChild(sec);
});
const close = () => overlay.remove();
overlay.addEventListener("click", (e) => {
if (e.target.classList.contains("edit-modal") || e.target.classList.contains("lightbox-close")) close();
});
overlay.appendChild(panel);
document.body.appendChild(overlay);
}

View File

@@ -114,6 +114,16 @@ button .chip {
border-color: #a83a35; border-color: #a83a35;
} }
.help-chip {
background: #dff3ff;
border-color: #b6ddff;
color: #0f3a6f;
font-weight: 700;
}
.help-chip:hover {
background: #cde8ff;
}
.nav-btn { .nav-btn {
min-width: 64px; min-width: 64px;
font-weight: 700; font-weight: 700;

View File

@@ -76,3 +76,41 @@
.preview-card { .preview-card {
pointer-events: none; pointer-events: none;
} }
.faq-panel .faq-list {
display: flex;
flex-direction: column;
gap: 8px;
}
.faq-intro {
margin-top: 0;
margin-bottom: 12px;
color: #5b4a32;
}
.faq-section {
border: 1px solid #e3d4bd;
border-radius: 10px;
padding: 8px 10px;
background: #fff7ec;
}
.faq-section > summary {
font-weight: 700;
cursor: pointer;
}
.faq-items {
display: flex;
flex-direction: column;
gap: 6px;
padding-top: 6px;
margin-left: 6px;
}
.faq-item {
border: 1px solid #eedec3;
border-radius: 8px;
padding: 6px 8px;
background: #ffffff;
}
.faq-item > summary {
font-weight: 600;
cursor: pointer;
}

View File

@@ -76,6 +76,7 @@
<span class="counts" id="counts"></span> <span class="counts" id="counts"></span>
</div> </div>
<div class="status-right"> <div class="status-right">
<button id="help-chip" class="chip icon help-chip" type="button" aria-label="Help" title="Help" data-i18n="help.label" data-i18n-attr="aria-label,title">?</button>
<div class="lang-switch" id="lang-switch-status"> <div class="lang-switch" id="lang-switch-status">
<button class="lang-button chip icon" type="button" aria-label="Language" title="Language">🌐</button> <button class="lang-button chip icon" type="button" aria-label="Language" title="Language">🌐</button>
<div class="lang-menu hidden"> <div class="lang-menu hidden">

View File

@@ -158,6 +158,42 @@ const translations = {
"modal.close": "Close", "modal.close": "Close",
"lightbox.close": "Close", "lightbox.close": "Close",
"help.label": "Help",
"help.title": "FAQ & tips",
"help.intro": "Expand a section to see answers for players and admins.",
"help.cat.gettingStarted": "Getting started",
"help.cat.suggest": "Suggest phase",
"help.cat.vote": "Vote phase",
"help.cat.results": "Results phase",
"help.cat.admin": "Admin tools",
"help.q.join": "How do I join?",
"help.a.join": "Register with a username, password, and display name. Then log in with the same credentials.",
"help.q.adminKey": "What is the admin key?",
"help.a.adminKey": "If your host shares the admin key, enter it during registration to unlock admin controls.",
"help.q.limit": "How many games can I add?",
"help.a.limit": "Up to 5 suggestions per player. A joker lets you add one extra during the vote phase.",
"help.q.editNames": "Can I edit or delete suggestions later?",
"help.a.editNames": "Game names lock after leaving Suggest. Optional fields stay editable; admins can edit or delete any time.",
"help.q.mediaRules": "Any rules for links and images?",
"help.a.mediaRules": "Use http(s) links. Screenshots must end with an image file (png, jpg, gif, webp, avif). Invalid links are rejected.",
"help.q.howVote": "How do votes work?",
"help.a.howVote": "Move the 010 slider for each game. Your score and emoji update immediately.",
"help.q.finalize": "What does finalize do?",
"help.a.finalize": "Finalize locks your votes until you unfinalize. You must score every game first.",
"help.q.linkedVotes": "What are linked games?",
"help.a.linkedVotes": "Admins can link duplicates. Changing one linked slider updates all siblings; votes are shared.",
"help.q.resultsLocked": "Why can't I see results?",
"help.a.resultsLocked": "Results stay hidden until an admin opens them. You move to Results automatically when unlocked.",
"help.q.resultsContent": "What do results show?",
"help.a.resultsContent": "A leaderboard with averages, vote counts, your own score, and any links or media for each game.",
"help.q.openResults": "How do I open or close results?",
"help.a.openResults": "Admins toggle results on the Admin panel. Opening moves everyone to Results; closing sends players back to Vote and clears finalizations.",
"help.q.linkDuplicates": "How do I merge duplicates?",
"help.a.linkDuplicates": "In Vote, pick two games under Admin Link games. Linking clears votes for that group and unfinalizes affected players.",
"help.q.grantJoker": "When should I grant a joker?",
"help.a.grantJoker": "Grant a joker during Vote to let a player add one extra suggestion; their finalized state resets.",
"help.q.reset": "Reset vs factory reset?",
"help.a.reset": "Reset clears suggestions/votes and phases but keeps players. Factory reset wipes everything, including players, and reseeds the app.",
}, },
de: { de: {
"lang.label": "Sprache", "lang.label": "Sprache",
@@ -318,6 +354,42 @@ const translations = {
"modal.close": "Schließen", "modal.close": "Schließen",
"lightbox.close": "Schließen", "lightbox.close": "Schließen",
"help.label": "Hilfe",
"help.title": "FAQ & Tipps",
"help.intro": "Klappe einen Abschnitt auf, um Antworten für Spieler und Admins zu sehen.",
"help.cat.gettingStarted": "Erste Schritte",
"help.cat.suggest": "Phase Vorschlagen",
"help.cat.vote": "Phase Bewerten",
"help.cat.results": "Phase Ergebnisse",
"help.cat.admin": "Admin-Werkzeuge",
"help.q.join": "Wie kann ich mitmachen?",
"help.a.join": "Registriere dich mit Benutzername, Passwort und Anzeigenamen. Danach mit denselben Daten anmelden.",
"help.q.adminKey": "Was ist der Admin-Schlüssel?",
"help.a.adminKey": "Wenn der Host dir den Admin-Schlüssel gibt, trage ihn bei der Registrierung ein, um Admin-Rechte zu erhalten.",
"help.q.limit": "Wie viele Spiele darf ich hinzufügen?",
"help.a.limit": "Bis zu 5 Vorschläge pro Spieler. Ein Joker erlaubt während der Abstimmung einen zusätzlichen Vorschlag.",
"help.q.editNames": "Kann ich Vorschläge später ändern oder löschen?",
"help.a.editNames": "Spieltitel sind nach Verlassen der Vorschlagsphase gesperrt. Optionale Felder bleiben bearbeitbar; Admins können jederzeit ändern oder löschen.",
"help.q.mediaRules": "Gibt es Regeln für Links und Bilder?",
"help.a.mediaRules": "Nutze http(s)-Links. Screenshots müssen auf eine Bilddatei enden (png, jpg, gif, webp, avif). Ungültige Links werden abgelehnt.",
"help.q.howVote": "Wie funktioniert die Abstimmung?",
"help.a.howVote": "Bewege den 010 Schieberegler für jedes Spiel. Deine Wertung und das Emoji aktualisieren sich sofort.",
"help.q.finalize": "Was bedeutet Abschließen?",
"help.a.finalize": "Abschließen sperrt deine Stimmen, bis du wieder öffnest. Zuerst müssen alle Spiele bewertet sein.",
"help.q.linkedVotes": "Was sind verknüpfte Spiele?",
"help.a.linkedVotes": "Admins können Duplikate verknüpfen. Ein Regler ändert dann die Stimmen aller verknüpften Spiele gemeinsam.",
"help.q.resultsLocked": "Warum sehe ich keine Ergebnisse?",
"help.a.resultsLocked": "Ergebnisse bleiben verborgen, bis ein Admin sie freigibt. Danach wechselst du automatisch zur Ergebnisphase.",
"help.q.resultsContent": "Was zeigen die Ergebnisse?",
"help.a.resultsContent": "Eine Rangliste mit Durchschnitt, Stimmenanzahl, deiner eigenen Stimme sowie Links und Medien pro Spiel.",
"help.q.openResults": "Wie öffne oder schließe ich Ergebnisse?",
"help.a.openResults": "Admins schalten im Admin-Bereich die Ergebnisse frei. Öffnen bringt alle in die Ergebnisphase; Schließen schickt Spieler zurück zur Abstimmung und entfernt Finalisierungen.",
"help.q.linkDuplicates": "Wie verbinde ich Duplikate?",
"help.a.linkDuplicates": "In der Abstimmungsphase zwei Spiele unter Admin Spiele verknüpfen auswählen. Verknüpfen löscht die Stimmen dieser Gruppe und öffnet betroffene Spieler erneut.",
"help.q.grantJoker": "Wann sollte ich einen Joker vergeben?",
"help.a.grantJoker": "Vergib während der Abstimmung einen Joker, damit ein Spieler einen zusätzlichen Vorschlag machen kann; seine Finalisierung wird zurückgesetzt.",
"help.q.reset": "Reset vs. Werkseinstellung?",
"help.a.reset": "Reset löscht Vorschläge/Stimmen und Phasen, behält aber Spieler. Werkseinstellung entfernt alles inklusive Spieler und setzt die App neu auf.",
} }
}; };