Files
dogfather-universe/assets/js/wd-portal.js
T
DogFatherGitandClaude Opus 5 2539fdea8d Zahlenkreise repariert und vier Prozessfarben durchgaengig
"wieso sehen die zahlen immer noch so scheisse aus?" -- zu Recht, und die
Ursache war nicht Geschmack, sondern ein Fehler. Nachgemessen sass die
Ziffer 15px NEBEN der Kreismitte.

Ursache: ".po-leer-punkt span" (fuer den Beschreibungstext) trifft auch
den Zahlen-Span und ist spezifischer (Klasse + Element) als
".po-leer-nr" (nur Klasse). Sie erzwang display:block, 14,4px Schrift und
23px Zeilenhoehe -- exakt die gemessenen Werte. Meine eigene
"line-height: 1"-Regel kam gar nicht zum Zug.

Das ist derselbe Spezifitaets-Fehler wie zuvor beim Hauptknopf (blaue
Schrift auf blauem Grund) und bei den Namen auf der Zugangswand. Dreimal
dieselbe Falle, deshalb steht die Begruendung jetzt ausfuehrlich im Code.

Behoben ueber :not(.po-leer-nr) an beiden Textregeln. Nachgemessen:
  Versatz waagerecht  -15px -> 0px
  Versatz senkrecht    -8px -> -1,3px
  display             block -> grid
  Schrift/Zeile   14,4/23px -> 18,4/18,4px

Zweite Rueckmeldung: "es soll 4 farben geben wie 4 kategorien zum
prozess". Sehr gute Idee -- sie macht das System erst schluessig. Die
sieben Phasen sind in Wahrheit vier Abschnitte, und die vier
Merkmalskacheln trugen ohnehin schon vier Farben. Jetzt bedeuten diese
Farben ueberall dasselbe:

  1  Blau   Start        Briefing, Angebot
  2  Lila   Gestaltung   Design
  3  Gruen  Umsetzung    Entwicklung, Tests
  4  Gold   Abschluss    Abnahme, Uebergabe

Die Phasenleiste faerbt erledigte Abschnitte in IHRER Farbe statt
pauschal gruen -- man sieht dadurch, wie weit man ist, nicht nur DASS
etwas fertig ist. Die Vorschau laeuft in denselben Farben durch. Die
Legende nennt die vier Abschnitte beim Namen, damit Farbe nicht geraten
werden muss: Farbe allein ist nie Information.

45/45 Handy-Abnahme, i18n vollstaendig.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-22 22:26:04 +02:00

509 lines
24 KiB
JavaScript

