// ============================= // SETUP: Wichtige HTML-Elemente holen // Diese Konstanten verbinden unser JavaScript mit dem HTML. // So können wir später Buttons, Formularfelder und Bereiche steuern. // ============================= const form = document.getElementById("eventForm"); const steps = Array.from(document.querySelectorAll(".step")); const backButton = document.getElementById("backButton"); const nextButton = document.getElementById("nextButton"); const progressBar = document.getElementById("progressBar"); const errorMessage = document.getElementById("errorMessage"); const usernameElement = document.getElementById("username"); const flowFooter = document.getElementById("flowFooter"); // ============================= // STATE: aktueller Schritt im Flow // currentStep merkt sich, auf welchem Schritt der User gerade ist. // lastStep ist der letzte Schritt im Formular. // ============================= let currentStep = 0; const lastStep = steps.length - 1; // Text für den Weiter-Button je nach Schritt const nextLabels = { 0: "Weiter", 1: "Weiter", 2: "Weiter", 3: "Weiter", 4: "Weiter", 5: "Event veröffentlichen" }; // Demo-Wert: Später könnte der Name z. B. aus einem User-Profil kommen usernameElement.textContent = "Mia"; // ============================= // STEP 1: Kleine Hilfsfunktionen // Diese Funktionen helfen uns später an mehreren Stellen im Code. // ============================= /** * Gibt alle Eingabefelder eines bestimmten Schritts zurück. * Rückgabe: Array mit input-, textarea- und select-Feldern. */ function getStepFields(stepIndex) { return Array.from( steps[stepIndex].querySelectorAll("input, textarea, select") ); } /** * Zeigt eine Fehlermeldung im Formular an. * Wenn keine Nachricht übergeben wird, wird die Meldung geleert. */ function setErrorMessage(message = "") { errorMessage.textContent = message; } // ============================= // STEP 2: Schritt anzeigen & Oberfläche aktualisieren // showStep() ist eine der wichtigsten Funktionen: // Sie bestimmt, welcher Schritt sichtbar ist, // und aktualisiert gleichzeitig die restliche Oberfläche. // ============================= /** * Zeigt den gewünschten Schritt an. * Dabei werden auch Buttons, Progress Bar und Review aktualisiert. */ function showStep(index) { currentStep = index; // Nur der aktuelle Schritt soll sichtbar sein steps.forEach((step, stepIndex) => { step.classList.toggle("step--active", stepIndex === index); }); updateFlowVisibility(index); updateReviewIfNeeded(index); updateProgressBar(index, lastStep); setErrorMessage(""); // Für bessere UX: bei jedem Schritt wieder nach oben scrollen window.scrollTo({ top: 0, behavior: "smooth" }); } /** * Steuert Sichtbarkeit von Footer und Buttons. * Im Intro-Schritt werden Footer und Zurück-Button versteckt. * Zusätzlich bekommt der Weiter-Button je nach Schritt ein anderes Label. */ function updateFlowVisibility(stepIndex) { const isIntroStep = stepIndex === 0; flowFooter.hidden = isIntroStep; backButton.hidden = isIntroStep; nextButton.textContent = nextLabels[stepIndex]; } // ============================= // STEP 3: Fortschrittsanzeige // Die Progress Bar zeigt, wie weit der User im Flow ist. // Das Intro zählt noch nicht als eigentlicher Formularschritt. // ============================= /** * Berechnet den Fortschritt in Prozent und setzt die Breite der Progress Bar. * Beispiel: * - Intro = 0% * - erster echter Formularschritt = 0% * - letzter Schritt = 100% */ function updateProgressBar(stepIndex, totalStepIndex) { let progress = 0; if (stepIndex > 0) { progress = ((stepIndex - 1) / (totalStepIndex - 1)) * 100; } progressBar.style.width = `${progress}%`; } // ============================= // STEP 4: Review-Daten vorbereiten // Im letzten Schritt werden alle eingegebenen Daten nochmals angezeigt. // Dafür brauchen wir Funktionen, die Feldwerte sauber auslesen und formatieren. // ============================= /** * Führt das Update der Review nur aus, * wenn wirklich der letzte Schritt geöffnet ist. */ function updateReviewIfNeeded(stepIndex) { if (stepIndex === lastStep) { updateReview(); } } /** * Gibt den Wert eines Formularfeldes zurück. * Rückgabe: * - eingegebener Text / ausgewählte Option * - oder "–", falls nichts vorhanden ist */ function getFieldValue(name) { const field = form.elements[name]; if (!field) return "–"; // Spezialfall: Radio-Gruppen verhalten sich anders als normale Inputs if (field instanceof RadioNodeList) { return field.value || "–"; } return field.value.trim() || "–"; } /** * Gibt alle ausgewählten Checkbox-Werte als Text zurück. * Beispiel: "Vegetarisch, Vegan" * Falls nichts ausgewählt wurde: "Keine Angabe" */ function getCheckboxValues(name) { const checked = Array.from( form.querySelectorAll(`input[name="${name}"]:checked`) ); return checked.length ? checked.map(item => item.value).join(", ") : "Keine Angabe"; } /** * Formatiert ein Datum für die Review-Anzeige. * Beispiel: aus "2026-03-26" wird "26.03.2026" */ function formatDate(value) { if (!value || value === "–") return "–"; const date = new Date(value); if (Number.isNaN(date.getTime())) return value; return new Intl.DateTimeFormat("de-CH", { day: "2-digit", month: "2-digit", year: "numeric" }).format(date); } /** * Schreibt einen Wert in das passende Feld der Review-Ansicht. * Gesucht wird ein HTML-Element mit data-review="..." */ function updateReviewField(fieldName, value) { const target = document.querySelector(`[data-review="${fieldName}"]`); if (target) { target.textContent = value; } } /** * Baut den Text für Allergien / Hinweise zusammen. * Dabei werden Checkboxen und zusätzliches Freitextfeld kombiniert. */ function buildAllergiesReviewValue() { const selected = getCheckboxValues("allergies"); const notes = getFieldValue("allergiesOther"); if (selected === "Keine Angabe" && notes === "–") return "Keine Angabe"; if (selected !== "Keine Angabe" && notes !== "–") { return `${selected}, ${notes}`; } return selected !== "Keine Angabe" ? selected : notes; } /** * Liest alle wichtigen Formularwerte aus * und schreibt sie gesammelt in die Review-Ansicht. */ function updateReview() { const reviewValues = { eventTitle: getFieldValue("eventTitle"), eventType: getFieldValue("eventType"), menuDescription: getFieldValue("menuDescription"), eventDescription: getFieldValue("eventDescription"), maxGuests: getFieldValue("maxGuests"), dietType: getFieldValue("dietType"), allergies: buildAllergiesReviewValue(), eventDate: formatDate(getFieldValue("eventDate")), eventTime: getFieldValue("eventTime"), eventAddress: getFieldValue("eventAddress"), eventCity: getFieldValue("eventCity") }; Object.entries(reviewValues).forEach(([key, value]) => { updateReviewField(key, value); }); } // ============================= // STEP 5: Validierung // Bevor der User weitergehen darf, prüfen wir: // 1. Sind Pflichtfelder ausgefüllt? // 2. Wurde bei Pflicht-Radios etwas ausgewählt? // ============================= /** * Prüft, ob der aktuelle Schritt gültig ist. * Rückgabe: * - true = alles okay * - false = es gibt einen Fehler */ function validateCurrentStep() { // Intro und Review müssen nicht validiert werden if (currentStep === 0 || currentStep === lastStep) return true; const fields = getStepFields(currentStep); // Zuerst Radio-Gruppen prüfen const radioCheck = validateRadioGroups(fields); if (!radioCheck.isValid) { setErrorMessage(radioCheck.message); return false; } // Danach normale Pflichtfelder prüfen const requiredCheck = validateRequiredFields(fields); if (!requiredCheck.isValid) { setErrorMessage(requiredCheck.message); return false; } setErrorMessage(""); return true; } /** * Prüft alle Radio-Gruppen eines Schritts. * Rückgabe: * - Objekt mit isValid: true/false * - bei Fehler zusätzlich eine passende Meldung */ function validateRadioGroups(fields) { const names = [...new Set(fields.filter(f => f.type === "radio").map(f => f.name))]; for (const name of names) { const group = Array.from(form.querySelectorAll(`input[name="${name}"]`)); const required = group.some(f => f.required); const selected = group.some(f => f.checked); if (required && !selected) { return { isValid: false, message: "Bitte wähle eine Option aus." }; } } return { isValid: true }; } /** * Prüft alle Pflichtfelder ausser Radios und Checkboxen. * Rückgabe: * - Objekt mit isValid: true/false * - bei Fehler zusätzlich eine passende Meldung */ function validateRequiredFields(fields) { for (const field of fields) { if (field.type === "radio" || field.type === "checkbox") continue; if (!field.checkValidity()) { return { isValid: false, message: "Bitte fülle alle Pflichtfelder aus." }; } } return { isValid: true }; } // ============================= // STEP 6: Navigation mit Zurück / Weiter // Diese Funktionen bestimmen, was beim Klicken auf die Buttons passiert. // ============================= /** * Einen Schritt zurückgehen. * Wenn der User im ersten Formularschritt ist, geht es zurück zum Intro. */ function handleBackClick() { if (currentStep > 1) { showStep(currentStep - 1); } else { showStep(0); } } /** * Einen Schritt weitergehen. * Vorher wird geprüft, ob der aktuelle Schritt gültig ist. * Im letzten Schritt wird stattdessen das Formular abgeschickt. */ function handleNextClick() { if (!validateCurrentStep()) return; if (currentStep < lastStep) { showStep(currentStep + 1); } else { form.requestSubmit(); } } // ============================= // STEP 7: Submit // Aktuell ist das nur eine Demo. // Später könnte hier ein API-Call oder Speichern in einer Datenbank passieren. // ============================= /** * Reagiert auf das Absenden des Formulars. * preventDefault verhindert, dass die Seite neu lädt. */ function handleFormSubmit(event) { event.preventDefault(); // 1. Feedback geben alert("Dein Event wurde erfolgreich veröffentlicht!"); // 2. Weiterleiten (z. B. zur Event-Übersicht) window.location.href = "event_overview.html"; } // ============================= // STEP 8: Counter-Felder (+ / -) // Für Zahlenfelder wie z. B. Anzahl Gäste. // ============================= /** * Sucht alle Counter-Komponenten * und verbindet die Plus-/Minus-Buttons mit den passenden Funktionen. */ function registerCounterHandlers() { document.querySelectorAll("[data-counter]").forEach(counter => { const input = counter.querySelector("input[type='number']"); const dec = counter.querySelector("[data-counter-action='decrease']"); const inc = counter.querySelector("[data-counter-action='increase']"); dec.addEventListener("click", () => updateCounterValue(input, -1)); inc.addEventListener("click", () => updateCounterValue(input, 1)); }); } /** * Erhöht oder verringert den Wert eines Zahlenfelds. * Der Wert darf dabei nie kleiner als das definierte Minimum werden. */ function updateCounterValue(input, change) { const min = Number(input.min || 1); const currentValue = Number(input.value || min); input.value = Math.max(min, currentValue + change); } // ============================= // STEP 9: Alles starten // Hier werden alle Event Listener registriert // und der Flow startet mit dem Intro-Schritt. // ============================= /** * Initialisiert den kompletten Event-Erstellungs-Flow. * Diese Funktion wird einmal beim Laden der Seite aufgerufen. */ function initEventCreationFlow() { // Buttons, die den Flow starten document.querySelectorAll("[data-start-flow]").forEach(btn => { btn.addEventListener("click", () => showStep(1)); }); // Navigation backButton.addEventListener("click", handleBackClick); nextButton.addEventListener("click", handleNextClick); // Formular absenden form.addEventListener("submit", handleFormSubmit); // Counter aktivieren registerCounterHandlers(); // Startzustand: Intro anzeigen showStep(0); } // Startpunkt des Skripts initEventCreationFlow();