Compare commits

..

14 Commits

Author SHA1 Message Date
Yacha-Shan
c640bdc4d3 mise à jour de ma branche 2025-05-05 10:07:45 +02:00
Yacha-Shan
3777067950 modification de l'entité et le form intervention et des templates intervention _form et new 2025-05-05 10:07:08 +02:00
4fc91211f0 correction des conditions pour supprimer et éditer un utilisateur 2025-04-25 18:07:10 +02:00
65093ec738 correction des conditions pour supprimer et éditer un utilisateur 2025-04-25 18:06:21 +02:00
a47abaabfd mise en place de calendrier dynamique 2025-04-25 17:34:37 +02:00
b20dc2e00f 🔧 Sécurisation des contrôleurs : gestion des accès par rôles (admin, secrétaire, chauffagiste) 2025-04-25 15:40:06 +02:00
28f63a5363 ajout du README 2025-04-25 15:19:19 +02:00
b35c19fa5b Mise à jour majeure : liaisons entre entités, formulaires et affichages
- Relations entre entités (User, Intervention, etc.)\n- Formulaires adaptés\n- Refacto visuel des vues\n- Nouvelle base.html.twig\n- Dashboards par rôle
2025-04-25 14:55:06 +02:00
b80e2365ad Merge branch 'feature/Maxim' 2025-04-24 16:24:21 +02:00
d464aa9b2d modification des vues fault index et show + du form fault 2025-04-24 16:23:19 +02:00
f098f5beeb modification des vues fault index et show + du form fault 2025-04-24 16:20:20 +02:00
Yacha-Shan
3a4e9ce733 Merge branch 'feature/Alyssa' into develop 2025-04-19 21:59:01 +02:00
Yacha-Shan
8e1e5180dd Merge branch 'feature/Alyssa' into develop 2025-04-19 15:10:55 +02:00
0d6b65ef20 tous les rôles ont leur redirection vers leur tableau de bord, avancement sur le canlendrier controller 2025-04-18 15:54:51 +02:00
57 changed files with 1398 additions and 654 deletions

4
.idea/dataSources.xml generated
View File

@ -1,11 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
<data-source source="LOCAL" name="hegreetconfort@localhost" uuid="21423ae4-3232-4641-affb-06399f70655a">
<data-source source="LOCAL" name="HegreEtConfort@localhost" uuid="21423ae4-3232-4641-affb-06399f70655a">
<driver-ref>postgresql</driver-ref>
<synchronize>true</synchronize>
<jdbc-driver>org.postgresql.Driver</jdbc-driver>
<jdbc-url>jdbc:postgresql://localhost:5432/hegreetconfort</jdbc-url>
<jdbc-url>jdbc:postgresql://localhost:5433/HegreEtConfort</jdbc-url>
<working-dir>$ProjectFileDir$</working-dir>
</data-source>
</component>

15
.idea/php.xml generated
View File

@ -10,6 +10,11 @@
<option name="highlightLevel" value="WARNING" />
<option name="transferred" value="true" />
</component>
<component name="PhpCodeSniffer">
<phpcs_settings>
<phpcs_by_interpreter asDefaultInterpreter="true" interpreter_id="4bd9484f-d78e-4315-b899-c673e4a83c70" timeout="30000" />
</phpcs_settings>
</component>
<component name="PhpIncludePathManager">
<include_path>
<path value="$PROJECT_DIR$/vendor/sebastian/exporter" />
@ -146,6 +151,11 @@
</include_path>
</component>
<component name="PhpProjectSharedConfiguration" php_language_level="8.2" />
<component name="PhpStan">
<PhpStan_settings>
<phpstan_by_interpreter asDefaultInterpreter="true" interpreter_id="4bd9484f-d78e-4315-b899-c673e4a83c70" timeout="60000" />
</PhpStan_settings>
</component>
<component name="PhpStanOptionsConfiguration">
<option name="transferred" value="true" />
</component>
@ -154,6 +164,11 @@
<PhpUnitSettings configuration_file_path="$PROJECT_DIR$/phpunit.xml.dist" custom_loader_path="$PROJECT_DIR$/vendor/autoload.php" use_configuration_file="true" />
</phpunit_settings>
</component>
<component name="Psalm">
<Psalm_settings>
<psalm_fixer_by_interpreter asDefaultInterpreter="true" interpreter_id="4bd9484f-d78e-4315-b899-c673e4a83c70" timeout="60000" />
</Psalm_settings>
</component>
<component name="PsalmOptionsConfiguration">
<option name="transferred" value="true" />
</component>

59
README.md Normal file
View File

@ -0,0 +1,59 @@
# 🚀 Application de Gestion Chauffagiste
## Description
Cette application permet de gérer les interventions, les utilisateurs (chauffagistes, secrétaires, admin), les pièces détachées, les véhicules, et les plannings dans une entreprise de chauffagistes.
## Fonctionnalités principales
- **Gestion des utilisateurs** : création de chauffagistes, secrétaires, et admins avec des rôles spécifiques.
- **Gestion des interventions** : assignation des chauffagistes, suivi des véhicules et des pièces détachées.
- **Gestion des stocks et des véhicules** : gestion des pièces détachées et des véhicules.
- **Planning des interventions** : chaque rôle a accès à un planning adapté (chauffagiste, secrétaire, admin).
- **Sécurisation par rôles** : accès aux pages limité par rôle (Admin, Secrétaire, Chauffagiste).
### Précisions pour les rôles :
- **Admin** : Accès complet à toutes les fonctionnalités (gestion des utilisateurs, véhicules, stocks, plannings, etc.)
- **Secrétaire** : Peut gérer les chauffagistes, les interventions et le planning des chauffagistes, mais **ne peut pas gérer d'autres secrétaires ni les admins**.
- **Chauffagiste** : Accède uniquement à ses propres interventions et à son planning.
## Installation
### Prérequis :
- PHP 8.x
- Composer
- Symfony 7.x
### Étapes d'installation :
1. Clonez le projet :
```bash
git clone https://gitea.btssio-poitiers.fr/sermandm/HegreEtConfort.git
```
2. Installez les dépendances avec Composer :
```bash
cd chauffagiste-app
composer install
```
3. Créez la base de données :
```bash
php bin/console doctrine:database:create
```
4. Exécutez les migrations pour créer les tables :
```bash
php bin/console doctrine:migrations:migrate
```
5. Lancez le serveur Symfony :
```bash
symfony server:start
```
Accédez ensuite à l'application sur `http://localhost:8000`.
## Sécurisation des accès
Les secrétaires ont accès à toutes les pages **sauf celles concernant d'autres secrétaires et les admins**. Cela est géré par les contrôleurs via la méthode `denyAccessUnlessGranted()` pour vérifier le rôle de l'utilisateur. Par exemple, un secrétaire ne pourra pas modifier un autre secrétaire.
---
## Technologies utilisées
- Symfony 7.x
- Doctrine ORM
- Twig
- PHP 8.x

View File

@ -108,19 +108,19 @@ body {
text-align: center;
background-color: white;
}
#vehicule1 {
#oui {
position: absolute;
margin: 30.7% 0 0 25%;
}
#oui {
#oui p {
position: absolute;
margin: 30.3% 0 0 27%;
}
#vehicule2 {
#non {
position: absolute;
margin: 30.7% 0 0 32%;
}
#non {
#non p {
position: absolute;
margin: 30.3% 0 0 34%;
}

View File

