Compare commits

...

12 Commits

20 changed files with 820 additions and 757 deletions

View File

@ -134,6 +134,7 @@
<excludeFolder url="file://$MODULE_DIR$/vendor/twig/extra-bundle" /> <excludeFolder url="file://$MODULE_DIR$/vendor/twig/extra-bundle" />
<excludeFolder url="file://$MODULE_DIR$/vendor/twig/twig" /> <excludeFolder url="file://$MODULE_DIR$/vendor/twig/twig" />
<excludeFolder url="file://$MODULE_DIR$/vendor/webmozart/assert" /> <excludeFolder url="file://$MODULE_DIR$/vendor/webmozart/assert" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-php84" />
</content> </content>
<orderEntry type="inheritedJdk" /> <orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />

4
.idea/dataSources.xml generated
View File

@ -1,11 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="DataSourceManagerImpl" format="xml" multifile-model="true"> <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> <driver-ref>postgresql</driver-ref>
<synchronize>true</synchronize> <synchronize>true</synchronize>
<jdbc-driver>org.postgresql.Driver</jdbc-driver> <jdbc-driver>org.postgresql.Driver</jdbc-driver>
<jdbc-url>jdbc:postgresql://localhost:5433/HegreEtConfort</jdbc-url> <jdbc-url>jdbc:postgresql://localhost:5432/hegreetconfort</jdbc-url>
<working-dir>$ProjectFileDir$</working-dir> <working-dir>$ProjectFileDir$</working-dir>
</data-source> </data-source>
</component> </component>

1
.idea/php.xml generated
View File

@ -142,6 +142,7 @@
<path value="$PROJECT_DIR$/vendor/sebastian/object-enumerator" /> <path value="$PROJECT_DIR$/vendor/sebastian/object-enumerator" />
<path value="$PROJECT_DIR$/vendor/sebastian/type" /> <path value="$PROJECT_DIR$/vendor/sebastian/type" />
<path value="$PROJECT_DIR$/vendor/sebastian/diff" /> <path value="$PROJECT_DIR$/vendor/sebastian/diff" />
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-php84" />
</include_path> </include_path>
</component> </component>
<component name="PhpProjectSharedConfiguration" php_language_level="8.2" /> <component name="PhpProjectSharedConfiguration" php_language_level="8.2" />

File diff suppressed because one or more lines are too long

View File

