Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5d3b63b343 |
@@ -1,5 +1,4 @@
|
|||||||
@import url('https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,400;0,600;1,400;1,600&display=swap');
|
@import url('https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,400;0,600;1,400;1,600&display=swap');
|
||||||
@import "button.css";
|
|
||||||
|
|
||||||
@tailwind base;
|
@tailwind base;
|
||||||
@tailwind components;
|
@tailwind components;
|
||||||
|
|||||||
@@ -1,91 +0,0 @@
|
|||||||
.btnPrimary {
|
|
||||||
@apply bg-[#2a8d57] text-white font-bold py-2 px-4 border-b-4 rounded transition-all duration-300 ease-in-out;
|
|
||||||
|
|
||||||
/* Bordure */
|
|
||||||
border-color: #1e7a43;
|
|
||||||
|
|
||||||
/* Survol */
|
|
||||||
&:hover {
|
|
||||||
@apply bg-[#1e7a43] border-[#155d2e];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Clique (État actif) */
|
|
||||||
&:active {
|
|
||||||
@apply bg-[#1b6b38] border-[#155d2e];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Focus (État focus) */
|
|
||||||
&:focus {
|
|
||||||
outline: none;
|
|
||||||
box-shadow: 0 0 0 4px rgba(42, 141, 87, 0.5);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.btnSecondary {
|
|
||||||
@apply bg-[#2f4858] text-white font-bold py-2 px-4 border-b-4 rounded transition-all duration-300 ease-in-out;
|
|
||||||
|
|
||||||
/* Bordure */
|
|
||||||
border-color: #1e3c46;
|
|
||||||
|
|
||||||
/* Survol */
|
|
||||||
&:hover {
|
|
||||||
@apply bg-[#1e3c46] border-[#16323a];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Clique (État actif) */
|
|
||||||
&:active {
|
|
||||||
@apply bg-[#193d49] border-[#16323a];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Focus (État focus) */
|
|
||||||
&:focus {
|
|
||||||
outline: none;
|
|
||||||
box-shadow: 0 0 0 4px rgba(47, 72, 88, 0.5);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.btnThird {
|
|
||||||
@apply bg-[#c0ab4d] text-white font-bold py-2 px-4 border-b-4 rounded transition-all duration-300 ease-in-out;
|
|
||||||
|
|
||||||
/* Bordure */
|
|
||||||
border-color: #b0a139;
|
|
||||||
|
|
||||||
/* Survol */
|
|
||||||
&:hover {
|
|
||||||
@apply bg-[#b0a139] border-[#9a8b2b];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Clique (État actif) */
|
|
||||||
&:active {
|
|
||||||
@apply bg-[#9a8b2b] border-[#6f6a1c];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Focus (État focus) */
|
|
||||||
&:focus {
|
|
||||||
outline: none;
|
|
||||||
box-shadow: 0 0 0 4px rgba(192, 171, 77, 0.5);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.btnAnnule {
|
|
||||||
@apply bg-[#d54f45] text-white font-bold py-2 px-4 border-b-4 rounded transition-all duration-300 ease-in-out;
|
|
||||||
|
|
||||||
/* Bordure */
|
|
||||||
border-color: #a73a2d;
|
|
||||||
|
|
||||||
/* Survol */
|
|
||||||
&:hover {
|
|
||||||
@apply bg-[#a73a2d] border-[#8b2d1e];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Clique (État actif) */
|
|
||||||
&:active {
|
|
||||||
@apply bg-[#90271e] border-[#8b2d1e];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Focus (État focus) */
|
|
||||||
&:focus {
|
|
||||||
outline: none;
|
|
||||||
box-shadow: 0 0 0 4px rgba(213, 79, 69, 0.5);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -28,12 +28,5 @@ services:
|
|||||||
arguments:
|
arguments:
|
||||||
$targetDirectory: '../public/upload/image/profile/'
|
$targetDirectory: '../public/upload/image/profile/'
|
||||||
|
|
||||||
App\EventListener\UserProfileListener:
|
|
||||||
arguments:
|
|
||||||
- '@security.token_storage'
|
|
||||||
- '@twig'
|
|
||||||
tags:
|
|
||||||
- { name: 'kernel.event_subscriber' }
|
|
||||||
|
|
||||||
# add more service definitions when explicit configuration is needed
|
# add more service definitions when explicit configuration is needed
|
||||||
# please note that last definitions always *replace* previous ones
|
# please note that last definitions always *replace* previous ones
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 6.5 KiB |
|
Before Width: | Height: | Size: 8.6 KiB |
|
Before Width: | Height: | Size: 8.6 KiB |
|
Before Width: | Height: | Size: 8.6 KiB |
|
Before Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 3.0 KiB |
@@ -44,12 +44,16 @@ class AdminController extends AbstractController
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Gestion des utilisateurs
|
||||||
#[Route('/admin/user', name: 'app_adminUser')]
|
#[Route('/admin/user', name: 'app_adminUser')]
|
||||||
public function adminUser(TokenStorageInterface $tokenStorage): Response
|
public function adminUser(TokenStorageInterface $tokenStorage): Response
|
||||||
{
|
{
|
||||||
|
$token = $tokenStorage->getToken();
|
||||||
|
$userConnect = $token?->getUser();
|
||||||
$participants = $this->participantRepository->findAll();
|
$participants = $this->participantRepository->findAll();
|
||||||
$sites = $this->siteRepository->findAll();
|
$sites = $this->siteRepository->findAll();
|
||||||
return $this->render('admin/user.html.twig', [
|
return $this->render('admin/user.html.twig', [
|
||||||
|
'profile' => $userConnect,
|
||||||
'participants' => $participants,
|
'participants' => $participants,
|
||||||
'sites' => $sites,
|
'sites' => $sites,
|
||||||
'controller_name' => 'AdminController',
|
'controller_name' => 'AdminController',
|
||||||
@@ -58,56 +62,68 @@ class AdminController extends AbstractController
|
|||||||
#[Route('/admin/user/disable/{id}', name: 'app_adminUserDisable')]
|
#[Route('/admin/user/disable/{id}', name: 'app_adminUserDisable')]
|
||||||
public function disableParticipant(string $id, ParticipantRepository $participantRepository, EntityManagerInterface $entityManager): RedirectResponse
|
public function disableParticipant(string $id, ParticipantRepository $participantRepository, EntityManagerInterface $entityManager): RedirectResponse
|
||||||
{
|
{
|
||||||
|
// Récupérer le participant à partir de l'id
|
||||||
$participant = $participantRepository->find($id);
|
$participant = $participantRepository->find($id);
|
||||||
|
|
||||||
|
// Vérifier si le participant existe
|
||||||
if (!$participant) {
|
if (!$participant) {
|
||||||
$this->addFlash('error', 'Le participant demandé n\'existe pas.');
|
$this->addFlash('error', 'Le participant demandé n\'existe pas.');
|
||||||
return $this->redirectToRoute('app_adminUser');
|
return $this->redirectToRoute('app_adminUser'); // Redirigez vers une liste ou une autre page
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Désactiver le participant (par exemple, définir une propriété "isActive" à false)
|
||||||
if ($participant->isActif()){
|
if ($participant->isActif()){
|
||||||
$participant->setActif(false);
|
$participant->setActif(false);
|
||||||
}else{
|
}else{
|
||||||
$participant->setActif(true);
|
$participant->setActif(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sauvegarder la modification en base de données
|
||||||
$entityManager->persist($participant);
|
$entityManager->persist($participant);
|
||||||
$entityManager->flush();
|
$entityManager->flush();
|
||||||
|
|
||||||
|
// Ajouter un message de succès et rediriger
|
||||||
$this->addFlash('success', 'Participant désactivé avec succès.');
|
$this->addFlash('success', 'Participant désactivé avec succès.');
|
||||||
return $this->redirectToRoute('app_adminUser');
|
return $this->redirectToRoute('app_adminUser');
|
||||||
}
|
}
|
||||||
#[Route('/admin/user/delete/{id}', name: 'app_adminUserDelete')]
|
#[Route('/admin/user/delete/{id}', name: 'app_adminUserDelete')]
|
||||||
public function deleteUser(string $id, ParticipantRepository $participantRepository, EntityManagerInterface $entityManager): RedirectResponse
|
public function deleteUser(string $id, ParticipantRepository $participantRepository, EntityManagerInterface $entityManager): RedirectResponse
|
||||||
{
|
{
|
||||||
|
// Récupérer l'utilisateur ou le participant à partir de l'id
|
||||||
$participant = $participantRepository->find($id);
|
$participant = $participantRepository->find($id);
|
||||||
|
|
||||||
|
// Vérifier si l'utilisateur existe
|
||||||
if (!$participant) {
|
if (!$participant) {
|
||||||
$this->addFlash('error', 'L\'utilisateur demandé n\'existe pas.');
|
$this->addFlash('error', 'L\'utilisateur demandé n\'existe pas.');
|
||||||
return $this->redirectToRoute('app_adminUser'); // Redirigez vers une liste ou une autre page
|
return $this->redirectToRoute('app_adminUser'); // Redirigez vers une liste ou une autre page
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Supprimer l'utilisateur
|
||||||
$entityManager->remove($participant);
|
$entityManager->remove($participant);
|
||||||
$entityManager->flush();
|
$entityManager->flush();
|
||||||
|
|
||||||
|
// Ajouter un message de succès et rediriger
|
||||||
$this->addFlash('success', 'Utilisateur supprimé avec succès.');
|
$this->addFlash('success', 'Utilisateur supprimé avec succès.');
|
||||||
return $this->redirectToRoute('app_adminUser');
|
return $this->redirectToRoute('app_adminUser'); // Redirigez vers la liste des utilisateurs
|
||||||
}
|
}
|
||||||
#[Route('/admin/user/add', name: 'app_adminUserAdd', methods: ['POST'])]
|
#[Route('/admin/user/add', name: 'app_adminUserAdd', methods: ['POST'])]
|
||||||
public function userAdd(Request $request, EntityManagerInterface $entityManager, MailerInterface $mailer, UrlGeneratorInterface $urlGenerator): Response
|
public function userAdd(Request $request, EntityManagerInterface $entityManager, MailerInterface $mailer, UrlGeneratorInterface $urlGenerator): Response
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
// Récupérer les données envoyées par le formulaire
|
||||||
$nom = $request->request->get('nom');
|
$nom = $request->request->get('nom');
|
||||||
$prenom = $request->request->get('prenom');
|
$prenom = $request->request->get('prenom');
|
||||||
$pseudo = $request->request->get('pseudo');
|
$pseudo = $request->request->get('pseudo');
|
||||||
$telephone = $request->request->get('phone');
|
$telephone = $request->request->get('phone');
|
||||||
$mail = $request->request->get('mail');
|
$mail = $request->request->get('mail');
|
||||||
|
|
||||||
|
// Vérifier que les champs ne sont pas vides
|
||||||
if (!$mail || !$pseudo) {
|
if (!$mail || !$pseudo) {
|
||||||
$this->addFlash('error', 'Tous les champs sont requis.');
|
$this->addFlash('error', 'Tous les champs sont requis.');
|
||||||
return $this->redirectToRoute('app_adminUser');
|
return $this->redirectToRoute('app_adminUser');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Vérifier que le mail est valide avec une regex
|
||||||
if (!filter_var($mail, FILTER_VALIDATE_EMAIL)) {
|
if (!filter_var($mail, FILTER_VALIDATE_EMAIL)) {
|
||||||
$this->addFlash('error', 'L\'adresse e-mail n\'est pas valide.');
|
$this->addFlash('error', 'L\'adresse e-mail n\'est pas valide.');
|
||||||
return $this->redirectToRoute('app_adminUser');
|
return $this->redirectToRoute('app_adminUser');
|
||||||
@@ -127,8 +143,10 @@ class AdminController extends AbstractController
|
|||||||
|
|
||||||
$entityManager->persist($participant);
|
$entityManager->persist($participant);
|
||||||
|
|
||||||
|
// Générer un token unique
|
||||||
$token = bin2hex(random_bytes(32));
|
$token = bin2hex(random_bytes(32));
|
||||||
|
|
||||||
|
// Enregistrer le token dans la base de données
|
||||||
$passwordResetToken = new PasswordResetToken();
|
$passwordResetToken = new PasswordResetToken();
|
||||||
$passwordResetToken->setToken($token)
|
$passwordResetToken->setToken($token)
|
||||||
->setEmail($participant->getEmail())
|
->setEmail($participant->getEmail())
|
||||||
@@ -137,12 +155,14 @@ class AdminController extends AbstractController
|
|||||||
$entityManager->persist($passwordResetToken);
|
$entityManager->persist($passwordResetToken);
|
||||||
$entityManager->flush();
|
$entityManager->flush();
|
||||||
|
|
||||||
|
// Générer un lien de réinitialisation
|
||||||
$resetLink = $urlGenerator->generate(
|
$resetLink = $urlGenerator->generate(
|
||||||
'app_password_reset',
|
'app_password_reset',
|
||||||
['token' => $token],
|
['token' => $token],
|
||||||
UrlGeneratorInterface::ABSOLUTE_URL
|
UrlGeneratorInterface::ABSOLUTE_URL
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Envoyer un email de notification
|
||||||
$email = (new Email())
|
$email = (new Email())
|
||||||
->from('contact@sortir.com')
|
->from('contact@sortir.com')
|
||||||
->to($participant->getEmail())
|
->to($participant->getEmail())
|
||||||
@@ -188,8 +208,10 @@ class AdminController extends AbstractController
|
|||||||
$participant->setPending(true);
|
$participant->setPending(true);
|
||||||
$em->persist($participant);
|
$em->persist($participant);
|
||||||
|
|
||||||
|
// Générer un token unique
|
||||||
$token = bin2hex(random_bytes(32));
|
$token = bin2hex(random_bytes(32));
|
||||||
|
|
||||||
|
// Enregistrer le token dans la base de données
|
||||||
$passwordResetToken = new PasswordResetToken();
|
$passwordResetToken = new PasswordResetToken();
|
||||||
$passwordResetToken->setToken($token)
|
$passwordResetToken->setToken($token)
|
||||||
->setEmail($participant->getEmail())
|
->setEmail($participant->getEmail())
|
||||||
@@ -197,12 +219,14 @@ class AdminController extends AbstractController
|
|||||||
|
|
||||||
$em->persist($passwordResetToken);
|
$em->persist($passwordResetToken);
|
||||||
|
|
||||||
|
// Générer un lien de réinitialisation
|
||||||
$resetLink = $urlGenerator->generate(
|
$resetLink = $urlGenerator->generate(
|
||||||
'app_password_reset',
|
'app_password_reset',
|
||||||
['token' => $token],
|
['token' => $token],
|
||||||
UrlGeneratorInterface::ABSOLUTE_URL
|
UrlGeneratorInterface::ABSOLUTE_URL
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Envoyer un email de notification
|
||||||
$email = (new Email())
|
$email = (new Email())
|
||||||
->from('contact@sortir.com')
|
->from('contact@sortir.com')
|
||||||
->to($participant->getEmail())
|
->to($participant->getEmail())
|
||||||
@@ -231,6 +255,7 @@ class AdminController extends AbstractController
|
|||||||
// $participants = $participantRepository->findAll();
|
// $participants = $participantRepository->findAll();
|
||||||
$userList = $request->request->all('userList');
|
$userList = $request->request->all('userList');
|
||||||
if (empty($userList)) {
|
if (empty($userList)) {
|
||||||
|
// Aucun utilisateur sélectionné
|
||||||
$this->addFlash('warning', 'Veuillez sélectionner au moins un utilisateur.');
|
$this->addFlash('warning', 'Veuillez sélectionner au moins un utilisateur.');
|
||||||
return $this->redirectToRoute('app_adminUser');
|
return $this->redirectToRoute('app_adminUser');
|
||||||
}
|
}
|
||||||
@@ -257,6 +282,7 @@ class AdminController extends AbstractController
|
|||||||
return $response;
|
return $response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Gestion des sites
|
||||||
#[Route('/admin/site', name: 'app_adminSite')]
|
#[Route('/admin/site', name: 'app_adminSite')]
|
||||||
public function adminSite(SiteRepository $siteRepository, TokenStorageInterface $tokenStorage): Response
|
public function adminSite(SiteRepository $siteRepository, TokenStorageInterface $tokenStorage): Response
|
||||||
{
|
{
|
||||||
@@ -272,15 +298,19 @@ class AdminController extends AbstractController
|
|||||||
public function adminSiteAdd(Request $request, EntityManagerInterface $entityManager): Response
|
public function adminSiteAdd(Request $request, EntityManagerInterface $entityManager): Response
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
// Récupérer les données envoyées par le formulaire
|
||||||
$nom = $request->request->get('nom');
|
$nom = $request->request->get('nom');
|
||||||
|
|
||||||
|
// Vérifier que les champs ne sont pas vides
|
||||||
if (!$nom) {
|
if (!$nom) {
|
||||||
return new Response('Tous les champs sont requis.', Response::HTTP_BAD_REQUEST);
|
return new Response('Tous les champs sont requis.', Response::HTTP_BAD_REQUEST);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Créer une nouvelle entité City et définir ses propriétés
|
||||||
$site = new Site();
|
$site = new Site();
|
||||||
$site->setNom($nom);
|
$site->setNom($nom);
|
||||||
|
|
||||||
|
// Enregistrer la ville dans la base de données
|
||||||
$entityManager->persist($site);
|
$entityManager->persist($site);
|
||||||
$entityManager->flush();
|
$entityManager->flush();
|
||||||
$this->addFlash('success', "Site ajouté !");
|
$this->addFlash('success', "Site ajouté !");
|
||||||
@@ -293,13 +323,17 @@ class AdminController extends AbstractController
|
|||||||
#[Route('/admin/site/delete/{id}', name: 'app_adminSiteDelete')]
|
#[Route('/admin/site/delete/{id}', name: 'app_adminSiteDelete')]
|
||||||
public function adminSiteDelete(string $id, SiteRepository $siteRepository, EntityManagerInterface $entityManager): RedirectResponse
|
public function adminSiteDelete(string $id, SiteRepository $siteRepository, EntityManagerInterface $entityManager): RedirectResponse
|
||||||
{
|
{
|
||||||
|
// Récupérer la ville à supprimer
|
||||||
$site = $siteRepository->find($id);
|
$site = $siteRepository->find($id);
|
||||||
|
|
||||||
|
// Vérifier si la ville existe
|
||||||
if (!$site) {
|
if (!$site) {
|
||||||
|
// Si la ville n'existe pas, rediriger avec un message d'erreur
|
||||||
$this->addFlash('error', 'Le site demandée n\'existe pas.');
|
$this->addFlash('error', 'Le site demandée n\'existe pas.');
|
||||||
return $this->redirectToRoute('app_adminSite'); // Rediriger vers la liste des villes
|
return $this->redirectToRoute('app_adminSite'); // Rediriger vers la liste des villes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Supprimer la ville
|
||||||
$entityManager->remove($site);
|
$entityManager->remove($site);
|
||||||
$entityManager->flush();
|
$entityManager->flush();
|
||||||
|
|
||||||
|
|||||||
@@ -20,8 +20,10 @@ class LoginController extends AbstractController
|
|||||||
return $this->redirectToRoute('home'); // Redirection pour utilisateurs connectés
|
return $this->redirectToRoute('home'); // Redirection pour utilisateurs connectés
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get the login error if there is one
|
||||||
$error = $authenticationUtils->getLastAuthenticationError();
|
$error = $authenticationUtils->getLastAuthenticationError();
|
||||||
|
|
||||||
|
// last username entered by the user
|
||||||
$lastUsername = $authenticationUtils->getLastUsername();
|
$lastUsername = $authenticationUtils->getLastUsername();
|
||||||
$user = new Participant();
|
$user = new Participant();
|
||||||
$formUser = $this->createForm(RegistrationFormType::class, $user);
|
$formUser = $this->createForm(RegistrationFormType::class, $user);
|
||||||
@@ -36,6 +38,7 @@ class LoginController extends AbstractController
|
|||||||
#[Route(path: '/logout', name: 'app_logout')]
|
#[Route(path: '/logout', name: 'app_logout')]
|
||||||
public function logout(Security $security): ?Response
|
public function logout(Security $security): ?Response
|
||||||
{
|
{
|
||||||
|
// throw new \LogicException('This method can be blank - it will be intercepted by the logout key on your firewall.');
|
||||||
return $security->logout(false);
|
return $security->logout(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,10 @@ class MainController extends AbstractController
|
|||||||
#[Route('/', name: 'home')]
|
#[Route('/', name: 'home')]
|
||||||
public function home(SortieRepository $sortieRepository, TokenStorageInterface $tokenStorage,): Response
|
public function home(SortieRepository $sortieRepository, TokenStorageInterface $tokenStorage,): Response
|
||||||
{
|
{
|
||||||
|
// Récupérer les 5 dernières sorties
|
||||||
$latestSorties = $sortieRepository->findBy([], ['dateHeureDebut' => 'DESC'], 5);
|
$latestSorties = $sortieRepository->findBy([], ['dateHeureDebut' => 'DESC'], 5);
|
||||||
|
$token = $tokenStorage->getToken();
|
||||||
|
$userConnect = $token?->getUser();
|
||||||
$dateLimit = new \DateTime();
|
$dateLimit = new \DateTime();
|
||||||
$dateLimit->modify('-30 days'); // Date limite = il y a 30 jours
|
$dateLimit->modify('-30 days'); // Date limite = il y a 30 jours
|
||||||
|
|
||||||
@@ -32,6 +35,7 @@ class MainController extends AbstractController
|
|||||||
|
|
||||||
return $this->render('main/home.html.twig', [
|
return $this->render('main/home.html.twig', [
|
||||||
'lastSorties' => $latestSorties,
|
'lastSorties' => $latestSorties,
|
||||||
|
'profile' => $userConnect,
|
||||||
'pastEvents' => $pastEvents,
|
'pastEvents' => $pastEvents,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,13 +30,16 @@ class PasswordResetController extends AbstractController
|
|||||||
try{
|
try{
|
||||||
$email = $request->request->get('email');
|
$email = $request->request->get('email');
|
||||||
|
|
||||||
|
// Vérifier si l'email est valide
|
||||||
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||||
$this->addFlash('error', 'Adresse e-mail invalide.');
|
$this->addFlash('error', 'Adresse e-mail invalide.');
|
||||||
return $this->redirectToRoute('app_login');
|
return $this->redirectToRoute('app_login');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Générer un token unique
|
||||||
$token = bin2hex(random_bytes(32));
|
$token = bin2hex(random_bytes(32));
|
||||||
|
|
||||||
|
// Enregistrer le token dans la base de données
|
||||||
$passwordResetToken = new PasswordResetToken();
|
$passwordResetToken = new PasswordResetToken();
|
||||||
$passwordResetToken->setToken($token)
|
$passwordResetToken->setToken($token)
|
||||||
->setEmail($email)
|
->setEmail($email)
|
||||||
@@ -52,6 +55,7 @@ class PasswordResetController extends AbstractController
|
|||||||
UrlGeneratorInterface::ABSOLUTE_URL
|
UrlGeneratorInterface::ABSOLUTE_URL
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Envoyer l'email avec le lien
|
||||||
$email = (new Email())
|
$email = (new Email())
|
||||||
->from('contact@sortir.com')
|
->from('contact@sortir.com')
|
||||||
->to($email)
|
->to($email)
|
||||||
@@ -75,6 +79,7 @@ class PasswordResetController extends AbstractController
|
|||||||
Request $request,
|
Request $request,
|
||||||
EntityManagerInterface $entityManager,
|
EntityManagerInterface $entityManager,
|
||||||
): Response {
|
): Response {
|
||||||
|
// Vérifier si le token existe
|
||||||
$repository = $entityManager->getRepository(PasswordResetToken::class);
|
$repository = $entityManager->getRepository(PasswordResetToken::class);
|
||||||
$passwordResetToken = $repository->findOneBy(['token' => $token]);
|
$passwordResetToken = $repository->findOneBy(['token' => $token]);
|
||||||
|
|
||||||
@@ -83,11 +88,14 @@ class PasswordResetController extends AbstractController
|
|||||||
return $this->redirectToRoute('app_login');
|
return $this->redirectToRoute('app_login');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Si la requête est POST, traiter le nouveau mot de passe
|
||||||
if ($request->isMethod('POST')) {
|
if ($request->isMethod('POST')) {
|
||||||
$newPassword = $request->request->get('password');
|
$newPassword = $request->request->get('password');
|
||||||
$newPasswordConfirm = $request->request->get('passwordConfirm');
|
$newPasswordConfirm = $request->request->get('passwordConfirm');
|
||||||
|
|
||||||
|
// Vérifier si les mots de passe sont identiques
|
||||||
if ($newPassword !== $newPasswordConfirm) {
|
if ($newPassword !== $newPasswordConfirm) {
|
||||||
|
// Les mots de passe ne correspondent pas
|
||||||
$this->addFlash('error', 'Les mots de passe ne correspondent pas.');
|
$this->addFlash('error', 'Les mots de passe ne correspondent pas.');
|
||||||
return $this->redirectToRoute('app_password_reset'); // Vous pouvez rediriger vers la page de réinitialisation
|
return $this->redirectToRoute('app_password_reset'); // Vous pouvez rediriger vers la page de réinitialisation
|
||||||
}
|
}
|
||||||
@@ -97,6 +105,7 @@ class PasswordResetController extends AbstractController
|
|||||||
return $this->redirectToRoute('app_password_reset', ['token' => $token]);
|
return $this->redirectToRoute('app_password_reset', ['token' => $token]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Récupérer l'utilisateur et mettre à jour le mot de passe
|
||||||
$user = $entityManager->getRepository(Participant::class)->findOneBy(['email' => $passwordResetToken->getEmail()]);
|
$user = $entityManager->getRepository(Participant::class)->findOneBy(['email' => $passwordResetToken->getEmail()]);
|
||||||
$user->setPassword(password_hash($newPassword, PASSWORD_BCRYPT));
|
$user->setPassword(password_hash($newPassword, PASSWORD_BCRYPT));
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ class RegistrationController extends AbstractController
|
|||||||
/** @var string $plainPassword */
|
/** @var string $plainPassword */
|
||||||
$plainPassword = $form->get('plainPassword')->getData();
|
$plainPassword = $form->get('plainPassword')->getData();
|
||||||
|
|
||||||
|
// encode the plain password
|
||||||
$user->setPassword($userPasswordHasher->hashPassword($user, $plainPassword));
|
$user->setPassword($userPasswordHasher->hashPassword($user, $plainPassword));
|
||||||
$user->setRoles(['ROLE_USER']);
|
$user->setRoles(['ROLE_USER']);
|
||||||
$user->setActif(false);
|
$user->setActif(false);
|
||||||
@@ -46,6 +47,7 @@ class RegistrationController extends AbstractController
|
|||||||
return $this->redirectToRoute('app_register');
|
return $this->redirectToRoute('app_register');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// do anything else you need here, like send an email
|
||||||
|
|
||||||
return $this->render('main/inscription.html.twig', ['fullName' => $fullName]);
|
return $this->render('main/inscription.html.twig', ['fullName' => $fullName]);
|
||||||
// return $security->login($user, 'form_login', 'main');
|
// return $security->login($user, 'form_login', 'main');
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ use App\Repository\EtatRepository;
|
|||||||
use App\Repository\LieuRepository;
|
use App\Repository\LieuRepository;
|
||||||
use App\Repository\ParticipantRepository;
|
use App\Repository\ParticipantRepository;
|
||||||
use App\Repository\SortieRepository;
|
use App\Repository\SortieRepository;
|
||||||
use App\Service\FileUploader;
|
|
||||||
use DateTime;
|
use DateTime;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use OpenAI\Factory;
|
use OpenAI\Factory;
|
||||||
@@ -33,6 +32,7 @@ class SortieController extends AbstractController
|
|||||||
$token = $tokenStorage->getToken();
|
$token = $tokenStorage->getToken();
|
||||||
$userConnect = $token?->getUser();
|
$userConnect = $token?->getUser();
|
||||||
|
|
||||||
|
// Récupérer les paramètres de filtre
|
||||||
$search = $request->query->get('search', '');
|
$search = $request->query->get('search', '');
|
||||||
$siteId = $request->query->get('site', '');
|
$siteId = $request->query->get('site', '');
|
||||||
$startDate = $request->query->get('start_date', '');
|
$startDate = $request->query->get('start_date', '');
|
||||||
@@ -42,6 +42,7 @@ class SortieController extends AbstractController
|
|||||||
$nonInscrit = $request->query->get('non_inscrit', false);
|
$nonInscrit = $request->query->get('non_inscrit', false);
|
||||||
$passees = $request->query->get('passees', false);
|
$passees = $request->query->get('passees', false);
|
||||||
|
|
||||||
|
// Recherche par nom de sortie
|
||||||
$sorties = $sortieRepository->findWithFilters($search, $siteId, $startDate, $endDate, $organisateur, $inscrit, $nonInscrit, $passees, $userConnect);
|
$sorties = $sortieRepository->findWithFilters($search, $siteId, $startDate, $endDate, $organisateur, $inscrit, $nonInscrit, $passees, $userConnect);
|
||||||
|
|
||||||
return $this->render('sortie/list.html.twig', [
|
return $this->render('sortie/list.html.twig', [
|
||||||
@@ -61,6 +62,7 @@ class SortieController extends AbstractController
|
|||||||
ParticipantRepository $participantRepository,
|
ParticipantRepository $participantRepository,
|
||||||
EtatRepository $etatRepository
|
EtatRepository $etatRepository
|
||||||
): Response {
|
): Response {
|
||||||
|
// Vérifier si l'utilisateur est connecté
|
||||||
$userConnect = $tokenStorage->getToken()?->getUser();
|
$userConnect = $tokenStorage->getToken()?->getUser();
|
||||||
if (!$userConnect) {
|
if (!$userConnect) {
|
||||||
$this->addFlash('error', 'Vous devez être connecté pour créer une sortie.');
|
$this->addFlash('error', 'Vous devez être connecté pour créer une sortie.');
|
||||||
@@ -79,17 +81,20 @@ class SortieController extends AbstractController
|
|||||||
return $this->redirectToRoute('sortie_create');
|
return $this->redirectToRoute('sortie_create');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Vérifier le participant (organisateur)
|
||||||
$participant = $participantRepository->find($userConnect->getIdParticipant());
|
$participant = $participantRepository->find($userConnect->getIdParticipant());
|
||||||
if (!$participant) {
|
if (!$participant) {
|
||||||
$this->addFlash('error', 'Impossible de trouver votre profil.');
|
$this->addFlash('error', 'Impossible de trouver votre profil.');
|
||||||
return $this->redirectToRoute('sortie_create');
|
return $this->redirectToRoute('sortie_create');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Gérer l'image (upload)
|
||||||
$imageFile = $form->get('imageFile')->getData();
|
$imageFile = $form->get('imageFile')->getData();
|
||||||
if ($imageFile) {
|
if ($imageFile) {
|
||||||
$sortie->setImageFile($imageFile);
|
$sortie->setImageFile($imageFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Définir les relations et l'état initial
|
||||||
$etat = $etatRepository->findOneBy(['libelle' => 'Créée']);
|
$etat = $etatRepository->findOneBy(['libelle' => 'Créée']);
|
||||||
if (!$etat) {
|
if (!$etat) {
|
||||||
$this->addFlash('error', 'Erreur interne : état introuvable.');
|
$this->addFlash('error', 'Erreur interne : état introuvable.');
|
||||||
@@ -105,10 +110,11 @@ class SortieController extends AbstractController
|
|||||||
$entityManager->flush();
|
$entityManager->flush();
|
||||||
|
|
||||||
$this->addFlash('success', 'La sortie a été créée avec succès.');
|
$this->addFlash('success', 'La sortie a été créée avec succès.');
|
||||||
return $this->redirectToRoute('home');
|
return $this->redirectToRoute('sortie_view', ['id'=> $sortie->getIdSortie()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->render('sortie/create.html.twig', [
|
return $this->render('sortie/create.html.twig', [
|
||||||
|
'profile' => $userConnect,
|
||||||
'form' => $form->createView(),
|
'form' => $form->createView(),
|
||||||
'sortie' => $sortie,
|
'sortie' => $sortie,
|
||||||
]);
|
]);
|
||||||
@@ -118,6 +124,7 @@ class SortieController extends AbstractController
|
|||||||
public function view(
|
public function view(
|
||||||
string $id,
|
string $id,
|
||||||
EntityManagerInterface $entityManager,
|
EntityManagerInterface $entityManager,
|
||||||
|
TokenStorageInterface $tokenStorage
|
||||||
): Response {
|
): Response {
|
||||||
$sortie = $entityManager->getRepository(Sortie::class)->find($id);
|
$sortie = $entityManager->getRepository(Sortie::class)->find($id);
|
||||||
if (!$sortie) {
|
if (!$sortie) {
|
||||||
@@ -129,8 +136,17 @@ class SortieController extends AbstractController
|
|||||||
return $this->redirectToRoute('home');
|
return $this->redirectToRoute('home');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Changer l'état si date de début commencé.
|
||||||
|
if($sortie->getDateLimiteInscription() < new DateTime()){
|
||||||
|
$sortie->setEtat($entityManager->getRepository(Etat::class)->findOneBy(['libelle' => 'Clôturée']));
|
||||||
|
}
|
||||||
|
if ($sortie->getDateHeureDebut() <= (new DateTime())) {
|
||||||
|
$sortie->setEtat($entityManager->getRepository(Etat::class)->findOneBy(['libelle' => 'En cours']));
|
||||||
|
}
|
||||||
|
|
||||||
return $this->render('sortie/view.html.twig', [
|
return $this->render('sortie/view.html.twig', [
|
||||||
'sortie' => $sortie,
|
'sortie' => $sortie,
|
||||||
|
'profile' => $tokenStorage->getToken()?->getUser(),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -224,17 +240,10 @@ class SortieController extends AbstractController
|
|||||||
$form->handleRequest($request);
|
$form->handleRequest($request);
|
||||||
|
|
||||||
if ($form->isSubmitted() && $form->isValid()) {
|
if ($form->isSubmitted() && $form->isValid()) {
|
||||||
// Vérification et mise à jour du lieu
|
|
||||||
$lieu = $form->get('lieu')->getData();
|
$lieu = $form->get('lieu')->getData();
|
||||||
if ($lieu) {
|
if ($lieu) {
|
||||||
$sortie->setLieu($lieu);
|
$sortie->setLieu($lieu);
|
||||||
}
|
}
|
||||||
|
|
||||||
$imageFile = $form->get('imageFile')->getData();
|
|
||||||
if ($imageFile) {
|
|
||||||
$sortie->setImageFile($imageFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
$entityManager->flush();
|
$entityManager->flush();
|
||||||
|
|
||||||
$this->addFlash('success', 'Les modifications ont été enregistrées.');
|
$this->addFlash('success', 'Les modifications ont été enregistrées.');
|
||||||
@@ -279,4 +288,22 @@ class SortieController extends AbstractController
|
|||||||
return $this->redirectToRoute('home');
|
return $this->redirectToRoute('home');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[Route('/confirm/{id}', name: 'confirm', methods: ['POST'])]
|
||||||
|
public function confirm(string $id, EntityManagerInterface $entityManager, TokenStorageInterface $tokenStorage)
|
||||||
|
{
|
||||||
|
$user = $tokenStorage->getToken()?->getUser();
|
||||||
|
$sortie = $entityManager->getRepository(Sortie::class)->find($id);
|
||||||
|
|
||||||
|
if (!$sortie || $sortie->getOrganisateur()->getIdParticipant() !== $user->getIdParticipant()) {
|
||||||
|
$this->addFlash('error', 'Vous n\'avez pas l\'autorisation de démarrer les inscriptions pour cette sortie.');
|
||||||
|
return $this->redirectToRoute('sortie_view', ['id' => $id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$sortie->setEtat($entityManager->getRepository(Etat::class)->findOneBy(['libelle' => 'Ouverte']));
|
||||||
|
$entityManager->flush();
|
||||||
|
|
||||||
|
$this->addFlash('success', 'Les inscriptions sont ouvertes pour cette sortie');
|
||||||
|
return $this->redirectToRoute('sortie_view', ['id' => $id]);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,40 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\EventListener;
|
|
||||||
|
|
||||||
use Symfony\Component\HttpKernel\Event\RequestEvent;
|
|
||||||
use Symfony\Component\HttpKernel\KernelEvents;
|
|
||||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
|
||||||
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
|
|
||||||
use Twig\Environment;
|
|
||||||
|
|
||||||
class UserProfileListener implements EventSubscriberInterface
|
|
||||||
{
|
|
||||||
private $tokenStorage;
|
|
||||||
private $twig;
|
|
||||||
|
|
||||||
public function __construct(TokenStorageInterface $tokenStorage, Environment $twig)
|
|
||||||
{
|
|
||||||
$this->tokenStorage = $tokenStorage;
|
|
||||||
$this->twig = $twig;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function onKernelRequest(RequestEvent $event)
|
|
||||||
{
|
|
||||||
$token = $this->tokenStorage->getToken();
|
|
||||||
|
|
||||||
if ($token && $token->getUser()) {
|
|
||||||
$profile = $token->getUser();
|
|
||||||
$event->getRequest()->attributes->set('profile', $profile);
|
|
||||||
|
|
||||||
$this->twig->addGlobal('profile', $profile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getSubscribedEvents()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
KernelEvents::REQUEST => 'onKernelRequest',
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
<!-- Actions: Ajouter -->
|
<!-- Actions: Ajouter -->
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
<!-- Bouton pour ouvrir la modale -->
|
<!-- Bouton pour ouvrir la modale -->
|
||||||
<button id="openModal" class="btnPrimary">
|
<button id="openModal" class="inline-block bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-700">
|
||||||
Ajouter un site
|
Ajouter un site
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
<div class="flex flex-row mb-4 justify-between">
|
<div class="flex flex-row mb-4 justify-between">
|
||||||
<button
|
<button
|
||||||
id="openModal"
|
id="openModal"
|
||||||
class="btnPrimary">
|
class="bg-green-600 text-white px-4 py-2 rounded hover:bg-green-700 items-end justify-end">
|
||||||
Ajouter un utilisateur
|
Ajouter un utilisateur
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -24,10 +24,10 @@
|
|||||||
<div class="flex flex-row">
|
<div class="flex flex-row">
|
||||||
<button
|
<button
|
||||||
id="openModalCsv" type="button"
|
id="openModalCsv" type="button"
|
||||||
class="btnSecondary">
|
class="bg-green-500 text-white mb-4 mr-4 px-4 py-2 rounded hover:bg-green-600">
|
||||||
Importer CSV
|
Importer CSV
|
||||||
</button>
|
</button>
|
||||||
<button type="submit" class="btnThird">
|
<button type="submit" class="bg-blue-500 text-white px-4 py-2 mb-4 rounded hover:bg-blue-600">
|
||||||
<a href="{{ path('participant_export') }}">Exporter CSV</a>
|
<a href="{{ path('participant_export') }}">Exporter CSV</a>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -177,7 +177,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<p>* Champ obligatoire</p>
|
<p>* Champ obligatoire</p>
|
||||||
<div class="flex justify-end">
|
<div class="flex justify-end">
|
||||||
<button type="submit" class="btnPrimary">Ajouter</button>
|
<button type="submit" class="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-700">Ajouter</button>
|
||||||
<button type="button" id="closeModal" class="ml-2 bg-gray-500 text-white px-4 py-2 rounded hover:bg-gray-700">Annuler</button>
|
<button type="button" id="closeModal" class="ml-2 bg-gray-500 text-white px-4 py-2 rounded hover:bg-gray-700">Annuler</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
@@ -205,7 +205,7 @@
|
|||||||
<div class="flex flex-row">
|
<div class="flex flex-row">
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
class="btnSecondary">
|
class="bg-green-500 text-white mr-4 px-4 py-2 rounded hover:bg-green-600">
|
||||||
Importer CSV
|
Importer CSV
|
||||||
</button>
|
</button>
|
||||||
<button type="button" id="closeModalCsv" class="ml-2 bg-gray-500 text-white px-4 py-2 rounded hover:bg-gray-700">Annuler</button>
|
<button type="button" id="closeModalCsv" class="ml-2 bg-gray-500 text-white px-4 py-2 rounded hover:bg-gray-700">Annuler</button>
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
{% extends 'main/base.html.twig' %}
|
{% extends 'main/base.html.twig' %}
|
||||||
|
|
||||||
{% block head %}
|
{% block head %}
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
@@ -17,29 +16,28 @@
|
|||||||
<form action="{{ path('app_login') }}" method="post">
|
<form action="{{ path('app_login') }}" method="post">
|
||||||
<label class="text-gray-700 font-bold" for="username">Email ou pseudo</label>
|
<label class="text-gray-700 font-bold" for="username">Email ou pseudo</label>
|
||||||
<input class="w-full mb-4 px-4 py-2 border-2 border-gray-300 rounded-lg focus:outline-none focus:border-blue-500" placeholder="Adresse e-mail ou pseudo" type="text" value="{{ last_username }}" name="username" id="username" autocomplete="email" required autofocus>
|
<input class="w-full mb-4 px-4 py-2 border-2 border-gray-300 rounded-lg focus:outline-none focus:border-blue-500" placeholder="Adresse e-mail ou pseudo" type="text" value="{{ last_username }}" name="username" id="username" autocomplete="email" required autofocus>
|
||||||
|
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
<label class="text-gray-700 font-bold" for="password">Mot de passe</label>
|
<label class="text-gray-700 font-bold" for="password">Mot de passe</label>
|
||||||
<input class="w-full mb-2 px-4 py-2 border-2 border-gray-300 rounded-lg focus:outline-none focus:border-blue-500" placeholder="Mot de passe" type="password" name="password" id="password" autocomplete="current-password" required>
|
<input class="w-full mb-2 px-4 py-2 border-2 border-gray-300 rounded-lg focus:outline-none focus:border-blue-500" placeholder="Mot de passe" type="password" name="password" id="password" autocomplete="current-password" required>
|
||||||
<a id="openModal" class="text-blue-500 text-sm hover:underline">Mot de passe oublié ?</a>
|
<a id="openModal" class="text-blue-500 text-sm hover:underline">Mot de passe oublié ?</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" name="_remember_me" checked>
|
<input type="checkbox" name="_remember_me" checked>
|
||||||
Se souvenir de moi
|
Se souvenir de moi
|
||||||
</label>
|
</label>
|
||||||
|
<input type="hidden" name="_csrf_token"
|
||||||
<input type="hidden" name="_csrf_token" value="{{ csrf_token('authenticate') }}">
|
value="{{ csrf_token('authenticate') }}"
|
||||||
|
>
|
||||||
<div class="flex flex-row justify-between">
|
<div class="flex flex-row justify-between">
|
||||||
<button class="btnPrimary" type="submit">Se connecter</button>
|
<button class=" text-white font-bold py-2 px-4 border-b-4 btnRegister rounded" type="submit">Se connecter</button>
|
||||||
<a href="{{ path('app_register') }}">
|
<a href="{{ path('app_register') }}">
|
||||||
<button class="btnSecondary" type="button">S'inscrire</button>
|
<button class=" text-white font-bold py-2 px-4 border-b-4 btnRegister rounded mx-auto" type="button">S'inscrire</button>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Modale pour ajouter une ville -->
|
||||||
<div id="resetModal" class="fixed inset-0 z-50 hidden bg-gray-900 bg-opacity-50">
|
<div id="resetModal" class="fixed inset-0 z-50 hidden bg-gray-900 bg-opacity-50">
|
||||||
<div class="flex justify-center items-center min-h-screen">
|
<div class="flex justify-center items-center min-h-screen">
|
||||||
<div class="bg-white p-6 rounded shadow-md w-1/3">
|
<div class="bg-white p-6 rounded shadow-md w-1/3">
|
||||||
@@ -51,8 +49,8 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex justify-end">
|
<div class="flex justify-end">
|
||||||
<button type="submit" class="btnPrimary">Envoyer</button>
|
<button type="submit" class="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-700">Envoyer</button>
|
||||||
<button type="button" id="closeModal" class="btnAnnule ml-2">Annuler</button>
|
<button type="button" id="closeModal" class="ml-2 bg-gray-500 text-white px-4 py-2 rounded hover:bg-gray-700">Annuler</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
@@ -60,13 +58,16 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
// Ouvrir la modale
|
||||||
document.getElementById('openModal').addEventListener('click', function() {
|
document.getElementById('openModal').addEventListener('click', function() {
|
||||||
document.getElementById('resetModal').classList.remove('hidden');
|
document.getElementById('resetModal').classList.remove('hidden');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Fermer la modale
|
||||||
document.getElementById('closeModal').addEventListener('click', function() {
|
document.getElementById('closeModal').addEventListener('click', function() {
|
||||||
document.getElementById('resetModal').classList.add('hidden');
|
document.getElementById('resetModal').classList.add('hidden');
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
{{ form_row(registrationForm.email) }}
|
{{ form_row(registrationForm.email) }}
|
||||||
{{ form_row(registrationForm.pseudo) }}
|
{{ form_row(registrationForm.pseudo) }}
|
||||||
{{ form_row(registrationForm.plainPassword) }}
|
{{ form_row(registrationForm.plainPassword) }}
|
||||||
<button class="btnSecondary" type="submit">S'inscrire</button>
|
<button class="btnRegister text-white font-bold py-2 px-4 border-b-4 rounded mx-auto" type="submit">S'inscrire</button>
|
||||||
{{ form_end(registrationForm) }}
|
{{ form_end(registrationForm) }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -14,11 +14,13 @@
|
|||||||
</head>
|
</head>
|
||||||
<body class="bg-gray-100">
|
<body class="bg-gray-100">
|
||||||
<nav class="flex justify-between items-center py-4 px-6 shadow-md bg-gradient-to-r from-[#2A8D57] via-[#008167] to-[#1B5667]">
|
<nav class="flex justify-between items-center py-4 px-6 shadow-md bg-gradient-to-r from-[#2A8D57] via-[#008167] to-[#1B5667]">
|
||||||
|
<!-- Logo et Titre -->
|
||||||
<a href="/" class="flex items-center space-x-3">
|
<a href="/" class="flex items-center space-x-3">
|
||||||
<img src="{{ asset('img/logo.svg') }}" alt="Logo" class="h-10">
|
<img src="{{ asset('img/logo.svg') }}" alt="Logo" class="h-10">
|
||||||
<h3 class="text-3xl text-white font-bold">Sortie</h3>
|
<h3 class="text-3xl text-white font-bold">Sortie</h3>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
<!-- Navigation principale -->
|
||||||
<ul class="hidden md:flex space-x-6 text-white font-medium">
|
<ul class="hidden md:flex space-x-6 text-white font-medium">
|
||||||
<li>
|
<li>
|
||||||
<a href="{{ path('home') }}"
|
<a href="{{ path('home') }}"
|
||||||
@@ -40,6 +42,7 @@
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
<!-- Profil utilisateur -->
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
{% if app.user %}
|
{% if app.user %}
|
||||||
<button id="profile-menu" class="flex items-center space-x-2 focus:outline-none">
|
<button id="profile-menu" class="flex items-center space-x-2 focus:outline-none">
|
||||||
@@ -66,6 +69,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Menu mobile -->
|
||||||
<button id="mobile-menu-button" class="md:hidden text-white focus:outline-none">
|
<button id="mobile-menu-button" class="md:hidden text-white focus:outline-none">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
|
||||||
@@ -73,6 +77,7 @@
|
|||||||
</button>
|
</button>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
|
<!-- Menu mobile (hidden par défaut) -->
|
||||||
<div id="mobile-menu" class="hidden bg-gradient-to-r from-[#2A8D57] via-[#008167] to-[#1B5667] text-white py-4 md:hidden">
|
<div id="mobile-menu" class="hidden bg-gradient-to-r from-[#2A8D57] via-[#008167] to-[#1B5667] text-white py-4 md:hidden">
|
||||||
<ul class="space-y-4 px-6">
|
<ul class="space-y-4 px-6">
|
||||||
<li><a href="{{ path('home') }}" class="block hover:text-gray-200">Accueil</a></li>
|
<li><a href="{{ path('home') }}" class="block hover:text-gray-200">Accueil</a></li>
|
||||||
@@ -84,7 +89,9 @@
|
|||||||
{% block content %}
|
{% block content %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
<!-- Scripts -->
|
||||||
<script>
|
<script>
|
||||||
|
// Toggle menu pour mobile
|
||||||
const mobileMenuButton = document.getElementById('mobile-menu-button');
|
const mobileMenuButton = document.getElementById('mobile-menu-button');
|
||||||
const mobileMenu = document.getElementById('mobile-menu');
|
const mobileMenu = document.getElementById('mobile-menu');
|
||||||
|
|
||||||
@@ -92,6 +99,7 @@
|
|||||||
mobileMenu.classList.toggle('hidden');
|
mobileMenu.classList.toggle('hidden');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Toggle menu déroulant pour le profil
|
||||||
const profileMenuButton = document.getElementById('profile-menu');
|
const profileMenuButton = document.getElementById('profile-menu');
|
||||||
const dropdown = document.getElementById('dropdown');
|
const dropdown = document.getElementById('dropdown');
|
||||||
|
|
||||||
@@ -101,6 +109,7 @@
|
|||||||
dropdown.classList.toggle('hidden');
|
dropdown.classList.toggle('hidden');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Fermer le menu lorsqu'on clique ailleurs
|
||||||
document.addEventListener('click', (event) => {
|
document.addEventListener('click', (event) => {
|
||||||
if (!dropdown.classList.contains('hidden')) {
|
if (!dropdown.classList.contains('hidden')) {
|
||||||
const isClickInsideMenu = dropdown.contains(event.target) || profileMenuButton.contains(event.target);
|
const isClickInsideMenu = dropdown.contains(event.target) || profileMenuButton.contains(event.target);
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
<style>
|
<style>
|
||||||
.swiper-container {
|
.swiper-container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 500px;
|
height: 500px; /* Ajustez selon vos besoins */
|
||||||
}
|
}
|
||||||
|
|
||||||
.swiper-slide {
|
.swiper-slide {
|
||||||
@@ -56,7 +56,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.swiper-pagination-bullet-active {
|
.swiper-pagination-bullet-active {
|
||||||
background: #16a34a;
|
background: #16a34a; /* Vert plus foncé pour le point actif */
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@@ -65,6 +65,7 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="container mx-auto py-10">
|
<div class="container mx-auto py-10">
|
||||||
|
<!-- Section carrousel -->
|
||||||
<h2 class="text-4xl font-bold text-center mb-8">Dernières sorties</h2>
|
<h2 class="text-4xl font-bold text-center mb-8">Dernières sorties</h2>
|
||||||
<div class="w-full relative">
|
<div class="w-full relative">
|
||||||
<div class="swiper default-carousel swiper-container">
|
<div class="swiper default-carousel swiper-container">
|
||||||
@@ -98,6 +99,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Section des événements terminés -->
|
||||||
<div class="mt-12">
|
<div class="mt-12">
|
||||||
<h2 class="text-3xl font-bold text-center mb-6">Événements terminés</h2>
|
<h2 class="text-3xl font-bold text-center mb-6">Événements terminés</h2>
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||||
@@ -121,6 +123,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Inclure les fichiers JavaScript Swiper -->
|
||||||
{% block javascripts %}
|
{% block javascripts %}
|
||||||
<script src="https://unpkg.com/swiper/swiper-bundle.min.js"></script>
|
<script src="https://unpkg.com/swiper/swiper-bundle.min.js"></script>
|
||||||
<script>
|
<script>
|
||||||
|
|||||||
@@ -44,7 +44,7 @@
|
|||||||
{{ form_row(formProfile.deleteImage) }}
|
{{ form_row(formProfile.deleteImage) }}
|
||||||
<img class="mx-auto justify-items-center" alt="image de profile" src="{{ asset('/upload/image/profile/' ~ formProfile.vars.value.fileName) }}" height="128" width="128">
|
<img class="mx-auto justify-items-center" alt="image de profile" src="{{ asset('/upload/image/profile/' ~ formProfile.vars.value.fileName) }}" height="128" width="128">
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<button class="btnPrimary" type="submit">Actualiser les informations</button>
|
<button class="btnRegister text-white font-bold py-2 px-4 border-b-4 rounded mx-auto" type="submit">Actualiser les informations</button>
|
||||||
{{ form_end(formProfile) }}
|
{{ form_end(formProfile) }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,4 +1,13 @@
|
|||||||
{% extends 'main/base.html.twig' %}
|
{% extends 'main/base.html.twig' %}
|
||||||
|
{% block head %}
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
{% block title %}📣 Sortie.com {{ profile.pseudo }} 🔊{% endblock %}
|
||||||
|
{% block stylesheets %}
|
||||||
|
{{ encore_entry_link_tags('app') }}
|
||||||
|
{% endblock %}
|
||||||
|
</head>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="relative mx-auto md:max-w-xl pt-24 pb-8 min-w-0 break-words">
|
<div class="relative mx-auto md:max-w-xl pt-24 pb-8 min-w-0 break-words">
|
||||||
@@ -26,7 +35,7 @@
|
|||||||
<div class="flex flex-wrap justify-center">
|
<div class="flex flex-wrap justify-center">
|
||||||
<div class="w-full px-4">
|
<div class="w-full px-4">
|
||||||
<a href="{{ path('profile_edit', {'uuid': app.user.idParticipant}) }}" class="text-slate-700 hover:text-slate-400">
|
<a href="{{ path('profile_edit', {'uuid': app.user.idParticipant}) }}" class="text-slate-700 hover:text-slate-400">
|
||||||
<button class="btnThird">
|
<button class="bg-green-500 px-4 py-2 rounded hover:bg-green-700">
|
||||||
Modifier
|
Modifier
|
||||||
</button>
|
</button>
|
||||||
</a>
|
</a>
|
||||||
@@ -37,9 +46,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex flex-col lg:flex-row mx-8 gap-6">
|
<div class="flex flex-row flex-wrap mx-8">
|
||||||
{# Tableau des sorties dont je suis participant#}
|
{# Tableau des sorties dont je suis participant#}
|
||||||
<div class="w-full lg:w-1/2 p-4 shadow-lg rounded-lg overflow-x-auto">
|
<div class="w-1/2 p-4 shadow-lg rounded-lg">
|
||||||
<h4 class="text-start bg-gray-50 items-start justify-start text-base p-2 font-bold uppercase">Mes participations</h4>
|
<h4 class="text-start bg-gray-50 items-start justify-start text-base p-2 font-bold uppercase">Mes participations</h4>
|
||||||
<table class="min-w-full bg-white divide-y divide-gray-200">
|
<table class="min-w-full bg-white divide-y divide-gray-200">
|
||||||
<thead class="bg-gray-50">
|
<thead class="bg-gray-50">
|
||||||
@@ -61,7 +70,7 @@
|
|||||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">{{ sortie.dateHeureDebut|date('d/m/Y H:i') }}</td>
|
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">{{ sortie.dateHeureDebut|date('d/m/Y H:i') }}</td>
|
||||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">{{ sortie.duree }}</td>
|
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">{{ sortie.duree }}</td>
|
||||||
<td class="flex flex-row px-6 py-4 whitespace-nowrap text-base text-gray-900">
|
<td class="flex flex-row px-6 py-4 whitespace-nowrap text-base text-gray-900">
|
||||||
<a href="{{ path('sortie_view', { id: sortie.idSortie }) }}" class="text-green-500">👁️</a>
|
<a href={{ path('sortie_view', { id: sortie.idSortie }) }}>👁️</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% else %}
|
{% else %}
|
||||||
@@ -74,7 +83,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{# Tableau des sorties dont je suis organisateur #}
|
{# Tableau des sorties dont je suis organisateur #}
|
||||||
<div class="w-full lg:w-1/2 p-4 shadow-lg rounded-lg overflow-x-auto">
|
<div class="w-1/2 p-4 shadow-lg rounded-lg">
|
||||||
<h4 class="text-start bg-gray-50 items-start justify-start text-base p-2 font-bold uppercase">Mes organisations</h4>
|
<h4 class="text-start bg-gray-50 items-start justify-start text-base p-2 font-bold uppercase">Mes organisations</h4>
|
||||||
<table class="min-w-full bg-white divide-y divide-gray-200">
|
<table class="min-w-full bg-white divide-y divide-gray-200">
|
||||||
<thead class="bg-gray-50">
|
<thead class="bg-gray-50">
|
||||||
@@ -96,7 +105,7 @@
|
|||||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">{{ sortie.dateHeureDebut|date('d/m/Y H:i') }}</td>
|
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">{{ sortie.dateHeureDebut|date('d/m/Y H:i') }}</td>
|
||||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">{{ sortie.duree }}</td>
|
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">{{ sortie.duree }}</td>
|
||||||
<td class="flex flex-row px-6 py-4 whitespace-nowrap text-base text-gray-900">
|
<td class="flex flex-row px-6 py-4 whitespace-nowrap text-base text-gray-900">
|
||||||
<a href="{{ path('sortie_view', { id: sortie.idSortie }) }}" class="text-green-500">👁️</a>
|
<a href={{ path('sortie_view', { id: sortie.idSortie }) }}>👁️</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% else %}
|
{% else %}
|
||||||
|
|||||||
@@ -110,6 +110,9 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
<button type="button" id="generate-image" class="modern-button mt-2">
|
||||||
|
Générer une image
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -118,7 +121,10 @@
|
|||||||
<label for="sortie_lieu">Lieu</label>
|
<label for="sortie_lieu">Lieu</label>
|
||||||
<div class="flex items-center space-x-2">
|
<div class="flex items-center space-x-2">
|
||||||
{{ form_widget(form.lieu, { 'attr': { 'class': 'block w-full p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500', 'id': 'lieu-select' } }) }}
|
{{ form_widget(form.lieu, { 'attr': { 'class': 'block w-full p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500', 'id': 'lieu-select' } }) }}
|
||||||
<button type="button" id="add-lieu-button" class="btnThird">
|
<button type="button" id="add-lieu-button" class="modern-button">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" class="w-5 h-5">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4" />
|
||||||
|
</svg>
|
||||||
Ajouter
|
Ajouter
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -148,10 +154,10 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mt-6 flex justify-center space-x-4">
|
<div class="mt-6 flex justify-center space-x-4">
|
||||||
<button type="submit" name="action" value="save" class="btnPrimary">
|
<button type="submit" name="action" value="save" class="px-6 py-3 bg-green-500 text-white rounded-lg shadow hover:bg-green-600 focus:outline-none focus:ring-2 focus:ring-green-300">
|
||||||
Enregistrer
|
Enregistrer
|
||||||
</button>
|
</button>
|
||||||
<a href="{{ path('home') }}" class="btnAnnule">
|
<a href="{{ path('home') }}" class="px-6 py-3 bg-gray-500 text-white rounded-lg shadow hover:bg-gray-600 focus:outline-none focus:ring-2 focus:ring-gray-300">
|
||||||
Annuler
|
Annuler
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -70,51 +70,15 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="sortie_imageFile">Image de la sortie</label>
|
|
||||||
{{ form_widget(form.imageFile, { 'attr': { 'class': 'block w-full p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500', 'onchange': 'previewImage(event)' } }) }}
|
|
||||||
<small class="text-gray-500">Formats acceptés : JPG, PNG (max. 5 Mo).</small>
|
|
||||||
|
|
||||||
{% if sortie and sortie.image %}
|
|
||||||
<div class="mt-4">
|
|
||||||
<img id="image-preview" src="{{ asset('img/sortie/' ~ sortie.image) }}" alt="Image prévisualisée" class="max-w-xs h-auto rounded-lg border border-gray-300">
|
|
||||||
</div>
|
|
||||||
{% else %}
|
|
||||||
<div class="mt-4">
|
|
||||||
<img id="image-preview" src="" alt="Aucune image sélectionnée" class="max-w-xs h-auto rounded-lg border border-gray-300 hidden">
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex justify-end mt-8 space-x-4">
|
<div class="flex justify-end mt-8 space-x-4">
|
||||||
<a href="{{ path('sortie_view', { id: sortie.getIdSortie() }) }}" class="btnAnnule">
|
<a href="{{ path('sortie_view', { id: sortie.getIdSortie() }) }}" class="px-6 py-3 bg-gray-500 text-white rounded-lg shadow-md hover:bg-gray-600">
|
||||||
❌ Annuler
|
❌ Annuler
|
||||||
</a>
|
</a>
|
||||||
<button type="submit" class="btnPrimary">
|
<button type="submit" class="px-6 py-3 bg-blue-500 text-white rounded-lg shadow-md hover:bg-blue-600">
|
||||||
💾 Enregistrer
|
💾 Enregistrer
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{ form_end(form) }}
|
{{ form_end(form) }}
|
||||||
</div>
|
</div>
|
||||||
{% block javascripts %}
|
|
||||||
<script>
|
|
||||||
function previewImage(event) {
|
|
||||||
const input = event.target;
|
|
||||||
const preview = document.getElementById('image-preview');
|
|
||||||
|
|
||||||
if (input.files && input.files[0]) {
|
|
||||||
const reader = new FileReader();
|
|
||||||
reader.onload = function (e) {
|
|
||||||
preview.src = e.target.result;
|
|
||||||
preview.classList.remove('hidden');
|
|
||||||
};
|
|
||||||
reader.readAsDataURL(input.files[0]);
|
|
||||||
} else {
|
|
||||||
preview.src = '';
|
|
||||||
preview.classList.add('hidden');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
{% endblock %}
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@@ -31,14 +31,17 @@
|
|||||||
<div class="container mx-auto p-4 sm:p-6 bg-gray-50 rounded-lg shadow-lg mt-4 sm:mt-6">
|
<div class="container mx-auto p-4 sm:p-6 bg-gray-50 rounded-lg shadow-lg mt-4 sm:mt-6">
|
||||||
<h1 class="text-2xl sm:text-3xl font-bold text-center text-gray-800 mb-6 sm:mb-8">🎉 Liste des sorties</h1>
|
<h1 class="text-2xl sm:text-3xl font-bold text-center text-gray-800 mb-6 sm:mb-8">🎉 Liste des sorties</h1>
|
||||||
|
|
||||||
|
<!-- Formulaire de recherche -->
|
||||||
<form method="GET" action="{{ path('home') }}" class="bg-white rounded-lg shadow-md p-4 mb-6">
|
<form method="GET" action="{{ path('home') }}" class="bg-white rounded-lg shadow-md p-4 mb-6">
|
||||||
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 sm:gap-6">
|
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 sm:gap-6">
|
||||||
|
<!-- Recherche -->
|
||||||
<div>
|
<div>
|
||||||
<label for="search" class="block text-sm font-medium text-gray-700">🔍 Recherche</label>
|
<label for="search" class="block text-sm font-medium text-gray-700">🔍 Recherche</label>
|
||||||
<input type="text" name="search" id="search" value="{{ app.request.query.get('search', '') }}"
|
<input type="text" name="search" id="search" value="{{ app.request.query.get('search', '') }}"
|
||||||
class="block w-full mt-2 rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm"
|
class="block w-full mt-2 rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm"
|
||||||
placeholder="Nom de la sortie">
|
placeholder="Nom de la sortie">
|
||||||
</div>
|
</div>
|
||||||
|
<!-- Site -->
|
||||||
<div>
|
<div>
|
||||||
<label for="site" class="block text-sm font-medium text-gray-700">📍 Site</label>
|
<label for="site" class="block text-sm font-medium text-gray-700">📍 Site</label>
|
||||||
<select name="site" id="site" class="block w-full mt-2 rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm">
|
<select name="site" id="site" class="block w-full mt-2 rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm">
|
||||||
@@ -50,11 +53,13 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- Date de début -->
|
||||||
<div>
|
<div>
|
||||||
<label for="start_date" class="block text-sm font-medium text-gray-700">📅 Date de début</label>
|
<label for="start_date" class="block text-sm font-medium text-gray-700">📅 Date de début</label>
|
||||||
<input type="date" name="start_date" id="start_date" value="{{ app.request.query.get('start_date', '') }}"
|
<input type="date" name="start_date" id="start_date" value="{{ app.request.query.get('start_date', '') }}"
|
||||||
class="block w-full mt-2 rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm">
|
class="block w-full mt-2 rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm">
|
||||||
</div>
|
</div>
|
||||||
|
<!-- Date de fin -->
|
||||||
<div>
|
<div>
|
||||||
<label for="end_date" class="block text-sm font-medium text-gray-700">📅 Date de fin</label>
|
<label for="end_date" class="block text-sm font-medium text-gray-700">📅 Date de fin</label>
|
||||||
<input type="date" name="end_date" id="end_date" value="{{ app.request.query.get('end_date', '') }}"
|
<input type="date" name="end_date" id="end_date" value="{{ app.request.query.get('end_date', '') }}"
|
||||||
@@ -62,6 +67,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Options supplémentaires -->
|
||||||
<div class="mt-4 grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
|
<div class="mt-4 grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<input type="checkbox" name="organisateur" id="organisateur" value="1"
|
<input type="checkbox" name="organisateur" id="organisateur" value="1"
|
||||||
@@ -89,13 +95,15 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Bouton de filtrage -->
|
||||||
<div class="mt-6 flex justify-end">
|
<div class="mt-6 flex justify-end">
|
||||||
<button type="submit" class="btnPrimary">
|
<button type="submit" class="px-6 py-2 bg-blue-500 text-white rounded-md shadow hover:bg-blue-600">
|
||||||
🔎 Filtrer
|
🔎 Filtrer
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<!-- Liste des sorties -->
|
||||||
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4 sm:gap-6">
|
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4 sm:gap-6">
|
||||||
<a href="/sortie/creates" class="hidden sm:block">
|
<a href="/sortie/creates" class="hidden sm:block">
|
||||||
<div class="bg-white rounded-lg shadow-md p-4 text-center">
|
<div class="bg-white rounded-lg shadow-md p-4 text-center">
|
||||||
|
|||||||
@@ -11,6 +11,9 @@
|
|||||||
#delete-modal {
|
#delete-modal {
|
||||||
z-index: 1050;
|
z-index: 1050;
|
||||||
}
|
}
|
||||||
|
#confirm-modal {
|
||||||
|
z-index: 1050;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
</head>
|
</head>
|
||||||
@@ -74,10 +77,9 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
{% if app.user and sortie.etat.libelle == 'Ouverte' and not sortie.participants.contains(app.user) and date(sortie.dateLimiteInscription) > date() %}
|
{% if app.user and sortie.etat.libelle == 'Ouverte' and not sortie.participants.contains(app.user) and date(sortie.dateLimiteInscription) > date() %}
|
||||||
<form action="{{ path('sortie_inscription', { id: sortie.idSortie }) }}" method="post" class="mt-6">
|
<form action="{{ path('sortie_inscription', { id: sortie.idSortie }) }}" method="post" class="mt-6">
|
||||||
<button type="submit" class="btnPrimary">
|
<button type="submit" class="px-6 py-3 bg-green-500 text-white rounded-md shadow hover:bg-green-600">
|
||||||
✅ S'inscrire
|
✅ S'inscrire
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
@@ -86,7 +88,7 @@
|
|||||||
<p class="text-green-600 font-bold">✅ Vous êtes déjà inscrit à cette sortie.</p>
|
<p class="text-green-600 font-bold">✅ Vous êtes déjà inscrit à cette sortie.</p>
|
||||||
{% if sortie.etat.libelle == 'Ouverte' %}
|
{% if sortie.etat.libelle == 'Ouverte' %}
|
||||||
<form action="{{ path('sortie_unsubscribe', { id: sortie.idSortie }) }}" method="post" class="mt-6">
|
<form action="{{ path('sortie_unsubscribe', { id: sortie.idSortie }) }}" method="post" class="mt-6">
|
||||||
<button type="submit" class="btnAnnule">
|
<button type="submit" class="px-6 py-3 bg-red-500 text-white rounded-md shadow hover:bg-red-600">
|
||||||
❌ Se désister
|
❌ Se désister
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
@@ -94,18 +96,32 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if app.user and app.user.idParticipant == sortie.organisateur.idParticipant and sortie.etat.libelle == 'Créée' %}
|
{% if app.user and app.user.idParticipant == sortie.organisateur.idParticipant %}
|
||||||
<div class="mt-6 flex justify-end space-x-4">
|
<div class="mt-6 flex justify-end space-x-4">
|
||||||
|
{% if sortie.etat.libelle == 'Créée' or sortie.etat.libelle == 'Ouverte'%}
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
id="open-delete-modal"
|
id="open-delete-modal"
|
||||||
class="btnAnnule">
|
class="px-6 py-3 bg-red-500 text-white rounded-md shadow hover:bg-red-600">
|
||||||
❌ Annuler la sortie
|
❌ Annuler la sortie
|
||||||
</button>
|
</button>
|
||||||
|
{% endif %}
|
||||||
|
{% if sortie.etat.libelle == 'Créée' %}
|
||||||
<a href="{{ path('sortie_edit', { id: sortie.idSortie }) }}"
|
<a href="{{ path('sortie_edit', { id: sortie.idSortie }) }}"
|
||||||
class="btnSecondary">
|
class="px-6 py-3 bg-yellow-500 text-white rounded-md shadow hover:bg-yellow-600">
|
||||||
✏️ Modifier la sortie
|
✏️ Modifier la sortie
|
||||||
</a>
|
</a>
|
||||||
|
{# <a href="{{ path('sortie_open', { id: sortie.idSortie }) }}"#}
|
||||||
|
{# class="px-6 py-3 bg-yellow-500 text-white rounded-md shadow hover:bg-yellow-600">#}
|
||||||
|
{# 🚀 Démarrer les inscriptions#}
|
||||||
|
{# </a>#}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
id="open-confirm-modal"
|
||||||
|
class="px-6 py-3 bg-green-500 text-white rounded-md shadow hover:bg-green-600">
|
||||||
|
🚀 Démarrer les inscriptions
|
||||||
|
</button>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
@@ -154,7 +170,7 @@
|
|||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
class="btnAnnule">
|
class="px-4 py-2 bg-red-500 text-white rounded-md hover:bg-red-600">
|
||||||
Confirmer l'annulation
|
Confirmer l'annulation
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -162,6 +178,31 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{# Modale pour ouvrir les inscriptions d'une sortie#}
|
||||||
|
<div id="confirm-modal" class="fixed inset-0 flex items-center justify-center bg-gray-800 bg-opacity-50 hidden">
|
||||||
|
<div class="bg-white p-6 rounded-lg shadow-lg w-full max-w-lg">
|
||||||
|
<h2 class="text-xl font-bold mb-4">Ouvrir les inscriptions</h2>
|
||||||
|
<form action="{{ path('sortie_confirm', {id: sortie.idSortie}) }}" method="post">
|
||||||
|
<div class="mb-4">
|
||||||
|
<p class="text-base">La modification de cette sortie ne sera pu possible une fois les inscriptions ouverte</p>
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-end space-x-4">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
id="cancel-confirm-modal"
|
||||||
|
class="px-4 py-2 bg-gray-500 text-white rounded-md hover:bg-gray-600">
|
||||||
|
Annuler
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
class="px-4 py-2 bg-green-500 text-white rounded-md hover:bg-green-600">
|
||||||
|
Démarrer les inscriptions
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="flex justify-center mt-8">
|
<div class="flex justify-center mt-8">
|
||||||
<a href="{{ path('home') }}" class="px-6 py-3 bg-blue-500 text-white rounded-md shadow hover:bg-blue-600">
|
<a href="{{ path('home') }}" class="px-6 py-3 bg-blue-500 text-white rounded-md shadow hover:bg-blue-600">
|
||||||
🔙 Retour à l'accueil
|
🔙 Retour à l'accueil
|
||||||
@@ -192,16 +233,27 @@
|
|||||||
.openPopup();
|
.openPopup();
|
||||||
|
|
||||||
const openModalButton = document.getElementById('open-delete-modal');
|
const openModalButton = document.getElementById('open-delete-modal');
|
||||||
|
const openModalConfirm = document.getElementById('open-confirm-modal');
|
||||||
|
|
||||||
const cancelModalButton = document.getElementById('cancel-modal');
|
const cancelModalButton = document.getElementById('cancel-modal');
|
||||||
|
const cancelModalConfirm = document.getElementById('cancel-confirm-modal')
|
||||||
|
|
||||||
const deleteModal = document.getElementById('delete-modal');
|
const deleteModal = document.getElementById('delete-modal');
|
||||||
|
const confirmModal = document.getElementById('confirm-modal');
|
||||||
|
|
||||||
openModalButton?.addEventListener('click', () => {
|
openModalButton?.addEventListener('click', () => {
|
||||||
deleteModal.classList.remove('hidden');
|
deleteModal.classList.remove('hidden');
|
||||||
});
|
});
|
||||||
|
|
||||||
cancelModalButton?.addEventListener('click', () => {
|
cancelModalButton?.addEventListener('click', () => {
|
||||||
deleteModal.classList.add('hidden');
|
deleteModal.classList.add('hidden');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
openModalConfirm?.addEventListener('click', () => {
|
||||||
|
confirmModal.classList.remove('hidden');
|
||||||
|
})
|
||||||
|
cancelModalConfirm?.addEventListener('click', () => {
|
||||||
|
confirmModal.classList.add('hidden')
|
||||||
|
})
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||