From bfc2370d2e8d32b16eb9db8ecadbdd8a18babb3c Mon Sep 17 00:00:00 2001 From: Serreau Jovann Date: Sun, 23 Nov 2025 17:06:10 +0100 Subject: [PATCH] =?UTF-8?q?```=20=E2=9C=A8=20feat(security):=20Ajoute=20la?= =?UTF-8?q?=20route=20de=20d=C3=A9connexion=20et=20configure=20la=20redire?= =?UTF-8?q?ction.=20=E2=9C=A8=20feat(Dto/Ag):=20Cr=C3=A9e=20les=20DTOs=20A?= =?UTF-8?q?gType,=20AgMembersType=20et=20AgOrderType.=20=E2=9C=A8=20feat(C?= =?UTF-8?q?ontroller/Admin):=20Impl=C3=A9mente=20la=20gestion=20des=20AG?= =?UTF-8?q?=20(CRUD=20complet).=20=E2=9C=A8=20feat(templates/admin):=20Ajo?= =?UTF-8?q?ute=20les=20templates=20pour=20la=20gestion=20des=20AG.=20```?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/packages/security.yaml | 3 +- migrations/Version20251123150147.php | 39 ++++ migrations/Version20251123150400.php | 58 +++++ migrations/Version20251123150802.php | 35 +++ migrations/Version20251123150833.php | 36 ++++ migrations/Version20251123151054.php | 36 ++++ migrations/Version20251123152416.php | 32 +++ migrations/Version20251123160153.php | 35 +++ src/Controller/Admin/AdminController.php | 101 ++++++++- src/Controller/SecurityController.php | 5 + src/Dto/Ag/AgMembersType.php | 53 +++++ src/Dto/Ag/AgOrderType.php | 48 +++++ src/Dto/Ag/AgType.php | 70 ++++++ src/Entity/Ag/Main.php | 237 +++++++++++++++++++++ src/Entity/Ag/MainMember.php | 67 ++++++ src/Entity/Ag/MainOrder.php | 66 ++++++ src/Entity/Ag/MainVote.php | 82 +++++++ src/Entity/Members.php | 151 +++++++++++++ src/Repository/Ag/MainMemberRepository.php | 43 ++++ src/Repository/Ag/MainOrderRepository.php | 43 ++++ src/Repository/Ag/MainRepository.php | 43 ++++ src/Repository/Ag/MainVoteRepository.php | 43 ++++ src/Security/AuthenticationEntryPoint.php | 2 +- templates/admin/ag.twig | 79 +++++++ templates/admin/ag/edit.twig | 176 +++++++++++++++ templates/admin/ag/new.twig | 70 ++++++ templates/admin/base.twig | 14 +- templates/base.twig | 6 +- templates/form_admin.twig | 2 +- 29 files changed, 1653 insertions(+), 22 deletions(-) create mode 100644 migrations/Version20251123150147.php create mode 100644 migrations/Version20251123150400.php create mode 100644 migrations/Version20251123150802.php create mode 100644 migrations/Version20251123150833.php create mode 100644 migrations/Version20251123151054.php create mode 100644 migrations/Version20251123152416.php create mode 100644 migrations/Version20251123160153.php create mode 100644 src/Dto/Ag/AgMembersType.php create mode 100644 src/Dto/Ag/AgOrderType.php create mode 100644 src/Dto/Ag/AgType.php create mode 100644 src/Entity/Ag/Main.php create mode 100644 src/Entity/Ag/MainMember.php create mode 100644 src/Entity/Ag/MainOrder.php create mode 100644 src/Entity/Ag/MainVote.php create mode 100644 src/Repository/Ag/MainMemberRepository.php create mode 100644 src/Repository/Ag/MainOrderRepository.php create mode 100644 src/Repository/Ag/MainRepository.php create mode 100644 src/Repository/Ag/MainVoteRepository.php create mode 100644 templates/admin/ag.twig create mode 100644 templates/admin/ag/edit.twig create mode 100644 templates/admin/ag/new.twig diff --git a/config/packages/security.yaml b/config/packages/security.yaml index 48cd583..c93de53 100644 --- a/config/packages/security.yaml +++ b/config/packages/security.yaml @@ -27,7 +27,8 @@ security: entry_point: App\Security\AuthenticationEntryPoint custom_authenticator: - App\Security\LoginFormAuthenticator - + logout: + target: app_logout # Configuration des algorithmes de hachage des mots de passe. # Symfony choisira automatiquement le meilleur algorithme par défaut si non spécifié, diff --git a/migrations/Version20251123150147.php b/migrations/Version20251123150147.php new file mode 100644 index 0000000..02d9ac5 --- /dev/null +++ b/migrations/Version20251123150147.php @@ -0,0 +1,39 @@ +addSql('CREATE TABLE main (id SERIAL NOT NULL, president_id INT DEFAULT NULL, secretaire_id INT DEFAULT NULL, ag_date_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, locate VARCHAR(255) NOT NULL, locate_zipcode VARCHAR(255) NOT NULL, locate_city VARCHAR(255) NOT NULL, type VARCHAR(255) NOT NULL, PRIMARY KEY(id))'); + $this->addSql('CREATE INDEX IDX_BF28CD64B40A33C7 ON main (president_id)'); + $this->addSql('CREATE INDEX IDX_BF28CD64A90F02B2 ON main (secretaire_id)'); + $this->addSql('COMMENT ON COLUMN main.ag_date_at IS \'(DC2Type:datetime_immutable)\''); + $this->addSql('ALTER TABLE main ADD CONSTRAINT FK_BF28CD64B40A33C7 FOREIGN KEY (president_id) REFERENCES members (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE main ADD CONSTRAINT FK_BF28CD64A90F02B2 FOREIGN KEY (secretaire_id) REFERENCES members (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('CREATE SCHEMA public'); + $this->addSql('ALTER TABLE main DROP CONSTRAINT FK_BF28CD64B40A33C7'); + $this->addSql('ALTER TABLE main DROP CONSTRAINT FK_BF28CD64A90F02B2'); + $this->addSql('DROP TABLE main'); + } +} diff --git a/migrations/Version20251123150400.php b/migrations/Version20251123150400.php new file mode 100644 index 0000000..756dcc4 --- /dev/null +++ b/migrations/Version20251123150400.php @@ -0,0 +1,58 @@ +addSql('DROP SEQUENCE main_id_seq CASCADE'); + $this->addSql('CREATE TABLE ag_main (id SERIAL NOT NULL, president_id INT DEFAULT NULL, secretaire_id INT DEFAULT NULL, ag_date_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, locate VARCHAR(255) NOT NULL, locate_zipcode VARCHAR(255) NOT NULL, locate_city VARCHAR(255) NOT NULL, type VARCHAR(255) NOT NULL, PRIMARY KEY(id))'); + $this->addSql('CREATE INDEX IDX_8173EDAFB40A33C7 ON ag_main (president_id)'); + $this->addSql('CREATE INDEX IDX_8173EDAFA90F02B2 ON ag_main (secretaire_id)'); + $this->addSql('COMMENT ON COLUMN ag_main.ag_date_at IS \'(DC2Type:datetime_immutable)\''); + $this->addSql('CREATE TABLE ag_main_member (id SERIAL NOT NULL, member_id INT DEFAULT NULL, main_id INT DEFAULT NULL, is_voted_allow BOOLEAN NOT NULL, PRIMARY KEY(id))'); + $this->addSql('CREATE INDEX IDX_7979FD617597D3FE ON ag_main_member (member_id)'); + $this->addSql('CREATE INDEX IDX_7979FD61627EA78A ON ag_main_member (main_id)'); + $this->addSql('ALTER TABLE ag_main ADD CONSTRAINT FK_8173EDAFB40A33C7 FOREIGN KEY (president_id) REFERENCES members (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE ag_main ADD CONSTRAINT FK_8173EDAFA90F02B2 FOREIGN KEY (secretaire_id) REFERENCES members (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE ag_main_member ADD CONSTRAINT FK_7979FD617597D3FE FOREIGN KEY (member_id) REFERENCES members (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE ag_main_member ADD CONSTRAINT FK_7979FD61627EA78A FOREIGN KEY (main_id) REFERENCES ag_main (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE main DROP CONSTRAINT fk_bf28cd64a90f02b2'); + $this->addSql('ALTER TABLE main DROP CONSTRAINT fk_bf28cd64b40a33c7'); + $this->addSql('DROP TABLE main'); + } + + 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('CREATE SEQUENCE main_id_seq INCREMENT BY 1 MINVALUE 1 START 1'); + $this->addSql('CREATE TABLE main (id SERIAL NOT NULL, president_id INT DEFAULT NULL, secretaire_id INT DEFAULT NULL, ag_date_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, locate VARCHAR(255) NOT NULL, locate_zipcode VARCHAR(255) NOT NULL, locate_city VARCHAR(255) NOT NULL, type VARCHAR(255) NOT NULL, PRIMARY KEY(id))'); + $this->addSql('CREATE INDEX idx_bf28cd64a90f02b2 ON main (secretaire_id)'); + $this->addSql('CREATE INDEX idx_bf28cd64b40a33c7 ON main (president_id)'); + $this->addSql('COMMENT ON COLUMN main.ag_date_at IS \'(DC2Type:datetime_immutable)\''); + $this->addSql('ALTER TABLE main ADD CONSTRAINT fk_bf28cd64a90f02b2 FOREIGN KEY (secretaire_id) REFERENCES members (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE main ADD CONSTRAINT fk_bf28cd64b40a33c7 FOREIGN KEY (president_id) REFERENCES members (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE ag_main DROP CONSTRAINT FK_8173EDAFB40A33C7'); + $this->addSql('ALTER TABLE ag_main DROP CONSTRAINT FK_8173EDAFA90F02B2'); + $this->addSql('ALTER TABLE ag_main_member DROP CONSTRAINT FK_7979FD617597D3FE'); + $this->addSql('ALTER TABLE ag_main_member DROP CONSTRAINT FK_7979FD61627EA78A'); + $this->addSql('DROP TABLE ag_main'); + $this->addSql('DROP TABLE ag_main_member'); + } +} diff --git a/migrations/Version20251123150802.php b/migrations/Version20251123150802.php new file mode 100644 index 0000000..5331410 --- /dev/null +++ b/migrations/Version20251123150802.php @@ -0,0 +1,35 @@ +addSql('CREATE TABLE main_order (id SERIAL NOT NULL, title VARCHAR(255) NOT NULL, description VARCHAR(255) DEFAULT NULL, PRIMARY KEY(id))'); + $this->addSql('ALTER TABLE ag_main ADD closed_at TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL'); + $this->addSql('COMMENT ON COLUMN ag_main.closed_at IS \'(DC2Type:datetime_immutable)\''); + } + + 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 TABLE main_order'); + $this->addSql('ALTER TABLE ag_main DROP closed_at'); + } +} diff --git a/migrations/Version20251123150833.php b/migrations/Version20251123150833.php new file mode 100644 index 0000000..364885b --- /dev/null +++ b/migrations/Version20251123150833.php @@ -0,0 +1,36 @@ +addSql('DROP SEQUENCE main_order_id_seq CASCADE'); + $this->addSql('CREATE TABLE ag_order (id SERIAL NOT NULL, title VARCHAR(255) NOT NULL, description VARCHAR(255) DEFAULT NULL, PRIMARY KEY(id))'); + $this->addSql('DROP TABLE main_order'); + } + + 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('CREATE SEQUENCE main_order_id_seq INCREMENT BY 1 MINVALUE 1 START 1'); + $this->addSql('CREATE TABLE main_order (id SERIAL NOT NULL, title VARCHAR(255) NOT NULL, description VARCHAR(255) DEFAULT NULL, PRIMARY KEY(id))'); + $this->addSql('DROP TABLE ag_order'); + } +} diff --git a/migrations/Version20251123151054.php b/migrations/Version20251123151054.php new file mode 100644 index 0000000..010c408 --- /dev/null +++ b/migrations/Version20251123151054.php @@ -0,0 +1,36 @@ +addSql('ALTER TABLE ag_order ADD main_id INT DEFAULT NULL'); + $this->addSql('ALTER TABLE ag_order ADD CONSTRAINT FK_F9A1D380627EA78A FOREIGN KEY (main_id) REFERENCES ag_main (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('CREATE INDEX IDX_F9A1D380627EA78A ON ag_order (main_id)'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('CREATE SCHEMA public'); + $this->addSql('ALTER TABLE ag_order DROP CONSTRAINT FK_F9A1D380627EA78A'); + $this->addSql('DROP INDEX IDX_F9A1D380627EA78A'); + $this->addSql('ALTER TABLE ag_order DROP main_id'); + } +} diff --git a/migrations/Version20251123152416.php b/migrations/Version20251123152416.php new file mode 100644 index 0000000..5642752 --- /dev/null +++ b/migrations/Version20251123152416.php @@ -0,0 +1,32 @@ +addSql('ALTER TABLE ag_main ADD is_closed BOOLEAN DEFAULT NULL'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('CREATE SCHEMA public'); + $this->addSql('ALTER TABLE ag_main DROP is_closed'); + } +} diff --git a/migrations/Version20251123160153.php b/migrations/Version20251123160153.php new file mode 100644 index 0000000..03334b7 --- /dev/null +++ b/migrations/Version20251123160153.php @@ -0,0 +1,35 @@ +addSql('CREATE TABLE ag_main_vote (id SERIAL NOT NULL, member_id INT DEFAULT NULL, role VARCHAR(255) NOT NULL, pour INT NOT NULL, contre INT NOT NULL, PRIMARY KEY(id))'); + $this->addSql('CREATE INDEX IDX_A5781BDE7597D3FE ON ag_main_vote (member_id)'); + $this->addSql('ALTER TABLE ag_main_vote ADD CONSTRAINT FK_A5781BDE7597D3FE FOREIGN KEY (member_id) REFERENCES members (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('CREATE SCHEMA public'); + $this->addSql('ALTER TABLE ag_main_vote DROP CONSTRAINT FK_A5781BDE7597D3FE'); + $this->addSql('DROP TABLE ag_main_vote'); + } +} diff --git a/src/Controller/Admin/AdminController.php b/src/Controller/Admin/AdminController.php index 78a252e..94ee617 100644 --- a/src/Controller/Admin/AdminController.php +++ b/src/Controller/Admin/AdminController.php @@ -2,8 +2,14 @@ namespace App\Controller\Admin; +use App\Dto\Ag\AgMembersType; +use App\Dto\Ag\AgOrderType; +use App\Dto\Ag\AgType; use App\Entity\Account; use App\Entity\AccountResetPasswordRequest; +use App\Entity\Ag\Main; +use App\Entity\Ag\MainMember; +use App\Entity\Ag\MainOrder; use App\Entity\Members; use App\Entity\MembersCotisations; use App\Entity\Products; @@ -11,6 +17,7 @@ use App\Form\MembersType; use App\Form\ProductsType; use App\Form\RequestPasswordConfirmType; use App\Form\RequestPasswordRequestType; +use App\Repository\Ag\MainRepository; use App\Repository\MembersCotisationsRepository; use App\Repository\MembersRepository; use App\Repository\ProductsRepository; @@ -223,17 +230,99 @@ class AdminController extends AbstractController ]); } - #[Route(path: '/admin/account', name: 'admin_accounts_list', options: ['sitemap' => false], methods: ['GET'])] - public function adminAccount(): Response + + #[Route(path: '/admin/ag', name: 'admin_ag', options: ['sitemap' => false], methods: ['GET'])] + public function adminAg(MainRepository $mainRepository): Response { - return $this->render('admin/dashboard.twig', [ + + return $this->render('admin/ag.twig', [ + 'ags' => $mainRepository->findBy([],['agDateAt'=>'DESC']), ]); } - #[Route(path: '/admin/ag', name: 'admin_ag', options: ['sitemap' => false], methods: ['GET'])] - public function adminAg(): Response + #[Route(path: '/admin/ag/new', name: 'admin_ag_new', options: ['sitemap' => false], methods: ['GET','POST'])] + public function adminAgNew(Request $request,EntityManagerInterface $entityManager): Response { - return $this->render('admin/ag.twig', [ + $ag = new Main(); + $form = $this->createForm(AgType::class,$ag); + $form->handleRequest($request); + if($form->isSubmitted() && $form->isValid()){ + $entityManager->persist($ag); + $entityManager->flush(); + return $this->redirectToRoute('admin_ag'); + } + return $this->render('admin/ag/new.twig', [ + 'ag' => $form->createView(), ]); } + #[Route(path: '/admin/ag/{id}', name: 'admin_ag_edit', options: ['sitemap' => false], methods: ['GET','POST'])] + public function adminAgeDIT(?Main $ag,Request $request,EntityManagerInterface $entityManager): Response + { + $form = $this->createForm(AgType::class,$ag); + $form->handleRequest($request); + if($form->isSubmitted() && $form->isValid()){ + $entityManager->persist($ag); + $entityManager->flush(); + return $this->redirectToRoute('admin_ag_edit',['id'=>$ag->getId()]); + } + + $agOrder = new MainOrder(); + $agOrder->setMain($ag); + $formOrder = $this->createForm(AgOrderType::class,$agOrder); + $formOrder->handleRequest($request); + if($formOrder->isSubmitted() && $formOrder->isValid()){ + $entityManager->persist($agOrder); + $entityManager->flush(); + return $this->redirectToRoute('admin_ag_edit',['id'=>$agOrder->getId()]); + } + + $agMembers = new MainMember(); + $agMembers->setMain($ag); + $formMember = $this->createForm(AgMembersType::class,$agMembers); + $formMember->handleRequest($request); + if($formMember->isSubmitted() && $formMember->isValid()){ + $rf = $entityManager->getRepository(Members::class)->find($request->request->all()['ag_members']['member']); + + $agMembers->setMember($rf); + $t = new \DateTime(); + $tz = new \DateTime(); + $tz->modify('+1 year'); + $isValid = $agMembers->getMember()->getMembersCotisations()->filter(function (MembersCotisations $membersCotisations) use ($t,$tz) { + return ($membersCotisations->getStartdATE()->format('Y') == $t->format('Y')) && ($membersCotisations->getEndDate()->format('Y') == $tz->format('Y')); + })->first(); + $agMembers->setIsVotedAllow($isValid instanceof MembersCotisations); + $entityManager->persist($agMembers); + $entityManager->flush(); + return $this->redirectToRoute('admin_ag_edit',['id'=>$ag->getId()]); + } + if($request->query->has('idMember')) { + $agMembers = $entityManager->getRepository(MainMember::class)->find($request->query->get('idMember')); + $entityManager->remove($agMembers); + $entityManager->flush(); + return $this->redirectToRoute('admin_ag_edit',['id'=>$ag->getId()]); + } + if($request->query->has('orderId')) { + $agOders = $entityManager->getRepository(MainOrder::class)->find($request->query->get('orderId')); + $entityManager->remove($agOders); + $entityManager->flush(); + return $this->redirectToRoute('admin_ag_edit',['id'=>$ag->getId()]); + } + return $this->render('admin/ag/edit.twig', [ + 'ag' => $form->createView(), + 'agMain' => $ag, + 'agMembers' => $formMember->createView(), + 'agOrder' => $formOrder->createView(), + ]); + } + #[Route(path: '/admin/ag/delete/{id}', name: 'admin_ag_delete', options: ['sitemap' => false], methods: ['GET','POST'])] + public function adminAgDelete(?Main $ag,Request $request,EntityManagerInterface $entityManager): Response + { + foreach ($ag->getMainMembers() as $member) + $entityManager->remove($member); + foreach ($ag->getOrders() as $order) + $entityManager->remove($order); + $entityManager->remove($ag); + $entityManager->flush(); + return $this->redirectToRoute('admin_ag'); + } } diff --git a/src/Controller/SecurityController.php b/src/Controller/SecurityController.php index 2654a33..c68c14a 100644 --- a/src/Controller/SecurityController.php +++ b/src/Controller/SecurityController.php @@ -50,6 +50,11 @@ class SecurityController extends AbstractController 'last_username' => $authenticationUtils->getLastUsername(), 'error' => $authenticationUtils->getLastAuthenticationError(), ]); + } + #[Route(path: '/logout', name: 'app_logout', options: ['sitemap' => false], methods: ['GET','POST'])] + public function logout(): Response + { + } #[Route(path: '/mot-de-passe-oublie', name: 'app_forgot_password', options: ['sitemap' => false], methods: ['GET','POST'])] public function forgotPassword(Request $request,EventDispatcherInterface $eventDispatcher): Response diff --git a/src/Dto/Ag/AgMembersType.php b/src/Dto/Ag/AgMembersType.php new file mode 100644 index 0000000..e6e9be9 --- /dev/null +++ b/src/Dto/Ag/AgMembersType.php @@ -0,0 +1,53 @@ +getMain(); + $choices = []; + + foreach ($this->membersRepository->findAll() as $member) { + $present = $main->getMainMembers()->filter(function (MainMember $mainMember) use ($member) { + return $member->getId() == $mainMember->getMember()->getId(); + })->first(); + if(!$present instanceof MainMember) { + $choices[$member->getPseudo()] = $member->getId(); + } + } + + $builder + ->add('member', ChoiceType::class, [ + 'label' => 'Membre', + 'choices' => $choices, + 'mapped' => false, + ]) + ; + } + + public function configureOptions(OptionsResolver $resolver) + { + $resolver->setDefault('data_class',MainMember::class); + } +} diff --git a/src/Dto/Ag/AgOrderType.php b/src/Dto/Ag/AgOrderType.php new file mode 100644 index 0000000..a1b12be --- /dev/null +++ b/src/Dto/Ag/AgOrderType.php @@ -0,0 +1,48 @@ +add('title', TextType::class, [ + 'label' => 'Titre', + 'required' => true, + ]) + + ->add('description', TextareaType::class, [ + 'label' => 'Description', + 'required' => false + ]) + ; + } + + public function configureOptions(OptionsResolver $resolver) + { + $resolver->setDefault('data_class',MainOrder::class); + } +} diff --git a/src/Dto/Ag/AgType.php b/src/Dto/Ag/AgType.php new file mode 100644 index 0000000..7c36ccc --- /dev/null +++ b/src/Dto/Ag/AgType.php @@ -0,0 +1,70 @@ +add('agDateAt',DateTimeType::class,[ + 'label' => 'Date de l\'Assemblée Générale', + 'required' => true, + ]) + ->add('type',ChoiceType::class,[ + 'label' => 'Type de l\'AG', + 'required' => true, + 'choices' => [ + 'Ordinaire' => 'Ordinaire', + 'Extraordinaire' => 'Extraordinaire' + ] + ]) + ->add('locate',TextType::class,[ + 'label' => 'Adresse', + 'required' => true, + ]) + ->add('locateCity',TextType::class,[ + 'label' => 'Ville', + 'required' => true, + ]) + ->add('locateZipcode',TextType::class,[ + 'label' => 'Code postal', + 'required' => true, + ]) + ->add('president',EntityType::class,[ + 'label' => 'President de la scéance', + 'class' => Members::class, + 'choice_label' => function (Members $member) { + return $member->getPseudo()." - ".$member->getRole(); + } + ]) + ->add('secretaire',EntityType::class,[ + 'label' => 'Secretaire de la scéance', + 'class' => Members::class, + 'choice_label' => function (Members $member) { + return $member->getPseudo()." - ".$member->getRole(); + } + ]) + ->add('closedAt',DateTimeType::class,[ + 'label' => 'Date de cloture', + 'required' => true, + ]) + ; + } + + public function configureOptions(OptionsResolver $resolver) + { + $resolver->setDefault('data_class',Main::class); + } +} diff --git a/src/Entity/Ag/Main.php b/src/Entity/Ag/Main.php new file mode 100644 index 0000000..a63574e --- /dev/null +++ b/src/Entity/Ag/Main.php @@ -0,0 +1,237 @@ + + */ + #[ORM\OneToMany(targetEntity: MainMember::class, mappedBy: 'main')] + private Collection $mainMembers; + + #[ORM\Column(nullable: true)] + private ?\DateTimeImmutable $closedAt = null; + + /** + * @var Collection + */ + #[ORM\OneToMany(targetEntity: MainOrder::class, mappedBy: 'main')] + private Collection $orders; + + #[ORM\Column(nullable: true)] + private ?bool $isClosed = null; + + public function __construct() + { + $this->mainMembers = new ArrayCollection(); + $this->orders = new ArrayCollection(); + } + + public function getId(): ?int + { + return $this->id; + } + + public function getAgDateAt(): ?\DateTimeImmutable + { + return $this->agDateAt; + } + + public function setAgDateAt(\DateTimeImmutable $agDateAt): static + { + $this->agDateAt = $agDateAt; + + return $this; + } + + public function getPresident(): ?Members + { + return $this->president; + } + + public function setPresident(?Members $president): static + { + $this->president = $president; + + return $this; + } + + public function getSecretaire(): ?Members + { + return $this->secretaire; + } + + public function setSecretaire(?Members $secretaire): static + { + $this->secretaire = $secretaire; + + return $this; + } + + public function getLocate(): ?string + { + return $this->locate; + } + + public function setLocate(string $locate): static + { + $this->locate = $locate; + + return $this; + } + + public function getLocateZipcode(): ?string + { + return $this->locateZipcode; + } + + public function setLocateZipcode(string $locateZipcode): static + { + $this->locateZipcode = $locateZipcode; + + return $this; + } + + public function getLocateCity(): ?string + { + return $this->locateCity; + } + + public function setLocateCity(string $locateCity): static + { + $this->locateCity = $locateCity; + + return $this; + } + + public function getType(): ?string + { + return $this->type; + } + + public function setType(string $type): static + { + $this->type = $type; + + return $this; + } + + /** + * @return Collection + */ + public function getMainMembers(): Collection + { + return $this->mainMembers; + } + + public function addMainMember(MainMember $mainMember): static + { + if (!$this->mainMembers->contains($mainMember)) { + $this->mainMembers->add($mainMember); + $mainMember->setMain($this); + } + + return $this; + } + + public function removeMainMember(MainMember $mainMember): static + { + if ($this->mainMembers->removeElement($mainMember)) { + // set the owning side to null (unless already changed) + if ($mainMember->getMain() === $this) { + $mainMember->setMain(null); + } + } + + return $this; + } + + public function getClosedAt(): ?\DateTimeImmutable + { + return $this->closedAt; + } + + public function setClosedAt(?\DateTimeImmutable $closedAt): static + { + $this->closedAt = $closedAt; + + return $this; + } + + /** + * @return Collection + */ + public function getOrders(): Collection + { + return $this->orders; + } + + public function addOrder(MainOrder $order): static + { + if (!$this->orders->contains($order)) { + $this->orders->add($order); + $order->setMain($this); + } + + return $this; + } + + public function removeOrder(MainOrder $order): static + { + if ($this->orders->removeElement($order)) { + // set the owning side to null (unless already changed) + if ($order->getMain() === $this) { + $order->setMain(null); + } + } + + return $this; + } + + public function isClosed(): ?bool + { + return $this->isClosed; + } + + public function setIsClosed(?bool $isClosed): static + { + $this->isClosed = $isClosed; + + return $this; + } +} diff --git a/src/Entity/Ag/MainMember.php b/src/Entity/Ag/MainMember.php new file mode 100644 index 0000000..9b8fca4 --- /dev/null +++ b/src/Entity/Ag/MainMember.php @@ -0,0 +1,67 @@ +id; + } + + public function getMember(): ?Members + { + return $this->member; + } + + public function setMember(?Members $member): static + { + $this->member = $member; + + return $this; + } + + public function isVotedAllow(): ?bool + { + return $this->isVotedAllow; + } + + public function setIsVotedAllow(bool $isVotedAllow): static + { + $this->isVotedAllow = $isVotedAllow; + + return $this; + } + + public function getMain(): ?Main + { + return $this->main; + } + + public function setMain(?Main $main): static + { + $this->main = $main; + + return $this; + } +} diff --git a/src/Entity/Ag/MainOrder.php b/src/Entity/Ag/MainOrder.php new file mode 100644 index 0000000..8ab519f --- /dev/null +++ b/src/Entity/Ag/MainOrder.php @@ -0,0 +1,66 @@ +id; + } + + public function getTitle(): ?string + { + return $this->title; + } + + public function setTitle(string $title): static + { + $this->title = $title; + + return $this; + } + + public function getDescription(): ?string + { + return $this->description; + } + + public function setDescription(?string $description): static + { + $this->description = $description; + + return $this; + } + + public function getMain(): ?Main + { + return $this->main; + } + + public function setMain(?Main $main): static + { + $this->main = $main; + + return $this; + } +} diff --git a/src/Entity/Ag/MainVote.php b/src/Entity/Ag/MainVote.php new file mode 100644 index 0000000..d4facc4 --- /dev/null +++ b/src/Entity/Ag/MainVote.php @@ -0,0 +1,82 @@ +id; + } + + public function getMember(): ?Members + { + return $this->member; + } + + public function setMember(?Members $member): static + { + $this->member = $member; + + return $this; + } + + public function getRole(): ?string + { + return $this->role; + } + + public function setRole(string $role): static + { + $this->role = $role; + + return $this; + } + + public function getPour(): ?int + { + return $this->pour; + } + + public function setPour(int $pour): static + { + $this->pour = $pour; + + return $this; + } + + public function getContre(): ?int + { + return $this->contre; + } + + public function setContre(int $contre): static + { + $this->contre = $contre; + + return $this; + } +} diff --git a/src/Entity/Members.php b/src/Entity/Members.php index 119471e..a92b9ca 100644 --- a/src/Entity/Members.php +++ b/src/Entity/Members.php @@ -2,6 +2,9 @@ namespace App\Entity; +use App\Entity\Ag\Main; +use App\Entity\Ag\MainMember; +use App\Entity\Ag\MainVote; use App\Repository\MembersRepository; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; @@ -67,9 +70,37 @@ class Members #[ORM\Column(length: 255,nullable: true)] private ?string $Email = null; + /** + * @var Collection + */ + #[ORM\OneToMany(targetEntity: Main::class, mappedBy: 'president')] + private Collection $ag_president; + + /** + * @var Collection + */ + #[ORM\OneToMany(targetEntity: Main::class, mappedBy: 'secretaire')] + private Collection $secretaire; + + /** + * @var Collection + */ + #[ORM\OneToMany(targetEntity: MainMember::class, mappedBy: 'member')] + private Collection $mainMembers; + + /** + * @var Collection + */ + #[ORM\OneToMany(targetEntity: MainVote::class, mappedBy: 'member')] + private Collection $agVotes; + public function __construct() { $this->membersCotisations = new ArrayCollection(); + $this->ag_president = new ArrayCollection(); + $this->secretaire = new ArrayCollection(); + $this->mainMembers = new ArrayCollection(); + $this->agVotes = new ArrayCollection(); } public function getId(): ?int @@ -342,4 +373,124 @@ class Members return $this; } + + /** + * @return Collection + */ + public function getAgPresident(): Collection + { + return $this->ag_president; + } + + public function addAgPresident(Main $agPresident): static + { + if (!$this->ag_president->contains($agPresident)) { + $this->ag_president->add($agPresident); + $agPresident->setPresident($this); + } + + return $this; + } + + public function removeAgPresident(Main $agPresident): static + { + if ($this->ag_president->removeElement($agPresident)) { + // set the owning side to null (unless already changed) + if ($agPresident->getPresident() === $this) { + $agPresident->setPresident(null); + } + } + + return $this; + } + + /** + * @return Collection + */ + public function getSecretaire(): Collection + { + return $this->secretaire; + } + + public function addSecretaire(Main $secretaire): static + { + if (!$this->secretaire->contains($secretaire)) { + $this->secretaire->add($secretaire); + $secretaire->setSecretaire($this); + } + + return $this; + } + + public function removeSecretaire(Main $secretaire): static + { + if ($this->secretaire->removeElement($secretaire)) { + // set the owning side to null (unless already changed) + if ($secretaire->getSecretaire() === $this) { + $secretaire->setSecretaire(null); + } + } + + return $this; + } + + /** + * @return Collection + */ + public function getMainMembers(): Collection + { + return $this->mainMembers; + } + + public function addMainMember(MainMember $mainMember): static + { + if (!$this->mainMembers->contains($mainMember)) { + $this->mainMembers->add($mainMember); + $mainMember->setMember($this); + } + + return $this; + } + + public function removeMainMember(MainMember $mainMember): static + { + if ($this->mainMembers->removeElement($mainMember)) { + // set the owning side to null (unless already changed) + if ($mainMember->getMember() === $this) { + $mainMember->setMember(null); + } + } + + return $this; + } + + /** + * @return Collection + */ + public function getAgVotes(): Collection + { + return $this->agVotes; + } + + public function addAgVote(MainVote $agVote): static + { + if (!$this->agVotes->contains($agVote)) { + $this->agVotes->add($agVote); + $agVote->setMember($this); + } + + return $this; + } + + public function removeAgVote(MainVote $agVote): static + { + if ($this->agVotes->removeElement($agVote)) { + // set the owning side to null (unless already changed) + if ($agVote->getMember() === $this) { + $agVote->setMember(null); + } + } + + return $this; + } } diff --git a/src/Repository/Ag/MainMemberRepository.php b/src/Repository/Ag/MainMemberRepository.php new file mode 100644 index 0000000..fff76e1 --- /dev/null +++ b/src/Repository/Ag/MainMemberRepository.php @@ -0,0 +1,43 @@ + + */ +class MainMemberRepository extends ServiceEntityRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, MainMember::class); + } + + // /** + // * @return MainMember[] Returns an array of MainMember objects + // */ + // public function findByExampleField($value): array + // { + // return $this->createQueryBuilder('m') + // ->andWhere('m.exampleField = :val') + // ->setParameter('val', $value) + // ->orderBy('m.id', 'ASC') + // ->setMaxResults(10) + // ->getQuery() + // ->getResult() + // ; + // } + + // public function findOneBySomeField($value): ?MainMember + // { + // return $this->createQueryBuilder('m') + // ->andWhere('m.exampleField = :val') + // ->setParameter('val', $value) + // ->getQuery() + // ->getOneOrNullResult() + // ; + // } +} diff --git a/src/Repository/Ag/MainOrderRepository.php b/src/Repository/Ag/MainOrderRepository.php new file mode 100644 index 0000000..a87fb35 --- /dev/null +++ b/src/Repository/Ag/MainOrderRepository.php @@ -0,0 +1,43 @@ + + */ +class MainOrderRepository extends ServiceEntityRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, MainOrder::class); + } + + // /** + // * @return MainOrder[] Returns an array of MainOrder objects + // */ + // public function findByExampleField($value): array + // { + // return $this->createQueryBuilder('m') + // ->andWhere('m.exampleField = :val') + // ->setParameter('val', $value) + // ->orderBy('m.id', 'ASC') + // ->setMaxResults(10) + // ->getQuery() + // ->getResult() + // ; + // } + + // public function findOneBySomeField($value): ?MainOrder + // { + // return $this->createQueryBuilder('m') + // ->andWhere('m.exampleField = :val') + // ->setParameter('val', $value) + // ->getQuery() + // ->getOneOrNullResult() + // ; + // } +} diff --git a/src/Repository/Ag/MainRepository.php b/src/Repository/Ag/MainRepository.php new file mode 100644 index 0000000..b568590 --- /dev/null +++ b/src/Repository/Ag/MainRepository.php @@ -0,0 +1,43 @@ + + */ +class MainRepository extends ServiceEntityRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, Main::class); + } + +// /** +// * @return Main[] Returns an array of Main objects +// */ +// public function findByExampleField($value): array +// { +// return $this->createQueryBuilder('m') +// ->andWhere('m.exampleField = :val') +// ->setParameter('val', $value) +// ->orderBy('m.id', 'ASC') +// ->setMaxResults(10) +// ->getQuery() +// ->getResult() +// ; +// } + +// public function findOneBySomeField($value): ?Main +// { +// return $this->createQueryBuilder('m') +// ->andWhere('m.exampleField = :val') +// ->setParameter('val', $value) +// ->getQuery() +// ->getOneOrNullResult() +// ; +// } +} diff --git a/src/Repository/Ag/MainVoteRepository.php b/src/Repository/Ag/MainVoteRepository.php new file mode 100644 index 0000000..3401e51 --- /dev/null +++ b/src/Repository/Ag/MainVoteRepository.php @@ -0,0 +1,43 @@ + + */ +class MainVoteRepository extends ServiceEntityRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, MainVote::class); + } + +// /** +// * @return MainVote[] Returns an array of MainVote objects +// */ +// public function findByExampleField($value): array +// { +// return $this->createQueryBuilder('m') +// ->andWhere('m.exampleField = :val') +// ->setParameter('val', $value) +// ->orderBy('m.id', 'ASC') +// ->setMaxResults(10) +// ->getQuery() +// ->getResult() +// ; +// } + +// public function findOneBySomeField($value): ?MainVote +// { +// return $this->createQueryBuilder('m') +// ->andWhere('m.exampleField = :val') +// ->setParameter('val', $value) +// ->getQuery() +// ->getOneOrNullResult() +// ; +// } +} diff --git a/src/Security/AuthenticationEntryPoint.php b/src/Security/AuthenticationEntryPoint.php index c421c46..bae4862 100644 --- a/src/Security/AuthenticationEntryPoint.php +++ b/src/Security/AuthenticationEntryPoint.php @@ -51,6 +51,6 @@ class AuthenticationEntryPoint implements AuthenticationEntryPointInterface ); } - return new RedirectResponse($this->urlGenerator->generate('frontend_login')); + return new RedirectResponse($this->urlGenerator->generate('app_login')); } } diff --git a/templates/admin/ag.twig b/templates/admin/ag.twig new file mode 100644 index 0000000..3bb05b2 --- /dev/null +++ b/templates/admin/ag.twig @@ -0,0 +1,79 @@ +{% extends 'admin/base.twig' %} +{% block title %}AG (Assemblée Générale){% endblock %} +{% block page_title %}AG (Assemblée Générale){% endblock %} + +{% block body %} +
+ +
+

