diff --git a/src/Controller/Administrative/Assignment/IndexController.php b/src/Controller/Administrative/Assignment/IndexController.php index e976642..4c76741 100644 --- a/src/Controller/Administrative/Assignment/IndexController.php +++ b/src/Controller/Administrative/Assignment/IndexController.php @@ -36,6 +36,7 @@ class IndexController extends AbstractController $request->query->getInt('page', 1), 10, [ + 'wrap-queries' => true, // Required for query with 'having' clause 'defaultSortFieldName' => 'destination.dateFrom', 'defaultSortDirection' => 'desc', ] diff --git a/src/Controller/Common/AssignmentFilterController.php b/src/Controller/Common/AssignmentFilterController.php index c39c24b..4dec39b 100644 --- a/src/Controller/Common/AssignmentFilterController.php +++ b/src/Controller/Common/AssignmentFilterController.php @@ -34,6 +34,7 @@ class AssignmentFilterController extends AbstractController 'max_date' => $filterOptions['maxDate'], 'job_profiles' => $filterOptions['jobProfiles'], 'hotels' => $filterOptions['hotels'], + 'user' => $this->getUser(), ]; $form = $this->createForm(AssignmentFilterType::class, $formData, $formOptions); diff --git a/src/Entity/Assignment.php b/src/Entity/Assignment.php index 232c843..ca17557 100644 --- a/src/Entity/Assignment.php +++ b/src/Entity/Assignment.php @@ -25,6 +25,10 @@ class Assignment implements BlameableEntityInterface, TimestampableEntityInterfa public const STATUS_OPEN = 'open'; public const STATUS_CLOSED = 'closed'; + public const STATUS_STAFFED = 'staffed'; + public const STATUS_PARTLY_STAFFED = 'partly_staffed'; + public const STATUS_STAFFING = 'staffing'; + #[ORM\Id] #[ORM\GeneratedValue] #[ORM\Column] @@ -475,9 +479,8 @@ class Assignment implements BlameableEntityInterface, TimestampableEntityInterfa && 0 === $this->applications->count(); } - public function isInProgress(): bool + public function isStaffing(): bool { - return $this->availableDispositions > $this->dispositions->count() - && 0 < $this->applications->count(); + return 0 === $this->dispositions->count() && 0 < $this->applications->count(); } } diff --git a/src/Form/AssignmentFilterType.php b/src/Form/AssignmentFilterType.php index 40cee25..690a8d7 100644 --- a/src/Form/AssignmentFilterType.php +++ b/src/Form/AssignmentFilterType.php @@ -5,6 +5,7 @@ namespace App\Form; use App\Entity\Assignment; use App\Entity\Destination; use App\Entity\JobProfile; +use App\Entity\User; use App\Model\AssignmentFilterDto; use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\Form\AbstractType; @@ -22,15 +23,22 @@ class AssignmentFilterType extends AbstractType 'label' => 'Zeitraum von', 'min_date' => $options['min_date'], 'max_date' => $options['max_date'], + 'attr' => [ + 'placeholder' => 'nicht filtern', + ], ]) ->add('dateTo', DatepickerType::class, [ 'label' => 'Zeitraum bis', 'min_date' => $options['min_date'], 'max_date' => $options['max_date'], + 'attr' => [ + 'placeholder' => 'nicht filtern', + ], ]) ->add('duration', ChoiceType::class, [ 'label' => 'Einsatzdauer', 'required' => false, + 'placeholder' => 'nicht filtern', 'choices' => [ 'Wochenende (2-4 Tage)' => AssignmentFilterDto::DURATION_WEEKEND, 'Midweek (5-6 Tage)' => AssignmentFilterDto::DURATION_MID_WEEK, @@ -38,15 +46,6 @@ class AssignmentFilterType extends AbstractType 'Mehr als einen Woche' => AssignmentFilterDto::DURATION_MORE, ], ]) - ->add('status', ChoiceType::class, [ - 'label' => 'Status', - 'required' => false, - 'choices' => [ - 'Entwurf' => Assignment::STATUS_DRAFT, - 'offen' => Assignment::STATUS_OPEN, - 'geschlossen' => Assignment::STATUS_CLOSED, - ], - ]) ->add('apply', SubmitType::class, [ 'label' => 'filtern', ]) @@ -55,12 +54,28 @@ class AssignmentFilterType extends AbstractType ]) ; + /** @var User $user */ + $user = $options['user']; + if ($user->hasRole('ROLE_ADMINISTRATIVE')) { + $builder->add('status', ChoiceType::class, [ + 'label' => 'Status', + 'required' => false, + 'placeholder' => 'nicht filtern', + 'choices' => [ + 'voll besetzt' => Assignment::STATUS_STAFFED, + 'teilweise besetzt' => Assignment::STATUS_PARTLY_STAFFED, + 'mit Bewerbungen' => Assignment::STATUS_STAFFING, + ], + ]); + } + if (0 < count($options['job_profiles'])) { $builder ->add('jobProfile', EntityType::class, [ 'label' => 'Job-Profil', 'class' => JobProfile::class, 'required' => false, + 'placeholder' => 'nicht filtern', 'choice_label' => 'name', 'choices' => $options['job_profiles'], ]) @@ -75,6 +90,7 @@ class AssignmentFilterType extends AbstractType ->add('hotel', ChoiceType::class, [ 'label' => 'Haus', 'required' => false, + 'placeholder' => 'nicht filtern', 'choices' => $choices, ]) ; @@ -91,8 +107,10 @@ class AssignmentFilterType extends AbstractType 'job_profiles' => [], 'hotels' => [], ]) + ->setRequired(['user']) ->setAllowedTypes('min_date', [\DateTimeImmutable::class, 'null']) ->setAllowedTypes('max_date', [\DateTimeImmutable::class, 'null']) + ->setAllowedTypes('user', User::class) ; } } \ No newline at end of file diff --git a/src/Repository/AssignmentRepository.php b/src/Repository/AssignmentRepository.php index 62bb7a3..b8b5e33 100644 --- a/src/Repository/AssignmentRepository.php +++ b/src/Repository/AssignmentRepository.php @@ -40,6 +40,7 @@ class AssignmentRepository extends ServiceEntityRepository ->leftJoin('assignment.applications', 'application') ->leftJoin('assignment.dispositions', 'disposition') ->leftJoin('disposition.teamer', 'teamer') + ->groupBy('assignment') ; $this->applyFilterSettings($filterDto, $qb); @@ -67,6 +68,7 @@ class AssignmentRepository extends ServiceEntityRepository )) ->leftJoin('assignment.applications', 'application', Join::WITH, $qb->expr()->eq('application.teamer', ':teamer')) ->leftJoin('assignment.dispositions', 'disposition', Join::WITH, $qb->expr()->eq('disposition.teamer', ':teamer')) + ->groupBy('assignment') ->setParameter('teamer', $teamer) ->setParameter('now', new \DateTimeImmutable()) ; @@ -79,10 +81,23 @@ class AssignmentRepository extends ServiceEntityRepository private function applyFilterSettings(AssignmentFilterDto $filterDto, QueryBuilder $qb): void { if (null !== $status = $filterDto->getStatus()) { - $qb - ->andWhere($qb->expr()->eq('assignment.status', ':status')) - ->setParameter('status', $status) - ; + switch ($status) { + case Assignment::STATUS_STAFFED: + $qb->having('assignment.availableDispositions = COUNT(disposition.id)'); + break; + case Assignment::STATUS_PARTLY_STAFFED: + $qb + ->having('assignment.availableDispositions > COUNT(disposition.id)') + ->andHaving('COUNT(application.id) = 0') + ; + break; + case Assignment::STATUS_STAFFING: + $qb + ->having('COUNT(disposition.id) = 0') + ->andHaving('COUNT(application.id) > 0') + ; + break; + } } if (null !== $dateFrom = $filterDto->getDateFrom()) { diff --git a/templates/administrative/assignment/index.html.twig b/templates/administrative/assignment/index.html.twig index 1d9ee56..7803748 100644 --- a/templates/administrative/assignment/index.html.twig +++ b/templates/administrative/assignment/index.html.twig @@ -76,7 +76,7 @@ - {% elseif assignment.inProgress %} + {% elseif assignment.staffing %} diff --git a/templates/common/modal_assignment_filter.html.twig b/templates/common/modal_assignment_filter.html.twig index 0f9997d..fd06459 100644 --- a/templates/common/modal_assignment_filter.html.twig +++ b/templates/common/modal_assignment_filter.html.twig @@ -4,8 +4,10 @@ {% block content %} {{ form_start(filterForm, { 'attr': { 'hx-post': app.request.uri, 'hx-target': '#htmx-modal', 'hx-swap': 'outerHTML' } }) }} -
- {{ form_row(filterForm.status) }} +
+ {% if form.status is defined %} + {{ form_row(filterForm.status) }} + {% endif %} {{ form_row(filterForm.dateFrom) }} {{ form_row(filterForm.dateTo) }} {{ form_row(filterForm.jobProfile) }} diff --git a/templates/forms.html.twig b/templates/forms.html.twig index 5718cf5..74b0f13 100644 --- a/templates/forms.html.twig +++ b/templates/forms.html.twig @@ -166,17 +166,16 @@ {%- if errors|length -%} {%- set attr = attr|merge({'class': attr.class|default('') ~ ' ring-red-500 placeholder:red-500 focus:ring-red-500' }) -%} {% else %} - {%- set attr = attr|merge({'class': attr.class|default('') ~ ' placeholder:text-gray-400 focus:ring-primary' }) -%} + {%- set attr = attr|merge({'class': attr.class|default('') ~ ' placeholder:text-gray-900 focus:ring-primary' }) -%} {%- endif -%} {%- if disabled is defined and disabled == true -%} {%- set attr = attr|merge({'class': attr.class|default('') ~ ' cursor-not-allowed' }) -%} {%- endif -%}
- +
{%- if disabled is defined and disabled == true -%} - + {%- endif -%} {%- endblock datepicker_widget %}