feat: additional filter options for documents

This commit is contained in:
Björn Fromme
2025-01-13 11:58:32 +01:00
parent 520e6b79f0
commit c5df1c2a67
8 changed files with 146 additions and 15 deletions
+1 -4
View File
@@ -32,6 +32,7 @@ services:
autoconfigure: true autoconfigure: true
bind: bind:
$logger: '@monolog.logger.myep' $logger: '@monolog.logger.myep'
$destinations: '%destinations%'
App\: App\:
resource: '../src/' resource: '../src/'
@@ -166,7 +167,3 @@ services:
$defaults: $defaults:
from: '%default_email_from%' from: '%default_email_from%'
to: '%default_email_to%' to: '%default_email_to%'
App\Form\AssignmentFilterType:
arguments:
$destinations: '%destinations%'
@@ -4,6 +4,7 @@ namespace App\Controller\Common;
use App\Form\DocumentFilterType; use App\Form\DocumentFilterType;
use App\Htmx\HxRedirectResponse; use App\Htmx\HxRedirectResponse;
use App\Repository\AssignmentRepository;
use App\Service\Common\DocumentFilterHandler; use App\Service\Common\DocumentFilterHandler;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
@@ -13,8 +14,10 @@ use Symfony\Component\Security\Http\Attribute\IsGranted;
class DocumentFilterController extends AbstractController class DocumentFilterController extends AbstractController
{ {
public function __construct(private readonly DocumentFilterHandler $filterHandler) public function __construct(
{ private readonly DocumentFilterHandler $filterHandler,
private readonly AssignmentRepository $assignmentRepository,
) {
} }
#[Route('/common/document/filter', name: 'app_common_document_filter')] #[Route('/common/document/filter', name: 'app_common_document_filter')]
@@ -22,8 +25,12 @@ class DocumentFilterController extends AbstractController
public function index(Request $request): Response public function index(Request $request): Response
{ {
$formData = $this->filterHandler->getFilterSettings(); $formData = $this->filterHandler->getFilterSettings();
$filterOptions = $this->assignmentRepository->getFilterOptions();
$formOptions = [
'job_profiles' => $filterOptions['jobProfiles'],
];
$form = $this->createForm(DocumentFilterType::class, $formData); $form = $this->createForm(DocumentFilterType::class, $formData, $formOptions);
$form->handleRequest($request); $form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) { if ($form->isSubmitted() && $form->isValid()) {
+9 -7
View File
@@ -19,8 +19,15 @@ use Symfony\Component\OptionsResolver\OptionsResolver;
class AssignmentFilterType extends AbstractType class AssignmentFilterType extends AbstractType
{ {
private array $hotelChoices = [];
public function __construct(private readonly Security $security, private readonly array $destinations) public function __construct(private readonly Security $security, private readonly array $destinations)
{ {
$destinations = $this->destinations;
sort($destinations);
foreach ($destinations as $item) {
$this->hotelChoices[$item] = $item;
}
} }
public function buildForm(FormBuilderInterface $builder, array $options): void public function buildForm(FormBuilderInterface $builder, array $options): void
@@ -99,18 +106,13 @@ class AssignmentFilterType extends AbstractType
]) ])
; ;
} }
$hotelChoices = [];
$destinations = $this->destinations;
sort($destinations);
foreach ($destinations as $item) {
$hotelChoices[$item] = $item;
}
$builder $builder
->add('hotels', MultiselectType::class, [ ->add('hotels', MultiselectType::class, [
'label' => 'Haus/Destination', 'label' => 'Haus/Destination',
'required' => false, 'required' => false,
'empty_label' => 'nicht filtern', 'empty_label' => 'nicht filtern',
'choices' => $hotelChoices, 'choices' => $this->hotelChoices,
]) ])
; ;
} }
+39
View File
@@ -2,6 +2,7 @@
namespace App\Form; namespace App\Form;
use App\Entity\JobProfile;
use App\Entity\Teamer; use App\Entity\Teamer;
use App\Entity\Upload; use App\Entity\Upload;
use App\Model\DocumentFilterDto; use App\Model\DocumentFilterDto;
@@ -13,6 +14,17 @@ use Symfony\Component\OptionsResolver\OptionsResolver;
class DocumentFilterType extends AbstractType class DocumentFilterType extends AbstractType
{ {
private array $hotelChoices = [];
public function __construct(private readonly array $destinations)
{
$destinations = $this->destinations;
sort($destinations);
foreach ($destinations as $item) {
$this->hotelChoices[$item] = $item;
}
}
public function buildForm(FormBuilderInterface $builder, array $options): void public function buildForm(FormBuilderInterface $builder, array $options): void
{ {
$builder $builder
@@ -32,6 +44,20 @@ class DocumentFilterType extends AbstractType
'Honorarvertrag' => Upload::TYPE_CONTRACT, 'Honorarvertrag' => Upload::TYPE_CONTRACT,
], ],
]) ])
->add('jobProfiles', MultiselectEntityType::class, [
'label' => 'Job-Profil',
'class' => JobProfile::class,
'required' => false,
'empty_label' => 'nicht filtern',
'choice_label' => 'name',
'choices' => $options['job_profiles'],
])
->add('hotels', MultiselectType::class, [
'label' => 'Haus/Destination',
'required' => false,
'empty_label' => 'nicht filtern',
'choices' => $this->hotelChoices,
])
->add('dateFrom', DatepickerType::class, [ ->add('dateFrom', DatepickerType::class, [
'label' => 'Einsatzzeitraum von', 'label' => 'Einsatzzeitraum von',
'attr' => [ 'attr' => [
@@ -44,6 +70,18 @@ class DocumentFilterType extends AbstractType
'placeholder' => 'nicht filtern', 'placeholder' => 'nicht filtern',
], ],
]) ])
->add('status', ChoiceType::class, [
'label' => 'Status',
'required' => false,
'placeholder' => 'nicht filtern',
'choices' => [
'neu' => Upload::STATUS_NEW,
'in Bearbeitung' => Upload::STATUS_PENDING,
'bestätigt' => Upload::STATUS_CHECKED,
'abgelehnt' => Upload::STATUS_REJECTED,
'bezahlt' => Upload::STATUS_PAID,
],
])
->add('apply', SubmitType::class, [ ->add('apply', SubmitType::class, [
'label' => 'filtern', 'label' => 'filtern',
]) ])
@@ -58,6 +96,7 @@ class DocumentFilterType extends AbstractType
$resolver $resolver
->setDefaults([ ->setDefaults([
'data_class' => DocumentFilterDto::class, 'data_class' => DocumentFilterDto::class,
'job_profiles' => [],
]) ])
; ;
} }
+39
View File
@@ -8,8 +8,11 @@ class DocumentFilterDto extends AbstractFilterDto
{ {
protected ?Teamer $teamer = null; protected ?Teamer $teamer = null;
protected ?string $type = null; protected ?string $type = null;
protected array $jobProfiles = [];
protected array $hotels = [];
protected ?\DateTimeImmutable $dateFrom = null; protected ?\DateTimeImmutable $dateFrom = null;
protected ?\DateTimeImmutable $dateTo = null; protected ?\DateTimeImmutable $dateTo = null;
protected ?string $status = null;
public function getTeamer(): ?Teamer public function getTeamer(): ?Teamer
{ {
@@ -35,6 +38,30 @@ class DocumentFilterDto extends AbstractFilterDto
return $this; return $this;
} }
public function getJobProfiles(): array
{
return $this->jobProfiles;
}
public function setJobProfiles(array $jobProfiles): static
{
$this->jobProfiles = $jobProfiles;
return $this;
}
public function getHotels(): ?array
{
return $this->hotels;
}
public function setHotels(array $hotels): static
{
$this->hotels = $hotels;
return $this;
}
public function getDateFrom(): ?\DateTimeImmutable public function getDateFrom(): ?\DateTimeImmutable
{ {
return $this->dateFrom; return $this->dateFrom;
@@ -58,4 +85,16 @@ class DocumentFilterDto extends AbstractFilterDto
return $this; return $this;
} }
public function getStatus(): ?string
{
return $this->status;
}
public function setStatus(?string $status): static
{
$this->status = $status;
return $this;
}
} }
+21
View File
@@ -123,6 +123,27 @@ class UploadRepository extends ServiceEntityRepository
; ;
} }
if (null !== $status = $filterDto->getStatus()) {
$qb
->andWhere($qb->expr()->eq('upload.status', ':status'))
->setParameter('status', $status)
;
}
if ($jobProfiles = $filterDto->getJobProfiles()) {
$qb
->andWhere($qb->expr()->in('assignment.jobProfile', ':jobProfiles'))
->setParameter('jobProfiles', $jobProfiles)
;
}
foreach ($filterDto->getHotels() as $hotel) {
$qb
->andWhere($qb->expr()->like('destination.hotel', ':hotel'))
->setParameter('hotel', '%'.$hotel.'%')
;
}
return $qb->getQuery(); return $qb->getQuery();
} }
@@ -2,6 +2,7 @@
namespace App\Service\Common; namespace App\Service\Common;
use App\Entity\JobProfile;
use App\Entity\Teamer; use App\Entity\Teamer;
use App\Model\AbstractFilterDto; use App\Model\AbstractFilterDto;
use App\Model\DocumentFilterDto; use App\Model\DocumentFilterDto;
@@ -33,6 +34,23 @@ class DocumentFilterHandler extends AbstractFilterHandler
$filterDto->setTeamer($teamer); $filterDto->setTeamer($teamer);
} }
if (isset($data['job_profiles']) && 0 < count($data['job_profiles'])) {
$jobProfiles = $this
->entityManager
->getRepository(JobProfile::class)
->findBy(['id' => $data['job_profiles']])
;
$filterDto->setJobProfiles($jobProfiles);
}
if (isset($data['hotels']) && 0 < count($data['hotels'])) {
$filterDto->setHotels($data['hotels']);
}
if (isset($data['status'])) {
$filterDto->setStatus($data['status']);
}
return $filterDto; return $filterDto;
} }
@@ -44,6 +62,11 @@ class DocumentFilterHandler extends AbstractFilterHandler
'type' => $filterDto->getType(), 'type' => $filterDto->getType(),
'date_from' => $filterDto->getDateFrom(), 'date_from' => $filterDto->getDateFrom(),
'date_to' => $filterDto->getDateTo(), 'date_to' => $filterDto->getDateTo(),
'job_profiles' => array_map(function (JobProfile $jobProfile) {
return $jobProfile->getId();
}, $filterDto->getJobProfiles()),
'hotels' => $filterDto->getHotels(),
'status' => $filterDto->getStatus(),
]); ]);
} }
} }
@@ -6,6 +6,9 @@
{{ form_start(filterForm, { 'attr': { 'hx-post': app.request.uri, 'hx-target': '#htmx-modal', 'hx-swap': 'outerHTML' } }) }} {{ form_start(filterForm, { 'attr': { 'hx-post': app.request.uri, 'hx-target': '#htmx-modal', 'hx-swap': 'outerHTML' } }) }}
<div class="flex flex-col space-y-2 pb-4"> <div class="flex flex-col space-y-2 pb-4">
{{ form_row(filterForm.type) }} {{ form_row(filterForm.type) }}
{{ form_row(filterForm.status) }}
{{ form_row(filterForm.jobProfiles) }}
{{ form_row(filterForm.hotels) }}
{{ form_row(filterForm.teamer) }} {{ form_row(filterForm.teamer) }}
{{ form_row(filterForm.dateFrom) }} {{ form_row(filterForm.dateFrom) }}
{{ form_row(filterForm.dateTo) }} {{ form_row(filterForm.dateTo) }}