Liste des AG

+ + {# NOUVEAU BOUTON: CRÉER UNE AG #} + + + Créer une AG + +
+ + {% for ag in ags %} +
+ +
+

+ {{ ag.agDateAt|date('d/m/Y H:i') }} + + {{ ag.type }} + +

+
+ +

+ Président: {{ ag.president.pseudo }} / + Secrétaire: {{ ag.secretaire.pseudo }} +

+ +
+ Lieu: {{ ag.locate }} + {{ ag.locateZipcode }} {{ ag.locateCity }} +
+ +
+

+ Membres principaux: {{ ag.mainMembers.count }} +

+

+ Ordres du jour: {{ ag.orders.count }} +

+
+ + {% if ag.closed == false or ag.closed == null %} + {# Bloc Actions: SUPPRIMER / MODIFIER #} +
+ + {# Bouton Modifier #} + + Modifier + + + {# Bouton Supprimer #} +
+ + + +
+
+ {% endif %} +
+ {% endfor %} + +
+{% endblock %} diff --git a/templates/admin/ag/edit.twig b/templates/admin/ag/edit.twig new file mode 100644 index 0000000..65878e7 --- /dev/null +++ b/templates/admin/ag/edit.twig @@ -0,0 +1,176 @@ +{% extends 'admin/base.twig' %} +{% block title %}AG (Assemblée Générale){% endblock %} +{% block page_title %}AG (Assemblée Générale){% endblock %} + +{% block body %} +
+ + {# Titre Principal (Créer / Modifier) #} +

+ {% if ag.vars.value.id is defined and ag.vars.value.id is not null %} + Modifier l'Assemblée Générale + {% else %} + Créer une nouvelle Assemblée Générale + {% endif %} +

+ + {% form_theme ag 'form_admin.twig' %} + + {# ============================================== #} + {# FORMULAIRE 1: DÉTAILS DE L'AG (Non répété ici) #} + {# ============================================== #} + {{ form_start(ag, {'attr': {'class': 'space-y-6 mb-10'}}) }} + + {# Section 1: Détails de l'AG #} +
+

Détails de l'AG

+ {{ form_row(ag.agDateAt) }} + {{ form_row(ag.closedAt) }} + {{ form_row(ag.type) }} +
+
+ + {# Section 2: Localisation #} +
+

Lieu

+ {{ form_row(ag.locate) }} + {{ form_row(ag.locateZipcode) }} + {{ form_row(ag.locateCity) }} +
+ + {# Section 3: Rôles #} +
+

Rôles

+ {{ form_row(ag.president) }} + {{ form_row(ag.secretaire) }} +
+ + {# Boutons d'action pour le formulaire AG #} +
+ + Annuler + + +
+ + {{ form_end(ag) }} + + {# ============================================== #} + {# BLOC GESTION DES MEMBRES (Non répété ici) #} + {# ============================================== #} +
+

Ajouter/Gérer les membres

+ + {% form_theme agMembers 'form_admin.twig' %} + + {{ form_start(agMembers, {'attr': {'class': 'space-y-4 p-4 border rounded-lg bg-gray-50 dark:bg-gray-700 mb-6'}}) }} +
+
{{ form_row(agMembers.member) }}
+
+ +
+
+ {{ form_end(agMembers) }} + + {# LISTE DES MEMBRES ACTUELS #} + {% if ag.vars.value.mainMembers is defined and ag.vars.value.mainMembers|length > 0 %} +

+ Liste des Membres Principaux ({{ ag.vars.value.mainMembers|length }}) +

+
    + {% for memberAg in ag.vars.value.mainMembers %} +
  • + + {{ memberAg.member.pseudo }} - {{ memberAg.member.role }} + +
    + + +
    +
  • + {% endfor %} +
+ {% else %} +

+ Aucun membre principal n'est encore rattaché à cette Assemblée Générale. +

+ {% endif %} +
+ + {# ============================================== #} + {# BLOC ORDRES DU JOUR (ODJ) #} + {# ============================================== #} +
+

Ajouter un Ordre du Jour

+ + {% form_theme agOrder 'form_admin.twig' %} + + {# FORMULAIRE D'AJOUT D'ODJ #} + {{ form_start(agOrder, {'attr': {'class': 'space-y-4 p-4 border rounded-lg bg-gray-50 dark:bg-gray-700 mb-6'}}) }} + +
+ {{ form_row(agOrder.title) }} + +
+ + {{ form_row(agOrder.description) }} + +
+ +
+ + {{ form_end(agOrder) }} + + {# LISTE DES ORDRES DU JOUR ACTUELS #} + {% if ag.vars.value.orders is defined and ag.vars.value.orders|length > 0 %} +

+ Ordres du Jour ({{ ag.vars.value.orders|length }}) +

+ +
    + {% for order in ag.vars.value.orders %} +
  • + +
    + + {{ loop.index }}. {{ order.title }} + + + {# Formulaire de Suppression de l'ODJ #} +
    + + + +
    +
    + +

    + {{ order.description|default('Aucune description fournie.') }} +

    +
  • + {% endfor %} +
+ {% else %} +

+ Aucun Ordre du Jour n'a encore été défini pour cette Assemblée Générale. +

+ {% endif %} + +
+ +
+{% endblock %} diff --git a/templates/admin/ag/new.twig b/templates/admin/ag/new.twig new file mode 100644 index 0000000..683a99b --- /dev/null +++ b/templates/admin/ag/new.twig @@ -0,0 +1,70 @@ +{% extends 'admin/base.twig' %} +{% block title %}AG (Assemblée Générale){% endblock %} +{% block page_title %}AG (Assemblée Générale){% endblock %} + +{% block body %} +
+ +

+ {% if ag.vars.value.id is defined and ag.vars.value.id is not null %} + Modifier l'Assemblée Générale + {% else %} + Créer une nouvelle Assemblée Générale + {% endif %} +

+ + {# Le formulaire doit utiliser le thème 'form_admin.twig' pour le style #} + {% form_theme ag 'form_admin.twig' %} + + {{ form_start(ag, {'attr': {'class': 'space-y-6'}}) }} + + {# Section 1: Informations principales et dates (Grid Layout) #} +
+ +

Détails de l'AG

+ + {{ form_row(ag.agDateAt) }} + {{ form_row(ag.closedAt) }} + {{ form_row(ag.type) }} + {# Ce champ est seul sur la deuxième colonne pour l'exemple #} +
+
+ + {# Section 2: Localisation (Grid Layout) #} +
+ +

Lieu

+ + {{ form_row(ag.locate) }} + {{ form_row(ag.locateZipcode) }} + {{ form_row(ag.locateCity) }} +
+ + {# Section 3: Rôles (Simple Layout) #} +
+ +

Rôles

+ + {{ form_row(ag.president) }} + {{ form_row(ag.secretaire) }} +
+ + {# Boutons d'action #} +
+ + {# Bouton Annuler/Retour #} + + Annuler + + + {# Bouton Soumettre/Enregistrer #} + +
+ + {{ form_end(ag) }} +
+{% endblock %} diff --git a/templates/admin/base.twig b/templates/admin/base.twig index f072e04..4799b14 100644 --- a/templates/admin/base.twig +++ b/templates/admin/base.twig @@ -26,9 +26,9 @@ Elle défilera donc avec le reste du contenu de la page. L'overflow-y-auto n'est plus nécessaire ici car c'est le qui gère le scroll. --> - - - -
diff --git a/templates/base.twig b/templates/base.twig index b29f337..91f6f74 100644 --- a/templates/base.twig +++ b/templates/base.twig @@ -223,7 +223,11 @@ tabindex="-1"> {{ 'logged_admin'|trans }} - + + {{ 'logout_link'|trans }} + {% else %} {# Afficher la connexion si non connecté #} +
{{ form_label(form) }}
{{ form_widget(form) }}