Compare commits

..

2 Commits

2 changed files with 420 additions and 37 deletions

View File

@@ -19,15 +19,39 @@ function normalizeUser(user) {
} }
return { return {
salutation: user.salutation || "",
firstName: user.firstName || "", firstName: user.firstName || "",
lastName: user.lastName || "", lastName: user.lastName || "",
birthday: user.birthday || "",
street: user.street || "",
houseNumber: user.houseNumber || "",
zip: user.zip || "",
city: user.city || "",
country: user.country || "Deutschland",
email: user.email || "", email: user.email || "",
password: user.password || "", passwordHash: user.passwordHash || (user.password ? hashPassword(user.password) : ""),
orders: Array.isArray(user.orders) ? user.orders : [], orders: Array.isArray(user.orders) ? user.orders : [],
paymentMethods: Array.isArray(user.paymentMethods) ? user.paymentMethods : [] paymentMethods: Array.isArray(user.paymentMethods) ? user.paymentMethods : []
}; };
} }
function hashPassword(password) {
let hashA = 0xdeadbeef;
let hashB = 0x41c6ce57;
const text = String(password || "");
for (let index = 0; index < text.length; index += 1) {
const charCode = text.charCodeAt(index);
hashA = Math.imul(hashA ^ charCode, 2654435761);
hashB = Math.imul(hashB ^ charCode, 1597334677);
}
hashA = Math.imul(hashA ^ (hashA >>> 16), 2246822507) ^ Math.imul(hashB ^ (hashB >>> 13), 3266489909);
hashB = Math.imul(hashB ^ (hashB >>> 16), 2246822507) ^ Math.imul(hashA ^ (hashA >>> 13), 3266489909);
return `hash$${(hashB >>> 0).toString(16).padStart(8, "0")}${(hashA >>> 0).toString(16).padStart(8, "0")}`;
}
function escapeHtml(value) { function escapeHtml(value) {
return String(value || "") return String(value || "")
.replaceAll("&", "&amp;") .replaceAll("&", "&amp;")
@@ -58,45 +82,56 @@ if (!Array.isArray(users)) {
users = []; users = [];
} }
users = users.map(normalizeUser).filter(Boolean); users = users.map(normalizeUser).filter(Boolean);
persistUsers();
let currentUser = normalizeUser(readStorageJson("currentUser", null)); let currentUser = normalizeUser(readStorageJson("currentUser", null));
if (currentUser && currentUser.email) { if (currentUser && currentUser.email) {
const storedMatch = users.find((user) => user.email === currentUser.email); const storedMatch = users.find((user) => user.email === currentUser.email);
if (storedMatch) { if (storedMatch) {
currentUser = storedMatch; currentUser = storedMatch;
persistCurrentUser();
} else { } else {
users.push(currentUser); users.push(currentUser);
persistUsers(); persistUsers();
persistCurrentUser();
} }
} }
function registerUser() { function registerUser() {
if (!validateRegisterForm()) {
return;
}
const salutation = document.getElementById("reg-salutation")?.value.trim() || "";
const firstName = document.getElementById("reg-firstname")?.value.trim() || ""; const firstName = document.getElementById("reg-firstname")?.value.trim() || "";
const lastName = document.getElementById("reg-lastname")?.value.trim() || ""; const lastName = document.getElementById("reg-lastname")?.value.trim() || "";
const birthday = document.getElementById("reg-birthday")?.value.trim() || "";
const street = document.getElementById("reg-street")?.value.trim() || "";
const houseNumber = document.getElementById("reg-house-number")?.value.trim() || "";
const zip = document.getElementById("reg-zip")?.value.trim() || "";
const city = document.getElementById("reg-city")?.value.trim() || "";
const country = document.getElementById("reg-country")?.value.trim() || "";
const email = (document.getElementById("reg-email")?.value.trim() || "").toLowerCase(); const email = (document.getElementById("reg-email")?.value.trim() || "").toLowerCase();
const password = document.getElementById("reg-password")?.value || ""; const password = document.getElementById("reg-password")?.value || "";
if (!firstName || !lastName || !email || !password) {
alert("Bitte fuelle alle Felder aus.");
return;
}
if (!email.includes("@")) {
alert("Bitte gib eine gueltige E-Mail-Adresse ein.");
return;
}
const existingUser = users.find((user) => user.email.toLowerCase() === email); const existingUser = users.find((user) => user.email.toLowerCase() === email);
if (existingUser) { if (existingUser) {
alert("E-Mail bereits registriert"); setFieldError(document.getElementById("reg-email"), "Diese E-Mail ist bereits registriert.");
return; return;
} }
const newUser = { const newUser = {
salutation,
firstName, firstName,
lastName, lastName,
birthday,
street,
houseNumber,
zip,
city,
country,
email, email,
password, passwordHash: hashPassword(password),
orders: [], orders: [],
paymentMethods: [] paymentMethods: []
}; };
@@ -107,22 +142,28 @@ function registerUser() {
persistUsers(); persistUsers();
persistCurrentUser(); persistCurrentUser();
alert("Registrierung erfolgreich");
document.getElementById("register-modal")?.classList.add("hidden"); document.getElementById("register-modal")?.classList.add("hidden");
openAccountDashboard(); openAccountDashboard();
} }
function loginUser() { function loginUser() {
if (!validateLoginForm()) {
return;
}
const email = (document.getElementById("login-email")?.value.trim() || "").toLowerCase(); const email = (document.getElementById("login-email")?.value.trim() || "").toLowerCase();
const password = document.getElementById("login-password")?.value || ""; const password = document.getElementById("login-password")?.value || "";
const passwordHash = hashPassword(password);
const user = users.find( const user = users.find(
(entry) => entry.email.toLowerCase() === email && entry.password === password (entry) => entry.email.toLowerCase() === email && entry.passwordHash === passwordHash
); );
if (!user) { if (!user) {
document.getElementById("login-error")?.classList.remove("hidden"); document.getElementById("login-error")?.classList.remove("hidden");
setFieldError(document.getElementById("login-email"), "E-Mail oder Passwort stimmen nicht.");
setFieldError(document.getElementById("login-password"), "E-Mail oder Passwort stimmen nicht.");
return; return;
} }
@@ -146,7 +187,15 @@ function openAccountDashboard() {
<div class="account-panel"> <div class="account-panel">
<div class="account-panel-header"> <div class="account-panel-header">
<h2>Mein Konto</h2> <h2>Mein Konto</h2>
<button class="account-logout-btn" onclick="logoutUser()">Abmelden</button> <div class="account-header-actions">
<button class="account-edit-btn" type="button" onclick="renderPersonalInfo(true)" aria-label="Persönliche Daten bearbeiten" title="Daten bearbeiten">
<svg viewBox="0 0 24 24" aria-hidden="true">
<path d="M12 20h9"></path>
<path d="M16.5 3.5a2.1 2.1 0 0 1 3 3L7 19l-4 1 1-4Z"></path>
</svg>
</button>
<button class="account-logout-btn" onclick="logoutUser()">Abmelden</button>
</div>
</div> </div>
<div class="account-tabs"> <div class="account-tabs">
@@ -162,21 +211,282 @@ function openAccountDashboard() {
renderPersonalInfo(); renderPersonalInfo();
} }
function renderPersonalInfo() { function renderPersonalInfo(isEditing = false) {
const target = document.getElementById("account-tab-content"); const target = document.getElementById("account-tab-content");
if (!target || !currentUser) { if (!target || !currentUser) {
return; return;
} }
if (isEditing) {
target.innerHTML = `
<div class="account-card account-personal-card">
<div class="account-card-title-row">
<h3>Persönliche Daten bearbeiten</h3>
<span>E-Mail bleibt unverändert</span>
</div>
<div class="account-edit-form">
<label class="auth-field">
<span>Anrede</span>
<select id="account-edit-salutation">
<option value="">Bitte wählen</option>
<option value="Frau" ${currentUser.salutation === "Frau" ? "selected" : ""}>Frau</option>
<option value="Herr" ${currentUser.salutation === "Herr" ? "selected" : ""}>Herr</option>
<option value="Divers" ${currentUser.salutation === "Divers" ? "selected" : ""}>Divers</option>
</select>
<span class="field-error"></span>
</label>
<label class="auth-field">
<span>Vorname</span>
<input type="text" id="account-edit-firstname" value="${escapeHtml(currentUser.firstName)}" required data-required-message="Vorname ist ein Pflichtfeld.">
<span class="field-error"></span>
</label>
<label class="auth-field">
<span>Nachname</span>
<input type="text" id="account-edit-lastname" value="${escapeHtml(currentUser.lastName)}" required data-required-message="Nachname ist ein Pflichtfeld.">
<span class="field-error"></span>
</label>
<label class="auth-field">
<span>Geburtstag</span>
<input type="date" id="account-edit-birthday" value="${escapeHtml(currentUser.birthday)}">
<span class="field-error"></span>
</label>
<label class="auth-field">
<span>Straße</span>
<input type="text" id="account-edit-street" value="${escapeHtml(currentUser.street)}">
<span class="field-error"></span>
</label>
<label class="auth-field">
<span>Nr.</span>
<input type="text" id="account-edit-house-number" value="${escapeHtml(currentUser.houseNumber)}">
<span class="field-error"></span>
</label>
<label class="auth-field">
<span>PLZ</span>
<input type="text" id="account-edit-zip" value="${escapeHtml(currentUser.zip)}">
<span class="field-error"></span>
</label>
<label class="auth-field">
<span>Ort</span>
<input type="text" id="account-edit-city" value="${escapeHtml(currentUser.city)}">
<span class="field-error"></span>
</label>
<label class="auth-field">
<span>Land</span>
<select id="account-edit-country">
<option value="Deutschland" ${currentUser.country === "Deutschland" ? "selected" : ""}>Deutschland</option>
<option value="Österreich" ${currentUser.country === "Österreich" ? "selected" : ""}>Österreich</option>
<option value="Schweiz" ${currentUser.country === "Schweiz" ? "selected" : ""}>Schweiz</option>
<option value="Luxemburg" ${currentUser.country === "Luxemburg" ? "selected" : ""}>Luxemburg</option>
</select>
<span class="field-error"></span>
</label>
<label class="auth-field account-email-locked">
<span>E-Mail</span>
<input type="email" value="${escapeHtml(currentUser.email)}" disabled>
<small>Die E-Mail-Adresse ist fest mit deinem Konto verbunden.</small>
</label>
</div>
<div class="account-edit-actions">
<button type="button" class="account-secondary-btn" onclick="renderPersonalInfo()">Abbrechen</button>
<button type="button" class="account-save-btn" onclick="savePersonalInfo()">Speichern</button>
</div>
</div>
`;
return;
}
target.innerHTML = ` target.innerHTML = `
<div class="account-card"> <div class="account-card account-personal-card">
<p><strong>Vorname:</strong> ${currentUser.firstName || "-"}</p> <h3>Persönliche Daten</h3>
<p><strong>Nachname:</strong> ${currentUser.lastName || "-"}</p> <div class="account-personal-grid">
<p><strong>E-Mail:</strong> ${currentUser.email || "-"}</p> <p><span>Anrede</span><strong>${escapeHtml(currentUser.salutation || "-")}</strong></p>
<p><span>Vorname</span><strong>${escapeHtml(currentUser.firstName || "-")}</strong></p>
<p><span>Nachname</span><strong>${escapeHtml(currentUser.lastName || "-")}</strong></p>
<p><span>Geburtstag</span><strong>${escapeHtml(currentUser.birthday || "-")}</strong></p>
<p><span>Straße</span><strong>${escapeHtml(currentUser.street || "-")}</strong></p>
<p><span>Nr.</span><strong>${escapeHtml(currentUser.houseNumber || "-")}</strong></p>
<p><span>PLZ</span><strong>${escapeHtml(currentUser.zip || "-")}</strong></p>
<p><span>Ort</span><strong>${escapeHtml(currentUser.city || "-")}</strong></p>
<p><span>Land</span><strong>${escapeHtml(currentUser.country || "-")}</strong></p>
<p><span>E-Mail</span><strong>${escapeHtml(currentUser.email || "-")}</strong></p>
</div>
</div> </div>
`; `;
} }
function savePersonalInfo() {
if (!currentUser) {
return;
}
const requiredFields = ["account-edit-firstname", "account-edit-lastname"];
const isValid = requiredFields
.map((id) => validateAccountField(document.getElementById(id)))
.every(Boolean);
if (!isValid) {
return;
}
currentUser.salutation = document.getElementById("account-edit-salutation")?.value || "";
currentUser.firstName = document.getElementById("account-edit-firstname")?.value.trim() || "";
currentUser.lastName = document.getElementById("account-edit-lastname")?.value.trim() || "";
currentUser.birthday = document.getElementById("account-edit-birthday")?.value || "";
currentUser.street = document.getElementById("account-edit-street")?.value.trim() || "";
currentUser.houseNumber = document.getElementById("account-edit-house-number")?.value.trim() || "";
currentUser.zip = document.getElementById("account-edit-zip")?.value.trim() || "";
currentUser.city = document.getElementById("account-edit-city")?.value.trim() || "";
currentUser.country = document.getElementById("account-edit-country")?.value || "Deutschland";
const userIndex = users.findIndex((user) => user.email === currentUser.email);
if (userIndex >= 0) {
users[userIndex] = currentUser;
}
persistUsers();
persistCurrentUser();
renderPersonalInfo();
}
function getFieldWrapper(input) {
if (!input) {
return null;
}
return input.closest(".auth-field") || document.querySelector(`[data-field-for="${input.id}"]`);
}
function setFieldError(input, message) {
const wrapper = getFieldWrapper(input);
if (!wrapper) {
return;
}
input.dataset.touched = "true";
wrapper.classList.add("is-invalid");
const error = wrapper.querySelector(".field-error");
if (error) {
error.textContent = message;
}
}
function clearFieldError(input) {
const wrapper = getFieldWrapper(input);
if (!wrapper) {
return;
}
wrapper.classList.remove("is-invalid");
const error = wrapper.querySelector(".field-error");
if (error) {
error.textContent = "";
}
}
function validateAccountField(input) {
if (!input) {
return true;
}
const value = (input.value || "").trim();
if (input.required && !value) {
setFieldError(input, input.dataset.requiredMessage || "Dieses Feld ist ein Pflichtfeld.");
return false;
}
if (input.type === "email" && value && !value.includes("@")) {
setFieldError(input, input.dataset.emailMessage || "Bitte gib eine gültige E-Mail-Adresse ein.");
return false;
}
if (input.type === "password" && value && (value.length < 8 || value.length > 32)) {
setFieldError(input, input.dataset.passwordMessage || "Passwort muss 8 bis 32 Zeichen haben.");
return false;
}
clearFieldError(input);
return true;
}
function validateRegisterForm() {
const fieldIds = [
"reg-salutation",
"reg-firstname",
"reg-lastname",
"reg-birthday",
"reg-street",
"reg-house-number",
"reg-zip",
"reg-city",
"reg-country",
"reg-email",
"reg-password"
];
return fieldIds
.map((id) => validateAccountField(document.getElementById(id)))
.every(Boolean);
}
function validateLoginForm() {
return ["login-email", "login-password"]
.map((id) => validateAccountField(document.getElementById(id)))
.every(Boolean);
}
function initAccountFieldValidation() {
const fieldSelector = [
"#login-email",
"#login-password",
"#reg-salutation",
"#reg-firstname",
"#reg-lastname",
"#reg-birthday",
"#reg-street",
"#reg-house-number",
"#reg-zip",
"#reg-city",
"#reg-country",
"#reg-email",
"#reg-password"
].join(",");
document.querySelectorAll(fieldSelector).forEach((input) => {
input.addEventListener("blur", () => {
input.dataset.touched = "true";
validateAccountField(input);
});
input.addEventListener("input", () => {
input.dataset.touched = "true";
if (input.type !== "hidden") {
validateAccountField(input);
}
});
input.addEventListener("change", () => {
input.dataset.touched = "true";
validateAccountField(input);
});
});
document.querySelectorAll(".gender-selector button").forEach((button) => {
button.addEventListener("click", () => {
const salutationInput = document.getElementById("reg-salutation");
if (!salutationInput) {
return;
}
salutationInput.value = button.dataset.gender || "";
salutationInput.dataset.touched = "true";
button.closest(".gender-selector")?.querySelectorAll("button").forEach((item) => {
item.classList.toggle("active", item === button);
});
validateAccountField(salutationInput);
});
});
}
function renderOrders() { function renderOrders() {
const target = document.getElementById("account-tab-content"); const target = document.getElementById("account-tab-content");
if (!target || !currentUser) { if (!target || !currentUser) {
@@ -220,7 +530,7 @@ function renderOrders() {
target.innerHTML = ` target.innerHTML = `
<div class="account-orders-shell"> <div class="account-orders-shell">
<h3>Meine Bestellungen</h3> <h3>Meine Bestellungen</h3>
<p class="account-payments-note">Klicke auf eine Bestellung, um dein Ticket-Detail zu sehen.</p> <p class="account-payments-note">Klicke auf eine Bestellung, um genauere Informationen zu sehen.</p>
<div class="account-orders-grid">${orderHtml}</div> <div class="account-orders-grid">${orderHtml}</div>
<div id="order-ticket-details" class="order-ticket-details hidden"></div> <div id="order-ticket-details" class="order-ticket-details hidden"></div>
</div> </div>
@@ -293,7 +603,7 @@ function renderPayments() {
target.innerHTML = ` target.innerHTML = `
<div class="account-card"> <div class="account-card">
<h3>Zahlungsmethoden</h3> <h3>Zahlungsmethoden</h3>
<p class="account-payments-note">Platzhalter zum Hinterlegen deiner Logos oder Anbieter-Informationen.</p> <p class="account-payments-note">Hier kannst du deine bevorzugten Zahlungsmethoden verwalten.</p>
<div class="account-payment-grid"> <div class="account-payment-grid">
<button type="button" class="account-payment-card account-pay-trigger" data-pay-modal="pay-modal-card"> <button type="button" class="account-payment-card account-pay-trigger" data-pay-modal="pay-modal-card">
<div class="payment-logo-slot"> <div class="payment-logo-slot">
@@ -314,14 +624,14 @@ function renderPayments() {
<img src="img/applepay.png" alt="Apple Pay"> <img src="img/applepay.png" alt="Apple Pay">
</div> </div>
<h4>Apple Pay</h4> <h4>Apple Pay</h4>
<p>Geraet freischalten</p> <p>Gerät freischalten</p>
</button> </button>
<button type="button" class="account-payment-card account-pay-trigger" data-pay-modal="pay-modal-google"> <button type="button" class="account-payment-card account-pay-trigger" data-pay-modal="pay-modal-google">
<div class="payment-logo-slot"> <div class="payment-logo-slot">
<img src="img/googlepay.png" alt="Google Pay"> <img src="img/googlepay.png" alt="Google Pay">
</div> </div>
<h4>Google Pay</h4> <h4>Google Pay</h4>
<p>Wallet verknuepfen</p> <p>Wallet verknüpfen</p>
</button> </button>
</div> </div>
</div> </div>

View File

@@ -305,7 +305,7 @@
<div class="snack-info"> <div class="snack-info">
<span class="badge">Metallbecher</span> <span class="badge">Metallbecher</span>
<h3>Limitierter Metallbecher - Zoomania 2</h3> <h3>Limitierter Metallbecher - Zoomania 2</h3>
<p class="snack-card-note">Sammlerbecher mit Popcornfüllung, wahlweise süß oder salzig.</p> <p class="snack-card-note">Metall Sammelbecher im Zoomania 2 Design.</p>
<div class="option-group"> <div class="option-group">
<button class="opt-btn active">Süß</button> <button class="opt-btn active">Süß</button>
<button class="opt-btn">Salzig</button> <button class="opt-btn">Salzig</button>
@@ -343,7 +343,7 @@
<div class="snack-info"> <div class="snack-info">
<span class="badge">Collector</span> <span class="badge">Collector</span>
<h3>Limitierter Sammelbecher - Scream VII</h3> <h3>Limitierter Sammelbecher - Scream VII</h3>
<p class="snack-card-note">Hallo Sydney! Ghostface in der Tür.</p> <p class="snack-card-note">Hallo Sydney! Ghostface ist an der Tür.</p>
<div class="option-group"> <div class="option-group">
<button class="opt-btn active">Süß</button> <button class="opt-btn active">Süß</button>
<button class="opt-btn">Salzig</button> <button class="opt-btn">Salzig</button>
@@ -427,7 +427,7 @@
<div class="snack-info"> <div class="snack-info">
<span class="badge">Metallbecher</span> <span class="badge">Metallbecher</span>
<h3>Limitierter Metallbecher - Hoppers</h3> <h3>Limitierter Metallbecher - Hoppers</h3>
<p class="snack-card-note">Stabiler Becher mit Popcornfüllung.</p> <p class="snack-card-note">Metall Sammelbecher im HOPPERS Design.</p>
<div class="option-group"> <div class="option-group">
<button class="opt-btn active">Süß</button> <button class="opt-btn active">Süß</button>
<button class="opt-btn">Salzig</button> <button class="opt-btn">Salzig</button>
@@ -442,7 +442,7 @@
<div class="snack-info"> <div class="snack-info">
<span class="badge">Collector</span> <span class="badge">Collector</span>
<h3>Limitierter Sammelbecher - Hoppers</h3> <h3>Limitierter Sammelbecher - Hoppers</h3>
<p class="snack-card-note">Sammlerbecher mit warmem Popcornmoment.</p> <p class="snack-card-note">Limitierter Sammelbecher als Biberdamm - Mit verteckter Eidechse.</p>
<div class="option-group"> <div class="option-group">
<button class="opt-btn active">Süß</button> <button class="opt-btn active">Süß</button>
<button class="opt-btn">Salzig</button> <button class="opt-btn">Salzig</button>
@@ -480,7 +480,7 @@
<div class="snack-info"> <div class="snack-info">
<span class="badge">Yoshi Cup</span> <span class="badge">Yoshi Cup</span>
<h3>Limitierter Sammelbecher - Yoshi Becher</h3> <h3>Limitierter Sammelbecher - Yoshi Becher</h3>
<p class="snack-card-note">Verspielter Sammlerbecher für Mario-Fans.</p> <p class="snack-card-note">Limitierter Sammelbecher in der Form von Yoshi.</p>
<div class="option-group"> <div class="option-group">
<button class="opt-btn active">Süß</button> <button class="opt-btn active">Süß</button>
<button class="opt-btn">Salzig</button> <button class="opt-btn">Salzig</button>
@@ -841,6 +841,7 @@
<div class="snack-card highlight"> <div class="snack-card highlight">
<div class="snack-img"><img src="img/nachokombigross.png" alt="Nacho Kombi Groß"></div> <div class="snack-img"><img src="img/nachokombigross.png" alt="Nacho Kombi Groß"></div>
<div class="snack-info"> <div class="snack-info">
<span class="badge">Bestseller</span>
<h3>Nacho Menü Groß</h3> <h3>Nacho Menü Groß</h3>
<p class="snack-card-note">Nachos groß + 1 Dip + 1x 0,33L Getränk</p> <p class="snack-card-note">Nachos groß + 1 Dip + 1x 0,33L Getränk</p>
<div class="option-group"> <div class="option-group">
@@ -985,8 +986,14 @@
<div class="account-login-box"> <div class="account-login-box">
<h2>Mein Konto</h2> <h2>Mein Konto</h2>
<input type="email" id="login-email" placeholder="E-Mail"> <label class="auth-field">
<input type="password" id="login-password" placeholder="Passwort"> <input type="email" id="login-email" placeholder="E-Mail" required data-required-message="E-Mail ist ein Pflichtfeld." data-email-message="Bitte gib eine gültige E-Mail-Adresse ein.">
<span class="field-error"></span>
</label>
<label class="auth-field">
<input type="password" id="login-password" placeholder="Passwort" required data-required-message="Passwort ist ein Pflichtfeld.">
<span class="field-error"></span>
</label>
<button id="btn-forgot-password" type="button">Passwort vergessen?</button> <button id="btn-forgot-password" type="button">Passwort vergessen?</button>
<div id="login-error" class="hidden"> <div id="login-error" class="hidden">
@@ -1001,14 +1008,80 @@
<div id="register-modal" class="modal hidden"> <div id="register-modal" class="modal hidden">
<div class="modal-content account-auth-modal"> <div class="modal-content account-auth-modal register-auth-modal">
<button id="btn-close-register" class="modal-close-btn" type="button" aria-label="Registrierung schliessen">&times;</button> <button id="btn-close-register" class="modal-close-btn" type="button" aria-label="Registrierung schliessen">&times;</button>
<h2>Registrieren</h2> <h2>Registrieren</h2>
<p class="auth-modal-subtitle">Erstelle dein Konto für schnellere Buchungen.</p> <p class="auth-modal-subtitle">Erstelle dein Konto für schnellere Buchungen.</p>
<input type="text" id="reg-firstname" placeholder="Vorname">
<input type="text" id="reg-lastname" placeholder="Nachname"> <div class="register-form-grid">
<input type="email" id="reg-email" placeholder="E-Mail"> <section class="register-form-section">
<input type="password" id="reg-password" placeholder="Passwort"> <h3>Meine Daten</h3>
<div class="gender-field auth-field" data-field-for="reg-salutation">
<input type="hidden" id="reg-salutation" required data-required-message="Anrede ist ein Pflichtfeld.">
<div class="gender-selector" role="group" aria-label="Anrede">
<button type="button" data-gender="Frau">Frau</button>
<button type="button" data-gender="Herr">Herr</button>
<button type="button" data-gender="Divers">Divers</button>
</div>
<span class="field-error"></span>
</div>
<label class="auth-field">
<input type="text" id="reg-firstname" placeholder="Vorname" required data-required-message="Vorname ist ein Pflichtfeld.">
<span class="field-error"></span>
</label>
<label class="auth-field">
<input type="text" id="reg-lastname" placeholder="Nachname" required data-required-message="Nachname ist ein Pflichtfeld.">
<span class="field-error"></span>
</label>
<label class="auth-field">
<input type="date" id="reg-birthday" placeholder="Geburtstag" required data-required-message="Geburtstag ist ein Pflichtfeld.">
<span class="field-error"></span>
</label>
<div class="register-row street-row">
<label class="auth-field">
<input type="text" id="reg-street" placeholder="Straße" required data-required-message="Straße ist ein Pflichtfeld.">
<span class="field-error"></span>
</label>
<label class="auth-field">
<input type="text" id="reg-house-number" placeholder="Nr." required data-required-message="Hausnummer ist ein Pflichtfeld.">
<span class="field-error"></span>
</label>
</div>
<div class="register-row">
<label class="auth-field">
<input type="text" id="reg-zip" placeholder="PLZ" required data-required-message="PLZ ist ein Pflichtfeld.">
<span class="field-error"></span>
</label>
<label class="auth-field">
<input type="text" id="reg-city" placeholder="Ort" required data-required-message="Ort ist ein Pflichtfeld.">
<span class="field-error"></span>
</label>
</div>
<label class="auth-field">
<select id="reg-country" required data-required-message="Land ist ein Pflichtfeld.">
<option value="Deutschland">Deutschland</option>
<option value="Österreich">Österreich</option>
<option value="Schweiz">Schweiz</option>
<option value="Luxemburg">Luxemburg</option>
</select>
<span class="field-error"></span>
</label>
</section>
<section class="register-form-section">
<h3>Anmeldeinformationen</h3>
<label class="auth-field">
<input type="email" id="reg-email" placeholder="E-Mail" required data-required-message="E-Mail ist ein Pflichtfeld." data-email-message="Bitte gib eine gültige E-Mail-Adresse ein.">
<span class="field-error"></span>
</label>
<label class="auth-field">
<input type="password" id="reg-password" placeholder="Passwort" required minlength="8" maxlength="32" data-required-message="Passwort ist ein Pflichtfeld." data-password-message="Passwort muss 8 bis 32 Zeichen haben.">
<span class="field-error"></span>
</label>
<p class="register-warning">Achte auf eine korrekte E-Mail-Adresse. An diese Adresse wird deine Buchungsbestätigung gesendet.</p>
<p class="register-warning">Passwort mit 8 bis 32 Zeichen.</p>
</section>
</div>
<button id="btn-register-save" class="auth-submit-btn" type="button">Konto erstellen</button> <button id="btn-register-save" class="auth-submit-btn" type="button">Konto erstellen</button>
</div> </div>
</div> </div>