Эх сурвалжийг харах

changements cosmétiques sur le planning et préparation formulaire de demandes

garthh 4 долоо хоног өмнө
parent
commit
cf8a738603

+ 37 - 0
assets/controllers/partyrequest_selector_controller.js

@@ -0,0 +1,37 @@
+import { Controller } from '@hotwired/stimulus';
+
+/*
+ * Contrôleur Stimulus pour le formulaire JEUX.
+ */
+
+export default class extends Controller {
+
+    connect() {
+        this.initDisabling();
+        console.log("Stimulus: gestion des désactivation d'options dans les demandes");
+        
+    }
+
+initDisabling() {
+    const checkInGame = document.querySelector('#game-controller select');
+
+    checkInGame.addEventListener('change', () => {
+        const selectedOption = checkInGame.options[checkInGame.selectedIndex];
+        const gameID = checkInGame.value;
+        const gameCanHaveMaster = selectedOption.dataset.gamemasters
+            ? selectedOption.dataset.gamemasters.split("|")
+            : [];
+
+        const gamemastersToRefresh = Array.from(document.querySelectorAll("#gamemaster-controller option"));
+
+        gamemastersToRefresh.forEach((el) => {
+            if (!gameID) {
+                el.disabled = true;
+            } else {
+                el.disabled = !gameCanHaveMaster.includes(el.value);
+            }
+        });
+    });
+}
+
+}

+ 24 - 5
assets/styles/app.css

@@ -166,11 +166,10 @@
   border: solid 0.2rem transparent;
   background-color: transparent;
   width: 100%;
-  color: black;
   position: absolute;
   top: 0rem;
   left: 0rem;
-  z-index: 99 !important;
+  /*z-index: 99 !important;*/
   white-space: normal;
   text-overflow: clip;
   overflow: hidden;
