feat: inform user about automatic reassignments or reset selections

This commit is contained in:
Björn Fromme
2025-10-02 17:57:56 +02:00
parent 8e43edcbaf
commit fba9edc931
10 changed files with 173 additions and 5 deletions
@@ -131,6 +131,9 @@ class CreateStep2Controller extends AbstractController
$this->bookingService->saveBookingCreateDto($request, $bookingCreateDto);
// Collect notifications from all participants
$notifications = $this->collectParticipantNotifications($bookingCreateDto);
$summary = $this->bookingService->getRoomSummaryAndParticipantCount($bookingCreateDto);
$roomAssignmentCounts = $this->bookingService->getRoomAssignmentCounts($bookingCreateDto);
$availableRooms = $bookingCreateDto->travel->getAvailableRooms();
@@ -139,7 +142,7 @@ class CreateStep2Controller extends AbstractController
// The DTO is now updated with the latest selection and submitted data has been cleaned.
// We can now render the blocks with the fresh data.
return $this->htmxOobResponse(
$response = $this->htmxOobResponse(
'booking/create_step_2.html.twig',
['participants_form', 'booking_summary'],
[
@@ -152,6 +155,15 @@ class CreateStep2Controller extends AbstractController
'groupedSelectedRooms' => $groupedSelectedRooms,
]
);
// Add notifications to HTMX trigger header if any exist
if ([] !== $notifications) {
$response->headers->set('HX-Trigger', json_encode([
'showNotifications' => ['notifications' => $notifications],
]));
}
return $response;
}
/**
@@ -233,4 +245,28 @@ class CreateStep2Controller extends AbstractController
$this->roomAssignmentService->assignParticipantsToRooms($bookingCreateDto);
}
}
/**
* Collects all notifications from participants and clears them.
*
* @param BookingCreateDto $bookingCreateDto The booking DTO containing participants
*
* @return array<array{type: string, message: string}> Array of notification messages
*/
private function collectParticipantNotifications(BookingCreateDto $bookingCreateDto): array
{
$notifications = [];
foreach ($bookingCreateDto->participants as $participant) {
if ([] !== $participant->notifications) {
foreach ($participant->notifications as $notification) {
$notifications[] = $notification;
}
// Clear notifications after collection
$participant->notifications = [];
}
}
return $notifications;
}
}
+19
View File
@@ -77,6 +77,11 @@ class ParticipantDto
// Bulk insurance booking flag (applicant only: when checked, assigns same insurance type to all participants)
public bool $bulkInsuranceBooking = false;
/**
* @var array<array{type: string, message: string}> Notification messages for user feedback
*/
public array $notifications = [];
public static function fromPersonalData(PersonalData $personalData): static
{
$instance = new static();
@@ -164,4 +169,18 @@ class ParticipantDto
{
return $this->insurance?->price ?? 0.0;
}
/**
* Adds a notification message for user feedback.
*
* @param string $type The notification type (info, warning, success)
* @param string $message The notification message
*/
public function addNotification(string $type, string $message): void
{
$this->notifications[] = [
'type' => $type,
'message' => $message,
];
}
}
@@ -151,6 +151,13 @@ class ParticipantInsuranceFieldHandler extends AbstractParticipantFieldHandler
$bookingDto
);
$participant->insurance = $reassignedInsurance;
if (null !== $reassignedInsurance) {
$participant->addNotification(
'info',
sprintf('Versicherung automatisch angepasst: %s', $reassignedInsurance->label)
);
}
}
} else {
// Insurance not found - clear selection
@@ -174,6 +181,14 @@ class ParticipantInsuranceFieldHandler extends AbstractParticipantFieldHandler
$participant,
$bookingDto
);
if (null !== $reassignedInsurance && $reassignedInsurance->id !== $currentInsurance->id) {
$participant->addNotification(
'info',
sprintf('Versicherung automatisch angepasst an neuen Preis: %s', $reassignedInsurance->label)
);
}
$participant->insurance = $reassignedInsurance; // null if no suitable match found
}
@@ -64,9 +64,18 @@ class ParticipantRentalsFieldHandler extends AbstractParticipantFieldHandler
return;
}
// Store previous rental state BEFORE checking skipass
$previousRentals = $participant->rentals;
// Check if rentals should be available based on skipass selection
if (null === $participant->skiPass) {
// No skipass selected = clear all rental selections
if ([] !== $previousRentals) {
$participant->addNotification(
'warning',
'Ausrüstung wurde entfernt (kein Skipass ausgewählt)'
);
}
$participant->rentals = [];
return;
@@ -85,6 +94,14 @@ class ParticipantRentalsFieldHandler extends AbstractParticipantFieldHandler
$participantIndex
);
// Notify if rentals were cleared due to skipass duration change
if ([] !== $previousRentals && [] === $validSelections) {
$participant->addNotification(
'warning',
'Ausrüstung wurde entfernt (andere Skipass-Dauer ausgewählt)'
);
}
$participant->rentals = $validSelections;
}
@@ -168,7 +185,7 @@ class ParticipantRentalsFieldHandler extends AbstractParticipantFieldHandler
* - rental.dateFrom === skipass.dateFrom
* - rental.dateTo === skipass.dateTo
*
* @param array $rentals All available rental services
* @param array $rentals All available rental services
* @param \App\Form\Model\ParticipantDto $participant The participant with skipass selection
*
* @return array Filtered rentals matching the skipass duration