Răsfoiți Sursa

finalisation gestion des parties

garthh 2 zile în urmă
părinte
comite
70940ca318

+ 137 - 9
src/Controller/PartyController.php

@@ -33,25 +33,151 @@ final class PartyController extends AbstractController
 
     // supprimer une partie
     #[Route('/party/delete/{id}', name: 'app_party_delete', requirements: ['id' => Requirement::UUID_V7], methods: ['GET'])]
-    public function delete(?Party $party, EntityManager $manager, SlotRepository $slotRepository): Response
+    public function delete(?Party $party, EntityManagerInterface $manager, SlotRepository $slotRepository): Response
     {
         // Seuls admin (ADMIN)
         $user = $this->getUser();
-        if (!in_array('ROLE_ADMIN', $roles)) {}
-
+        $roles = $user->getRoles();
+        if (in_array('ROLE_ADMIN', $roles)) {
+            if ($party) {
+                $party->purgeSlots();
+                $manager->remove($party);
+                $manager->flush($party);
+                $this->addFlash('success', 'Partie supprimée !');
+            } else {
+                $this->addFlash('danger', 'Partie inexistante');
+            }
+        } else {
+            $this->addFlash('danger', 'Seuls les admins peuvent supprimer une partie.');
+        }
+        return $this->redirectToRoute('app_main'); // @todo: à changer   
     }
 
     // valider une partie proposée par un MJ
-    #[Route('/party/validate/{id}', name: 'app_party_delete', requirements: ['id' => Requirement::UUID_V7], methods: ['GET'])]
-    public function validate(?Party $party, EntityManager $manager, SlotRepository $slotRepository): Response
+    #[Route('/party/validate/{id}', name: 'app_party_validate', requirements: ['id' => Requirement::UUID_V7], methods: ['GET'])]
+    public function validate(?Party $party, EntityManagerInterface $manager, SlotRepository $slotRepository): Response
     {
         // Seuls gestionnaires (MANAGER), admin (ADMIN)
         $user = $this->getUser();
-        if (!in_array('ROLE_ADMIN', $roles) && !in_array('ROLE_MANAGER', $roles)) {}
-            
+        $roles = $user->getRoles();
+        if (in_array('ROLE_ADMIN', $roles) || in_array('ROLE_MANAGER', $roles)) {
+            if ($party) {
+                $party->setValidated(true);
+                $manager->persist($party);
+                $manager->flush($party);
+                $this->addFlash('success', 'Partie validée !');
+            } else {
+                $this->addFlash('danger', 'Partie inexistante');
+            }
+        } else {
+            $this->addFlash('danger', 'Seuls les rôles Gestionnaires et Admin peuvent valider une partie');
+        }
+        return $this->redirectToRoute('app_main'); // @todo: à changer    
     }
 
     // modifier une partie
