diff --git a/assets/styles/components/button.css b/assets/styles/components/button.css index e719082..8da0b48 100644 --- a/assets/styles/components/button.css +++ b/assets/styles/components/button.css @@ -3,7 +3,7 @@ } .btn--small { - @apply text-xs py-1 px-2 rounded-sm; + @apply text-xs py-1 px-2 rounded-md; } .btn--secondary { diff --git a/src/Controller/Teamer/IndexController.php b/src/Controller/Teamer/IndexController.php index 10072aa..486d81a 100644 --- a/src/Controller/Teamer/IndexController.php +++ b/src/Controller/Teamer/IndexController.php @@ -4,6 +4,7 @@ namespace App\Controller\Teamer; use App\Entity\User; use App\Repository\AssignmentRepository; +use App\Service\Common\AssignmentFilterHandler; use Knp\Component\Pager\PaginatorInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Request; @@ -16,6 +17,7 @@ class IndexController extends AbstractController { public function __construct( private readonly ValidatorInterface $validator, + private readonly AssignmentFilterHandler $filterHandler, private readonly PaginatorInterface $paginator, private readonly AssignmentRepository $assignmentRepository ) { @@ -32,9 +34,15 @@ class IndexController extends AbstractController // Validate teamer data to show missing data right away $errors = $this->validator->validate($teamer, null, ['profile_preflight']); + $filterForm = $this->filterHandler->getForm('assignment_filter_teamer'); + $filterDto = $this->filterHandler->handleRequest($filterForm, $request, 'assignment_filter_teamer'); + if (true === $filterDto->isReset()) { + return $this->redirectToRoute('app_teamer_index'); + } + $query = $this ->assignmentRepository - ->getListQueryForTeamer($teamer) + ->getListQueryForTeamer($teamer, $filterDto) ; $pagination = $this->paginator->paginate( @@ -49,8 +57,9 @@ class IndexController extends AbstractController return $this->render('teamer/index.html.twig', [ 'teamer' => $teamer, - 'pagination' => $pagination, 'errors' => $errors, + 'pagination' => $pagination, + 'filterForm' => $filterForm, ]); } } \ No newline at end of file diff --git a/src/Model/AssignmentFilterDto.php b/src/Model/AssignmentFilterDto.php index 9636135..047cd78 100644 --- a/src/Model/AssignmentFilterDto.php +++ b/src/Model/AssignmentFilterDto.php @@ -17,7 +17,12 @@ class AssignmentFilterDto private ?JobProfile $jobProfile = null; private ?Destination $destination = null; private ?int $duration = null; - private bool $reset = false; + private bool $reset; + + public function __construct(bool $reset = false) + { + $this->reset = $reset; + } public function getDateFrom(): ?\DateTimeImmutable { @@ -83,11 +88,4 @@ class AssignmentFilterDto { return $this->reset; } - - public function setReset(bool $reset): static - { - $this->reset = $reset; - - return $this; - } } \ No newline at end of file diff --git a/src/Repository/AssignmentRepository.php b/src/Repository/AssignmentRepository.php index a1f5a30..55fb73b 100644 --- a/src/Repository/AssignmentRepository.php +++ b/src/Repository/AssignmentRepository.php @@ -10,6 +10,7 @@ use App\Model\AssignmentFilterDto; use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; use Doctrine\ORM\Query; use Doctrine\ORM\Query\Expr\Join; +use Doctrine\ORM\QueryBuilder; use Doctrine\Persistence\ManagerRegistry; /** @@ -39,8 +40,34 @@ class AssignmentRepository extends ServiceEntityRepository ->leftJoin('assignment.applications', 'application', Join::WITH, $qb->expr()->neq('application.status', ':status')) ->leftJoin('assignment.dispositions', 'disposition') ->setParameter('status', Application::STATUS_REJECTED) - ; + ; + $this->applyFilterSettings($filterDto, $qb); + + return $qb->getQuery(); + } + + public function getListQueryForTeamer(Teamer $teamer, AssignmentFilterDto $filterDto): Query + { + $qb = $this->createQueryBuilder('assignment'); + + $qb + ->select('assignment', 'destination', 'job_profile', 'application', 'disposition') + ->innerJoin('assignment.destination', 'destination') + ->innerJoin('assignment.jobProfile', 'job_profile') + ->where($qb->expr()->isNull('assignment.deletedAt')) + ->leftJoin('assignment.applications', 'application', Join::WITH, $qb->expr()->eq('application.teamer', ':teamer')) + ->leftJoin('assignment.dispositions', 'disposition', Join::WITH, $qb->expr()->eq('disposition.teamer', ':teamer')) + ->setParameter('teamer', $teamer) + ; + + $this->applyFilterSettings($filterDto, $qb); + + return $qb->getQuery(); + } + + private function applyFilterSettings(AssignmentFilterDto $filterDto, QueryBuilder $qb): void + { if (null !== $dateFrom = $filterDto->getDateFrom()) { $qb ->andWhere($qb->expr()->gte('destination.dateFrom', ':dateFrom')) @@ -91,24 +118,6 @@ class AssignmentRepository extends ServiceEntityRepository ; } } - - return $qb->getQuery(); - } - - public function getListQueryForTeamer(Teamer $teamer = null): Query - { - $qb = $this->createQueryBuilder('assignment'); - - return $qb - ->select('assignment', 'destination', 'job_profile', 'application', 'disposition') - ->innerJoin('assignment.destination', 'destination') - ->innerJoin('assignment.jobProfile', 'job_profile') - ->where($qb->expr()->isNull('assignment.deletedAt')) - ->leftJoin('assignment.applications', 'application', Join::WITH, $qb->expr()->eq('application.teamer', ':teamer')) - ->leftJoin('assignment.dispositions', 'disposition', Join::WITH, $qb->expr()->eq('disposition.teamer', ':teamer')) - ->setParameter('teamer', $teamer) - ->getQuery() - ; } public function getBookmarkQueryForTeamer(Teamer $teamer): Query diff --git a/src/Service/Common/AssignmentFilterHandler.php b/src/Service/Common/AssignmentFilterHandler.php index d9270e0..6dec60b 100644 --- a/src/Service/Common/AssignmentFilterHandler.php +++ b/src/Service/Common/AssignmentFilterHandler.php @@ -23,11 +23,14 @@ class AssignmentFilterHandler ) { } - public function getForm(): FormInterface + public function getForm(string $namespace = 'assignment_filter'): FormInterface { - $filterDto = $this->getFilterSettings(); + $filterDto = $this->getFilterSettings($namespace); - $assignmentRepository = $this->entityManager->getRepository(Assignment::class); + $assignmentRepository = $this + ->entityManager + ->getRepository(Assignment::class) + ; $filterOptions = $assignmentRepository->getFilterOptions(); return $this->formFactory->create(AssignmentFilterType::class, $filterDto, [ @@ -38,35 +41,30 @@ class AssignmentFilterHandler ]); } - public function handleRequest(FormInterface $form, Request $request): AssignmentFilterDto - { + public function handleRequest( + FormInterface $form, + Request $request, + string $namespace = 'assignment_filter' + ): AssignmentFilterDto { $form->handleRequest($request); $filterDto = $form->getData(); if ($form->has('reset') && $form->get('reset')->isClicked()) { - $filterDto = new AssignmentFilterDto(); - $filterDto->setReset(true); - $this->getSession()->remove('assignment_filter'); + $filterDto = new AssignmentFilterDto(true); + $this->getSession()->remove($namespace); } elseif ($form->has('apply') && $form->get('apply')->isClicked()) { - $this->saveFilterSettings($filterDto); + $this->saveFilterSettings($filterDto, $namespace); } return $filterDto; } - public function getFilterSettings(): AssignmentFilterDto + private function getFilterSettings(string $namespace): AssignmentFilterDto { - if (null === $this->getSession()->get('assignment_filter')) { + if (null === $data = $this->getSession()->get($namespace)) { return new AssignmentFilterDto(); } - return $this->loadFilterSettings(); - } - - public function loadFilterSettings(): AssignmentFilterDto - { - $data = $this->getSession()->get('assignment_filter'); - $filterDto = new AssignmentFilterDto(); if (isset($data['date_from'])) { @@ -98,9 +96,9 @@ class AssignmentFilterHandler return $filterDto; } - public function saveFilterSettings(AssignmentFilterDto $filterDto): void + private function saveFilterSettings(AssignmentFilterDto $filterDto, string $namespace): void { - $this->getSession()->set('assignment_filter', [ + $this->getSession()->set($namespace, [ 'date_from' => $filterDto->getDateFrom()?->format('Y-m-d'), 'date_to' => $filterDto->getDateTo()?->format('Y-m-d'), 'job_profile' => $filterDto->getJobProfile()?->getId(), diff --git a/templates/forms.html.twig b/templates/forms.html.twig index 4314cdd..43cd2d7 100644 --- a/templates/forms.html.twig +++ b/templates/forms.html.twig @@ -35,7 +35,7 @@ {%- block form_widget_simple -%} {%- set type = type|default('text') -%} {%- if type != 'hidden' -%} - {%- set attr = attr|merge({'class': (attr.class|default('') ~ ' block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-inset sm:text-sm sm:leading-6')|trim }) -%} + {%- set attr = attr|merge({'class': (attr.class|default('') ~ ' block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-inset text-sm')|trim }) -%} {%- if errors|length -%} {%- set attr = attr|merge({'class': attr.class|default('') ~ ' ring-red-500 placeholder-red-500 focus:ring-red-500' }) -%} {% else %} @@ -52,7 +52,7 @@ {%- endblock form_widget_simple -%} {%- block textarea_widget -%} - {%- set attr = attr|merge({'class': (attr.class|default('') ~ ' block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-inset sm:text-sm sm:leading-6')|trim }) -%} + {%- set attr = attr|merge({'class': (attr.class|default('') ~ ' block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-inset text-sm')|trim }) -%} {%- if errors|length -%} {%- set attr = attr|merge({'class': attr.class|default('') ~ ' ring-red-500 placeholder-red-500 focus:ring-red-500' }) -%} {% else %} @@ -95,7 +95,7 @@ {% endblock %} {%- block choice_widget_collapsed -%} - {%- set attr = attr|merge({'class': (attr.class|default('') ~ ' block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-inset sm:text-sm sm:leading-6')|trim }) -%} + {%- set attr = attr|merge({'class': (attr.class|default('') ~ ' block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-inset text-sm')|trim }) -%} {%- if errors|length -%} {%- set attr = attr|merge({'class': attr.class|default('') ~ ' ring-red-500 placeholder-red-500 focus:ring-red-500' }) -%} {% else %} @@ -162,7 +162,7 @@ {%- block datepicker_widget -%} {%- set minDate = form.vars.min_date ? form.vars.min_date | date('Y-m-d') : null -%} {%- set maxDate = form.vars.max_date ? form.vars.max_date | date('Y-m-d') : null -%} - {%- set attr = attr|merge({'class': (attr.class|default('') ~ ' block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-inset sm:text-sm sm:leading-6')|trim }) -%} + {%- set attr = attr|merge({'class': (attr.class|default('') ~ ' block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-inset text-sm')|trim }) -%} {%- if errors|length -%} {%- set attr = attr|merge({'class': attr.class|default('') ~ ' ring-red-500 placeholder:red-500 focus:ring-red-500' }) -%} {% else %} @@ -181,7 +181,7 @@ {%- endblock datepicker_widget %} {%- block autocomplete_widget -%} - {%- set attr = attr|merge({'class': (attr.class|default('') ~ ' autocomplete-input block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-inset sm:text-sm sm:leading-6')|trim }) -%} + {%- set attr = attr|merge({'class': (attr.class|default('') ~ ' autocomplete-input block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-inset text-sm')|trim }) -%} {%- if errors|length -%} {%- set attr = attr|merge({'class': attr.class|default('') ~ ' ring-red-500 placeholder-red-500 focus:ring-red-500' }) -%} {% else %} diff --git a/templates/teamer/index.html.twig b/templates/teamer/index.html.twig index ecfa9e8..acb6bd5 100644 --- a/templates/teamer/index.html.twig +++ b/templates/teamer/index.html.twig @@ -3,7 +3,7 @@ {% block title %}Einsatzübersicht{% endblock %} {% block content %} -

+

Einsatzübersicht

{% if errors|length %} @@ -18,6 +18,24 @@ {% endif %} + {{ form_start(filterForm) }} +
+
+ {{ form_widget(filterForm.dateFrom) }} + {{ form_widget(filterForm.dateTo) }} + {{ form_widget(filterForm.jobProfile) }} + {{ form_widget(filterForm.destination) }} + {{ form_widget(filterForm.duration) }} +
+
+ {{ form_widget(filterForm.apply, { 'attr': { 'class': 'btn btn--small' } }) }} + {{ form_widget(filterForm.reset, { 'attr': { 'class': 'btn btn--small btn--secondary' } }) }} +
+
+ {{ form_rest(filterForm) }} + {{ form_end(filterForm) }} + +
{% for assignment in pagination %}