wip: confirmation step
This commit is contained in:
@@ -41,6 +41,7 @@ trait BookingCreateTrait
|
||||
$route = match ($bookingCreateDto->currentStep) {
|
||||
2 => 'app_booking_create_step_2',
|
||||
3 => 'app_booking_create_step_3',
|
||||
4 => 'app_booking_create_step_4',
|
||||
default => 'app_booking_create_step_1',
|
||||
};
|
||||
|
||||
|
||||
@@ -49,12 +49,10 @@ class CreateStep3Controller extends AbstractController
|
||||
$form->handleRequest($request);
|
||||
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
$bookingCreateDto->currentStep = 4;
|
||||
$this->bookingService->saveBookingCreateDto($request, $bookingCreateDto);
|
||||
|
||||
// TODO: Redirect to confirmation page or direct API submission
|
||||
$this->addFlash('success', 'Zahlungsart erfolgreich ausgewählt.');
|
||||
|
||||
return $this->redirectToRoute('app_booking_create_step_3');
|
||||
return $this->redirectToRoute('app_booking_create_step_4');
|
||||
}
|
||||
|
||||
return $this->render('booking/create_step_3.html.twig', [
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Controller\Booking;
|
||||
|
||||
use App\Controller\Traits\HtmxControllerTrait;
|
||||
use App\Form\BookingCreateStep4Type;
|
||||
use App\Service\BookingService;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
/**
|
||||
* Handles the fourth step of the booking creation process (confirmation).
|
||||
*/
|
||||
class CreateStep4Controller extends AbstractController
|
||||
{
|
||||
use BookingCreateTrait;
|
||||
use BookingExceptionHandlerTrait;
|
||||
use HtmxControllerTrait;
|
||||
|
||||
public function __construct(
|
||||
private readonly BookingService $bookingService,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays booking summary and confirmation form.
|
||||
*/
|
||||
#[Route('/bookings/create/confirmation', name: 'app_booking_create_step_4')]
|
||||
public function step4(Request $request): Response
|
||||
{
|
||||
$result = $this->getOrCreateBookingCreateDto($this->bookingService, $request);
|
||||
if ($result instanceof Response) {
|
||||
return $result;
|
||||
}
|
||||
$bookingCreateDto = $result;
|
||||
|
||||
// Validate step access
|
||||
if ($redirect = $this->validateStepAccess($bookingCreateDto, 4)) {
|
||||
return $redirect;
|
||||
}
|
||||
|
||||
$form = $this->createForm(BookingCreateStep4Type::class, $bookingCreateDto);
|
||||
$form->handleRequest($request);
|
||||
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
// TODO: Perform inquiry API call
|
||||
// TODO: If inquiry successful, perform booking API call
|
||||
// TODO: Clear session and redirect to success page
|
||||
|
||||
$this->addFlash('success', 'Buchung erfolgreich abgeschlossen.');
|
||||
|
||||
return $this->redirectToRoute('app_booking_create_step_4');
|
||||
}
|
||||
|
||||
return $this->render('booking/create_step_4.html.twig', [
|
||||
'bookingCreateDto' => $bookingCreateDto,
|
||||
'form' => $form->createView(),
|
||||
...$this->getSummaryVariables($bookingCreateDto),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Form;
|
||||
|
||||
use App\Form\Model\BookingCreateDto;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
|
||||
class BookingCreateStep4Type extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder
|
||||
->add('confirmationAccepted', CheckboxType::class, [
|
||||
'label' => 'Ich bestätige, dass alle Angaben korrekt sind und möchte verbindlich buchen.',
|
||||
'mapped' => false,
|
||||
'constraints' => [
|
||||
new Assert\IsTrue(
|
||||
message: 'Bitte bestätigen Sie Ihre Buchung.'
|
||||
),
|
||||
],
|
||||
])
|
||||
;
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'data_class' => BookingCreateDto::class,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
{% extends 'layout.html.twig' %}
|
||||
|
||||
{% block title %}Buchung bestätigen{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% include '_partials/_flashes.html.twig' %}
|
||||
|
||||
<h1>Neue Buchung</h1>
|
||||
|
||||
<div class="grid grid-cols-3 gap-8">
|
||||
<div class="col-span-2">
|
||||
<h2 class="mb-6">Buchung bestätigen</h2>
|
||||
|
||||
{# Travel Summary #}
|
||||
<div class="mb-8 p-6 bg-gray-50 rounded-lg border border-gray-200">
|
||||
<h3 class="text-xl font-semibold mb-4">Reise</h3>
|
||||
<dl class="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<dt class="font-semibold text-gray-700">Reiseziel</dt>
|
||||
<dd>{{ bookingCreateDto.travel.label }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="font-semibold text-gray-700">Zeitraum</dt>
|
||||
<dd>{{ bookingCreateDto.travel.dateFrom|date('d.m.Y') }} - {{ bookingCreateDto.travel.dateTo|date('d.m.Y') }}</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
{# Rooms Summary #}
|
||||
<div class="mb-8 p-6 bg-gray-50 rounded-lg border border-gray-200">
|
||||
<h3 class="text-xl font-semibold mb-4">Zimmer / Unterkünfte</h3>
|
||||
{% if pricingData.rooms is not empty %}
|
||||
<ul class="space-y-2">
|
||||
{% for roomPricing in pricingData.rooms %}
|
||||
<li class="flex justify-between">
|
||||
<span>{{ roomPricing.quantity }}x {{ roomPricing.label }}</span>
|
||||
<span class="font-semibold">{{ roomPricing.totalPrice|number_format(2, ',', '.') }} €</span>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{# Participants Summary #}
|
||||
<div class="mb-8 p-6 bg-gray-50 rounded-lg border border-gray-200">
|
||||
<h3 class="text-xl font-semibold mb-4">Teilnehmer ({{ participantsCount }})</h3>
|
||||
{% for participant in bookingCreateDto.participants %}
|
||||
<div class="mb-6 pb-6 {% if not loop.last %}border-b border-gray-300{% endif %}">
|
||||
<h4 class="font-semibold text-lg mb-3">
|
||||
{{ participant.firstName }} {{ participant.lastName }}
|
||||
{% if loop.first %}<span class="text-sm text-gray-600">(Anmelder)</span>{% endif %}
|
||||
</h4>
|
||||
|
||||
<dl class="grid grid-cols-2 gap-2 text-sm">
|
||||
{% if participant.dateOfBirth %}
|
||||
<div>
|
||||
<dt class="text-gray-600">Geburtsdatum</dt>
|
||||
<dd>{{ participant.dateOfBirth|date('d.m.Y') }}</dd>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if participant.email %}
|
||||
<div>
|
||||
<dt class="text-gray-600">E-Mail</dt>
|
||||
<dd>{{ participant.email }}</dd>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if participant.mobile %}
|
||||
<div>
|
||||
<dt class="text-gray-600">Telefon</dt>
|
||||
<dd>{{ participant.mobile }}</dd>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{# Selected Services #}
|
||||
{% set services = [] %}
|
||||
{% if participant.board is not empty %}
|
||||
{% for boardItem in participant.board %}
|
||||
{% set services = services|merge(['Verpflegung: ' ~ boardItem.label]) %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if participant.skiPass %}
|
||||
{% set services = services|merge(['Skipass: ' ~ participant.skiPass.label]) %}
|
||||
{% endif %}
|
||||
{% if participant.rentals is not empty %}
|
||||
{% for rental in participant.rentals %}
|
||||
{% set services = services|merge(['Ausrüstung: ' ~ rental.label]) %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if participant.courses is not empty %}
|
||||
{% for course in participant.courses %}
|
||||
{% set services = services|merge(['Kurs: ' ~ course.label]) %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if participant.additionalServices is not empty %}
|
||||
{% for service in participant.additionalServices %}
|
||||
{% set services = services|merge(['Service: ' ~ service.label]) %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if participant.insurance %}
|
||||
{% set services = services|merge(['Versicherung: ' ~ participant.insurance.label]) %}
|
||||
{% endif %}
|
||||
|
||||
{% if services is not empty %}
|
||||
<div class="col-span-2">
|
||||
<dt class="text-gray-600 mb-1">Gebuchte Leistungen</dt>
|
||||
<dd>
|
||||
<ul class="list-disc list-inside">
|
||||
{% for service in services %}
|
||||
<li>{{ service }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</dd>
|
||||
</div>
|
||||
{% endif %}
|
||||
</dl>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
{# Payment Summary #}
|
||||
<div class="mb-8 p-6 bg-gray-50 rounded-lg border border-gray-200">
|
||||
<h3 class="text-xl font-semibold mb-4">Zahlungsart</h3>
|
||||
<dl class="space-y-2">
|
||||
<div>
|
||||
<dt class="font-semibold text-gray-700">Gewählte Zahlungsart</dt>
|
||||
<dd>
|
||||
{% if bookingCreateDto.paymentMethod == constant('App\\BusProNet\\Constants::PAYMENT_METHOD_DEBIT') %}
|
||||
Lastschrift (Einzugsermächtigungsverfahren)
|
||||
{% else %}
|
||||
Überweisung
|
||||
{% endif %}
|
||||
</dd>
|
||||
</div>
|
||||
{% if bookingCreateDto.paymentMethod == constant('App\\BusProNet\\Constants::PAYMENT_METHOD_DEBIT') and bookingCreateDto.bankAccount %}
|
||||
<div>
|
||||
<dt class="font-semibold text-gray-700">IBAN</dt>
|
||||
<dd class="font-mono">{{ bookingCreateDto.bankAccount.iban }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="font-semibold text-gray-700">Kontoinhaber</dt>
|
||||
<dd>{{ bookingCreateDto.bankAccount.accountHolder }}</dd>
|
||||
</div>
|
||||
{% endif %}
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
{# Confirmation Form #}
|
||||
{{ form_start(form, {'attr': {'novalidate': 'novalidate'}}) }}
|
||||
|
||||
<div class="mb-8 p-6 bg-blue-50 border border-blue-200 rounded-lg">
|
||||
{{ form_row(form.confirmationAccepted, {
|
||||
'label_attr': {'class': 'text-base font-semibold'}
|
||||
}) }}
|
||||
</div>
|
||||
|
||||
<div class="flex justify-between pt-6">
|
||||
<a href="{{ path('app_booking_create_step_3') }}" class="button bg-button bg-button--secondary">Zurück</a>
|
||||
<button type="submit" class="button bg-button bg-button--primary">Verbindlich buchen</button>
|
||||
</div>
|
||||
|
||||
{{ form_end(form) }}
|
||||
</div>
|
||||
|
||||
<div id="booking-summary">
|
||||
{% include 'booking/_summary.html.twig' with {
|
||||
'bookingCreateDto': bookingCreateDto,
|
||||
'participantCount': participantsCount,
|
||||
'groupedSelectedRooms': groupedSelectedRooms,
|
||||
'assignmentCounts': assignmentCounts
|
||||
} %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user