diff --git a/.env b/.env index 0b234e8..f117148 100644 --- a/.env +++ b/.env @@ -51,7 +51,7 @@ PATH_URL=https://esyweb.local STRIPE_PK=pk_test_51SUA22173W4aeFB1nO6oFfDZ12HOTffDKtCshhZ8rkUg6kUO2ZaQC0tK72rhE79Tr8treeHX9KMcZtvcQZ0X8VSm00Q6GQ365V STRIPE_SK=sk_test_51SUA22173W4aeFB16EB2LxGI0hNvNJzFshDI98zRImWBIhSfzqOGAz5TlPxSpUWbj3x4COm6kmSsaal9FpQR1A7M0022DvjbbR STRIPE_WEBHOOKS_SIGN=whsec_0DOZJAwgMwkcHl2RWXI8h8YItj9q7v3A -DEV_URL=https://265fcc9dd2a6.ngrok-free.app +DEV_URL=https://240fba7426df.ngrok-free.app VAPID_PK=DsOg7jToRSD-VpNSV1Gt3YAhSwz4l-nqeu7yFvzbSxg VAPID_PC=BKz0kdcsG6kk9KxciPpkfP8kEDAd408inZecij5kBDbQ1ZGZSNwS4KZ8FerC28LFXvgSqpDXtor3ePo0zBCdNqo diff --git a/config/packages/vich_uploader.yaml b/config/packages/vich_uploader.yaml index d6ab51d..6853e2e 100644 --- a/config/packages/vich_uploader.yaml +++ b/config/packages/vich_uploader.yaml @@ -24,6 +24,13 @@ vich_uploader: inject_on_load: true delete_on_update: true delete_on_remove: true + ag_adh: + uri_prefix: /storage/ag_adh + upload_destination: '%kernel.project_dir%/public/storage/ag_adh' + namer: Vich\UploaderBundle\Naming\UniqidNamer # Replaced namer + inject_on_load: true + delete_on_update: true + delete_on_remove: true #mappings: # products: # uri_prefix: /images/products diff --git a/migrations/Version20251123201651.php b/migrations/Version20251123201651.php new file mode 100644 index 0000000..524f6d2 --- /dev/null +++ b/migrations/Version20251123201651.php @@ -0,0 +1,40 @@ +addSql('ALTER TABLE ag_main ADD ag_adh_file_name VARCHAR(255) DEFAULT NULL'); + $this->addSql('ALTER TABLE ag_main ADD ag_adh_dimensions JSON DEFAULT NULL'); + $this->addSql('ALTER TABLE ag_main ADD ag_adh_size VARCHAR(255) DEFAULT NULL'); + $this->addSql('ALTER TABLE ag_main ADD ag_adh_mine_type VARCHAR(255) DEFAULT NULL'); + $this->addSql('ALTER TABLE ag_main ADD ag_adh_original_name VARCHAR(255) 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 ag_adh_file_name'); + $this->addSql('ALTER TABLE ag_main DROP ag_adh_dimensions'); + $this->addSql('ALTER TABLE ag_main DROP ag_adh_size'); + $this->addSql('ALTER TABLE ag_main DROP ag_adh_mine_type'); + $this->addSql('ALTER TABLE ag_main DROP ag_adh_original_name'); + } +} diff --git a/migrations/Version20251123210026.php b/migrations/Version20251123210026.php new file mode 100644 index 0000000..ec9a7ee --- /dev/null +++ b/migrations/Version20251123210026.php @@ -0,0 +1,40 @@ +addSql('CREATE TABLE ag_main_signed (id SERIAL NOT NULL, main_id INT DEFAULT NULL, members_id INT DEFAULT NULL, submiter_id VARCHAR(255) NOT NULL, PRIMARY KEY(id))'); + $this->addSql('CREATE INDEX IDX_1726DFDA627EA78A ON ag_main_signed (main_id)'); + $this->addSql('CREATE INDEX IDX_1726DFDABD01F5ED ON ag_main_signed (members_id)'); + $this->addSql('ALTER TABLE ag_main_signed ADD CONSTRAINT FK_1726DFDA627EA78A FOREIGN KEY (main_id) REFERENCES ag_main (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE ag_main_signed ADD CONSTRAINT FK_1726DFDABD01F5ED FOREIGN KEY (members_id) REFERENCES members (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE ag_main ADD is_attestation 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_signed DROP CONSTRAINT FK_1726DFDA627EA78A'); + $this->addSql('ALTER TABLE ag_main_signed DROP CONSTRAINT FK_1726DFDABD01F5ED'); + $this->addSql('DROP TABLE ag_main_signed'); + $this->addSql('ALTER TABLE ag_main DROP is_attestation'); + } +} diff --git a/src/Controller/AdController.php b/src/Controller/AdController.php new file mode 100644 index 0000000..746ffe2 --- /dev/null +++ b/src/Controller/AdController.php @@ -0,0 +1,54 @@ + false], methods: ['GET','POST'])] + public function adh(?Main $main,Request $request): Response + { + if(!$main instanceof Main){ + return $this->redirectToRoute('app_home'); + } + + if($request->query->has('validateResult')) { + return $this->render('adh_validate.twig',[ + 'main' => $main, + ]); + } + $signedList =[]; + foreach ($main->getMainSigneds() as $signed) { + $docuseal = new \Docuseal\Api('pgAU116mCFmeF7WQSezHqxtZW8V1fgo31u5d2FXoaKe', 'https://signature.esy-web.dev/api'); + $submiter = $docuseal->getSubmitter($signed->getSubmiterId()); + $signed->sign = "https://signature.esy-web.dev/s/".$submiter['slug']; + $signedList[] = $signed; + } + return $this->render('adh.twig',[ + 'main' => $main, + 'signed' => $signedList, + ]); + } +} diff --git a/src/Controller/Admin/AdminController.php b/src/Controller/Admin/AdminController.php index 20906d1..104571e 100644 --- a/src/Controller/Admin/AdminController.php +++ b/src/Controller/Admin/AdminController.php @@ -5,12 +5,14 @@ namespace App\Controller\Admin; use App\Dto\Ag\AgMembersType; use App\Dto\Ag\AgOrderType; use App\Dto\Ag\AgType; +use App\Dto\Ag\AgTypeEdit; use App\Dto\Ag\AgVoteType; 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\Ag\MainSigned; use App\Entity\Ag\MainVote; use App\Entity\Members; use App\Entity\MembersCotisations; @@ -25,11 +27,20 @@ use App\Repository\MembersRepository; use App\Repository\ProductsRepository; use App\Service\Mailer\Mailer; use App\Service\Payments\PaymentClient; +use App\Service\Pdf\AgAdh; +use App\Service\Pdf\AgConvocation; use App\Service\Pdf\AgGenerator; use App\Service\Pdf\CotaReceiptGenerator; use App\Service\ResetPassword\Event\ResetPasswordConfirmEvent; use App\Service\ResetPassword\Event\ResetPasswordEvent; use Doctrine\ORM\EntityManagerInterface; +use Endroid\QrCode\Builder\Builder; +use Endroid\QrCode\Encoding\Encoding; +use Endroid\QrCode\ErrorCorrectionLevel; +use Endroid\QrCode\Label\Font\OpenSans; +use Endroid\QrCode\Label\LabelAlignment; +use Endroid\QrCode\RoundBlockSizeMode; +use Endroid\QrCode\Writer\PngWriter; use Psr\EventDispatcher\EventDispatcherInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\File\UploadedFile; @@ -239,7 +250,7 @@ class AdminController extends AbstractController #[Route(path: '/admin/ag', name: 'admin_ag', options: ['sitemap' => false], methods: ['GET'])] - public function adminAg(UploaderHelper $uploaderHelper,KernelInterface $kernel,EntityManagerInterface $entityManager,Request $request,MainRepository $mainRepository): Response + public function adminAg(Mailer $mailer,UploaderHelper $uploaderHelper,KernelInterface $kernel,EntityManagerInterface $entityManager,Request $request,MainRepository $mainRepository): Response { if($request->query->has('idValidateSign')) { @@ -260,6 +271,31 @@ class AdminController extends AbstractController $entityManager->flush(); return $this->redirectToRoute('admin_ag'); } + if($request->query->has('idQrCode')) { + /** @var Main $idQrCode */ + $idQrCode = $mainRepository->find($request->query->get('idQrCode')); + + if($_ENV['APP_ENV'] == "prod") { + $urlRoot = $request->getContentTypeFormat(); + } else { + $urlRoot = $_ENV['DEV_URL']; + } + $builder = new Builder( + writer: new PngWriter(), + writerOptions: [], + validateResult: false, + data: $urlRoot.$this->generateUrl('app_adh',['id'=>$idQrCode->getId()]), + encoding: new Encoding('UTF-8'), + errorCorrectionLevel: ErrorCorrectionLevel::High, + size: 300, + margin: 10, + ); + $result = $builder->build(); + $response = new Response($result->getString(),Response::HTTP_OK,[ + 'Content-Type' => $result->getMimeType() + ]); + return $response; + } if($request->query->has('idSign')) { /** @var Main $main */ $main = $mainRepository->find($request->query->get('id')); @@ -298,6 +334,86 @@ class AdminController extends AbstractController $r->setStatusCode(302); return $r; } + if($request->query->has('idConvocation')) { + /** @var Main $main */ + $main = $mainRepository->find($request->query->get('idConvocation')); + $agAdh = new AgAdh(); + $agAdh->setData($main,$kernel,$entityManager); + $agAdh->GeneratePdf(); + $content = $agAdh->Output('S'); + $fileName = 'adh_ecosplay_' . $main->getAgDateAt()->format('Ymd') . '.pdf'; + $tempFilePath = sys_get_temp_dir() . DIRECTORY_SEPARATOR . $fileName; + + file_put_contents($tempFilePath, $content); + $file = new UploadedFile($tempFilePath,$fileName,"application/pdf",0,true); + $main->setAgAdh($file); + $main->setUpdateAt(new \DateTimeImmutable()); + $main->setIsAttestation(true); + $entityManager->persist($main); + $entityManager->flush(); + $url = $uploaderHelper->asset($main,'agAdh'); + + if($_ENV['APP_ENV'] == "prod") { + $url = $request->getContentTypeFormat() . $url; + $urlRoot = $request->getContentTypeFormat(); + } else { + $url = $_ENV['DEV_URL'] . $url; + $urlRoot = $_ENV['DEV_URL']; + } + $rList =[]; + $submitters =[]; + foreach ($entityManager->getRepository(Members::class)->findAll() as $member) { + $isSigned = $member->getMainSigneds()->filter(function (MainSigned $sign) use ($member) { + return $sign->getMembers()->getId() == $member->getId(); + })->first(); + if(!$isSigned instanceof MainSigned) { + $isSigned = new MainSigned(); + $isSigned->setMembers($member); + $isSigned->setMain($main); + $isSigned->setSubmiterId(0); + $entityManager->persist($isSigned); + //$entityManager->flush(); + $submitters[] = [ + 'role' => 'member_'.$member->getId(), + 'email' => $member->getEmail(), + ]; + $rList[$member->getId()] = $isSigned; + } + $agConvocation = new AgConvocation(); + $agConvocation->setData($main,$kernel,$member); + $agConvocation->generateConvocation(); + $content = $agConvocation->Output('S'); + $fileName = 'convocation_ag_ecosplay_' . $main->getAgDateAt()->format('Ymd') . '.pdf'; + $mailer->send($member->getEmail(),$member->getCiv()." ".$member->getSurname().' '.$member->getName(),"[E-Cosplay] Convocation assemblée générale","mails/ag_convocation.twig",[ + 'main' => $main, + 'member' => $member, + ],[ + new DataPart($content,$fileName,"application/pdf"), + ]); + + } + $data = [ + 'name' => 'ADH E-COSPLAY '.$main->getAgDateAt()->format('d/m/Y'), + 'documents' => [ + [ + 'name' => 'adh-ecosplay-'.$main->getAgDateAt()->format('d-m-Y'), + 'file' => $url + ] + ], + 'completed_redirect_url' => $urlRoot.$this->generateUrl('app_adh',['id'=>$main->getId(),'validateResult'=>true]), + 'submitters' => $submitters + ]; + $docuseal = new \Docuseal\Api('pgAU116mCFmeF7WQSezHqxtZW8V1fgo31u5d2FXoaKe', 'https://signature.esy-web.dev/api'); + $content = $docuseal->createSubmissionFromPdf($data); + foreach ($content['submitters'] as $submitter) { + /** @var MainSigned $role */ + $role = $rList[str_replace("member_","",$submitter['role'])]; + $role->setSubmiterId($submitter['id']); + $entityManager->persist($role); + } + $entityManager->flush(); + return $this->redirectToRoute('admin_ag'); + } if($request->query->has('idGenerate')) { /** @var Main $main */ $main = $mainRepository->find($request->query->get('idGenerate')); @@ -317,6 +433,7 @@ class AdminController extends AbstractController file_put_contents($tempFilePath, $content); $file = new UploadedFile($tempFilePath,$fileName,"application/pdf",0,true); $main->setAg($file); + $main->setUpdateAt(new \DateTimeImmutable()); $main->setIsClosed(true); $entityManager->persist($main); $entityManager->flush(); @@ -334,6 +451,7 @@ class AdminController extends AbstractController $form = $this->createForm(AgType::class,$ag); $form->handleRequest($request); if($form->isSubmitted() && $form->isValid()){ + $entityManager->persist($ag); $entityManager->flush(); return $this->redirectToRoute('admin_ag'); @@ -345,7 +463,7 @@ class AdminController extends AbstractController #[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 = $this->createForm(AgTypeEdit::class,$ag); $form->handleRequest($request); if($form->isSubmitted() && $form->isValid()){ $entityManager->persist($ag); @@ -413,6 +531,7 @@ class AdminController extends AbstractController return $this->redirectToRoute('admin_ag_edit',['id'=>$ag->getId()]); } + return $this->render('admin/ag/edit.twig', [ 'ag' => $form->createView(), 'agMain' => $ag, diff --git a/src/Controller/ContactController.php b/src/Controller/ContactController.php index 1714321..c35d0e9 100644 --- a/src/Controller/ContactController.php +++ b/src/Controller/ContactController.php @@ -25,7 +25,7 @@ use Twig\Environment; class ContactController extends AbstractController { - #[Route(path: '/contact', name: 'app_contact', options: ['sitemap' => true], methods: ['GET','POST'])] + #[Route(path: '/contact', name: 'app_contact', options: ['sitemap' => false], methods: ['GET','POST'])] public function index(Request $request,Mailer $mailer): Response { $dto = new DtoContact(); diff --git a/src/Controller/DocumentController.php b/src/Controller/DocumentController.php new file mode 100644 index 0000000..10aad46 --- /dev/null +++ b/src/Controller/DocumentController.php @@ -0,0 +1,37 @@ + false], methods: ['GET','POST'])] + public function index(MainRepository $mainRepository): Response + { + return $this->render('doc.twig',[ + 'ag_ordinaire' => $mainRepository->findBy(['isSigned'=>true,'type'=>'Ordinaire'],['agDateAt'=>'DESC']), + 'ag_extraordinaire' => $mainRepository->findBy(['isSigned'=>true,'type'=>'Extraordinaire'],['agDateAt'=>'DESC']), + ]); + } +} diff --git a/src/Dto/Ag/AgType.php b/src/Dto/Ag/AgType.php index 7c36ccc..e1175df 100644 --- a/src/Dto/Ag/AgType.php +++ b/src/Dto/Ag/AgType.php @@ -56,10 +56,7 @@ class AgType extends AbstractType return $member->getPseudo()." - ".$member->getRole(); } ]) - ->add('closedAt',DateTimeType::class,[ - 'label' => 'Date de cloture', - 'required' => true, - ]) + ; } diff --git a/src/Dto/Ag/AgTypeEdit.php b/src/Dto/Ag/AgTypeEdit.php new file mode 100644 index 0000000..e6166fc --- /dev/null +++ b/src/Dto/Ag/AgTypeEdit.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' => false, + ]) + ; + } + + 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 index 3045394..395d068 100644 --- a/src/Entity/Ag/Main.php +++ b/src/Entity/Ag/Main.php @@ -69,7 +69,6 @@ class Main #[Vich\UploadableField(mapping: 'ag',fileNameProperty: 'agFileName', size: 'agSize', mimeType: 'agMineType', originalName: 'agOriginalName',dimensions: 'agDimensions')] private ?File $ag = null; - #[ORM\Column(nullable: true)] private ?string $agFileName = null; #[ORM\Column(nullable: true)] @@ -80,6 +79,21 @@ class Main private ?string $agMineType = null; #[ORM\Column(length: 255,nullable: true)] private ?string $agOriginalName = null; + + + #[Vich\UploadableField(mapping: 'ag_adh',fileNameProperty: 'agAdhFileName', size: 'agAdhSize', mimeType: 'agAdhMineType', originalName: 'agAdhOriginalName',dimensions: 'agAdhDimensions')] + private ?File $agAdh = null; + + #[ORM\Column(nullable: true)] + private ?string $agAdhFileName = null; + #[ORM\Column(nullable: true)] + private ?array $agAdhDimensions = []; + #[ORM\Column(length: 255,nullable: true)] + private ?string $agAdhSize = null; + #[ORM\Column(length: 255,nullable: true)] + private ?string $agAdhMineType = null; + #[ORM\Column(length: 255,nullable: true)] + private ?string $agAdhOriginalName = null; #[ORM\Column(nullable: true)] private ?\DateTimeImmutable $updateAt; @@ -88,6 +102,15 @@ class Main #[ORM\Column(nullable: true)] private ?string $submiterId = null; + + #[ORM\Column(nullable: true)] + private ?bool $isAttestation = null; + + /** + * @var Collection + */ + #[ORM\OneToMany(targetEntity: MainSigned::class, mappedBy: 'main')] + private Collection $mainSigneds; /** * @return File|null */ @@ -109,6 +132,7 @@ class Main $this->mainMembers = new ArrayCollection(); $this->orders = new ArrayCollection(); $this->mainVote = new ArrayCollection(); + $this->mainSigneds = new ArrayCollection(); } public function getId(): ?int @@ -441,4 +465,142 @@ class Main return $this; } + + /** + * @return File|null + */ + public function getAgAdh(): ?File + { + return $this->agAdh; + } + + /** + * @return array|null + */ + public function getAgAdhDimensions(): ?array + { + return $this->agAdhDimensions; + } + + /** + * @return string|null + */ + public function getAgAdhFileName(): ?string + { + return $this->agAdhFileName; + } + + /** + * @return string|null + */ + public function getAgAdhMineType(): ?string + { + return $this->agAdhMineType; + } + + /** + * @return string|null + */ + public function getAgAdhOriginalName(): ?string + { + return $this->agAdhOriginalName; + } + + /** + * @return string|null + */ + public function getAgAdhSize(): ?string + { + return $this->agAdhSize; + } + + /** + * @param File|null $agAdh + */ + public function setAgAdh(?File $agAdh): void + { + $this->agAdh = $agAdh; + } + + /** + * @param array|null $agAdhDimensions + */ + public function setAgAdhDimensions(?array $agAdhDimensions): void + { + $this->agAdhDimensions = $agAdhDimensions; + } + + /** + * @param string|null $agAdhFileName + */ + public function setAgAdhFileName(?string $agAdhFileName): void + { + $this->agAdhFileName = $agAdhFileName; + } + + /** + * @param string|null $agAdhMineType + */ + public function setAgAdhMineType(?string $agAdhMineType): void + { + $this->agAdhMineType = $agAdhMineType; + } + + /** + * @param string|null $agAdhOriginalName + */ + public function setAgAdhOriginalName(?string $agAdhOriginalName): void + { + $this->agAdhOriginalName = $agAdhOriginalName; + } + + /** + * @param string|null $agAdhSize + */ + public function setAgAdhSize(?string $agAdhSize): void + { + $this->agAdhSize = $agAdhSize; + } + + public function isAttestation(): ?bool + { + return $this->isAttestation; + } + + public function setIsAttestation(bool $isAttestation): static + { + $this->isAttestation = $isAttestation; + + return $this; + } + + /** + * @return Collection + */ + public function getMainSigneds(): Collection + { + return $this->mainSigneds; + } + + public function addMainSigned(MainSigned $mainSigned): static + { + if (!$this->mainSigneds->contains($mainSigned)) { + $this->mainSigneds->add($mainSigned); + $mainSigned->setMain($this); + } + + return $this; + } + + public function removeMainSigned(MainSigned $mainSigned): static + { + if ($this->mainSigneds->removeElement($mainSigned)) { + // set the owning side to null (unless already changed) + if ($mainSigned->getMain() === $this) { + $mainSigned->setMain(null); + } + } + + return $this; + } } diff --git a/src/Entity/Ag/MainSigned.php b/src/Entity/Ag/MainSigned.php new file mode 100644 index 0000000..861112c --- /dev/null +++ b/src/Entity/Ag/MainSigned.php @@ -0,0 +1,67 @@ +id; + } + + public function getMain(): ?Main + { + return $this->main; + } + + public function setMain(?Main $main): static + { + $this->main = $main; + + return $this; + } + + public function getMembers(): ?Members + { + return $this->members; + } + + public function setMembers(?Members $members): static + { + $this->members = $members; + + return $this; + } + + public function getSubmiterId(): ?string + { + return $this->submiterId; + } + + public function setSubmiterId(string $submiterId): static + { + $this->submiterId = $submiterId; + + return $this; + } +} diff --git a/src/Entity/Members.php b/src/Entity/Members.php index dda4e67..0f243de 100644 --- a/src/Entity/Members.php +++ b/src/Entity/Members.php @@ -4,6 +4,7 @@ namespace App\Entity; use App\Entity\Ag\Main; use App\Entity\Ag\MainMember; +use App\Entity\Ag\MainSigned; use App\Entity\Ag\MainVote; use App\Repository\MembersRepository; use Doctrine\Common\Collections\ArrayCollection; @@ -103,6 +104,12 @@ class Members #[ORM\Column(length: 255, nullable: true)] private ?string $civ = null; + /** + * @var Collection + */ + #[ORM\OneToMany(targetEntity: MainSigned::class, mappedBy: 'members')] + private Collection $mainSigneds; + public function __construct() { $this->membersCotisations = new ArrayCollection(); @@ -110,6 +117,7 @@ class Members $this->secretaire = new ArrayCollection(); $this->mainMembers = new ArrayCollection(); $this->agVotes = new ArrayCollection(); + $this->mainSigneds = new ArrayCollection(); } public function getId(): ?int @@ -538,4 +546,34 @@ class Members return $this; } + + /** + * @return Collection + */ + public function getMainSigneds(): Collection + { + return $this->mainSigneds; + } + + public function addMainSigned(MainSigned $mainSigned): static + { + if (!$this->mainSigneds->contains($mainSigned)) { + $this->mainSigneds->add($mainSigned); + $mainSigned->setMembers($this); + } + + return $this; + } + + public function removeMainSigned(MainSigned $mainSigned): static + { + if ($this->mainSigneds->removeElement($mainSigned)) { + // set the owning side to null (unless already changed) + if ($mainSigned->getMembers() === $this) { + $mainSigned->setMembers(null); + } + } + + return $this; + } } diff --git a/src/EventSubscriber/SitemapSubscriber.php b/src/EventSubscriber/SitemapSubscriber.php index d2d6664..0b6e2ef 100644 --- a/src/EventSubscriber/SitemapSubscriber.php +++ b/src/EventSubscriber/SitemapSubscriber.php @@ -86,6 +86,24 @@ class SitemapSubscriber } $urlContainer->addUrl($urlDons, 'default'); + $urlContact = new UrlConcrete($urlGenerator->generate('app_contact', [], UrlGeneratorInterface::ABSOLUTE_URL)); + $urlContact = new GoogleImageUrlDecorator($urlContact); + $urlContact->addImage(new GoogleImage($this->cacheManager->resolve('assets/images/logo.jpg','webp'))); + $urlContact = new GoogleMultilangUrlDecorator($urlContact); + foreach ($langs as $lang) { + $urlContact->addLink($urlGenerator->generate('app_contact',['lang'=>$lang], UrlGeneratorInterface::ABSOLUTE_URL), $lang); + } + $urlContainer->addUrl($urlContact, 'default'); + + $urlDoc = new UrlConcrete($urlGenerator->generate('app_doc', [], UrlGeneratorInterface::ABSOLUTE_URL)); + $urlDoc = new GoogleImageUrlDecorator($urlDoc); + $urlDoc->addImage(new GoogleImage($this->cacheManager->resolve('assets/images/logo.jpg','webp'))); + $urlDoc = new GoogleMultilangUrlDecorator($urlDoc); + foreach ($langs as $lang) { + $urlContact->addLink($urlGenerator->generate('app_doc',['lang'=>$lang], UrlGeneratorInterface::ABSOLUTE_URL), $lang); + } + $urlContainer->addUrl($urlDoc, 'default'); + $urlAbout = new UrlConcrete($urlGenerator->generate('app_about', [], UrlGeneratorInterface::ABSOLUTE_URL)); $decoratedUrlAbout = new GoogleImageUrlDecorator($urlAbout); $decoratedUrlAbout->addImage(new GoogleImage($this->cacheManager->resolve('assets/images/logo.jpg','webp'))); diff --git a/src/Repository/Ag/MainSignedRepository.php b/src/Repository/Ag/MainSignedRepository.php new file mode 100644 index 0000000..ca20e1b --- /dev/null +++ b/src/Repository/Ag/MainSignedRepository.php @@ -0,0 +1,43 @@ + + */ +class MainSignedRepository extends ServiceEntityRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, MainSigned::class); + } + + // /** + // * @return MainSigned[] Returns an array of MainSigned 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): ?MainSigned + // { + // return $this->createQueryBuilder('m') + // ->andWhere('m.exampleField = :val') + // ->setParameter('val', $value) + // ->getQuery() + // ->getOneOrNullResult() + // ; + // } +} diff --git a/src/Service/Mailer/Mailer.php b/src/Service/Mailer/Mailer.php index baad40c..51b773b 100644 --- a/src/Service/Mailer/Mailer.php +++ b/src/Service/Mailer/Mailer.php @@ -83,11 +83,18 @@ class Mailer ]); $htmlContent = $this->convertMjmlToHtml($mjmlGenerator); - + $txtContent = $this->environment->render('txt-'.$template,[ + 'system' => [ + 'subject' => $subject, + 'path' => $_ENV['PATH_URL'], + ], + 'datas' => $data, + ]); foreach ($files as $file) { $mail->addPart($file); } $mail->html($htmlContent); + $mail->text($txtContent); $this->mailer->send($mail); } diff --git a/src/Service/Pdf/AgAdh.php b/src/Service/Pdf/AgAdh.php new file mode 100644 index 0000000..5219c75 --- /dev/null +++ b/src/Service/Pdf/AgAdh.php @@ -0,0 +1,239 @@ + 'E-Cosplay Association', + 'address' => '42 rue de saint-quentin', + 'city' => '02800 Beautor', + 'rna' => 'W022006988', + 'type' => 'Association loi 1901 à but non lucratif', + ]; + + public function setData(Main $main, KernelInterface $kernel,EntityManagerInterface $entityManager) + { + $this->main = $main; + $this->kernel = $kernel; + $this->em = $entityManager; + } + + /** + * En-tête du document (Informations de l'association et titre - NOUVELLE IMPLÉMENTATION) + */ + public function Header() + { + // --- 1. Définition des constantes de position --- + $yPos = 10; + $xPos = 10; + $logoWidth = 20; + $logoHeight = 10; + + // --- 2. Logo (Haut Gauche) --- + $logoPath = $this->kernel->getProjectDir() . '/public/assets/images/logo.jpg'; + if (file_exists($logoPath)) { + $this->Image($logoPath, $xPos, $yPos, $logoWidth, $logoHeight); + } + + // --- 3. Titre de l'Association (Haut Centré) --- + $this->SetY($yPos); + $this->SetFont('Arial', 'B', 14); + $this->SetTextColor(0, 0, 0); + $this->Cell(0, 7, utf8_decode('E-Cosplay Association'), 0, 1, 'C'); + + $this->SetFont('Arial', '', 10); + $this->Cell(0, 5, utf8_decode('Siège social : 42 rue de saint-quentin 02800 Beautor'), 0, 1, 'C'); + + // Ligne de séparation + $this->Ln(3); + $this->Cell(0, 1, '', 'B', 1, 'L', false); + $this->Ln(10); + } + + /** + * Pied de page du document + */ + public function Footer() + { + // Positionnement à 15 mm du bas + $this->SetY(-15); + // Police Arial italique 8 + $this->SetFont('Arial', 'I', 8); + $this->SetTextColor(120, 120, 120); + // Numéro de page + $this->Cell(0, 10, utf8_decode('Page ' . $this->PageNo() . '/{nb}'), 0, 0, 'C'); + } + + /** + * Bloc d'information contextuelle pour l'Assemblée Générale. + */ + public function writeAgContextBlock() + { + // Récupération des données nécessaires + $agDate = $this->main->getAgDateAt() ? $this->main->getAgDateAt()->format('d/m/Y') : 'Date non spécifiée'; + $agTime = $this->main->getAgDateAt() ? $this->main->getAgDateAt()->format('H\hi') : 'Heure non spécifiée'; + + $this->SetFont('Arial', '', 10); + $this->SetTextColor(0, 0, 0); + + // Largeur de la cellule de données (pour aligner les données) + $dataCellWidth = 100; + $labelCellWidth = 60; + + // Message introductif pour la convocation + $text = utf8_decode("Cher(e) membre de l'Association E-Cosplay,"); + $this->MultiCell(0, 6, $text); + $this->Ln(3); + $text = utf8_decode("Vous êtes cordialement convoqué(e) à l'Assemblée Générale de notre association dont les détails sont les suivants :"); + $this->MultiCell(0, 6, $text); + $this->Ln(5); + + // Ajout du cadre + $this->Cell(0, 1, '', 'T', 1, 'L', false); // Ligne supérieure légère + $this->Ln(2); // Petite marge après la ligne + + $this->SetFont('Arial', 'B', 10); + $this->Cell(0, 5, utf8_decode('DÉTAILS DE L\'ASSEMBLÉE GÉNÉRALE'), 0, 1, 'C'); + $this->Ln(1); + + $this->SetFont('Arial', '', 10); + + // Ligne 1: Association / RNA + $this->SetFont('Arial', 'B', 10); + $this->Cell($labelCellWidth, 5, utf8_decode('Association :'), 0, 0, 'L'); + $this->SetFont('Arial', '', 10); + $this->Cell($dataCellWidth, 5, utf8_decode('E-Cosplay Association loi 1901 – RNA N°W022006988'), 0, 1, 'L'); + + // Ligne 2: Siège social + $this->SetFont('Arial', 'B', 10); + $this->Cell($labelCellWidth, 5, utf8_decode('Siège social :'), 0, 0, 'L'); + $this->SetFont('Arial', '', 10); + $this->Cell($dataCellWidth, 5, utf8_decode('42 rue de saint-quentin 02800 Beautor'), 0, 1, 'L'); + + // Ligne 3: Date de l'AG + $this->SetFont('Arial', 'B', 10); + $this->Cell($labelCellWidth, 5, utf8_decode("Date de l'Assemblée Générale :"), 0, 0, 'L'); + $this->SetFont('Arial', '', 10); + $this->Cell($dataCellWidth, 5, utf8_decode($agDate), 0, 1, 'L'); + + // Ligne 4: Heure de début + $this->SetFont('Arial', 'B', 10); + $this->Cell($labelCellWidth, 5, utf8_decode('Heure de début de la séance :'), 0, 0, 'L'); + $this->SetFont('Arial', '', 10); + $this->Cell($dataCellWidth, 5, utf8_decode($agTime), 0, 1, 'L'); + + // Ligne 5: Lieu (Dynamique) + $this->SetFont('Arial', 'B', 10); + $this->Cell($labelCellWidth, 5, utf8_decode('Lieu :'), 0, 0, 'L'); + $this->SetFont('Arial', '', 10); + // Utiliser l'opérateur de coalescence null si les getters peuvent retourner null + $location = $this->main->getLocate() . " " . $this->main->getLocateZipcode() . " " . $this->main->getLocateCity(); + $this->Cell($dataCellWidth, 5, utf8_decode($location), 0, 1, 'L'); + + // Ligne 6: Nature de l'AG (Dynamique) + $this->SetFont('Arial', 'B', 10); + $this->Cell($labelCellWidth, 5, utf8_decode("Nature de l'AG :"), 0, 0, 'L'); + $this->SetFont('Arial', '', 10); + $this->Cell($dataCellWidth, 5, utf8_decode($this->main->getType()), 0, 1, 'L'); + + // Fin de la mise en forme du bloc + $this->Ln(2); // Petite marge avant la ligne + $this->Cell(0, 1, '', 'T', 1, 'L', false); // Ligne inférieure légère + $this->Ln(5); // Espacement final avant le corps du texte + } + + /** + * Méthode principale pour générer le PDF + */ + public function GeneratePdf() + { + $this->AliasNbPages(); // Active le comptage total des pages + $this->AddPage(); + + // ---------------------------------------------------- + // 1. Informations Générales de l'AG (Utilisation du nouveau bloc) + // ---------------------------------------------------- + $this->writeAgContextBlock(); + + // ---------------------------------------------------- + // 2. Listing des Membres de l'AG + // ---------------------------------------------------- + $membersCount = $this->em->getRepository(Members::class)->count([]); + + $this->SetFont('Arial', 'B', 11); + $this->SetFillColor(230, 230, 230); + $this->Cell(0, 7, utf8_decode('Liste de tous les Membres de l\'association (' . $membersCount . ' membres)'), 0, 1, 'L', true); + + // En-têtes du tableau (Hauteur 7) + $idCellWidth = 5; + $signatureCellWidth = 105; + + // Nouvelle répartition des largeurs pour utiliser 190mm: 5 (id) + 60 (name) + 75 (email) + 50 (signature) = 190 + $nameCellWidth = 40; + $emailCellWidth = 40; + + $this->SetFont('Arial', 'B', 9); + $this->SetFillColor(200, 220, 255); + $this->Cell($idCellWidth, 7, utf8_decode('#'), 1, 0, 'C', true); + $this->Cell($nameCellWidth, 7, utf8_decode('Nom Prénom / Pseudo'), 1, 0, 'L', true); + $this->Cell($emailCellWidth, 7, utf8_decode('E-mail.'), 1, 0, 'L', true); + $this->Cell($signatureCellWidth, 7, utf8_decode('Signature de Présence'), 1, 1, 'C', true); + + // Lignes du tableau + $this->SetFont('Arial', '', 9); + $i = 1; + $fill = false; + $cellHeight = 15; // Hauteur de cellule définie à 15 + + // Récupérer tous les membres + $members = $this->em->getRepository(Members::class)->findAll(); + + foreach ($members as $member) { + // Alternance des couleurs de fond + $this->SetFillColor($fill ? 240 : 255, 240, 240); + $fill = !$fill; + + // Affichage des informations + $this->Cell($idCellWidth, $cellHeight, $i++, 1, 0, 'C', true); + $this->Cell($nameCellWidth, $cellHeight, + utf8_decode($member->getName() . ' ' . $member->getSurname() . ' (' . $member->getPseudo() . ')'), + 1, 0, 'C', true + ); + $this->Cell($emailCellWidth, $cellHeight, + utf8_decode($member->getEmail()), + 1, 0, 'C' , true + ); + + + // Espace pour la signature (avec le placeholder de signature) - Largeur de la cellule et du placeholder = 50. Alignement 'C' + $this->Cell($signatureCellWidth, $cellHeight, '{{Signature;width=250;height=25;type=signature;role=member_'.$member->getId().'}}', 1, 1, 'C', true); + + // Gérer les sauts de page automatiques + if ($this->GetY() + $cellHeight > 280) { // Si la prochaine ligne dépasse la limite + $this->AddPage(); + // Ré-afficher les en-têtes du tableau sur la nouvelle page (Hauteur 7) + $this->SetFont('Arial', 'B', 9); + $this->SetFillColor(200, 220, 255); + $this->Cell($idCellWidth, 7, utf8_decode('#'), 1, 0, 'C', true); + $this->Cell($nameCellWidth, 7, utf8_decode('Nom Prénom / Pseudo'), 1, 0, 'L', true); + $this->Cell($emailCellWidth, 7, utf8_decode('E-mail.'), 1, 0, 'L', true); + $this->Cell($signatureCellWidth, 7, utf8_decode('Signature de Présence'), 1, 1, 'C', true); + $this->SetFont('Arial', '', 9); + $this->SetFillColor(240, 240, 240); + } + } + + } +} diff --git a/src/Service/Pdf/AgConvocation.php b/src/Service/Pdf/AgConvocation.php new file mode 100644 index 0000000..92d9b73 --- /dev/null +++ b/src/Service/Pdf/AgConvocation.php @@ -0,0 +1,235 @@ +SetAutoPageBreak(true, 20); // Marge inférieure de 20mm + } + + /** + * Définit les données de l'Assemblée Générale et le membre destinataire. + */ + public function setData(Main $main, KernelInterface $kernel, \App\Entity\Members $member) + { + $this->main = $main; + $this->kernel = $kernel; + $this->member = $member; + } + + /** + * Définit l'en-tête de chaque page (logo et titre). + */ + public function Header() + { + // --- 1. Définition des constantes de position --- + $yPos = 10; + $xPos = 10; + $logoWidth = 20; + $logoHeight = 10; + + // --- 2. Logo (Haut Gauche) --- + $logoPath = $this->kernel->getProjectDir() . '/public/assets/images/logo.jpg'; + if (file_exists($logoPath)) { + $this->Image($logoPath, $xPos, $yPos, $logoWidth, $logoHeight); + } + + // --- 3. Titre de l'Association (Haut Centré) --- + $this->SetY($yPos); + $this->SetFont('Arial', 'B', 14); + $this->SetTextColor(0, 0, 0); + $this->Cell(0, 7, utf8_decode('E-Cosplay Association'), 0, 1, 'C'); + + $this->SetFont('Arial', '', 10); + $this->Cell(0, 5, utf8_decode('Siège social : 42 rue de saint-quentin 02800 Beautor'), 0, 1, 'C'); + + // Ligne de séparation + $this->Ln(3); + $this->Cell(0, 1, '', 'B', 1, 'L', false); + $this->Ln(10); + } + + /** + * Définit le pied de page (numéro de page si besoin). + */ + public function Footer() + { + // Positionnement à 15 mm du bas + $this->SetY(-15); + // Police Arial italique 8 + $this->SetFont('Arial', 'I', 8); + // Numéro de page + $this->Cell(0, 10, 'Page ' . $this->PageNo() . '/{nb}', 0, 0, 'C'); + } + + /** + * Génère la convocation complète. + */ + public function generateConvocation() + { + $this->AddPage(); + $this->writeRecipientBlock(); + $this->writeConvocationTitle(); + $this->writeAgContextBlock(); // Remplacement de writeDetailsBlock + $this->writeClosure(); + } + + /** + * Écrit le bloc destinataire (en haut à droite), en incluant l'adresse postale. + */ + private function writeRecipientBlock() + { + // Positionnement à droite (Marge - Largeur du bloc destinataire) + $this->SetX(120); + $this->SetFont('Arial', '', 10); + + $civility = $this->member->getCiv(); + $name = $this->member->getName(); + $surname = $this->member->getSurname(); + + + $this->Cell(0, 5, utf8_decode("À l'attention de :"), 0, 1, 'L'); + $this->SetX(120); + $this->SetFont('Arial', 'B', 10); + $this->Cell(0, 5, utf8_decode("$civility $name $surname"), 0, 1, 'L');; + + $this->Ln(10); + } + + /** + * Écrit le titre principal de la convocation. + */ + private function writeConvocationTitle() + { + $this->SetFont('Arial', 'B', 16); + $this->Cell(0, 10, utf8_decode("CONVOCATION À L'ASSEMBLÉE GÉNÉRALE"), 0, 1, 'C'); + $this->Ln(5); + } + + /** + * Écrit le bloc d'informations légales et contextuelles de l'AG (copié depuis AgGenerator). + */ + public function writeAgContextBlock() + { + // Récupération des données nécessaires + $agDate = $this->main->getAgDateAt() ? $this->main->getAgDateAt()->format('d/m/Y') : 'Date non spécifiée'; + $agTime = $this->main->getAgDateAt() ? $this->main->getAgDateAt()->format('H\hi') : 'Heure non spécifiée'; + + $this->SetFont('Arial', '', 10); + $this->SetTextColor(0, 0, 0); + + // Largeur de la cellule de données (pour aligner les données) + $dataCellWidth = 100; + $labelCellWidth = 60; + + // Message introductif pour la convocation + $text = utf8_decode("Cher(e) membre de l'Association E-Cosplay,"); + $this->MultiCell(0, 6, $text); + $this->Ln(3); + $text = utf8_decode("Vous êtes cordialement convoqué(e) à l'Assemblée Générale de notre association dont les détails sont les suivants :"); + $this->MultiCell(0, 6, $text); + $this->Ln(5); + + // Ajout du cadre + $this->Cell(0, 1, '', 'T', 1, 'L', false); // Ligne supérieure légère + $this->Ln(2); // Petite marge après la ligne + + $this->SetFont('Arial', 'B', 10); + $this->Cell(0, 5, utf8_decode('DÉTAILS DE L\'ASSEMBLÉE GÉNÉRALE'), 0, 1, 'C'); + $this->Ln(1); + + $this->SetFont('Arial', '', 10); + + // Ligne 1: Association / RNA + $this->SetFont('Arial', 'B', 10); + $this->Cell($labelCellWidth, 5, utf8_decode('Association :'), 0, 0, 'L'); + $this->SetFont('Arial', '', 10); + $this->Cell($dataCellWidth, 5, utf8_decode('E-Cosplay Association loi 1901 – RNA N°W022006988'), 0, 1, 'L'); + + // Ligne 2: Siège social + $this->SetFont('Arial', 'B', 10); + $this->Cell($labelCellWidth, 5, utf8_decode('Siège social :'), 0, 0, 'L'); + $this->SetFont('Arial', '', 10); + $this->Cell($dataCellWidth, 5, utf8_decode('42 rue de saint-quentin 02800 Beautor'), 0, 1, 'L'); + + // Ligne 3: Date de l'AG + $this->SetFont('Arial', 'B', 10); + $this->Cell($labelCellWidth, 5, utf8_decode("Date de l'Assemblée Générale :"), 0, 0, 'L'); + $this->SetFont('Arial', '', 10); + $this->Cell($dataCellWidth, 5, utf8_decode($agDate), 0, 1, 'L'); + + // Ligne 4: Heure de début + $this->SetFont('Arial', 'B', 10); + $this->Cell($labelCellWidth, 5, utf8_decode('Heure de début de la séance :'), 0, 0, 'L'); + $this->SetFont('Arial', '', 10); + $this->Cell($dataCellWidth, 5, utf8_decode($agTime), 0, 1, 'L'); + + // Ligne 5: Lieu (Dynamique) + $this->SetFont('Arial', 'B', 10); + $this->Cell($labelCellWidth, 5, utf8_decode('Lieu :'), 0, 0, 'L'); + $this->SetFont('Arial', '', 10); + $this->Cell($dataCellWidth, 5, utf8_decode($this->main->getLocate() . " " . $this->main->getLocateZipcode() . " " . $this->main->getLocateCity()), 0, 1, 'L'); + + // Ligne 6: Nature de l'AG (Dynamique) + $this->SetFont('Arial', 'B', 10); + $this->Cell($labelCellWidth, 5, utf8_decode("Nature de l'AG :"), 0, 0, 'L'); + $this->SetFont('Arial', '', 10); + $this->Cell($dataCellWidth, 5, utf8_decode($this->main->getType()), 0, 1, 'L'); + + // Fin de la mise en forme du bloc + $this->Ln(2); // Petite marge avant la ligne + $this->Cell(0, 1, '', 'T', 1, 'L', false); // Ligne inférieure légère + $this->Ln(5); // Espacement final avant le corps du texte + } + + /** + * Écrit la formule de politesse et les informations de signature, y compris l'avertissement en rouge. + */ + private function writeClosure() + { + $agCity = $this->main->getLocateCity() ?? 'Lieu non défini'; + $agDate = $this->main->getAgDateAt() ? $this->main->getAgDateAt()->format('d F Y') : 'Date non définie'; + $presidentCiv = $this->main->getPresident()->getCiv(); + $presidentTitle = ($presidentCiv === 'Mme') ? 'Présidente' : 'Président'; + $presidentFullName = $this->main->getPresident()->getName() . ' ' . $this->main->getPresident()->getSurname(); + + $this->SetFont('Arial', '', 10); + $text = utf8_decode("Nous vous rappelons que votre présence est essentielle pour la vie démocratique de l'association. En cas d'impossibilité d'être présent, vous pouvez vous faire représenter en donnant une procuration à un autre membre de votre choix."); + $this->MultiCell(0, 6, $text); + $this->Ln(3); + + // --- AJOUT DE LA MENTION ROUGE SUR LE VOTE --- + $this->SetTextColor(255, 0, 0); // Rouge + $this->SetFont('Arial', 'B', 10); + $warningText = utf8_decode("ATTENTION : Seuls les membres à jour de leurs adhésions pourront voter."); + $this->MultiCell(0, 6, $warningText, 0, 'C'); + $this->SetTextColor(0, 0, 0); // Réinitialiser en Noir + $this->SetFont('Arial', '', 10); // Réinitialiser la police + $this->Ln(5); + + $this->MultiCell(0, 6, utf8_decode("Dans l'attente de vous accueillir, veuillez agréer, cher(e) membre, l'expression de nos salutations distinguées.")); + $this->Ln(10); + + // Lieu et Date + $this->Cell(0, 5, utf8_decode("Fait à $agCity, le $agDate"), 0, 1, 'R'); + $this->Ln(10); + + // Signature + $this->Cell(0, 5, utf8_decode("Le $presidentTitle de l'Association,"), 0, 1, 'R'); + $this->Ln(15); // Espace pour la signature + $this->SetFont('Arial', 'B', 10); + $this->Cell(0, 5, utf8_decode("$presidentCiv $presidentFullName"), 0, 1, 'R'); + $this->SetFont('Arial', '', 10); + } +} diff --git a/src/Service/Pdf/AgGenerator.php b/src/Service/Pdf/AgGenerator.php index 6453863..459309a 100644 --- a/src/Service/Pdf/AgGenerator.php +++ b/src/Service/Pdf/AgGenerator.php @@ -361,7 +361,7 @@ class AgGenerator extends Fpdf $this->Ln(5); $this->SetFont('Arial', '', 10); - $this->Cell(95, 5, utf8_decode("{{Sign;type=signature}}"), 0, 0, 'C'); + $this->Cell(95, 5, utf8_decode("{{Sign;type=signature,width=100}}"), 0, 0, 'C'); $this->Cell(95, 5, utf8_decode(""), 0, 1, 'C'); $this->Ln(5); $this->Cell(95, 5, utf8_decode($this->main->getPresident()->getName()." ".$this->main->getPresident()->getSurname()), 0, 0, 'C'); diff --git a/templates/adh.twig b/templates/adh.twig new file mode 100644 index 0000000..cd03e0b --- /dev/null +++ b/templates/adh.twig @@ -0,0 +1,132 @@ +{% extends 'base.twig' %} + +{# --- METADATA & SCHEMA (Already using trans) --- #} +{% block title %}{{'adh_age.title'|trans}}{% endblock %} +{% block meta_description %}{{'adh_age.description'|trans}}{% endblock %} + +{% block canonical_url %}{% endblock %} +{% block breadcrumb_schema %} + +{% endblock %} + +{# --- BODY with Tailwind Styling and full i18n --- #} +{% block body %} +
+ + +