+    #[Route('/party/modify/{id}', name: 'app_party_modify', requirements: ['id' => '\d+'], methods: ['GET','POST'])]
+    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);
+
+        $user = $this->getUser();
+
+        $party = $slot->getParty();
+        if (!$party) {
+            $this->addFlash('danger', 'Pas de MJ ou de jeu sélectionné.');
+            $this->redirectToRoute('app_main'); // @todo: à modifier
+        }
+
+        $roles = $user->getRoles();
+
+        // Check MJ 
+        if (in_array('ROLE_STAFF', $roles) && !(in_array('ROLE_ADMIN', $roles) || in_array('ROLE_MANAGER', $roles))) {
+            // C'est un MJ, est-ce qu'il édite bien l'une de ses parties
+            $gamemasters[] = $user->getLinkToGamemaster();
+            if ($party->gamemaster != $gamemasters[0]) {
+                // Alors dégage !
+                $this->addFlash('danger', 'Un MJ ne peut éditer que ses parties.');
+                $this->redirectToRoute('app_main'); // @todo: à modifier               
+            }
+        } else {
+            $gamemasters = $slot->getEvent()->getGamemastersAssigned();
+        }
+
+        // Création des valeurs dispo pour les formulaire
+        $games = $slot->getEvent()->getGameAssigned();
+        $lastSlot = $party->getSlots()->last();
+        $getSlotsAvailables = $slotRepository->findNextsAvailables($lastSlot);
+        $slotStart = $slot;
+        $slotsAvailables = array_merge($party->getSlots()->toArray(), $getSlotsAvailables);
+
+        $form = $this->createForm(PartyType::class, $party);
+
+        $form->handleRequest($request);
+        if ($form->isSubmitted() && $form->isValid()) {
+
+            // Ajouter les slots séléctionnés
+            $slotsString = $request->request->get('party_slots');
+            $gamemasterSelectedId = $request->request->get('party_gamemaster');
+            $gamemasterSelected = $gamemasterRepository->findByStrID($gamemasterSelectedId);
+            $gameSelectedId = $request->request->get('party_game');
+            $gameSelected = $gameRepository->findByStrID($gameSelectedId);
+
+            if ($gameSelected && $gamemasterSelected) {
+                $party->purgeSlots();
+                $new_slots = explode("|", $slotsString);
+                foreach($new_slots as $add_this_slot) {
+                    $add_this_slot_obj = $slotRepository->findById($add_this_slot);
+                    $party->addSlot($add_this_slot_obj);
+                }
+                $slots = $party->getSlots();
+                $party->setStartOn(clone $slots->first()->getStartOn());
+                $party->setEndOn(clone $slots->last()->getEndOn());
+
+                $party->setGamemaster($gamemasterSelected);
+                $party->setGame($gameSelected);
+
+                $manager->persist($party);
+                $manager->flush();
+
+                // @todo: si c'est une partie non validée, envoyer un mail aux admin+gestionnaires pour validation
+
+                $this->addFlash('success', 'Partie ajoutée au planning.');
+            } else {
+                $this->addFlash('danger', 'Pas de MJ ou de jeu sélectionné.');
+            }
+            return $this->redirectToRoute('app_main'); // @todo: à modifier !
+
+        }
+
+        return $this->render('party/edit.html.twig', [
+            'form' => $form,
+            'party' => $party,
+            'gamemasters' => $gamemasters,
+            'games' => $games,
+            'slotStart' => $slotStart,
+            'slotsAvailables' => $slotsAvailables,
+            'mod' => true
+        ]);
+    }
+
+    // afficher les détails d'une partie
+    #[Route('/party/view/{id}', name: 'app_party_view', requirements: ['id' => '\d+'], methods: ['GET', 'POST'])]
+    public function view(?Slot $slot, Request $request, SlotRepository $slotRepository, EntityManagerInterface $manager): Response
+    {
+        $party = $slot->getParty();
+        if (!$party) {
+            $this->addFlash('danger', 'Aucune partie associée à ce slot !');
+            $this->redirectToRoute('app_main'); // @todo: à déterminer
+        }
+
+        return $this->render('party/view.html.twig', [
+            'party' => $party,
+            'slot' => $slot,
+        ]);
+
+    }
 
 
     // ajouter une partie
@@ -109,14 +235,14 @@ final class PartyController extends AbstractController
                 $party->setStartOn(clone $party->getSlots()[0]->getStartOn());
                 $party->setEndOn(clone $party->getSlots()->last()->getEndOn());
 
-
-
                 $party->setGamemaster($gamemasterSelected);
                 $party->setGame($gameSelected);
 
                 $manager->persist($party);
                 $manager->flush();
 
+                // @todo: si c'est une partie non validée, envoyer un mail aux admin+gestionnaires pour validation
+
                 $this->addFlash('success', 'Partie ajoutée au planning.');
             } else {
                 $this->addFlash('danger', 'Pas de MJ ou de jeu sélectionné.');
@@ -127,10 +253,12 @@ final class PartyController extends AbstractController
 
         return $this->render('party/edit.html.twig', [
             'form' => $form,
+            'party' => $party,
             'gamemasters' => $gamemasters,
             'games' => $games,
             'slotStart' => $slotStart,
             'slotsAvailables' => $slotsAvailables,
+            'mod' => false
         ]);
     }
 

+ 8 - 0
src/Entity/Party.php

@@ -135,6 +135,14 @@ class Party
         return $this;
     }
 
