diff --git a/css/event_create.css b/css/event_create.css index c781e36..4bb5f02 100644 --- a/css/event_create.css +++ b/css/event_create.css @@ -310,32 +310,31 @@ textarea:focus { align-items: center; } -.error-message { - margin: 0; - text-align: center; - width: 100%; - -} - .progress-wrap { - width: min(100%, var(--content-width)); - margin: 0 auto; + flex: 1; + position: relative; display: flex; align-items: center; - gap: 0.75rem; + align-self: center; + min-height: 2.75rem; } .progress-label { - flex-shrink: 0; + position: absolute; + top: -1.1rem; + left: 50%; + transform: translateX(-50%); font-size: 1rem; font-weight: 400; color: var(--black); white-space: nowrap; + text-align: center; } .progress { flex: 1; - height: 0.3rem; + width: 100%; + height: 0.45rem; background: var(--olive-light); border-radius: var(--radius-sm); overflow: hidden; @@ -360,9 +359,16 @@ textarea:focus { } .flow-actions-right { + position: relative; display: flex; align-items: center; - gap: var(--space-4); + justify-content: flex-end; +} + +.error-message--callout { + position: absolute; + right: 0; + bottom: calc(100% + 1.25rem); } .button--ghost:hover { @@ -454,6 +460,26 @@ textarea:focus-visible { align-items: stretch; } + .error-message--callout { + position: static; + width: 100%; + max-width: 100%; + margin-bottom: var(--space-2); + } + + .progress-wrap { + min-height: auto; + } + + .progress-label { + position: static; + transform: none; + } + + .error-message--callout::after { + display: none; + } + .event-flow-header { justify-content: flex-start; } @@ -477,4 +503,4 @@ textarea:focus-visible { .option-grid--4 { grid-template-columns: repeat(4, minmax(0, 1fr)); } -} \ No newline at end of file +} diff --git a/css/login_signup.css b/css/login_signup.css index b2c8683..b257730 100644 --- a/css/login_signup.css +++ b/css/login_signup.css @@ -68,6 +68,10 @@ h1 { margin-bottom: var(--space-4); } +.form-group.has-error { + margin-bottom: var(--space-2); +} + label { display: block; margin-bottom: var(--space-1); @@ -138,9 +142,10 @@ button[type="submit"]:active { /* --- Hints & errors --- */ -.error-message { - margin-top: 5px; +.error-message--field-callout { display: none; + margin-top: 0.65rem; + margin-left: auto; } .form-group.has-error input { @@ -148,7 +153,7 @@ button[type="submit"]:active { box-shadow: 0 0 5px rgba(212, 75, 36, 0.3); } -.form-group.has-error .error-message { +.form-group.has-error .error-message--field-callout { display: block; } @@ -272,6 +277,16 @@ button[type="submit"]:active { .image-section { min-height: 300px; } + + .error-message--field-callout { + margin-top: var(--space-1); + max-width: 100%; + white-space: normal; + } + + .error-message--field-callout::after { + display: none; + } } @@ -299,4 +314,4 @@ button[type="submit"]:active { .snackbar--visible { transform: translateX(-50%) translateY(0); opacity: 1; -} \ No newline at end of file +} diff --git a/css/stylesheet_global.css b/css/stylesheet_global.css index fdd8e2d..1ff4ed6 100644 --- a/css/stylesheet_global.css +++ b/css/stylesheet_global.css @@ -175,8 +175,74 @@ label { } .error-message { - color: var(--error-text); + font-family: var(--font-main); font-size: 1rem; + line-height: 1.4; +} + +.error-message--inline { + color: var(--error-text); +} + +.error-message--field-callout { + display: none; + width: fit-content; + max-width: min(100%, 20rem); + padding: 0.35rem 0.8rem; + border-radius: var(--radius-md); + background: var(--error); + color: var(--white); + text-align: center; + font-size: 0.8125rem; + line-height: 1.2; + white-space: normal; + overflow-wrap: anywhere; + position: relative; +} + +.error-message--field-callout::after { + content: ""; + position: absolute; + top: -0.35rem; + right: 1.6rem; + width: 0.8rem; + height: 0.8rem; + background: inherit; + border-top-left-radius: 0.2rem; + transform: rotate(45deg); +} + +.error-message--callout { + display: none; + width: max-content; + max-width: 15rem; + padding: 0.7rem 1.15rem; + border-radius: 1rem; + background: var(--error); + color: #ffffff; + text-align: center; + font-size: 0.95rem; + line-height: 1.25; + white-space: normal; + overflow-wrap: anywhere; + box-shadow: var(--shadow-interaction); + z-index: 1; +} + +.error-message--callout:not(:empty) { + display: block; +} + +.error-message--callout::after { + content: ""; + position: absolute; + right: 2.1rem; + bottom: -0.55rem; + width: 1.1rem; + height: 1.1rem; + background: inherit; + border-bottom-right-radius: 0.25rem; + transform: rotate(45deg); } /* Margins */ diff --git a/event_create.html b/event_create.html index 245d70d..4651b48 100644 --- a/event_create.html +++ b/event_create.html @@ -240,7 +240,7 @@
- +
@@ -379,9 +379,6 @@ +
+
- -
- -
diff --git a/js/event_create.js b/js/event_create.js index 2f53f1e..557edc1 100644 --- a/js/event_create.js +++ b/js/event_create.js @@ -23,6 +23,7 @@ const CURRENT_USER_KEY = "socialCookingCurrentUser"; // ============================= let currentStep = 0; const lastStep = steps.length - 1; +let reviewReturnStep = null; // Text für den Weiter-Button je nach Schritt const nextLabels = { @@ -151,10 +152,14 @@ function showStep(index, pushHistory = true) { */ function updateFlowVisibility(stepIndex) { const isIntroStep = stepIndex === 0; + const isReviewReturnMode = reviewReturnStep === lastStep && stepIndex < lastStep; flowFooter.hidden = isIntroStep; backButton.hidden = isIntroStep; - nextButton.textContent = nextLabels[stepIndex]; + backButton.textContent = "Zurück"; + nextButton.textContent = isReviewReturnMode + ? "Zurück zur Übersicht" + : nextLabels[stepIndex]; } @@ -567,6 +572,10 @@ function validateRequiredFields(fields) { * Wenn der User im ersten Formularschritt ist, geht es zurück zum Intro. */ function handleBackClick() { + if (reviewReturnStep === lastStep && currentStep < lastStep) { + reviewReturnStep = null; + } + if (currentStep > 1) { showStep(currentStep - 1); } else { @@ -582,6 +591,12 @@ function handleBackClick() { function handleNextClick() { if (!validateCurrentStep()) return; + if (reviewReturnStep === lastStep && currentStep < lastStep) { + showStep(lastStep); + reviewReturnStep = null; + return; + } + if (currentStep < lastStep) { showStep(currentStep + 1); } else { @@ -701,6 +716,8 @@ function registerReviewEditHandlers() { const stepIndex = Number(item.dataset.editStep); const fieldName = item.dataset.editField; + // Öffnet einen Schritt aus der Übersicht und springt danach direkt zurück zu Schritt 7. + reviewReturnStep = lastStep; showStep(stepIndex); focusFieldByName(fieldName); }; diff --git a/js/login.js b/js/login.js index fc1414e..aa8118e 100644 --- a/js/login.js +++ b/js/login.js @@ -50,69 +50,74 @@ function createFallbackUser(email, passwort) { // Validierungsfunktion function validateForm(event) { event.preventDefault(); - - let isValid = true; - // Email-Validierung + // Wir zeigen bewusst immer nur den ersten Fehler im Formular an. + // So bleibt der Ablauf ruhig und führt den Nutzer Feld für Feld. const emailValue = emailInput.value.trim(); const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; const emailGroup = emailInput.parentElement; + const passwortGroup = passwortInput.parentElement; + + emailGroup.classList.remove('has-error'); + passwortGroup.classList.remove('has-error'); if (!emailValue) { emailGroup.classList.add('has-error'); emailError.textContent = 'Bitte gib deine E-Mail Adresse ein.'; - isValid = false; - } else if (!emailRegex.test(emailValue)) { + emailInput.focus(); + return; + } + + if (!emailRegex.test(emailValue)) { emailGroup.classList.add('has-error'); emailError.textContent = 'Bitte gib eine gültige E-Mail Adresse ein.'; - isValid = false; - } else { - emailGroup.classList.remove('has-error'); + emailInput.focus(); + return; } // Passwort-Validierung const passwortValue = passwortInput.value; - const passwortGroup = passwortInput.parentElement; if (!passwortValue) { passwortGroup.classList.add('has-error'); passwortError.textContent = 'Bitte gib dein Passwort ein.'; - isValid = false; - } else if (passwortValue.length < 6) { + passwortInput.focus(); + return; + } + + if (passwortValue.length < 6) { passwortGroup.classList.add('has-error'); passwortError.textContent = 'Dein Passwort ist zu kurz. Bitte überprüfe dein Passwort.'; - isValid = false; - } else { - passwortGroup.classList.remove('has-error'); + passwortInput.focus(); + return; } // Wenn alle Validierungen bestanden, prüfen wir: // 1) gibt es den Benutzer schon? // 2) ist das Passwort korrekt? // Danach speichern wir die aktive Session. - if (isValid) { - const users = getStoredUsers(); - const matchedUser = users.find(user => user.email?.toLowerCase() === emailValue.toLowerCase()); + const users = getStoredUsers(); + const matchedUser = users.find(user => user.email?.toLowerCase() === emailValue.toLowerCase()); - if (matchedUser && matchedUser.passwort !== passwortValue) { - passwortGroup.classList.add('has-error'); - passwortError.textContent = 'Das Passwort ist nicht korrekt.'; - return; - } + if (matchedUser && matchedUser.passwort !== passwortValue) { + passwortGroup.classList.add('has-error'); + passwortError.textContent = 'Das Passwort ist nicht korrekt.'; + passwortInput.focus(); + return; + } - const userToLogin = matchedUser || createFallbackUser(emailValue, passwortValue); - setCurrentUser(userToLogin); + const userToLogin = matchedUser || createFallbackUser(emailValue, passwortValue); + setCurrentUser(userToLogin); - // Snackbar anzeigen und dann zur Event-Übersicht weiterleiten. - var snackbar = document.getElementById('snackbar'); - if (snackbar) { - snackbar.classList.add('snackbar--visible'); - setTimeout(function() { - window.location.href = 'event_overview.html'; - }, 2000); - } else { + // Snackbar anzeigen und dann zur Event-Übersicht weiterleiten. + var snackbar = document.getElementById('snackbar'); + if (snackbar) { + snackbar.classList.add('snackbar--visible'); + setTimeout(function() { window.location.href = 'event_overview.html'; - } + }, 2000); + } else { + window.location.href = 'event_overview.html'; } } @@ -132,4 +137,4 @@ passwortInput.addEventListener('input', function() { }); // Form Submit Event -loginForm.addEventListener('submit', validateForm); \ No newline at end of file +loginForm.addEventListener('submit', validateForm); diff --git a/js/signup.js b/js/signup.js index 9329f96..8afb01d 100644 --- a/js/signup.js +++ b/js/signup.js @@ -52,95 +52,100 @@ function closeWelcomeModal() { // Hauptfunktion für Formularvalidierung und Speicherung. function validateForm(event) { event.preventDefault(); - - let isValid = true; - // Vorname-Validierung + // Wir zeigen pro Submit nur den ersten Fehler an. + // So bleibt der Formularfluss klar und ruhig. const vornameValue = vornameInput.value.trim(); const vornameGroup = vornameInput.parentElement; + const nachnameGroup = nachnameInput.parentElement; + const emailGroup = emailInput.parentElement; + const passwortGroup = passwortInput.parentElement; + + vornameGroup.classList.remove('has-error'); + nachnameGroup.classList.remove('has-error'); + emailGroup.classList.remove('has-error'); + passwortGroup.classList.remove('has-error'); if (!vornameValue) { vornameGroup.classList.add('has-error'); - isValid = false; - } else { - vornameGroup.classList.remove('has-error'); + vornameInput.focus(); + return; } // Nachname-Validierung const nachnameValue = nachnameInput.value.trim(); - const nachnameGroup = nachnameInput.parentElement; if (!nachnameValue) { nachnameGroup.classList.add('has-error'); - isValid = false; - } else { - nachnameGroup.classList.remove('has-error'); + nachnameInput.focus(); + return; } // Email-Validierung const emailValue = emailInput.value.trim(); const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; - const emailGroup = emailInput.parentElement; if (!emailValue) { emailGroup.classList.add('has-error'); document.getElementById('emailError').textContent = 'Bitte gib deine E-Mail Adresse ein.'; - isValid = false; - } else if (!emailRegex.test(emailValue)) { + emailInput.focus(); + return; + } + + if (!emailRegex.test(emailValue)) { emailGroup.classList.add('has-error'); document.getElementById('emailError').textContent = 'Bitte gib eine gültige E-Mail Adresse ein.'; - isValid = false; - } else { - emailGroup.classList.remove('has-error'); + emailInput.focus(); + return; } // Passwort-Validierung const passwortValue = passwortInput.value; - const passwortGroup = passwortInput.parentElement; if (!passwortValue) { passwortGroup.classList.add('has-error'); document.getElementById('passwortError').textContent = 'Bitte gib ein Passwort ein.'; - isValid = false; - } else if (passwortValue.length < 8) { + passwortInput.focus(); + return; + } + + if (passwortValue.length < 8) { passwortGroup.classList.add('has-error'); document.getElementById('passwortError').textContent = 'Dein Passwort muss mindestens 8 Zeichen lang sein.'; - isValid = false; - } else { - passwortGroup.classList.remove('has-error'); + passwortInput.focus(); + return; } // Wenn alles gültig ist: // 1) auf doppelte E-Mail prüfen // 2) neuen Benutzer speichern // 3) als aktuellen Benutzer einloggen - if (isValid) { - const existingUsers = getStoredUsers(); - const emailLower = emailValue.toLowerCase(); - const emailAlreadyUsed = existingUsers.some(user => user.email?.toLowerCase() === emailLower); + const existingUsers = getStoredUsers(); + const emailLower = emailValue.toLowerCase(); + const emailAlreadyUsed = existingUsers.some(user => user.email?.toLowerCase() === emailLower); - if (emailAlreadyUsed) { - emailGroup.classList.add('has-error'); - document.getElementById('emailError').textContent = 'Diese E-Mail ist bereits registriert. Bitte nutze den Login.'; - return; - } - - const newUser = { - id: Date.now(), - vorname: vornameValue, - nachname: nachnameValue, - email: emailValue, - passwort: passwortValue, - createdAt: new Date().toISOString(), - source: 'signup' - }; - - setStoredUsers([newUser, ...existingUsers]); - setCurrentUser(newUser); - - openWelcomeModal(); - // Weiterleitung erfolgt beim Klick auf "Weiter zu den Events". + if (emailAlreadyUsed) { + emailGroup.classList.add('has-error'); + document.getElementById('emailError').textContent = 'Diese E-Mail ist bereits registriert. Bitte nutze den Login.'; + emailInput.focus(); + return; } + + const newUser = { + id: Date.now(), + vorname: vornameValue, + nachname: nachnameValue, + email: emailValue, + passwort: passwortValue, + createdAt: new Date().toISOString(), + source: 'signup' + }; + + setStoredUsers([newUser, ...existingUsers]); + setCurrentUser(newUser); + + openWelcomeModal(); + // Weiterleitung erfolgt beim Klick auf "Weiter zu den Events". } // Fehlerbehandlung bei Input-Änderung (entfernt Fehler wenn Benutzer korrigiert) @@ -180,4 +185,4 @@ welcomeModal.addEventListener('click', function(event) { }); // Form Submit Event -signupForm.addEventListener('submit', validateForm); \ No newline at end of file +signupForm.addEventListener('submit', validateForm); diff --git a/login.html b/login.html index 6c8455d..946f00f 100644 --- a/login.html +++ b/login.html @@ -35,17 +35,17 @@

Login

-
+
-
Bitte gib eine gültige E-Mail Adresse ein.
+
Bitte gib eine gültige E-Mail Adresse ein.
-
Bitte gib dein Passwort ein.
+
Bitte gib dein Passwort ein.
@@ -76,4 +76,4 @@
- \ No newline at end of file + diff --git a/signup.html b/signup.html index d5eac27..56b7a4c 100644 --- a/signup.html +++ b/signup.html @@ -39,29 +39,29 @@ Hinweis: Sichtbar auf der Plattform ist nur dein Vorname. Erst einer Anmeldung zum Event ist der Nachname für die Teilnehmenden sichtbar. - +
-
Bitte gib deinen Vornamen ein.
+
Bitte gib deinen Vornamen ein.
-
Bitte gib deinen Nachnamen ein.
+
Bitte gib deinen Nachnamen ein.
-
Bitte gib eine gültige E-Mail Adresse ein.
+
Bitte gib eine gültige E-Mail Adresse ein.
-
Dein Passwort muss mindestens 8 Zeichen lang sein.
+
Dein Passwort muss mindestens 8 Zeichen lang sein.
@@ -108,4 +108,4 @@ - \ No newline at end of file +