From 0ada2e48e88e0d1e55586306f04444fab16169ef Mon Sep 17 00:00:00 2001 From: Hugo LE GLEUT Date: Tue, 24 Mar 2020 12:07:07 +0700 Subject: [PATCH 1/2] Display planning without searching --- assets/js/planning.js | 8 +++- .../Organization/PlanningController.php | 39 ++++++++++++------- src/Form/Type/PlanningSearchType.php | 15 ++++++- .../CommissionableAssetRepository.php | 4 +- src/Repository/UserRepository.php | 10 +---- .../planning/_search_type.html.twig | 4 +- 6 files changed, 51 insertions(+), 29 deletions(-) diff --git a/assets/js/planning.js b/assets/js/planning.js index 99871622..ea2974c9 100644 --- a/assets/js/planning.js +++ b/assets/js/planning.js @@ -35,7 +35,7 @@ function initDatesRange($picker, $from, $to, withTime) cancelClass: 'btn-sm btn-default', locale: { cancelLabel: 'Supprimer', - format: 'DD/MM/YYYY hh:mm', + format: 'DD/MM/YYYY HH:mm', separator: ' - ', applyLabel: 'Valider', fromLabel: 'De', @@ -151,5 +151,11 @@ $(document).ready(function () { // Datepickers initDatesRange($('#fromToRange'), $('#from'), $('#to')); initDatesRange($('#availableRange'), $('#availableFrom'), $('#availableTo'), true); + + // Form submit + $('#submit-search').on('click', function () { + $("#planning-form").submit() + }); + }); diff --git a/src/Controller/Organization/PlanningController.php b/src/Controller/Organization/PlanningController.php index fed3c74c..7dbaaba4 100644 --- a/src/Controller/Organization/PlanningController.php +++ b/src/Controller/Organization/PlanningController.php @@ -38,16 +38,27 @@ public function __construct(UserRepository $userRepository, CommissionableAssetR public function __invoke(Request $request): Response { - $data = [ - 'from' => new \DateTimeImmutable('monday'), - 'to' => (new \DateTimeImmutable('monday'))->add(new \DateInterval('P1W')), - 'volunteer' => true, - 'volunteerEquipped' => true, - 'volunteerHideVulnerable' => true, - 'asset' => true, - ]; - - $form = $this->container->get('form.factory')->createNamed('', PlanningSearchType::class, $data, ['method' => 'GET', 'attr' => ['autocomplete' => 'off']]); + $data = []; + if (!$request->query->get('from') instanceof \DateTimeImmutable) { + $data['from'] = new \DateTimeImmutable('monday'); + } + + if (!$request->query->get('to') instanceof \DateTimeImmutable) { + $data['to'] = (new \DateTimeImmutable('monday'))->add(new \DateInterval('P1W')); + } + + if (0 === $request->query->count()) { + $data = [ + 'from' => new \DateTimeImmutable('monday'), + 'to' => (new \DateTimeImmutable('monday'))->add(new \DateInterval('P1W')), + 'volunteer' => true, + 'volunteerEquipped' => true, + 'volunteerHideVulnerable' => true, + 'asset' => true, + ]; + } + + $form = $this->container->get('form.factory')->createNamed('', PlanningSearchType::class, $data, ['method' => 'GET', 'action' => $this->generateUrl('planning'), 'attr' => ['autocomplete' => 'off']]); $form->handleRequest($request); $from = $form->get('from')->getData(); @@ -55,11 +66,9 @@ public function __invoke(Request $request): Response $periodCalculator = DatePeriodCalculator::createRoundedToDay($from, new \DateInterval('PT2H'), $to); - if ($form->isSubmitted() && $form->isValid()) { - [$users, $assets] = $this->searchEntities($form->getData()); - $usersAvailabilities = $this->prepareAvailabilities($this->userAvailabilityRepository, $users, $periodCalculator); - $assetsAvailabilities = $this->prepareAvailabilities($this->assetAvailabilityRepository, $assets, $periodCalculator); - } + [$users, $assets] = $this->searchEntities($form->getData()); + $usersAvailabilities = $this->prepareAvailabilities($this->userAvailabilityRepository, $users, $periodCalculator); + $assetsAvailabilities = $this->prepareAvailabilities($this->assetAvailabilityRepository, $assets, $periodCalculator); return $this->render('organization/planning.html.twig', [ 'form' => $form->createView(), diff --git a/src/Form/Type/PlanningSearchType.php b/src/Form/Type/PlanningSearchType.php index 8be1df34..2353cba7 100644 --- a/src/Form/Type/PlanningSearchType.php +++ b/src/Form/Type/PlanningSearchType.php @@ -12,10 +12,10 @@ use Symfony\Component\Form\Extension\Core\Type\CheckboxType; use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\Extension\Core\Type\DateTimeType; -use Symfony\Component\Form\Extension\Core\Type\SubmitType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormEvent; use Symfony\Component\Form\FormEvents; +use Symfony\Component\OptionsResolver\OptionsResolver; class PlanningSearchType extends AbstractType { @@ -94,7 +94,6 @@ public function buildForm(FormBuilderInterface $builder, array $options): void 'required' => false, 'attr' => ['class' => 'selectpicker'], ]) - ->add('submit', SubmitType::class, ['label' => 'Filtrer']) ; // Cannot use contraint in upper types, because it's not bound to an entity (therefore PropertyAccessor cannot succeed) @@ -105,4 +104,16 @@ public function buildForm(FormBuilderInterface $builder, array $options): void } }); } + + /** + * @param OptionsResolver $resolver + * + * @return array + */ + public function configureOptions(OptionsResolver $resolver): array + { + $resolver->setDefaults([ + 'csrf_protection' => false, + ]); + } } diff --git a/src/Repository/CommissionableAssetRepository.php b/src/Repository/CommissionableAssetRepository.php index 229b0a78..e78f34ff 100644 --- a/src/Repository/CommissionableAssetRepository.php +++ b/src/Repository/CommissionableAssetRepository.php @@ -36,11 +36,11 @@ public function findByFilters(array $formData) { $qb = $this->createQueryBuilder('a'); - if (0 < count($formData['assetTypes'])) { + if (!empty($formData['assetTypes'])) { $qb->andWhere('a.type IN (:types)')->setParameter('types', $formData['assetTypes']); } - if (0 < $formData['organizations']->count()) { + if (!empty($formData['organizations'])) { $qb->andWhere('a.organization IN (:organisations)')->setParameter('organisations', $formData['organizations']); } diff --git a/src/Repository/UserRepository.php b/src/Repository/UserRepository.php index cf0e00d6..596fb98f 100644 --- a/src/Repository/UserRepository.php +++ b/src/Repository/UserRepository.php @@ -51,13 +51,7 @@ public function findByFilters(array $formData) { $qb = $this->createQueryBuilder('u'); - $skillsQueries = []; - foreach (array_values($formData['volunteerSkills']) as $key => $skill) { - $skillsQueries[] = sprintf('CONTAINS(u.skillSet, ARRAY(:skill%d)) = TRUE', $key); - $qb->setParameter(sprintf('skill%d', $key), $skill); - } - - if (0 < $formData['organizations']->count()) { + if (!empty($formData['organizations'])) { $qb->andWhere('u.organization IN (:organisations)')->setParameter('organisations', $formData['organizations']); } @@ -69,7 +63,7 @@ public function findByFilters(array $formData) $qb->andWhere('u.vulnerable = FALSE'); } - if (0 < count($formData['volunteerSkills'])) { + if (!empty($formData['volunteerSkills'])) { $skillsQueries = []; foreach (array_values($formData['volunteerSkills']) as $key => $skill) { $skillsQueries[] = sprintf('CONTAINS(u.skillSet, ARRAY(:skill%d)) = TRUE', $key); diff --git a/templates/organization/planning/_search_type.html.twig b/templates/organization/planning/_search_type.html.twig index f2c14b30..5f402b09 100644 --- a/templates/organization/planning/_search_type.html.twig +++ b/templates/organization/planning/_search_type.html.twig @@ -1,5 +1,5 @@ From f66165edb6a9b9ce1efeb47056de1255f2767a46 Mon Sep 17 00:00:00 2001 From: Michel Roca Date: Tue, 24 Mar 2020 17:26:22 +0100 Subject: [PATCH 2/2] Finish search form --- assets/js/planning.js | 53 +++++++++---------- .../Organization/PlanningController.php | 38 ++++++------- src/Form/Type/PlanningSearchType.php | 33 ++++-------- .../CommissionableAssetRepository.php | 4 +- src/Repository/UserRepository.php | 10 ++-- templates/organization/home.html.twig | 4 +- .../planning/_search_type.html.twig | 17 +++--- 7 files changed, 67 insertions(+), 92 deletions(-) diff --git a/assets/js/planning.js b/assets/js/planning.js index ea2974c9..2531873f 100644 --- a/assets/js/planning.js +++ b/assets/js/planning.js @@ -19,10 +19,13 @@ function selectTableBox ($tableBox) { colorTableBox($tableBox); } -function initDatesRange($picker, $from, $to, withTime) -{ - function displayDate() { - $picker.val($picker.data('daterangepicker').startDate.format('DD/MM/YYYY HH:mm') + ' à ' + $picker.data('daterangepicker').endDate.format('DD/MM/YYYY HH:mm')); +function initDatesRange ($picker, $from, $to, withTime) { + function displayDate () { + if(withTime) { + $picker.val($picker.data('daterangepicker').startDate.format('DD/MM/YYYY HH:mm') + ' à ' + $picker.data('daterangepicker').endDate.format('DD/MM/YYYY HH:mm')); + } else { + $picker.val($picker.data('daterangepicker').startDate.format('DD/MM/YYYY') + ' au ' + $picker.data('daterangepicker').endDate.format('DD/MM/YYYY')); + } } $picker.daterangepicker({ @@ -41,13 +44,13 @@ function initDatesRange($picker, $from, $to, withTime) fromLabel: 'De', toLabel: 'à', customRangeLabel: 'Custom', - daysOfWeek: [ 'Dim', 'Lun', 'Mar', 'Mer', 'Jeu', 'Ven', 'Sam' ], - monthNames: [ 'Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin', 'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre' ], + daysOfWeek: ['Dim', 'Lun', 'Mar', 'Mer', 'Jeu', 'Ven', 'Sam'], + monthNames: ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin', 'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'], firstDay: 1 } }); - if($from.val() !== '' && $to.val() !== '') { + if ($from.val() !== '' && $to.val() !== '') { $picker.data('daterangepicker').setStartDate(new Date($from.val())); $picker.data('daterangepicker').setEndDate(new Date($to.val())); displayDate(); @@ -66,9 +69,9 @@ function initDatesRange($picker, $from, $to, withTime) }); } -function triggerUpdate(url, newStatus, $planning) { +function triggerUpdate (url, newStatus, $planning) { var payload = generatePayload($planning); - if(!Object.keys(payload.assets).length && !Object.keys(payload.users).length) { + if (!Object.keys(payload.assets).length && !Object.keys(payload.users).length) { return; } @@ -83,32 +86,32 @@ function triggerUpdate(url, newStatus, $planning) { updatePlanningFromPayload($planning, newStatus, payload); $('.planning-actions-container .btn').prop('disabled', false); }, - error: function(data) { + error: function (data) { window.alert('Une erreur est survenue, merci de vérifier vos paramètres.'); $('.planning-actions-container .btn').prop('disabled', false); } }); } -function updatePlanningFromPayload($planning, newStatus, payload) { +function updatePlanningFromPayload ($planning, newStatus, payload) { ['users', 'assets'].forEach(ownerType => { var currentObjects = payload[ownerType] || {}; Object.keys(currentObjects).forEach(objectId => { - payload[ownerType][objectId].forEach(schedule => { - var [from,to] = schedule; - $td = $planning.find('tr[data-type="'+ownerType+'"][data-id="'+objectId+'"] td[data-from="'+from+'"][data-to="'+to+'"]'); - $td - .removeClass($td.data('status')) - .addClass(newStatus) - .data('status', newStatus); - }); + payload[ownerType][objectId].forEach(schedule => { + var [from, to] = schedule; + $td = $planning.find('tr[data-type="' + ownerType + '"][data-id="' + objectId + '"] td[data-from="' + from + '"][data-to="' + to + '"]'); + $td + .removeClass($td.data('status')) + .addClass(newStatus) + .data('status', newStatus); + }); }); }); $planning.find('.checked').removeClass('checked').find('input:checkbox').prop('checked', false); } -function generatePayload($planning) { +function generatePayload ($planning) { var payload = { users: {}, assets: {} @@ -120,7 +123,7 @@ function generatePayload($planning) { var type = $owner.data('type'); var $parent = $(this).closest('td'); - if(!payload[type][ownerId]) { + if (!payload[type][ownerId]) { payload[type][ownerId] = []; } payload[type][ownerId].push([$parent.data('from'), $parent.data('to')]); @@ -146,16 +149,10 @@ $(document).ready(function () { triggerUpdate($(this).data('href'), $(this).data('status'), $planning); }); - $planning.find('input[type=checkbox]:checked').closest('.slot-box').addClass('checked'); - // Datepickers initDatesRange($('#fromToRange'), $('#from'), $('#to')); initDatesRange($('#availableRange'), $('#availableFrom'), $('#availableTo'), true); - // Form submit - $('#submit-search').on('click', function () { - $("#planning-form").submit() - }); - + $planning.find('input[type=checkbox]:checked').closest('.slot-box').addClass('checked'); }); diff --git a/src/Controller/Organization/PlanningController.php b/src/Controller/Organization/PlanningController.php index 7dbaaba4..622155d3 100644 --- a/src/Controller/Organization/PlanningController.php +++ b/src/Controller/Organization/PlanningController.php @@ -38,35 +38,27 @@ public function __construct(UserRepository $userRepository, CommissionableAssetR public function __invoke(Request $request): Response { - $data = []; - if (!$request->query->get('from') instanceof \DateTimeImmutable) { - $data['from'] = new \DateTimeImmutable('monday'); + if (!$request->query->has('from')) { + $request->query->set('from', (new \DateTimeImmutable('monday this week'))->format('Y-m-d\T00:00:00')); } - if (!$request->query->get('to') instanceof \DateTimeImmutable) { - $data['to'] = (new \DateTimeImmutable('monday'))->add(new \DateInterval('P1W')); + if (!$request->query->has('to')) { + $from = new \DateTimeImmutable($request->query->get('from', 'monday this week')); + $request->query->set('to', $from->add(new \DateInterval('P1W'))->format('Y-m-d\T00:00:00')); } - if (0 === $request->query->count()) { - $data = [ - 'from' => new \DateTimeImmutable('monday'), - 'to' => (new \DateTimeImmutable('monday'))->add(new \DateInterval('P1W')), - 'volunteer' => true, - 'volunteerEquipped' => true, - 'volunteerHideVulnerable' => true, - 'asset' => true, - ]; - } - - $form = $this->container->get('form.factory')->createNamed('', PlanningSearchType::class, $data, ['method' => 'GET', 'action' => $this->generateUrl('planning'), 'attr' => ['autocomplete' => 'off']]); + $form = $this->container->get('form.factory')->createNamed('', PlanningSearchType::class, [], ['method' => 'GET', 'attr' => ['autocomplete' => 'off']]); $form->handleRequest($request); - $from = $form->get('from')->getData(); - $to = $form->get('to')->getData(); + $data = $form->getData(); + if (!isset($data['from'], $data['to'])) { + // This may happen if the passed date is invalid. TODO check it before, the format must be 2020-03-30T00:00:00 + throw $this->createNotFoundException(); + } - $periodCalculator = DatePeriodCalculator::createRoundedToDay($from, new \DateInterval('PT2H'), $to); + $periodCalculator = DatePeriodCalculator::createRoundedToDay($data['from'], new \DateInterval('PT2H'), $data['to']); - [$users, $assets] = $this->searchEntities($form->getData()); + [$users, $assets] = $this->searchEntities($data); $usersAvailabilities = $this->prepareAvailabilities($this->userAvailabilityRepository, $users, $periodCalculator); $assetsAvailabilities = $this->prepareAvailabilities($this->assetAvailabilityRepository, $assets, $periodCalculator); @@ -80,8 +72,8 @@ public function __invoke(Request $request): Response private function searchEntities(array $formData): array { - $users = $this->userRepository->findByFilters($formData); - $assets = $this->assetRepository->findByFilters($formData); + $users = $formData['hideUsers'] ?? false ? [] : $this->userRepository->findByFilters($formData); + $assets = $formData['hideAssets'] ?? false ? [] : $this->assetRepository->findByFilters($formData); return [$users, $assets]; } diff --git a/src/Form/Type/PlanningSearchType.php b/src/Form/Type/PlanningSearchType.php index 2353cba7..a94d4348 100644 --- a/src/Form/Type/PlanningSearchType.php +++ b/src/Form/Type/PlanningSearchType.php @@ -13,8 +13,6 @@ use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\Extension\Core\Type\DateTimeType; use Symfony\Component\Form\FormBuilderInterface; -use Symfony\Component\Form\FormEvent; -use Symfony\Component\Form\FormEvents; use Symfony\Component\OptionsResolver\OptionsResolver; class PlanningSearchType extends AbstractType @@ -64,27 +62,27 @@ public function buildForm(FormBuilderInterface $builder, array $options): void 'required' => false, 'attr' => ['class' => 'selectpicker'], ]) - ->add('volunteer', CheckboxType::class, [ - 'label' => 'Bénévoles', + ->add('hideUsers', CheckboxType::class, [ + 'label' => 'Cacher les bénévoles', 'required' => false, ]) - ->add('volunteerSkills', ChoiceType::class, [ + ->add('userSkills', ChoiceType::class, [ 'label' => 'Compétences', 'choices' => array_flip($this->availableSkillSets), 'multiple' => true, 'required' => false, 'attr' => ['class' => 'selectpicker'], ]) - ->add('volunteerEquipped', CheckboxType::class, [ + ->add('onlyFullyEquiped', CheckboxType::class, [ 'label' => 'Avec uniforme seulement', 'required' => false, ]) - ->add('volunteerHideVulnerable', CheckboxType::class, [ - 'label' => 'Cacher les personnes signalées comme vulnérables', + ->add('displayVulnerables', CheckboxType::class, [ + 'label' => 'Afficher aussi les personnes signalées comme vulnérables', 'required' => false, ]) - ->add('asset', CheckboxType::class, [ - 'label' => 'Véhicules', + ->add('hideAssets', CheckboxType::class, [ + 'label' => 'Cacher les véhicules', 'required' => false, ]) ->add('assetTypes', ChoiceType::class, [ @@ -95,22 +93,9 @@ public function buildForm(FormBuilderInterface $builder, array $options): void 'attr' => ['class' => 'selectpicker'], ]) ; - - // Cannot use contraint in upper types, because it's not bound to an entity (therefore PropertyAccessor cannot succeed) - $builder->addEventListener(FormEvents::PRE_SUBMIT, static function (FormEvent $event) { - $data = $event->getData() ?? []; - if (array_key_exists('from', $data) && array_key_exists('to', $data) && $data['from'] >= $data['to']) { - throw new \InvalidArgumentException('Invalid payload'); // TODO Put a better error - } - }); } - /** - * @param OptionsResolver $resolver - * - * @return array - */ - public function configureOptions(OptionsResolver $resolver): array + public function configureOptions(OptionsResolver $resolver): void { $resolver->setDefaults([ 'csrf_protection' => false, diff --git a/src/Repository/CommissionableAssetRepository.php b/src/Repository/CommissionableAssetRepository.php index e78f34ff..38b31d2e 100644 --- a/src/Repository/CommissionableAssetRepository.php +++ b/src/Repository/CommissionableAssetRepository.php @@ -36,11 +36,11 @@ public function findByFilters(array $formData) { $qb = $this->createQueryBuilder('a'); - if (!empty($formData['assetTypes'])) { + if (count($formData['assetTypes'] ?? []) > 0) { $qb->andWhere('a.type IN (:types)')->setParameter('types', $formData['assetTypes']); } - if (!empty($formData['organizations'])) { + if (count($formData['organizations'] ?? []) > 0) { $qb->andWhere('a.organization IN (:organisations)')->setParameter('organisations', $formData['organizations']); } diff --git a/src/Repository/UserRepository.php b/src/Repository/UserRepository.php index 596fb98f..56f54988 100644 --- a/src/Repository/UserRepository.php +++ b/src/Repository/UserRepository.php @@ -51,21 +51,21 @@ public function findByFilters(array $formData) { $qb = $this->createQueryBuilder('u'); - if (!empty($formData['organizations'])) { + if (count($formData['organizations'] ?? []) > 0) { $qb->andWhere('u.organization IN (:organisations)')->setParameter('organisations', $formData['organizations']); } - if ($formData['volunteerEquipped']) { + if ($formData['onlyFullyEquiped'] ?? false) { $qb->andWhere('u.fullyEquipped = TRUE'); } - if ($formData['volunteerHideVulnerable']) { + if ($formData['displayVulnerables'] ?? false) { $qb->andWhere('u.vulnerable = FALSE'); } - if (!empty($formData['volunteerSkills'])) { + if (count($formData['userSkills'] ?? []) > 0) { $skillsQueries = []; - foreach (array_values($formData['volunteerSkills']) as $key => $skill) { + foreach (array_values($formData['userSkills']) as $key => $skill) { $skillsQueries[] = sprintf('CONTAINS(u.skillSet, ARRAY(:skill%d)) = TRUE', $key); $qb->setParameter(sprintf('skill%d', $key), $skill); } diff --git a/templates/organization/home.html.twig b/templates/organization/home.html.twig index d68bee2a..8fd32d33 100644 --- a/templates/organization/home.html.twig +++ b/templates/organization/home.html.twig @@ -8,10 +8,10 @@

{{ app.user }}

Semaine actuelle : du {{ 'this week' | date('d/m/Y') }} au {{ 'sunday this week' | date('d/m/Y') }}

-

+

Afficher les disponibilités de mes bénévoles pour la semaine actuelle

Semaine prochaine : du {{ 'next week' | date('d/m/Y') }} au {{ 'sunday next week' | date('d/m/Y') }}

-

+

Afficher les disponibilités de mes bénévoles pour la semaine prochaine


diff --git a/templates/organization/planning/_search_type.html.twig b/templates/organization/planning/_search_type.html.twig index 5f402b09..eea864e1 100644 --- a/templates/organization/planning/_search_type.html.twig +++ b/templates/organization/planning/_search_type.html.twig @@ -27,29 +27,29 @@
- {{ form_row(form.volunteerHideVulnerable) }} + {{ form_row(form.displayVulnerables) }}
- {{ form_row(form.volunteer) }} + {{ form_row(form.hideUsers) }}
- {{ form_label(form.volunteerSkills, null, {'label_attr': {'class': 'col-12 col-md-3'}}) }} + {{ form_label(form.userSkills, null, {'label_attr': {'class': 'col-12 col-md-3'}}) }}
- {{ form_widget(form.volunteerSkills) }} + {{ form_widget(form.userSkills) }}
- {{ form_row(form.volunteerEquipped) }} + {{ form_row(form.onlyFullyEquiped) }}
- {{ form_row(form.asset) }} + {{ form_row(form.hideAssets) }}
{{ form_label(form.assetTypes, null, {'label_attr': {'class': 'col-12 col-md-3'}}) }} @@ -57,8 +57,9 @@ {{ form_widget(form.assetTypes) }}
+
+ +
{{ form_end(form) }} - -