From b84997d058f7da5ef9570b74b66af7442bc45500 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Fromme?= Date: Wed, 3 Sep 2025 17:48:07 +0200 Subject: [PATCH] wip: rentals insurance field --- config/services.yaml | 1 + docs/FIELD_STATE_SYSTEM.md | 68 +++++++++ docs/FORM_PROCESSING.md | 59 ++++++-- docs/SYSTEM_STATUS_2025.md | 2 + src/BusProNet/Constants.php | 1 + src/Form/BookingCreateParticipantType.php | 17 +-- src/Form/Model/ParticipantDto.php | 44 +----- src/Form/Service/CreateFieldStateProvider.php | 21 ++- .../ParticipantFieldOptionsProvider.php | 19 +++ ...ParticipantRentalInsuranceFieldHandler.php | 133 ++++++++++++++++++ .../ParticipantRentalsFieldHandler.php | 5 - src/Service/BookingPriceCalculatorService.php | 7 +- templates/booking/create_step_2.html.twig | 21 ++- 13 files changed, 315 insertions(+), 83 deletions(-) create mode 100644 src/Form/Service/ParticipantRentalInsuranceFieldHandler.php diff --git a/config/services.yaml b/config/services.yaml index 14e2ad5..6e58be4 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -85,3 +85,4 @@ services: - 'App\Form\Service\ParticipantPickupOutboundFieldHandler' - 'App\Form\Service\ParticipantPickupInboundFieldHandler' - 'App\Form\Service\ParticipantParkingFieldHandler' + - 'App\Form\Service\ParticipantRentalInsuranceFieldHandler' diff --git a/docs/FIELD_STATE_SYSTEM.md b/docs/FIELD_STATE_SYSTEM.md index f09c6c5..49d5c69 100644 --- a/docs/FIELD_STATE_SYSTEM.md +++ b/docs/FIELD_STATE_SYSTEM.md @@ -297,4 +297,72 @@ class CustomFieldOptionsProvider extends AbstractFieldOptionsProvider } ``` +## Current Implementation Examples + +### Body Dimensions and Rental Insurance Conditional Fields + +The current system implements sophisticated conditional field visibility for body dimensions and rental insurance: + +```php +// CreateFieldStateProvider.php +protected function registerFieldStateConditions(): void +{ + $rentalCondition = new RentalSelectionCondition(); + + // Hide body dimensions section unless rental services are selected + $this->fieldStateConditions['bodyDimensions'] = [ + 'hidden' => CompositeCondition::not($rentalCondition), + ]; + + // Hide rental insurance unless rental services are selected + $this->fieldStateConditions['rentalInsurance'] = [ + 'hidden' => CompositeCondition::not($rentalCondition), + ]; + + // Hide parking unless outbound transportation is PKW (car) + $this->fieldStateConditions['parking'] = [ + 'hidden' => CompositeCondition::not( + ServiceSubTypeCondition::equals('transportationOutbound', DirectionMapper::SUBTYPE_CAR_API) + ), + ]; +} +``` + +### Rental Insurance Checkbox Implementation + +The rental insurance field demonstrates the checkbox pattern used for service selection: + +```php +// Field Options Provider +$this->fieldOptionProviders['rentalInsurance'] = fn (BookingDtoInterface $bookingDto, int $participantIndex, array $options = []) => [ + 'label' => $this->getRentalInsuranceCheckboxLabel($rentalInsuranceServices), + 'required' => false, + 'property_path' => 'rentalInsuranceSelected', // Maps to boolean property +]; + +// Field Handler Processing +public function processField(array $submittedData, BookingDtoInterface $bookingDto, int $participantIndex): void +{ + $isRentalInsuranceSelected = (bool) $this->getFieldValue($submittedData, $this->getFieldName()); + + // Store boolean value for form state + $participant->rentalInsuranceSelected = $isRentalInsuranceSelected; + + // Store Service object for pricing calculations + if ($isRentalInsuranceSelected) { + $participant->rentalInsurance = $this->findRentalInsuranceService($bookingDto); + } else { + $participant->rentalInsurance = null; + } +} +``` + +### Benefits of Current Architecture + +1. **Clean Separation**: Boolean properties handle form state, Service objects handle business logic +2. **Automatic Pricing Integration**: Service objects are automatically included in pricing calculations +3. **Dynamic Visibility**: Fields appear/disappear based on related selections +4. **Consistent UX**: Checkbox pattern provides intuitive user interface +5. **Validation-Free**: Conditional visibility eliminates need for complex validation rules + This system provides a powerful, maintainable foundation for complex conditional field behavior while maintaining clean separation of concerns and extensibility. \ No newline at end of file diff --git a/docs/FORM_PROCESSING.md b/docs/FORM_PROCESSING.md index a337fd0..2b4a16a 100644 --- a/docs/FORM_PROCESSING.md +++ b/docs/FORM_PROCESSING.md @@ -35,7 +35,8 @@ class BookingCreateDto implements BookingDtoInterface #### ParticipantDto (`src/Form/Model/ParticipantDto.php`) - Individual participant data container - Includes personal data, body dimensions, and service selections -- Custom validation for body dimensions when rental services are selected +- Body dimensions are hidden unless rental services are selected (conditional visibility) +- Rental insurance field with checkbox interface and boolean state tracking ```php class ParticipantDto @@ -46,7 +47,7 @@ class ParticipantDto public ?\DateTimeImmutable $dateOfBirth = null; public ?string $email = null; - // Body dimensions (conditional) + // Body dimensions (hidden unless rental services are selected) public ?string $height = null; public ?string $weight = null; public ?string $shoeSize = null; @@ -56,7 +57,16 @@ class ParticipantDto public array $courses = []; public array $additionalServices = []; public array $rentals = []; - // ... other service arrays + public ?Service $rentalInsurance = null; + public bool $rentalInsuranceSelected = false; // Checkbox state + + // Transportation services + public ?Service $transportationOutbound = null; + public ?Service $transportationInbound = null; + public ?Pickup $pickupOutbound = null; + public ?Pickup $pickupInbound = null; + public bool $parking = false; + public ?Service $parkingService = null; } ``` @@ -87,9 +97,21 @@ Central registry for dynamic field configurations using a provider pattern with - **`rentals`**: Rental equipment options - Date-filtered rental services - - Triggers body dimension requirements when selected + - Controls visibility of body dimensions and rental insurance fields - Populated from `TOKEN_RENTALS` subtype services +- **`rentalInsurance`**: Rental insurance checkbox + - Checkbox interface (similar to parking field) + - Maps to `rentalInsuranceSelected` boolean property + - Only visible when rental services are selected + - Automatically manages Service object for pricing calculations + - Populated from `TOKEN_RENTAL_INSURANCE` subtype services + +- **`parking`**: Parking service checkbox + - Boolean checkbox for self-organized transportation + - Only visible when outbound transportation is PKW (car) + - Manages both boolean state and Service object for pricing + **Provider Pattern Implementation:** ```php protected function registerFieldOptionProviders(): void @@ -108,7 +130,8 @@ protected function registerFieldOptionProviders(): void **CreateFieldStateProvider (`src/Form/Service/CreateFieldStateProvider.php`)** - Manages field states for the booking creation workflow -- Currently implements body dimension requirements for rental services +- Controls conditional field visibility and state based on participant data +- Implements body dimensions and rental insurance conditional visibility **Field State Types:** - `readonly`: Field is visible but not editable @@ -122,10 +145,22 @@ protected function registerFieldStateConditions(): void { $rentalCondition = new RentalSelectionCondition(); - // Body dimensions become required when rentals are selected - $this->fieldStateConditions['height'] = ['required' => $rentalCondition]; - $this->fieldStateConditions['weight'] = ['required' => $rentalCondition]; - $this->fieldStateConditions['shoeSize'] = ['required' => $rentalCondition]; + // Hide body dimensions section unless rental services are selected + $this->fieldStateConditions['bodyDimensions'] = [ + 'hidden' => CompositeCondition::not($rentalCondition), + ]; + + // Hide rental insurance unless rental services are selected + $this->fieldStateConditions['rentalInsurance'] = [ + 'hidden' => CompositeCondition::not($rentalCondition), + ]; + + // Hide parking unless outbound transportation is PKW (car) + $this->fieldStateConditions['parking'] = [ + 'hidden' => CompositeCondition::not( + ServiceSubTypeCondition::equals('transportationOutbound', DirectionMapper::SUBTYPE_CAR_API) + ), + ]; } ``` @@ -227,10 +262,14 @@ Base class providing common functionality: - **`ParticipantCoursesFieldHandler`**: Course selections filtering - **`ParticipantBoardFieldHandler`**: Board/meal options filtering - **`ParticipantRentalsFieldHandler`**: Rental equipment filtering + - **`ParticipantRentalInsuranceFieldHandler`**: Rental insurance checkbox handling + - Depends on `['dateOfBirth', 'rentals']` (only visible when rentals selected) + - Processes boolean checkbox input and converts to Service object + - Manages both `rentalInsuranceSelected` (bool) and `rentalInsurance` (Service) properties All service handlers share these characteristics: - Depend on `dateOfBirth` field (processed first) - - Filter selections based on age constraints + - Filter selections based on age constraints (except rental insurance which uses conditional visibility) - Instantiate `ServiceAgeEvaluator` directly when needed - Remove invalid selections to prevent form validation errors - **Store complete Service objects** in ParticipantDto (not just IDs) for pricing calculations diff --git a/docs/SYSTEM_STATUS_2025.md b/docs/SYSTEM_STATUS_2025.md index 8f6ca6f..6b5fb9f 100644 --- a/docs/SYSTEM_STATUS_2025.md +++ b/docs/SYSTEM_STATUS_2025.md @@ -31,6 +31,8 @@ - **Parking Services**: Self-organized transport handling ✅ - **Accommodation Services**: Board selection, room assignment ✅ - **Activity Services**: Ski passes, courses, rentals ✅ +- **Rental Insurance**: Checkbox interface with conditional visibility ✅ +- **Body Dimensions**: Hidden unless rental services selected ✅ - **Additional Services**: Flexible service extension system ✅ #### Pricing & Display System diff --git a/src/BusProNet/Constants.php b/src/BusProNet/Constants.php index 04fbfb6..3678ef4 100644 --- a/src/BusProNet/Constants.php +++ b/src/BusProNet/Constants.php @@ -15,6 +15,7 @@ final class Constants public const TOKEN_ADDITIONAL = 'SON'; public const TOKEN_BOARD = 'VPF'; public const TOKEN_RENTALS = ['VER', 'VE2', 'VE3', 'VE4', 'VE5', 'VE6', 'VE7', 'VE8']; + public const TOKEN_RENTAL_INSURANCE = 'LVS'; public const TOKEN_PARKING = 'PAR'; public const STATUS_AVAILABLE = 'Frei'; diff --git a/src/Form/BookingCreateParticipantType.php b/src/Form/BookingCreateParticipantType.php index 8f624a8..1d1efcb 100644 --- a/src/Form/BookingCreateParticipantType.php +++ b/src/Form/BookingCreateParticipantType.php @@ -147,15 +147,10 @@ class BookingCreateParticipantType extends AbstractType 'clean_xss' => true, ], $getFieldState('mobile'))); - // Add body dimensions with state handling - $bodyDimensionStates = ['height' => $getFieldState('height'), 'weight' => $getFieldState('weight'), 'shoeSize' => $getFieldState('shoeSize')]; - $bodyDimensionOptions = []; - foreach ($bodyDimensionStates as $fieldName => $fieldState) { - if (isset($fieldState['required']) && true === $fieldState['required']) { - $bodyDimensionOptions[$fieldName.'_required'] = true; - } + // Add body dimensions with state handling - use shouldIncludeField method + if ($this->fieldStateProvider->shouldIncludeField('bodyDimensions', $bookingDto, $participantIndex)) { + $form->add('bodyDimensions', BodyDimensionsType::class); } - $form->add('bodyDimensions', BodyDimensionsType::class, $bodyDimensionOptions); } /** @@ -178,6 +173,7 @@ class BookingCreateParticipantType extends AbstractType 'additionalServices', 'board', 'rentals', + 'rentalInsurance', 'skiPass', 'transportationOutbound', 'transportationInbound', @@ -204,7 +200,7 @@ class BookingCreateParticipantType extends AbstractType // Clear the form and rebuild from scratch with updated states // Rebuild base fields with updated states - $baseFields = ['firstName', 'lastName', 'dateOfBirth', 'gender', 'nationality', 'email', 'mobile']; + $baseFields = ['firstName', 'lastName', 'dateOfBirth', 'gender', 'nationality', 'email', 'mobile', 'bodyDimensions']; foreach ($baseFields as $fieldName) { if ($form->has($fieldName)) { $form->remove($fieldName); @@ -215,7 +211,7 @@ class BookingCreateParticipantType extends AbstractType $this->addBaseFields($form, $bookingDto, $participantIndex); // Rebuild dynamic fields - $dynamicFields = ['assignedRoomId', 'remarksRoom', 'courses', 'additionalServices', 'board', 'rentals', 'skiPass', 'transportationOutbound', 'transportationInbound', 'pickupOutbound', 'pickupInbound', 'parking']; + $dynamicFields = ['assignedRoomId', 'remarksRoom', 'courses', 'additionalServices', 'board', 'rentals', 'rentalInsurance', 'skiPass', 'transportationOutbound', 'transportationInbound', 'pickupOutbound', 'pickupInbound', 'parking']; foreach ($dynamicFields as $fieldName) { if ($form->has($fieldName)) { $form->remove($fieldName); @@ -238,6 +234,7 @@ class BookingCreateParticipantType extends AbstractType 'additionalServices' => ChoiceType::class, 'board' => ChoiceType::class, 'rentals' => ChoiceType::class, + 'rentalInsurance' => CheckboxType::class, 'skiPass' => ChoiceType::class, 'transportationOutbound' => ChoiceType::class, 'transportationInbound' => ChoiceType::class, diff --git a/src/Form/Model/ParticipantDto.php b/src/Form/Model/ParticipantDto.php index 3398240..6705a4f 100644 --- a/src/Form/Model/ParticipantDto.php +++ b/src/Form/Model/ParticipantDto.php @@ -10,7 +10,6 @@ use Symfony\Component\Validator\Constraints as Assert; use Symfony\Component\Validator\Context\ExecutionContextInterface; #[AppAssert\Participant(groups: ['booking_edit'])] -#[Assert\Callback('validateBodyDimensionsForRentals', groups: ['booking_create_step_2', 'booking_edit'])] class ParticipantDto { public ?int $index = null; @@ -51,6 +50,10 @@ class ParticipantDto public ?Service $skiPass = null; public array $board = []; public array $rentals = []; + public ?Service $rentalInsurance = null; + + // Rental insurance checkbox state (boolean: true if rental insurance requested) + public bool $rentalInsuranceSelected = false; // Transportation services with improved naming (outbound/inbound) public ?Service $transportationOutbound = null; @@ -103,43 +106,4 @@ class ParticipantDto return 'O' === $this->status; } - /** - * Validates that body dimension fields are provided when rental services are selected. - * - * This callback validator ensures that height, weight, and shoe size are mandatory - * when the participant has selected any rental services. This is required for - * proper equipment sizing and rental fulfillment. - * - * @param ExecutionContextInterface $context The validation context - */ - public function validateBodyDimensionsForRentals(ExecutionContextInterface $context): void - { - $rentals = $this->rentals ?? []; - - // If no rental services are selected, body dimensions are not required - if (empty($rentals)) { - return; - } - - // Validate height field - if (true === empty($this->height)) { - $context->buildViolation('Deine Körpergröße ist erforderlich wenn Leihmaterial ausgewählt wurde') - ->atPath('height') - ->addViolation(); - } - - // Validate weight field - if (true === empty($this->weight)) { - $context->buildViolation('Dein Gewicht ist erforderlich wenn Leihmaterial ausgewählt wurde') - ->atPath('weight') - ->addViolation(); - } - - // Validate shoe size field - if (true === empty($this->shoeSize)) { - $context->buildViolation('Deine Schuhgröße ist erforderlich wenn Leihmaterial ausgewählt wurde') - ->atPath('shoeSize') - ->addViolation(); - } - } } diff --git a/src/Form/Service/CreateFieldStateProvider.php b/src/Form/Service/CreateFieldStateProvider.php index 30e5549..0577353 100644 --- a/src/Form/Service/CreateFieldStateProvider.php +++ b/src/Form/Service/CreateFieldStateProvider.php @@ -21,7 +21,7 @@ use App\Form\Service\Condition\ServiceSubTypeCondition; * state functionality provided by AbstractFieldStateProvider. * * Current field state conditions: - * - Body dimension fields become required when rental services are selected + * - Body dimension fields are hidden unless rental services are selected * - Age-dependent service fields are hidden until birth date is provided * - Transportation pickup fields are hidden by default, shown only when transportation type is BUS * - Parking field is hidden by default, shown only when outbound transportation is PKW @@ -56,17 +56,9 @@ class CreateFieldStateProvider extends AbstractFieldStateProvider { $rentalCondition = new RentalSelectionCondition(); - // Make body dimension fields required when rental services are selected - $this->fieldStateConditions['height'] = [ - 'required' => $rentalCondition, - ]; - - $this->fieldStateConditions['weight'] = [ - 'required' => $rentalCondition, - ]; - - $this->fieldStateConditions['shoeSize'] = [ - 'required' => $rentalCondition, + // Hide body dimensions section unless rental services are selected + $this->fieldStateConditions['bodyDimensions'] = [ + 'hidden' => CompositeCondition::not($rentalCondition), ]; // Hide age-dependent fields when no date of birth is provided @@ -121,6 +113,11 @@ class CreateFieldStateProvider extends AbstractFieldStateProvider ), ]; + // Show rental insurance only when rental services are selected (hidden by default) + $this->fieldStateConditions['rentalInsurance'] = [ + 'hidden' => CompositeCondition::not($rentalCondition), + ]; + // Example field state conditions would be registered here // For demonstration purposes, here are some example patterns: diff --git a/src/Form/Service/ParticipantFieldOptionsProvider.php b/src/Form/Service/ParticipantFieldOptionsProvider.php index 1e68e4a..3b00dea 100644 --- a/src/Form/Service/ParticipantFieldOptionsProvider.php +++ b/src/Form/Service/ParticipantFieldOptionsProvider.php @@ -183,6 +183,13 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider 'choice_label' => fn (?Service $service) => $this->formatServiceLabelWithPrice($service), ]; + // Rental insurance field provider - provides rental insurance options when rental services are selected + $this->fieldOptionProviders['rentalInsurance'] = fn (BookingDtoInterface $bookingDto, int $participantIndex, array $options = []) => [ + 'label' => $this->getRentalInsuranceCheckboxLabel($bookingDto->travel->getAdditionalServicesBySubTypes(Constants::TOKEN_RENTAL_INSURANCE, true, true)), + 'required' => false, + 'property_path' => 'rentalInsuranceSelected', + ]; + // Skipass field provider - provides age-appropriate skipass options from travel data filtered by date range $this->fieldOptionProviders['skiPass'] = fn (BookingDtoInterface $bookingDto, int $participantIndex, array $options = []) => [ 'label' => 'Skipass', @@ -477,4 +484,16 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider return $ageEvaluator->isServiceAvailableForParticipant($service, $bookingDto, $participantIndex); }); } + + /** + * Generates label for rental insurance checkbox including pricing information. + */ + private function getRentalInsuranceCheckboxLabel(array $rentalInsuranceServices): string + { + if (empty($rentalInsuranceServices)) { + return 'Leihmaterial-Versicherung'; + } + $rentalInsuranceService = reset($rentalInsuranceServices); // Get the first (and only) rental insurance service + return $this->formatServiceLabelWithPrice($rentalInsuranceService); + } } diff --git a/src/Form/Service/ParticipantRentalInsuranceFieldHandler.php b/src/Form/Service/ParticipantRentalInsuranceFieldHandler.php new file mode 100644 index 0000000..3d9baad --- /dev/null +++ b/src/Form/Service/ParticipantRentalInsuranceFieldHandler.php @@ -0,0 +1,133 @@ + $submittedData The submitted participant form data + * @param int $participantIndex The index of the participant being processed + * + * @return bool Always returns true for service selection fields + */ + public function shouldProcess(array $submittedData, int $participantIndex): bool + { + return true; // Always process to handle deselection cases + } + + /** + * Processes the rentalInsurance field for a specific participant. + * + * This method extracts the rental insurance selection from submitted form data, + * validates the selection against the participant's age constraints, and + * updates the participant DTO with the valid selection. If the rental insurance + * is no longer appropriate for the participant's age, it is automatically cleared. + * + * @param array $submittedData The submitted participant form data + * @param BookingDtoInterface $bookingDto The booking DTO to update (create or edit) + * @param int $participantIndex The index of the participant being processed + */ + public function processField(array $submittedData, BookingDtoInterface $bookingDto, int $participantIndex): void + { + // Safely get the participant object, returning early if not found + $participant = $this->getParticipant($bookingDto, $participantIndex); + if (null === $participant) { + return; + } + + // Check if rental insurance should be visible based on rental selections + $hasRentals = false === empty($participant->rentals); + + if (false === $hasRentals) { + // If no rentals are selected, clear rental insurance data + $participant->rentalInsuranceSelected = false; + $participant->rentalInsurance = null; + return; + } + + // Extract checkbox value from submitted data (this comes from the rentalInsuranceSelected property) + $rentalInsuranceSelected = $this->getFieldValue($submittedData, $this->getFieldName()); + $isRentalInsuranceSelected = (bool) $rentalInsuranceSelected; + + // Store boolean value + $participant->rentalInsuranceSelected = $isRentalInsuranceSelected; + + // Store service object based on checkbox state for pricing calculations + if (true === $isRentalInsuranceSelected) { + $participant->rentalInsurance = $this->findRentalInsuranceService($bookingDto); + } else { + $participant->rentalInsurance = null; + } + } + + /** + * Finds the rental insurance service from available services. + * + * Gets the first (and typically only) rental insurance service. + * Returns null if no rental insurance services are available. + * + * @param BookingDtoInterface $bookingDto The booking DTO containing travel data + * + * @return Service|null The rental insurance service object, or null if not found + */ + private function findRentalInsuranceService(BookingDtoInterface $bookingDto): ?Service + { + $rentalInsuranceServices = $bookingDto->travel->getAdditionalServicesBySubTypes(Constants::TOKEN_RENTAL_INSURANCE, true, true); + + if (empty($rentalInsuranceServices)) { + return null; + } + + return reset($rentalInsuranceServices); // Get the first (and typically only) rental insurance service + } +} diff --git a/src/Form/Service/ParticipantRentalsFieldHandler.php b/src/Form/Service/ParticipantRentalsFieldHandler.php index 5da61eb..dd6dab2 100644 --- a/src/Form/Service/ParticipantRentalsFieldHandler.php +++ b/src/Form/Service/ParticipantRentalsFieldHandler.php @@ -26,11 +26,6 @@ class ParticipantRentalsFieldHandler extends AbstractParticipantFieldHandler return 'rentals'; } - public function getDependencies(): array - { - return ['dateOfBirth']; - } - /** * Determines if this handler should process the field based on submitted data. * diff --git a/src/Service/BookingPriceCalculatorService.php b/src/Service/BookingPriceCalculatorService.php index 0f6a942..4491229 100644 --- a/src/Service/BookingPriceCalculatorService.php +++ b/src/Service/BookingPriceCalculatorService.php @@ -337,6 +337,7 @@ class BookingPriceCalculatorService Constants::TOKEN_SKI_PASS => 'Skipässe', Constants::TOKEN_ADDITIONAL => 'Zusatzleistungen', Constants::TOKEN_BOARD => 'Verpflegung', + Constants::TOKEN_RENTAL_INSURANCE => 'Leihmaterial-Versicherung', 'transportation' => 'Beförderung', 'rentals' => 'Leihmaterial', // Normalized rental subtype ]; @@ -354,11 +355,15 @@ class BookingPriceCalculatorService */ private function aggregateParticipantServices(ParticipantDto $participant, array &$serviceAggregation): void { - // Handle single service selections (skiPass) + // Handle single service selections (skiPass, rentalInsurance) if (null !== $participant->skiPass && null !== $participant->skiPass->price) { $this->addToServiceAggregation($serviceAggregation, $participant->skiPass, 1); } + if (null !== $participant->rentalInsurance && null !== $participant->rentalInsurance->price) { + $this->addToServiceAggregation($serviceAggregation, $participant->rentalInsurance, 1); + } + // Handle multiple service selections $multipleServiceArrays = [ 'courses' => $participant->courses, diff --git a/templates/booking/create_step_2.html.twig b/templates/booking/create_step_2.html.twig index 46651f2..236634b 100644 --- a/templates/booking/create_step_2.html.twig +++ b/templates/booking/create_step_2.html.twig @@ -41,11 +41,13 @@ {{ form_row(participant.email) }} {{ form_row(participant.mobile) }} -
- {{ form_row(participant.bodyDimensions.height) }} - {{ form_row(participant.bodyDimensions.shoeSize) }} - {{ form_row(participant.bodyDimensions.weight) }} -
+ {% if participant.bodyDimensions is defined %} +
+ {{ form_row(participant.bodyDimensions.height) }} + {{ form_row(participant.bodyDimensions.shoeSize) }} + {{ form_row(participant.bodyDimensions.weight) }} +
+ {% endif %}
{{ form_row(participant.assignedRoomId, { 'attr': { @@ -113,6 +115,15 @@ } }) }} {% endif %} + {% if participant.rentalInsurance is defined %} + {{ form_row(participant.rentalInsurance, { + 'attr': { + 'hx-trigger': 'change', + 'hx-post': path('app_booking_create_step_2_refresh'), + 'hx-swap': 'none' + } + }) }} + {% endif %} {% if participant.board is defined %} {{ form_row(participant.board, { 'attr': {