✅ Finalisation application : sécurité, calendrier, vérifs d’unicité, filtres chauffagiste
This commit is contained in:
parent
a992c2ea6a
commit
461e661f62
44
migrations/Version20250508121539.php
Normal file
44
migrations/Version20250508121539.php
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<?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 Version20250508121539 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(<<<'SQL'
|
||||||
|
ALTER TABLE intervention ADD remarque TEXT DEFAULT NULL
|
||||||
|
SQL);
|
||||||
|
$this->addSql(<<<'SQL'
|
||||||
|
ALTER TABLE intervention ADD start_date TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL
|
||||||
|
SQL);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(Schema $schema): void
|
||||||
|
{
|
||||||
|
// this down() migration is auto-generated, please modify it to your needs
|
||||||
|
$this->addSql(<<<'SQL'
|
||||||
|
CREATE SCHEMA public
|
||||||
|
SQL);
|
||||||
|
$this->addSql(<<<'SQL'
|
||||||
|
ALTER TABLE intervention DROP remarque
|
||||||
|
SQL);
|
||||||
|
$this->addSql(<<<'SQL'
|
||||||
|
ALTER TABLE intervention DROP start_date
|
||||||
|
SQL);
|
||||||
|
}
|
||||||
|
}
|
@ -13,24 +13,21 @@ class CalendrierController extends AbstractController
|
|||||||
#[Route('/chauffagiste', name: 'app_calendrier_indexChauffagiste')]
|
#[Route('/chauffagiste', name: 'app_calendrier_indexChauffagiste')]
|
||||||
public function indexChauffagiste(InterventionRepository $interventionRepository): Response
|
public function indexChauffagiste(InterventionRepository $interventionRepository): Response
|
||||||
{
|
{
|
||||||
// Vérification : seul le chauffagiste connecté peut voir son propre calendrier
|
|
||||||
$this->denyAccessUnlessGranted('ROLE_CHAUFFAGISTE');
|
$this->denyAccessUnlessGranted('ROLE_CHAUFFAGISTE');
|
||||||
|
|
||||||
// Récupérer les interventions du chauffagiste connecté
|
|
||||||
$interventions = $interventionRepository->findByUser($this->getUser());
|
$interventions = $interventionRepository->findByUser($this->getUser());
|
||||||
|
|
||||||
// Préparer les événements pour FullCalendar
|
|
||||||
$events = [];
|
$events = [];
|
||||||
foreach ($interventions as $intervention) {
|
foreach ($interventions as $intervention) {
|
||||||
$events[] = [
|
$events[] = [
|
||||||
'title' => $intervention->getTitle(),
|
'title' => ' - ' . $intervention->getWording() . ' (' . $intervention->getStatus() . ')',
|
||||||
'start' => $intervention->getStartDate()->format('Y-m-d H:i:s'),
|
'start' => $intervention->getTimestamp()?->format('Y-m-d\TH:i:s') ?? '',
|
||||||
'end' => $intervention->getEndDate()->format('Y-m-d H:i:s'),
|
'end' => $intervention->getTimestamp()?->format('Y-m-d\TH:i:s') ?? '',
|
||||||
'description' => $intervention->getDescription(),
|
'description' => $intervention->getDescription(),
|
||||||
|
'url' => $this->generateUrl('app_intervention_show', ['id' => $intervention->getId()])
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Passer les événements à la vue
|
|
||||||
return $this->render('calendrier/indexChauffagiste.html.twig', [
|
return $this->render('calendrier/indexChauffagiste.html.twig', [
|
||||||
'events' => json_encode($events),
|
'events' => json_encode($events),
|
||||||
]);
|
]);
|
||||||
@ -39,24 +36,21 @@ class CalendrierController extends AbstractController
|
|||||||
#[Route('/secretaire', name: 'app_calendrier_indexSecretaire')]
|
#[Route('/secretaire', name: 'app_calendrier_indexSecretaire')]
|
||||||
public function indexSecretaire(InterventionRepository $interventionRepository): Response
|
public function indexSecretaire(InterventionRepository $interventionRepository): Response
|
||||||
{
|
{
|
||||||
// Vérification : seul le secrétaire peut accéder à ce calendrier
|
|
||||||
$this->denyAccessUnlessGranted('ROLE_SECRETAIRE');
|
$this->denyAccessUnlessGranted('ROLE_SECRETAIRE');
|
||||||
|
|
||||||
// Récupérer toutes les interventions de tous les chauffagistes
|
|
||||||
$interventions = $interventionRepository->findAll();
|
$interventions = $interventionRepository->findAll();
|
||||||
|
|
||||||
// Préparer les événements pour FullCalendar
|
|
||||||
$events = [];
|
$events = [];
|
||||||
foreach ($interventions as $intervention) {
|
foreach ($interventions as $intervention) {
|
||||||
$events[] = [
|
$events[] = [
|
||||||
'title' => $intervention->getTitle(),
|
'title' => ' - ' . $intervention->getWording() . ' (' . $intervention->getStatus() . ')',
|
||||||
'start' => $intervention->getStartDate()->format('Y-m-d H:i:s'),
|
'start' => $intervention->getTimestamp()?->format('Y-m-d\TH:i:s') ?? '',
|
||||||
'end' => $intervention->getEndDate()->format('Y-m-d H:i:s'),
|
'end' => $intervention->getTimestamp()?->format('Y-m-d\TH:i:s') ?? '',
|
||||||
'description' => $intervention->getDescription(),
|
'description' => $intervention->getDescription(),
|
||||||
|
'url' => $this->generateUrl('app_intervention_show', ['id' => $intervention->getId()])
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Passer les événements à la vue
|
|
||||||
return $this->render('calendrier/indexSecretaire.html.twig', [
|
return $this->render('calendrier/indexSecretaire.html.twig', [
|
||||||
'events' => json_encode($events),
|
'events' => json_encode($events),
|
||||||
]);
|
]);
|
||||||
@ -65,24 +59,21 @@ class CalendrierController extends AbstractController
|
|||||||
#[Route('/admin', name: 'app_calendrier_index')]
|
#[Route('/admin', name: 'app_calendrier_index')]
|
||||||
public function indexAdmin(InterventionRepository $interventionRepository): Response
|
public function indexAdmin(InterventionRepository $interventionRepository): Response
|
||||||
{
|
{
|
||||||
// Vérification : seul un admin peut accéder à ce calendrier
|
|
||||||
$this->denyAccessUnlessGranted('ROLE_ADMIN');
|
$this->denyAccessUnlessGranted('ROLE_ADMIN');
|
||||||
|
|
||||||
// Récupérer toutes les interventions de tous les chauffagistes
|
|
||||||
$interventions = $interventionRepository->findAll();
|
$interventions = $interventionRepository->findAll();
|
||||||
|
|
||||||
// Préparer les événements pour FullCalendar
|
|
||||||
$events = [];
|
$events = [];
|
||||||
foreach ($interventions as $intervention) {
|
foreach ($interventions as $intervention) {
|
||||||
$events[] = [
|
$events[] = [
|
||||||
'title' => $intervention->getTitle(),
|
'title' => ' - ' . $intervention->getWording() . ' (' . $intervention->getStatus() . ')',
|
||||||
'start' => $intervention->getStartDate()->format('Y-m-d H:i:s'),
|
'start' => $intervention->getTimestamp()?->format('Y-m-d\TH:i:s') ?? '',
|
||||||
'end' => $intervention->getEndDate()->format('Y-m-d H:i:s'),
|
'end' => $intervention->getTimestamp()?->format('Y-m-d\TH:i:s') ?? '',
|
||||||
'description' => $intervention->getDescription(),
|
'description' => $intervention->getDescription(),
|
||||||
|
'url' => $this->generateUrl('app_intervention_show', ['id' => $intervention->getId()])
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Passer les événements à la vue
|
|
||||||
return $this->render('calendrier/index.html.twig', [
|
return $this->render('calendrier/index.html.twig', [
|
||||||
'events' => json_encode($events),
|
'events' => json_encode($events),
|
||||||
]);
|
]);
|
||||||
|
@ -4,6 +4,7 @@ namespace App\Controller;
|
|||||||
|
|
||||||
use App\Entity\Intervention;
|
use App\Entity\Intervention;
|
||||||
use App\Form\InterventionType;
|
use App\Form\InterventionType;
|
||||||
|
use App\Form\RemarqueType;
|
||||||
use App\Repository\InterventionRepository;
|
use App\Repository\InterventionRepository;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
@ -24,7 +25,7 @@ class InterventionController extends AbstractController
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[Route('/new', name: 'app_intervention_new', methods: ['GET', 'POST'])]
|
#[Route('/new', name: 'app_intervention_new', methods: ['GET', 'POST'])]
|
||||||
public function new(Request $request, EntityManagerInterface $entityManager): Response
|
public function new(Request $request, EntityManagerInterface $entityManager, InterventionRepository $interventionRepository): Response
|
||||||
{
|
{
|
||||||
$this->denyUnlessAdminOrSecretaire();
|
$this->denyUnlessAdminOrSecretaire();
|
||||||
|
|
||||||
@ -33,6 +34,36 @@ class InterventionController extends AbstractController
|
|||||||
$form->handleRequest($request);
|
$form->handleRequest($request);
|
||||||
|
|
||||||
if ($form->isSubmitted() && $form->isValid()) {
|
if ($form->isSubmitted() && $form->isValid()) {
|
||||||
|
$timestamp = $intervention->getTimestamp();
|
||||||
|
$chauffagiste = $intervention->getUser();
|
||||||
|
$vehicule = $intervention->getVehicle();
|
||||||
|
|
||||||
|
$conflictsUser = $interventionRepository->findBy([
|
||||||
|
'Timestamp' => $timestamp,
|
||||||
|
'user' => $chauffagiste,
|
||||||
|
]);
|
||||||
|
|
||||||
|
if ($conflictsUser) {
|
||||||
|
$this->addFlash('error', 'Ce chauffagiste a déjà une intervention à cette date.');
|
||||||
|
return $this->render('intervention/new.html.twig', [
|
||||||
|
'form' => $form,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($vehicule) {
|
||||||
|
$conflictsVehicule = $interventionRepository->findBy([
|
||||||
|
'Timestamp' => $timestamp,
|
||||||
|
'vehicle' => $vehicule,
|
||||||
|
]);
|
||||||
|
|
||||||
|
if ($conflictsVehicule) {
|
||||||
|
$this->addFlash('error', 'Ce véhicule est déjà utilisé à cette date.');
|
||||||
|
return $this->render('intervention/new.html.twig', [
|
||||||
|
'form' => $form,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$entityManager->persist($intervention);
|
$entityManager->persist($intervention);
|
||||||
$entityManager->flush();
|
$entityManager->flush();
|
||||||
|
|
||||||
@ -54,7 +85,7 @@ class InterventionController extends AbstractController
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[Route('/{id}/edit', name: 'app_intervention_edit', methods: ['GET', 'POST'])]
|
#[Route('/{id}/edit', name: 'app_intervention_edit', methods: ['GET', 'POST'])]
|
||||||
public function edit(Request $request, Intervention $intervention, EntityManagerInterface $entityManager): Response
|
public function edit(Request $request, Intervention $intervention, EntityManagerInterface $entityManager, InterventionRepository $interventionRepository): Response
|
||||||
{
|
{
|
||||||
$this->denyUnlessAdminOrSecretaire();
|
$this->denyUnlessAdminOrSecretaire();
|
||||||
|
|
||||||
@ -62,8 +93,49 @@ class InterventionController extends AbstractController
|
|||||||
$form->handleRequest($request);
|
$form->handleRequest($request);
|
||||||
|
|
||||||
if ($form->isSubmitted() && $form->isValid()) {
|
if ($form->isSubmitted() && $form->isValid()) {
|
||||||
$entityManager->flush();
|
$timestamp = $intervention->getTimestamp();
|
||||||
|
$chauffagiste = $intervention->getUser();
|
||||||
|
$vehicule = $intervention->getVehicle();
|
||||||
|
|
||||||
|
$conflictUser = $interventionRepository->createQueryBuilder('i')
|
||||||
|
->where('i.Timestamp = :time')
|
||||||
|
->andWhere('i.user = :user')
|
||||||
|
->andWhere('i != :current')
|
||||||
|
->setParameter('time', $timestamp)
|
||||||
|
->setParameter('user', $chauffagiste)
|
||||||
|
->setParameter('current', $intervention)
|
||||||
|
->getQuery()
|
||||||
|
->getResult();
|
||||||
|
|
||||||
|
if ($conflictUser) {
|
||||||
|
$this->addFlash('error', 'Ce chauffagiste a déjà une autre intervention à cette date.');
|
||||||
|
return $this->render('intervention/edit.html.twig', [
|
||||||
|
'form' => $form,
|
||||||
|
'intervention' => $intervention,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($vehicule) {
|
||||||
|
$conflictVehicule = $interventionRepository->createQueryBuilder('i')
|
||||||
|
->where('i.Timestamp = :time')
|
||||||
|
->andWhere('i.vehicle = :vehicule')
|
||||||
|
->andWhere('i != :current')
|
||||||
|
->setParameter('time', $timestamp)
|
||||||
|
->setParameter('user', $chauffagiste)
|
||||||
|
->setParameter('current', $intervention)
|
||||||
|
->getQuery()
|
||||||
|
->getResult();
|
||||||
|
|
||||||
|
if ($conflictVehicule) {
|
||||||
|
$this->addFlash('error', 'Ce véhicule est déjà utilisé à cette date.');
|
||||||
|
return $this->render('intervention/edit.html.twig', [
|
||||||
|
'form' => $form,
|
||||||
|
'intervention' => $intervention,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$entityManager->flush();
|
||||||
return $this->redirectToRoute('app_intervention_index');
|
return $this->redirectToRoute('app_intervention_index');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,7 +161,6 @@ class InterventionController extends AbstractController
|
|||||||
#[Route('/{id}/remarque', name: 'app_intervention_remarque', methods: ['GET', 'POST'])]
|
#[Route('/{id}/remarque', name: 'app_intervention_remarque', methods: ['GET', 'POST'])]
|
||||||
public function ajouterRemarque(Request $request, Intervention $intervention, EntityManagerInterface $entityManager): Response
|
public function ajouterRemarque(Request $request, Intervention $intervention, EntityManagerInterface $entityManager): Response
|
||||||
{
|
{
|
||||||
// Vérifie que l'utilisateur est le chauffagiste assigné à l'intervention
|
|
||||||
$user = $this->getUser();
|
$user = $this->getUser();
|
||||||
if (!$this->isGranted('ROLE_CHAUFFAGISTE') || $intervention->getUser() !== $user) {
|
if (!$this->isGranted('ROLE_CHAUFFAGISTE') || $intervention->getUser() !== $user) {
|
||||||
throw $this->createAccessDeniedException("Vous ne pouvez modifier que vos propres interventions.");
|
throw $this->createAccessDeniedException("Vous ne pouvez modifier que vos propres interventions.");
|
||||||
@ -100,7 +171,6 @@ class InterventionController extends AbstractController
|
|||||||
|
|
||||||
if ($form->isSubmitted() && $form->isValid()) {
|
if ($form->isSubmitted() && $form->isValid()) {
|
||||||
$entityManager->flush();
|
$entityManager->flush();
|
||||||
|
|
||||||
$this->addFlash('success', 'Remarque ajoutée avec succès.');
|
$this->addFlash('success', 'Remarque ajoutée avec succès.');
|
||||||
return $this->redirectToRoute('app_intervention_show', ['id' => $intervention->getId()]);
|
return $this->redirectToRoute('app_intervention_show', ['id' => $intervention->getId()]);
|
||||||
}
|
}
|
||||||
|
@ -13,9 +13,9 @@ use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
|
|||||||
use Symfony\Component\Routing\Annotation\Route;
|
use Symfony\Component\Routing\Annotation\Route;
|
||||||
use Symfony\Component\Security\Http\Attribute\IsGranted;
|
use Symfony\Component\Security\Http\Attribute\IsGranted;
|
||||||
|
|
||||||
|
#[IsGranted('ROLE_ADMIN')] // accès global restreint
|
||||||
final class UserController extends AbstractController
|
final class UserController extends AbstractController
|
||||||
{
|
{
|
||||||
// Route pour afficher tous les utilisateurs
|
|
||||||
#[Route('/user', name: 'app_user_index', methods: ['GET'])]
|
#[Route('/user', name: 'app_user_index', methods: ['GET'])]
|
||||||
public function index(UserRepository $userRepository): Response
|
public function index(UserRepository $userRepository): Response
|
||||||
{
|
{
|
||||||
@ -24,7 +24,6 @@ final class UserController extends AbstractController
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Route pour créer un nouvel utilisateur
|
|
||||||
#[Route('/user/new', name: 'app_user_new', methods: ['GET', 'POST'])]
|
#[Route('/user/new', name: 'app_user_new', methods: ['GET', 'POST'])]
|
||||||
public function new(Request $request, EntityManagerInterface $entityManager, UserPasswordHasherInterface $passwordHasher): Response
|
public function new(Request $request, EntityManagerInterface $entityManager, UserPasswordHasherInterface $passwordHasher): Response
|
||||||
{
|
{
|
||||||
@ -33,7 +32,6 @@ final class UserController extends AbstractController
|
|||||||
$form->handleRequest($request);
|
$form->handleRequest($request);
|
||||||
|
|
||||||
if ($form->isSubmitted() && $form->isValid()) {
|
if ($form->isSubmitted() && $form->isValid()) {
|
||||||
// Hash du mot de passe
|
|
||||||
$plainPassword = $form->get('plainPassword')->getData();
|
$plainPassword = $form->get('plainPassword')->getData();
|
||||||
$hashedPassword = $passwordHasher->hashPassword($user, $plainPassword);
|
$hashedPassword = $passwordHasher->hashPassword($user, $plainPassword);
|
||||||
$user->setPassword($hashedPassword);
|
$user->setPassword($hashedPassword);
|
||||||
@ -50,7 +48,6 @@ final class UserController extends AbstractController
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Route pour afficher un utilisateur spécifique
|
|
||||||
#[Route('/user/{id}', name: 'app_user_show', methods: ['GET'])]
|
#[Route('/user/{id}', name: 'app_user_show', methods: ['GET'])]
|
||||||
public function show(Utilisateur $user): Response
|
public function show(Utilisateur $user): Response
|
||||||
{
|
{
|
||||||
@ -59,21 +56,9 @@ final class UserController extends AbstractController
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Route pour modifier un utilisateur spécifique
|
|
||||||
#[Route('/user/{id}/edit', name: 'app_user_edit', methods: ['GET', 'POST'])]
|
#[Route('/user/{id}/edit', name: 'app_user_edit', methods: ['GET', 'POST'])]
|
||||||
public function edit(Request $request, Utilisateur $user, EntityManagerInterface $entityManager): Response
|
public function edit(Request $request, Utilisateur $user, EntityManagerInterface $entityManager): Response
|
||||||
{
|
{
|
||||||
if ($this->isGranted('ROLE_SECRETAIRE') && $user->hasRole('ROLE_SECRETAIRE')) {
|
|
||||||
throw $this->createAccessDeniedException('Vous ne pouvez pas modifier un autre secrétaire.');
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->isGranted('ROLE_CHAUFFAGISTE') && ($user->hasRole('ROLE_CHAUFFAGISTE') || $user->hasRole('ROLE_SECRETAIRE'))) {
|
|
||||||
throw $this->createAccessDeniedException('Vous ne pouvez pas modifier un admin.');
|
|
||||||
}
|
|
||||||
|
|
||||||
// On s'assure que seul un admin peut éditer un autre admin
|
|
||||||
$this->denyAccessUnlessGranted('ROLE_ADMIN');
|
|
||||||
|
|
||||||
$form = $this->createForm(UserType::class, $user);
|
$form = $this->createForm(UserType::class, $user);
|
||||||
$form->handleRequest($request);
|
$form->handleRequest($request);
|
||||||
|
|
||||||
@ -89,15 +74,12 @@ final class UserController extends AbstractController
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Route pour supprimer un utilisateur spécifique
|
|
||||||
#[IsGranted('ROLE_ADMIN', 'ROLE_SECRETAIRE')]
|
|
||||||
#[Route('/user/{id}', name: 'app_user_delete', methods: ['POST'])]
|
#[Route('/user/{id}', name: 'app_user_delete', methods: ['POST'])]
|
||||||
public function delete(Request $request, Utilisateur $utilisateur): Response
|
public function delete(Request $request, Utilisateur $utilisateur, EntityManagerInterface $entityManager): Response
|
||||||
{
|
{
|
||||||
if ($this->isCsrfTokenValid('delete' . $utilisateur->getId(), $request->request->get('_token'))) {
|
if ($this->isCsrfTokenValid('delete' . $utilisateur->getId(), $request->request->get('_token'))) {
|
||||||
$this->entityManager->remove($utilisateur);
|
$entityManager->remove($utilisateur);
|
||||||
$this->entityManager->flush();
|
$entityManager->flush();
|
||||||
|
|
||||||
$this->addFlash('success', 'Utilisateur supprimé avec succès.');
|
$this->addFlash('success', 'Utilisateur supprimé avec succès.');
|
||||||
} else {
|
} else {
|
||||||
$this->addFlash('error', 'Token CSRF invalide.');
|
$this->addFlash('error', 'Token CSRF invalide.');
|
||||||
|
@ -7,6 +7,7 @@ use App\Entity\Intervention;
|
|||||||
use App\Entity\Stock;
|
use App\Entity\Stock;
|
||||||
use App\Entity\Utilisateur;
|
use App\Entity\Utilisateur;
|
||||||
use App\Entity\Vehicle;
|
use App\Entity\Vehicle;
|
||||||
|
use Doctrine\ORM\EntityRepository;
|
||||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||||
use Symfony\Component\Form\AbstractType;
|
use Symfony\Component\Form\AbstractType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\DateTimeType;
|
use Symfony\Component\Form\Extension\Core\Type\DateTimeType;
|
||||||
@ -28,7 +29,15 @@ class InterventionType extends AbstractType
|
|||||||
->add('Status', TextType::class)
|
->add('Status', TextType::class)
|
||||||
->add('user', EntityType::class, [
|
->add('user', EntityType::class, [
|
||||||
'class' => Utilisateur::class,
|
'class' => Utilisateur::class,
|
||||||
'choice_label' => 'FirstName', // ou autre (LastName, email)
|
'choice_label' => function (Utilisateur $user) {
|
||||||
|
return $user->getFirstName() . ' ' . $user->getLastName();
|
||||||
|
},
|
||||||
|
'query_builder' => function (EntityRepository $er) {
|
||||||
|
return $er->createQueryBuilder('u')
|
||||||
|
->where('JSON_CONTAINS(u.roles, :role) = 1')
|
||||||
|
->setParameter('role', '"ROLE_CHAUFFAGISTE"');
|
||||||
|
},
|
||||||
|
'label' => 'Chauffagiste assigné',
|
||||||
])
|
])
|
||||||
->add('fault', EntityType::class, [
|
->add('fault', EntityType::class, [
|
||||||
'class' => Fault::class,
|
'class' => Fault::class,
|
||||||
@ -44,16 +53,7 @@ class InterventionType extends AbstractType
|
|||||||
'choice_label' => 'Wording',
|
'choice_label' => 'Wording',
|
||||||
'multiple' => true,
|
'multiple' => true,
|
||||||
'expanded' => true,
|
'expanded' => true,
|
||||||
])
|
]);
|
||||||
;
|
|
||||||
|
|
||||||
// // 👉 Sélecteur de véhicule
|
|
||||||
// ->add('vehicule', EntityType::class, [
|
|
||||||
// 'class' => Vehicule::class,
|
|
||||||
// 'choice_label' => 'immatriculation', // ou n'importe quel champ que tu veux afficher
|
|
||||||
// 'placeholder' => 'Aucun véhicule sélectionné',
|
|
||||||
// 'required' => false,
|
|
||||||
// ]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function configureOptions(OptionsResolver $resolver): void
|
public function configureOptions(OptionsResolver $resolver): void
|
||||||
|
@ -95,18 +95,23 @@
|
|||||||
<ul>
|
<ul>
|
||||||
{% if is_granted('ROLE_ADMIN') %}
|
{% if is_granted('ROLE_ADMIN') %}
|
||||||
<li><a href="{{ path('admin_dashboard') }}">Dashboard Admin</a></li>
|
<li><a href="{{ path('admin_dashboard') }}">Dashboard Admin</a></li>
|
||||||
|
<li><a href="{{ path('app_intervention_index') }}">Gérer les interventions</a></li>
|
||||||
<li><a href="{{ path('app_user_index') }}">Gérer les utilisateurs</a></li>
|
<li><a href="{{ path('app_user_index') }}">Gérer les utilisateurs</a></li>
|
||||||
<li><a href="{{ path('app_vehicle_index') }}">Gérer les véhicules</a></li>
|
<li><a href="{{ path('app_vehicle_index') }}">Gérer les véhicules</a></li>
|
||||||
<li><a href="{{ path('app_stock_index') }}">Gérer les stocks</a></li>
|
<li><a href="{{ path('app_stock_index') }}">Gérer les stocks</a></li>
|
||||||
<li><a href="{{ path('app_fault_index') }}">Gérer les pannes</a></li>
|
<li><a href="{{ path('app_fault_index') }}">Gérer les pannes</a></li>
|
||||||
|
<li><a href="{{ path('app_skill_index') }}">Gérer les compétences</a></li>
|
||||||
<li><a href="{{ path('app_calendrier_index') }}">Tous les plannings</a></li>
|
<li><a href="{{ path('app_calendrier_index') }}">Tous les plannings</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if is_granted('ROLE_SECRETAIRE') %}
|
{% if is_granted('ROLE_SECRETAIRE') %}
|
||||||
<li><a href="{{ path('secretaire_dashboard') }}">Dashboard Secrétaire</a></li>
|
<li><a href="{{ path('secretaire_dashboard') }}">Dashboard Secrétaire</a></li>
|
||||||
|
<li><a href="{{ path('app_intervention_index') }}">Gérer les interventions</a></li>
|
||||||
<li><a href="{{ path('app_user_index') }}">Créer un chauffagiste</a></li>
|
<li><a href="{{ path('app_user_index') }}">Créer un chauffagiste</a></li>
|
||||||
<li><a href="{{ path('app_vehicle_index') }}">Gérer les véhicules</a></li>
|
<li><a href="{{ path('app_vehicle_index') }}">Gérer les véhicules</a></li>
|
||||||
<li><a href="{{ path('app_stock_index') }}">Gérer les stocks</a></li>
|
<li><a href="{{ path('app_stock_index') }}">Gérer les stocks</a></li>
|
||||||
|
<li><a href="{{ path('app_fault_index') }}">Gérer les pannes</a></li>
|
||||||
|
<li><a href="{{ path('app_skill_index') }}">Gérer les compétences</a></li>
|
||||||
<li><a href="{{ path('app_calendrier_indexSecretaire') }}">Plannings chauffagistes</a></li>
|
<li><a href="{{ path('app_calendrier_indexSecretaire') }}">Plannings chauffagistes</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
@ -5,38 +5,24 @@
|
|||||||
{% block body %}
|
{% block body %}
|
||||||
<h1>📅 Calendrier des interventions</h1>
|
<h1>📅 Calendrier des interventions</h1>
|
||||||
|
|
||||||
<a href="{{ path('app_intervention_new') }}" class="btn btn-success mt-3">➕ Ajouter une intervention</a>
|
|
||||||
|
|
||||||
<div id="calendar"></div>
|
<div id="calendar"></div>
|
||||||
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/fullcalendar@6.1.8/index.global.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/fullcalendar@6.1.8/index.global.min.js"></script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
var calendarEl = document.getElementById('calendar');
|
var calendarEl = document.getElementById('calendar');
|
||||||
|
|
||||||
var calendar = new FullCalendar.Calendar(calendarEl, {
|
var calendar = new FullCalendar.Calendar(calendarEl, {
|
||||||
initialView: 'dayGridMonth',
|
initialView: 'dayGridMonth',
|
||||||
events: '/api/interventions' // ✅ Point d’accès API
|
locale: 'fr',
|
||||||
|
eventTimeFormat: {
|
||||||
|
hour: '2-digit',
|
||||||
|
minute: '2-digit',
|
||||||
|
hour12: false
|
||||||
|
},
|
||||||
|
events: {{ events|raw }}
|
||||||
});
|
});
|
||||||
|
|
||||||
calendar.render();
|
calendar.render();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{# <div id="calendar"></div>#}
|
|
||||||
|
|
||||||
{# <script>#}
|
|
||||||
{# document.addEventListener('DOMContentLoaded', function() {#}
|
|
||||||
{# var calendarEl = document.getElementById('calendar');#}
|
|
||||||
{# var calendar = new FullCalendar.Calendar(calendarEl, {#}
|
|
||||||
{# initialView: 'dayGridMonth',#}
|
|
||||||
{# events: {{ events | raw }},#}
|
|
||||||
{# eventClick: function(info) {#}
|
|
||||||
{# alert('Intervention : ' + info.event.title + '\n' + info.event.extendedProps.description);#}
|
|
||||||
{# }#}
|
|
||||||
{# });#}
|
|
||||||
{# calendar.render();#}
|
|
||||||
{# });#}
|
|
||||||
{# </script>#}
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -7,15 +7,20 @@
|
|||||||
|
|
||||||
<div id="calendar"></div>
|
<div id="calendar"></div>
|
||||||
|
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/fullcalendar@6.1.8/index.global.min.js"></script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
var calendarEl = document.getElementById('calendar');
|
var calendarEl = document.getElementById('calendar');
|
||||||
var calendar = new FullCalendar.Calendar(calendarEl, {
|
var calendar = new FullCalendar.Calendar(calendarEl, {
|
||||||
initialView: 'dayGridMonth',
|
initialView: 'dayGridMonth',
|
||||||
events: {{ events | raw }},
|
locale: 'fr',
|
||||||
eventClick: function(info) {
|
eventTimeFormat: {
|
||||||
alert('Intervention : ' + info.event.title + '\n' + info.event.extendedProps.description);
|
hour: '2-digit',
|
||||||
}
|
minute: '2-digit',
|
||||||
|
hour12: false
|
||||||
|
},
|
||||||
|
events: {{ events|raw }}
|
||||||
});
|
});
|
||||||
calendar.render();
|
calendar.render();
|
||||||
});
|
});
|
||||||
|
@ -7,15 +7,20 @@
|
|||||||
|
|
||||||
<div id="calendar"></div>
|
<div id="calendar"></div>
|
||||||
|
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/fullcalendar@6.1.8/index.global.min.js"></script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
var calendarEl = document.getElementById('calendar');
|
var calendarEl = document.getElementById('calendar');
|
||||||
var calendar = new FullCalendar.Calendar(calendarEl, {
|
var calendar = new FullCalendar.Calendar(calendarEl, {
|
||||||
initialView: 'dayGridMonth',
|
initialView: 'dayGridMonth',
|
||||||
events: {{ events | raw }},
|
locale: 'fr',
|
||||||
eventClick: function(info) {
|
eventTimeFormat: {
|
||||||
alert('Intervention : ' + info.event.title + '\n' + info.event.extendedProps.description);
|
hour: '2-digit',
|
||||||
}
|
minute: '2-digit',
|
||||||
|
hour12: false
|
||||||
|
},
|
||||||
|
events: {{ events|raw }}
|
||||||
});
|
});
|
||||||
calendar.render();
|
calendar.render();
|
||||||
});
|
});
|
||||||
|
@ -1,9 +1,3 @@
|
|||||||
{#{{ form_start(form) }}#}
|
|
||||||
{# {{ form_widget(form) }}#}
|
|
||||||
{# <button class="btn">{{ button_label|default('Save') }}</button>#}
|
|
||||||
{#{{ form_end(form) }}#}
|
|
||||||
|
|
||||||
|
|
||||||
{{ form_start(form) }}
|
{{ form_start(form) }}
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
@ -36,12 +30,30 @@
|
|||||||
{{ form_errors(form.Status) }}
|
{{ form_errors(form.Status) }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#<div class="form-group">#}
|
<div class="form-group">
|
||||||
{# {{ form_label(form.vehicule, 'Véhicule associé') }}#}
|
{{ form_label(form.fault, 'Panne') }}
|
||||||
{# {{ form_widget(form.vehicule, {'attr': {'class': 'form-control'}}) }}#}
|
{{ form_widget(form.fault, {'attr': {'class': 'form-control'}}) }}
|
||||||
{# {{ form_errors(form.vehicule) }}#}
|
{{ form_errors(form.fault) }}
|
||||||
{#</div>#}
|
</div>
|
||||||
|
|
||||||
<button class="btn btn-primary mt-3">{{ button_label|default('Enregistrer') }}</button>
|
<div class="form-group">
|
||||||
|
{{ form_label(form.user, 'Chauffagiste assigné') }}
|
||||||
|
{{ form_widget(form.user, {'attr': {'class': 'form-control'}}) }}
|
||||||
|
{{ form_errors(form.user) }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
{{ form_label(form.vehicle, 'Véhicule') }}
|
||||||
|
{{ form_widget(form.vehicle, {'attr': {'class': 'form-control'}}) }}
|
||||||
|
{{ form_errors(form.vehicle) }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
{{ form_label(form.stocks, 'Pièces utilisées') }}
|
||||||
|
{{ form_widget(form.stocks, {'attr': {'class': 'form-control'}}) }}
|
||||||
|
{{ form_errors(form.stocks) }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button class="btn btn-primary mt-4">{{ button_label|default('Enregistrer') }}</button>
|
||||||
|
|
||||||
{{ form_end(form) }}
|
{{ form_end(form) }}
|
||||||
|
@ -5,67 +5,7 @@
|
|||||||
{% block body %}
|
{% block body %}
|
||||||
<h1 class="mb-4">➕ Créer une nouvelle intervention</h1>
|
<h1 class="mb-4">➕ Créer une nouvelle intervention</h1>
|
||||||
|
|
||||||
{#<<<<<<< HEAD#}
|
|
||||||
<div class="background-intervention">
|
<div class="background-intervention">
|
||||||
{# <input required id="nom" placeholder="Nom de l'intervention">#}
|
|
||||||
|
|
||||||
{# <select required type="select" name="pannes" id="pannes">#}
|
|
||||||
{# <option value="pan1">pan1</option>#}
|
|
||||||
{# <option value="pan2">pan2</option>#}
|
|
||||||
{# <option value="pan3">pan3</option>#}
|
|
||||||
{# <option value="pan4">pan4</option>#}
|
|
||||||
{# <option value="pan5">pan5</option>#}
|
|
||||||
{# <option value="pan6">pan6</option>#}
|
|
||||||
{# </select>#}
|
|
||||||
|
|
||||||
{# <select required type="select" name="pannes" id="employesCompetences">#}
|
|
||||||
{# <option value="emp1">employé1 - sa compétence</option>#}
|
|
||||||
{# <option value="emp2">employé2 - sa compétence</option>#}
|
|
||||||
{# <option value="emp3">employé3 - sa compétence</option>#}
|
|
||||||
{# <option value="emp4">employé4 - sa compétence</option>#}
|
|
||||||
{# <option value="emp5">employé5 - sa compétence</option>#}
|
|
||||||
{# <option value="emp6">employé6 - sa compétence</option>#}
|
|
||||||
{# </select>#}
|
|
||||||
|
|
||||||
{# <!-- <input required id="employesCompetences" placeholder="Liste des employés et de leurs compétences"> -->#}
|
|
||||||
{# <select required type="select" name="piece" id="piece">#}
|
|
||||||
{# <option value="pièce1">Pièce affecter 1</option>#}
|
|
||||||
{# <option value="pièce2">Pièce affecter 2</option>#}
|
|
||||||
{# <option value="pièce3">Pièce affecter 3</option>#}
|
|
||||||
{# <option value="pièce4">Pièce affecter 4</option>#}
|
|
||||||
{# <option value="pièce5">Pièce affecter 5</option>#}
|
|
||||||
{# <option value="pièce6">Pièce affecter 6</option>#}
|
|
||||||
{# </select>#}
|
|
||||||
|
|
||||||
{# <p id="vehicule">Véhicule nécessaire</p>#}
|
|
||||||
{# <form>#}
|
|
||||||
{# <label id="oui">#}
|
|
||||||
{# <input type="radio" name="choix" value="option1">#}
|
|
||||||
{# Oui </label>#}
|
|
||||||
{# <label id="non">#}
|
|
||||||
{# <input type="radio" name="choix" value="option2">#}
|
|
||||||
{# Non </label>#}
|
|
||||||
{# </form>#}
|
|
||||||
|
|
||||||
{# <p id="date">Jour de l'intervention</p>#}
|
|
||||||
{# <input type="date" id="calendar">#}
|
|
||||||
|
|
||||||
{# <input type="text" id="description" placeholder="Description">#}
|
|
||||||
{# <input type="text" id="adresse" placeholder="Adresse">#}
|
|
||||||
|
|
||||||
{# <button type="submit" class="applique"> Appliquée </button>#}
|
|
||||||
{# <button type="reset" class="supprimer"> Supprimer </button>#}
|
|
||||||
|
|
||||||
{{ include('intervention/_form.html.twig') }}
|
{{ include('intervention/_form.html.twig') }}
|
||||||
</div>
|
</div>
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
{#=======#}
|
|
||||||
<div class="mt-3">
|
|
||||||
<a href="{{ path('app_intervention_index') }}" class="btn btn-secondary">← Retour à la liste des interventions</a>
|
|
||||||
</div>
|
|
||||||
<div class="mt-3">
|
|
||||||
<a href="{{ path('app_calendrier_index') }}" class="btn btn-secondary">← Retour au calendrier</a>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{#>>>>>>> 4fc91211f0d814453d2ed97caf6a1d94d709058e#}
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user