Browse Source

gestion de la préparation des événements

garthh 3 weeks ago
parent
commit
6515b0afb2

+ 22 - 0
src/Controller/ParticipationController.php

@@ -61,6 +61,28 @@ final class ParticipationController extends AbstractController
 
     }
 
+    #[Route('/cancel/{id}/direct', name: 'app_participation_cancel_direct', requirements: ['id' => Requirement::UUID_V7], methods: ['GET', 'POST'])]
+    public function cancelByAdmin(?Participation $participation, Request $request, EntityManagerInterface $manager): Response
+    {
+        if (!$this->isGranted('ROLE_MANAGER')) {
+            $this->addFlash('danger', 'Seuls les gestionnaires et les admins sont autorisés à utiliser cette fonction.');
+            return $this->redirectToRoute('app_main');
+        }
+
+        if (!$participation) {
+            // TGCM, ça ressemble à un UUID, mais y'a rien derrière
+            $this->addFlash('danger', 'Participation inexistante !');
+            return $this->redirectToRoute('app_main');
+        }
+
+        $manager->remove($participation);
+        $manager->flush();
+
+        $referer = $request->headers->get('referer'); 
+        return $this->redirect($referer);
+
+    }
+
     // afficher les détails d'une partie à partir d'un slot et permettre l'inscription
     #[Route('/party/participation/{id}', name: 'app_participation', requirements: ['id' => '\d+'], methods: ['GET', 'POST'])]
     public function partiticipation(?Slot $slot, Request $request, SlotRepository $slotRepository, EntityManagerInterface $manager, MailerInterface $mailer): Response

+ 3 - 3
src/Controller/PartyController.php

@@ -19,7 +19,7 @@ use App\Repository\GamemasterRepository;
 use App\Repository\GameRepository;
 use App\Form\PartyType;
 