+    public function purgeSlots(): static
+    {
+        foreach ($this->getSlots() as $slot) {
+            $this->removeSlot($slot);
+        }
+        return $this;
+    }
+
     public function getEvent(): ?Event
     {
         return $this->event;

+ 1 - 0
src/Twig/Components/Planning.php

@@ -20,6 +20,7 @@ class Planning
     public bool $onlyUserSlot = false;
     public bool $onlyEmptyParty = false;
     public bool $displayLocked = false;
+    public bool $displayUnvalidates = false;
 
     private DateTimeHelper $dateTimeHelper;
     private SlotRepository $repository;

+ 1 - 1
templates/admin/event/config/party.html.twig

@@ -51,7 +51,7 @@
           </article>
     {% else %}
         <div id="planning">
-          {{ component('Planning', {event: event, pathEmptySlot: 'app_party_add', pathFullSlot: 'app_main'}) }}
+          {{ component('Planning', {event: event, pathEmptySlot: 'app_party_add', pathFullSlot: 'app_party_modify', displayUnvalidates: true}) }}
         </div>
     {% endif %}
 

+ 13 - 5
templates/components/Planning.html.twig

@@ -46,28 +46,31 @@
                         {% endif %}
                         {# si une partie est sur le slot #}
                         {% if thisSlot.party  %}
-                            {% if thisSlot == thisSlot.party.slots[0] and thisSlot.party.isValidated %}
+                            {% if thisSlot.party.isValidated or displayUnvalidates %}
+                                {% if thisSlot == thisSlot.party.slots[0] %}
                                 {# Premier slot d'une partie ou partie non validée #}
                                 <div class="cell planning-cell planning-cell-game-parent">
                                     <div class="planning-cell-game" style="height: {{ thisSlot.party.slots|length * 4 }}rem !important">
                                       {# Carte "jeu" DEBUT #}
-                                        <div class="card" style="height: {{ thisSlot.party.slots|length * 4 - 0.7 }}rem !important">
+                                        <div class="card" style="height: {{ thisSlot.party.slots|length * 4 - 0.7 }}rem !important {% if not thisSlot.party.isValidated %}; filter:grayscale(1) opacity(0.3);{% endif %}">
                                             
                                                 <div class="card-header">
                                                     <div class="media">
                                                         <div class="media-left">
                                                             <figure class="image is-48x48">
+                                                                {% if pathFullSlot %}<a href="{{ path(pathFullSlot, {id: thisSlot.id}) }}">{% endif %}
                                                                 {% if thisSlot.party.gamemaster.picture %}
                                                                 <img class="is-rounded" src="/images/gamemasters/{{ thisSlot.party.gamemaster.picture }}"  />
                                                                 {% else %}
                                                                 <twig:ux:icon name="bi:person-fill"/>
                                                                 {% endif %}
+                                                                {% if pathFullSlot %}</a>{% endif %}
                                                             </figure>
                                                         </div>
                                                     </div>
                                                         <div class="media-content">
                                                             {% if pathFullSlot %}<a href="{{ path(pathFullSlot, {id: thisSlot.id}) }}">{% endif %}<span class="title is-6">{{ thisSlot.party.game.name }}</span>{% if pathFullSlot %}</a>{% endif %}<br/>
-                                                            <small class="hax-text-grey-light">@{{ thisSlot.party.gamemaster.preferedName }}</small>
+                                                            <small class="hax-text-grey-light">@{{ thisSlot.party.gamemaster.preferedName }}{% if thisSlot.party.gamemasterIsAuthor %}<br/><span class="tag is-info">animée par l'auteur</span>{% endif %}</small>
                                                         </div>
 
                                                 </div>
@@ -80,10 +83,15 @@
                                       {# Carte "jeu" FIN #}
                                     </div>
                                 </div>
-                            {% else %}
-                                {# Slot suivants d'une partie #}
+                                {% else %}
+                                {# Slot suivant d'une partie #}
                                 <div class="cell planning-cell planning-cell-game-parent">
                                 </div>
+                                {% endif %}
+                            {% else %}
+                                {# Partie non validée masquée #}
+                                <div class="cell planning-cell planning-cell-free">
+                                </div>
                             {% endif %}
                         {% endif %}
                         {# si le slot est disponible et sans partie #}

+ 21 - 5
templates/party/edit.html.twig

@@ -4,7 +4,16 @@
 
 {% block content %}
 
-    
+    {% if not party.isValidated %}
+    <article class="message is-info">
+        <div class="message-header">
+            <p>Partie nécessitant une validation</p>
+        </div>
+        <div class="message-body">
+            Cette partie a été proposée par {{ party.getSubmitter.fullName }} le {{ party.getSubmittedDate|date('d/m/y h:i', app_timezone) }}
+        </div>
+    </article>
+    {% endif %}
 
     {{ form_errors(form) }}
     {{ form_start(form) }}
@@ -14,7 +23,7 @@
         <select id="party_gamemaster" name="party_gamemaster" class="input">
             <option value=""></option>
             {% for gamemaster in gamemasters %}
-            <option value="{{ gamemaster.id }}" data-games="{{ gamemaster.getGamesCanMaster|map(game => game.getId)|join('|') }}">{{ gamemaster.getPreferedName()|capitalize }}</option> 
+            <option value="{{ gamemaster.id }}" data-games="{{ gamemaster.getGamesCanMaster|map(game => game.getId)|join('|') }}"{% if party.getGamemaster == gamemaster %}selected='selected'{% endif %}>{{ gamemaster.getPreferedName() }}</option> 
             {% endfor %}
         </select>
     </div>
@@ -24,7 +33,7 @@
         <select id="party_game" name="party_game" class="input">
             <option value=""></option>
             {% for game in games %}
-            <option value="{{ game.id }}" disabled>{{ game.getName() }}</option> 
+            <option value="{{ game.id }}" {% if party.getGame == game %}selected='selected'{% else %}disabled{% endif %}>{{ game.getName() }}</option> 
             {% endfor %}
         </select>
     </div>
@@ -53,7 +62,7 @@
                 <div class="field">
                     <label class="label">Horaire de début</label>
                     <select id="party_start_slot" name="party_start_slot" class="input">
-                        <option value="{{ slotStart.id }}">{{ slotStart.startOn|date('d/m/Y H:i', app_timezone) }}</option>
+                        <option value="{{ slotStart.id }}" selected="selected">{{ slotStart.startOn|date('d/m/Y H:i', app_timezone) }}</option>
                     </select>
                 </div>
             </div>
@@ -64,7 +73,7 @@
                     {% set SlotC = [] %}
                     {% for slot in slotsAvailables %}
                         {% set SlotC = SlotC|merge([slot.id]) %}
-                        <option value="{{ SlotC|join("|") }}">{{ slot.endOn|date('d/m/Y H:i', app_timezone) }}</option>
+                        <option value="{{ SlotC|join("|") }}" {% if slot == party.getSlots.last %}selected="selected"{% endif %}>{{ slot.endOn|date('d/m/Y H:i', app_timezone) }}</option>
                     {% endfor %}
                     </select>
                 </div>
@@ -76,6 +85,13 @@
     
     <div class="control">
         <button class="button is-primary" type="submit">Envoyer</button>
+        {% if mod %}
+        <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 %}
+        <a href="{{ path('app_party_validate', {id: party.getId}) }}" class="button is-info" data-turbo="false">Valider la partie</a>
+        {% endif %}
     </div>
 
     {{ form_end(form) }}

+ 20 - 0
templates/party/view.html.twig

@@ -0,0 +1,20 @@
+{% extends 'bulma.html.twig' %}
+
+{% block title %}{{ party.game.name }}{% endblock %}
+
+{% block content %}
+
+    <section class="hero is-info">
+    <div class="hero-body">
+        <p class="title">{{ party.game.name }}</p>
+        <p class="subtitle">Date : {{ party.startOn|date('d/m/y', app_timezone) }} de {{ party.startOn|date('H:i', app_timezone) }} à {{ party.endOn|date('H:i', app_timezone) }} • Espace : {{ party.slots[0].space.name }} • Meneur(euse) de jeu : {{ party.gamemaster.preferedName }}</p>
+    </div>
+    </section>
+    <section class="content">
+        <p>{{ party.game.description }}</p>
+    </section>
+    
+    
+
+
+{% endblock %}