@@ -188,11 +187,31 @@
 }
 
 .planning-cell-game .card {
-  /*border-left: solid 0.2rem blue;
-  border-top: solid 0.2rem blue;*/
-  background-color: hsl(204, 70%, 96%);
   padding: 0.2rem;
   overflow: hidden;
+  background-size: cover;         /* remplit toute la div */
+  background-position: center;    /* centre l’image */
+  background-repeat: no-repeat;   /* pas de répétition */
+  background-color: hsl(
+      var(--bulma-primary-h),
+      var(--bulma-primary-s),
+      var(--bulma-primary-45-l)
+  );
+  background-blend-mode: multiply; 
+}
+
+.planning-cell-game .card::after {
+  content: "";
+  position: absolute;
+  inset: 0; /* couvre toute la carte */
+  background: rgba(255, 255, 255, 0.8); /* calque sombre semi‑transparent */
+  pointer-events: none; /* clics passent à travers */
+  z-index: 10;
+}
+
+.planning-cell-game .card > * {
+  position: relative;
+  z-index: 20; /* texte au-dessus */
 }
 
 .planning-cell-game .card-header {

+ 31 - 0
migrations/Version20250806080643.php

@@ -0,0 +1,31 @@
+<?php
+
+declare(strict_types=1);
+
+namespace DoctrineMigrations;
+
+use Doctrine\DBAL\Schema\Schema;
+use Doctrine\Migrations\AbstractMigration;
+
+/**
+ * Auto-generated Migration: Please modify to your needs!
+ */
+final class Version20250806080643 extends AbstractMigration
+{
+    public function getDescription(): string
+    {
+        return '';
+    }
+
+    public function up(Schema $schema): void
+    {
+        // this up() migration is auto-generated, please modify it to your needs
+        $this->addSql('ALTER TABLE party_request ADD message LONGTEXT DEFAULT NULL');
+    }
+
+    public function down(Schema $schema): void
+    {
+        // this down() migration is auto-generated, please modify it to your needs
+        $this->addSql('ALTER TABLE party_request DROP message');
+    }
+}

+ 0 - 7
src/Controller/PartyController.php

@@ -23,13 +23,6 @@ use App\Security\Voter\SlotAccessVoter;
 
 final class PartyController extends AbstractController
 {
-/*     #[Route('/party', name: 'app_party')]
-    public function index(): Response
-    {
-        return $this->render('party/index.html.twig', [
-            'controller_name' => 'PartyController',
-        ]);
-    } */
 
     // supprimer une partie
     #[Route('/party/delete/{id}', name: 'app_party_delete', requirements: ['id' => Requirement::UUID_V7], methods: ['GET'])]

+ 84 - 0
src/Controller/PartyRequestController.php

@@ -0,0 +1,84 @@
+<?php
+
+namespace App\Controller;
+
+use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\Routing\Requirement\Requirement;
+use Symfony\Component\Routing\Attribute\Route;
+use Doctrine\ORM\EntityManagerInterface;
+use Symfony\Bridge\Twig\Mime\TemplatedEmail;
+use Symfony\Component\Mime\Address;
+use Symfony\Component\Mailer\MailerInterface;
+use Symfony\Component\Mime\Email;
+
+use App\Entity\PartyRequest;
+use App\Entity\Gamemaster;
+use App\Entity\Game;
+use App\Entity\Event;
+use App\Repository\GamemasterRepository;
+use App\Repository\GameRepository;
+use App\Form\PartyRequestType;
+
+final class PartyRequestController extends AbstractController
+{
+    #[Route('/party/request/{id}', name: 'app_party_request', requirements: ['id' => Requirement::UUID_V7], methods: ['GET', 'POST'])]
+    public function request(?Event $event, Request $request, GamemasterRepository $gamemasterRepository, GameRepository $gameRepository, EntityManagerInterface $manager, MailerInterface $mailer): Response
+    {
+        // Pas d'événement trouvé ?
+        if (!$event) {
+            $this->addFlash('danger', 'Aucun événement avec cet ID !');
+            $this->redirectToRoute('app_main');
+        }
+
+        // L'événement n'autorise pas les demandes ?
+        if (!$event->isEveryoneCanAskForGame()) {
+            $this->addFlash('danger', 'Cet événement n\'autorise pas les demandes.');
+            $this->redirectToRoute('app_main_booking', ['id' => $event->getId()]);
+        }
+
+        // Pas d'utilisateur connecté ?
+        $user = $this->getUser();
+        if (!$user) {
+            $this->addFlash('danger', 'Les demandes nécessitent d\'être connecté(e).');
+            $this->redirectToRoute('app_main_booking', ['id' => $event->getId()]);
+        }
+
+        // On génère un formulaire
+        $partyRequest = new PartyRequest();
+        $partyRequest->setEvent($event);
+        $partyRequest->setRequester($user);
+        $partyRequest->setDateRequest(new \Datetime('now'));
+
+        $form = $this->createForm(PartyRequestForm::class, $partyRequest);
+
+        // Traitement du formulaire
+        $form->handleRequest($request);
+        if ($form->isSubmitted() && $form->isValid()) {
+            $manager->persist($partyRequest);
+            $manager->flush();
+
+            // Envoyer un mail
+            // Maintenant uniquement pour avoir l'ID
+            $email = (new TemplatedEmail())
+                ->from(new Address($_ENV['CONTACT_EMAIL'], $_ENV['CONTACT_NAME']))
+                ->to((string) $user->getEmail())
+                ->subject('Votre demande pour '.$event->getName())
+                ->htmlTemplate('party_request/request.email.html.twig')
+                ->textTemplate('party_request/request.email.txt.twig')
+                ->context([
+                    'partyRequest' => $partyRequest
+                ]);
+            $mailer->send($email);
+
+            $this->addFlash('danger', 'Demande enregistrée, les gestionnaires y répondront prochainement.');
+            $this->redirectToRoute('app_main_booking', ['id' => $event.getId()]);
+        }
+
+        return $this->render('party_request/_modal.request.html.twig', [
+            'form' => $form,
+            'partyRequest' => $partyRequest
+        ]);
+    }
+}

+ 15 - 0
src/Entity/PartyRequest.php

@@ -47,6 +47,9 @@ class PartyRequest
     #[ORM\Column(nullable: true)]
     private ?bool $accepted = null;
 
+    #[ORM\Column(type: Types::TEXT, nullable: true)]
+    private ?string $message = null;
+
     public function getId(): ?int
     {
         return $this->id;
@@ -147,4 +150,16 @@ class PartyRequest
 
         return $this;
     }
+
+    public function getMessage(): ?string
+    {
+        return $this->message;
+    }
+
+    public function setMessage(?string $message): static
+    {
+        $this->message = $message;
+
+        return $this;
+    }
 }

+ 40 - 0
src/Form/PartyRequestType.php

@@ -0,0 +1,40 @@
+<?php
+
+namespace App\Form;
+
+use App\Entity\Event;
+use App\Entity\Game;
+use App\Entity\Gamemaster;
+use App\Entity\PartyRequest;
+use App\Entity\User;
+use Symfony\Bridge\Doctrine\Form\Type\EntityType;
+use Symfony\Component\Form\AbstractType;
+use Symfony\Component\Form\FormBuilderInterface;
+use Symfony\Component\OptionsResolver\OptionsResolver;
+
+class PartyRequestType extends AbstractType
+{
+    public function buildForm(FormBuilderInterface $builder, array $options): void
+    {
+        $builder
+            ->add('message', null, [
+                'label' => 'Message',
+                'label_attr' => ['class' => 'label'],
+                'help' => 'Un message à l\'attention des organisateurs.',
+                'attr' => ['class' => 'textarea',
+                           'rows' => 6],
+                'required' => false,
+                'row_attr' => ['class' => 'field'],
+                'help_attr' => ['class' => 'help'],
+            ])
+
+        ;
+    }
+
+    public function configureOptions(OptionsResolver $resolver): void
+    {
+        $resolver->setDefaults([
+            'data_class' => PartyRequest::class,
+        ]);
+    }
+}

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

@@ -53,7 +53,7 @@
                                 <div class="cell planning-cell planning-cell-game-parent">
                                     <div class="planning-cell-game {% if thisSlot.party.getSeatsLeft < 1 %}planning-cell-game-isfull {% endif %}{% if pathFullSlot %} open-modal" href="{{ path(pathFullSlot, {id: thisSlot.id}) }}"{% else %}"{% endif %} style="height: {{ thisSlot.party.slots|length * 4 * app_hmult }}rem !important">
                                       {# Carte "jeu" DEBUT #}
-                                        <div class="card" style="height: {{ thisSlot.party.slots|length * 4 * app_hmult - 0.7 }}rem !important {% if not thisSlot.party.isValidated %}; filter:grayscale(1) opacity(0.3);{% endif %}">
+                                        <div class="card has-background-primary-45 has-text-primary-45-invert" style="{% if thisSlot.party.game.picture %}background-image: url('/images/games/{{ thisSlot.party.game.picture }}');{% endif %} height: {{ thisSlot.party.slots|length * 4 * app_hmult - 0.7 }}rem !important; {% if not thisSlot.party.isValidated %}filter:grayscale(1) opacity(0.3);{% endif %};">
                                             
                                                 <div class="card-header">
                                                     <div class="media">
@@ -70,14 +70,16 @@
                                                         </div>
                                                     </div>
                                                         <div class="media-content">
-                                                            <small class="hax-text-grey-light">@{{ thisSlot.party.gamemaster.preferedName }}</small><br/>
-                                                            <span class="title is-6">{{ thisSlot.party.game.name }}</span>
-                                                            {% if thisSlot.party.gamemasterIsAuthor %}<br/><span class="tag is-info">animée par l'auteur</span>{% endif %}
+                                                            <small class="hax-text-grey-light">{{ thisSlot.party.gamemaster.preferedName }}</small><br/>
+                                                            <span class="title is-6 has-text-primary-45-invert">{{ thisSlot.party.game.name }}</span>
+                                                            
                                                         </div>
 
                                                 </div>
                                                 <div class="card-content">
-                                                    <small>Places : {{ thisSlot.party.getSeatsLeft }} / {{ thisSlot.party.getMaxParticipants }}</small>
+                                                    <p>Places : {{ thisSlot.party.getSeatsLeft }} / {{ thisSlot.party.getMaxParticipants }}</p>
+                                                    {% if thisSlot.party.gamemasterIsAuthor %}<p><span class="tag is-warning">partie animée par l'auteur</span></p>{% endif %}
+                                                    
                                                 </div>
 
                                             {% if pathFullSlot %}</a>{% endif %}

+ 6 - 3
templates/main/_modal.gamemaster.html.twig

@@ -29,18 +29,21 @@
   </div>
 </article>
 {% if is_granted('ROLE_MANAGER') or is_granted('ROLE_ADMIN') %}
-  <div class="box">
+
     <div class="columns">
       {% if gamemaster.phone %}
       <div class="column has-text-centered">
-        <a class="button is-primary" href="tel:{{ gamemaster.phone }}"><span class="icon-text"><span class="icon mr-3"><twig:ux:icon name="bi:telephone-fill"/></span>Appeler</a>
+        <a class="button is-primary" href="tel:{{ gamemaster.phone }}"><span class="icon-text"><span class="icon mr-3"><twig:ux:icon name="bi:telephone-fill"/></span>Appel</a>
+      </div>
+      <div class="column has-text-centered">
+        <a class="button is-primary" href="sms:{{ gamemaster.phone }}"><span class="icon-text"><span class="icon mr-3"><twig:ux:icon name="bi:envelope"/></span>SMS</a>
       </div>
       {% endif %}
       <div class="column has-text-centered">
         <a class="button is-primary" href="mailto:{{ gamemaster.email }}"><span class="icon-text"><span class="icon mr-3"><twig:ux:icon name="bi:envelope-fill"/></span>Mail</a>
       </div>
     </div>
-  </div>
+
 {% endif %}
 
 

+ 1 - 1
templates/participation/_modal.add.html.twig

@@ -47,7 +47,7 @@
             {% endif %}
             </figure>
         </span> 
-        {{ party.getGamemaster.getPreferedName }}
+        {{ party.getGamemaster.getPreferedName }}{% if party.gamemasterIsAuthor %} / auteur(rice) du jeu{% endif %}
       </div>
     </div>
     <div class="columns">

+ 20 - 0
templates/party_request/index.html.twig

@@ -0,0 +1,20 @@
+{% extends 'base.html.twig' %}
+
+{% block title %}Hello PartyRequestController!{% endblock %}
+
+{% block body %}
+<style>
+    .example-wrapper { margin: 1em auto; max-width: 800px; width: 95%; font: 18px/1.5 sans-serif; }
+    .example-wrapper code { background: #F5F5F5; padding: 2px 6px; }
+</style>
+
+<div class="example-wrapper">
+    <h1>Hello {{ controller_name }}! ✅</h1>
+
+    This friendly message is coming from:
+    <ul>
+        <li>Your controller at <code>/Users/garthh/Developpement/orgasso/src/Controller/PartyRequestController.php</code></li>
+        <li>Your template at <code>/Users/garthh/Developpement/orgasso/templates/party_request/index.html.twig</code></li>
+    </ul>
+</div>
+{% endblock %}