feat: Enhance booking flow with form data handling and validation
This commit is contained in:
+40
-1
@@ -1862,6 +1862,20 @@ def booking_payment():
|
||||
return redirect(url_for("preise"))
|
||||
|
||||
flow_config = BOOKING_FLOW_MAP[booking_flow]
|
||||
form_data = {
|
||||
"contact_person": _sanitize_text(request.values.get("contact_person") or "", 120),
|
||||
"school_name": _sanitize_text(request.values.get("school_name") or "", 160),
|
||||
"contact_email": _sanitize_text(request.values.get("contact_email") or "", 160).lower(),
|
||||
"contact_phone": _sanitize_text(request.values.get("contact_phone") or "", 60),
|
||||
"billing_street": _sanitize_text(request.values.get("billing_street") or "", 160),
|
||||
"billing_zip": _sanitize_text(request.values.get("billing_zip") or "", 20),
|
||||
"billing_city": _sanitize_text(request.values.get("billing_city") or "", 120),
|
||||
"notes": _sanitize_text(request.values.get("notes") or "", 600),
|
||||
}
|
||||
|
||||
if request.method == "GET" and not form_data["contact_person"]:
|
||||
form_data["contact_person"] = session.get("display_name") or session.get("username") or ""
|
||||
|
||||
review_data = {
|
||||
"package": selected_package,
|
||||
"package_key": package_raw,
|
||||
@@ -1871,13 +1885,37 @@ def booking_payment():
|
||||
"confirm_label": flow_config["confirm_label"],
|
||||
"username": session.get("username"),
|
||||
"display_name": session.get("display_name") or session.get("username"),
|
||||
"form_data": form_data,
|
||||
}
|
||||
|
||||
if request.method == "GET":
|
||||
return render_template("schnell_buchung.html", **review_data)
|
||||
|
||||
required_fields = {
|
||||
"contact_person": "Ansprechpartner",
|
||||
"school_name": "Schule/Einrichtung",
|
||||
"contact_email": "E-Mail",
|
||||
"billing_street": "Straße",
|
||||
"billing_zip": "PLZ",
|
||||
"billing_city": "Ort",
|
||||
}
|
||||
missing_labels = [label for key, label in required_fields.items() if not form_data.get(key)]
|
||||
if missing_labels:
|
||||
flash(f"Bitte folgende Pflichtfelder ausfüllen: {', '.join(missing_labels)}.", "error")
|
||||
return render_template("schnell_buchung.html", **review_data)
|
||||
|
||||
if not _validate_email(form_data["contact_email"]):
|
||||
flash("Bitte eine gültige E-Mail-Adresse angeben.", "error")
|
||||
return render_template("schnell_buchung.html", **review_data)
|
||||
|
||||
message = (
|
||||
f"{flow_config['message_prefix']} {selected_package} {flow_config['message_suffix']}"
|
||||
f"{flow_config['message_prefix']} {selected_package} {flow_config['message_suffix']}\n"
|
||||
f"Ansprechpartner: {form_data['contact_person']}\n"
|
||||
f"Schule: {form_data['school_name']}\n"
|
||||
f"E-Mail: {form_data['contact_email']}\n"
|
||||
f"Telefon: {form_data['contact_phone'] or '-'}\n"
|
||||
f"Rechnungsadresse: {form_data['billing_street']}, {form_data['billing_zip']} {form_data['billing_city']}\n"
|
||||
f"Notizen: {form_data['notes'] or '-'}"
|
||||
)
|
||||
|
||||
client = None
|
||||
@@ -1892,6 +1930,7 @@ def booking_payment():
|
||||
"created_at": _utc_now_iso(),
|
||||
"booking_flow": booking_flow,
|
||||
"booking_package": package_raw,
|
||||
"booking_data": form_data,
|
||||
}
|
||||
)
|
||||
except PyMongoError:
|
||||
|
||||
@@ -524,7 +524,7 @@
|
||||
<p style="font-size: 0.95rem;">In beiden Fällen werden Ihre Angaben noch einmal geprüft, bevor die Buchung per POST an /booking/payment gesendet wird.</p>
|
||||
<div class="custom-modal-actions">
|
||||
<button class="btn secondary" type="button" onclick="closeBookingModal()">Abbrechen</button>
|
||||
<button class="btn secondary" type="button" onclick="goToBookingReview('consultation')">Gesprächstermin vereinbaren</button>
|
||||
<button class="btn" type="button" onclick="goToBookingReview('consultation')">Gesprächstermin vereinbaren</button>
|
||||
<button class="btn" type="button" onclick="goToBookingReview('payment')">Buchen</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -75,6 +75,43 @@
|
||||
line-height: 1.65;
|
||||
}
|
||||
|
||||
.booking-review__form-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 0.9rem;
|
||||
margin-top: 1.25rem;
|
||||
}
|
||||
|
||||
.booking-review__field {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.35rem;
|
||||
}
|
||||
|
||||
.booking-review__field--full {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
|
||||
.booking-review__field label {
|
||||
font-weight: 700;
|
||||
color: #2b4f58;
|
||||
font-size: 0.93rem;
|
||||
}
|
||||
|
||||
.booking-review__field input,
|
||||
.booking-review__field textarea {
|
||||
border: 1px solid #cedbdd;
|
||||
border-radius: 12px;
|
||||
background: #ffffff;
|
||||
color: #173a42;
|
||||
padding: 0.75rem 0.82rem;
|
||||
}
|
||||
|
||||
.booking-review__field textarea {
|
||||
min-height: 110px;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
.booking-review__actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
@@ -93,6 +130,10 @@
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.booking-review__form-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.booking-review__actions .btn,
|
||||
.booking-review__actions a {
|
||||
width: 100%;
|
||||
@@ -124,11 +165,49 @@
|
||||
Bitte prüfen Sie die Angaben noch einmal. Wenn alles passt, senden Sie die Buchung mit dem folgenden Button per POST an /booking/payment.
|
||||
</div>
|
||||
|
||||
<form method="POST" action="{{ url_for('booking_payment') }}" class="booking-review__actions">
|
||||
<form method="POST" action="{{ url_for('booking_payment') }}">
|
||||
<input type="hidden" name="package" value="{{ package_key }}">
|
||||
<input type="hidden" name="flow" value="{{ flow }}">
|
||||
<button class="btn" type="submit">{{ confirm_label }}</button>
|
||||
<a class="btn secondary" href="{{ url_for('preise') }}">Zurück zu den Preisen</a>
|
||||
|
||||
<div class="booking-review__form-grid" aria-label="Pflichtdaten für Buchung">
|
||||
<div class="booking-review__field">
|
||||
<label for="contact_person">Ansprechpartner *</label>
|
||||
<input id="contact_person" name="contact_person" type="text" required maxlength="120" value="{{ form_data.contact_person }}">
|
||||
</div>
|
||||
<div class="booking-review__field">
|
||||
<label for="school_name">Schule/Einrichtung *</label>
|
||||
<input id="school_name" name="school_name" type="text" required maxlength="160" value="{{ form_data.school_name }}">
|
||||
</div>
|
||||
<div class="booking-review__field">
|
||||
<label for="contact_email">E-Mail *</label>
|
||||
<input id="contact_email" name="contact_email" type="email" required maxlength="160" value="{{ form_data.contact_email }}">
|
||||
</div>
|
||||
<div class="booking-review__field">
|
||||
<label for="contact_phone">Telefon</label>
|
||||
<input id="contact_phone" name="contact_phone" type="text" maxlength="60" value="{{ form_data.contact_phone }}">
|
||||
</div>
|
||||
<div class="booking-review__field booking-review__field--full">
|
||||
<label for="billing_street">Straße *</label>
|
||||
<input id="billing_street" name="billing_street" type="text" required maxlength="160" value="{{ form_data.billing_street }}">
|
||||
</div>
|
||||
<div class="booking-review__field">
|
||||
<label for="billing_zip">PLZ *</label>
|
||||
<input id="billing_zip" name="billing_zip" type="text" required maxlength="20" value="{{ form_data.billing_zip }}">
|
||||
</div>
|
||||
<div class="booking-review__field">
|
||||
<label for="billing_city">Ort *</label>
|
||||
<input id="billing_city" name="billing_city" type="text" required maxlength="120" value="{{ form_data.billing_city }}">
|
||||
</div>
|
||||
<div class="booking-review__field booking-review__field--full">
|
||||
<label for="notes">Zusatzinformationen</label>
|
||||
<textarea id="notes" name="notes" maxlength="600" placeholder="Optional: gewünschter Starttermin, besondere Anforderungen, Rückrufzeiten">{{ form_data.notes }}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="booking-review__actions">
|
||||
<button class="btn" type="submit">{{ confirm_label }}</button>
|
||||
<a class="btn secondary" href="{{ url_for('preise') }}">Zurück zu den Preisen</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
Reference in New Issue
Block a user