feat: improved promotional and purchase vouchers
This commit is contained in:
@@ -150,10 +150,20 @@ class Step2Controller extends AbstractController
|
||||
|
||||
$form->handleRequest($request);
|
||||
|
||||
// Collect notifications from field handlers (run during PRE_SUBMIT)
|
||||
$notifications = $this->collectAndClearNotifications($bookingDto);
|
||||
|
||||
if (true === $form->isSubmitted() && true === $form->isValid()) {
|
||||
// Save BookingDto to session
|
||||
$this->bookingService->saveBookingDto($request, $bookingDto, BookingDto::MODE_CREATE);
|
||||
|
||||
// Add notifications as flash messages (redirects destroy HTMX-triggered toasts)
|
||||
if (false === empty($notifications)) {
|
||||
foreach ($notifications as $notification) {
|
||||
$this->addFlash($notification['type'], $notification['message']);
|
||||
}
|
||||
}
|
||||
|
||||
// HTMX redirect to cards view
|
||||
return $this->hxRedirect($request, $this->generateUrl('app_booking_create_step_2'));
|
||||
}
|
||||
@@ -174,12 +184,21 @@ class Step2Controller extends AbstractController
|
||||
|
||||
// HTMX request: render blocks only with OOB swap
|
||||
if ($this->isHxRequest($request)) {
|
||||
return $this->htmxOobResponse(
|
||||
$response = $this->htmxOobResponse(
|
||||
'booking/_participant_form.html.twig',
|
||||
['participant_form', 'booking_summary'],
|
||||
$templateData,
|
||||
$this->generateUrl('app_booking_create_step_2_participant', ['index' => $index])
|
||||
);
|
||||
|
||||
// Add notifications to render response if present
|
||||
if (false === empty($notifications)) {
|
||||
$response->headers->set('HX-Trigger', json_encode([
|
||||
'showNotifications' => $notifications,
|
||||
]));
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
// Regular request: render full template
|
||||
|
||||
@@ -76,17 +76,22 @@ class Step3Controller extends AbstractController
|
||||
return $this->handleApiError(
|
||||
'Booking inquiry failed',
|
||||
['message' => $inquiryResponse->message],
|
||||
'Ein Fehler ist aufgetreten. Bitte versuchen Sie es erneut.',
|
||||
$inquiryResponse->message ?? 'Ein Fehler ist aufgetreten. Bitte versuchen Sie es erneut.',
|
||||
$bookingCreateDto,
|
||||
$form
|
||||
);
|
||||
}
|
||||
|
||||
if (false === $inquiryResponse->isInquiryValid()) {
|
||||
$errorMessage = 'Buchung konnte nicht validiert werden.';
|
||||
if (null !== $inquiryResponse->message && '' !== trim($inquiryResponse->message)) {
|
||||
$errorMessage .= ' '.$inquiryResponse->message;
|
||||
}
|
||||
|
||||
return $this->handleApiError(
|
||||
'Booking inquiry validation failed',
|
||||
['status' => $inquiryResponse->status],
|
||||
'Buchung konnte nicht validiert werden.',
|
||||
['status' => $inquiryResponse->status, 'message' => $inquiryResponse->message],
|
||||
$errorMessage,
|
||||
$bookingCreateDto,
|
||||
$form
|
||||
);
|
||||
|
||||
@@ -76,17 +76,22 @@ class Step4Controller extends AbstractController
|
||||
return $this->handleApiError(
|
||||
'Booking creation failed - API notification',
|
||||
['message' => $bookingResponse->message],
|
||||
$bookingResponse->message,
|
||||
$bookingResponse->message ?? 'Ein Fehler ist aufgetreten. Bitte versuchen Sie es erneut.',
|
||||
$bookingCreateDto,
|
||||
$form
|
||||
);
|
||||
}
|
||||
|
||||
if (false === $bookingResponse->isBookingSuccessful()) {
|
||||
$errorMessage = 'Buchung konnte nicht erstellt werden.';
|
||||
if (null !== $bookingResponse->message && '' !== trim($bookingResponse->message)) {
|
||||
$errorMessage .= ' '.$bookingResponse->message;
|
||||
}
|
||||
|
||||
return $this->handleApiError(
|
||||
'Booking creation unsuccessful',
|
||||
['status' => $bookingResponse->status],
|
||||
'Buchung konnte nicht erstellt werden.',
|
||||
['status' => $bookingResponse->status, 'message' => $bookingResponse->message],
|
||||
$errorMessage,
|
||||
$bookingCreateDto,
|
||||
$form
|
||||
);
|
||||
|
||||
@@ -267,10 +267,20 @@ class IndexController extends AbstractController
|
||||
|
||||
$form->handleRequest($request);
|
||||
|
||||
// Collect notifications from field handlers (run during PRE_SUBMIT)
|
||||
$notifications = $this->collectAndClearNotifications($bookingDto);
|
||||
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
// Save updated booking data to session
|
||||
$this->bookingService->saveBookingDto($request, $bookingDto, BookingDto::MODE_EDIT);
|
||||
|
||||
// Add notifications as flash messages (redirects destroy HTMX-triggered toasts)
|
||||
if (false === empty($notifications)) {
|
||||
foreach ($notifications as $notification) {
|
||||
$this->addFlash($notification['type'], $notification['message']);
|
||||
}
|
||||
}
|
||||
|
||||
// Redirect back to cards
|
||||
return $this->hxRedirect($request, $this->generateUrl('app_booking_edit', ['id' => $id]));
|
||||
}
|
||||
@@ -300,12 +310,21 @@ class IndexController extends AbstractController
|
||||
|
||||
// HTMX request: render blocks only with OOB swap
|
||||
if ($this->isHxRequest($request)) {
|
||||
return $this->htmxOobResponse(
|
||||
$response = $this->htmxOobResponse(
|
||||
'booking/_participant_form.html.twig',
|
||||
['participant_form', 'booking_summary'],
|
||||
$templateData,
|
||||
$this->generateUrl('app_booking_edit_participant', ['id' => $id, 'index' => $index])
|
||||
);
|
||||
|
||||
// Add notifications to render response if present
|
||||
if (false === empty($notifications)) {
|
||||
$response->headers->set('HX-Trigger', json_encode([
|
||||
'showNotifications' => $notifications,
|
||||
]));
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
// Regular request: render full template
|
||||
@@ -362,18 +381,12 @@ class IndexController extends AbstractController
|
||||
|
||||
$form->handleRequest($request);
|
||||
|
||||
// Collect notifications from field handlers
|
||||
$notifications = $this->collectAndClearNotifications($bookingDto);
|
||||
|
||||
// Save updated booking data to session
|
||||
$this->bookingService->saveBookingDto($request, $bookingDto, BookingDto::MODE_EDIT);
|
||||
|
||||
// Collect notifications from participant DTO
|
||||
$participant = $bookingDto->participants[$index] ?? null;
|
||||
$notifications = $participant?->notifications ?? [];
|
||||
|
||||
// Clear notifications after collecting
|
||||
if (null !== $participant) {
|
||||
$participant->notifications = [];
|
||||
}
|
||||
|
||||
// Get complete summary data (pricing, rooms, CMS data)
|
||||
$summaryData = $this->summaryDataService->getSummaryData($bookingDto);
|
||||
|
||||
@@ -400,9 +413,9 @@ class IndexController extends AbstractController
|
||||
);
|
||||
|
||||
// Add notifications to HX-Trigger header if present
|
||||
if ([] !== $notifications) {
|
||||
if (false === empty($notifications)) {
|
||||
$response->headers->set('HX-Trigger', json_encode([
|
||||
'showNotifications' => ['notifications' => $notifications],
|
||||
'showNotifications' => $notifications,
|
||||
]));
|
||||
}
|
||||
|
||||
|
||||
@@ -76,6 +76,34 @@ trait ParticipantCardFlowTrait
|
||||
return $this->createForm(BookingParticipantType::class, $wrapper, $formOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects notifications from all participants and clears them from DTOs.
|
||||
*
|
||||
* This is important for scenarios where field handlers add notifications
|
||||
* during form processing (e.g., voucher validation, auto-unassignment).
|
||||
* Notifications from ALL participants are collected, not just the current one,
|
||||
* because cross-participant logic may add notifications to multiple participants.
|
||||
*
|
||||
* Note: ParticipantDto stores notifications with MD5 keys to prevent duplicates,
|
||||
* but the JavaScript toast controller expects a simple indexed array. We use
|
||||
* array_values() to convert the associative array to an indexed array.
|
||||
*
|
||||
* @return array<array{type: string, message: string}> Array of notification messages
|
||||
*/
|
||||
private function collectAndClearNotifications(BookingDto $bookingDto): array
|
||||
{
|
||||
$notifications = [];
|
||||
foreach ($bookingDto->participants as $participant) {
|
||||
if (false === empty($participant->notifications)) {
|
||||
$notifications = array_merge($notifications, $participant->notifications);
|
||||
$participant->notifications = [];
|
||||
}
|
||||
}
|
||||
|
||||
// Convert MD5-keyed associative array to simple indexed array
|
||||
return array_values($notifications);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process single participant form refresh.
|
||||
*
|
||||
@@ -95,16 +123,8 @@ trait ParticipantCardFlowTrait
|
||||
|
||||
$form->handleRequest($request);
|
||||
|
||||
// Collect notifications from ALL participants (not just current one)
|
||||
// This is important for auto-unassignment scenarios where other participants
|
||||
// may receive notifications when the current participant takes an action
|
||||
$notifications = [];
|
||||
foreach ($bookingDto->participants as $participant) {
|
||||
if (false === empty($participant->notifications)) {
|
||||
$notifications = array_merge($notifications, $participant->notifications);
|
||||
$participant->notifications = [];
|
||||
}
|
||||
}
|
||||
// Collect notifications from field handlers
|
||||
$notifications = $this->collectAndClearNotifications($bookingDto);
|
||||
|
||||
// Save updated booking data to session (after collecting & clearing notifications)
|
||||
$this->bookingService->saveBookingDto($request, $bookingDto, $bookingDto->getMode());
|
||||
|
||||
Reference in New Issue
Block a user