Arbeit der parallelen Session gesichert (Profilbilder für Stimmen, Diene-Zugang)
Wie beim vorherigen Mal lag das nur als Arbeitskopie auf dem Server, nicht in git. Unverändert übernommen, bevor darauf aufgebaut wird: - Profilbild-Auswahl für die Stimmen (data-stimmen-avatare.js neu, stimmen.js, i18n-stimmen.js, stimmen.html, verwaltung.html, main.css) - Dritte Zugangs-Kachel für Diene (gate.html, server/gate.js) Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
+43
-2
@@ -9,6 +9,38 @@
|
||||
(function () {
|
||||
const API_BASE_URL = "https://postfach.dogfather-universe.com";
|
||||
|
||||
/* ---------- Profilbild-Auswahl (21.08.2026) ----------
|
||||
Baut die Kacheln aus der gemeinsamen Liste in data-stimmen-avatare.js
|
||||
auf. Bewusst echte <button>-Elemente statt <div>: so ist die Auswahl
|
||||
auch mit der Tastatur bedienbar und Screenreader melden sie als
|
||||
Schaltfläche. "" = bewusst kein Bild (erste Kachel). */
|
||||
let gewaehlterAvatar = "";
|
||||
const wahlEl = document.getElementById("stimmen-avatar-wahl");
|
||||
if (wahlEl) {
|
||||
const liste = window.STIMMEN_AVATARE || [];
|
||||
const kachel = (id, inhalt, titel) =>
|
||||
`<button type="button" class="stimmen-avatar-kachel" data-avatar="${id}" role="radio" aria-checked="false" title="${titel}" aria-label="${titel}">${inhalt}</button>`;
|
||||
|
||||
wahlEl.innerHTML =
|
||||
kachel("", `<span class="stimmen-avatar-keins" aria-hidden="true">✦</span>`, "Kein Bild") +
|
||||
liste.map((a) => kachel(a.id, `<img src="${a.datei}" alt="" loading="lazy" />`, a.name)).join("");
|
||||
|
||||
const markiere = () => {
|
||||
wahlEl.querySelectorAll(".stimmen-avatar-kachel").forEach((b) => {
|
||||
const aktiv = b.dataset.avatar === gewaehlterAvatar;
|
||||
b.classList.toggle("aktiv", aktiv);
|
||||
b.setAttribute("aria-checked", aktiv ? "true" : "false");
|
||||
});
|
||||
};
|
||||
wahlEl.addEventListener("click", (e) => {
|
||||
const btn = e.target.closest(".stimmen-avatar-kachel");
|
||||
if (!btn) return;
|
||||
gewaehlterAvatar = btn.dataset.avatar;
|
||||
markiere();
|
||||
});
|
||||
markiere();
|
||||
}
|
||||
|
||||
const formular = document.getElementById("stimmen-formular");
|
||||
if (formular) {
|
||||
const erfolgEl = document.getElementById("stimmen-erfolg");
|
||||
@@ -28,7 +60,7 @@
|
||||
const res = await fetch(`${API_BASE_URL}/testimonials/submit`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ name, tiktokUsername, nachricht }),
|
||||
body: JSON.stringify({ name, tiktokUsername, nachricht, avatar: gewaehlterAvatar || null }),
|
||||
});
|
||||
const data = await res.json();
|
||||
if (!data.ok) {
|
||||
@@ -74,7 +106,16 @@
|
||||
(t) => `<div class="stimmen-karte">
|
||||
<p class="stimmen-karte-text">${escapeHtml(t.nachricht)}</p>
|
||||
<div class="stimmen-karte-fuss">
|
||||
<span class="stimmen-karte-avatar" aria-hidden="true">${escapeHtml(erstesZeichen(t.name))}</span>
|
||||
${
|
||||
/* Gewähltes Bild, sonst wie bisher der Anfangsbuchstabe.
|
||||
escapeHtml auch auf der Bildadresse: sie stammt zwar aus
|
||||
unserer eigenen Liste bzw. vom Server, aber sie landet
|
||||
hier in einem HTML-Attribut — da gehört sie grundsätzlich
|
||||
entschärft, unabhängig von der Herkunft. */
|
||||
(window.stimmeAvatarBild && window.stimmeAvatarBild(t.avatar))
|
||||
? `<span class="stimmen-karte-avatar stimmen-karte-avatar-bild" aria-hidden="true"><img src="${escapeHtml(window.stimmeAvatarBild(t.avatar))}" alt="" loading="lazy" /></span>`
|
||||
: `<span class="stimmen-karte-avatar" aria-hidden="true">${escapeHtml(erstesZeichen(t.name))}</span>`
|
||||
}
|
||||
<span class="stimmen-karte-namen">
|
||||
<span class="stimmen-karte-name">${escapeHtml(t.name)}</span>
|
||||
${t.tiktokUsername ? `<span class="stimmen-karte-tiktok">@${escapeHtml(t.tiktokUsername)}</span>` : ""}
|
||||
|
||||
Reference in New Issue
Block a user