feat: filter assignments by assignment status
This commit is contained in:
@@ -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',
|
||||
]
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
;
|
||||
}
|
||||
}
|
||||
@@ -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()) {
|
||||
switch ($status) {
|
||||
case Assignment::STATUS_STAFFED:
|
||||
$qb->having('assignment.availableDispositions = COUNT(disposition.id)');
|
||||
break;
|
||||
case Assignment::STATUS_PARTLY_STAFFED:
|
||||
$qb
|
||||
->andWhere($qb->expr()->eq('assignment.status', ':status'))
|
||||
->setParameter('status', $status)
|
||||
->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()) {
|
||||
|
||||
@@ -76,7 +76,7 @@
|
||||
<svg viewBox="0 0 5 5" class="h-4 w-4 fill-current text-yellow-500">
|
||||
<circle cx="3" cy="3" r="2" />
|
||||
</svg>
|
||||
{% elseif assignment.inProgress %}
|
||||
{% elseif assignment.staffing %}
|
||||
<svg viewBox="0 0 5 5" class="h-4 w-4 fill-current text-blue-500">
|
||||
<circle cx="3" cy="3" r="2" />
|
||||
</svg>
|
||||
|
||||
@@ -4,8 +4,10 @@
|
||||
|
||||
{% block content %}
|
||||
{{ 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-2">
|
||||
<div class="flex flex-col space-y-2 pb-4">
|
||||
{% if form.status is defined %}
|
||||
{{ form_row(filterForm.status) }}
|
||||
{% endif %}
|
||||
{{ form_row(filterForm.dateFrom) }}
|
||||
{{ form_row(filterForm.dateTo) }}
|
||||
{{ form_row(filterForm.jobProfile) }}
|
||||
|
||||
@@ -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 -%}
|
||||
<div {{ stimulus_controller('datepicker', { 'minDate': minDate, 'maxDate': maxDate, 'disableWeekends': form.vars.disable_weekends }) }}>
|
||||
<input type="text" name="{{ form.vars.full_name }}" value="{{ value }}" {{ block('widget_attributes') }}
|
||||
{{ stimulus_target('datepicker', 'field') }} />
|
||||
<input type="text" name="{{ form.vars.full_name }}" value="{{ value }}" {{ block('widget_attributes') }} {{ stimulus_target('datepicker', 'field') }} />
|
||||
</div>
|
||||
{%- if disabled is defined and disabled == true -%}
|
||||
<input type="hidden" name="{{ form.vars.full_name }}" value="{{ form.vars.value }}">
|
||||
<input type="hidden" name="{{ form.vars.full_name }}" value="{{ value }}">
|
||||
{%- endif -%}
|
||||
{%- endblock datepicker_widget %}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user