@ -1,5 +1,9 @@
/** /**
<<<<<<< HEAD
* Bundled by jsDelivr using Rollup v2.79.1 and Terser v5.19.2. * Bundled by jsDelivr using Rollup v2.79.1 and Terser v5.19.2.
=======
* Bundled by jsDelivr using Rollup v2.79.2 and Terser v5.39.0.
>>>>>>> b29391d6550c182d7b75efab8795a238c1936a27
* Original file: /npm/@hotwired/turbo@7.3.0/dist/turbo.es2017-esm.js * Original file: /npm/@hotwired/turbo@7.3.0/dist/turbo.es2017-esm.js
* *
* Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files

View File

@ -35,6 +35,7 @@
"symfony/property-info": "7.1.*", "symfony/property-info": "7.1.*",
"symfony/runtime": "7.1.*", "symfony/runtime": "7.1.*",
"symfony/security-bundle": "7.1.*", "symfony/security-bundle": "7.1.*",
"symfony/security-csrf": "7.1.*",
"symfony/serializer": "7.1.*", "symfony/serializer": "7.1.*",
"symfony/stimulus-bundle": "^2.20", "symfony/stimulus-bundle": "^2.20",
"symfony/string": "7.1.*", "symfony/string": "7.1.*",

1381
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -14,14 +14,14 @@ security:
lazy: true lazy: true
provider: app_user_provider provider: app_user_provider
form_login: form_login:
login_path: _login login_path: app_login
check_path: _login check_path: app_login
success_handler: App\Security\CustomAuthenticationSuccessHandler success_handler: App\Security\CustomAuthenticationSuccessHandler
enable_csrf: true enable_csrf: true
csrf_token_id: authenticate csrf_token_id: authenticate
logout: logout:
path: _logout path: app_logout
target: _login target: app_login
# activate different ways to authenticate # activate different ways to authenticate
# https://symfony.com/doc/current/security.html#the-firewall # https://symfony.com/doc/current/security.html#the-firewall

View File

@ -1,71 +0,0 @@
<?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 Version20250410124208 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 SEQUENCE fault_id_seq INCREMENT BY 1 MINVALUE 1 START 1');
$this->addSql('CREATE SEQUENCE intervention_id_seq INCREMENT BY 1 MINVALUE 1 START 1');
$this->addSql('CREATE SEQUENCE skill_id_seq INCREMENT BY 1 MINVALUE 1 START 1');
$this->addSql('CREATE SEQUENCE stock_id_seq INCREMENT BY 1 MINVALUE 1 START 1');
$this->addSql('CREATE SEQUENCE utilisateur_id_seq INCREMENT BY 1 MINVALUE 1 START 1');
$this->addSql('CREATE SEQUENCE vehicle_id_seq INCREMENT BY 1 MINVALUE 1 START 1');
$this->addSql('CREATE TABLE fault (id INT NOT NULL, wording VARCHAR(255) NOT NULL, description VARCHAR(255) NOT NULL, PRIMARY KEY(id))');
$this->addSql('CREATE TABLE intervention (id INT NOT NULL, wording VARCHAR(255) NOT NULL, timestamp TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, description VARCHAR(255) NOT NULL, address VARCHAR(255) NOT NULL, status VARCHAR(255) NOT NULL, PRIMARY KEY(id))');
$this->addSql('CREATE TABLE skill (id INT NOT NULL, wording VARCHAR(255) NOT NULL, description VARCHAR(255) NOT NULL, PRIMARY KEY(id))');
$this->addSql('CREATE TABLE stock (id INT NOT NULL, wording VARCHAR(255) NOT NULL, description VARCHAR(255) NOT NULL, quantity VARCHAR(255) NOT NULL, PRIMARY KEY(id))');
$this->addSql('CREATE TABLE utilisateur (id INT NOT NULL, email VARCHAR(180) NOT NULL, first_name VARCHAR(255) NOT NULL, last_name VARCHAR(255) NOT NULL, birth_date DATE NOT NULL, phone VARCHAR(255) NOT NULL, roles JSON NOT NULL, password VARCHAR(255) NOT NULL, PRIMARY KEY(id))');
$this->addSql('CREATE UNIQUE INDEX UNIQ_IDENTIFIER_EMAIL ON utilisateur (email)');
$this->addSql('CREATE TABLE vehicle (id INT NOT NULL, license_plate VARCHAR(255) NOT NULL, brand VARCHAR(255) NOT NULL, model VARCHAR(255) NOT NULL, PRIMARY KEY(id))');
$this->addSql('CREATE TABLE messenger_messages (id BIGSERIAL NOT NULL, body TEXT NOT NULL, headers TEXT NOT NULL, queue_name VARCHAR(190) NOT NULL, created_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, available_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, delivered_at TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL, PRIMARY KEY(id))');
$this->addSql('CREATE INDEX IDX_75EA56E0FB7336F0 ON messenger_messages (queue_name)');
$this->addSql('CREATE INDEX IDX_75EA56E0E3BD61CE ON messenger_messages (available_at)');
$this->addSql('CREATE INDEX IDX_75EA56E016BA31DB ON messenger_messages (delivered_at)');
$this->addSql('COMMENT ON COLUMN messenger_messages.created_at IS \'(DC2Type:datetime_immutable)\'');
$this->addSql('COMMENT ON COLUMN messenger_messages.available_at IS \'(DC2Type:datetime_immutable)\'');
$this->addSql('COMMENT ON COLUMN messenger_messages.delivered_at IS \'(DC2Type:datetime_immutable)\'');
$this->addSql('CREATE OR REPLACE FUNCTION notify_messenger_messages() RETURNS TRIGGER AS $$
BEGIN
PERFORM pg_notify(\'messenger_messages\', NEW.queue_name::text);
RETURN NEW;
END;
$$ LANGUAGE plpgsql;');
$this->addSql('DROP TRIGGER IF EXISTS notify_trigger ON messenger_messages;');
$this->addSql('CREATE TRIGGER notify_trigger AFTER INSERT OR UPDATE ON messenger_messages FOR EACH ROW EXECUTE PROCEDURE notify_messenger_messages();');
}
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('DROP SEQUENCE fault_id_seq CASCADE');
$this->addSql('DROP SEQUENCE intervention_id_seq CASCADE');
$this->addSql('DROP SEQUENCE skill_id_seq CASCADE');
$this->addSql('DROP SEQUENCE stock_id_seq CASCADE');
$this->addSql('DROP SEQUENCE utilisateur_id_seq CASCADE');
$this->addSql('DROP SEQUENCE vehicle_id_seq CASCADE');
$this->addSql('DROP TABLE fault');
$this->addSql('DROP TABLE intervention');
$this->addSql('DROP TABLE skill');
$this->addSql('DROP TABLE stock');
$this->addSql('DROP TABLE utilisateur');
$this->addSql('DROP TABLE vehicle');
$this->addSql('DROP TABLE messenger_messages');
}
}

View File

@ -9,7 +9,7 @@ use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
class AuthenticationController extends AbstractController class AuthenticationController extends AbstractController
{ {
#[Route(path: '/', name: '_login')] #[Route(path: '/', name: 'app_login')]
public function login(AuthenticationUtils $authenticationUtils): Response public function login(AuthenticationUtils $authenticationUtils): Response
{ {
// get the login error if there is one // get the login error if there is one
@ -24,7 +24,7 @@ class AuthenticationController extends AbstractController
]); ]);
} }
#[Route(path: '/logout', name: '_logout')] #[Route(path: '/logout', name: 'app_logout')]
public function logout(): void public function logout(): void
{ {
throw new \LogicException('This method can be blank - it will be intercepted by the logout key on your firewall.'); throw new \LogicException('This method can be blank - it will be intercepted by the logout key on your firewall.');

View File

@ -6,9 +6,10 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Routing\Attribute\Route;
#[Route('/calendrier')]
class CalendrierController extends AbstractController class CalendrierController extends AbstractController
{ {
#[Route('/calendrier', name: 'app_calendrier')] #[Route(name: 'app_calendrier_index', methods: ['GET'])]
public function index(): Response public function index(): Response
{ {
return $this->render('calendrier/index.html.twig', [ return $this->render('calendrier/index.html.twig', [
@ -16,16 +17,31 @@ class CalendrierController extends AbstractController
]); ]);
} }
#[Route('/calendrier/secretaire', name: 'calendrier_index_secretaire')] #[Route('/secretaire', name: 'app_calendrier_indexSecretaire')]
public function indexSecretaire(): Response public function indexSecretaire(): Response
{ {
return $this->render('calendrier/indexSecretaire.html.twig'); return $this->render('calendrier/indexSecretaire.html.twig', [
'controller_name' => 'CalendrierController',
]);
} }
#[Route('/calendrier/chauffagiste', name: 'calendrier_index_chauffagiste')] #[Route('/chauffagiste', name: 'app_calendrier_indexChauffagiste')]
public function indexChauffagiste(): Response public function indexChauffagiste(): Response
{ {
return $this->render('calendrier/indexChauffagiste.html.twig'); return $this->render('calendrier/indexChauffagiste.html.twig', [
} 'controller_name' => 'CalendrierController',
]); }
// 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

@ -17,7 +17,7 @@ final class InterventionController extends AbstractController
#[Route(name: 'app_intervention_index', methods: ['GET'])] #[Route(name: 'app_intervention_index', methods: ['GET'])]
public function index(InterventionRepository $interventionRepository): Response public function index(InterventionRepository $interventionRepository): Response
{ {
return $this->render('intervention/admin.html.twig', [ return $this->render('intervention/index.html.twig', [
'interventions' => $interventionRepository->findAll(), 'interventions' => $interventionRepository->findAll(),
]); ]);
} }

View File

@ -9,6 +9,7 @@ use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Routing\Attribute\Route;
#[Route('/user')] #[Route('/user')]
@ -17,19 +18,24 @@ final class UserController extends AbstractController
#[Route(name: 'app_user_index', methods: ['GET'])] #[Route(name: 'app_user_index', methods: ['GET'])]
public function index(UserRepository $userRepository): Response public function index(UserRepository $userRepository): Response
{ {
return $this->render('user/admin.html.twig', [ return $this->render('user/index.html.twig', [
'users' => $userRepository->findAll(), 'users' => $userRepository->findAll(),
]); ]);
} }
#[Route('/new', name: 'app_user_new', methods: ['GET', 'POST'])] #[Route('/new', name: 'app_user_new', methods: ['GET', 'POST'])]
public function new(Request $request, EntityManagerInterface $entityManager): Response public function new(Request $request, EntityManagerInterface $entityManager, UserPasswordHasherInterface $passwordHasher): Response
{ {
$user = new Utilisateur(); $user = new Utilisateur();
$form = $this->createForm(UserType::class, $user); $form = $this->createForm(UserType::class, $user);
$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();
$hashedPassword = $passwordHasher->hashPassword($user, $plainPassword);
$user->setPassword($hashedPassword);
$entityManager->persist($user); $entityManager->persist($user);
$entityManager->flush(); $entityManager->flush();

View File

@ -17,7 +17,7 @@ final class VehicleController extends AbstractController
#[Route(name: 'app_vehicle_index', methods: ['GET'])] #[Route(name: 'app_vehicle_index', methods: ['GET'])]
public function index(VehicleRepository $vehicleRepository): Response public function index(VehicleRepository $vehicleRepository): Response
{ {
return $this->render('vehicle/admin.html.twig', [ return $this->render('vehicle/index.html.twig', [
'vehicles' => $vehicleRepository->findAll(), 'vehicles' => $vehicleRepository->findAll(),
]); ]);
} }

View File

@ -31,6 +31,19 @@ class Utilisateur implements UserInterface, PasswordAuthenticatedUserInterface
#[ORM\Column(length: 255)] #[ORM\Column(length: 255)]
private ?string $Phone = null; private ?string $Phone = null;
private ?string $plainPassword = null;
public function getPlainPassword(): ?string
{
return $this->plainPassword;
}
public function setPlainPassword(?string $plainPassword): void
{
$this->plainPassword = $plainPassword;
}
/** /**
* @var list<string> The user roles * @var list<string> The user roles
*/ */
@ -179,6 +192,6 @@ class Utilisateur implements UserInterface, PasswordAuthenticatedUserInterface
public function eraseCredentials(): void public function eraseCredentials(): void
{ {
// If you store any temporary, sensitive data on the user, clear it here // If you store any temporary, sensitive data on the user, clear it here
// $this->plainPassword = null; $this->plainPassword = null;
} }
} }

