feat: refactor to cards
This commit is contained in:
@@ -125,7 +125,10 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider
|
||||
'expanded' => true,
|
||||
'required' => false,
|
||||
'choices' => $this->filterServicesByAgeConstraints(
|
||||
$bookingDto->travel->getAdditionalServicesBySubTypes(Constants::TOKEN_COURSES),
|
||||
$bookingDto->travel->getAdditionalServicesBySubTypes(
|
||||
Constants::TOKEN_COURSES,
|
||||
BookingDto::MODE_CREATE === $bookingDto->getMode() // Only filter by availability in create mode
|
||||
),
|
||||
$bookingDto,
|
||||
$participantIndex
|
||||
),
|
||||
@@ -143,8 +146,8 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider
|
||||
$attributes['data-description'] = $service->description;
|
||||
}
|
||||
|
||||
// Make readonly if service is unavailable
|
||||
if ($this->isServiceUnavailableForParticipant($service, $bookingDto, $participantIndex)) {
|
||||
// Make readonly if service is unavailable (intelligently handles edit mode)
|
||||
if ($this->shouldMakeServiceReadonly($service, $bookingDto, $participantIndex, 'courses')) {
|
||||
$attributes['readonly'] = true;
|
||||
$attributes['data-tooltip'] = 'ausgebucht';
|
||||
}
|
||||
@@ -160,7 +163,10 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider
|
||||
'expanded' => true,
|
||||
'required' => false,
|
||||
'choices' => $this->filterServicesByAgeConstraints(
|
||||
$bookingDto->travel->getAdditionalServicesBySubTypes(Constants::TOKEN_ADDITIONAL),
|
||||
$bookingDto->travel->getAdditionalServicesBySubTypes(
|
||||
Constants::TOKEN_ADDITIONAL,
|
||||
BookingDto::MODE_EDIT !== $bookingDto->getMode() // Only filter by availability in create mode
|
||||
),
|
||||
$bookingDto,
|
||||
$participantIndex
|
||||
),
|
||||
@@ -187,7 +193,7 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider
|
||||
}
|
||||
|
||||
// Make readonly if service is unavailable (only if not already mandatory)
|
||||
if (false === $service->mandatory && $this->isServiceUnavailableForParticipant($service, $bookingDto, $participantIndex)) {
|
||||
if (false === $service->mandatory && $this->shouldMakeServiceReadonly($service, $bookingDto, $participantIndex, 'additionalServices')) {
|
||||
$attributes['readonly'] = true;
|
||||
$attributes['data-tooltip'] = 'ausgebucht';
|
||||
}
|
||||
@@ -203,7 +209,10 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider
|
||||
'expanded' => true,
|
||||
'required' => false,
|
||||
'choices' => $this->filterServicesByAgeConstraints(
|
||||
$bookingDto->travel->getAdditionalServicesBySubTypes(Constants::TOKEN_BOARD),
|
||||
$bookingDto->travel->getAdditionalServicesBySubTypes(
|
||||
Constants::TOKEN_BOARD,
|
||||
BookingDto::MODE_CREATE === $bookingDto->getMode() // Only filter by availability in create mode
|
||||
),
|
||||
$bookingDto,
|
||||
$participantIndex
|
||||
),
|
||||
@@ -216,8 +225,8 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider
|
||||
|
||||
$attributes = [];
|
||||
|
||||
// Make readonly if service is unavailable
|
||||
if ($this->isServiceUnavailableForParticipant($service, $bookingDto, $participantIndex)) {
|
||||
// Make readonly if service is unavailable (intelligently handles edit mode)
|
||||
if ($this->shouldMakeServiceReadonly($service, $bookingDto, $participantIndex, 'board')) {
|
||||
$attributes['readonly'] = true;
|
||||
$attributes['data-tooltip'] = 'ausgebucht';
|
||||
}
|
||||
@@ -234,7 +243,11 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider
|
||||
'required' => false,
|
||||
'choices' => $this->filterServicesByAgeConstraints(
|
||||
$this->filterRentalsBySkiPassDuration(
|
||||
$bookingDto->travel->getAdditionalServicesBySubTypes(Constants::TOKEN_RENTALS, true, true),
|
||||
$bookingDto->travel->getAdditionalServicesBySubTypes(
|
||||
Constants::TOKEN_RENTALS,
|
||||
BookingDto::MODE_CREATE === $bookingDto->getMode(), // Only filter by availability in create mode
|
||||
true // Filter by travel date range
|
||||
),
|
||||
$bookingDto,
|
||||
$participantIndex
|
||||
),
|
||||
@@ -255,8 +268,8 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider
|
||||
$attributes['data-description'] = $service->description;
|
||||
}
|
||||
|
||||
// Make readonly if service is unavailable
|
||||
if ($this->isServiceUnavailableForParticipant($service, $bookingDto, $participantIndex)) {
|
||||
// Make readonly if service is unavailable (intelligently handles edit mode)
|
||||
if ($this->shouldMakeServiceReadonly($service, $bookingDto, $participantIndex, 'rentals')) {
|
||||
$attributes['readonly'] = true;
|
||||
$attributes['data-tooltip'] = 'ausgebucht';
|
||||
}
|
||||
@@ -292,7 +305,11 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider
|
||||
'expanded' => true,
|
||||
'required' => true,
|
||||
'choices' => $this->filterServicesByAgeConstraints(
|
||||
$bookingDto->travel->getAdditionalServicesBySubTypes(Constants::TOKEN_SKI_PASS, true, true),
|
||||
$bookingDto->travel->getAdditionalServicesBySubTypes(
|
||||
Constants::TOKEN_SKI_PASS,
|
||||
BookingDto::MODE_CREATE === $bookingDto->getMode(), // Only filter by availability in create mode
|
||||
true // Filter by travel date range
|
||||
),
|
||||
$bookingDto,
|
||||
$participantIndex
|
||||
),
|
||||
@@ -310,8 +327,8 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider
|
||||
$attributes['data-description'] = $service->description;
|
||||
}
|
||||
|
||||
// Make readonly if service is unavailable
|
||||
if ($this->isServiceUnavailableForParticipant($service, $bookingDto, $participantIndex)) {
|
||||
// Make readonly if service is unavailable (intelligently handles edit mode)
|
||||
if ($this->shouldMakeServiceReadonly($service, $bookingDto, $participantIndex, 'skiPass')) {
|
||||
$attributes['readonly'] = true;
|
||||
$attributes['data-tooltip'] = 'ausgebucht';
|
||||
}
|
||||
@@ -348,19 +365,14 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider
|
||||
|
||||
$attributes = [];
|
||||
|
||||
// Make readonly if service is unavailable
|
||||
if ($this->isServiceUnavailableForParticipant($service, $bookingDto, $participantIndex)) {
|
||||
// Make readonly if service is unavailable (intelligently handles edit mode)
|
||||
if ($this->shouldMakeServiceReadonly($service, $bookingDto, $participantIndex, 'transportationOutbound')) {
|
||||
$attributes['readonly'] = true;
|
||||
$attributes['data-tooltip'] = 'ausgebucht';
|
||||
}
|
||||
|
||||
return $attributes;
|
||||
},
|
||||
'attr' => [
|
||||
'hx-post' => $this->urlGenerator->generate('app_booking_create_step_2_refresh'),
|
||||
'hx-swap' => 'none',
|
||||
'hx-trigger' => 'change',
|
||||
],
|
||||
];
|
||||
|
||||
// Inbound Transportation
|
||||
@@ -379,19 +391,14 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider
|
||||
|
||||
$attributes = [];
|
||||
|
||||
// Make readonly if service is unavailable
|
||||
if ($this->isServiceUnavailableForParticipant($service, $bookingDto, $participantIndex)) {
|
||||
// Make readonly if service is unavailable (intelligently handles edit mode)
|
||||
if ($this->shouldMakeServiceReadonly($service, $bookingDto, $participantIndex, 'transportationInbound')) {
|
||||
$attributes['readonly'] = true;
|
||||
$attributes['data-tooltip'] = 'ausgebucht';
|
||||
}
|
||||
|
||||
return $attributes;
|
||||
},
|
||||
'attr' => [
|
||||
'hx-post' => $this->urlGenerator->generate('app_booking_create_step_2_refresh'),
|
||||
'hx-swap' => 'none',
|
||||
'hx-trigger' => 'change',
|
||||
],
|
||||
];
|
||||
|
||||
// Pickup (conditional - only shown when either transportation direction is bus)
|
||||
@@ -419,11 +426,6 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider
|
||||
$this->fieldOptionProviders['bulkInsuranceBooking'] = fn (BookingDto $bookingDto, int $participantIndex, array $options = []) => [
|
||||
'label' => 'Für alle Teilnehmer buchen',
|
||||
'required' => false,
|
||||
'attr' => [
|
||||
'hx-post' => $this->urlGenerator->generate('app_booking_create_step_2_refresh'),
|
||||
'hx-swap' => 'none',
|
||||
'hx-trigger' => 'change',
|
||||
],
|
||||
];
|
||||
|
||||
// Insurance field provider - provides age and eligibility filtered insurances for participants
|
||||
@@ -434,11 +436,6 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider
|
||||
'expanded' => true,
|
||||
'required' => false,
|
||||
'insurances' => $this->getEligibleInsurances($bookingDto, $participantIndex),
|
||||
'attr' => [
|
||||
'hx-post' => $this->urlGenerator->generate('app_booking_create_step_2_refresh'),
|
||||
'hx-swap' => 'none',
|
||||
'hx-trigger' => 'change',
|
||||
],
|
||||
];
|
||||
|
||||
// Future field providers would be added here, for example:
|
||||
@@ -601,6 +598,78 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider
|
||||
return $this->serviceAvailabilityCalculator->isServiceUnavailable($service->id, $bookingDto, $participantIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a service should be rendered as read-only.
|
||||
*
|
||||
* This method intelligently handles readonly state for services in both create and edit modes:
|
||||
*
|
||||
* - CREATE MODE: Uses existing availability calculator logic
|
||||
* - EDIT MODE: Services unavailable (available <= 0) are readonly ONLY if participant doesn't already have them
|
||||
*
|
||||
* This prevents fingerprint false positives in edit mode by allowing participants to keep
|
||||
* services they already have, even if those services are now fully booked.
|
||||
*
|
||||
* @param Service $service The service to check
|
||||
* @param BookingDto $bookingDto The booking DTO containing participant data
|
||||
* @param int $participantIndex Index of the participant currently selecting services
|
||||
* @param string $fieldName Name of the service field (e.g., 'courses', 'board', 'rentals')
|
||||
*
|
||||
* @return bool True if the service should be read-only
|
||||
*/
|
||||
private function shouldMakeServiceReadonly(Service $service, BookingDto $bookingDto, int $participantIndex, string $fieldName): bool
|
||||
{
|
||||
// In CREATE mode, use existing availability logic
|
||||
if (BookingDto::MODE_CREATE === $bookingDto->getMode()) {
|
||||
return $this->isServiceUnavailableForParticipant($service, $bookingDto, $participantIndex);
|
||||
}
|
||||
|
||||
// In EDIT mode, apply intelligent readonly logic
|
||||
// If service is available (available > 0), it's never readonly
|
||||
if (null !== $service->available && $service->available > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Service is unavailable - check if participant already has it
|
||||
$participant = $bookingDto->getParticipant($participantIndex);
|
||||
if (null === $participant) {
|
||||
return true; // Readonly if no participant data
|
||||
}
|
||||
|
||||
// Check if participant has this service based on field type
|
||||
$participantHasService = match ($fieldName) {
|
||||
'courses' => $this->hasServiceById($participant->courses, $service->id),
|
||||
'additionalServices' => $this->hasServiceById($participant->additionalServices, $service->id),
|
||||
'board' => $this->hasServiceById($participant->board, $service->id),
|
||||
'rentals' => $this->hasServiceById($participant->rentals, $service->id),
|
||||
'skiPass' => $participant->skiPass?->id === $service->id,
|
||||
'transportationOutbound' => $participant->transportationOutbound?->id === $service->id,
|
||||
'transportationInbound' => $participant->transportationInbound?->id === $service->id,
|
||||
default => false,
|
||||
};
|
||||
|
||||
// Make readonly only if participant doesn't have it
|
||||
return false === $participantHasService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a service array contains a service with the given ID.
|
||||
*
|
||||
* @param array $services Array of Service objects
|
||||
* @param int $serviceId Service ID to search for
|
||||
*
|
||||
* @return bool True if the service is found in the array
|
||||
*/
|
||||
private function hasServiceById(array $services, int $serviceId): bool
|
||||
{
|
||||
foreach ($services as $service) {
|
||||
if ($service->id === $serviceId) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters services based on participant's age constraints.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user