-use App\Security\Voter\SlotAccessVoter;
+use App\Security\Voter\EventAccessVoter;
 
 final class PartyController extends AbstractController
 {
@@ -75,7 +75,7 @@ final class PartyController extends AbstractController
     public function modify(?Slot $slot, Request $request, SlotRepository $slotRepository, GamemasterRepository $gamemasterRepository, GameRepository $gameRepository, EntityManagerInterface $manager): Response
     {
         // Seuls gestionnaires (MANAGER), admin (ADMIN) ou un MJ de l'asso (STAFF) qui est associé à l'événement
-        $this->denyAccessUnlessGranted(SlotAccessVoter::ACCESS_SLOT, $slot);
+        $this->denyAccessUnlessGranted(EventAccessVoter::ACCESS_EVENT, $slot->getEvent());
 
         $user = $this->getUser();
 
@@ -165,7 +165,7 @@ final class PartyController extends AbstractController
     public function add(?Slot $slot, Request $request, SlotRepository $slotRepository, GamemasterRepository $gamemasterRepository, GameRepository $gameRepository, EntityManagerInterface $manager): Response
     {
         // Seuls gestionnaires (MANAGER), admin (ADMIN) ou un MJ de l'asso (STAFF) qui est associé à l'événement
-        $this->denyAccessUnlessGranted(SlotAccessVoter::ACCESS_SLOT, $slot);
+        $this->denyAccessUnlessGranted(EventAccessVoter::ACCESS_EVENT, $slot->getEvent());
 
         $user = $this->getUser();
         // Création de l'objet minimaliste

+ 66 - 3
src/Controller/PrepareController.php

@@ -14,15 +14,78 @@ use App\Entity\Gamemaster;
 final class PrepareController extends AbstractController
 {
     #[Route('/prepare', name: 'app_prepare')]
-    public function prepare(): Response
+    public function prepare(EventRepository $repository): Response
     {
         // Faut être au moins du STAFF
         $this->denyAccessUnlessGranted('ROLE_STAFF');
 
+        // La personne connectée est-elle un MJ ?
+        $gamemaster = null;
+        $user = $this->getUser();
+        if (in_array('ROLE_STAFF', $user->GetRoles())) {
+            // C'est un membre du staff, on check son profil MJ
+            $gamemaster = $user->getLinkToGamemaster();
+            if (!$gamemaster) {
+                // Mais c'est pas un MJ
+                $this->addFlash('danger', 'Seuls les MJ de l\'équipe peuvent préparer un événement');
+                return $this->redirectToRoute('app_main');
+            }
+        }
+
         // Tous événement même non publiés à venir
+        $events = $repository->findEventsToPrepare($gamemaster);
+
+        if (count($events) < 1) {
+            // Aucun événement à préparer
+            $this->addFlash('danger', 'Aucun événement en cours de préparation pour le moment.');
+            return $this->redirectToRoute('app_main');
+        }
+
+        $event = $events[0];
+
+        return $this->render('prepare/prepare.html.twig', [
+            'event' => $event,
+            'events' => $events
+        ]);
+    }
+
+    #[Route('/prepare/{id}', name: 'app_prepare_planning', requirements: ['id' => Requirement::UUID_V7], methods: ['GET', 'POST'])]
+    public function preparePlanning(?Event $event, EventRepository $repository): Response
+    {
+        // Si pas d'événement, ça dégage
+        if (!$event) {
+            $this->addFlash('danger', 'Événement inconnu !');
+            return $this->redirectToRoute('app_prepare');
+        }
+
+        // On controle les droits d'accès sur l'événement
+        $this->denyAccessUnlessGranted(EventAccessVoter::ACCESS_EVENT, $event);
+
+        // La personne connectée est-elle un MJ ?
+        $gamemaster = null;
+        $user = $this->getUser();
+        if (in_array('ROLE_STAFF', $user->GetRoles())) {
+            // C'est un membre du staff, on check son profil MJ
+            $gamemaster = $user->getLinkToGamemaster();
+            if (!$gamemaster) {
+                // Mais c'est pas un MJ
+                $this->addFlash('danger', 'Seuls les MJ de l\'équipe peuvent préparer un événement');
+                return $this->redirectToRoute('app_main');
+            }
+        }
+
+        // Tous événement même non publiés à venir
+        $events = $repository->findEventsToPrepare($gamemaster);
+
+        if (count($events) < 1) {
+            // Aucun événement à préparer
+            $this->addFlash('danger', 'Aucun événement en cours de préparation pour le moment.');
+            return $this->redirectToRoute('app_main');
+        }
 
-        return $this->render('prepare/index.html.twig', [
-            'controller_name' => 'PrepareController',
+        return $this->render('prepare/prepare.html.twig', [
+            'event' => $event,
+            'events' => $events
         ]);
     }
 }

+ 23 - 11
src/Repository/EventRepository.php

@@ -6,6 +6,7 @@ use App\Entity\Event;
 use App\Entity\Gamemaster;
 use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
 use Doctrine\Persistence\ManagerRegistry;
+use Doctrine\DBAL\Types\Types;
 
 /**
  * @extends ServiceEntityRepository<Event>
@@ -53,22 +54,33 @@ class EventRepository extends ServiceEntityRepository
         // Et il faut la date du moment
         $dateNow = new \DateTime('now');
 
-        $qb = $this->createQueryBuilder('e')
-            ->where('e.published = :published')
-            ->andWhere('e.endOn > :dateNow')
-            ->setParameter('published', true)
-            ->setParameter('dateNow', $dateNow);
+         $qb = $this->createQueryBuilder('e')
+             ->where('e.published = :published')
+             ->andWhere('e.endOn > :dateNow')
+             ->setParameter('published', false)
+             ->setParameter('dateNow', $dateNow)
+              ;
 
-        if ($gamemaster) {
-            $qb->andWhere(':gamemaster MEMBER OF e.gamemastersAssigned')
-            ->setParameter('gamemaster', $gamemaster);
-        }
+        // Ne marche pas, va savoir ! => ajouter ?Gamemaster $gamemaster dans la function
+        // TODO: à revoir
+        // if ($gamemaster) {
+        //     $qb->andWhere(':gamemaster MEMBER OF e.gamemastersAssigned')
+        //     ->setParameter('gamemaster', $gamemaster);
+        //}
         
         $query = $qb->orderBy('e.startOn', 'ASC')
            ->getQuery();
 
-        return $query->getResult();
-    }
+        $result = $query->getResult();
+
+        if ($gamemaster) {
+            $result = array_filter($result, function (Event $event) use ($gamemaster) {
+                return $event->getGamemastersAssigned()->contains($gamemaster);
+            });
+        }
+
+        return array_values($result);
+}
 
 
     //    /**

+ 50 - 0
src/Security/Voter/EventAccessVoter.php

@@ -0,0 +1,50 @@
+<?php
+
+namespace App\Security\Voter;
+
+use App\Entity\Event;
+use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
+use Symfony\Component\Security\Core\Authorization\Voter\Voter;
+use Symfony\Component\Security\Core\User\UserInterface;
+
+class EventAccessVoter extends Voter
+{
+    public const ACCESS_EVENT = 'ACCESS_EVENT';
+
+    protected function supports(string $attribute, $subject): bool
+    {
+        return $attribute === self::ACCESS_EVENT && $subject instanceof Event;
+    }
+
+    protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token): bool
+    {
+        $user = $token->getUser();
+
+        if (!$user instanceof UserInterface) {
+            return false;
+        }
+
+        $roles = $user->getRoles();
+
+        // Accès immédiat pour ADMIN ou MANAGER
+        if (in_array('ROLE_ADMIN', $roles, true) || in_array('ROLE_MANAGER', $roles, true)) {
+            return true;
+        }
+
+        // Vérifie les droits pour ROLE_STAFF
+        if (in_array('ROLE_STAFF', $roles, true)) {
+            $gamemaster = $user->getLinkToGamemaster();
+
+            if (!$gamemaster) {
+                return false;
+            }
+
+            $assignedGamemasters = $subject->getGamemastersAssigned();
+
+            return in_array($gamemaster, $assignedGamemasters->toArray(), true);
+        }
+
+        // ❌ Aucun autre rôle n'est autorisé
+        return false;
+    }
+}

+ 6 - 1
templates/bulma.html.twig

@@ -57,9 +57,14 @@
             <div class="navbar-start">
               <a class="navbar-item" href="{{ path('app_main') }}">Accueil</a>
               <a class="navbar-item" href="{{ path('app_main_booking_main') }}">Réserver</a>
-              {% if is_granted('ROLE_ADMIN') or is_granted('ROLE_MANAGER') %}
+              {% if is_granted('ROLE_MANAGER') %}
               <a class="navbar-item" href="{{ path('app_manage') }}">Gérer</a>
               {% endif %}
+              {% if app.user %}
+                {% if is_granted('ROLE_MANAGER') or (is_granted('ROLE_STAFF') and app.user.linkToGamemaster)%}
+                <a class="navbar-item" href="{{ path('app_prepare') }}">Préparer</a>
+                {% endif %}
+              {% endif %}
             </div>
 
             <div class="navbar-end">

+ 9 - 7
templates/main/index.html.twig

@@ -11,15 +11,17 @@
             
             <div class="card">
               <div class="card-image">
-              <figure class="image is-3by1">
+                <a href="{{ path('app_main_booking', {id: event.id}) }}" title="Réserver une place">
+                  <figure class="image is-3by1">
 
-                  {% if event.picture %}
-                  <img src="/images/events/{{ event.picture }}"  />
-                  {% else %}
-                  <img src="/images/events/placeholder.webp"  />
-                  {% endif %}
+                      {% if event.picture %}
+                      <img src="/images/events/{{ event.picture }}"  />
+                      {% else %}
+                      <img src="/images/events/placeholder.webp"  />
+                      {% endif %}
 
-              </figure>
+                  </figure>
+               </a>
               </div>
               <div class="card-content">
                 <div class="content text-limit-height">

+ 1 - 1
templates/manage/booking.html.twig

@@ -100,7 +100,7 @@
                     
 
                     <td>
-                      
+                      <a href="#" data-id="{{ path('app_participation_cancel_direct', {id: participation.id}) }}" class="button is-danger" {{ stimulus_controller('admin_confirm') }}>Annuler</a>
                     </td>
                 </tr>
             {% endfor %}

+ 1 - 1
templates/manage/list.html.twig

@@ -95,7 +95,7 @@
                     <td>{{ party.gamemaster.preferedName }}</td>
                     <td>{{ party.getSeatsOccuped }} / {{ party.getMaxParticipants }}</td>
                     <td>
-                      
+                      <a href="{{ path('app_party_modify', {id: party.slots.first.id}) }}" class="button is-primary open-modal">Éditer</a>
                     </td>
                 </tr>
             {% endfor %}

+ 2 - 2
templates/party/_modal.edit.html.twig

@@ -4,7 +4,7 @@
 
 {% block content %}
 
-    {% if not party.isValidated %}
+    {% if not party.isValidated and party.getId %}
     <article class="message is-info">
         <div class="message-header">
             <p>Partie nécessitant une validation</p>
@@ -89,7 +89,7 @@
         <a href="#" class="button" data-turbo="false">Déplacer</a>
         <a href="#" data-id="{{ path('app_party_delete', {id: party.getId}) }}" class="button is-danger" data-turbo="false" {{ stimulus_controller('admin_confirm') }}>Supprimer</a>
         {% endif %}
-        {% if not party.isValidated %}
+        {% if not party.isValidated and party.getId %}
         <a href="{{ path('app_party_validate', {id: party.getId}) }}" class="button is-info" data-turbo="false">Valider la partie</a>
         {% endif %}
     </div>

+ 5 - 0
templates/prepare/prepare.html.twig

@@ -58,6 +58,11 @@
         {% endfor %}
       </p>
     </div>
+    {% if is_granted('ROLE_MANAGER') %}
+    <div class="column">
+      <a class="button is-primary" href="{{ path('app_admin_event_config', {id: event.id}) }}">Configurer</a>
+    </div>
+    {% endif %}
   </div>
 
 </div>