View File

@ -6,6 +6,7 @@ use App\Entity\Utilisateur;
use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\DateType; use Symfony\Component\Form\Extension\Core\Type\DateType;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\OptionsResolver\OptionsResolver;
@ -23,10 +24,17 @@ class UserType extends AbstractType
]) ])
->add('Phone', TextType::class) ->add('Phone', TextType::class)
->add('roles', ChoiceType::class, [ ->add('roles', ChoiceType::class, [
'choices' => [
'Secretaire' => 'ROLE_SECRETAIRE',
'Chauffagiste' => 'ROLE_CHAUFFAGISTE',
],
'multiple' => true, 'multiple' => true,
]) ])
->add('password', TextType::class) ->add('plainPassword', PasswordType::class, [
; 'mapped' => false,
'required' => true,
'label' => 'Mot de passe',
]);
} }
public function configureOptions(OptionsResolver $resolver): void public function configureOptions(OptionsResolver $resolver): void

View File

@ -15,6 +15,7 @@ class VehicleType extends AbstractType
$builder $builder
->add('LicensePlate', TextType::class) ->add('LicensePlate', TextType::class)
->add('Brand', TextType::class) ->add('Brand', TextType::class)
->add('Model', TextType::class)
; ;
} }

View File

@ -19,28 +19,28 @@
<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_user_new') }}">Créer un utilisateur</a></li> <li><a href="{{ path('app_user_index') }}">Gérer un utilisateur</a></li>
<li><a href="{{ path('app_calendrier') }}">Tous les plannings</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_stock_index') }}">Gérer les stocks</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>
{% 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('user_new') }}">Créer un chauffagiste</a></li> <li><a href="{{ path('app_user_index') }}">Créer un chauffagiste</a></li>
<li><a href="{{ path('calendrier_index_secretaire') }}">Plannings chauffagistes</a></li> <li><a href="{{ path('app_calendrier_indexSecretaire') }}">Plannings chauffagistes</a></li>
<li><a href="{{ path('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('vehicle_index') }}">Gérer les véhicules</a></li> <li><a href="{{ path('app_vehicle_index') }}">Gérer les véhicules</a></li>
{% endif %} {% endif %}
{% if is_granted('ROLE_CHAUFFAGISTE') %} {% if is_granted('ROLE_CHAUFFAGISTE') %}
<li><a href="{{ path('chauffagiste_dashboard') }}">Dashboard Chauffagiste</a></li> <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('app_calendrier_indexChauffagiste') }}">Mon planning</a></li>
<li><a href="{{ path('intervention_index') }}">Mes interventions</a></li> <li><a href="{{ path('app_intervention_index') }}">Mes interventions</a></li>
<li><a href="{{ path('stock_index') }}">Pièces détachées</a></li> <li><a href="{{ path('app_stock_index') }}">Pièces détachées</a></li>
{% endif %} {% endif %}
<li><a href="{{ path('_logout') }}">Déconnexion</a></li> <li><a href="{{ path('app_logout') }}">Déconnexion</a></li>
</ul> </ul>
</nav> </nav>
{% endif %} {% endif %}

View File

@ -25,7 +25,7 @@
<img class="logoProfile" src="{{ asset('styles/image/profile.png') }}" alt="Logo Profil"> <img class="logoProfile" src="{{ asset('styles/image/profile.png') }}" alt="Logo Profil">
<img class="logoPassword" src="{{ asset('styles/image/password.png') }}" alt="Logo Password"> <img class="logoPassword" src="{{ asset('styles/image/password.png') }}" alt="Logo Password">
<form method="post" action="{{ path('_login') }}"> <form method="post" action="{{ path('app_login') }}">
{% if error %} {% if error %}
<div class="alert alert-danger">{{ error.messageKey|trans(error.messageData, 'security') }}</div> <div class="alert alert-danger">{{ error.messageKey|trans(error.messageData, 'security') }}</div>
{% endif %} {% endif %}