Masterplan S.10 "Projektanfrage ohne Informationsverlust" umgesetzt.
End-to-End gegen die echte Datenbank belegt: Anfrage A-2608-0002 liegt drin.
38/38 Browsertests, 45/45 Handy-Abnahme.
Aufbau: 4 Schritte + Zusammenfassung + Dankeseite.
- Zwischenspeicherung nach jedem Tastendruck. Geschlossener Tab, leerer
Akku oder versehentliches Zurueck kosten keine Arbeit. Entwuerfe
verfallen nach 30 Tagen -- ein halbes Jahr alter Entwurf verwirrt mehr
als er hilft.
- Folgefragen erscheinen nur passend zur Auswahl und werden beim
Zurueckwechseln NICHT mitgeschickt, sonst landen alte Shop-Antworten in
einer Onepager-Anfrage.
- Die Zusammenfassung wird aus den Feldern gelesen, nicht aus einem
nebenher gepflegten Objekt -- sie kann dadurch nie etwas anderes
behaupten als das, was tatsaechlich abgeschickt wird.
- Telefon wird zur Pflicht, sobald "Per Telefon" gewaehlt ist. Ein Wunsch,
der nicht erfuellbar ist, ist schlimmer als eine Pflichtangabe.
Drei Fehler, die erst der Test gefunden hat:
1. Der "Zurueck"-Knopf stand auch auf Schritt 1 da. Ursache: das
hidden-Attribut wird nur ueber "display:none" umgesetzt und verliert
gegen jedes eigene display -- und Knoepfe sind inline-flex. Zentral
behoben ueber ".wd [hidden] { display:none !important }".
2. Nach erfolgreichem Absenden legte zeigeSeite() den geloeschten Entwurf
sofort wieder an. Beim naechsten Besuch haette eine bereits gesendete
Anfrage erneut im Formular gestanden.
3. Der Server meldet bei Spamverdacht bewusst Erfolg ohne Nummer. Ein
echter Mensch, der zufaellig sehr schnell war, haette eine Dankeseite
mit "—" gesehen und auf eine Antwort gewartet, die nie kommt. Jetzt
ein ehrlicher Hinweis mit zweitem Weg, und der Entwurf bleibt erhalten.
Pruefskripte geschaerft:
- i18n-Pruefung liest jetzt auch assets/js/wd-<seite>.js, sonst meldet sie
reihenweise "unbenutzt" fuer Schluessel, die aus dem Seitenskript kommen.
- Handy-Abnahme ueberspringt absichtlich unsichtbare Eingabefelder
(Auswahlkacheln, Honigtopf). Fehlalarme sind das Ende jeder Pruefung,
weil man sie irgendwann pauschal ignoriert.
Co-Authored-By: Claude Opus 5 <[email protected]>
541 lines
22 KiB
JavaScript
541 lines
22 KiB
JavaScript
/* =====================================================================
|
|
wd-anfrage.js — Steuerung des mehrstufigen Anfrageformulars
|
|
(Masterplan S.10 "Projektanfrage ohne Informationsverlust")
|
|
|
|
Aufbau: 4 Eingabeschritte + Zusammenfassung + Dankeseite.
|
|
|
|
Leitgedanken:
|
|
- Nichts geht verloren. Jede Eingabe landet sofort im Browserspeicher;
|
|
ein geschlossener Tab, ein leerer Akku oder ein versehentliches
|
|
Zurück kostet keine Arbeit.
|
|
- Geprüft wird beim Weitergehen, nicht beim Tippen. Wer noch mitten im
|
|
Wort ist, soll nicht angemeckert werden.
|
|
- Die Zusammenfassung zeigt, was WIRKLICH abgeschickt wird — aus den
|
|
Feldern gelesen, nicht aus dem, was das Skript zu wissen glaubt.
|
|
===================================================================== */
|
|
(function () {
|
|
"use strict";
|
|
|
|
var SPEICHER = "wd-anfrage-entwurf";
|
|
var LETZTE_SEITE_EINGABE = 4; // danach kommt die Zusammenfassung
|
|
var SEITE_ZUSAMMENFASSUNG = 5;
|
|
var SEITE_DANKE = 6;
|
|
|
|
var form, seiten, zaehler, marken, meldung, knopfLeiste, btnZurueck, btnWeiter;
|
|
var aktuelleSeite = 1;
|
|
var geoeffnetAm = Date.now();
|
|
|
|
/* ------------------------------------------------------------------
|
|
Werte einsammeln. Bewusst direkt aus dem Formular statt aus einem
|
|
nebenher gepflegten Objekt: so kann die Anzeige nie etwas anderes
|
|
behaupten als das, was tatsächlich in den Feldern steht.
|
|
------------------------------------------------------------------ */
|
|
function werte() {
|
|
var d = new FormData(form);
|
|
var funktionen = d.getAll("funktionen");
|
|
|
|
/* Kategoriespezifische Felder tragen das Präfix "d_" und wandern
|
|
gesammelt in ein Unterobjekt. Neue Folgefragen brauchen dadurch
|
|
weder hier noch in der Datenbank eine Änderung. */
|
|
var details = {};
|
|
form.querySelectorAll("[name^='d_']").forEach(function (el) {
|
|
/* Nur Felder mitnehmen, die gerade sichtbar sind — sonst schickt
|
|
jemand, der von "Shop" auf "Onepager" zurückwechselt, immer noch
|
|
seine alten Shop-Antworten mit. */
|
|
if (el.closest("[hidden]")) return;
|
|
var wert = (el.value || "").trim();
|
|
if (wert) details[el.name.slice(2)] = wert;
|
|
});
|
|
|
|
var vorhanden = {};
|
|
form.querySelectorAll("[name^='v_']").forEach(function (el) {
|
|
if (el.checked) vorhanden[el.name.slice(2)] = el.value;
|
|
});
|
|
|
|
return {
|
|
paket: d.get("paket") || "",
|
|
ziel: (d.get("ziel") || "").trim(),
|
|
zielgruppe: (d.get("zielgruppe") || "").trim(),
|
|
seiten: (d.get("seiten") || "").trim(),
|
|
funktionen: funktionen,
|
|
details: details,
|
|
hatTexte: vorhanden.texte || "",
|
|
hatBilder: vorhanden.bilder || "",
|
|
hatLogo: vorhanden.logo || "",
|
|
hatDomain: vorhanden.domain || "",
|
|
budget: d.get("budget") || "",
|
|
termin: (d.get("termin") || "").trim(),
|
|
name: (d.get("name") || "").trim(),
|
|
email: (d.get("email") || "").trim(),
|
|
telefon: (d.get("telefon") || "").trim(),
|
|
kontaktweg: d.get("kontaktweg") || "email",
|
|
bemerkung: (d.get("bemerkung") || "").trim(),
|
|
datenschutz: !!d.get("datenschutz"),
|
|
website: (d.get("website") || "").trim(), // Honigtopf
|
|
};
|
|
}
|
|
|
|
/* ------------------------------------------------------------------
|
|
Zwischenspeicherung (Masterplan S.10: "Zwischenspeicherung verhindert
|
|
Datenverlust")
|
|
------------------------------------------------------------------ */
|
|
function speichern() {
|
|
try {
|
|
var w = werte();
|
|
delete w.website; // Honigtopf nie speichern
|
|
sessionStorageSchreiben(JSON.stringify({ stand: w, seite: aktuelleSeite, zeit: Date.now() }));
|
|
} catch (e) { /* Speicher voll oder gesperrt — kein Grund abzubrechen */ }
|
|
}
|
|
|
|
function sessionStorageSchreiben(text) {
|
|
/* localStorage, NICHT sessionStorage: der Entwurf soll auch morgen
|
|
noch da sein. Das ist der ganze Zweck. */
|
|
try { localStorage.setItem(SPEICHER, text); } catch (e) { /* egal */ }
|
|
}
|
|
|
|
function gespeichertesLesen() {
|
|
try {
|
|
var roh = localStorage.getItem(SPEICHER);
|
|
if (!roh) return null;
|
|
var daten = JSON.parse(roh);
|
|
/* Entwürfe verfallen nach 30 Tagen. Ein halbes Jahr alter Entwurf
|
|
verwirrt mehr, als er hilft. */
|
|
if (!daten.zeit || Date.now() - daten.zeit > 30 * 24 * 3600 * 1000) {
|
|
localStorage.removeItem(SPEICHER);
|
|
return null;
|
|
}
|
|
return daten;
|
|
} catch (e) { return null; }
|
|
}
|
|
|
|
function entwurfLoeschen() {
|
|
try { localStorage.removeItem(SPEICHER); } catch (e) { /* egal */ }
|
|
}
|
|
|
|
function wiederherstellen(daten) {
|
|
var w = daten.stand || {};
|
|
function setz(name, wert) {
|
|
var el = form.querySelector("[name='" + name + "']");
|
|
if (el && typeof wert === "string") el.value = wert;
|
|
}
|
|
setz("ziel", w.ziel); setz("zielgruppe", w.zielgruppe); setz("seiten", w.seiten);
|
|
setz("termin", w.termin); setz("name", w.name); setz("email", w.email);
|
|
setz("telefon", w.telefon); setz("bemerkung", w.bemerkung);
|
|
var budget = form.querySelector("[name='budget']");
|
|
if (budget && w.budget) budget.value = w.budget;
|
|
|
|
if (w.paket) {
|
|
var p = form.querySelector("[name='paket'][value='" + w.paket + "']");
|
|
if (p) p.checked = true;
|
|
}
|
|
if (w.kontaktweg) {
|
|
var k = form.querySelector("[name='kontaktweg'][value='" + w.kontaktweg + "']");
|
|
if (k) k.checked = true;
|
|
}
|
|
(w.funktionen || []).forEach(function (f) {
|
|
var el = form.querySelector("[name='funktionen'][value='" + f + "']");
|
|
if (el) el.checked = true;
|
|
});
|
|
Object.keys(w.details || {}).forEach(function (k) {
|
|
var el = form.querySelector("[name='d_" + k + "']");
|
|
if (el) el.value = w.details[k];
|
|
});
|
|
[["texte", w.hatTexte], ["bilder", w.hatBilder], ["logo", w.hatLogo], ["domain", w.hatDomain]]
|
|
.forEach(function (paar) {
|
|
if (!paar[1]) return;
|
|
var el = form.querySelector("[name='v_" + paar[0] + "'][value='" + paar[1] + "']");
|
|
if (el) el.checked = true;
|
|
});
|
|
if (w.datenschutz) {
|
|
var ds = form.querySelector("[name='datenschutz']");
|
|
if (ds) ds.checked = true;
|
|
}
|
|
}
|
|
|
|
/* ------------------------------------------------------------------
|
|
Tabelle "Was hast du schon?" — per JS gebaut, damit die vier Zeilen
|
|
nicht viermal fast gleich im HTML stehen.
|
|
------------------------------------------------------------------ */
|
|
function baueVorhandenTabelle() {
|
|
var koerper = document.getElementById("af-vorhanden");
|
|
if (!koerper) return;
|
|
var zeilen = [
|
|
["texte", "af3_texte"],
|
|
["bilder", "af3_bilder"],
|
|
["logo", "af3_logo"],
|
|
["domain", "af3_domain"],
|
|
];
|
|
var antworten = [["ja", "af3_ja"], ["teilweise", "af3_teilweise"], ["nein", "af3_nein"]];
|
|
|
|
koerper.innerHTML = zeilen.map(function (z) {
|
|
var knoepfe = antworten.map(function (a) {
|
|
return '<label class="wd-wahl-kachel" style="display:inline-block;margin:0 .3rem .3rem 0">' +
|
|
'<input type="radio" name="v_' + z[0] + '" value="' + a[0] + '" />' +
|
|
'<span class="wd-wahl-inhalt" style="padding:.55rem .8rem .55rem 2.4rem">' +
|
|
'<span class="wd-wahl-titel" style="font-size:.9rem" data-i18n="' + a[1] + '">' +
|
|
window.WD.t(a[1]) + "</span></span></label>";
|
|
}).join("");
|
|
return '<tr><td style="white-space:nowrap;vertical-align:middle"><strong data-i18n="' + z[1] + '">' +
|
|
window.WD.t(z[1]) + "</strong></td><td>" + knoepfe + "</td></tr>";
|
|
}).join("");
|
|
}
|
|
|
|
/* ------------------------------------------------------------------
|
|
Prüfung je Schritt
|
|
------------------------------------------------------------------ */
|
|
function fehlerSetzen(feldId, text) {
|
|
var feld = document.getElementById(feldId);
|
|
if (!feld) return;
|
|
feld.classList.toggle("wd-feld--fehler", !!text);
|
|
var p = feld.querySelector(".wd-feld-fehler");
|
|
if (p) p.textContent = text || "";
|
|
}
|
|
|
|
function fehlerLeeren() {
|
|
form.querySelectorAll(".wd-feld--fehler").forEach(function (f) {
|
|
f.classList.remove("wd-feld--fehler");
|
|
var p = f.querySelector(".wd-feld-fehler");
|
|
if (p) p.textContent = "";
|
|
});
|
|
}
|
|
|
|
/* Dieselbe absichtlich einfache Prüfung wie auf dem Server: Die einzige
|
|
zuverlässige Kontrolle ist ohnehin, ob die Antwortmail ankommt. Eine
|
|
strenge Regel lehnt vor allem gültige Adressen ab. */
|
|
function istEmail(s) {
|
|
var t = String(s || "").trim().toLowerCase();
|
|
var teile = t.split("@");
|
|
return teile.length === 2 && !!teile[0] && teile[1].indexOf(".") > 0 && !/\s/.test(t);
|
|
}
|
|
|
|
function pruefeSeite(nr) {
|
|
fehlerLeeren();
|
|
var w = werte();
|
|
var fehler = [];
|
|
|
|
if (nr === 1) {
|
|
if (!w.paket) { fehlerSetzen("feld-paket", window.WD.t("af_fehler_paket")); fehler.push("feld-paket"); }
|
|
if (!w.ziel) { fehlerSetzen("feld-ziel", window.WD.t("af_fehler_ziel")); fehler.push("feld-ziel"); }
|
|
}
|
|
if (nr === 4) {
|
|
if (!w.name) { fehlerSetzen("feld-name", window.WD.t("af_fehler_name")); fehler.push("feld-name"); }
|
|
if (!istEmail(w.email)) { fehlerSetzen("feld-email", window.WD.t("af_fehler_email")); fehler.push("feld-email"); }
|
|
/* Wer telefonisch kontaktiert werden möchte, muss eine Nummer
|
|
angeben — sonst kann der Wunsch gar nicht erfüllt werden. */
|
|
if (w.kontaktweg === "telefon" && !w.telefon) {
|
|
fehlerSetzen("feld-telefon", window.WD.t("af_fehler_telefon")); fehler.push("feld-telefon");
|
|
}
|
|
if (!w.datenschutz) {
|
|
fehlerSetzen("feld-datenschutz", window.WD.t("af_fehler_datenschutz")); fehler.push("feld-datenschutz");
|
|
}
|
|
}
|
|
|
|
if (fehler.length) {
|
|
window.WD.meldung(meldung, window.WD.t("af_fehler_schritt"), "fehler");
|
|
var erstes = document.getElementById(fehler[0]);
|
|
if (erstes) {
|
|
erstes.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
var eingabe = erstes.querySelector("input, textarea, select");
|
|
if (eingabe) setTimeout(function () { eingabe.focus({ preventScroll: true }); }, 300);
|
|
}
|
|
return false;
|
|
}
|
|
window.WD.meldungAus(meldung);
|
|
return true;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------
|
|
Navigation
|
|
------------------------------------------------------------------ */
|
|
function zeigeSeite(nr) {
|
|
aktuelleSeite = nr;
|
|
seiten.forEach(function (s) {
|
|
s.classList.toggle("af-sichtbar", Number(s.dataset.seite) === nr);
|
|
});
|
|
|
|
marken.forEach(function (m) {
|
|
var i = Number(m.dataset.marke);
|
|
m.classList.toggle("af-aktiv", i === nr);
|
|
m.classList.toggle("af-erledigt", i < nr);
|
|
});
|
|
|
|
var fortschritt = document.getElementById("af-fortschritt");
|
|
var hinweis = document.getElementById("af-speicherhinweis");
|
|
|
|
if (nr === SEITE_DANKE) {
|
|
knopfLeiste.hidden = true;
|
|
if (fortschritt) fortschritt.hidden = true;
|
|
if (hinweis) hinweis.hidden = true;
|
|
} else {
|
|
knopfLeiste.hidden = false;
|
|
if (fortschritt) fortschritt.hidden = false;
|
|
if (hinweis) hinweis.hidden = false;
|
|
btnZurueck.hidden = nr === 1;
|
|
btnWeiter.textContent =
|
|
nr === SEITE_ZUSAMMENFASSUNG ? window.WD.t("af_senden")
|
|
: nr === LETZTE_SEITE_EINGABE ? window.WD.t("af_pruefen")
|
|
: window.WD.t("af_weiter");
|
|
zaehler.textContent =
|
|
window.WD.t("af_schritt") + " " + Math.min(nr, 4) + " " + window.WD.t("af_von") + " 4";
|
|
}
|
|
|
|
/* Nach oben, aber nur bis zum Formular — ein Sprung ganz nach oben
|
|
zwingt sonst bei jedem Schritt zum Zurückscrollen. */
|
|
var rahmen = document.getElementById("af-rahmen");
|
|
if (rahmen) {
|
|
var y = rahmen.getBoundingClientRect().top + window.scrollY - 80;
|
|
window.scrollTo({ top: Math.max(0, y), behavior: "smooth" });
|
|
}
|
|
|
|
/* Auf der Dankeseite NICHT mehr speichern.
|
|
Sonst passiert Folgendes: absenden() loescht den Entwurf, ruft dann
|
|
zeigeSeite(DANKE) auf — und diese Zeile legt ihn sofort wieder an.
|
|
Beim naechsten Besuch stuenden die Daten einer bereits gesendeten
|
|
Anfrage wieder im Formular, samt "Wir haben deine letzte Eingabe
|
|
wiederhergestellt". Genau so im Selbsttest aufgefallen. */
|
|
if (nr !== SEITE_DANKE) speichern();
|
|
}
|
|
|
|
function folgefragenAktualisieren() {
|
|
var w = werte();
|
|
var shop = w.paket === "shop" || w.funktionen.indexOf("shop") >= 0;
|
|
var vw = w.paket === "verwaltung" || w.funktionen.indexOf("login") >= 0;
|
|
var eShop = document.getElementById("af-folge-shop");
|
|
var eVw = document.getElementById("af-folge-verwaltung");
|
|
if (eShop) eShop.hidden = !shop;
|
|
if (eVw) eVw.hidden = !vw;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------
|
|
Zusammenfassung
|
|
------------------------------------------------------------------ */
|
|
function beschriftung(schluessel, roh) {
|
|
if (!roh) return null;
|
|
var t = window.WD.t(schluessel);
|
|
return t === schluessel ? roh : t;
|
|
}
|
|
|
|
function baueZusammenfassung() {
|
|
var w = werte();
|
|
var ziel = document.getElementById("af-zusammenfassung");
|
|
var sicher = window.WD.sicher;
|
|
var leer = window.WD.t("af_zus_leer");
|
|
|
|
function zeile(was, wert) {
|
|
var istLeer = !wert;
|
|
return '<div class="af-zus-zeile"><span class="af-zus-was">' + sicher(was) + "</span>" +
|
|
'<span class="af-zus-wert' + (istLeer ? " af-leer" : "") + '">' +
|
|
sicher(istLeer ? leer : wert) + "</span></div>";
|
|
}
|
|
|
|
function block(titel, schrittNr, zeilen) {
|
|
return '<div class="af-zus-block"><div class="af-zus-kopf"><h3>' + sicher(titel) + "</h3>" +
|
|
'<button type="button" data-zu="' + schrittNr + '">' + sicher(window.WD.t("af_zus_aendern")) + "</button>" +
|
|
"</div>" + zeilen.join("") + "</div>";
|
|
}
|
|
|
|
var paketName = {
|
|
onepager: "pk_onepager_h3", website: "pk_website_h3", shop: "pk_shop_h3",
|
|
verwaltung: "pk_verwaltung_h3", betreuung: "betreuung_h3", unklar: "af1_unklar",
|
|
}[w.paket];
|
|
|
|
var funktionsNamen = {
|
|
kontaktformular: "af2_f_kontakt", galerie: "af2_f_galerie", termin: "af2_f_termin",
|
|
shop: "af2_f_shop", login: "af2_f_login", blog: "af2_f_blog",
|
|
sprachen: "af2_f_sprachen", karte: "af2_f_karte", newsletter: "af2_f_newsletter", app: "af2_f_app",
|
|
};
|
|
var funktionenText = w.funktionen
|
|
.map(function (f) { return beschriftung(funktionsNamen[f], f); })
|
|
.filter(Boolean)
|
|
/* HTML-Entities aus dem Wörterbuch (&) in echte Zeichen —
|
|
hier ist reiner Text gefragt, er wird gleich escaped. */
|
|
.map(function (s) { return s.replace(/&/g, "&"); })
|
|
.join(", ");
|
|
|
|
var antwortNamen = { ja: "af3_ja", teilweise: "af3_teilweise", nein: "af3_nein" };
|
|
var budgetNamen = {
|
|
"bis-500": "af3_budget_1", "500-1000": "af3_budget_2",
|
|
"1000-2500": "af3_budget_3", "ueber-2500": "af3_budget_4", unklar: "af3_budget_5",
|
|
};
|
|
|
|
var html = "";
|
|
html += block(window.WD.t("af_s1"), 1, [
|
|
zeile(window.WD.t("af1_paket_label"), beschriftung(paketName, w.paket)),
|
|
zeile(window.WD.t("af1_ziel_label"), w.ziel),
|
|
zeile(window.WD.t("af1_zielgruppe_label"), w.zielgruppe),
|
|
]);
|
|
var zeilen2 = [
|
|
zeile(window.WD.t("af2_seiten_label"), w.seiten),
|
|
zeile(window.WD.t("af2_funktionen_label"), funktionenText),
|
|
];
|
|
Object.keys(w.details).forEach(function (k) {
|
|
zeilen2.push(zeile(k.replace(/_/g, " "), w.details[k]));
|
|
});
|
|
html += block(window.WD.t("af_s2"), 2, zeilen2);
|
|
|
|
html += block(window.WD.t("af_s3"), 3, [
|
|
zeile(window.WD.t("af3_texte"), beschriftung(antwortNamen[w.hatTexte], w.hatTexte)),
|
|
zeile(window.WD.t("af3_bilder"), beschriftung(antwortNamen[w.hatBilder], w.hatBilder)),
|
|
zeile(window.WD.t("af3_logo"), beschriftung(antwortNamen[w.hatLogo], w.hatLogo)),
|
|
zeile(window.WD.t("af3_domain"), beschriftung(antwortNamen[w.hatDomain], w.hatDomain)),
|
|
zeile(window.WD.t("af3_budget_label"), beschriftung(budgetNamen[w.budget], w.budget)),
|
|
zeile(window.WD.t("af3_termin_label"), w.termin),
|
|
]);
|
|
html += block(window.WD.t("af_s4"), 4, [
|
|
zeile(window.WD.t("af4_name"), w.name),
|
|
zeile(window.WD.t("af4_email"), w.email),
|
|
zeile(window.WD.t("af4_telefon"), w.telefon),
|
|
zeile(window.WD.t("af4_weg"), window.WD.t(w.kontaktweg === "telefon" ? "af4_weg_telefon" : "af4_weg_email")),
|
|
zeile(window.WD.t("af4_bemerkung"), w.bemerkung),
|
|
]);
|
|
|
|
ziel.innerHTML = html;
|
|
ziel.querySelectorAll("[data-zu]").forEach(function (b) {
|
|
b.addEventListener("click", function () { zeigeSeite(Number(b.dataset.zu)); });
|
|
});
|
|
}
|
|
|
|
/* ------------------------------------------------------------------
|
|
Absenden
|
|
------------------------------------------------------------------ */
|
|
async function absenden() {
|
|
var w = werte();
|
|
window.WD.knopfSperren(btnWeiter, true, window.WD.t("af_sendet"));
|
|
window.WD.meldungAus(meldung);
|
|
|
|
try {
|
|
var antwort = await window.WD.api("/webdesign/anfrage", {
|
|
method: "POST",
|
|
body: Object.assign({}, w, {
|
|
sprache: window.WD.getSprache(),
|
|
geoeffnetAm: geoeffnetAm,
|
|
}),
|
|
});
|
|
|
|
/* Erst NACH bestätigtem Eingang löschen. Bricht das Absenden ab,
|
|
sind die Eingaben noch da und man kann es erneut versuchen. */
|
|
/* Der Server meldet bei verdächtigen Absendungen bewusst Erfolg,
|
|
speichert aber nichts und liefert KEINE Nummer (Honigtopf oder
|
|
verdächtig kurze Ausfüllzeit — siehe routes/webdesign-anfragen.js).
|
|
Das ist gegenüber automatischen Skripten richtig so.
|
|
|
|
Für einen echten Menschen, der zufällig sehr schnell war, wäre
|
|
eine Dankeseite mit "—" als Nummer aber eine stille Lüge: er
|
|
glaubt, die Anfrage sei unterwegs, und wartet auf eine Antwort,
|
|
die nie kommt. Deshalb hier ohne Nummer ein ehrlicher Hinweis mit
|
|
einem zweiten Weg. */
|
|
var nummerFeld = document.getElementById("af-nummer");
|
|
if (antwort.nummer) {
|
|
entwurfLoeschen();
|
|
nummerFeld.textContent = antwort.nummer;
|
|
} else {
|
|
/* Entwurf ABSICHTLICH behalten — falls wirklich nichts ankam,
|
|
soll die Arbeit nicht weg sein. */
|
|
nummerFeld.textContent = "—";
|
|
nummerFeld.style.fontSize = "1rem";
|
|
nummerFeld.style.letterSpacing = "normal";
|
|
nummerFeld.textContent = window.WD.t("af_danke_ohne_nummer");
|
|
}
|
|
zeigeSeite(SEITE_DANKE);
|
|
} catch (fehler) {
|
|
/* Feldbezogene Fehler vom Server anzeigen — der Server ist die
|
|
maßgebliche Prüfung, nicht der Browser. */
|
|
var felder = fehler && fehler.daten && fehler.daten.felder;
|
|
if (felder) {
|
|
Object.keys(felder).forEach(function (f) { fehlerSetzen("feld-" + f, felder[f]); });
|
|
}
|
|
window.WD.meldung(meldung, fehler.message || window.WD.t("fehler_allg"), "fehler");
|
|
window.WD.knopfSperren(btnWeiter, false);
|
|
}
|
|
}
|
|
|
|
/* ------------------------------------------------------------------
|
|
Start
|
|
------------------------------------------------------------------ */
|
|
function start() {
|
|
form = document.getElementById("af-form");
|
|
if (!form) return;
|
|
seiten = Array.prototype.slice.call(form.querySelectorAll(".af-seite"));
|
|
marken = Array.prototype.slice.call(document.querySelectorAll(".af-schritt-marke"));
|
|
zaehler = document.getElementById("af-zaehler");
|
|
meldung = document.getElementById("af-meldung");
|
|
knopfLeiste = document.getElementById("af-knopfleiste");
|
|
btnZurueck = document.getElementById("af-zurueck");
|
|
btnWeiter = document.getElementById("af-weiter");
|
|
|
|
baueVorhandenTabelle();
|
|
|
|
/* Paket aus der Adresszeile vorauswählen — die Leistungsseite
|
|
verlinkt mit ?paket=shop. Spart einen Klick und zeigt, dass der
|
|
Weg mitgedacht ist. */
|
|
var params = new URLSearchParams(location.search);
|
|
var paket = params.get("paket");
|
|
if (paket) {
|
|
var el = form.querySelector("[name='paket'][value='" + paket.replace(/[^a-z]/g, "") + "']");
|
|
if (el) el.checked = true;
|
|
}
|
|
|
|
var entwurf = gespeichertesLesen();
|
|
if (entwurf) {
|
|
wiederherstellen(entwurf);
|
|
var box = document.getElementById("af-wieder");
|
|
if (box) box.hidden = false;
|
|
}
|
|
|
|
document.getElementById("af-verwerfen").addEventListener("click", function () {
|
|
entwurfLoeschen();
|
|
location.reload();
|
|
});
|
|
|
|
folgefragenAktualisieren();
|
|
|
|
btnWeiter.addEventListener("click", function () {
|
|
if (aktuelleSeite === SEITE_ZUSAMMENFASSUNG) return absenden();
|
|
if (!pruefeSeite(aktuelleSeite)) return;
|
|
if (aktuelleSeite === LETZTE_SEITE_EINGABE) {
|
|
baueZusammenfassung();
|
|
zeigeSeite(SEITE_ZUSAMMENFASSUNG);
|
|
return;
|
|
}
|
|
zeigeSeite(aktuelleSeite + 1);
|
|
});
|
|
|
|
btnZurueck.addEventListener("click", function () {
|
|
if (aktuelleSeite > 1) zeigeSeite(aktuelleSeite - 1);
|
|
});
|
|
|
|
/* Eingaben laufend sichern und Folgefragen nachziehen. "input" statt
|
|
"change", damit auch beim Tippen gespeichert wird. */
|
|
form.addEventListener("input", function () { folgefragenAktualisieren(); speichern(); });
|
|
form.addEventListener("change", function () { folgefragenAktualisieren(); speichern(); });
|
|
|
|
/* Enter im Formular soll weitergehen, nicht absenden — sonst ist die
|
|
Anfrage nach dem ersten Feld unterwegs. */
|
|
form.addEventListener("submit", function (e) { e.preventDefault(); btnWeiter.click(); });
|
|
|
|
/* Telefon wird bei "Per Telefon" zur Pflicht — das Wörtchen
|
|
"optional" muss dann verschwinden, sonst ist die spätere
|
|
Fehlermeldung unfair. */
|
|
form.querySelectorAll("[name='kontaktweg']").forEach(function (r) {
|
|
r.addEventListener("change", function () {
|
|
var opt = document.getElementById("af-telefon-opt");
|
|
if (opt) opt.hidden = r.value === "telefon" && r.checked;
|
|
});
|
|
});
|
|
|
|
/* Nach einem Sprachwechsel Knopfbeschriftung, Zähler und die per JS
|
|
gebauten Teile neu setzen. */
|
|
document.addEventListener("wd-sprache-geaendert", function () {
|
|
baueVorhandenTabelle();
|
|
wiederherstellen({ stand: werte() }); // Auswahl der Tabelle retten
|
|
if (aktuelleSeite === SEITE_ZUSAMMENFASSUNG) baueZusammenfassung();
|
|
zeigeSeite(aktuelleSeite);
|
|
});
|
|
|
|
zeigeSeite(entwurf && entwurf.seite && entwurf.seite <= LETZTE_SEITE_EINGABE ? entwurf.seite : 1);
|
|
}
|
|
|
|
if (window.WD) start();
|
|
else document.addEventListener("wd-bereit", start);
|
|
})();
|