feat: Implement booking package and flow mappings with review page

This commit is contained in:
2026-05-22 23:40:22 +02:00
parent df30cee1a2
commit beb9cb34f4
3 changed files with 229 additions and 26 deletions
+20 -11
View File
@@ -483,7 +483,7 @@
</div>
<div class="package-card__price">
<span>150 €</span>
<small>/ Jahr</small>
<small>/ Jahr ab 400 Schüler</small>
</div>
</div>
<p class="package-card__description">Ein spezialisiertes Modul für Bibliotheken und Medienzentren mit Ausleihprozessen, Medienbeständen und historischer Transparenz.</p>
@@ -520,37 +520,46 @@
<div class="custom-modal-backdrop" onclick="closeBookingModal()"></div>
<div class="custom-modal-content">
<h3>Buchung bestätigen</h3>
<p>Möchten Sie dieses Paket wirklich anfragen?</p>
<p style="font-size: 0.95rem;">Ein Administrator wird sich innerhalb von ca. 48 Stunden über das Portal mit Ihnen in Verbindung setzen. Dort wird dann ein Termin ausgemacht, um die Buchung persönlich vor Ort zu besprechen oder einen direkten Start zu vereinbaren.</p>
<p>Möchten Sie dieses Paket jetzt direkt buchen oder zuerst einen Gesprächstermin vereinbaren?</p>
<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" type="button" id="confirmBookingBtn">Anfrage bestätigen</button>
<button class="btn secondary" type="button" onclick="goToBookingReview('consultation')">Gesprächstermin vereinbaren</button>
<button class="btn" type="button" onclick="goToBookingReview('payment')">Buchen</button>
</div>
</div>
</div>
<script>
let currentFormToSubmit = null;
const bookingReviewBaseUrl = "{{ url_for('booking_payment') }}";
let currentBookingForm = null;
function openBookingModal(formElement) {
currentFormToSubmit = formElement;
currentBookingForm = formElement;
const modal = document.getElementById('bookingModal');
modal.classList.add('active');
modal.setAttribute('aria-hidden', 'false');
}
function closeBookingModal() {
currentFormToSubmit = null;
currentBookingForm = null;
const modal = document.getElementById('bookingModal');
modal.classList.remove('active');
modal.setAttribute('aria-hidden', 'true');
}
document.getElementById('confirmBookingBtn').addEventListener('click', function() {
if (currentFormToSubmit) {
currentFormToSubmit.submit();
function goToBookingReview(flow) {
if (!currentBookingForm) {
return;
}
});
const packageInput = currentBookingForm.querySelector('input[name="package"]');
const packageValue = packageInput ? packageInput.value : '';
const reviewUrl = new URL(bookingReviewBaseUrl, window.location.origin);
reviewUrl.searchParams.set('package', packageValue);
reviewUrl.searchParams.set('flow', flow);
window.location.href = reviewUrl.toString();
}
document.addEventListener('keydown', function(event) {
if (event.key === 'Escape') {
+135
View File
@@ -0,0 +1,135 @@
{% extends "base.html" %}
{% block title %}Buchung prüfen{% endblock %}
{% block head %}
{{ super() }}
<style>
.booking-review {
max-width: 920px;
margin: 2rem auto 3rem;
padding: 0;
}
.booking-review__panel {
background: linear-gradient(180deg, rgba(255, 255, 255, 0.98), rgba(244, 247, 244, 0.96));
border: 1px solid rgba(28, 90, 99, 0.14);
border-radius: 26px;
padding: clamp(1.4rem, 2.6vw, 2.2rem);
box-shadow: 0 18px 44px rgba(18, 49, 54, 0.08);
}
.booking-review__eyebrow {
display: inline-flex;
align-items: center;
gap: 0.45rem;
padding: 0.42rem 0.82rem;
border-radius: 999px;
background: #e8f4f4;
color: #166271;
font-weight: 700;
margin-bottom: 1rem;
}
.booking-review__lead {
margin-top: 0.75rem;
max-width: 66ch;
}
.booking-review__grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 1rem;
margin: 1.5rem 0;
}
.booking-review__card {
background: #ffffff;
border: 1px solid #dbe6e7;
border-radius: 18px;
padding: 1rem 1.1rem;
}
.booking-review__label {
display: block;
font-size: 0.88rem;
font-weight: 700;
letter-spacing: 0.03em;
text-transform: uppercase;
color: #5e757d;
margin-bottom: 0.45rem;
}
.booking-review__value {
color: #173a42;
font-weight: 700;
line-height: 1.45;
}
.booking-review__note {
padding: 1rem 1.1rem;
border-radius: 18px;
background: #f8fbfb;
border: 1px solid #dbe6e7;
color: #49626b;
line-height: 1.65;
}
.booking-review__actions {
display: flex;
flex-wrap: wrap;
gap: 0.85rem;
margin-top: 1.5rem;
}
.booking-review__actions .btn,
.booking-review__actions a {
width: auto;
min-width: 190px;
}
@media (max-width: 720px) {
.booking-review__grid {
grid-template-columns: 1fr;
}
.booking-review__actions .btn,
.booking-review__actions a {
width: 100%;
min-width: 0;
}
}
</style>
{% endblock %}
{% block content %}
<section class="booking-review">
<div class="booking-review__panel">
<span class="booking-review__eyebrow">Buchung prüfen</span>
<h1>{{ title }}</h1>
<p class="booking-review__lead">{{ intro }}</p>
<div class="booking-review__grid" aria-label="Buchungsübersicht">
<div class="booking-review__card">
<span class="booking-review__label">Paket</span>
<div class="booking-review__value">{{ package }}</div>
</div>
<div class="booking-review__card">
<span class="booking-review__label">Angemeldet als</span>
<div class="booking-review__value">{{ display_name }}</div>
</div>
</div>
<div class="booking-review__note">
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">
<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>
</form>
</div>
</section>
{% endblock %}