Vertragsverwaltung_Plusfit24/views/signup.ejs
2026-03-28 08:20:24 +00:00

482 lines
20 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>PlusFit24 Mitgliedschaft abschließen</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;600;700;800&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/css/style.css">
</head>
<body>
<header class="site-header">
<div class="header-inner">
<div class="logo">Plusfit<span>24</span></div>
</div>
</header>
<main class="signup-main">
<!-- Fortschrittsleiste -->
<nav class="progress-nav" id="progressNav">
<span class="step" data-step="0">TARIF</span>
<span class="step-dot"></span>
<span class="step active" data-step="1">4</span>
<span class="step-dot"></span>
<span class="step" data-step="2">ANSCHRIFT</span>
<span class="step-dot"></span>
<span class="step" data-step="3">LASTSCHRIFT</span>
<span class="step-dot"></span>
<span class="step" data-step="4">ABSCHLUSS</span>
</nav>
<div class="signup-container">
<!-- ===== SCHRITT 1: Persönliche Daten ===== -->
<div class="form-step active" id="step1">
<h2 class="step-title italic">Geburtsdatum</h2>
<div class="salutation-group">
<button type="button" class="sal-btn" data-value="Herr" onclick="selectSalutation(this)">Herr</button>
<button type="button" class="sal-btn" data-value="Frau" onclick="selectSalutation(this)">Frau</button>
<button type="button" class="sal-btn" data-value="Keine Angabe" onclick="selectSalutation(this)">Keine Angabe</button>
</div>
<div class="form-group">
<label>Titel</label>
<div class="input-wrap">
<span class="input-icon">👤</span>
<input type="text" id="title" placeholder="Dein Titel">
</div>
</div>
<div class="form-group">
<label>Vorname <span class="req">*</span></label>
<div class="input-wrap">
<span class="input-icon">👤</span>
<input type="text" id="first_name" placeholder="Dein Vorname" required>
</div>
</div>
<div class="form-group">
<label>Nachname <span class="req">*</span></label>
<div class="input-wrap">
<span class="input-icon">👤</span>
<input type="text" id="last_name" placeholder="Dein Nachname" required>
</div>
</div>
<div class="form-group">
<label>Geburtsdatum <span class="req">*</span></label>
<div class="input-wrap">
<span class="input-icon">📅</span>
<input type="date" id="birth_date" placeholder="TT.mm.jjjj" required>
</div>
</div>
<div class="form-group">
<label>Email <span class="req">*</span></label>
<div class="input-wrap">
<span class="input-icon">✉️</span>
<input type="email" id="email" placeholder="Deine Email" required>
<span class="email-status" id="emailStatus"></span>
</div>
<div class="email-message" id="emailMessage"></div>
</div>
<div class="form-group">
<label>Telefon</label>
<div class="input-wrap">
<span class="input-icon">📞</span>
<input type="tel" id="phone" placeholder="Deine Telefon-/ Handynummer">
</div>
</div>
<div class="minor-warning hidden" id="minorWarning">
<div class="warning-box">
<strong>⚠️ Hinweis für Minderjährige</strong>
<p>Da du noch nicht 18 Jahre alt bist, benötigst du die Einverständniserklärung deiner Erziehungsberechtigten. Du wirst am Ende aufgefordert, diese zu bestätigen.</p>
</div>
</div>
<div class="step-buttons">
<a href="/" class="btn btn-outline">Zurück</a>
<button type="button" class="btn btn-primary" onclick="validateStep1()">Weiter</button>
</div>
</div>
<!-- ===== SCHRITT 2: Anschrift ===== -->
<div class="form-step" id="step2">
<h2 class="step-title bold">Anschrift</h2>
<p class="step-subtitle italic">Wo wohnst du?</p>
<div class="form-group">
<label>Deine Adresse <span class="req">*</span></label>
<div class="input-wrap">
<span class="input-icon">📍</span>
<input type="text" id="street" placeholder="Straße, Hausnummer" required>
</div>
</div>
<div class="form-group">
<div class="input-wrap">
<span class="input-icon">📍</span>
<input type="text" id="address_addition" placeholder="Adress-Zusatz">
</div>
</div>
<div class="form-group">
<div class="input-wrap">
<span class="input-icon">📍</span>
<input type="text" id="zip" placeholder="PLZ" maxlength="5" required>
</div>
</div>
<div class="form-group">
<div class="input-wrap">
<span class="input-icon">📍</span>
<input type="text" id="city" placeholder="Ort" required>
</div>
</div>
<div class="step-buttons">
<button type="button" class="btn btn-outline" onclick="goToStep(1)">Zurück</button>
<button type="button" class="btn btn-primary" onclick="validateStep2()">Weiter</button>
</div>
</div>
<!-- ===== SCHRITT 3: Lastschrift ===== -->
<div class="form-step" id="step3">
<h2 class="step-title bold">Lastschrift</h2>
<p class="step-subtitle italic">Damit wir deinen Mitgliedsbeitrag abbuchen können</p>
<div class="sepa-mandate">
<label class="checkbox-label">
<input type="checkbox" id="sepa_accepted">
<span class="checkbox-custom"></span>
<span>Ich ermächtige PlusFit24 UG - NL: Moosleiten 12 84089 Aiglsbach, Zahlungen von meinem Konto mittels Lastschrift einzuziehen. Zugleich weise ich mein Kreditinstitut an, die von PlusFit24 UG - NL: Moosleiten 12 84089 Aiglsbach auf mein Konto gezogenen Lastschriften einzulösen. Hinweis: Ich kann innerhalb von acht Wochen, beginnend mit dem Belastungsdatum, die Erstattung des belasteten Betrages verlangen. Es gelten dabei die mit meinem Kreditinstitut vereinbarten Bedingungen. Gläubiger-Identifikationsnummer: DE1200100002549495</span>
</label>
</div>
<label class="form-label-section">Sepa Lastschriftmandat</label>
<div class="form-group">
<div class="input-wrap">
<span class="input-icon">🏦</span>
<input type="text" id="bank_name" placeholder="Geldinstitut">
</div>
</div>
<div class="form-group">
<div class="input-wrap">
<span class="input-icon">👤</span>
<input type="text" id="account_holder" placeholder="Name des Kontoinhabers">
</div>
</div>
<div class="form-group">
<div class="input-wrap">
<span class="input-icon">💳</span>
<input type="text" id="iban" placeholder="DE00 0000 0000 0000 0000 00" maxlength="34" autocomplete="off">
<span class="email-status" id="ibanStatus"></span>
</div>
<div class="email-message" id="ibanMessage"></div>
</div>
<div class="step-buttons">
<button type="button" class="btn btn-outline" onclick="goToStep(2)">Zurück</button>
<button type="button" class="btn btn-primary" onclick="validateStep3()">Weiter</button>
</div>
</div>
<!-- ===== SCHRITT 4: Abschluss ===== -->
<div class="form-step" id="step4">
<h2 class="step-title bold">Abschluss</h2>
<p class="step-subtitle italic">Alles auf einen Blick</p>
<div class="abschluss-grid">
<!-- Tarif Übersicht -->
<div class="tarif-summary-card">
<div class="summary-badge">◑ Wir freuen uns auf dich!</div>
<h3>Dein Wunsch-Tarif</h3>
<div class="summary-feature">
<span class="feature-icon">📦</span>
<span>Laufzeit (Monate): <%= tariff.duration_months %></span>
</div>
<div class="summary-feature">
<span class="feature-icon">📦</span>
<span>Tarif: <%= tariff.name %></span>
</div>
<div class="summary-feature">
<span class="feature-icon">📦</span>
<span>Zusätzlich: Startpaket <%= Number(tariff.start_package_price).toFixed(2).replace('.', ',') %>€ einmalig</span>
</div>
<div class="summary-price">
<span class="price-big"><%= Number(tariff.price_monthly).toFixed(2).replace('.', ',') %>€</span>
<span class="price-period">/Monat</span>
</div>
</div>
<!-- Letzter Schritt -->
<div class="final-checks">
<h3>Letzter Schritt</h3>
<div class="doc-buttons">
<a href="https://plusfit24.de/wp-content/uploads/2022/11/AG_PlusFit24.pdf" target="_blank" class="doc-btn">Widerrufsbelehrung</a>
<a href="https://plusfit24.de/datenschutz-2/" target="_blank" class="doc-btn">Datenschutz</a>
<a href="https://plusfit24.de/wp-content/uploads/2022/11/AG_PlusFit24.pdf" target="_blank" class="doc-btn">AGB</a>
</div>
<label class="checkbox-label">
<input type="checkbox" id="agb_accepted">
<span class="checkbox-custom"></span>
<span>Ich habe die AGBs und Widerrufsbelehrung gelesen und akzeptiert</span>
</label>
<label class="checkbox-label">
<input type="checkbox" id="datenschutz_accepted">
<span class="checkbox-custom"></span>
<span>Ich akzeptiere die Erhebung und Verarbeitung meiner eingegebenen Daten</span>
</label>
<label class="checkbox-label">
<input type="checkbox" id="data_correct">
<span class="checkbox-custom"></span>
<span>Ich bestätige die Richtigkeit meiner Angaben</span>
</label>
<!-- Einverständniserklärung für Minderjährige -->
<div id="guardianSection" class="hidden">
<div class="minor-info-box">
<strong>Einverständniserklärung der Erziehungsberechtigten</strong>
<p>Da du noch nicht 18 Jahre alt bist, muss ein Erziehungsberechtigter zustimmen.</p>
<a href="/pdfs/Einverstaendniserklaerung.pdf" target="_blank" class="doc-btn">📄 Einverständniserklärung öffnen</a>
</div>
<label class="checkbox-label">
<input type="checkbox" id="guardian_consent">
<span class="checkbox-custom"></span>
<span>Die Einverständniserklärung der Erziehungsberechtigten liegt vor und wurde unterschrieben.</span>
</label>
</div>
</div>
</div>
<div class="submit-section">
<p class="submit-notice"><strong>Schließe deinen Mitgliedschaftsvertrag mit einem Klick auf den untenstehenden Button verbindlich ab.</strong></p>
<div id="submitError" class="alert alert-error hidden"></div>
<button type="button" class="btn btn-submit" id="submitBtn" onclick="submitForm()">
Kostenpflichtige Mitgliedschaft abschließen
</button>
<p class="ssl-notice">🔒 Deine Daten werden ausschließlich verschlüsselt übermittelt.</p>
</div>
<div class="step-buttons">
<button type="button" class="btn btn-outline" onclick="goToStep(3)">Zurück</button>
</div>
</div>
</div>
</main>
<footer class="site-footer">
<p>© 2024 PlusFit24 UG · <a href="https://plusfit24.de/datenschutz-2/" target="_blank">Datenschutz</a> · <a href="https://plusfit24.de/wp-content/uploads/2022/11/AG_PlusFit24.pdf" target="_blank">AGB</a></p>
</footer>
<script src="/js/iban.js"></script>
<script>
const TARIFF_ID = '<%= tariff.id %>';
let currentStep = 1;
let salutation = '';
let emailVerified = false;
let emailVerifyTimer = null;
let isMinor = false;
// Anrede auswählen
function selectSalutation(btn) {
document.querySelectorAll('.sal-btn').forEach(b => b.classList.remove('active'));
btn.classList.add('active');
salutation = btn.dataset.value;
}
// Schritt wechseln
function goToStep(step) {
document.querySelectorAll('.form-step').forEach(s => s.classList.remove('active'));
document.getElementById('step' + step).classList.add('active');
// Progress nav aktualisieren
const steps = document.querySelectorAll('.progress-nav .step');
steps.forEach((s, i) => {
s.classList.toggle('active', i === step);
s.classList.toggle('done', i < step);
});
currentStep = step;
window.scrollTo({ top: 0, behavior: 'smooth' });
}
// E-Mail Verifizierung
document.getElementById('email').addEventListener('input', function() {
clearTimeout(emailVerifyTimer);
emailVerified = false;
document.getElementById('emailStatus').textContent = '';
document.getElementById('emailMessage').textContent = '';
const email = this.value.trim();
if (email.includes('@') && email.includes('.')) {
document.getElementById('emailStatus').textContent = '⏳';
emailVerifyTimer = setTimeout(() => verifyEmail(email), 800);
}
});
async function verifyEmail(email) {
try {
const res = await fetch('/api/verify-email', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email })
});
const data = await res.json();
if (data.valid) {
emailVerified = true;
document.getElementById('emailStatus').textContent = '✅';
document.getElementById('emailMessage').textContent = '';
document.getElementById('emailMessage').className = 'email-message';
} else {
emailVerified = false;
document.getElementById('emailStatus').textContent = '❌';
document.getElementById('emailMessage').textContent = data.reason;
document.getElementById('emailMessage').className = 'email-message error';
}
} catch (e) {
document.getElementById('emailStatus').textContent = '⚠️';
}
}
// Geburtsdatum → Minderjähriger prüfen
document.getElementById('birth_date').addEventListener('change', function() {
const bd = new Date(this.value);
const today = new Date();
let age = today.getFullYear() - bd.getFullYear();
const m = today.getMonth() - bd.getMonth();
if (m < 0 || (m === 0 && today.getDate() < bd.getDate())) age--;
isMinor = age < 18;
document.getElementById('minorWarning').classList.toggle('hidden', !isMinor);
});
// Schritt 1 validieren
async function validateStep1() {
const firstName = document.getElementById('first_name').value.trim();
const lastName = document.getElementById('last_name').value.trim();
const birthDate = document.getElementById('birth_date').value;
const email = document.getElementById('email').value.trim();
if (!firstName || !lastName) return alert('Bitte Vor- und Nachname eingeben.');
if (!birthDate) return alert('Bitte Geburtsdatum eingeben.');
if (!email) return alert('Bitte E-Mail-Adresse eingeben.');
// Alter prüfen
const bd = new Date(birthDate);
const today = new Date();
let age = today.getFullYear() - bd.getFullYear();
const m = today.getMonth() - bd.getMonth();
if (m < 0 || (m === 0 && today.getDate() < bd.getDate())) age--;
if (age < 14) return alert('Das Mindestalter für eine Mitgliedschaft beträgt 14 Jahre.');
// E-Mail verifizieren falls noch nicht geschehen
if (!emailVerified) {
document.getElementById('emailStatus').textContent = '⏳';
await verifyEmail(email);
if (!emailVerified) return alert('Bitte eine gültige E-Mail-Adresse eingeben.');
}
goToStep(2);
}
// Schritt 2 validieren
function validateStep2() {
const street = document.getElementById('street').value.trim();
const zip = document.getElementById('zip').value.trim();
const city = document.getElementById('city').value.trim();
if (!street || !zip || !city) return alert('Bitte Straße, PLZ und Ort ausfüllen.');
goToStep(3);
}
// Schritt 3 validieren
function validateStep3() {
// Sepa ist optional aber Checkbox sollte gecheckt sein wenn Daten eingegeben
// Zeige Minderjährigen-Abschnitt im Abschluss
if (isMinor) {
document.getElementById('guardianSection').classList.remove('hidden');
}
goToStep(4);
}
// Formular absenden
async function submitForm() {
if (!document.getElementById('agb_accepted').checked) return alert('Bitte AGBs und Widerrufsbelehrung akzeptieren.');
if (!document.getElementById('datenschutz_accepted').checked) return alert('Bitte Datenschutzerklärung akzeptieren.');
if (!document.getElementById('data_correct').checked) return alert('Bitte die Richtigkeit Ihrer Angaben bestätigen.');
if (isMinor && !document.getElementById('guardian_consent').checked) {
return alert('Bei Minderjährigen ist die Einverständniserklärung der Erziehungsberechtigten erforderlich.');
}
const btn = document.getElementById('submitBtn');
btn.disabled = true;
btn.textContent = 'Wird gesendet...';
const errorDiv = document.getElementById('submitError');
errorDiv.classList.add('hidden');
const payload = {
tariff_id: TARIFF_ID,
salutation: salutation,
title: document.getElementById('title').value.trim(),
first_name: document.getElementById('first_name').value.trim(),
last_name: document.getElementById('last_name').value.trim(),
birth_date: document.getElementById('birth_date').value,
email: document.getElementById('email').value.trim(),
phone: document.getElementById('phone').value.trim(),
street: document.getElementById('street').value.trim(),
address_addition: document.getElementById('address_addition').value.trim(),
zip: document.getElementById('zip').value.trim(),
city: document.getElementById('city').value.trim(),
bank_name: document.getElementById('bank_name').value.trim(),
account_holder: document.getElementById('account_holder').value.trim(),
iban: document.getElementById('iban').value.trim().replace(/\s/g, ''),
sepa_accepted: document.getElementById('sepa_accepted').checked,
agb_accepted: document.getElementById('agb_accepted').checked,
datenschutz_accepted: document.getElementById('datenschutz_accepted').checked,
data_correct: document.getElementById('data_correct').checked,
guardian_consent: isMinor ? document.getElementById('guardian_consent').checked : false
};
try {
const res = await fetch('/api/submit-membership', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
});
const data = await res.json();
if (data.success) {
window.location.href = data.pending ? '/bestaetigung-ausstehend?email=' + encodeURIComponent(document.getElementById('email').value) : '/erfolg';
} else {
errorDiv.textContent = data.error || 'Fehler beim Absenden.';
errorDiv.classList.remove('hidden');
btn.disabled = false;
btn.textContent = 'Kostenpflichtige Mitgliedschaft abschließen';
}
} catch (e) {
errorDiv.textContent = 'Netzwerkfehler. Bitte versuche es erneut.';
errorDiv.classList.remove('hidden');
btn.disabled = false;
btn.textContent = 'Kostenpflichtige Mitgliedschaft abschließen';
}
}
// IBAN Validierung (iban.js wird vor diesem Script geladen)
const ibanInput = document.getElementById('iban');
if (ibanInput) {
attachIBANValidation(ibanInput, document.getElementById('ibanStatus'), document.getElementById('ibanMessage'));
}
</script>
</html>