Merge branch 'master' into develop
This commit is contained in:
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Controller\Administrative\Assignment;
|
namespace App\Controller\Administrative\Assignment;
|
||||||
|
|
||||||
|
use App\Controller\Traits\ReturnUrlTrait;
|
||||||
use App\Entity\Assignment;
|
use App\Entity\Assignment;
|
||||||
use App\Entity\User;
|
use App\Entity\User;
|
||||||
use App\Form\AssignmentType;
|
use App\Form\AssignmentType;
|
||||||
@@ -15,6 +16,8 @@ use Symfony\Component\Security\Http\Attribute\IsGranted;
|
|||||||
|
|
||||||
class CreateController extends AbstractController
|
class CreateController extends AbstractController
|
||||||
{
|
{
|
||||||
|
use ReturnUrlTrait;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly EntityManagerInterface $entityManager,
|
private readonly EntityManagerInterface $entityManager,
|
||||||
private readonly LoggerInterface $logger
|
private readonly LoggerInterface $logger
|
||||||
@@ -29,6 +32,7 @@ class CreateController extends AbstractController
|
|||||||
$user = $this->getUser();
|
$user = $this->getUser();
|
||||||
$assignment = new Assignment();
|
$assignment = new Assignment();
|
||||||
$assignment->setOwner($user);
|
$assignment->setOwner($user);
|
||||||
|
$returnUrl = $this->getReturnUrl($request, 'app_administrative_assignment_index');
|
||||||
|
|
||||||
$form = $this->createForm(
|
$form = $this->createForm(
|
||||||
AssignmentType::class,
|
AssignmentType::class,
|
||||||
@@ -51,11 +55,12 @@ class CreateController extends AbstractController
|
|||||||
'job_profile' => $assignment->getJobProfile()->getName(),
|
'job_profile' => $assignment->getJobProfile()->getName(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return $this->redirectToRoute('app_administrative_assignment_index');
|
return $this->redirectToRoute($returnUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->render('administrative/assignment/create.html.twig', [
|
return $this->render('administrative/assignment/create.html.twig', [
|
||||||
'form' => $form->createView(),
|
'form' => $form->createView(),
|
||||||
|
'returnUrl' => $returnUrl,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
namespace App\Controller\Common;
|
namespace App\Controller\Common;
|
||||||
|
|
||||||
use App\Controller\Traits\ReturnUrlTrait;
|
|
||||||
use App\Form\AssignmentFilterType;
|
use App\Form\AssignmentFilterType;
|
||||||
use App\Htmx\HxRedirectResponse;
|
use App\Htmx\HxRedirectResponse;
|
||||||
use App\Repository\AssignmentRepository;
|
use App\Repository\AssignmentRepository;
|
||||||
@@ -15,8 +14,6 @@ use Symfony\Component\Security\Http\Attribute\IsGranted;
|
|||||||
|
|
||||||
class AssignmentFilterController extends AbstractController
|
class AssignmentFilterController extends AbstractController
|
||||||
{
|
{
|
||||||
use ReturnUrlTrait;
|
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly AssignmentFilterHandler $filterHandler,
|
private readonly AssignmentFilterHandler $filterHandler,
|
||||||
private readonly AssignmentRepository $assignmentRepository
|
private readonly AssignmentRepository $assignmentRepository
|
||||||
@@ -41,9 +38,8 @@ class AssignmentFilterController extends AbstractController
|
|||||||
|
|
||||||
if ($form->isSubmitted() && $form->isValid()) {
|
if ($form->isSubmitted() && $form->isValid()) {
|
||||||
$this->filterHandler->handleRequest($form);
|
$this->filterHandler->handleRequest($form);
|
||||||
$returnUrl = $this->getReturnUrl($request, 'app_administrative_assignment_index');
|
|
||||||
|
|
||||||
return new HxRedirectResponse($returnUrl);
|
return new HxRedirectResponse($this->generateUrl('app_administrative_assignment_index'));
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->render('common/modal_assignment_filter.html.twig', [
|
return $this->render('common/modal_assignment_filter.html.twig', [
|
||||||
@@ -56,8 +52,7 @@ class AssignmentFilterController extends AbstractController
|
|||||||
public function reset(Request $request): Response
|
public function reset(Request $request): Response
|
||||||
{
|
{
|
||||||
$this->filterHandler->resetFilterSettings();
|
$this->filterHandler->resetFilterSettings();
|
||||||
$returnUrl = $this->getReturnUrl($request, 'app_administrative_assignment_index');
|
|
||||||
|
|
||||||
return $this->redirect($returnUrl);
|
return $this->redirectToRoute('app_administrative_assignment_index');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -80,11 +80,11 @@ class AssignmentFilterType extends AbstractType
|
|||||||
|
|
||||||
if (0 < count($options['job_profiles'])) {
|
if (0 < count($options['job_profiles'])) {
|
||||||
$builder
|
$builder
|
||||||
->add('jobProfile', EntityType::class, [
|
->add('jobProfiles', MultiselectEntityType::class, [
|
||||||
'label' => 'Job-Profil',
|
'label' => 'Job-Profil',
|
||||||
'class' => JobProfile::class,
|
'class' => JobProfile::class,
|
||||||
'required' => false,
|
'required' => false,
|
||||||
'placeholder' => 'nicht filtern',
|
'empty_label' => 'nicht filtern',
|
||||||
'choice_label' => 'name',
|
'choice_label' => 'name',
|
||||||
'choices' => $options['job_profiles'],
|
'choices' => $options['job_profiles'],
|
||||||
])
|
])
|
||||||
@@ -96,10 +96,10 @@ class AssignmentFilterType extends AbstractType
|
|||||||
$choices[$item->getHotel()] = $item->getHotelCode();
|
$choices[$item->getHotel()] = $item->getHotelCode();
|
||||||
}
|
}
|
||||||
$builder
|
$builder
|
||||||
->add('hotel', ChoiceType::class, [
|
->add('hotels', MultiselectType::class, [
|
||||||
'label' => 'Haus',
|
'label' => 'Haus',
|
||||||
'required' => false,
|
'required' => false,
|
||||||
'placeholder' => 'nicht filtern',
|
'empty_label' => 'nicht filtern',
|
||||||
'choices' => $choices,
|
'choices' => $choices,
|
||||||
])
|
])
|
||||||
;
|
;
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ class AssignmentType extends AbstractType
|
|||||||
}
|
}
|
||||||
])
|
])
|
||||||
->add('jobProfileInfo', TextareaType::class, [
|
->add('jobProfileInfo', TextareaType::class, [
|
||||||
'label' => 'Jobprofil Zusatzinfo',
|
'label' => 'Zusatzinfos',
|
||||||
'required' => false,
|
'required' => false,
|
||||||
'attr' => [
|
'attr' => [
|
||||||
'rows' => 3,
|
'rows' => 3,
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Form;
|
||||||
|
|
||||||
|
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||||
|
use Symfony\Component\Form\AbstractType;
|
||||||
|
use Symfony\Component\Form\FormInterface;
|
||||||
|
use Symfony\Component\Form\FormView;
|
||||||
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
|
|
||||||
|
class MultiselectEntityType extends AbstractType
|
||||||
|
{
|
||||||
|
public function getParent(): string
|
||||||
|
{
|
||||||
|
return EntityType::class;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildView(FormView $view, FormInterface $form, array $options): void
|
||||||
|
{
|
||||||
|
$view->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';
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Form;
|
||||||
|
|
||||||
|
use Symfony\Component\Form\AbstractType;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||||
|
use Symfony\Component\Form\FormInterface;
|
||||||
|
use Symfony\Component\Form\FormView;
|
||||||
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
|
|
||||||
|
class MultiselectType extends AbstractType
|
||||||
|
{
|
||||||
|
public function getParent(): string
|
||||||
|
{
|
||||||
|
return ChoiceType::class;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildView(FormView $view, FormInterface $form, array $options): void
|
||||||
|
{
|
||||||
|
$view->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';
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,8 +14,8 @@ class AssignmentFilterDto extends AbstractFilterDto
|
|||||||
|
|
||||||
protected ?\DateTimeImmutable $dateFrom = null;
|
protected ?\DateTimeImmutable $dateFrom = null;
|
||||||
protected ?\DateTimeImmutable $dateTo = null;
|
protected ?\DateTimeImmutable $dateTo = null;
|
||||||
protected ?JobProfile $jobProfile = null;
|
protected array $jobProfiles = [];
|
||||||
protected ?string $hotel = null;
|
protected array $hotels = [];
|
||||||
protected ?int $duration = null;
|
protected ?int $duration = null;
|
||||||
protected array $status = [];
|
protected array $status = [];
|
||||||
protected bool $includePast = false;
|
protected bool $includePast = false;
|
||||||
@@ -44,26 +44,26 @@ class AssignmentFilterDto extends AbstractFilterDto
|
|||||||
return $this;
|
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;
|
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;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -137,17 +137,17 @@ class AssignmentRepository extends ServiceEntityRepository
|
|||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (null !== $jobProfile = $filterDto->getJobProfile()) {
|
if ($jobProfiles = $filterDto->getJobProfiles()) {
|
||||||
$qb
|
$qb
|
||||||
->andWhere($qb->expr()->eq('assignment.jobProfile', ':jobProfile'))
|
->andWhere($qb->expr()->in('assignment.jobProfile', ':jobProfiles'))
|
||||||
->setParameter('jobProfile', $jobProfile)
|
->setParameter('jobProfiles', $jobProfiles)
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (null !== $hotelCode = $filterDto->getHotel()) {
|
if ($hotelCodes = $filterDto->getHotels()) {
|
||||||
$qb
|
$qb
|
||||||
->andWhere($qb->expr()->eq('destination.hotelCode', ':hotelCode'))
|
->andWhere($qb->expr()->in('destination.hotelCode', ':hotelCodes'))
|
||||||
->setParameter('hotelCode', $hotelCode)
|
->setParameter('hotelCodes', $hotelCodes)
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -59,16 +59,16 @@ class AssignmentFilterHandler
|
|||||||
if (isset($data['date_to'])) {
|
if (isset($data['date_to'])) {
|
||||||
$filterDto->setDateTo($data['date_to']);
|
$filterDto->setDateTo($data['date_to']);
|
||||||
}
|
}
|
||||||
if (isset($data['job_profile']) && 0 < (int) $data['job_profile']) {
|
if (isset($data['job_profiles']) && 0 < count($data['job_profiles'])) {
|
||||||
$jobProfile = $this
|
$jobProfiles = $this
|
||||||
->entityManager
|
->entityManager
|
||||||
->getRepository(JobProfile::class)
|
->getRepository(JobProfile::class)
|
||||||
->find($data['job_profile'])
|
->findBy(['id' => $data['job_profiles']])
|
||||||
;
|
;
|
||||||
$filterDto->setJobProfile($jobProfile);
|
$filterDto->setJobProfiles($jobProfiles);
|
||||||
}
|
}
|
||||||
if (isset($data['hotel'])) {
|
if (isset($data['hotels']) && 0 < count($data['hotels'])) {
|
||||||
$filterDto->setHotel($data['hotel']);
|
$filterDto->setHotels($data['hotels']);
|
||||||
}
|
}
|
||||||
if (isset($data['duration']) && 0 < (int) $data['duration']) {
|
if (isset($data['duration']) && 0 < (int) $data['duration']) {
|
||||||
$filterDto->setDuration($data['duration']);
|
$filterDto->setDuration($data['duration']);
|
||||||
@@ -88,8 +88,10 @@ class AssignmentFilterHandler
|
|||||||
$this->getSession()->set($this->namespace, [
|
$this->getSession()->set($this->namespace, [
|
||||||
'date_from' => $filterDto->getDateFrom(),
|
'date_from' => $filterDto->getDateFrom(),
|
||||||
'date_to' => $filterDto->getDateTo(),
|
'date_to' => $filterDto->getDateTo(),
|
||||||
'job_profile' => $filterDto->getJobProfile()?->getId(),
|
'job_profiles' => array_map(function (JobProfile $jobProfile) {
|
||||||
'hotel' => $filterDto->getHotel(),
|
return $jobProfile->getId();
|
||||||
|
}, $filterDto->getJobProfiles()),
|
||||||
|
'hotels' => $filterDto->getHotels(),
|
||||||
'duration' => $filterDto->getDuration(),
|
'duration' => $filterDto->getDuration(),
|
||||||
'status' => $filterDto->getStatus(),
|
'status' => $filterDto->getStatus(),
|
||||||
'include_past' => $filterDto->isIncludePast(),
|
'include_past' => $filterDto->isIncludePast(),
|
||||||
|
|||||||
@@ -31,7 +31,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="py-6 first:pt-0 last:pb-0 sm:grid sm:grid-cols-3 sm:gap-4">
|
<div class="py-6 first:pt-0 last:pb-0 sm:grid sm:grid-cols-3 sm:gap-4">
|
||||||
<dt class="text-sm font-bold leading-6 text-gray-900">
|
<dt class="text-sm font-bold leading-6 text-gray-900">
|
||||||
Anmerkungen
|
Anmerkungen intern
|
||||||
</dt>
|
</dt>
|
||||||
<dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
|
<dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
|
||||||
{{ assignment.remarks|nl2br|default('-') }}
|
{{ assignment.remarks|nl2br|default('-') }}
|
||||||
|
|||||||
@@ -10,8 +10,8 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{{ form_row(filterForm.dateFrom) }}
|
{{ form_row(filterForm.dateFrom) }}
|
||||||
{{ form_row(filterForm.dateTo) }}
|
{{ form_row(filterForm.dateTo) }}
|
||||||
{{ form_row(filterForm.jobProfile) }}
|
{{ form_row(filterForm.jobProfiles) }}
|
||||||
{{ form_row(filterForm.hotel) }}
|
{{ form_row(filterForm.hotels) }}
|
||||||
{{ form_row(filterForm.duration) }}
|
{{ form_row(filterForm.duration) }}
|
||||||
{{ form_row(filterForm.includePast) }}
|
{{ form_row(filterForm.includePast) }}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -216,4 +216,20 @@
|
|||||||
{{ icon('search', 'w-4 h-4 text-gray-700') }}
|
{{ icon('search', 'w-4 h-4 text-gray-700') }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{%- endblock autocomplete_widget %}
|
{%- endblock autocomplete_widget -%}
|
||||||
|
|
||||||
|
{%- block multiselect_widget -%}
|
||||||
|
<div class="relative" {{ stimulus_controller('multiselect', { 'placeholder': form.vars.empty_label }) }}>
|
||||||
|
<div {{ stimulus_action('multiselect', 'toggle', 'click')}}
|
||||||
|
role="button"
|
||||||
|
class="relative rounded-md border border-gray-300 flex items-start space-x-2 px-3">
|
||||||
|
<div {{ stimulus_target('multiselect', 'label') }} class="flex-1 min-h-8 py-1.5 text-gray-900 text-sm"></div>
|
||||||
|
{{ icon('dots', 'w-4 h-4 mt-2') }}
|
||||||
|
</div>
|
||||||
|
<div {{ stimulus_target('multiselect', 'dropdown')}} class="absolute z-10 top-full -mt-1.5 left-0 inset-x-0 px-3 py-2 bg-white shadow-lg rounded-md rounded-t-none border border-gray-300 border-t-0 hidden">
|
||||||
|
{% for item in form.children %}
|
||||||
|
{{ form_row(item, { 'attr': { 'data-action': 'multiselect#update', 'data-multiselect-target': 'choice', 'data-label': item.vars.label } }) }}
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{%- endblock multiselect_widget -%}
|
||||||
|
|||||||
Reference in New Issue
Block a user