/* =====================================================================
wd-portal.js — Kundenportal, Oberfläche (Masterplan S.12)
Vier Ansichten in einer Seite: Anmeldung, Passwort festlegen,
Übersicht, Projektdetail.
Wichtig zum Verständnis: Diese Datei stellt nur DAR. Sie entscheidet
nichts darüber, wer was sehen darf — das macht ausschließlich der
Server (routes/webdesign-portal.js), weil dieser Code hier im Browser
des Kunden läuft und dort beliebig veränderbar ist. Was hier ankommt,
ist bereits gefiltert.
===================================================================== */
(function () {
"use strict";
var TOKEN = "wd-portal-token";
var token = null;
var s = window.WD.sicher;
var meldung = document.getElementById("po-meldung");
/* Sitzungsspeicher, nicht dauerhaft: derselbe Grundsatz wie überall in
diesem Bereich — beim Schließen ist der Zugang weg. */
function tokenLesen() { try { return sessionStorage.getItem(TOKEN); } catch (e) { return null; } }
function tokenSchreiben(t) {
try { if (t) sessionStorage.setItem(TOKEN, t); else sessionStorage.removeItem(TOKEN); } catch (e) { /* egal */ }
}
function api(pfad, optionen) {
var opt = optionen || {};
opt.headers = Object.assign({ Authorization: "Bearer " + token }, opt.headers || {});
return window.WD.api(pfad, opt);
}
function zeige(welche) {
["po-anmeldung", "po-setzen", "po-uebersicht", "po-detail"].forEach(function (id) {
document.getElementById(id).hidden = id !== welche;
});
}
/* Sitzung abgelaufen: zurück zur Anmeldung mit klarer Ansage. Eine leere
Seite würde aussehen wie "du hast keine Projekte" — eine stille
Falschaussage. */
function behandleFehler(fehler) {
if (fehler && fehler.status === 401) {
token = null; tokenSchreiben(null);
zeige("po-anmeldung");
window.WD.meldung(meldung, window.WD.t("po_abgelaufen"), "fehler");
return true;
}
return false;
}
/* ---------------- Anmeldung ---------------- */
document.getElementById("po-login-form").addEventListener("submit", async function (e) {
e.preventDefault();
var feld = document.getElementById("po-feld-passwort");
var knopf = e.target.querySelector("button");
feld.classList.remove("wd-feld--fehler");
window.WD.meldungAus(meldung);
var email = document.getElementById("po-email").value.trim();
var passwort = document.getElementById("po-passwort").value;
if (!email || !passwort) {
feld.classList.add("wd-feld--fehler");
feld.querySelector(".wd-feld-fehler").textContent = window.WD.t("po_login_leer");
return;
}
window.WD.knopfSperren(knopf, true, window.WD.t("po_anmelden_laeuft"));
try {
var antwort = await window.WD.api("/webdesign/portal/anmelden", {
method: "POST", body: { email: email, passwort: passwort },
});
token = antwort.token;
tokenSchreiben(token);
/* Sprache des Kunden übernehmen, wenn er eine hinterlegt hat —
so kommt er in seiner Sprache an, ohne umschalten zu müssen. */
if (antwort.kunde && antwort.kunde.sprache) window.WD.setSprache(antwort.kunde.sprache);
ladeUebersicht();
} catch (fehler) {
feld.classList.add("wd-feld--fehler");
feld.querySelector(".wd-feld-fehler").textContent = fehler.message || window.WD.t("po_fehler_login");
document.getElementById("po-passwort").select();
} finally {
window.WD.knopfSperren(knopf, false);
}
});
document.getElementById("po-abmelden").addEventListener("click", async function () {
try { await api("/webdesign/portal/abmelden", { method: "POST", body: {} }); } catch (e) { /* egal */ }
token = null; tokenSchreiben(null);
location.href = "/webdesign/portal.html";
});
/* ---------------- Passwort setzen (Einladungslink) ---------------- */
var einladungsToken = new URLSearchParams(location.search).get("einladung");
document.getElementById("po-setz-form").addEventListener("submit", async function (e) {
e.preventDefault();
var feld = document.getElementById("po-feld-neu");
var knopf = e.target.querySelector("button");
feld.classList.remove("wd-feld--fehler");
var passwort = document.getElementById("po-neu").value;
window.WD.knopfSperren(knopf, true, window.WD.t("po_anmelden_laeuft"));
try {
await window.WD.api("/webdesign/portal/passwort", {
method: "POST", body: { token: einladungsToken, passwort: passwort },
});
zeige("po-anmeldung");
window.WD.meldung(meldung, window.WD.t("po_setz_fertig"), "ok");
/* Adresszeile säubern: der Einladungslink ist verbraucht und hat in
der Chronik nichts mehr verloren. */
history.replaceState(null, "", "/webdesign/portal.html");
} catch (fehler) {
feld.classList.add("wd-feld--fehler");
feld.querySelector(".wd-feld-fehler").textContent = fehler.message;
} finally {
window.WD.knopfSperren(knopf, false);
}
});
/* ---------------- Übersicht ---------------- */
var PHASEN = ["briefing", "angebot", "design", "entwicklung", "tests", "abnahme", "uebergeben"];
function statusText(st) { return window.WD.t("po_st_" + st); }
function phasenLeiste(status) {
var jetzt = PHASEN.indexOf(status);
/* Pausiert oder abgebrochen: keine Leiste. Ein Fortschrittsbalken bei
einem gestoppten Projekt wäre irreführend. */
if (jetzt < 0) return "";
return '<div class="po-phasen" aria-hidden="true">' +
PHASEN.map(function (p, i) {
return '<span class="po-phase' + (i < jetzt ? " po-erledigt" : i === jetzt ? " po-jetzt" : "") + '"></span>';
}).join("") + "</div>" +
'<div class="po-phasen-text">' + s(statusText(status)) + " · " +
(jetzt + 1) + "/" + PHASEN.length + "</div>";
}
function wartetMarke(wartet) {
if (wartet !== "kunde" && wartet !== "dogfather") return "";
return '<span class="po-wartet po-wartet--' + wartet + '">' +
s(window.WD.t(wartet === "kunde" ? "po_wartet_kunde" : "po_wartet_dogfather")) + "</span>";
}
async function ladeUebersicht() {
zeige("po-uebersicht");
var ziel = document.getElementById("po-inhalt");
ziel.innerHTML = '<div class="po-laedt">' + s(window.WD.t("laedt")) + "</div>";
try {
var d = await api("/webdesign/portal/uebersicht");
document.getElementById("po-name").textContent = d.kunde.name || "";
window.WD.meldungAus(meldung);
var html = "";
/* Offene Zahlungen ganz oben — das ist das Einzige, was den Kunden
zum Handeln auffordert. Gold, weil es wirklich wichtig ist. */
if (d.offeneZahlungen.length) {
html += '<div class="wd-hinweis" style="margin-bottom:1.2rem">' +
'<p class="wd-hinweis-titel">' + s(window.WD.t("po_offene_zahlungen")) + "</p>" +
d.offeneZahlungen.map(function (z) {
return "<p><strong>" + s(z.betrag_text) + "</strong> · " + s(z.zweck_text) + "</p>";
}).join("") + "</div>";
}
html += "<h2>" + s(window.WD.t("po_projekte")) + "</h2>";
if (!d.projekte.length) {
/* Ein leerer Bereich mit einem Satz ist eine verpasste Gelegenheit.
Wer hier zum ersten Mal landet, hat gerade sein Passwort gesetzt
und weiss noch nicht, was ihn erwartet. Statt "hier ist nichts"
zeigt der leere Zustand deshalb, WAS gleich hier stehen wird --
und was als Naechstes passiert. Das nimmt die Unsicherheit,
statt sie zu erzeugen. */
html += '<div class="wd-karte po-leer">' +
'<div class="po-leer-symbol" aria-hidden="true">' +
'<img src="/assets/img/logo-dogfather-transparent.png" alt="" width="472" height="476" />' +
"</div>" +
"<h3>" + s(window.WD.t("po_leer_h3")) + "</h3>" +
'<p class="wd-leise">' + s(window.WD.t("po_keine_projekte")) + "</p>" +
/* ZEIGEN statt erzaehlen: eine echte, als Vorschau erkennbare
Projektkarte mit laufender Phasenleiste. Man versteht in zwei
Sekunden, was drei Saetze nicht erklaeren. */
'<div class="po-vorschau">' +
'<span class="po-vorschau-etikett">' + s(window.WD.t("po_vorschau")) + "</span>" +
'<span class="po-vorschau-nr">P-0000-0000</span>' +
"<h4>" + s(window.WD.t("po_vorschau_titel")) + "</h4>" +
'<div class="po-vorschau-phasen" aria-hidden="true">' +
"<span></span><span></span><span></span><span></span><span></span><span></span><span></span>" +
"</div>" +
'<div class="po-vorschau-zeile">' +
'<span class="po-vorschau-marke">' + s(window.WD.t("po_wartet_dogfather")) + "</span>" +
"<span>" + s(window.WD.t("po_st_design")) + " · 3/7</span>" +
"</div>" +
'<p class="po-vorschau-schritt"><strong>' + s(window.WD.t("po_naechster_schritt")) +
":</strong> " + s(window.WD.t("po_vorschau_schritt")) + "</p>" +
/* Legende: Farbe allein ist nie Information. Wer sie nicht
unterscheiden kann, liest hier trotzdem, was sie bedeutet. */
/* Vier Abschnitte statt drei Zustaende: Ob eine Phase
erledigt ist, sieht man an der Position. Wertvoller ist,
WAS gerade passiert. */
'<div class="po-legende">' +
'<span><i class="po-l-1"></i>' + s(window.WD.t("po_k1")) + "</span>" +
'<span><i class="po-l-2"></i>' + s(window.WD.t("po_k2")) + "</span>" +
'<span><i class="po-l-3"></i>' + s(window.WD.t("po_k3")) + "</span>" +
'<span><i class="po-l-4"></i>' + s(window.WD.t("po_k4")) + "</span>" +
"</div>" +
"</div>" +
'<div class="po-leer-liste">' +
[["po_leer_1", "po_leer_1t"], ["po_leer_2", "po_leer_2t"],
["po_leer_3", "po_leer_3t"], ["po_leer_4", "po_leer_4t"]]
.map(function (paar, i) {
return '<div class="po-leer-punkt">' +
'<span class="po-leer-nr">' + (i + 1) + "</span>" +
"<div><strong>" + s(window.WD.t(paar[0])) + "</strong>" +
"<span>" + s(window.WD.t(paar[1])) + "</span></div></div>";
}).join("") +
"</div>" +
'<p class="wd-klein wd-leise po-leer-fuss">' + s(window.WD.t("po_leer_fuss")) + "</p>" +
"</div>";
} else {
html += d.projekte.map(function (p) {
return '<button type="button" class="wd-karte po-projekt" data-projekt="' + s(p.id) + '">' +
'<span class="po-projekt-kopf">' +
'<span class="po-projekt-nr">' + s(p.nummer) + "</span>" +
"<h3>" + s(p.titel) + "</h3>" +
wartetMarke(p.wartet_auf) +
"</span>" +
phasenLeiste(p.status) +
(p.naechster_schritt
? '<p class="wd-klein" style="margin:.7rem 0 .3rem"><strong>' +
s(window.WD.t("po_naechster_schritt")) + ":</strong> " + s(p.naechster_schritt) + "</p>"
: "") +
(p.richttermin
? '<p class="wd-klein wd-leise" style="margin:0">' + s(window.WD.t("po_richttermin")) +
": " + window.WD.datum(p.richttermin) + "</p>"
: "") +
(p.offene_aenderungen || p.ungelesene_nachrichten
? '<p class="wd-klein" style="margin:.5rem 0 0;color:var(--wd-blau)">' +
(p.ungelesene_nachrichten ? p.ungelesene_nachrichten + " " + s(window.WD.t("po_nachrichten")) : "") +
(p.ungelesene_nachrichten && p.offene_aenderungen ? " · " : "") +
(p.offene_aenderungen ? p.offene_aenderungen + " " + s(window.WD.t("po_ideen")) : "") +
"</p>"
: "") +
"</button>";
}).join("");
}
if (d.abos.length) {
html += '<h2 style="margin-top:1.8rem">' + s(window.WD.t("po_betreuung")) + "</h2>" +
'<div class="wd-karte"><ul class="po-liste">' +
d.abos.map(function (a) {
return '<li class="po-eintrag"><span class="po-eintrag-haupt">' +
'<span class="po-eintrag-name">' + s(a.paket) + "</span>" +
'<span class="po-eintrag-neben">' + s(a.preis_text) +
(a.naechste_zahlung ? " · " + s(window.WD.t("po_naechste_zahlung")) + ": " + window.WD.datum(a.naechste_zahlung) : "") +
"</span></span></li>";
}).join("") + "</ul></div>";
}
ziel.innerHTML = html;
ziel.querySelectorAll("[data-projekt]").forEach(function (b) {
b.addEventListener("click", function () { ladeProjekt(b.dataset.projekt); });
});
} catch (fehler) {
if (behandleFehler(fehler)) return;
ziel.innerHTML = "";
window.WD.meldung(meldung, fehler.message, "fehler");
}
}
/* ---------------- Projektdetail ---------------- */
var aktuellesProjekt = null;
document.getElementById("po-zurueck").addEventListener("click", function () {
aktuellesProjekt = null;
ladeUebersicht();
});
async function ladeProjekt(id) {
aktuellesProjekt = id;
zeige("po-detail");
var ziel = document.getElementById("po-detail-inhalt");
ziel.innerHTML = '<div class="po-laedt">' + s(window.WD.t("laedt")) + "</div>";
try {
var d = await api("/webdesign/portal/projekt/" + encodeURIComponent(id));
var p = d.projekt;
var html = "";
html += '<div class="wd-karte" style="margin-bottom:1.2rem">' +
'<span class="po-projekt-nr">' + s(p.nummer) + "</span>" +
"<h2 style=\"margin:.2rem 0 .6rem\">" + s(p.titel) + "</h2>" +
wartetMarke(p.wartet_auf) +
phasenLeiste(p.status) +
(p.naechster_schritt
? '<p style="margin:.8rem 0 0"><strong>' + s(window.WD.t("po_naechster_schritt")) +
":</strong> " + s(p.naechster_schritt) + "</p>"
: "") +
"</div>";
/* --- Zahlungen --- */
html += '<div class="wd-karte" style="margin-bottom:1.2rem"><h3>' + s(window.WD.t("po_zahlungen")) + "</h3>";
if (!d.zahlungen.length) {
html += '<p class="wd-leise wd-klein">' + s(window.WD.t("po_keine_zahlungen")) + "</p>";
} else {
html += '<ul class="po-liste">' + d.zahlungen.map(function (z) {
var offen = z.status === "offen" || z.status === "freigegeben";
return '<li class="po-eintrag"><span class="po-eintrag-haupt">' +
'<span class="po-eintrag-name">' + s(z.betrag_text) + "</span>" +
'<span class="po-eintrag-neben">' + s(z.zweck_text) + "</span></span>" +
'<span class="wd-tag' + (z.status === "bezahlt" ? " wd-tag--ok" : offen ? " wd-tag--gold" : "") + '">' +
s(window.WD.t("po_za_" + (z.status === "fehlge" ? "fehlge" : z.status))) + "</span>" +
(offen ? '<button type="button" class="wd-btn wd-btn--haupt wd-btn--klein" data-zahlung="' +
s(z.id) + '">' + s(window.WD.t("po_jetzt_bezahlen")) + "</button>" : "") +
"</li>";
}).join("") + "</ul>";
}
html += "</div>";
/* --- Dateien --- */
html += '<div class="wd-karte" style="margin-bottom:1.2rem"><h3>' + s(window.WD.t("po_dateien")) + "</h3>";
if (!d.dateien.length) {
html += '<p class="wd-leise wd-klein">' + s(window.WD.t("po_keine_dateien")) + "</p>";
} else {
html += '<ul class="po-liste">' + d.dateien.map(function (f) {
return '<li class="po-eintrag"><span class="po-eintrag-haupt">' +
'<span class="po-eintrag-name">' + s(f.anzeigename) + "</span>" +
'<span class="po-eintrag-neben">' + window.WD.datum(f.erstellt_am) +
(f.version > 1 ? " · " + s(window.WD.t("po_version")) + " " + f.version : "") +
"</span></span></li>";
}).join("") + "</ul>";
}
html += "</div>";
/* --- Ideen & Änderungswünsche --- */
html += '<div class="wd-karte" style="margin-bottom:1.2rem"><h3>' + s(window.WD.t("po_ideen")) + "</h3>" +
'<p class="wd-klein wd-leise">' + s(window.WD.t("po_ideen_text")) + "</p>";
if (d.aenderungen.length) {
html += d.aenderungen.map(function (a) {
var entscheidbar = a.status === "angeboten";
return '<div class="po-idee' + (entscheidbar ? " po-idee--offen" : "") + '">' +
'<div class="po-idee-kopf"><h4>' + s(a.titel) + "</h4>" +
'<span class="wd-tag' + (entscheidbar ? " wd-tag--gold" : a.status === "angenommen" ? " wd-tag--ok" : "") + '">' +
s(window.WD.t("po_ae_" + a.status)) + "</span></div>" +
'<p class="wd-klein" style="margin:0 0 .4rem">' + s(a.beschreibung) + "</p>" +
(a.aufwand_text ? '<p class="wd-klein wd-leise" style="margin:0">' + s(a.aufwand_text) + "</p>" : "") +
(a.zusatzpreis_text
? '<p class="wd-klein" style="margin:.3rem 0 0"><strong>' + s(window.WD.t("po_zusatzpreis")) +
": " + s(a.zusatzpreis_text) + "</strong>" +
(a.zusatzdauer_text ? " · " + s(window.WD.t("po_zusatzdauer")) + ": " + s(a.zusatzdauer_text) : "") +
"</p>"
: "") +
(entscheidbar
? '<div class="wd-btn-reihe" style="margin-top:.8rem">' +
'<button type="button" class="wd-btn wd-btn--haupt wd-btn--klein" data-ja="' + s(a.id) + '">' +
s(window.WD.t("po_annehmen")) + "</button>" +
'<button type="button" class="wd-btn wd-btn--still wd-btn--klein" data-nein="' + s(a.id) + '">' +
s(window.WD.t("po_ablehnen")) + "</button></div>"
: "") +
"</div>";
}).join("");
} else {
html += '<p class="wd-leise wd-klein">' + s(window.WD.t("po_keine_ideen")) + "</p>";
}
html += '<div class="wd-feld" style="margin:1rem 0 .6rem">' +
"<label for=\"po-idee-titel\">" + s(window.WD.t("po_idee_titel")) + "</label>" +
'<input type="text" id="po-idee-titel" maxlength="160" />' +
"</div>" +
'<div class="wd-feld" style="margin-bottom:.6rem" id="po-feld-idee">' +
"<label for=\"po-idee-text\">" + s(window.WD.t("po_idee_text")) + "</label>" +
'<textarea id="po-idee-text" rows="3"></textarea>' +
'<p class="wd-feld-fehler"></p></div>' +
'<button type="button" class="wd-btn wd-btn--zweit wd-btn--klein" id="po-idee-senden">' +
s(window.WD.t("po_idee_senden")) + "</button></div>";
/* --- Nachrichten --- */
html += '<div class="wd-karte"><h3>' + s(window.WD.t("po_nachrichten")) + "</h3>";
if (!d.nachrichten.length) {
html += '<p class="wd-leise wd-klein">' + s(window.WD.t("po_keine_nachrichten")) + "</p>";
} else {
html += d.nachrichten.map(function (n) {
var vonMir = n.autor === "kunde";
return '<div class="po-nachricht po-nachricht--' + (vonMir ? "ich" : "du") + '">' +
'<div class="po-nachricht-kopf">' + (vonMir ? "" : "Dogfather · ") +
window.WD.datum(n.erstellt_am) + "</div><p>" + s(n.text) + "</p></div>";
}).join("");
}
html += '<div class="wd-feld" style="margin:1rem 0 .6rem">' +
'<textarea id="po-nachricht-text" rows="2" placeholder="' +
s(window.WD.t("po_nachricht_platzhalter")) + '"></textarea></div>' +
'<button type="button" class="wd-btn wd-btn--zweit wd-btn--klein" id="po-nachricht-senden">' +
s(window.WD.t("po_senden")) + "</button></div>";
ziel.innerHTML = html;
/* --- Verknüpfungen --- */
ziel.querySelectorAll("[data-ja], [data-nein]").forEach(function (b) {
b.addEventListener("click", async function () {
var annehmen = !!b.dataset.ja;
var aeId = b.dataset.ja || b.dataset.nein;
/* Nur beim Annehmen nachfragen — das erzeugt eine Zahlungspflicht.
Ablehnen ist folgenlos und braucht keine Hürde. */
if (annehmen && !confirm(window.WD.t("po_annehmen_frage"))) return;
window.WD.knopfSperren(b, true, "…");
try {
await api("/webdesign/portal/aenderung/" + encodeURIComponent(aeId) + "/entscheiden",
{ method: "POST", body: { annehmen: annehmen } });
ladeProjekt(id);
} catch (f) {
if (behandleFehler(f)) return;
window.WD.knopfSperren(b, false);
window.WD.meldung(meldung, f.message, "fehler");
}
});
});
ziel.querySelector("#po-idee-senden").addEventListener("click", async function (e) {
var titel = ziel.querySelector("#po-idee-titel").value.trim();
var beschreibung = ziel.querySelector("#po-idee-text").value.trim();
var feld = ziel.querySelector("#po-feld-idee");
feld.classList.remove("wd-feld--fehler");
if (!titel || !beschreibung) {
feld.classList.add("wd-feld--fehler");
feld.querySelector(".wd-feld-fehler").textContent = window.WD.t("po_idee_leer");
return;
}
window.WD.knopfSperren(e.target, true, "…");
try {
await api("/webdesign/portal/aenderung",
{ method: "POST", body: { projektId: id, titel: titel, beschreibung: beschreibung } });
ladeProjekt(id);
} catch (f) {
if (behandleFehler(f)) return;
window.WD.knopfSperren(e.target, false);
window.WD.meldung(meldung, f.message, "fehler");
}
});
ziel.querySelector("#po-nachricht-senden").addEventListener("click", async function (e) {
var feld = ziel.querySelector("#po-nachricht-text");
var inhalt = feld.value.trim();
if (!inhalt) { feld.focus(); return; }
window.WD.knopfSperren(e.target, true, "…");
try {
await api("/webdesign/portal/nachricht",
{ method: "POST", body: { projektId: id, text: inhalt } });
ladeProjekt(id);
} catch (f) {
if (behandleFehler(f)) return;
window.WD.knopfSperren(e.target, false);
window.WD.meldung(meldung, f.message, "fehler");
}
});
/* Zahlungsknöpfe: die eigentliche PayPal-Anbindung folgt. Bis dahin
ein ehrlicher Hinweis statt eines Knopfes, der nichts tut. */
ziel.querySelectorAll("[data-zahlung]").forEach(function (b) {
b.addEventListener("click", function () {
window.WD.meldung(meldung,
"Die Zahlung über PayPal wird gerade eingerichtet. Bitte melde dich kurz bei Dogfather.", "fehler");
});
});
} catch (fehler) {
if (behandleFehler(fehler)) return;
ziel.innerHTML = "";
window.WD.meldung(meldung, fehler.message, "fehler");
}
}
/* ---------------- Start ---------------- */
async function start() {
/* Einladungslink hat Vorrang: wer gerade sein Passwort setzen soll,
will nicht zuerst eine Anmeldemaske sehen. */
if (einladungsToken) {
try {
var info = await window.WD.api("/webdesign/portal/einladung?token=" + encodeURIComponent(einladungsToken));
document.getElementById("po-setz-fuer").textContent = info.email || "";
zeige("po-setzen");
return;
} catch (fehler) {
zeige("po-anmeldung");
window.WD.meldung(meldung, fehler.message, "fehler");
return;
}
}
token = tokenLesen();
if (!token) { zeige("po-anmeldung"); return; }
ladeUebersicht();
}
/* Nach einem Sprachwechsel die per JS gebauten Teile neu zeichnen. */
document.addEventListener("wd-sprache-geaendert", function () {
if (!token) return;
if (aktuellesProjekt) ladeProjekt(aktuellesProjekt);
else if (!document.getElementById("po-uebersicht").hidden) ladeUebersicht();
});
if (window.WD) start();
else document.addEventListener("wd-bereit", start);
})();