@ -0,0 +1,64 @@
<?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 Version20250425115854 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('CREATE TABLE intervention_stock (intervention_id INT NOT NULL, stock_id INT NOT NULL, PRIMARY KEY(intervention_id, stock_id))');
$this->addSql('CREATE INDEX IDX_3E534AE48EAE3863 ON intervention_stock (intervention_id)');
$this->addSql('CREATE INDEX IDX_3E534AE4DCD6110 ON intervention_stock (stock_id)');
$this->addSql('CREATE TABLE user_skill (utilisateur_id INT NOT NULL, skill_id INT NOT NULL, PRIMARY KEY(utilisateur_id, skill_id))');
$this->addSql('CREATE INDEX IDX_BCFF1F2FFB88E14F ON user_skill (utilisateur_id)');
$this->addSql('CREATE INDEX IDX_BCFF1F2F5585C142 ON user_skill (skill_id)');
$this->addSql('ALTER TABLE intervention_stock ADD CONSTRAINT FK_3E534AE48EAE3863 FOREIGN KEY (intervention_id) REFERENCES intervention (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
$this->addSql('ALTER TABLE intervention_stock ADD CONSTRAINT FK_3E534AE4DCD6110 FOREIGN KEY (stock_id) REFERENCES stock (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
$this->addSql('ALTER TABLE user_skill ADD CONSTRAINT FK_BCFF1F2FFB88E14F FOREIGN KEY (utilisateur_id) REFERENCES utilisateur (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
$this->addSql('ALTER TABLE user_skill ADD CONSTRAINT FK_BCFF1F2F5585C142 FOREIGN KEY (skill_id) REFERENCES skill (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
$this->addSql('ALTER TABLE intervention ADD user_id INT NOT NULL');
$this->addSql('ALTER TABLE intervention ADD fault_id INT NOT NULL');
$this->addSql('ALTER TABLE intervention ADD vehicle_id INT DEFAULT NULL');
$this->addSql('ALTER TABLE intervention ADD CONSTRAINT FK_D11814ABA76ED395 FOREIGN KEY (user_id) REFERENCES utilisateur (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
$this->addSql('ALTER TABLE intervention ADD CONSTRAINT FK_D11814AB24171CD3 FOREIGN KEY (fault_id) REFERENCES fault (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
$this->addSql('ALTER TABLE intervention ADD CONSTRAINT FK_D11814AB545317D1 FOREIGN KEY (vehicle_id) REFERENCES vehicle (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
$this->addSql('CREATE INDEX IDX_D11814ABA76ED395 ON intervention (user_id)');
$this->addSql('CREATE INDEX IDX_D11814AB24171CD3 ON intervention (fault_id)');
$this->addSql('CREATE INDEX IDX_D11814AB545317D1 ON intervention (vehicle_id)');
}
public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('CREATE SCHEMA public');
$this->addSql('ALTER TABLE intervention_stock DROP CONSTRAINT FK_3E534AE48EAE3863');
$this->addSql('ALTER TABLE intervention_stock DROP CONSTRAINT FK_3E534AE4DCD6110');
$this->addSql('ALTER TABLE user_skill DROP CONSTRAINT FK_BCFF1F2FFB88E14F');
$this->addSql('ALTER TABLE user_skill DROP CONSTRAINT FK_BCFF1F2F5585C142');
$this->addSql('DROP TABLE intervention_stock');
$this->addSql('DROP TABLE user_skill');
$this->addSql('ALTER TABLE intervention DROP CONSTRAINT FK_D11814ABA76ED395');
$this->addSql('ALTER TABLE intervention DROP CONSTRAINT FK_D11814AB24171CD3');
$this->addSql('ALTER TABLE intervention DROP CONSTRAINT FK_D11814AB545317D1');
$this->addSql('DROP INDEX IDX_D11814ABA76ED395');
$this->addSql('DROP INDEX IDX_D11814AB24171CD3');
$this->addSql('DROP INDEX IDX_D11814AB545317D1');
$this->addSql('ALTER TABLE intervention DROP user_id');
$this->addSql('ALTER TABLE intervention DROP fault_id');
$this->addSql('ALTER TABLE intervention DROP vehicle_id');
}
}

View File

@ -12,10 +12,10 @@ class AuthenticationController extends AbstractController
#[Route(path: '/', name: 'app_login')]
public function login(AuthenticationUtils $authenticationUtils): Response
{
// get the login error if there is one
// Get the login error if there is one
$error = $authenticationUtils->getLastAuthenticationError();
// last username entered by the user
// Last username entered by the user
$lastUsername = $authenticationUtils->getLastUsername();
return $this->render('login/index.html.twig', [
@ -29,4 +29,4 @@ class AuthenticationController extends AbstractController
{
throw new \LogicException('This method can be blank - it will be intercepted by the logout key on your firewall.');
}
}
}

View File

@ -2,43 +2,89 @@
namespace App\Controller;
use App\Repository\InterventionRepository;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Routing\Annotation\Route;
#[Route('/calendrier')]
class CalendrierController extends AbstractController
{
#[Route(name: 'app_calendrier_index', methods: ['GET'])]
public function index(): Response
#[Route('/chauffagiste', name: 'app_calendrier_indexChauffagiste')]
public function indexChauffagiste(InterventionRepository $interventionRepository): Response
{
return $this->render('calendrier/index.html.twig', [
'controller_name' => 'CalendrierController',
// Vérification : seul le chauffagiste connecté peut voir son propre calendrier
$this->denyAccessUnlessGranted('ROLE_CHAUFFAGISTE');
// Récupérer les interventions du chauffagiste connecté
$interventions = $interventionRepository->findByUser($this->getUser());
// Préparer les événements pour FullCalendar
$events = [];
foreach ($interventions as $intervention) {
$events[] = [
'title' => $intervention->getTitle(),
'start' => $intervention->getStartDate()->format('Y-m-d H:i:s'),
'end' => $intervention->getEndDate()->format('Y-m-d H:i:s'),
'description' => $intervention->getDescription(),
];
}
// Passer les événements à la vue
return $this->render('calendrier/indexChauffagiste.html.twig', [
'events' => json_encode($events),
]);
}
#[Route('/calendrier/secretaire', name: 'calendrier_index_secretaire')]
public function indexSecretaire(): Response
#[Route('/secretaire', name: 'app_calendrier_indexSecretaire')]
public function indexSecretaire(InterventionRepository $interventionRepository): Response
{
return $this->render('calendrier/indexSecretaire.html.twig');
// Vérification : seul le secrétaire peut accéder à ce calendrier
$this->denyAccessUnlessGranted('ROLE_SECRETAIRE');
// Récupérer toutes les interventions de tous les chauffagistes
$interventions = $interventionRepository->findAll();
// Préparer les événements pour FullCalendar
$events = [];
foreach ($interventions as $intervention) {
$events[] = [
'title' => $intervention->getTitle(),
'start' => $intervention->getStartDate()->format('Y-m-d H:i:s'),
'end' => $intervention->getEndDate()->format('Y-m-d H:i:s'),
'description' => $intervention->getDescription(),
];
}
// Passer les événements à la vue
return $this->render('calendrier/indexSecretaire.html.twig', [
'events' => json_encode($events),
]);
}
#[Route('/calendrier/chauffagiste', name: 'calendrier_index_chauffagiste')]
public function indexChauffagiste(): Response
#[Route('/admin', name: 'app_calendrier_index')]
public function indexAdmin(InterventionRepository $interventionRepository): Response
{
return $this->render('calendrier/indexChauffagiste.html.twig');
// Vérification : seul un admin peut accéder à ce calendrier
$this->denyAccessUnlessGranted('ROLE_ADMIN');
// Récupérer toutes les interventions de tous les chauffagistes
$interventions = $interventionRepository->findAll();
// Préparer les événements pour FullCalendar
$events = [];
foreach ($interventions as $intervention) {
$events[] = [
'title' => $intervention->getTitle(),
'start' => $intervention->getStartDate()->format('Y-m-d H:i:s'),
'end' => $intervention->getEndDate()->format('Y-m-d H:i:s'),
'description' => $intervention->getDescription(),
];
}
// Passer les événements à la vue
return $this->render('calendrier/index.html.twig', [
'events' => json_encode($events),
]);
}
// créer intervention : secretaire + admin
// créer un nouveau rdv
// modifier un rdv
// supprimer un rdv
// choisir un chauffagiste
// deconnexion
// créer intervention : chauffagiste
// modifier un rdv
// deconnexion
// quand on créer une rdv, alors cela nous redirige vers la page intervention
}

View File

@ -4,26 +4,28 @@ namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Routing\Attribute\Route;
class DashboardController extends AbstractController
{
#[Route('/admin/dashboard', name: 'admin_dashboard')]
public function admin(): Response
{
$this->denyAccessUnlessGranted('ROLE_ADMIN');
return $this->render('dashboard/admin.html.twig');
}
#[Route('/secretaire/dashboard', name: 'secretaire_dashboard')]
public function secretaire(): Response
{
$this->denyAccessUnlessGranted('ROLE_SECRETAIRE');
return $this->render('dashboard/secretaire.html.twig');
}
#[Route('/chauffagiste/dashboard', name: 'chauffagiste_dashboard')]
public function chauffagiste(): Response
{
$this->denyAccessUnlessGranted('ROLE_CHAUFFAGISTE');
return $this->render('dashboard/chauffagiste.html.twig');
}
}

View File

@ -17,8 +17,13 @@ final class FaultController extends AbstractController
#[Route(name: 'app_fault_index', methods: ['GET'])]
public function index(FaultRepository $faultRepository): Response
{
// Filtrage des pannes : un chauffagiste ne peut voir que ses pannes
$faults = $this->isGranted('ROLE_CHAUFFAGISTE')
? $faultRepository->findByUser($this->getUser()) // Filtre les pannes par utilisateur
: $faultRepository->findAll(); // Admins voient toutes les pannes
return $this->render('fault/index.html.twig', [
'faults' => $faultRepository->findAll(),
'faults' => $faults,
]);
}
@ -30,6 +35,11 @@ final class FaultController extends AbstractController
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
// Associe la panne à un chauffagiste si c'est un chauffagiste
if ($this->isGranted('ROLE_CHAUFFAGISTE')) {
$fault->setUser($this->getUser());
}
$entityManager->persist($fault);
$entityManager->flush();
@ -45,6 +55,11 @@ final class FaultController extends AbstractController
#[Route('/{id}', name: 'app_fault_show', methods: ['GET'])]
public function show(Fault $fault): Response
{
// Un chauffagiste ne peut voir que ses pannes
if ($this->isGranted('ROLE_CHAUFFAGISTE') && $fault->getUser() !== $this->getUser()) {
throw $this->createAccessDeniedException('Vous ne pouvez pas voir cette panne.');
}
return $this->render('fault/show.html.twig', [
'fault' => $fault,
]);
@ -53,6 +68,11 @@ final class FaultController extends AbstractController
#[Route('/{id}/edit', name: 'app_fault_edit', methods: ['GET', 'POST'])]
public function edit(Request $request, Fault $fault, EntityManagerInterface $entityManager): Response
{
// Un chauffagiste ne peut modifier que ses propres pannes
if ($this->isGranted('ROLE_CHAUFFAGISTE') && $fault->getUser() !== $this->getUser()) {
throw $this->createAccessDeniedException('Vous ne pouvez pas modifier cette panne.');
}
$form = $this->createForm(FaultType::class, $fault);
$form->handleRequest($request);
@ -71,7 +91,12 @@ final class FaultController extends AbstractController
#[Route('/{id}', name: 'app_fault_delete', methods: ['POST'])]
public function delete(Request $request, Fault $fault, EntityManagerInterface $entityManager): Response
{
if ($this->isCsrfTokenValid('delete'.$fault->getId(), $request->getPayload()->getString('_token'))) {
// Un chauffagiste ne peut supprimer que ses propres pannes
if ($this->isGranted('ROLE_CHAUFFAGISTE') && $fault->getUser() !== $this->getUser()) {
throw $this->createAccessDeniedException('Vous ne pouvez pas supprimer cette panne.');
}
if ($this->isCsrfTokenValid('delete'.$fault->getId(), $request->get('csrf_token'))) {
$entityManager->remove($fault);
$entityManager->flush();
}

View File

@ -17,8 +17,16 @@ final class InterventionController extends AbstractController
#[Route(name: 'app_intervention_index', methods: ['GET'])]
public function index(InterventionRepository $interventionRepository): Response
{
// Vérifier si l'utilisateur est un chauffagiste, pour filtrer ses interventions
if ($this->isGranted('ROLE_CHAUFFAGISTE')) {
$interventions = $interventionRepository->findByUser($this->getUser()); // On filtre par utilisateur connecté
} else {
// Les autres rôles (admin) peuvent voir toutes les interventions
$interventions = $interventionRepository->findAll();
}
return $this->render('intervention/index.html.twig', [
'interventions' => $interventionRepository->findAll(),
'interventions' => $interventions,
]);
}
@ -30,6 +38,11 @@ final class InterventionController extends AbstractController
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
// Si l'utilisateur est un chauffagiste, on associe l'intervention à lui
if ($this->isGranted('ROLE_CHAUFFAGISTE')) {
$intervention->setUser($this->getUser());
}
$entityManager->persist($intervention);
$entityManager->flush();
@ -45,6 +58,11 @@ final class InterventionController extends AbstractController
#[Route('/{id}', name: 'app_intervention_show', methods: ['GET'])]
public function show(Intervention $intervention): Response
{
// Vérifier si l'utilisateur peut voir cette intervention (chauffagiste ne voit que ses interventions)
if ($this->isGranted('ROLE_CHAUFFAGISTE') && $intervention->getUser() !== $this->getUser()) {
throw $this->createAccessDeniedException('Vous ne pouvez pas voir cette intervention.');
}
return $this->render('intervention/show.html.twig', [
'intervention' => $intervention,
]);
@ -53,6 +71,11 @@ final class InterventionController extends AbstractController
#[Route('/{id}/edit', name: 'app_intervention_edit', methods: ['GET', 'POST'])]
public function edit(Request $request, Intervention $intervention, EntityManagerInterface $entityManager): Response
{
// Vérification de sécurité : un chauffagiste ne peut modifier que ses propres interventions
if ($this->isGranted('ROLE_CHAUFFAGISTE') && $intervention->getUser() !== $this->getUser()) {
throw $this->createAccessDeniedException('Vous ne pouvez pas modifier cette intervention.');
}
$form = $this->createForm(InterventionType::class, $intervention);
$form->handleRequest($request);
@ -71,7 +94,12 @@ final class InterventionController extends AbstractController
#[Route('/{id}', name: 'app_intervention_delete', methods: ['POST'])]
public function delete(Request $request, Intervention $intervention, EntityManagerInterface $entityManager): Response
{
if ($this->isCsrfTokenValid('delete'.$intervention->getId(), $request->getPayload()->getString('_token'))) {
// Vérification de sécurité : un chauffagiste ne peut supprimer que ses propres interventions
if ($this->isGranted('ROLE_CHAUFFAGISTE') && $intervention->getUser() !== $this->getUser()) {
throw $this->createAccessDeniedException('Vous ne pouvez pas supprimer cette intervention.');
}
if ($this->isCsrfTokenValid('delete'.$intervention->getId(), $request->get('csrf_token'))) {
$entityManager->remove($intervention);
$entityManager->flush();
}

View File

@ -17,6 +17,7 @@ final class SkillController extends AbstractController
#[Route(name: 'app_skill_index', methods: ['GET'])]
public function index(SkillRepository $skillRepository): Response
{
$this->denyAccessUnlessGranted('ROLE_ADMIN');
return $this->render('skill/admin.html.twig', [
'skills' => $skillRepository->findAll(),
]);
@ -25,6 +26,7 @@ final class SkillController extends AbstractController
#[Route('/new', name: 'app_skill_new', methods: ['GET', 'POST'])]
public function new(Request $request, EntityManagerInterface $entityManager): Response
{
$this->denyAccessUnlessGranted('ROLE_ADMIN');
$skill = new Skill();
$form = $this->createForm(SkillType::class, $skill);
$form->handleRequest($request);
@ -53,6 +55,7 @@ final class SkillController extends AbstractController
#[Route('/{id}/edit', name: 'app_skill_edit', methods: ['GET', 'POST'])]
public function edit(Request $request, Skill $skill, EntityManagerInterface $entityManager): Response
{
$this->denyAccessUnlessGranted('ROLE_ADMIN');
$form = $this->createForm(SkillType::class, $skill);
$form->handleRequest($request);
@ -71,7 +74,8 @@ final class SkillController extends AbstractController
#[Route('/{id}', name: 'app_skill_delete', methods: ['POST'])]
public function delete(Request $request, Skill $skill, EntityManagerInterface $entityManager): Response
{
if ($this->isCsrfTokenValid('delete'.$skill->getId(), $request->getPayload()->getString('_token'))) {
$this->denyAccessUnlessGranted('ROLE_ADMIN');
if ($this->isCsrfTokenValid('delete'.$skill->getId(), $request->get('csrf_token'))) {
$entityManager->remove($skill);
$entityManager->flush();
}

View File

@ -53,6 +53,11 @@ final class StockController extends AbstractController
#[Route('/{id}/edit', name: 'app_stock_edit', methods: ['GET', 'POST'])]
public function edit(Request $request, Stock $stock, EntityManagerInterface $entityManager): Response
{
// Si un chauffagiste essaie de modifier un stock d'admin
if ($this->isGranted('ROLE_CHAUFFAGISTE')) {
throw $this->createAccessDeniedException('Vous ne pouvez pas modifier ce stock.');
}
$form = $this->createForm(StockType::class, $stock);
$form->handleRequest($request);
@ -71,7 +76,7 @@ final class StockController extends AbstractController
#[Route('/{id}', name: 'app_stock_delete', methods: ['POST'])]
public function delete(Request $request, Stock $stock, EntityManagerInterface $entityManager): Response
{
if ($this->isCsrfTokenValid('delete'.$stock->getId(), $request->getPayload()->getString('_token'))) {
if ($this->isCsrfTokenValid('delete'.$stock->getId(), $request->get('csrf_token'))) {
$entityManager->remove($stock);
$entityManager->flush();
}

View File

@ -10,12 +10,12 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Routing\Annotation\Route;
#[Route('/user')]
final class UserController extends AbstractController
{
#[Route(name: 'app_user_index', methods: ['GET'])]
// Route pour afficher tous les utilisateurs
#[Route('/user', name: 'app_user_index', methods: ['GET'])]
public function index(UserRepository $userRepository): Response
{
return $this->render('user/index.html.twig', [
@ -23,7 +23,8 @@ final class UserController extends AbstractController
]);
}
#[Route('/new', name: 'app_user_new', methods: ['GET', 'POST'])]
// Route pour créer un nouvel utilisateur
#[Route('/user/new', name: 'app_user_new', methods: ['GET', 'POST'])]
public function new(Request $request, EntityManagerInterface $entityManager, UserPasswordHasherInterface $passwordHasher): Response
{
$user = new Utilisateur();
@ -48,7 +49,8 @@ final class UserController extends AbstractController
]);
}
#[Route('/{id}', name: 'app_user_show', methods: ['GET'])]
// Route pour afficher un utilisateur spécifique
#[Route('/user/{id}', name: 'app_user_show', methods: ['GET'])]
public function show(Utilisateur $user): Response
{
return $this->render('user/show.html.twig', [
@ -56,9 +58,21 @@ final class UserController extends AbstractController
]);
}
#[Route('/{id}/edit', name: 'app_user_edit', methods: ['GET', 'POST'])]
// Route pour modifier un utilisateur spécifique
#[Route('/user/{id}/edit', name: 'app_user_edit', methods: ['GET', 'POST'])]
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->handleRequest($request);
@ -74,10 +88,24 @@ final class UserController extends AbstractController
]);
}
#[Route('/{id}', name: 'app_user_delete', methods: ['POST'])]
// Route pour supprimer un utilisateur spécifique
#[Route('/user/{id}', name: 'app_user_delete', methods: ['POST'])]
public function delete(Request $request, Utilisateur $user, EntityManagerInterface $entityManager): Response
{
if ($this->isCsrfTokenValid('delete'.$user->getId(), $request->getPayload()->getString('_token'))) {
// Si l'utilisateur est un secrétaire et qu'il essaie de supprimer un autre secrétaire
if ($this->isGranted('ROLE_SECRETAIRE') && $user->hasRole('ROLE_SECRETAIRE')) {
throw $this->createAccessDeniedException('Vous ne pouvez pas supprimer un autre secrétaire ou un administrateur.');
}
// Si l'utilisateur est un chauffagiste et qu'il essaie de supprimer un admin
if ($this->isGranted('ROLE_CHAUFFAGISTE') && ($user->hasRole('ROLE_SECRETAIRE') || $user->hasRole('ROLE_CHAUFFAGISTE'))) {
throw $this->createAccessDeniedException('Vous ne pouvez pas supprimer un utilisateur.');
}
// On s'assure que seul un admin peut supprimer un autre admin
$this->denyAccessUnlessGranted("ROLE_ADMIN");
if ($this->isCsrfTokenValid('delete' . $user->getId(), $request->get('csrf_token'))) {
$entityManager->remove($user);
$entityManager->flush();
}

View File

@ -17,8 +17,13 @@ final class VehicleController extends AbstractController
#[Route(name: 'app_vehicle_index', methods: ['GET'])]
public function index(VehicleRepository $vehicleRepository): Response
{
// Admin peut voir tous les véhicules, chauffagiste ne peut voir que ses véhicules
$vehicles = $this->isGranted('ROLE_CHAUFFAGISTE')
? $vehicleRepository->findByUser($this->getUser()) // Filtre les véhicules par utilisateur
: $vehicleRepository->findAll(); // Les admins voient tout
return $this->render('vehicle/index.html.twig', [
'vehicles' => $vehicleRepository->findAll(),
'vehicles' => $vehicles,
]);
}
@ -30,6 +35,11 @@ final class VehicleController extends AbstractController
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
// Si l'utilisateur est un chauffagiste, on associe le véhicule à lui
if ($this->isGranted('ROLE_CHAUFFAGISTE')) {
$vehicle->setUser($this->getUser());
}
$entityManager->persist($vehicle);
$entityManager->flush();
@ -45,6 +55,11 @@ final class VehicleController extends AbstractController
#[Route('/{id}', name: 'app_vehicle_show', methods: ['GET'])]
public function show(Vehicle $vehicle): Response
{
// Si l'utilisateur est un chauffagiste et essaie de voir un véhicule d'un autre chauffagiste, on bloque
if ($this->isGranted('ROLE_CHAUFFAGISTE') && $vehicle->getUser() !== $this->getUser()) {
throw $this->createAccessDeniedException('Vous ne pouvez pas voir ce véhicule.');
}
return $this->render('vehicle/show.html.twig', [
'vehicle' => $vehicle,
]);
@ -53,6 +68,11 @@ final class VehicleController extends AbstractController
#[Route('/{id}/edit', name: 'app_vehicle_edit', methods: ['GET', 'POST'])]
public function edit(Request $request, Vehicle $vehicle, EntityManagerInterface $entityManager): Response
{
// Vérifier si un chauffagiste essaie de modifier un véhicule d'un autre chauffagiste
if ($this->isGranted('ROLE_CHAUFFAGISTE') && $vehicle->getUser() !== $this->getUser()) {
throw $this->createAccessDeniedException('Vous ne pouvez pas modifier ce véhicule.');
}
$form = $this->createForm(VehicleType::class, $vehicle);
$form->handleRequest($request);
@ -71,7 +91,12 @@ final class VehicleController extends AbstractController
#[Route('/{id}', name: 'app_vehicle_delete', methods: ['POST'])]
public function delete(Request $request, Vehicle $vehicle, EntityManagerInterface $entityManager): Response
{
if ($this->isCsrfTokenValid('delete'.$vehicle->getId(), $request->getPayload()->getString('_token'))) {
// Vérification de sécurité : un chauffagiste ne peut supprimer un véhicule d'un autre chauffagiste
if ($this->isGranted('ROLE_CHAUFFAGISTE') && $vehicle->getUser() !== $this->getUser()) {
throw $this->createAccessDeniedException('Vous ne pouvez pas supprimer ce véhicule.');
}
if ($this->isCsrfTokenValid('delete'.$vehicle->getId(), $request->get('csrf_token'))) {
$entityManager->remove($vehicle);
$entityManager->flush();
}

View File

@ -32,7 +32,6 @@ class Fault
public function setWording(string $Wording): static
{
$this->Wording = $Wording;
return $this;
}
@ -44,7 +43,6 @@ class Fault
public function setDescription(string $Description): static
{
$this->Description = $Description;
return $this;
}
}

View File

@ -5,6 +5,8 @@ namespace App\Entity;
use App\Repository\InterventionRepository;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
#[ORM\Entity(repositoryClass: InterventionRepository::class)]
class Intervention
@ -29,6 +31,32 @@ class Intervention
#[ORM\Column(length: 255)]
private ?string $Status = null;
<<<<<<< HEAD
// #[ORM\ManyToOne(targetEntity: Vehicule::class)]
// private ?Vehicule $vehicule = null;
=======
#[ORM\ManyToOne(inversedBy: 'interventions')]
#[ORM\JoinColumn(nullable: false)]
private ?Utilisateur $user = null;
#[ORM\ManyToOne]
#[ORM\JoinColumn(nullable: false)]
private ?Fault $fault = null;
#[ORM\ManyToOne(inversedBy: 'intervention')]
private ?Vehicle $vehicle = null;
#[ORM\ManyToMany(targetEntity: Stock::class)]
#[ORM\JoinTable(name: "intervention_stock")]
private Collection $stocks;
public function __construct()
{
$this->stocks = new ArrayCollection();
}
>>>>>>> 4fc91211f0d814453d2ed97caf6a1d94d709058e
public function getId(): ?int
{
return $this->id;
@ -42,7 +70,6 @@ class Intervention
public function setWording(string $Wording): static
{
$this->Wording = $Wording;
return $this;
}
@ -54,7 +81,6 @@ class Intervention
public function setTimestamp(\DateTimeInterface $Timestamp): static
{
$this->Timestamp = $Timestamp;
return $this;
}
@ -66,7 +92,6 @@ class Intervention
public function setDescription(string $Description): static
{
$this->Description = $Description;
return $this;
}
@ -78,7 +103,6 @@ class Intervention
public function setAddress(string $Address): static
{
$this->Address = $Address;
return $this;
}
@ -90,7 +114,58 @@ class Intervention
public function setStatus(string $Status): static
{
$this->Status = $Status;
return $this;
}
public function getUser(): ?Utilisateur
{
return $this->user;
}
public function setUser(?Utilisateur $user): static
{
$this->user = $user;
return $this;
}
public function getFault(): ?Fault
{
return $this->fault;
}
public function setFault(?Fault $fault): static
{
$this->fault = $fault;
return $this;
}
public function getVehicle(): ?Vehicle
{
return $this->vehicle;
}
public function setVehicle(?Vehicle $vehicle): static
{
$this->vehicle = $vehicle;
return $this;
}
public function getStocks(): Collection
{
return $this->stocks;
}
public function addStock(Stock $stock): static
{
if (!$this->stocks->contains($stock)) {
$this->stocks[] = $stock;
}
return $this;
}
public function removeStock(Stock $stock): static
{
$this->stocks->removeElement($stock);
return $this;
}
}

View File

@ -4,6 +4,8 @@ namespace App\Entity;
use App\Repository\SkillRepository;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
#[ORM\Entity(repositoryClass: SkillRepository::class)]
class Skill
@ -19,6 +21,14 @@ class Skill
#[ORM\Column(length: 255)]
private ?string $Description = null;
#[ORM\ManyToMany(mappedBy: 'skills', targetEntity: Utilisateur::class)]
private Collection $users;
public function __construct()
{
$this->users = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
@ -32,7 +42,6 @@ class Skill
public function setWording(string $Wording): static
{
$this->Wording = $Wording;
return $this;
}
@ -44,7 +53,28 @@ class Skill
public function setDescription(string $Description): static
{
$this->Description = $Description;
return $this;
}
public function getUsers(): Collection
{
return $this->users;
}
public function addUser(Utilisateur $user): static
{
if (!$this->users->contains($user)) {
$this->users[] = $user;
$user->addSkill($this);
}
return $this;
}
public function removeUser(Utilisateur $user): static
{
if ($this->users->removeElement($user)) {
$user->removeSkill($this);
}
return $this;
}
}

View File

@ -4,6 +4,8 @@ namespace App\Entity;
use App\Repository\StockRepository;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
#[ORM\Entity(repositoryClass: StockRepository::class)]
class Stock
@ -22,6 +24,14 @@ class Stock
#[ORM\Column(length: 255)]
private ?string $Quantity = null;
#[ORM\ManyToMany(mappedBy: 'stocks', targetEntity: Intervention::class)]
private Collection $interventions;
public function __construct()
{
$this->interventions = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
@ -35,7 +45,6 @@ class Stock
public function setWording(string $Wording): static
{
$this->Wording = $Wording;
return $this;
}
@ -47,7 +56,6 @@ class Stock
public function setDescription(string $Description): static
{
$this->Description = $Description;
return $this;
}
@ -59,7 +67,28 @@ class Stock
public function setQuantity(string $Quantity): static
{
$this->Quantity = $Quantity;
return $this;
}
public function getInterventions(): Collection
{
return $this->interventions;
}
public function addIntervention(Intervention $intervention): static
{
if (!$this->interventions->contains($intervention)) {
$this->interventions[] = $intervention;
$intervention->addStock($this);
}
return $this;
}
public function removeIntervention(Intervention $intervention): static
{
if ($this->interventions->removeElement($intervention)) {
$intervention->removeStock($this);
}
return $this;
}
}

View File

@ -5,6 +5,8 @@ namespace App\Entity;
use App\Repository\UserRepository;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
use Symfony\Component\Security\Core\User\UserInterface;
@ -32,8 +34,118 @@ class Utilisateur implements UserInterface, PasswordAuthenticatedUserInterface
#[ORM\Column(length: 255)]
private ?string $Phone = null;
#[ORM\Column]
private array $roles = [];
#[ORM\Column]
private ?string $password = null;
private ?string $plainPassword = null;
#[ORM\OneToMany(mappedBy: 'user', targetEntity: Intervention::class)]
private Collection $interventions;
#[ORM\ManyToMany(targetEntity: Skill::class)]
#[ORM\JoinTable(name: "user_skill")]
private Collection $skills;
public function __construct()
{
$this->interventions = new ArrayCollection();
$this->skills = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getEmail(): ?string
{
return $this->email;
}
public function setEmail(string $email): static
{
$this->email = $email;
return $this;
}
public function getFirstName(): ?string
{
return $this->FirstName;
}
public function setFirstName(?string $FirstName): void
{
$this->FirstName = $FirstName;
}
public function getLastName(): ?string
{
return $this->LastName;
}
public function setLastName(?string $LastName): void
{
$this->LastName = $LastName;
}
public function getBirthDate(): ?\DateTimeInterface
{
return $this->BirthDate;
}
public function setBirthDate(?\DateTimeInterface $BirthDate): void
{
$this->BirthDate = $BirthDate;
}
public function getPhone(): ?string
{
return $this->Phone;
}
public function setPhone(?string $Phone): void
{
$this->Phone = $Phone;
}
public function getRoles(): array
{
$roles = $this->roles;
$roles[] = 'ROLE_USER';
return array_unique($roles);
}
public function setRoles(array $roles): static
{
$this->roles = $roles;
return $this;
}
public function getPassword(): ?string
{
return $this->password;
}
public function setPassword(string $password): static
{
$this->password = $password;
return $this;
}
public function getUserIdentifier(): string
{
return (string) $this->email;
}
public function eraseCredentials(): void
{
$this->plainPassword = null;
}
public function getPlainPassword(): ?string
{
return $this->plainPassword;
@ -44,154 +156,46 @@ class Utilisateur implements UserInterface, PasswordAuthenticatedUserInterface
$this->plainPassword = $plainPassword;
}
/**
* @var list<string> The user roles
*/
#[ORM\Column]
private array $roles = [];
/**
* @var string The hashed password
*/
#[ORM\Column]
private ?string $password = null;
public function getId(): ?int
public function getInterventions(): Collection
{
return $this->id;
return $this->interventions;
}
/**
* @return \DateTimeInterface|null
*/
public function getBirthDate(): ?\DateTimeInterface
public function addIntervention(Intervention $intervention): static
{
return $this->BirthDate;
}
/**
* @param \DateTimeInterface|null $BirthDate
*/
public function setBirthDate(?\DateTimeInterface $BirthDate): void
{
$this->BirthDate = $BirthDate;
}
/**
* @return string|null
*/
public function getLastName(): ?string
{
return $this->LastName;
}
/**
* @param string|null $LastName
*/
public function setLastName(?string $LastName): void
{
$this->LastName = $LastName;
}
/**
* @return string|null
*/
public function getFirstName(): ?string
{
return $this->FirstName;
}
/**
* @param string|null $FirstName
*/
public function setFirstName(?string $FirstName): void
{
$this->FirstName = $FirstName;
}
/**
* @return string|null
*/
public function getPhone(): ?string
{
return $this->Phone;
}
/**
* @param string|null $Phone
*/
public function setPhone(?string $Phone): void
{
$this->Phone = $Phone;
}
public function getEmail(): ?string
{
return $this->email;
}
public function setEmail(string $email): static
{
$this->email = $email;
if (!$this->interventions->contains($intervention)) {
$this->interventions[] = $intervention;
$intervention->setUser($this);
}
return $this;
}
/**
* A visual identifier that represents this user.
*
* @see UserInterface
*/
public function getUserIdentifier(): string
public function removeIntervention(Intervention $intervention): static
{
return (string) $this->email;
}
/**
* @see UserInterface
*
* @return list<string>
*/
public function getRoles(): array
{
$roles = $this->roles;
// guarantee every user at least has ROLE_USER
$roles[] = 'ROLE_USER';
return array_unique($roles);
}
/**
* @param list<string> $roles
*/
public function setRoles(array $roles): static
{
$this->roles = $roles;
if ($this->interventions->removeElement($intervention)) {
if ($intervention->getUser() === $this) {
$intervention->setUser(null);
}
}
return $this;
}
/**
* @see PasswordAuthenticatedUserInterface
*/
public function getPassword(): ?string
public function getSkills(): Collection
{
return $this->password;
return $this->skills;
}
public function setPassword(string $password): static
public function addSkill(Skill $skill): static
{
$this->password = $password;
if (!$this->skills->contains($skill)) {
$this->skills[] = $skill;
}
return $this;
}
/**
* @see UserInterface
*/
public function eraseCredentials(): void
public function removeSkill(Skill $skill): static
{
// If you store any temporary, sensitive data on the user, clear it here
$this->plainPassword = null;
$this->skills->removeElement($skill);
return $this;
}
}

View File

@ -4,6 +4,8 @@ namespace App\Entity;
use App\Repository\VehicleRepository;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
#[ORM\Entity(repositoryClass: VehicleRepository::class)]
class Vehicle
@ -22,6 +24,14 @@ class Vehicle
#[ORM\Column(length: 255)]
private ?string $Model = null;
#[ORM\OneToMany(mappedBy: 'vehicle', targetEntity: Intervention::class)]
private Collection $intervention;
public function __construct()
{
$this->intervention = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
@ -35,7 +45,6 @@ class Vehicle
public function setLicensePlate(string $LicensePlate): static
{
$this->LicensePlate = $LicensePlate;
return $this;
}
@ -47,7 +56,6 @@ class Vehicle
public function setBrand(string $Brand): static
{
$this->Brand = $Brand;
return $this;
}
@ -60,4 +68,28 @@ class Vehicle
{
$this->Model = $Model;
}
public function getIntervention(): Collection
{
return $this->intervention;
}
public function addIntervention(Intervention $intervention): static
{
if (!$this->intervention->contains($intervention)) {
$this->intervention[] = $intervention;
$intervention->setVehicle($this);
}
return $this;
}
public function removeIntervention(Intervention $intervention): static
{
if ($this->intervention->removeElement($intervention)) {
if ($intervention->getVehicle() === $this) {
$intervention->setVehicle(null);
}
}
return $this;
}
}

View File

@ -3,6 +3,7 @@
namespace App\Form;
use App\Entity\Fault;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
@ -14,6 +15,7 @@ class FaultType extends AbstractType
{
$builder
->add('Wording', TextType::class)
->add('Description', TextType::class)
;
}

View File

@ -2,9 +2,14 @@
namespace App\Form;
use App\Entity\Fault;
use App\Entity\Intervention;
use App\Entity\Stock;
use App\Entity\Utilisateur;
use App\Entity\Vehicle;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\DateType;
use Symfony\Component\Form\Extension\Core\Type\DateTimeType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
@ -15,13 +20,40 @@ class InterventionType extends AbstractType
{
$builder
->add('Wording', TextType::class)
->add('Timestamp', DateType::class, [
->add('Timestamp', DateTimeType::class, [
'widget' => 'single_text',
])
->add('Description', TextType::class)
->add('Address', TextType::class)
->add('Status', TextType::class)
->add('user', EntityType::class, [
'class' => Utilisateur::class,
'choice_label' => 'FirstName', // ou autre (LastName, email)
])
->add('fault', EntityType::class, [
'class' => Fault::class,
'choice_label' => 'Wording',
])
->add('vehicle', EntityType::class, [
'class' => Vehicle::class,
'choice_label' => 'LicensePlate',
'required' => false,
])
->add('stocks', EntityType::class, [
'class' => Stock::class,
'choice_label' => 'Wording',
'multiple' => 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

View File

@ -13,7 +13,7 @@ class SkillType extends AbstractType
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('Wording',TextType::class)
->add('Wording', TextType::class)
->add('Description', TextType::class)
;
}

View File

@ -2,7 +2,9 @@
namespace App\Form;
use App\Entity\Skill;
use App\Entity\Utilisateur;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\DateType;
@ -27,14 +29,24 @@ class UserType extends AbstractType
'choices' => [
'Secretaire' => 'ROLE_SECRETAIRE',
'Chauffagiste' => 'ROLE_CHAUFFAGISTE',
'Admin' => 'ROLE_ADMIN', // Ajouté si besoin de créer des admins
],
'multiple' => true,
'expanded' => true, // pour avoir des cases à cocher
])
->add('skills', EntityType::class, [
'class' => Skill::class,
'choice_label' => 'Wording',
'multiple' => true,
'expanded' => true,
'required' => false,
])
->add('plainPassword', PasswordType::class, [
'mapped' => false,
'required' => true,
'label' => 'Mot de passe',
]);
])
;
}
public function configureOptions(OptionsResolver $resolver): void

View File

@ -3,45 +3,119 @@
<head>
<meta charset="UTF-8">
<title>{% block title %}Mon App Symfony{% endblock %}</title>
<style>
body {
font-family: sans-serif;
margin: 0;
padding: 0;
background-color: #f9f9f9;
}
header {
background-color: #222;
color: white;
padding: 1rem;
}
.user-info {
display: flex;
justify-content: space-between;
align-items: center;
}
nav {
background-color: #eee;
padding: 1rem;
}
nav ul {
list-style: none;
padding: 0;
display: flex;
flex-wrap: wrap;
gap: 1rem;
margin: 0;
}
nav li a {
text-decoration: none;
color: #0077cc;
font-weight: bold;
}
nav li a:hover {
text-decoration: underline;
}
.content {
padding: 2rem;
}
.role-badge {
background: #ddd;
padding: 0.2rem 0.6rem;
border-radius: 4px;
margin-right: 0.3rem;
font-size: 0.85em;
}
.logout-btn {
background: transparent;
color: white;
border: 1px solid white;
padding: 0.3rem 0.8rem;
text-decoration: none;
border-radius: 4px;
}
.logout-btn:hover {
background-color: white;
color: #222;
}
</style>
{% block stylesheets %}{% endblock %}
<!-- FullCalendar pour faire un calendrier dynamique -->
<link href="https://cdn.jsdelivr.net/npm/fullcalendar@5.10.1/main.min.css" rel="stylesheet" />
<script src="https://cdn.jsdelivr.net/npm/fullcalendar@5.10.1/main.min.js"></script>
</head>
<body>
{% if app.user %}
<div class="user-info">
Connecté en tant que {{ app.user.email }} |
Rôles :
{% for role in app.user.roles %}
{{ role }}{% if not loop.last %}, {% endif %}
{% endfor %}
</div>
<header>
<div class="user-info">
<div>
<strong>Connecté en tant que</strong> {{ app.user.email }}<br>
</div>
<a href="{{ path('app_logout') }}" class="logout-btn">Déconnexion</a>
</div>
</header>
<nav>
<ul>
{% if is_granted('ROLE_ADMIN') %}
<li><a href="{{ path('admin_dashboard') }}">Dashboard Admin</a></li>
<li><a href="{{ path('app_user_index') }}">Gérer un utilisateur</a></li>
<li><a href="{{ path('app_calendrier_index') }}">Tous les plannings</a></li>
<li><a href="{{ path('app_stock_index') }}">Gérer les stocks</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_fault_index') }}">Test des fault !</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_calendrier_index') }}">Tous les plannings</a></li>
{% endif %}
{% if is_granted('ROLE_SECRETAIRE') %}
<li><a href="{{ path('secretaire_dashboard') }}">Dashboard Secrétaire</a></li>
<li><a href="{{ path('user_new') }}">Créer un chauffagiste</a></li>
<li><a href="{{ path('calendrier_index_secretaire') }}">Plannings chauffagistes</a></li>
<li><a href="{{ path('stock_index') }}">Gérer les stocks</a></li>
<li><a href="{{ path('vehicle_index') }}">Gérer les véhicules</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_stock_index') }}">Gérer les stocks</a></li>
<li><a href="{{ path('app_calendrier_indexSecretaire') }}">Plannings chauffagistes</a></li>
{% endif %}
{% if is_granted('ROLE_CHAUFFAGISTE') %}
<li><a href="{{ path('chauffagiste_dashboard') }}">Dashboard Chauffagiste</a></li>
<li><a href="{{ path('calendrier_index_chauffagiste') }}">Mon planning</a></li>
<li><a href="{{ path('intervention_index') }}">Mes interventions</a></li>
<li><a href="{{ path('stock_index') }}">Pièces détachées</a></li>
<li><a href="{{ path('app_intervention_index') }}">Mes interventions</a></li>
<li><a href="{{ path('app_stock_index') }}">Pièces détachées</a></li>
<li><a href="{{ path('app_calendrier_indexChauffagiste') }}">Mon planning</a></li>
{% endif %}
<li><a href="{{ path('app_logout') }}">Déconnexion</a></li>
</ul>
</nav>
{% endif %}

View File

@ -1,57 +1,23 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}Authentification{% endblock %}</title>
<link href="{{ asset('styles/css/calendrier.css') }}" rel="stylesheet">
{% block stylesheets %}
{% endblock %}
</head>
{% extends 'base.html.twig' %}
<body>
<div class="sidebar">
<div class="sidebar-button">
<img class="logoEmail" src="{{ asset('styles/image/6.png') }}">
</div>
<div class="sidebar-button">
<img class="logoEmail" src="{{ asset('styles/image/3.png') }}">
</div>
<a href="{{ path('app_intervention_new') }}">
<button>Create Intervention</button>
</a>
<div class="sidebar-button">
<img class="logoEmail" src="{{ asset('styles/image/4.png') }}">
</div>
<span>Choose heating engineer</span>
<div class="sidebar-button">
<img class="logoEmail" src="{{ asset('styles/image/5.png') }}">
</div>
<span>Modify Intervention</span>
<div class="sidebar-button">
<img class="logoEmail" src="{{ asset('styles/image/7.png') }}">
</div>
<span>Surname Name</span>
{% block title %}Calendrier des interventions{% endblock %}
<div class="sidebar-button">
<img class="logoEmail" src="{{ asset('styles/image/LogoHegreConfort_SansFond.png') }}">
</div>
{% block body %}
<h1>📅 Calendrier des interventions</h1>
<div id="calendar"></div>
</div>
<div class="main-content">
<div class="calendar-header" style="gap: 50px">
<div class="day-header">Mo</div>
<div class="day-header">Tu</div>
<div class="day-header">We</div>
<div class="day-header">Th</div>
<div class="day-header">Fr</div>
<div class="day-header">Sa</div>
<div class="day-header">Su</div>
</div>
<div class="calendar-title">Calendar</div>
</div>
</body>
</html>
<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 %}

View File

@ -0,0 +1,23 @@
{% extends 'base.html.twig' %}
{% block title %}Mon calendrier d'interventions{% endblock %}
{% block body %}
<h1>📅 Mon calendrier d'interventions</h1>
<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 %}

View File

@ -1,20 +1,23 @@
{% extends 'base.html.twig' %}
{% block title %}Hello CalendrierController!{% endblock %}
{% block title %}Calendrier des interventions{% 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>
<h1>📅 Calendrier des interventions</h1>
<div class="example-wrapper">
<h1>Hello {{ controller_name }}! ✅</h1>
<div id="calendar"></div>
This friendly message is coming from:
<ul>
<li>Your controller at <code>/home/allarda@stsio.lan/Test/HegreEtConfort/src/Controller/CalendrierController.php</code></li>
<li>Your template at <code>/home/allarda@stsio.lan/Test/HegreEtConfort/templates/calendrier/index.html.twig</code></li>
</ul>
</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 %}

View File

@ -1,7 +1,15 @@
{% extends 'base.html.twig' %}
{% block body %}
<h1>Bienvenue Admin</h1>
<p>Tu as tous les droits ici !</p>
{% endblock %}
{% block title %}Dashboard Administrateur{% endblock %}
{% block body %}
<h1 class="mb-4">👑 Bienvenue {{ app.user.FirstName }} sur le Dashboard Administrateur</h1>
<p>Vous êtes connecté avec les rôles :
{% for role in app.user.roles %}
<span class="badge bg-primary">{{ role }}</span>
{% endfor %}
</p>
<p class="text-muted">Utilisez le menu en haut pour accéder aux fonctionnalités.</p>
{% endblock %}

View File

@ -1,7 +0,0 @@
{% extends 'base.html.twig' %}
{% block body %}
<h1>Bienvenue chauffagiste</h1>
<p>Vas travailler</p>
{% endblock %}

View File

@ -0,0 +1,15 @@
{% extends 'base.html.twig' %}
{% block title %}Dashboard Chauffagiste{% endblock %}
{% block body %}
<h1 class="mb-4">🔧 Bonjour {{ app.user.FirstName }}, bienvenue sur ton espace</h1>
<p>Rôle :
{% for role in app.user.roles %}
<span class="badge bg-success">{{ role }}</span>
{% endfor %}
</p>
<p class="text-muted">Retrouvez votre planning, vos interventions et vos outils dans le menu ci-dessus.</p>
{% endblock %}

View File

@ -1,7 +1,15 @@
{% extends 'base.html.twig' %}
{% block body %}
<h1>Bienvenue Secretaire</h1>
<p>Fais ce que tu veux</p>
{% endblock %}
{% block title %}Dashboard Secrétaire{% endblock %}
{% block body %}
<h1 class="mb-4">🗂️ Bienvenue {{ app.user.FirstName }} sur le Dashboard Secrétaire</h1>
<p>Vous êtes connectée avec les rôles :
{% for role in app.user.roles %}
<span class="badge bg-secondary">{{ role }}</span>
{% endfor %}
</p>
<p class="text-muted">Utilisez le menu pour créer des interventions, gérer les chauffagistes, etc.</p>
{% endblock %}

View File

@ -1,13 +1,14 @@
{% extends 'base.html.twig' %}
{% block title %}Edit Fault{% endblock %}
{% block title %}Modifier la panne{% endblock %}
{% block body %}
<h1>Edit Fault</h1>
<h1 class="mb-4">✏️ Modifier une panne</h1>
{{ include('fault/_form.html.twig', {'button_label': 'Update'}) }}
{{ include('fault/_form.html.twig', {'button_label': 'Mettre à jour'}) }}
<a href="{{ path('app_fault_index') }}">back to list</a>
{{ include('fault/_delete_form.html.twig') }}
<div class="mt-3">
<a href="{{ path('app_fault_index') }}" class="btn btn-secondary">← Retour à la liste</a>
{{ include('fault/_delete_form.html.twig') }}
</div>
{% endblock %}

View File

@ -1,36 +1,37 @@
{% extends 'base.html.twig' %}
{% block title %}Fault index{% endblock %}
{% block title %}Liste des pannes{% endblock %}
{% block body %}
<h1>Fault index</h1>
<h1 class="mb-4">📋 Liste des pannes enregistrées</h1>
<table class="table">
<thead>
<tr>
<th>Id</th>
<th>Wording</th>
<th>actions</th>
</tr>
<table class="table table-striped table-bordered align-middle">
<thead class="table-dark">
<tr>
<th>ID</th>
<th>Nom</th>
<th>Description</th>
<th class="text-center">Actions</th>
</tr>
</thead>
<tbody>
{% for fault in faults %}
<tr>
<td>{{ fault.id }}</td>
<td>{{ fault.Wording }}</td>
<td>{{ fault.actions }}</td>
<td>
<a href="{{ path('app_fault_show', {'id': fault.id}) }}">show</a>
<a href="{{ path('app_fault_edit', {'id': fault.id}) }}">edit</a>
<td>{{ fault.Description }}</td>
<td class="text-center">
<a href="{{ path('app_fault_show', {'id': fault.id}) }}" class="btn btn-sm btn-info">👁️ Voir</a>
<a href="{{ path('app_fault_edit', {'id': fault.id}) }}" class="btn btn-sm btn-warning">✏️ Modifier</a>
</td>
</tr>
{% else %}
<tr>
<td colspan="3">no records found</td>
<td colspan="4" class="text-center text-muted">Aucune panne trouvée.</td>
</tr>
{% endfor %}
</tbody>
</table>
<a href="{{ path('app_fault_new') }}">Create new</a>
<a href="{{ path('app_fault_new') }}" class="btn btn-success mt-3"> Ajouter une nouvelle panne</a>
{% endblock %}

View File

@ -1,11 +1,13 @@
{% extends 'base.html.twig' %}
{% block title %}New Fault{% endblock %}
{% block title %}Créer une panne{% endblock %}
{% block body %}
<h1>Create new Fault</h1>
<h1 class="mb-4"> Ajouter une nouvelle panne</h1>
{{ include('fault/_form.html.twig') }}
<a href="{{ path('app_fault_index') }}">back to list</a>
<div class="mt-3">
<a href="{{ path('app_fault_index') }}" class="btn btn-secondary">← Retour à la liste</a>
</div>
{% endblock %}

View File

@ -1,26 +1,30 @@
{% extends 'base.html.twig' %}
{% block title %}Fault{% endblock %}
{% block title %}Détail de la panne{% endblock %}
{% block body %}
<h1>Fault</h1>
<h1 class="mb-4">🔍 Détails de la panne</h1>
<table class="table">
<table class="table table-bordered">
<tbody>
<tr>
<th>Id</th>
<td>{{ fault.id }}</td>
</tr>
<tr>
<th>Wording</th>
<td>{{ fault.Wording }}</td>
</tr>
<tr>
<th>ID</th>
<td>{{ fault.id }}</td>
</tr>
<tr>
<th>Nom</th>
<td>{{ fault.Wording }}</td>
</tr>
<tr>
<th>Description</th>
<td>{{ fault.Description }}</td>
</tr>
</tbody>
</table>
<a href="{{ path('app_fault_index') }}">back to list</a>
<a href="{{ path('app_fault_edit', {'id': fault.id}) }}">edit</a>
{{ include('fault/_delete_form.html.twig') }}
<div class="mt-3">
<a href="{{ path('app_fault_index') }}" class="btn btn-secondary">← Retour à la liste</a>
<a href="{{ path('app_fault_edit', {'id': fault.id}) }}" class="btn btn-warning">✏️ Modifier</a>
{{ include('fault/_delete_form.html.twig') }}
</div>
{% endblock %}

View File

@ -1,4 +1,47 @@
{#{{ form_start(form) }}#}
{# {{ form_widget(form) }}#}
{# <button class="btn">{{ button_label|default('Save') }}</button>#}
{#{{ form_end(form) }}#}
{{ form_start(form) }}
{{ form_widget(form) }}
<button class="btn">{{ button_label|default('Save') }}</button>
<div class="form-group">
{{ form_label(form.Wording, 'Nom de l\'intervention') }}
{{ form_widget(form.Wording, {'attr': {'class': 'form-control'}}) }}
{{ form_errors(form.Wording) }}
</div>
<div class="form-group">
{{ form_label(form.Timestamp, 'Date') }}
{{ form_widget(form.Timestamp, {'attr': {'class': 'form-control'}}) }}
{{ form_errors(form.Timestamp) }}
</div>
<div class="form-group">
{{ form_label(form.Description, 'Description') }}
{{ form_widget(form.Description, {'attr': {'class': 'form-control'}}) }}
{{ form_errors(form.Description) }}
</div>
<div class="form-group">
{{ form_label(form.Address, 'Adresse') }}
{{ form_widget(form.Address, {'attr': {'class': 'form-control'}}) }}
{{ form_errors(form.Address) }}
</div>
<div class="form-group">
{{ form_label(form.Status, 'Statut') }}
{{ form_widget(form.Status, {'attr': {'class': 'form-control'}}) }}
{{ form_errors(form.Status) }}
</div>
{#<div class="form-group">#}
{# {{ form_label(form.vehicule, 'Véhicule associé') }}#}
{# {{ form_widget(form.vehicule, {'attr': {'class': 'form-control'}}) }}#}
{# {{ form_errors(form.vehicule) }}#}
{#</div>#}
<button class="btn btn-primary mt-3">{{ button_label|default('Enregistrer') }}</button>
{{ form_end(form) }}

View File

@ -1,13 +1,14 @@
{% extends 'base.html.twig' %}
{% block title %}Edit Intervention{% endblock %}
{% block title %}Modifier une intervention{% endblock %}
{% block body %}
<h1>Edit Intervention</h1>
<h1 class="mb-4">✏️ Modifier l'intervention</h1>
{{ include('intervention/_form.html.twig', {'button_label': 'Update'}) }}
{{ include('intervention/_form.html.twig', {'button_label': 'Mettre à jour'}) }}
<a href="{{ path('app_intervention_index') }}">back to list</a>
{{ include('intervention/_delete_form.html.twig') }}
<div class="mt-3">
<a href="{{ path('app_intervention_index') }}" class="btn btn-secondary">← Retour à la liste des interventions</a>
{{ include('intervention/_delete_form.html.twig') }}
</div>
{% endblock %}

View File

@ -1,43 +1,53 @@
{% extends 'base.html.twig' %}
{% block title %}Intervention index{% endblock %}
{% block title %}Liste des interventions{% endblock %}
{% block body %}
<h1>Intervention index</h1>
<h1>Liste des interventions</h1>
<table class="table">
<thead>
<tr>
<th>Id</th>
<th>Wording</th>
<th>Timestamp</th>
<th>Description</th>
<th>Address</th>
<th>Status</th>
<th>actions</th>
</tr>
<tr>
<th>Id</th>
<th>Nom</th>
<th>Chauffagiste</th>
<th>Véhicule</th>
<th>Panne</th>
<th>Date & Heure</th>
<th>Statut</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{% for intervention in interventions %}
<tr>
<td>{{ intervention.id }}</td>
<td>{{ intervention.Wording }}</td>
<td>{{ intervention.Timestamp ? intervention.Timestamp|date('Y-m-d H:i:s') : '' }}</td>
<td>{{ intervention.Description }}</td>
<td>{{ intervention.Address }}</td>
<td>
{{ intervention.user ? intervention.user.FirstName ~ ' ' ~ intervention.user.LastName : 'Non assigné' }}
</td>
<td>
{{ intervention.vehicle ? intervention.vehicle.LicensePlate : 'Aucun véhicule' }}
</td>
<td>
{{ intervention.fault ? intervention.fault.Wording : 'Non précisé' }}
</td>
<td>
{{ intervention.Timestamp ? intervention.Timestamp|date('Y-m-d H:i') : '' }}
</td>
<td>{{ intervention.Status }}</td>
<td>
<a href="{{ path('app_intervention_show', {'id': intervention.id}) }}">show</a>
<a href="{{ path('app_intervention_edit', {'id': intervention.id}) }}">edit</a>
<a href="{{ path('app_intervention_show', {'id': intervention.id}) }}" class="btn btn-info btn-sm">Voir</a>
<a href="{{ path('app_intervention_edit', {'id': intervention.id}) }}" class="btn btn-secondary btn-sm">Modifier</a>
</td>
</tr>
{% else %}
<tr>
<td colspan="7">no records found</td>
<td colspan="8">Aucune intervention trouvée</td>
</tr>
{% endfor %}
</tbody>
</table>
<a href="{{ path('app_intervention_new') }}">Create new</a>
<a href="{{ path('app_intervention_new') }}" class="btn btn-success">Créer une nouvelle intervention</a>
{% endblock %}

View File

@ -1,79 +1,72 @@
<!DOCTYPE html>
<html lang="fr, en">
<head>
<meta charset="UTF-8">
<title>Créer une intervention</title>
<link href="{{ asset('styles/css/intervention.css') }}" rel='stylesheet'>
<link rel="icon"
href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 128 128%22><text y=%221.2em%22 font-size=%2296%22>⚫️</text><text y=%221.3em%22 x=%220.2em%22 font-size=%2276%22 fill=%22%23fff%22>sf</text></svg>">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
{% extends 'base.html.twig' %}
<meta name="viewport" content="width=device-width, initial-scale=1">
{% block title %}Nouvelle intervention{% endblock %}
</head>
<body>
<div class="background"></div>
<img class="logoCalendar" src="{{ asset('styles/image/calendar.png') }}" alt="Logo Calendrier">
<a href="{{ path('app_intervention_index') }}" class="intervention">Voir les interventions</a>
<img class="logoChauffagiste" src="{{ asset('styles/image/chauffagiste.png') }}" alt="Logo Chauffagiste">
<h1 class="chauffagiste">Choisissez un chauffagiste</h1>
<img class="logoModifier" src="{{ asset('styles/image/modifier.png') }}">
<h1 class="modifier">Modifier l'intervention</h1>
<img class="logoHegre" src="{{ asset('styles/image/LogoHegreConfort_SansFond.png') }}">
{% block body %}
<h1 class="mb-4"> Créer une nouvelle intervention</h1>
{{ include('intervention/_form.html.twig') }}
<<<<<<< HEAD
<div class="background-intervention">
<input required id="nom" placeholder="Nom de l'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="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>
{# <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>
{# <!-- <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>
<input type="checkbox" name="vehicule1" id="vehicule1">
<label for=”vehicule1” id="oui"> Oui </label>
<input type="checkbox" name="vehicule2" id="vehicule2">
<label for=”vehicule2” id="non"> Non </label>
{# <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">
{# <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">
{# <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>
{# <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') }}
<a href="{{ path('app_intervention_index') }}" id="liste">Retour à la liste</a>
</div>
</body>
</html>
=======
<div class="mt-3">
<a href="{{ path('app_intervention_index') }}" class="btn btn-secondary">← Retour à la liste des interventions</a>
</div>
{% endblock %}
>>>>>>> 4fc91211f0d814453d2ed97caf6a1d94d709058e

View File

@ -3,40 +3,71 @@
{% block title %}Intervention{% endblock %}
{% block body %}
<h1>Intervention</h1>
<h1>Détails de l'Intervention</h1>
<table class="table">
<tbody>
<tr>
<th>Id</th>
<td>{{ intervention.id }}</td>
</tr>
<tr>
<th>Wording</th>
<td>{{ intervention.Wording }}</td>
</tr>
<tr>
<th>Timestamp</th>
<td>{{ intervention.Timestamp ? intervention.Timestamp|date('Y-m-d H:i:s') : '' }}</td>
</tr>
<tr>
<th>Description</th>
<td>{{ intervention.Description }}</td>
</tr>
<tr>
<th>Address</th>
<td>{{ intervention.Address }}</td>
</tr>
<tr>
<th>Status</th>
<td>{{ intervention.Status }}</td>
</tr>
<tr>
<th>Id</th>
<td>{{ intervention.id }}</td>
</tr>
<tr>
<th>Nom de l'intervention</th>
<td>{{ intervention.Wording }}</td>
</tr>
<tr>
<th>Date & Heure</th>
<td>{{ intervention.Timestamp ? intervention.Timestamp|date('Y-m-d H:i') : '' }}</td>
</tr>
<tr>
<th>Description</th>
<td>{{ intervention.Description }}</td>
</tr>
<tr>
<th>Adresse</th>
<td>{{ intervention.Address }}</td>
</tr>
<tr>
<th>Statut</th>
<td>{{ intervention.Status }}</td>
</tr>
<tr>
<th>Chauffagiste</th>
<td>
{{ intervention.user ? intervention.user.FirstName ~ ' ' ~ intervention.user.LastName : 'Non assigné' }}
</td>
</tr>
<tr>
<th>Panne</th>
<td>
{{ intervention.fault ? intervention.fault.Wording : 'Non précisé' }}
</td>
</tr>
<tr>
<th>Véhicule</th>
<td>
{{ intervention.vehicle ? intervention.vehicle.LicensePlate : 'Aucun véhicule associé' }}
</td>
</tr>
<tr>
<th>Pièces détachées utilisées</th>
<td>
{% if intervention.stocks|length > 0 %}
<ul>
{% for stock in intervention.stocks %}
<li>{{ stock.Wording }} (Quantité: {{ stock.Quantity }})</li>
{% endfor %}
</ul>
{% else %}
Aucune pièce utilisée
{% endif %}
</td>
</tr>
</tbody>
</table>
<a href="{{ path('app_intervention_index') }}">back to list</a>
<a href="{{ path('app_intervention_edit', {'id': intervention.id}) }}">edit</a>
<a href="{{ path('app_intervention_index') }}" class="btn btn-primary">Retour à la liste</a>
<a href="{{ path('app_intervention_edit', {'id': intervention.id}) }}" class="btn btn-warning">Modifier</a>
{{ include('intervention/_delete_form.html.twig') }}
{% endblock %}

View File

@ -1,13 +1,14 @@
{% extends 'base.html.twig' %}
{% block title %}Edit Skill{% endblock %}
{% block title %}Modifier une compétence{% endblock %}
{% block body %}
<h1>Edit Skill</h1>
<h1 class="mb-4">✏️ Modifier une compétence</h1>
{{ include('skill/_form.html.twig', {'button_label': 'Update'}) }}
{{ include('skill/_form.html.twig', {'button_label': 'Mettre à jour'}) }}
<a href="{{ path('app_skill_index') }}">back to list</a>
{{ include('skill/_delete_form.html.twig') }}
<div class="mt-3">
<a href="{{ path('app_skill_index') }}" class="btn btn-secondary">← Retour à la liste</a>
{{ include('skill/_delete_form.html.twig') }}
</div>
{% endblock %}

View File

@ -1,18 +1,18 @@
{% extends 'base.html.twig' %}
{% block title %}Skill index{% endblock %}
{% block title %}Compétences disponibles{% endblock %}
{% block body %}
<h1>Skill index</h1>
<h1 class="mb-4">📘 Liste des compétences</h1>
<table class="table">
<thead>
<tr>
<th>Id</th>
<th>Wording</th>
<th>Description</th>
<th>actions</th>
</tr>
<table class="table table-striped table-bordered align-middle">
<thead class="table-dark">
<tr>
<th>ID</th>
<th>Nom</th>
<th>Description</th>
<th class="text-center">Actions</th>
</tr>
</thead>
<tbody>
{% for skill in skills %}
@ -20,18 +20,18 @@
<td>{{ skill.id }}</td>
<td>{{ skill.Wording }}</td>
<td>{{ skill.Description }}</td>
<td>
<a href="{{ path('app_skill_show', {'id': skill.id}) }}">show</a>
<a href="{{ path('app_skill_edit', {'id': skill.id}) }}">edit</a>
<td class="text-center">
<a href="{{ path('app_skill_show', {'id': skill.id}) }}" class="btn btn-info btn-sm">👁️ Voir</a>
<a href="{{ path('app_skill_edit', {'id': skill.id}) }}" class="btn btn-warning btn-sm">✏️ Modifier</a>
</td>
</tr>
{% else %}
<tr>
<td colspan="4">no records found</td>
<td colspan="4" class="text-center text-muted">Aucune compétence enregistrée.</td>
</tr>
{% endfor %}
</tbody>
</table>
<a href="{{ path('app_skill_new') }}">Create new</a>
<a href="{{ path('app_skill_new') }}" class="btn btn-success mt-3"> Ajouter une compétence</a>
{% endblock %}

View File

@ -1,11 +1,13 @@
{% extends 'base.html.twig' %}
{% block title %}New Skill{% endblock %}
{% block title %}Nouvelle compétence{% endblock %}
{% block body %}
<h1>Create new Skill</h1>
<h1 class="mb-4"> Ajouter une nouvelle compétence</h1>
{{ include('skill/_form.html.twig') }}
<a href="{{ path('app_skill_index') }}">back to list</a>
<div class="mt-3">
<a href="{{ path('app_skill_index') }}" class="btn btn-secondary">← Retour à la liste</a>
</div>
{% endblock %}

View File

@ -1,30 +1,30 @@
{% extends 'base.html.twig' %}
{% block title %}Skill{% endblock %}
{% block title %}Détail de la compétence{% endblock %}
{% block body %}
<h1>Skill</h1>
<h1 class="mb-4">🔍 Détail de la compétence</h1>
<table class="table">
<table class="table table-bordered">
<tbody>
<tr>
<th>Id</th>
<td>{{ skill.id }}</td>
</tr>
<tr>
<th>Wording</th>
<td>{{ skill.Wording }}</td>
</tr>
<tr>
<th>Description</th>
<td>{{ skill.Description }}</td>
</tr>
<tr>
<th>ID</th>
<td>{{ skill.id }}</td>
</tr>
<tr>
<th>Nom</th>
<td>{{ skill.Wording }}</td>
</tr>
<tr>
<th>Description</th>
<td>{{ skill.Description }}</td>
</tr>
</tbody>
</table>
<a href="{{ path('app_skill_index') }}">back to list</a>
<a href="{{ path('app_skill_edit', {'id': skill.id}) }}">edit</a>
{{ include('skill/_delete_form.html.twig') }}
<div class="mt-3">
<a href="{{ path('app_skill_index') }}" class="btn btn-secondary">← Retour à la liste</a>
<a href="{{ path('app_skill_edit', {'id': skill.id}) }}" class="btn btn-warning">✏️ Modifier</a>
{{ include('skill/_delete_form.html.twig') }}
</div>
{% endblock %}

View File

@ -1,13 +1,14 @@
{% extends 'base.html.twig' %}
{% block title %}Edit Stock{% endblock %}
{% block title %}Modifier une pièce{% endblock %}
{% block body %}
<h1>Edit Stock</h1>
<h1 class="mb-4">✏️ Modifier une pièce détachée</h1>
{{ include('stock/_form.html.twig', {'button_label': 'Update'}) }}
{{ include('stock/_form.html.twig', {'button_label': 'Mettre à jour'}) }}
<a href="{{ path('app_stock_index') }}">back to list</a>
{{ include('stock/_delete_form.html.twig') }}
<div class="mt-3">
<a href="{{ path('app_stock_index') }}" class="btn btn-secondary">← Retour à la liste</a>
{{ include('stock/_delete_form.html.twig') }}
</div>
{% endblock %}

View File

@ -1,19 +1,19 @@
{% extends 'base.html.twig' %}
{% block title %}Stock index{% endblock %}
{% block title %}Liste des pièces détachées{% endblock %}
{% block body %}
<h1>Stock index</h1>
<h1 class="mb-4">📦 Pièces détachées disponibles</h1>
<table class="table">
<thead>
<tr>
<th>Id</th>
<th>Wording</th>
<th>Description</th>
<th>Quantity</th>
<th>actions</th>
</tr>
<table class="table table-striped table-bordered align-middle">
<thead class="table-dark">
<tr>
<th>ID</th>
<th>Nom</th>
<th>Description</th>
<th>Quantité</th>
<th class="text-center">Actions</th>
</tr>
</thead>
<tbody>
{% for stock in stocks %}
@ -22,18 +22,18 @@
<td>{{ stock.Wording }}</td>
<td>{{ stock.Description }}</td>
<td>{{ stock.Quantity }}</td>
<td>
<a href="{{ path('app_stock_show', {'id': stock.id}) }}">show</a>
<a href="{{ path('app_stock_edit', {'id': stock.id}) }}">edit</a>
<td class="text-center">
<a href="{{ path('app_stock_show', {'id': stock.id}) }}" class="btn btn-info btn-sm">👁️ Voir</a>
<a href="{{ path('app_stock_edit', {'id': stock.id}) }}" class="btn btn-warning btn-sm">✏️ Modifier</a>
</td>
</tr>
{% else %}
<tr>
<td colspan="5">no records found</td>
<td colspan="5" class="text-center text-muted">Aucune pièce en stock.</td>
</tr>
{% endfor %}
</tbody>
</table>
<a href="{{ path('app_stock_new') }}">Create new</a>
<a href="{{ path('app_stock_new') }}" class="btn btn-success mt-3"> Ajouter une pièce</a>
{% endblock %}

View File

@ -1,11 +1,13 @@
{% extends 'base.html.twig' %}
{% block title %}New Stock{% endblock %}
{% block title %}Nouvelle pièce détachée{% endblock %}
{% block body %}
<h1>Create new Stock</h1>
<h1 class="mb-4"> Ajouter une nouvelle pièce</h1>
{{ include('stock/_form.html.twig') }}
<a href="{{ path('app_stock_index') }}">back to list</a>
<div class="mt-3">
<a href="{{ path('app_stock_index') }}" class="btn btn-secondary">← Retour à la liste</a>
</div>
{% endblock %}

View File

@ -1,34 +1,34 @@
{% extends 'base.html.twig' %}
{% block title %}Stock{% endblock %}
{% block title %}Détail de la pièce{% endblock %}
{% block body %}
<h1>Stock</h1>
<h1 class="mb-4">🔍 Détail d'une pièce détachée</h1>
<table class="table">
<table class="table table-bordered">
<tbody>
<tr>
<th>Id</th>
<td>{{ stock.id }}</td>
</tr>
<tr>
<th>Wording</th>
<td>{{ stock.Wording }}</td>
</tr>
<tr>
<th>Description</th>
<td>{{ stock.Description }}</td>
</tr>
<tr>
<th>Quantity</th>
<td>{{ stock.Quantity }}</td>
</tr>
<tr>
<th>ID</th>
<td>{{ stock.id }}</td>
</tr>
<tr>
<th>Nom</th>
<td>{{ stock.Wording }}</td>
</tr>
<tr>
<th>Description</th>
<td>{{ stock.Description }}</td>
</tr>
<tr>
<th>Quantité</th>
<td>{{ stock.Quantity }}</td>
</tr>
</tbody>
</table>
<a href="{{ path('app_stock_index') }}">back to list</a>
<a href="{{ path('app_stock_edit', {'id': stock.id}) }}">edit</a>
{{ include('stock/_delete_form.html.twig') }}
<div class="mt-3">
<a href="{{ path('app_stock_index') }}" class="btn btn-secondary">← Retour à la liste</a>
<a href="{{ path('app_stock_edit', {'id': stock.id}) }}" class="btn btn-warning">✏️ Modifier</a>
{{ include('stock/_delete_form.html.twig') }}
</div>
{% endblock %}

View File

@ -1,13 +1,14 @@
{% extends 'base.html.twig' %}
{% block title %}Edit User{% endblock %}
{% block title %}Modifier un utilisateur{% endblock %}
{% block body %}
<h1>Edit User</h1>
<h1 class="mb-4">✏️ Modifier un utilisateur</h1>
{{ include('user/_form.html.twig', {'button_label': 'Update'}) }}
{{ include('user/_form.html.twig', {'button_label': 'Mettre à jour'}) }}
<a href="{{ path('app_user_index') }}">back to list</a>
{{ include('user/_delete_form.html.twig') }}
<div class="mt-3">
<a href="{{ path('app_user_index') }}" class="btn btn-secondary">← Retour à la liste des utilisateurs</a>
{{ include('user/_delete_form.html.twig') }}
</div>
{% endblock %}

View File

@ -1,23 +1,23 @@
{% extends 'base.html.twig' %}
{% block title %}User index{% endblock %}
{% block title %}Liste des utilisateurs{% endblock %}
{% block body %}
<h1>User index</h1>
<h1>Liste des utilisateurs</h1>
<table class="table">
<thead>
<tr>
<th>Id</th>
<th>Email</th>
<th>FirstName</th>
<th>LastName</th>
<th>BirthDate</th>
<th>Phone</th>
<th>Roles</th>
<th>Password</th>
<th>actions</th>
</tr>
<tr>
<th>Id</th>
<th>Email</th>
<th>Prénom</th>
<th>Nom</th>
<th>Date de naissance</th>
<th>Téléphone</th>
<th>Rôles</th>
<th>Compétences</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{% for user in users %}
@ -28,20 +28,38 @@
<td>{{ user.LastName }}</td>
<td>{{ user.BirthDate ? user.BirthDate|date('Y-m-d') : '' }}</td>
<td>{{ user.Phone }}</td>
<td>{{ user.roles ? user.roles|json_encode : '' }}</td>
<td>{{ user.password }}</td>
<td>
<a href="{{ path('app_user_show', {'id': user.id}) }}">show</a>
<a href="{{ path('app_user_edit', {'id': user.id}) }}">edit</a>
{% if user.roles %}
<ul>
{% for role in user.roles %}
<li>{{ role }}</li>
{% endfor %}
</ul>
{% endif %}
</td>
<td>
{% if user.skills|length > 0 %}
<ul>
{% for skill in user.skills %}
<li>{{ skill.Wording }}</li>
{% endfor %}
</ul>
{% else %}
Aucune compétence
{% endif %}
</td>
<td>
<a href="{{ path('app_user_show', {'id': user.id}) }}" class="btn btn-info btn-sm">Voir</a>
<a href="{{ path('app_user_edit', {'id': user.id}) }}" class="btn btn-secondary btn-sm">Modifier</a>
</td>
</tr>
{% else %}
<tr>
<td colspan="9">no records found</td>
<td colspan="9">Aucun utilisateur trouvé</td>
</tr>
{% endfor %}
</tbody>
</table>
<a href="{{ path('app_user_new') }}">Create new</a>
<a href="{{ path('app_user_new') }}" class="btn btn-success">Créer un nouvel utilisateur</a>
{% endblock %}

View File

@ -1,11 +1,13 @@
{% extends 'base.html.twig' %}
{% block title %}New User{% endblock %}
{% block title %}Nouveau utilisateur{% endblock %}
{% block body %}
<h1>Create new User</h1>
<h1 class="mb-4"> Créer un nouvel utilisateur</h1>
{{ include('user/_form.html.twig') }}
<a href="{{ path('app_user_index') }}">back to list</a>
<div class="mt-3">
<a href="{{ path('app_user_index') }}" class="btn btn-secondary">← Retour à la liste des utilisateurs</a>
</div>
{% endblock %}

View File

@ -1,50 +1,67 @@
{% extends 'base.html.twig' %}
{% block title %}User{% endblock %}
{% block title %}Utilisateur{% endblock %}
{% block body %}
<h1>User</h1>
<h1>Détails de l'utilisateur</h1>
<table class="table">
<tbody>
<tr>
<th>Id</th>
<td>{{ user.id }}</td>
</tr>
<tr>
<th>Email</th>
<td>{{ user.email }}</td>
</tr>
<tr>
<th>FirstName</th>
<td>{{ user.FirstName }}</td>
</tr>
<tr>
<th>LastName</th>
<td>{{ user.LastName }}</td>
</tr>
<tr>
<th>BirthDate</th>
<td>{{ user.BirthDate ? user.BirthDate|date('Y-m-d') : '' }}</td>
</tr>
<tr>
<th>Phone</th>
<td>{{ user.Phone }}</td>
</tr>
<tr>
<th>Roles</th>
<td>{{ user.roles ? user.roles|json_encode : '' }}</td>
</tr>
<tr>
<th>Password</th>
<td>{{ user.password }}</td>
</tr>
<tr>
<th>Id</th>
<td>{{ user.id }}</td>
</tr>
<tr>
<th>Email</th>
<td>{{ user.email }}</td>
</tr>
<tr>
<th>Prénom</th>
<td>{{ user.FirstName }}</td>
</tr>
<tr>
<th>Nom</th>
<td>{{ user.LastName }}</td>
</tr>
<tr>
<th>Date de naissance</th>
<td>{{ user.BirthDate ? user.BirthDate|date('Y-m-d') : '' }}</td>
</tr>
<tr>
<th>Téléphone</th>
<td>{{ user.Phone }}</td>
</tr>
<tr>
<th>Rôles</th>
<td>
{% if user.roles %}
<ul>
{% for role in user.roles %}
<li>{{ role }}</li>
{% endfor %}
</ul>
{% endif %}
</td>
</tr>
<tr>
<th>Compétences</th>
<td>
{% if user.skills|length > 0 %}
<ul>
{% for skill in user.skills %}
<li>{{ skill.Wording }}</li>
{% endfor %}
</ul>
{% else %}
Aucune compétence
{% endif %}
</td>
</tr>
</tbody>
</table>
<a href="{{ path('app_user_index') }}">back to list</a>
<a href="{{ path('app_user_edit', {'id': user.id}) }}">edit</a>
<a href="{{ path('app_user_index') }}" class="btn btn-primary">Retour à la liste</a>
<a href="{{ path('app_user_edit', {'id': user.id}) }}" class="btn btn-warning">Modifier</a>
{{ include('user/_delete_form.html.twig') }}
{% endblock %}

View File

@ -1,13 +1,14 @@
{% extends 'base.html.twig' %}
{% block title %}Edit Vehicle{% endblock %}
{% block title %}Modifier un véhicule{% endblock %}
{% block body %}
<h1>Edit Vehicle</h1>
<h1 class="mb-4">✏️ Modifier un véhicule</h1>
{{ include('vehicle/_form.html.twig', {'button_label': 'Update'}) }}
{{ include('vehicle/_form.html.twig', {'button_label': 'Mettre à jour'}) }}
<a href="{{ path('app_vehicle_index') }}">back to list</a>
{{ include('vehicle/_delete_form.html.twig') }}
<div class="mt-3">
<a href="{{ path('app_vehicle_index') }}" class="btn btn-secondary">← Retour à la liste</a>
{{ include('vehicle/_delete_form.html.twig') }}
</div>
{% endblock %}

View File

@ -1,18 +1,19 @@
{% extends 'base.html.twig' %}
{% block title %}Vehicle index{% endblock %}
{% block title %}Liste des véhicules{% endblock %}
{% block body %}
<h1>Vehicle index</h1>
<h1 class="mb-4">🚗 Liste des véhicules</h1>
<table class="table">
<thead>
<tr>
<th>Id</th>
<th>LicensePlate</th>
<th>Brand</th>
<th>actions</th>
</tr>
<table class="table table-striped table-bordered align-middle">
<thead class="table-dark">
<tr>
<th>ID</th>
<th>Immatriculation</th>
<th>Marque</th>
<th>Modèle</th>
<th class="text-center">Actions</th>
</tr>
</thead>
<tbody>
{% for vehicle in vehicles %}
@ -20,18 +21,19 @@
<td>{{ vehicle.id }}</td>
<td>{{ vehicle.LicensePlate }}</td>
<td>{{ vehicle.Brand }}</td>
<td>
<a href="{{ path('app_vehicle_show', {'id': vehicle.id}) }}">show</a>
<a href="{{ path('app_vehicle_edit', {'id': vehicle.id}) }}">edit</a>
<td>{{ vehicle.Model }}</td>
<td class="text-center">
<a href="{{ path('app_vehicle_show', {'id': vehicle.id}) }}" class="btn btn-info btn-sm">👁️ Voir</a>
<a href="{{ path('app_vehicle_edit', {'id': vehicle.id}) }}" class="btn btn-warning btn-sm">✏️ Modifier</a>
</td>
</tr>
{% else %}
<tr>
<td colspan="4">no records found</td>
<td colspan="5" class="text-center text-muted">Aucun véhicule enregistré.</td>
</tr>
{% endfor %}
</tbody>
</table>
<a href="{{ path('app_vehicle_new') }}">Create new</a>
<a href="{{ path('app_vehicle_new') }}" class="btn btn-success mt-3"> Ajouter un véhicule</a>
{% endblock %}

View File

@ -1,11 +1,13 @@
{% extends 'base.html.twig' %}
{% block title %}New Vehicle{% endblock %}
{% block title %}Nouveau véhicule{% endblock %}
{% block body %}
<h1>Create new Vehicle</h1>
<h1 class="mb-4"> Ajouter un nouveau véhicule</h1>
{{ include('vehicle/_form.html.twig') }}
<a href="{{ path('app_vehicle_index') }}">back to list</a>
<div class="mt-3">
<a href="{{ path('app_vehicle_index') }}" class="btn btn-secondary">← Retour à la liste</a>
</div>
{% endblock %}

View File

@ -1,30 +1,34 @@
{% extends 'base.html.twig' %}
{% block title %}Vehicle{% endblock %}
{% block title %}Détail du véhicule{% endblock %}
{% block body %}
<h1>Vehicle</h1>
<h1 class="mb-4">🔍 Détail du véhicule</h1>
<table class="table">
<table class="table table-bordered">
<tbody>
<tr>
<th>Id</th>
<td>{{ vehicle.id }}</td>
</tr>
<tr>
<th>LicensePlate</th>
<td>{{ vehicle.LicensePlate }}</td>
</tr>
<tr>
<th>Brand</th>
<td>{{ vehicle.Brand }}</td>
</tr>
<tr>
<th>ID</th>
<td>{{ vehicle.id }}</td>
</tr>
<tr>
<th>Immatriculation</th>
<td>{{ vehicle.LicensePlate }}</td>
</tr>
<tr>
<th>Marque</th>
<td>{{ vehicle.Brand }}</td>
</tr>
<tr>
<th>Modèle</th>
<td>{{ vehicle.Model }}</td>
</tr>
</tbody>
</table>
<a href="{{ path('app_vehicle_index') }}">back to list</a>
<a href="{{ path('app_vehicle_edit', {'id': vehicle.id}) }}">edit</a>
{{ include('vehicle/_delete_form.html.twig') }}
<div class="mt-3">
<a href="{{ path('app_vehicle_index') }}" class="btn btn-secondary">← Retour à la liste</a>
<a href="{{ path('app_vehicle_edit', {'id': vehicle.id}) }}" class="btn btn-warning">✏️ Modifier</a>
{{ include('vehicle/_delete_form.html.twig') }}
</div>
{% endblock %}