Drei Rueckmeldungen vom 22.08.2026, alle drei berechtigt.
1) "die kisten sollen auch eine spezielle farbe bekommen wenn sie fertig
sind" -- das war inhaltlich der wichtigste Punkt. Vorher sahen
erledigte und gerade laufende Phase fast gleich blau aus. Damit ging
die einzige Aussage verloren, die die Leiste ueberhaupt traegt:
naemlich WO man steht. Jetzt drei klar getrennte Zustaende:
erledigt gruen, ruhig
laeuft grad leuchtendes Blau mit langsamem Puls
kommt noch gedaempft
Die Vorschau erklaert die Logik gleich mit: ihre Leiste wandert von
blau nach gruen, statt nur an- und auszugehen.
Dazu eine Legende in Worten. Farbe allein ist nie Information -- wer
sie nicht unterscheiden kann, liest hier trotzdem, was sie bedeutet.
2) "die zahlen sollen besser im kreis sein" -- vorher ein flacher Kreis
mit Zahl. Jetzt ein doppelter Ring: aussen ein Farbverlauf als Rand,
innen die dunkle Flaeche, dazu ein weicher Schein. Derselbe Kniff wie
bei den Karten (Verlauf ueber border-box); der Kreis wirkt dadurch
plastisch statt aufgemalt. Jede der vier Kacheln hat ihre eigene
Farbe -- vier gleiche Kacheln wirken wie eine Aufzaehlung, vier
unterscheidbare wie ein System.
3) "schrift soll spezieller sein" -- die Kachel-Ueberschrift war so gross
wie der Text darunter und ging unter. Jetzt groesser, in der
Headline-Schrift, enger gesetzt mit leicht negativer Laufweite. Die
Ueberschrift "Gleich geht es los" bekommt einen Farbverlauf.
45/45 Handy-Abnahme, i18n vollstaendig, prefers-reduced-motion beachtet.
Co-Authored-By: Claude Opus 5 <[email protected]>
505 lines
23 KiB
JavaScript
505 lines
23 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. */
|
|
'<div class="po-legende">' +
|
|
'<span><i class="po-l-fertig"></i>' + s(window.WD.t("po_l_fertig")) + "</span>" +
|
|
'<span><i class="po-l-jetzt"></i>' + s(window.WD.t("po_l_jetzt")) + "</span>" +
|
|
'<span><i class="po-l-offen"></i>' + s(window.WD.t("po_l_offen")) + "</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);
|
|
})();
|