feat: configurable choices for body dimensions

closes #8697phk21
This commit is contained in:
Björn Fromme
2025-12-10 13:48:47 +01:00
parent 145a08e511
commit 0e9c5e6e1a
7 changed files with 174 additions and 22 deletions
+20
View File
@@ -7,6 +7,26 @@ parameters:
travel_info_base_url: '%env(APP_TRAVEL_INFO_BASE_URL)%' travel_info_base_url: '%env(APP_TRAVEL_INFO_BASE_URL)%'
path_to_keys: '%kernel.project_dir%/config/secret' path_to_keys: '%kernel.project_dir%/config/secret'
# Body dimensions choices for BodyDimensionsType
body_dimensions.height_choices:
'bis 148cm': '-148'
'149 - 157cm': '149-157'
'158 - 166cm': '158-166'
'167 - 178cm': '167-178'
'179 - 185cm': '179-185'
'186 - 194cm': '186-194'
'195cm oder mehr': '195cm+'
body_dimensions.shoe_size_min: 36
body_dimensions.shoe_size_max: 48
body_dimensions.weight_choices:
'42 - 48kg': '42-48'
'49 - 57kg': '49-57'
'58 - 66kg': '58-66'
'67 - 78kg': '67-78'
'79 - 85kg': '79-85'
'86 - 94kg': '86-94'
'95kg oder mehr': '95k+'
services: services:
# default configuration for services in *this* file # default configuration for services in *this* file
_defaults: _defaults:
@@ -18,6 +18,7 @@ use App\Service\ParticipantPrepopulationService;
use App\Service\RoomAssignmentService; use App\Service\RoomAssignmentService;
use App\Service\TravelDataService; use App\Service\TravelDataService;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Routing\Attribute\Route;
@@ -35,6 +36,10 @@ class Step2Controller extends AbstractController
use HxTrait; use HxTrait;
use ParticipantCardFlowTrait; use ParticipantCardFlowTrait;
/**
* @param array<string, string> $heightChoices
* @param array<string, string> $weightChoices
*/
public function __construct( public function __construct(
private readonly BookingService $bookingService, private readonly BookingService $bookingService,
private readonly BookingSummaryDataService $summaryDataService, private readonly BookingSummaryDataService $summaryDataService,
@@ -43,9 +48,43 @@ class Step2Controller extends AbstractController
private readonly ParticipantCardDataService $participantCardService, private readonly ParticipantCardDataService $participantCardService,
private readonly ParticipantFieldOptionsProvider $fieldOptionsProvider, private readonly ParticipantFieldOptionsProvider $fieldOptionsProvider,
private readonly ParticipantPrepopulationService $prepopulationService, private readonly ParticipantPrepopulationService $prepopulationService,
#[Autowire(param: 'body_dimensions.height_choices')]
private readonly array $heightChoices,
#[Autowire(param: 'body_dimensions.weight_choices')]
private readonly array $weightChoices,
#[Autowire(param: 'body_dimensions.shoe_size_min')]
private readonly int $shoeSizeMin,
#[Autowire(param: 'body_dimensions.shoe_size_max')]
private readonly int $shoeSizeMax,
) { ) {
} }
/**
* @return array<string, string>
*/
private function getHeightChoices(): array
{
return $this->heightChoices;
}
/**
* @return array<string, string>
*/
private function getWeightChoices(): array
{
return $this->weightChoices;
}
private function getShoeSizeMin(): int
{
return $this->shoeSizeMin;
}
private function getShoeSizeMax(): int
{
return $this->shoeSizeMax;
}
/** /**
* Display card grid for all participants. * Display card grid for all participants.
*/ */
@@ -25,6 +25,7 @@ use App\Service\ParticipantCardDataService;
use App\Service\TravelDataService; use App\Service\TravelDataService;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Routing\Attribute\Route;
@@ -45,6 +46,10 @@ class IndexController extends AbstractController
use HxTrait; use HxTrait;
use Traits\ParticipantCardFlowTrait; use Traits\ParticipantCardFlowTrait;
/**
* @param array<string, string> $heightChoices
* @param array<string, string> $weightChoices
*/
public function __construct( public function __construct(
private readonly ApiClient $apiClient, private readonly ApiClient $apiClient,
private readonly BookingEditDataLoaderService $dataLoader, private readonly BookingEditDataLoaderService $dataLoader,
@@ -55,9 +60,43 @@ class IndexController extends AbstractController
private readonly ParticipantCardDataService $participantCardService, private readonly ParticipantCardDataService $participantCardService,
private readonly Crypt $crypt, private readonly Crypt $crypt,
private readonly LoggerInterface $logger, private readonly LoggerInterface $logger,
#[Autowire(param: 'body_dimensions.height_choices')]
private readonly array $heightChoices,
#[Autowire(param: 'body_dimensions.weight_choices')]
private readonly array $weightChoices,
#[Autowire(param: 'body_dimensions.shoe_size_min')]
private readonly int $shoeSizeMin,
#[Autowire(param: 'body_dimensions.shoe_size_max')]
private readonly int $shoeSizeMax,
) { ) {
} }
/**
* @return array<string, string>
*/
private function getHeightChoices(): array
{
return $this->heightChoices;
}
/**
* @return array<string, string>
*/
private function getWeightChoices(): array
{
return $this->weightChoices;
}
private function getShoeSizeMin(): int
{
return $this->shoeSizeMin;
}
private function getShoeSizeMax(): int
{
return $this->shoeSizeMax;
}
/** /**
* Display participant cards overview. * Display participant cards overview.
*/ */
@@ -181,6 +220,10 @@ class IndexController extends AbstractController
// Create form for participant with booking context // Create form for participant with booking context
$form = $this->createForm(BookingParticipantType::class, $wrapper, [ $form = $this->createForm(BookingParticipantType::class, $wrapper, [
'booking_context' => $bookingDto, 'booking_context' => $bookingDto,
'height_choices' => $this->heightChoices,
'weight_choices' => $this->weightChoices,
'shoe_size_min' => $this->shoeSizeMin,
'shoe_size_max' => $this->shoeSizeMax,
]); ]);
$form->handleRequest($request); $form->handleRequest($request);
@@ -264,6 +307,10 @@ class IndexController extends AbstractController
$form = $this->createForm(BookingParticipantType::class, $wrapper, [ $form = $this->createForm(BookingParticipantType::class, $wrapper, [
'booking_context' => $bookingDto, 'booking_context' => $bookingDto,
'validation_groups' => false, 'validation_groups' => false,
'height_choices' => $this->heightChoices,
'weight_choices' => $this->weightChoices,
'shoe_size_min' => $this->shoeSizeMin,
'shoe_size_max' => $this->shoeSizeMax,
]); ]);
$form->handleRequest($request); $form->handleRequest($request);
@@ -71,14 +71,32 @@ trait ParticipantCardFlowTrait
bookingContext: $bookingDto, bookingContext: $bookingDto,
); );
// Merge default options with provided options // Merge default options with provided options and body dimensions
$formOptions = array_merge([ $formOptions = array_merge([
'booking_context' => $bookingDto, 'booking_context' => $bookingDto,
'height_choices' => $this->getHeightChoices(),
'weight_choices' => $this->getWeightChoices(),
'shoe_size_min' => $this->getShoeSizeMin(),
'shoe_size_max' => $this->getShoeSizeMax(),
], $options); ], $options);
return $this->createForm(BookingParticipantType::class, $wrapper, $formOptions); return $this->createForm(BookingParticipantType::class, $wrapper, $formOptions);
} }
/**
* @return array<string, string>
*/
abstract private function getHeightChoices(): array;
/**
* @return array<string, string>
*/
abstract private function getWeightChoices(): array;
abstract private function getShoeSizeMin(): int;
abstract private function getShoeSizeMax(): int;
/** /**
* Collects notifications from all participants and clears them from DTOs. * Collects notifications from all participants and clears them from DTOs.
* *
+21 -20
View File
@@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace App\Form; namespace App\Form;
use App\Form\Model\ParticipantDto; use App\Form\Model\ParticipantDto;
@@ -12,44 +14,35 @@ class BodyDimensionsType extends AbstractType
{ {
public function buildForm(FormBuilderInterface $builder, array $options): void public function buildForm(FormBuilderInterface $builder, array $options): void
{ {
$shoeSizeChoices = array_combine(
range($options['shoe_size_min'], $options['shoe_size_max']),
range($options['shoe_size_min'], $options['shoe_size_max'])
);
$builder $builder
->add('height', ChoiceType::class, [ ->add('height', ChoiceType::class, [
'label' => 'Körpergröße', 'label' => 'Körpergröße',
'required' => $options['height_required'] ?? false, 'required' => $options['height_required'],
'expanded' => false, 'expanded' => false,
'multiple' => false, 'multiple' => false,
'placeholder' => 'Keine Angabe', 'placeholder' => 'Keine Angabe',
'choices' => [ 'choices' => $options['height_choices'],
'bis 148cm' => '-148',
'149 - 157cm' => '149-157',
'158 - 166cm' => '158-166',
'167 - 178cm' => '167-178',
'179 - 194cm' => '179-197',
'195cm oder mehr' => '195cm+',
],
]) ])
->add('shoeSize', ChoiceType::class, [ ->add('shoeSize', ChoiceType::class, [
'label' => 'Schuhgröße', 'label' => 'Schuhgröße',
'required' => $options['shoeSize_required'] ?? false, 'required' => $options['shoeSize_required'],
'expanded' => false, 'expanded' => false,
'multiple' => false, 'multiple' => false,
'placeholder' => 'Keine Angabe', 'placeholder' => 'Keine Angabe',
'choices' => array_combine(range(36, 48), range(36, 48)), 'choices' => $shoeSizeChoices,
]) ])
->add('weight', ChoiceType::class, [ ->add('weight', ChoiceType::class, [
'label' => 'Gewicht', 'label' => 'Gewicht',
'required' => $options['weight_required'] ?? false, 'required' => $options['weight_required'],
'expanded' => false, 'expanded' => false,
'multiple' => false, 'multiple' => false,
'placeholder' => 'Keine Angabe', 'placeholder' => 'Keine Angabe',
'choices' => [ 'choices' => $options['weight_choices'],
'42 - 48kg' => '42-48',
'49 - 57kg' => '49-57',
'58 - 66kg' => '58-66',
'67 - 78kg' => '67-78',
'79 - 94kg' => '79-94',
'95kg oder mehr' => '95k+',
],
]); ]);
} }
@@ -60,6 +53,10 @@ class BodyDimensionsType extends AbstractType
'height_required' => false, 'height_required' => false,
'weight_required' => false, 'weight_required' => false,
'shoeSize_required' => false, 'shoeSize_required' => false,
'height_choices' => [],
'weight_choices' => [],
'shoe_size_min' => 36,
'shoe_size_max' => 48,
'help' => 'Du kannst die Daten auch später nachreichen', 'help' => 'Du kannst die Daten auch später nachreichen',
'help_attr' => [ 'help_attr' => [
'class' => 'lg:col-span-3 text-sm -mt-2 px-2', 'class' => 'lg:col-span-3 text-sm -mt-2 px-2',
@@ -69,5 +66,9 @@ class BodyDimensionsType extends AbstractType
$resolver->setAllowedTypes('height_required', 'bool'); $resolver->setAllowedTypes('height_required', 'bool');
$resolver->setAllowedTypes('weight_required', 'bool'); $resolver->setAllowedTypes('weight_required', 'bool');
$resolver->setAllowedTypes('shoeSize_required', 'bool'); $resolver->setAllowedTypes('shoeSize_required', 'bool');
$resolver->setAllowedTypes('height_choices', 'array');
$resolver->setAllowedTypes('weight_choices', 'array');
$resolver->setAllowedTypes('shoe_size_min', 'int');
$resolver->setAllowedTypes('shoe_size_max', 'int');
} }
} }
+16 -1
View File
@@ -88,7 +88,12 @@ class BookingEditParticipantType extends AbstractType
'required' => false, 'required' => false,
'sanitize_html' => true, 'sanitize_html' => true,
], $getState('mobile'))) ], $getState('mobile')))
->add('bodyDimensions', BodyDimensionsType::class); ->add('bodyDimensions', BodyDimensionsType::class, [
'height_choices' => $options['height_choices'],
'weight_choices' => $options['weight_choices'],
'shoe_size_min' => $options['shoe_size_min'],
'shoe_size_max' => $options['shoe_size_max'],
]);
$commonChoiceFieldOptions = [ $commonChoiceFieldOptions = [
'required' => false, 'required' => false,
@@ -371,7 +376,17 @@ class BookingEditParticipantType extends AbstractType
'applicant_id' => null, 'applicant_id' => null,
// Booking DTO for field state provider (must be set by parent form) // Booking DTO for field state provider (must be set by parent form)
'booking' => null, 'booking' => null,
// Body dimensions choices
'height_choices' => [],
'weight_choices' => [],
'shoe_size_min' => 36,
'shoe_size_max' => 48,
]); ]);
$resolver->setAllowedTypes('height_choices', 'array');
$resolver->setAllowedTypes('weight_choices', 'array');
$resolver->setAllowedTypes('shoe_size_min', 'int');
$resolver->setAllowedTypes('shoe_size_max', 'int');
} }
/** /**
+12
View File
@@ -252,6 +252,10 @@ class BookingParticipantType extends AbstractType
if ($this->fieldStateProvider->shouldIncludeField('bodyDimensions', $bookingDto, $participantIndex)) { if ($this->fieldStateProvider->shouldIncludeField('bodyDimensions', $bookingDto, $participantIndex)) {
$form->add('bodyDimensions', BodyDimensionsType::class, [ $form->add('bodyDimensions', BodyDimensionsType::class, [
'property_path' => 'participant', 'property_path' => 'participant',
'height_choices' => $form->getConfig()->getOption('height_choices'),
'weight_choices' => $form->getConfig()->getOption('weight_choices'),
'shoe_size_min' => $form->getConfig()->getOption('shoe_size_min'),
'shoe_size_max' => $form->getConfig()->getOption('shoe_size_max'),
]); ]);
} }
} }
@@ -435,6 +439,10 @@ class BookingParticipantType extends AbstractType
'data_class' => ParticipantEditDto::class, 'data_class' => ParticipantEditDto::class,
'selected_rooms' => [], 'selected_rooms' => [],
'booking_context' => null, 'booking_context' => null,
'height_choices' => [],
'weight_choices' => [],
'shoe_size_min' => 36,
'shoe_size_max' => 48,
'validation_groups' => function (FormInterface $form) { 'validation_groups' => function (FormInterface $form) {
/** @var ParticipantEditDto $data */ /** @var ParticipantEditDto $data */
$data = $form->getData(); $data = $form->getData();
@@ -462,5 +470,9 @@ class BookingParticipantType extends AbstractType
$resolver->setAllowedTypes('selected_rooms', 'array'); $resolver->setAllowedTypes('selected_rooms', 'array');
$resolver->setAllowedTypes('booking_context', ['null', BookingDto::class]); $resolver->setAllowedTypes('booking_context', ['null', BookingDto::class]);
$resolver->setAllowedTypes('height_choices', 'array');
$resolver->setAllowedTypes('weight_choices', 'array');
$resolver->setAllowedTypes('shoe_size_min', 'int');
$resolver->setAllowedTypes('shoe_size_max', 'int');
} }
} }