From 7d84f321dadf24c9ba6ece0b18d357f8ffe5d01b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Fromme?= Date: Mon, 15 Jul 2024 18:25:14 +0200 Subject: [PATCH] feat: multiselect field for filtering --- assets/controllers/multiselect_controller.js | 30 ++++++++++++++++ src/Form/AssignmentFilterType.php | 8 ++--- src/Form/MultiselectEntityType.php | 36 +++++++++++++++++++ src/Form/MultiselectType.php | 36 +++++++++++++++++++ src/Model/AssignmentFilterDto.php | 20 +++++------ src/Repository/AssignmentRepository.php | 12 +++---- .../Common/AssignmentFilterHandler.php | 18 +++++----- .../common/modal_assignment_filter.html.twig | 4 +-- templates/forms.html.twig | 18 +++++++++- 9 files changed, 151 insertions(+), 31 deletions(-) create mode 100644 assets/controllers/multiselect_controller.js create mode 100644 src/Form/MultiselectEntityType.php create mode 100644 src/Form/MultiselectType.php diff --git a/assets/controllers/multiselect_controller.js b/assets/controllers/multiselect_controller.js new file mode 100644 index 0000000..2dcca0e --- /dev/null +++ b/assets/controllers/multiselect_controller.js @@ -0,0 +1,30 @@ +import { Controller } from '@hotwired/stimulus' + +export default class extends Controller { + + static targets = [ 'label', 'dropdown', 'choice' ] + static values = { placeholder: String } + + connect() { + this.update() + } + + toggle() { + this.dropdownTarget.classList.toggle('hidden') + } + + update() { + let labels = [] + + this.choiceTargets.forEach((choice) => { + if (choice.checked) { + labels.push(choice.dataset.label) + } + this.labelTarget.innerText = labels.join(', ') + }) + + if (0 === labels.length) { + this.labelTarget.innerText = this.placeholderValue + } + } +} \ No newline at end of file diff --git a/src/Form/AssignmentFilterType.php b/src/Form/AssignmentFilterType.php index b37c9ae..a1d06c0 100644 --- a/src/Form/AssignmentFilterType.php +++ b/src/Form/AssignmentFilterType.php @@ -80,11 +80,11 @@ class AssignmentFilterType extends AbstractType if (0 < count($options['job_profiles'])) { $builder - ->add('jobProfile', EntityType::class, [ + ->add('jobProfiles', MultiselectEntityType::class, [ 'label' => 'Job-Profil', 'class' => JobProfile::class, 'required' => false, - 'placeholder' => 'nicht filtern', + 'empty_label' => 'nicht filtern', 'choice_label' => 'name', 'choices' => $options['job_profiles'], ]) @@ -96,10 +96,10 @@ class AssignmentFilterType extends AbstractType $choices[$item->getHotel()] = $item->getHotelCode(); } $builder - ->add('hotel', ChoiceType::class, [ + ->add('hotels', MultiselectType::class, [ 'label' => 'Haus', 'required' => false, - 'placeholder' => 'nicht filtern', + 'empty_label' => 'nicht filtern', 'choices' => $choices, ]) ; diff --git a/src/Form/MultiselectEntityType.php b/src/Form/MultiselectEntityType.php new file mode 100644 index 0000000..7a28c12 --- /dev/null +++ b/src/Form/MultiselectEntityType.php @@ -0,0 +1,36 @@ +vars['empty_label'] = $options['empty_label']; + } + + public function configureOptions(OptionsResolver $resolver): void + { + $resolver->setDefaults([ + 'multiple' => true, + 'expanded' => true, + 'empty_label' => null, + ]); + } + + public function getBlockPrefix(): string + { + return 'multiselect'; + } +} \ No newline at end of file diff --git a/src/Form/MultiselectType.php b/src/Form/MultiselectType.php new file mode 100644 index 0000000..51526c3 --- /dev/null +++ b/src/Form/MultiselectType.php @@ -0,0 +1,36 @@ +vars['empty_label'] = $options['empty_label']; + } + + public function configureOptions(OptionsResolver $resolver): void + { + $resolver->setDefaults([ + 'multiple' => true, + 'expanded' => true, + 'empty_label' => null, + ]); + } + + public function getBlockPrefix(): string + { + return 'multiselect'; + } +} \ No newline at end of file diff --git a/src/Model/AssignmentFilterDto.php b/src/Model/AssignmentFilterDto.php index 8627a58..7878388 100644 --- a/src/Model/AssignmentFilterDto.php +++ b/src/Model/AssignmentFilterDto.php @@ -14,8 +14,8 @@ class AssignmentFilterDto extends AbstractFilterDto protected ?\DateTimeImmutable $dateFrom = null; protected ?\DateTimeImmutable $dateTo = null; - protected ?JobProfile $jobProfile = null; - protected ?string $hotel = null; + protected array $jobProfiles = []; + protected array $hotels = []; protected ?int $duration = null; protected ?string $status = null; protected bool $includePast = false; @@ -44,26 +44,26 @@ class AssignmentFilterDto extends AbstractFilterDto return $this; } - public function getJobProfile(): ?JobProfile + public function getJobProfiles(): array { - return $this->jobProfile; + return $this->jobProfiles; } - public function setJobProfile(?JobProfile $jobProfile): static + public function setJobProfiles(array $jobProfiles): static { - $this->jobProfile = $jobProfile; + $this->jobProfiles = $jobProfiles; return $this; } - public function getHotel(): ?string + public function getHotels(): ?array { - return $this->hotel; + return $this->hotels; } - public function setHotel(?string $hotel): static + public function setHotels(array $hotels): static { - $this->hotel = $hotel; + $this->hotels = $hotels; return $this; } diff --git a/src/Repository/AssignmentRepository.php b/src/Repository/AssignmentRepository.php index 9b79818..1bf1bf9 100644 --- a/src/Repository/AssignmentRepository.php +++ b/src/Repository/AssignmentRepository.php @@ -154,17 +154,17 @@ class AssignmentRepository extends ServiceEntityRepository ; } - if (null !== $jobProfile = $filterDto->getJobProfile()) { + if ($jobProfiles = $filterDto->getJobProfiles()) { $qb - ->andWhere($qb->expr()->eq('assignment.jobProfile', ':jobProfile')) - ->setParameter('jobProfile', $jobProfile) + ->andWhere($qb->expr()->in('assignment.jobProfile', ':jobProfiles')) + ->setParameter('jobProfiles', $jobProfiles) ; } - if (null !== $hotelCode = $filterDto->getHotel()) { + if ($hotelCodes = $filterDto->getHotels()) { $qb - ->andWhere($qb->expr()->eq('destination.hotelCode', ':hotelCode')) - ->setParameter('hotelCode', $hotelCode) + ->andWhere($qb->expr()->in('destination.hotelCode', ':hotelCodes')) + ->setParameter('hotelCodes', $hotelCodes) ; } diff --git a/src/Service/Common/AssignmentFilterHandler.php b/src/Service/Common/AssignmentFilterHandler.php index d42d62c..884a221 100644 --- a/src/Service/Common/AssignmentFilterHandler.php +++ b/src/Service/Common/AssignmentFilterHandler.php @@ -59,16 +59,16 @@ class AssignmentFilterHandler if (isset($data['date_to'])) { $filterDto->setDateTo($data['date_to']); } - if (isset($data['job_profile']) && 0 < (int) $data['job_profile']) { - $jobProfile = $this + if (isset($data['job_profiles']) && 0 < count($data['job_profiles'])) { + $jobProfiles = $this ->entityManager ->getRepository(JobProfile::class) - ->find($data['job_profile']) + ->findBy(['id' => $data['job_profiles']]) ; - $filterDto->setJobProfile($jobProfile); + $filterDto->setJobProfiles($jobProfiles); } - if (isset($data['hotel'])) { - $filterDto->setHotel($data['hotel']); + if (isset($data['hotels']) && 0 < count($data['hotels'])) { + $filterDto->setHotels($data['hotels']); } if (isset($data['duration']) && 0 < (int) $data['duration']) { $filterDto->setDuration($data['duration']); @@ -88,8 +88,10 @@ class AssignmentFilterHandler $this->getSession()->set($this->namespace, [ 'date_from' => $filterDto->getDateFrom(), 'date_to' => $filterDto->getDateTo(), - 'job_profile' => $filterDto->getJobProfile()?->getId(), - 'hotel' => $filterDto->getHotel(), + 'job_profiles' => array_map(function (JobProfile $jobProfile) { + return $jobProfile->getId(); + }, $filterDto->getJobProfiles()), + 'hotels' => $filterDto->getHotels(), 'duration' => $filterDto->getDuration(), 'status' => $filterDto->getStatus(), 'include_past' => $filterDto->isIncludePast(), diff --git a/templates/common/modal_assignment_filter.html.twig b/templates/common/modal_assignment_filter.html.twig index db686c0..f1afe0c 100644 --- a/templates/common/modal_assignment_filter.html.twig +++ b/templates/common/modal_assignment_filter.html.twig @@ -10,8 +10,8 @@ {% endif %} {{ form_row(filterForm.dateFrom) }} {{ form_row(filterForm.dateTo) }} - {{ form_row(filterForm.jobProfile) }} - {{ form_row(filterForm.hotel) }} + {{ form_row(filterForm.jobProfiles) }} + {{ form_row(filterForm.hotels) }} {{ form_row(filterForm.duration) }} {{ form_row(filterForm.includePast) }} diff --git a/templates/forms.html.twig b/templates/forms.html.twig index 74b0f13..48deb0e 100644 --- a/templates/forms.html.twig +++ b/templates/forms.html.twig @@ -216,4 +216,20 @@ {{ icon('search', 'w-4 h-4 text-gray-700') }} -{%- endblock autocomplete_widget %} +{%- endblock autocomplete_widget -%} + +{%- block multiselect_widget -%} +
+
+
+ {{ icon('dots', 'w-4 h-4') }} +
+ +
+{%- endblock multiselect_widget -%}