+ {{ 'adh_age.title'|trans }} +

+ + +
+

+ {{ 'ag.info.title'|trans }} +

+ +
+ + +
+ + {{ 'ag.info.date_time'|trans }} : + + + {{ main.agDateAt | date('d/m/Y') }} {{ 'global.at'|trans }} {{ main.agDateAt | date('H:i') }} + +
+ + +
+ + {{ 'ag.info.location'|trans }} : + + + {{ main.locate }} {{ main.locateZipcode }} {{ main.locateCity }} + +
+ + +
+ + {{ 'ag.info.type'|trans }} : + + + {{ main.type }} + +
+
+
+ + +
+

+ {{ 'ag.content.signatures_list'|trans({'%count%': signed|length}) }} +

+ +
+ + + + + + + + + + + + + {% for sign in signed %} + + + + + + {% else %} + + + + {% endfor %} + +
+ {{ 'member.civility'|trans }} + + {{ 'member.name_surname'|trans }} + + {{ 'member.signature'|trans }} +
+ {{ sign.members.civ }} + + {{ sign.members.name }} {{ sign.members.surname }} + + + {{ 'global.signed_link'|trans }} + +
+ {{ 'ag.content.no_signatures_yet'|trans }} +
+
+
+ +
+{% endblock %} diff --git a/templates/adh_validate.twig b/templates/adh_validate.twig new file mode 100644 index 0000000..9ee6b48 --- /dev/null +++ b/templates/adh_validate.twig @@ -0,0 +1,54 @@ +{% extends 'base.twig' %} + +{# --- METADATA & SCHEMA (Already using trans) --- #} +{% block title %}{{'adh_page_validate.title'|trans}}{% endblock %} +{% block meta_description %}{{'adh_page_validate.description'|trans}}{% endblock %} + +{% block canonical_url %}{% endblock %} +{% block breadcrumb_schema %} + +{% endblock %} + +{# --- BODY with Tailwind Styling and full i18n --- #} +{% block body %} +
+
+ + + + + +

+ {{ 'adh_page_validate.title'|trans }} +

+ +

+ {{ 'adh_page_validate.success_message'|trans }} +

+ +

+ {{ 'adh_page_validate.thanks'|trans }} +

+ +
+
+{% endblock %} diff --git a/templates/admin/ag.twig b/templates/admin/ag.twig index 2f46683..8a656c3 100644 --- a/templates/admin/ag.twig +++ b/templates/admin/ag.twig @@ -18,62 +18,85 @@ {% 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 }} -

