Skip to content

Commit

Permalink
Merge pull request #106 from crf-devs/display-planning-without-search…
Browse files Browse the repository at this point in the history
…-#97

Display planning without search #97
  • Loading branch information
mRoca authored Mar 24, 2020
2 parents 9d719dc + f66165e commit 80cbf46
Show file tree
Hide file tree
Showing 7 changed files with 82 additions and 85 deletions.
51 changes: 27 additions & 24 deletions assets/js/planning.js
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand All @@ -35,19 +38,19 @@ 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',
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();
Expand All @@ -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;
}

Expand All @@ -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: {}
Expand All @@ -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')]);
Expand All @@ -146,10 +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);

$planning.find('input[type=checkbox]:checked').closest('.slot-box').addClass('checked');
});

41 changes: 21 additions & 20 deletions src/Controller/Organization/PlanningController.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,29 +38,30 @@ 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']]);
$form->handleRequest($request);
if (!$request->query->has('from')) {
$request->query->set('from', (new \DateTimeImmutable('monday this week'))->format('Y-m-d\T00:00:00'));
}

$from = $form->get('from')->getData();
$to = $form->get('to')->getData();
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'));
}

$periodCalculator = DatePeriodCalculator::createRoundedToDay($from, new \DateInterval('PT2H'), $to);
$form = $this->container->get('form.factory')->createNamed('', PlanningSearchType::class, [], ['method' => 'GET', 'attr' => ['autocomplete' => 'off']]);
$form->handleRequest($request);

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);
$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($data['from'], new \DateInterval('PT2H'), $data['to']);

[$users, $assets] = $this->searchEntities($data);
$usersAvailabilities = $this->prepareAvailabilities($this->userAvailabilityRepository, $users, $periodCalculator);
$assetsAvailabilities = $this->prepareAvailabilities($this->assetAvailabilityRepository, $assets, $periodCalculator);

return $this->render('organization/planning.html.twig', [
'form' => $form->createView(),
'periodCalculator' => $periodCalculator,
Expand All @@ -71,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];
}
Expand Down
34 changes: 15 additions & 19 deletions src/Form/Type/PlanningSearchType.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,8 @@
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
{
Expand Down Expand Up @@ -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, [
Expand All @@ -94,15 +92,13 @@ 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)
$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
}
});
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'csrf_protection' => false,
]);
}
}
4 changes: 2 additions & 2 deletions src/Repository/CommissionableAssetRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,11 @@ public function findByFilters(array $formData)
{
$qb = $this->createQueryBuilder('a');

if (0 < count($formData['assetTypes'])) {
if (count($formData['assetTypes'] ?? []) > 0) {
$qb->andWhere('a.type IN (:types)')->setParameter('types', $formData['assetTypes']);
}

if (0 < $formData['organizations']->count()) {
if (count($formData['organizations'] ?? []) > 0) {
$qb->andWhere('a.organization IN (:organisations)')->setParameter('organisations', $formData['organizations']);
}

Expand Down
16 changes: 5 additions & 11 deletions src/Repository/UserRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,27 +51,21 @@ 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 (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 (0 < count($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);
}
Expand Down
4 changes: 2 additions & 2 deletions templates/organization/home.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@
<h1>{{ app.user }}</h1>

<p>Semaine actuelle : du {{ 'this week' | date('d/m/Y') }} au {{ 'sunday this week' | date('d/m/Y') }}</p>
<p><button class="btn btn-primary" role="button" disabled>Afficher les disponibilités de mes bénévoles pour la semaine actuelle</button></p>
<p><a class="btn btn-primary" role="button" href="{{ path('planning', {'organization[]': app.user.id}) }}">Afficher les disponibilités de mes bénévoles pour la semaine actuelle</a></p>

<p>Semaine prochaine : du {{ 'next week' | date('d/m/Y') }} au {{ 'sunday next week' | date('d/m/Y') }}</p>
<p><button class="btn btn-primary" role="button" disabled>Afficher les disponibilités de mes bénévoles pour la semaine prochaine</button></p>
<p><a class="btn btn-primary" role="button" href="{{ path('planning', {'organization[]': app.user.id, 'from': 'monday next week' | date('Y-m-d\\T00:00:00')}) }}">Afficher les disponibilités de mes bénévoles pour la semaine prochaine</a></p>

<hr>

Expand Down
17 changes: 10 additions & 7 deletions templates/organization/planning/_search_type.html.twig
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<div class="search shadow-sm p-3 mb-5 bg-white rounded">
{{ form_start(form) }}
{{ form_start(form, {'attr': {'id': 'planning-form'}}) }}
<div class="row">
<div class="col-12 col-md-6">
<label for="fromToRange">Période affichée: </label><input type="text" id="fromToRange" class="form-control" readonly>
Expand Down Expand Up @@ -27,36 +27,39 @@
</div>
</div>
<div class="col-12 col-md-6">
{{ form_row(form.volunteerHideVulnerable) }}
{{ form_row(form.displayVulnerables) }}
</div>
</div>

<div class="row mt-md-2">
<div class="col-12 col-md-2">
{{ form_row(form.volunteer) }}
{{ form_row(form.hideUsers) }}
</div>
<div class="col-12 col-md-6 row">
{{ 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'}}) }}
<div class="col-12 col-md-9">
{{ form_widget(form.volunteerSkills) }}
{{ form_widget(form.userSkills) }}
</div>
</div>

<div class="col-12 col-md-4">
{{ form_row(form.volunteerEquipped) }}
{{ form_row(form.onlyFullyEquiped) }}
</div>
</div>

<div class="row mt-md-2">
<div class="col-12 col-md-2">
{{ form_row(form.asset) }}
{{ form_row(form.hideAssets) }}
</div>
<div class="col-12 col-md-6 row">
{{ form_label(form.assetTypes, null, {'label_attr': {'class': 'col-12 col-md-3'}}) }}
<div class="col-12 col-md-9">
{{ form_widget(form.assetTypes) }}
</div>
</div>
<div class="col-12 col-md-4 row">
<button class="btn btn-outline-primary btn-block" type="submit">Rechercher</button>
</div>
</div>
{{ form_end(form) }}
</div>

0 comments on commit 80cbf46

Please sign in to comment.