+ {# NOUVEAU CONTENEUR FLEX POUR DISPOSITION LATÉRALE #} +
+ + {# BLOC GAUCHE: Détails de l'AG #} +
+
+

+ {{ 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 }} +

+
+
+ {# FIN BLOC GAUCHE #} + + {# BLOC DROIT: QR CODE #} +
+

Détails AG

+ + QR Code pour {{ ag.type }} du {{ ag.agDateAt|date('d/m/Y') }} +
+ {# FIN BLOC DROIT #} +
+ {# BLOC ACTIONS (GARDÉ EN DESSOUS DES DÉTAILS) #} {% if ag.closed == false or ag.closed == null %} - {# Bloc Actions: SUPPRIMER / MODIFIER #} -
- - Générer le pdf - - {# Bouton Modifier #} - - Modifier - +
+ + Générer le pdf + + {% if ag.attestation == null or ag.attestation == false %} + + Envoyée les Convocation + + {% endif %} + {# Bouton Modifier #} + + Modifier + - {# Bouton Supprimer #} -
- + {# Bouton Supprimer #} + + - -
-
+ + +
{% else %} {% if ag.isSigned == true %}
diff --git a/templates/admin/ag/new.twig b/templates/admin/ag/new.twig index 683a99b..3457386 100644 --- a/templates/admin/ag/new.twig +++ b/templates/admin/ag/new.twig @@ -24,7 +24,6 @@

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 #}
diff --git a/templates/base.twig b/templates/base.twig index 91f6f74..033db99 100644 --- a/templates/base.twig +++ b/templates/base.twig @@ -125,6 +125,7 @@ { 'name': 'Nos membres'|trans, 'route': 'app_members' }, { 'name': 'Nos événements'|trans, 'route': 'app_events' }, { 'name': 'Boutiques'|trans, 'route': 'app_shop' }, + { 'name': 'Documents'|trans, 'route': 'app_doc' }, { 'name': 'Dons'|trans, 'route': 'app_dons' }, { 'name': 'Contact'|trans, 'route': 'app_contact' } ] %} diff --git a/templates/doc.twig b/templates/doc.twig new file mode 100644 index 0000000..60b33ca --- /dev/null +++ b/templates/doc.twig @@ -0,0 +1,115 @@ +{% extends 'base.twig' %} + +{# --- METADATA & SCHEMA (Already using trans) --- #} +{% block title %}{{'doc_page.title'|trans}}{% endblock %} +{% block meta_description %}{{'doc_page.description'|trans}}{% endblock %} + +{% block canonical_url %}{% endblock %} +{% block breadcrumb_schema %} + +{% endblock %} + +{# --- BODY with Tailwind Styling and full i18n --- #} +{% block body %} +
+ {# Main Title #} +

+ {{ 'doc_list.title'|trans }} +

+ + {# --- Ordinary General Assembly (AG Ordinaire) --- #} +
+

+ {{ 'doc_list.ag_ordinaire_title'|trans }} +

+
+ {% for ag in ag_ordinaire %} +
+
+ + {{ 'ag.date_at_prefix'|trans }} {{ ag.agDateAt|date('d/m/Y') }} + + + {{ 'ag.main_members_label'|trans }} : {{ ag.mainMembers.count }} + +
+
+
+ {{ 'ag.president_label'|trans }} : + {{ ag.president.civ }} {{ ag.president.name }} {{ ag.president.surname }} +
+
+ {{ 'ag.secretary_label'|trans }} : + {{ ag.secretaire.civ }} {{ ag.secretaire.name }} {{ ag.secretaire.surname }} +
+
+ + {{ 'ag.view_document_link'|trans }} + +
+ {% endfor %} +
+
+ + {# --- Extraordinary General Assembly (AG Extraordinaire) --- #} +
+

+ {{ 'doc_list.ag_extraordinaire_title'|trans }} +

+
+ {% for ag in ag_extraordinaire %} +
+
+ + {{ 'ag.date_at_prefix'|trans }} {{ ag.agDateAt|date('d/m/Y') }} + + + {{ 'ag.main_members_label'|trans }} : {{ ag.mainMembers.count }} + +
+
+
+ {{ 'ag.president_label'|trans }} : + {{ ag.president.civ }} {{ ag.president.name }} {{ ag.president.surname }} +
+
+ {{ 'ag.secretary_label'|trans }} : + {{ ag.secretaire.civ }} {{ ag.secretaire.name }} {{ ag.secretaire.surname }} +
+
+ + {{ 'ag.view_document_link'|trans }} + +
+ {% endfor %} +
+
+
+{% endblock %} diff --git a/templates/mails/ag_convocation.twig b/templates/mails/ag_convocation.twig new file mode 100644 index 0000000..e30182c --- /dev/null +++ b/templates/mails/ag_convocation.twig @@ -0,0 +1,111 @@ +{% extends 'mails/base.twig' %} + +{% block subject %} + Convocation à l'Assemblée Générale {{ datas.main.type|default('Ordinaire ou Extraordinaire') }} du {{ datas.main.ag_date_at_dmy|default('Date non spécifiée') }} +{% endblock %} + +{% block content %} + + + {# 1. En-tête (Logo et Titre) #} + + CONVOCATION À L'ASSEMBLÉE GÉNÉRALE + + + E-Cosplay Association + + + + + + {# 2. Bloc Destinataire (Style Courrier) #} + + + + + À l'attention de :
+ {{ datas.member.civ|default('') }} {{ datas.member.name|default('') }} {{ datas.member.surname|default('Cher Membre') }}
+ {# Ajout de l'adresse pour une référence complète (bien que ce soit un email) #} + {{ datas.member.locate|default('') }}
+ {{ datas.member.locateZipcode|default('') }} {{ datas.member.locateCity|default('') }} +
+
+
+ + {# 3. Introduction et Contexte #} + + + + Cher(e) membre de l'Association E-Cosplay, + + + Vous êtes cordialement convoqué(e) à l'Assemblée Générale ({{ datas.main.type|default('Type non défini') }}) de notre association. + + + + + {# 4. Bloc des Détails de l'AG (Mise en évidence) #} + + + + DÉTAILS DE L'ASSEMBLÉE + + + + Date : + {{ datas.main.agDateAt|date('d/m/Y') }} + + + Heure de début : + {{ datas.main.agDateAt|date('H:i') }} + + + Lieu : + {{ datas.main.locate }}, {{ datas.main.locateZipCode }} {{ datas.main.locateCity }} + + + Nature de l'AG : + {{ datas.main.type|default('Non spécifiée') }} + + + + + + {# 5. Avertissement sur les Droits de Vote (Nouveau bloc en rouge) #} + + + + ATTENTION : Seuls les membres à jour de leurs adhésions pourront voter. + + + + + {# 6. Clôture et Signature #} + + + + Nous vous rappelons que votre présence est essentielle pour la vie démocratique de l'association. En cas d'impossibilité, vous pouvez vous faire représenter par un autre membre de votre choix via procuration. + + + Dans l'attente de vous accueillir, veuillez agréer, cher(e) membre, l'expression de nos salutations distinguées. + + + + + + + + + Fait à {{ datas.main.locate_city|default('Lieu non défini') }}, le {{ datas.main.ag_date_at_dmy|default('Date non définie') }} + + + + Le {{ (datas.main.president.civ|default('M.') == 'Mme') ? 'Présidente' : 'Président' }} de l'Association, + + + {{ datas.main.president.civ|default('M.') }} {{ datas.main.president.name|default('Nom') }} {{ datas.main.president.surname|default('Prénom') }} + + + + +{% endblock %} diff --git a/templates/txt-mails/ag_convocation.twig b/templates/txt-mails/ag_convocation.twig new file mode 100644 index 0000000..608080b --- /dev/null +++ b/templates/txt-mails/ag_convocation.twig @@ -0,0 +1,43 @@ +{% extends 'txt-mails/base.twig' %} + +{% block subject %} + Convocation à l'Assemblée Générale {{ datas.main.type|default('Ordinaire ou Extraordinaire') }} du {{ datas.main.ag_date_at_dmy|default('Date non spécifiée') }} +{% endblock %} + +{% block content %} + CONVOCATION À L'ASSEMBLÉE GÉNÉRALE + =================================== + E-Cosplay Association + + --- Destinataire --- + À l'attention de : + {{ datas.member.civ|default('') }} {{ datas.member.name|default('') }} {{ datas.member.surname|default('Cher Membre') }} + {{ datas.member.locate|default('') }} + {{ datas.member.locateZipcode|default('') }} {{ datas.member.locateCity|default('') }} + -------------------- + + Cher(e) membre de l'Association E-Cosplay, + + Vous êtes cordialement convoqué(e) à l'Assemblée Générale ({{ datas.main.type|default('Type non défini') }}) de notre association. + + =================================== + DÉTAILS DE L'ASSEMBLÉE + =================================== + Nature de l'AG : {{ datas.main.type|default('Non spécifiée') }} + Date : {{ datas.main.agDateAt|date('d/m/Y') }} + Heure de début : {{ datas.main.agDateAt|date('H:i') }} + Lieu : {{ datas.main.locate }}, {{ datas.main.locateZipCode }} {{ datas.main.locateCity }} + =================================== + + ATTENTION : Seuls les membres à jour de leurs adhésions pourront voter. + ----------------------------------------------------------------------- + + Nous vous rappelons que votre présence est essentielle pour la vie démocratique de l'association. En cas d'impossibilité, vous pouvez vous faire représenter par un autre membre de votre choix via procuration. + + Dans l'attente de vous accueillir, veuillez agréer, cher(e) membre, l'expression de nos salutations distinguées. + + Fait à {{ datas.main.locate_city|default('Lieu non défini') }}, le {{ datas.main.ag_date_at_dmy|default('Date non définie') }} + + Le {{ (datas.main.president.civ|default('M.') == 'Mme') ? 'Présidente' : 'Président' }} de l'Association, + {{ datas.main.president.civ|default('M.') }} {{ datas.main.president.name|default('Nom') }} {{ datas.main.president.surname|default('Prénom') }} +{% endblock %} diff --git a/templates/txt-mails/base.twig b/templates/txt-mails/base.twig new file mode 100644 index 0000000..2c963ac --- /dev/null +++ b/templates/txt-mails/base.twig @@ -0,0 +1,20 @@ +[E-Cosplay] - {{ system.subject }} + +================================================== + +{% block content %} +{% endblock %} + +================================================== + +Si vous ne parvenez pas à cliquer sur un lien dans cet e-mail, veuillez copier et coller l'URL dans la barre d'adresse de votre navigateur. + +--- +© {{ "now"|date("Y") }} E-COSPLAY. Tous droits réservés. + +E-COSPLAY, 42 rue de saint-quentin, 02800 Beautor, France +Association loi 1901 à but non lucratif. - RNA N°W022006988 + +Retrouvez-nous sur : +Facebook : https://www.facebook.com/assocationecosplay/ +Instagram : https://www.instagram.com/asso_ecosplay/ diff --git a/templates/txt-mails/contact.twig b/templates/txt-mails/contact.twig new file mode 100644 index 0000000..2753fe2 --- /dev/null +++ b/templates/txt-mails/contact.twig @@ -0,0 +1,26 @@ +{% extends 'txt-mails/base.twig' %} + +{% block content %} + Nouveau Message de Contact + ========================= + Vous avez reçu un nouveau message via le formulaire de contact du site. + + --- DÉTAILS DE L'EXPÉDITEUR --- + + Expéditeur : {{ datas.dto.surname }} {{ datas.dto.name }} + E-mail : {{ datas.dto.email }} + {% if datas.dto.tel is not empty %}Tél : {{ datas.dto.tel }}{% endif %} + + -------------------------------- + + Objet du Message : {{ datas.dto.subject }} + + Contenu du Message : + -------------------------------------------------- + {{ datas.dto.message | raw }} + -------------------------------------------------- + + Pour répondre à l'expéditeur, utilisez l'adresse e-mail ci-dessous : + Répondre à : mailto:{{ datas.dto.email }} + +{% endblock %} diff --git a/templates/txt-mails/cota_validation.twig b/templates/txt-mails/cota_validation.twig new file mode 100644 index 0000000..917141a --- /dev/null +++ b/templates/txt-mails/cota_validation.twig @@ -0,0 +1,24 @@ +{% extends 'txt-mails/base.twig' %} + +{% block content %} + ✅ Paiement Confirmé + ==================== + + Bonjour {{ datas.pseudo }}, + + Nous vous confirmons la bonne réception de votre paiement de cotisation. Merci beaucoup pour votre soutien ! Votre adhésion est maintenant active. + + --- DÉTAILS DE VOTRE ADHÉSION --- + + Période d'Adhésion : Du {{ datas.start_at|date('d/m/Y') }} au {{ datas.end_at|date('d/m/Y') }} + + Montant Payé : {{ datas.amount|format_currency('EUR', locale='fr') }} + + --------------------------------- + + Une question ? N'hésitez pas à nous contacter. + + --- + + Cet e-mail est envoyé automatiquement. +{% endblock %} diff --git a/templates/txt-mails/coti_payment.twig b/templates/txt-mails/coti_payment.twig new file mode 100644 index 0000000..9266664 --- /dev/null +++ b/templates/txt-mails/coti_payment.twig @@ -0,0 +1,30 @@ +{% extends 'txt-mails/base.twig' %} + +{% block content %} + Votre Cotisation Annuelle + ========================= + + Bonjour {{ datas.pseudo }}, + + Nous vous remercions de votre engagement. Veuillez trouver ci-dessous les détails de votre cotisation pour la période à venir. + + --- DÉTAILS DE LA COTISATION --- + + Période : Du {{ datas.start_at|date('d/m/Y') }} au {{ datas.end_at|date('d/m/Y') }} + + Montant total : {{ datas.amount|format_currency('EUR', locale='fr') }} + + --------------------------------- + + Pour renouveler votre adhésion, veuillez procéder au paiement sécurisé en utilisant le lien ci-dessous : + + Procéder au Paiement Sécurisé : + {{ datas.link }} + + Si le lien ci-dessus ne fonctionne pas, veuillez copier-coller l'adresse suivante dans votre navigateur : + {{ datas.link }} + + --- + + Cet e-mail est envoyé automatiquement. Merci de ne pas y répondre. +{% endblock %} diff --git a/templates/txt-mails/dons.twig b/templates/txt-mails/dons.twig new file mode 100644 index 0000000..739b83b --- /dev/null +++ b/templates/txt-mails/dons.twig @@ -0,0 +1,39 @@ +{% extends 'txt-mails/base.twig' %} + +{% block content %} + Confirmation de votre Don + Un immense merci pour votre générosité ! + + ================================================== + + Nous avons bien reçu votre soutien d'un montant de : + + >>> {{ datas.don.amount|format_currency('EUR', locale='fr') }} <<< + + Bonjour {% if datas.don.name %}{{ datas.don.name }}{% else %}Cher Donateur{% endif %}, + + Votre don a été confirmé avec succès. Votre contribution est essentielle pour l'organisation de nos événements, l'achat de matériel et le maintien de nos activités. + + --- DÉTAILS DE VOTRE TRANSACTION --- + + Nom/Pseudo : {% if datas.don.name %}{{ datas.don.name }}{% else %}Anonyme{% endif %} + Montant : {{ datas.don.amount|format_currency('EUR', locale='fr') }} + + {% if datas.don.message %} + Message : + "{{ datas.don.message }}" + {% endif %} + -------------------------------------------------- + + ================================================== + + Votre reçu fiscal se trouve en pièce jointe de cet e-mail. + + NOTE IMPORTANTE : Notre association ne vous permet pas de bénéficier d'une réduction d'impôt. + + Si vous avez des questions, n'hésitez pas à nous contacter. + + Cordialement, + + L'équipe E-Cosplay +{% endblock %} diff --git a/templates/txt-mails/dons_new.twig b/templates/txt-mails/dons_new.twig new file mode 100644 index 0000000..3183b26 --- /dev/null +++ b/templates/txt-mails/dons_new.twig @@ -0,0 +1,23 @@ +{% extends 'txt-mails/base.twig' %} + +{% block content %} + ================================================== + 🚨 NOUVEAU DON ARRIVÉ ! 🚨 + ================================================== + + Un nouvel acte de générosité a été enregistré. Voici les détails : + + Montant du Don : {{ datas.don.amount|format_currency('EUR', locale='fr') }} + + --- DÉTAILS DU DONATEUR --- + Donateur : {% if datas.don.name %}{{ datas.don.name }}{% else %}Anonyme / Non spécifié{% endif %} + E-mail : {{ datas.don.email }} + --------------------------- + + {% if datas.don.message %} + Message laissé par le donateur : + -------------------------------------------------- + "{{ datas.don.message }}" + -------------------------------------------------- + {% endif %} +{% endblock %} diff --git a/templates/txt-mails/new_admin.twig b/templates/txt-mails/new_admin.twig new file mode 100644 index 0000000..522cba4 --- /dev/null +++ b/templates/txt-mails/new_admin.twig @@ -0,0 +1,25 @@ +{% extends 'txt-mails/base.twig' %} + +{% block content %} + Bonjour, + + Nous avons le plaisir de vous informer que votre compte administrateur a été créé. + + Voici vos identifiants de connexion temporaires : + -------------------------------------------------- + Nom d'utilisateur : {{ datas.username }} + Mot de passe : {{ datas.password }} + -------------------------------------------------- + + Pour des raisons de sécurité, nous vous demandons de bien vouloir modifier votre mot de passe lors de votre première connexion. + + Vous pouvez vous connecter à votre compte en utilisant le lien ci-dessous : + + Lien de connexion : {{ system.path }}{{ datas.url }} + + Si vous avez des questions ou rencontrez des difficultés, n'hésitez pas à nous contacter. + + Cordialement, + + L'équipe E-Cosplay +{% endblock %} diff --git a/templates/txt-mails/reset.twig b/templates/txt-mails/reset.twig new file mode 100644 index 0000000..94855c7 --- /dev/null +++ b/templates/txt-mails/reset.twig @@ -0,0 +1,27 @@ +{% extends 'txt-mails/base.twig' %} + +{% block content %} + Bonjour, + + {% if 'ROLE_CUSTOMER' in datas.account.roles %} + Nous avons reçu une demande de réinitialisation de mot de passe pour votre espace client. + {% else %} + Nous avons reçu une demande de réinitialisation de mot de passe pour votre compte E-Cosplay. + {% endif %} + + Pour réinitialiser votre mot de passe, veuillez cliquer sur le lien ci-dessous. Ce lien est valable pour une durée limitée. + + -------------------------------------------------- + Réinitialiser mon mot de passe : + {{ datas.resetLink }} + -------------------------------------------------- + + Ce lien expirera le {{ datas.request.expiresAt|date('d/m/Y à H:i') }}. + Veuillez l'utiliser avant cette date et heure. + + Si vous n'avez pas demandé cette réinitialisation de mot de passe, veuillez ignorer cet e-mail. Votre mot de passe actuel restera inchangé. + + Cordialement, + + L'équipe E-Cosplay +{% endblock %} diff --git a/translations/messages.cn.yaml b/translations/messages.cn.yaml index c675ef6..500eb65 100644 --- a/translations/messages.cn.yaml +++ b/translations/messages.cn.yaml @@ -616,3 +616,54 @@ shop.sales_note_main: > shop.sales_note_details: > 请注意,针对特定的 Cosplay 写真印品销售,收益分配如下:5% 用于支付协会的银行手续费,95% 直接返还给制作该印品的会员。 shop_more: '了解更多' +# Used in the section +doc_page: + title: "官方文件页面" + description: "协会的官方文件列表。" + breadcrumb: "文件" +breadcrumb: + home: "首页" + +# Used in the section +doc_list: + title: "文件列表" + ag_ordinaire_title: "普通会员大会" + ag_extraordinaire_title: "特别会员大会" + +ag: + date_at_prefix: "于" + president_label: "主席" + secretary_label: "秘书" + main_members_label: "主要参会者" + view_document_link: "查看文件" + info: + title: "会员大会详情" + date_time: "日期和时间" + location: "地点" + type: "大会类型" + content: + signatures_list: "出席签到名单 (共%count%名成员)" + no_signatures_yet: "暂未记录任何出席签名。" + +adh_age: + title: "会员大会" + description: "会员大会信息和签到跟踪。" + +adh_page: + breadcrumb: "会员大会成员" + +global: + at: "于" # Used for time, e.g., "at 10:00" -> "于 10:00" + signed_link: "已签署 (查看)" + +member: + civility: "称谓" + name_surname: "姓名" + signature: "签到状态" + +adh_page_validate: + title: "签名验证" + description: "确认您已签署会员大会文件。" + breadcrumb: "签名已验证" + success_message: "您的签名已成功记录并验证。您现在可以查看已签署的文件。" + thanks: "感谢您的参与!" diff --git a/translations/messages.en.yaml b/translations/messages.en.yaml index a7927d8..62285f0 100644 --- a/translations/messages.en.yaml +++ b/translations/messages.en.yaml @@ -684,3 +684,54 @@ shop.sales_note_main: > shop.sales_note_details: > Please note that for specific cosplay print sales, the distribution is as follows: 5% of the amount covers the association's banking fees, and 95% is directly remitted to the member who created the print. shop_more: 'Learn more' +# Used in the section +doc_page: + title: "Official Documents Page" + description: "List of the association's official documents." + breadcrumb: "Documents" +breadcrumb: + home: "Home" + +# Used in the section +doc_list: + title: "List of Documents" + ag_ordinaire_title: "Ordinary General Assemblies (OGA)" + ag_extraordinaire_title: "Extraordinary General Assemblies (EGA)" + +ag: + date_at_prefix: "On" # e.g., "On 23/11/2025" + president_label: "President" + secretary_label: "Secretary" + main_members_label: "Main Participants" + view_document_link: "View Document" + info: + title: "General Assembly Details" + date_time: "Date & Time" + location: "Location" + type: "Type of GA" + content: + signatures_list: "Attendance Signatures List (%count% members)" + no_signatures_yet: "No attendance signatures recorded yet." +adh_age: + title: "General Assembly" + description: "Information and signature tracking for the General Assembly." + + +adh_page: + breadcrumb: "GA Members" + +global: + at: "at" + signed_link: "Signed (View)" + +member: + civility: "Title" + name_surname: "Name Surname" + signature: "Signature Status" + +adh_page_validate: + title: "Signature Validation" + description: "Confirmation of your signature for the General Assembly." + breadcrumb: "Signature Validated" + success_message: "Your signature has been successfully recorded and validated for the General Assembly. You can now view the signed document." + thanks: "Thank you for your participation!" diff --git a/translations/messages.fr.yaml b/translations/messages.fr.yaml index 0d9ac69..02df7dd 100644 --- a/translations/messages.fr.yaml +++ b/translations/messages.fr.yaml @@ -629,3 +629,51 @@ soissons: Soissons gauchy: Gauchy quessy: Quessy guny: Guny + +doc_page: + title: "Page des Documents" + description: "Liste des documents officiels de l'association." + breadcrumb: "Documents" +breadcrumb: + home: "Accueil" +doc_list: + title: "Liste des documents" + ag_ordinaire_title: "Assemblées Générales Ordinaires" + ag_extraordinaire_title: "Assemblées Générales Extraordinaires" + +ag: + date_at_prefix: "Le" # e.g., "Le 23/11/2025" + president_label: "Président" + secretary_label: "Secrétaire" + main_members_label: "Participants principaux" + view_document_link: "Voir le document" + info: + title: "Détails de l'Assemblée Générale" + date_time: "Date & Heure" + location: "Lieu" + type: "Nature de l'AG" + content: + signatures_list: "Liste des Signatures de Présence (%count% membres)" + no_signatures_yet: "Aucune signature de présence enregistrée pour le moment." +adh_age: + title: "Assemblée Générale" + description: "Informations et suivi des signatures de l'Assemblée Générale." + +adh_page: + breadcrumb: "AG Adhérents" + +global: + at: "à" + signed_link: "Signée (Voir)" + +member: + civility: "Civilité" + name_surname: "Nom Prénom" + signature: "Statut de Signature" + +adh_page_validate: + title: "Validation de Signature" + description: "Confirmation de votre signature pour l'Assemblée Générale." + breadcrumb: "Signature validée" + success_message: "Votre signature a été enregistrée et validée avec succès pour l'Assemblée Générale. Vous pouvez consulter le document signé." + thanks: "Merci pour votre participation !"