```
✨ feat(doc): Ajoute la page de documents et les AGs au sitemap. ♻️ refactor(.env): Met à jour l'URL de développement Ngrok. ✨ feat(SitemapSubscriber): Ajoute les URLs contact et doc au sitemap. ✨ feat(AgGenerator): Ajoute l'option de largeur à la signature PDF. ✨ feat(Ag/Main): Ajoute le champ de signature de l'adhésion à l'AG. ✨ feat(AgTypeEdit): Crée le formulaire d'édition du type d'AG. 🌐 i18n: Ajoute des traductions chinoises pour la page documents. ✨ feat(Mailer): Ajoute le contenu texte aux e-mails. ✨ feat(DocumentController): Ajoute le contrôleur de documents. ✨ feat(txt-mails): Ajoute les templates de mails en texte. ✨ feat(AdController): Ajoute le contrôleur d'adhésion. ✨ feat(Service/Pdf): Crée le service PDF pour l'adhésion à l'AG. ✨ feat(AdminController): Ajoute la gestion de l'AG à l'admin. 🌐 i18n: Ajoute les traductions françaises pour la page documents. ✨ feat(Members): Ajoute la relation avec la signature de l'AG. ```
This commit is contained in:
2
.env
2
.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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
40
migrations/Version20251123201651.php
Normal file
40
migrations/Version20251123201651.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Auto-generated Migration: Please modify to your needs!
|
||||
*/
|
||||
final class Version20251123201651 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
// this up() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('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');
|
||||
}
|
||||
}
|
||||
40
migrations/Version20251123210026.php
Normal file
40
migrations/Version20251123210026.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Auto-generated Migration: Please modify to your needs!
|
||||
*/
|
||||
final class Version20251123210026 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
// this up() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('CREATE TABLE 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');
|
||||
}
|
||||
}
|
||||
54
src/Controller/AdController.php
Normal file
54
src/Controller/AdController.php
Normal file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use App\Dto\Contact\ContactType;
|
||||
use App\Dto\Contact\DtoContact;
|
||||
use App\Entity\Account;
|
||||
use App\Entity\AccountResetPasswordRequest;
|
||||
use App\Entity\Ag\Main;
|
||||
use App\Form\RequestPasswordConfirmType;
|
||||
use App\Form\RequestPasswordRequestType;
|
||||
use App\Repository\Ag\MainRepository;
|
||||
use App\Service\Mailer\Mailer;
|
||||
use App\Service\ResetPassword\Event\ResetPasswordConfirmEvent;
|
||||
use App\Service\ResetPassword\Event\ResetPasswordEvent;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Psr\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
|
||||
use Twig\Environment;
|
||||
|
||||
class AdController extends AbstractController
|
||||
{
|
||||
|
||||
#[Route(path: '/adh/{id}', name: 'app_adh', options: ['sitemap' => 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,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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();
|
||||
|
||||
37
src/Controller/DocumentController.php
Normal file
37
src/Controller/DocumentController.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use App\Dto\Contact\ContactType;
|
||||
use App\Dto\Contact\DtoContact;
|
||||
use App\Entity\Account;
|
||||
use App\Entity\AccountResetPasswordRequest;
|
||||
use App\Form\RequestPasswordConfirmType;
|
||||
use App\Form\RequestPasswordRequestType;
|
||||
use App\Repository\Ag\MainRepository;
|
||||
use App\Service\Mailer\Mailer;
|
||||
use App\Service\ResetPassword\Event\ResetPasswordConfirmEvent;
|
||||
use App\Service\ResetPassword\Event\ResetPasswordEvent;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Psr\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
|
||||
use Twig\Environment;
|
||||
|
||||
class DocumentController extends AbstractController
|
||||
{
|
||||
|
||||
#[Route(path: '/documents', name: 'app_doc', options: ['sitemap' => 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']),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -56,10 +56,7 @@ class AgType extends AbstractType
|
||||
return $member->getPseudo()." - ".$member->getRole();
|
||||
}
|
||||
])
|
||||
->add('closedAt',DateTimeType::class,[
|
||||
'label' => 'Date de cloture',
|
||||
'required' => true,
|
||||
])
|
||||
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
70
src/Dto/Ag/AgTypeEdit.php
Normal file
70
src/Dto/Ag/AgTypeEdit.php
Normal file
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace App\Dto\Ag;
|
||||
|
||||
use App\Entity\Ag\Main;
|
||||
use App\Entity\Members;
|
||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\DateTimeType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\DateType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
class AgTypeEdit extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$builder
|
||||
->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);
|
||||
}
|
||||
}
|
||||
@@ -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<int, MainSigned>
|
||||
*/
|
||||
#[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<int, MainSigned>
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
67
src/Entity/Ag/MainSigned.php
Normal file
67
src/Entity/Ag/MainSigned.php
Normal file
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
namespace App\Entity\Ag;
|
||||
|
||||
use App\Entity\Members;
|
||||
use App\Repository\Ag\MainSignedRepository;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
#[ORM\Entity(repositoryClass: MainSignedRepository::class)]
|
||||
#[ORM\Table(name: 'ag_main_signed')]
|
||||
class MainSigned
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\ManyToOne(inversedBy: 'mainSigneds')]
|
||||
private ?Main $main = null;
|
||||
|
||||
#[ORM\ManyToOne(inversedBy: 'mainSigneds')]
|
||||
private ?Members $members = null;
|
||||
|
||||
#[ORM\Column(length: 255)]
|
||||
private ?string $submiterId = null;
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->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;
|
||||
}
|
||||
}
|
||||
@@ -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<int, MainSigned>
|
||||
*/
|
||||
#[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<int, MainSigned>
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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')));
|
||||
|
||||
43
src/Repository/Ag/MainSignedRepository.php
Normal file
43
src/Repository/Ag/MainSignedRepository.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace App\Repository\Ag;
|
||||
|
||||
use App\Entity\Ag\MainSigned;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
/**
|
||||
* @extends ServiceEntityRepository<MainSigned>
|
||||
*/
|
||||
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()
|
||||
// ;
|
||||
// }
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
239
src/Service/Pdf/AgAdh.php
Normal file
239
src/Service/Pdf/AgAdh.php
Normal file
@@ -0,0 +1,239 @@
|
||||
<?php
|
||||
|
||||
namespace App\Service\Pdf;
|
||||
|
||||
use App\Entity\Ag\Main;
|
||||
use App\Entity\Members;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Fpdf\Fpdf;
|
||||
use Symfony\Component\HttpKernel\KernelInterface;
|
||||
|
||||
class AgAdh extends Fpdf
|
||||
{
|
||||
private Main $main;
|
||||
private KernelInterface $kernel;
|
||||
private EntityManagerInterface $em;
|
||||
|
||||
// Données d'association simulées (à remplacer par de vraies données si disponibles via Kernel ou autre)
|
||||
private array $associationData = [
|
||||
'name' => '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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
235
src/Service/Pdf/AgConvocation.php
Normal file
235
src/Service/Pdf/AgConvocation.php
Normal file
@@ -0,0 +1,235 @@
|
||||
<?php
|
||||
|
||||
namespace App\Service\Pdf;
|
||||
|
||||
use App\Entity\Ag\Main;
|
||||
use Fpdf\Fpdf;
|
||||
use Symfony\Component\HttpKernel\KernelInterface;
|
||||
|
||||
class AgConvocation extends Fpdf
|
||||
{
|
||||
private Main $main;
|
||||
private KernelInterface $kernel;
|
||||
private \App\Entity\Members $member;
|
||||
|
||||
public function __construct($orientation = 'P', $unit = 'mm', $size = 'A4')
|
||||
{
|
||||
parent::__construct($orientation, $unit, $size);
|
||||
$this->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);
|
||||
}
|
||||
}
|
||||
@@ -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');
|
||||
|
||||
132
templates/adh.twig
Normal file
132
templates/adh.twig
Normal file
@@ -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 %}<link rel="canonical" href="{{ url('app_adh',{id:main.id}) }}" />{% endblock %}
|
||||
{% block breadcrumb_schema %}
|
||||
<script type="application/ld+json">
|
||||
{
|
||||
"@context": "https://schema.org",
|
||||
"@type": "BreadcrumbList",
|
||||
"itemListElement": [
|
||||
{
|
||||
"@type": "ListItem",
|
||||
"position": 1,
|
||||
"name": "{{ 'breadcrumb.home'|trans }}",
|
||||
"item": "{{ app.request.schemeAndHttpHost }}"
|
||||
},
|
||||
{
|
||||
"@type": "ListItem",
|
||||
"position": 2,
|
||||
"name": "{{ 'adh_page.breadcrumb'|trans }}",
|
||||
"item": "{{ app.request.schemeAndHttpHost }}{{ app.request.pathInfo }}"
|
||||
}
|
||||
]
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
{# --- BODY with Tailwind Styling and full i18n --- #}
|
||||
{% block body %}
|
||||
<div class="max-w-4xl mx-auto p-4 sm:p-6 lg:p-8">
|
||||
|
||||
<!-- Titre principal de la page -->
|
||||
<h1 class="text-3xl font-extrabold text-gray-900 mb-8 text-center">
|
||||
{{ 'adh_age.title'|trans }}
|
||||
</h1>
|
||||
|
||||
<!-- Carte d'information de l'AG -->
|
||||
<div class="bg-white shadow-xl rounded-2xl p-6 md:p-10 border border-gray-100">
|
||||
<h2 class="text-2xl font-bold text-indigo-700 mb-6 border-b pb-2">
|
||||
{{ 'ag.info.title'|trans }}
|
||||
</h2>
|
||||
|
||||
<div class="space-y-4">
|
||||
|
||||
<!-- Ligne 1: Date & Heure -->
|
||||
<div class="flex flex-col sm:flex-row sm:items-center">
|
||||
<span class="font-semibold text-gray-600 w-full sm:w-1/4">
|
||||
{{ 'ag.info.date_time'|trans }} :
|
||||
</span>
|
||||
<span class="text-lg font-medium text-gray-900 mt-1 sm:mt-0 sm:w-3/4">
|
||||
{{ main.agDateAt | date('d/m/Y') }} {{ 'global.at'|trans }} {{ main.agDateAt | date('H:i') }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- Ligne 2: Lieu -->
|
||||
<div class="flex flex-col sm:flex-row sm:items-center">
|
||||
<span class="font-semibold text-gray-600 w-full sm:w-1/4">
|
||||
{{ 'ag.info.location'|trans }} :
|
||||
</span>
|
||||
<span class="text-gray-800 mt-1 sm:mt-0 sm:w-3/4">
|
||||
{{ main.locate }} {{ main.locateZipcode }} {{ main.locateCity }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- Ligne 3: Nature de l'AG -->
|
||||
<div class="flex flex-col sm:flex-row sm:items-center">
|
||||
<span class="font-semibold text-gray-600 w-full sm:w-1/4">
|
||||
{{ 'ag.info.type'|trans }} :
|
||||
</span>
|
||||
<span class="text-gray-800 mt-1 sm:mt-0 sm:w-3/4">
|
||||
{{ main.type }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Espace pour le contenu spécifique aux adhérents : Liste des Signatures -->
|
||||
<div class="mt-12">
|
||||
<h2 class="text-xl font-semibold text-gray-900 border-b pb-2 mb-4">
|
||||
{{ 'ag.content.signatures_list'|trans({'%count%': signed|length}) }}
|
||||
</h2>
|
||||
|
||||
<div class="overflow-x-auto shadow-md rounded-lg">
|
||||
<table class="min-w-full divide-y divide-gray-200">
|
||||
<!-- En-tête du tableau -->
|
||||
<thead class="bg-indigo-50">
|
||||
<tr>
|
||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider w-1/4">
|
||||
{{ 'member.civility'|trans }}
|
||||
</th>
|
||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider w-1/2">
|
||||
{{ 'member.name_surname'|trans }}
|
||||
</th>
|
||||
<th scope="col" class="px-6 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider w-1/4">
|
||||
{{ 'member.signature'|trans }}
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<!-- Corps du tableau -->
|
||||
<tbody class="bg-white divide-y divide-gray-200">
|
||||
{% for sign in signed %}
|
||||
<tr>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
||||
{{ sign.members.civ }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">
|
||||
{{ sign.members.name }} {{ sign.members.surname }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-center text-sm font-medium">
|
||||
<a href="{{ sign.sign }}" target="_blank" class="text-indigo-600 hover:text-indigo-900 font-bold hover:underline transition duration-150">
|
||||
{{ 'global.signed_link'|trans }}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr>
|
||||
<td colspan="3" class="px-6 py-4 text-center text-sm text-gray-500 italic">
|
||||
{{ 'ag.content.no_signatures_yet'|trans }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{% endblock %}
|
||||
54
templates/adh_validate.twig
Normal file
54
templates/adh_validate.twig
Normal file
@@ -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 %}<link rel="canonical" href="{{ url('app_adh',{id:main.id,validateResult:1}) }}" />{% endblock %}
|
||||
{% block breadcrumb_schema %}
|
||||
<script type="application/ld+json">
|
||||
{
|
||||
"@context": "https://schema.org",
|
||||
"@type": "BreadcrumbList",
|
||||
"itemListElement": [
|
||||
{
|
||||
"@type": "ListItem",
|
||||
"position": 1,
|
||||
"name": "{{ 'breadcrumb.home'|trans }}",
|
||||
"item": "{{ app.request.schemeAndHttpHost }}"
|
||||
},
|
||||
{
|
||||
"@type": "ListItem",
|
||||
"position": 2,
|
||||
"name": "{{ 'adh_page_validate.breadcrumb'|trans }}",
|
||||
"item": "{{ app.request.schemeAndHttpHost }}{{ app.request.pathInfo }}"
|
||||
}
|
||||
]
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
{# --- BODY with Tailwind Styling and full i18n --- #}
|
||||
{% block body %}
|
||||
<div class="max-w-xl mx-auto p-4 sm:p-6 lg:p-8">
|
||||
<div class="bg-white shadow-2xl rounded-2xl p-8 md:p-12 text-center border-t-4 border-green-500">
|
||||
<!-- Icone de succès -->
|
||||
<svg class="mx-auto h-16 w-16 text-green-500" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path>
|
||||
</svg>
|
||||
|
||||
<h1 class="mt-4 text-3xl font-extrabold text-gray-900">
|
||||
{{ 'adh_page_validate.title'|trans }}
|
||||
</h1>
|
||||
|
||||
<p class="mt-4 text-lg text-gray-600">
|
||||
{{ 'adh_page_validate.success_message'|trans }}
|
||||
</p>
|
||||
|
||||
<p class="mt-6 text-xl font-semibold text-indigo-600">
|
||||
{{ 'adh_page_validate.thanks'|trans }}
|
||||
</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -18,62 +18,85 @@
|
||||
{% for ag in ags %}
|
||||
<div class="mb-6 p-4 border rounded-lg shadow-md bg-gray-50 dark:bg-gray-700 dark:border-gray-600 hover:shadow-lg transition duration-300">
|
||||
|
||||
<div class="flex justify-between items-start mb-2">
|
||||
<p class="text-lg font-semibold text-indigo-600 dark:text-indigo-400">
|
||||
{{ ag.agDateAt|date('d/m/Y H:i') }}
|
||||
<span class="ml-2 px-3 py-1 text-xs font-medium rounded-full
|
||||
{% if ag.type == 'Extraordinaire' %}
|
||||
bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-300
|
||||
{% else %}
|
||||
bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-300
|
||||
{% endif %}">
|
||||
{{ ag.type }}
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<p class="text-sm">
|
||||
<strong>Président:</strong> {{ ag.president.pseudo }} /
|
||||
<strong>Secrétaire:</strong> {{ ag.secretaire.pseudo }}
|
||||
</p>
|
||||
|
||||
<div class="mt-2 text-sm text-gray-600 dark:text-gray-300">
|
||||
<strong>Lieu:</strong> {{ ag.locate }}
|
||||
{{ ag.locateZipcode }} {{ ag.locateCity }}
|
||||
</div>
|
||||
|
||||
<div class="mt-3 pt-3 border-t border-gray-200 dark:border-gray-600 text-sm">
|
||||
<p>
|
||||
<strong>Membres principaux:</strong> <span class="font-medium">{{ ag.mainMembers.count }}</span>
|
||||
</p>
|
||||
<p>
|
||||
<strong>Ordres du jour:</strong> <span class="font-medium">{{ ag.orders.count }}</span>
|
||||
</p>
|
||||
{# NOUVEAU CONTENEUR FLEX POUR DISPOSITION LATÉRALE #}
|
||||
<div class="flex justify-between items-start">
|
||||
|
||||
{# BLOC GAUCHE: Détails de l'AG #}
|
||||
<div class="flex-grow pr-4">
|
||||
<div class="flex justify-between items-start mb-2">
|
||||
<p class="text-lg font-semibold text-indigo-600 dark:text-indigo-400">
|
||||
{{ ag.agDateAt|date('d/m/Y H:i') }}
|
||||
<span class="ml-2 px-3 py-1 text-xs font-medium rounded-full
|
||||
{% if ag.type == 'Extraordinaire' %}
|
||||
bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-300
|
||||
{% else %}
|
||||
bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-300
|
||||
{% endif %}">
|
||||
{{ ag.type }}
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<p class="text-sm">
|
||||
<strong>Président:</strong> {{ ag.president.pseudo }} /
|
||||
<strong>Secrétaire:</strong> {{ ag.secretaire.pseudo }}
|
||||
</p>
|
||||
|
||||
<div class="mt-2 text-sm text-gray-600 dark:text-gray-300">
|
||||
<strong>Lieu:</strong> {{ ag.locate }}
|
||||
{{ ag.locateZipcode }} {{ ag.locateCity }}
|
||||
</div>
|
||||
|
||||
<div class="mt-3 pt-3 border-t border-gray-200 dark:border-gray-600 text-sm">
|
||||
<p>
|
||||
<strong>Membres principaux:</strong> <span class="font-medium">{{ ag.mainMembers.count }}</span>
|
||||
</p>
|
||||
<p>
|
||||
<strong>Ordres du jour:</strong> <span class="font-medium">{{ ag.orders.count }}</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{# FIN BLOC GAUCHE #}
|
||||
|
||||
{# BLOC DROIT: QR CODE #}
|
||||
<div class="flex-shrink-0 pt-1">
|
||||
<p class="text-xs text-gray-500 dark:text-gray-400 text-center mb-1">Détails AG</p>
|
||||
|
||||
<img src="{{ path('admin_ag',{idQrCode:ag.id}) }}" alt="QR Code pour {{ ag.type }} du {{ ag.agDateAt|date('d/m/Y') }}" class="w-54 h-54 border border-gray-300 dark:border-gray-600 rounded-md p-1"/>
|
||||
</div>
|
||||
{# FIN BLOC DROIT #}
|
||||
|
||||
</div>
|
||||
|
||||
{# BLOC ACTIONS (GARDÉ EN DESSOUS DES DÉTAILS) #}
|
||||
{% if ag.closed == false or ag.closed == null %}
|
||||
{# Bloc Actions: SUPPRIMER / MODIFIER #}
|
||||
<div class="mt-4 pt-4 border-t border-gray-200 dark:border-gray-600 flex space-x-3">
|
||||
<a href="{{ path('admin_ag', {'idGenerate': ag.id}) }}"
|
||||
class="inline-flex items-center px-3 py-1.5 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-cyan-600 hover:bg-cyan-700 dark:bg-cyan-500 dark:hover:bg-cyan-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
|
||||
Générer le pdf
|
||||
</a>
|
||||
{# Bouton Modifier #}
|
||||
<a href="{{ path('admin_ag_edit', {'id': ag.id}) }}"
|
||||
class="inline-flex items-center px-3 py-1.5 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-indigo-600 hover:bg-indigo-700 dark:bg-indigo-500 dark:hover:bg-indigo-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
|
||||
Modifier
|
||||
</a>
|
||||
<div class="mt-4 pt-4 border-t border-gray-200 dark:border-gray-600 flex space-x-3">
|
||||
<a href="{{ path('admin_ag', {'idGenerate': ag.id}) }}"
|
||||
class="inline-flex items-center px-3 py-1.5 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-cyan-600 hover:bg-cyan-700 dark:bg-cyan-500 dark:hover:bg-cyan-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
|
||||
Générer le pdf
|
||||
</a>
|
||||
{% if ag.attestation == null or ag.attestation == false %}
|
||||
<a href="{{ path('admin_ag', {'idConvocation': ag.id}) }}"
|
||||
class="inline-flex items-center px-3 py-1.5 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-purple-600 hover:bg-purple-700 dark:bg-purple-500 dark:hover:bg-purple-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
|
||||
Envoyée les Convocation
|
||||
</a>
|
||||
{% endif %}
|
||||
{# Bouton Modifier #}
|
||||
<a href="{{ path('admin_ag_edit', {'id': ag.id}) }}"
|
||||
class="inline-flex items-center px-3 py-1.5 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-indigo-600 hover:bg-indigo-700 dark:bg-indigo-500 dark:hover:bg-indigo-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
|
||||
Modifier
|
||||
</a>
|
||||
|
||||
{# Bouton Supprimer #}
|
||||
<form method="POST" action="{{ path('admin_ag_delete', {'id': ag.id}) }}" onsubmit="return confirm('Êtes-vous sûr de vouloir supprimer cette Assemblée Générale ?');">
|
||||
<input type="hidden" name="_method" value="DELETE">
|
||||
{# Bouton Supprimer #}
|
||||
<form method="POST" action="{{ path('admin_ag_delete', {'id': ag.id}) }}" onsubmit="return confirm('Êtes-vous sûr de vouloir supprimer cette Assemblée Générale ?');">
|
||||
<input type="hidden" name="_method" value="DELETE">
|
||||
|
||||
<button type="submit"
|
||||
class="inline-flex items-center px-3 py-1.5 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-red-600 hover:bg-red-700 dark:bg-red-500 dark:hover:bg-red-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500">
|
||||
Supprimer
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
<button type="submit"
|
||||
class="inline-flex items-center px-3 py-1.5 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-red-600 hover:bg-red-700 dark:bg-red-500 dark:hover:bg-red-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500">
|
||||
Supprimer
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
{% else %}
|
||||
{% if ag.isSigned == true %}
|
||||
<div class="mt-4 pt-4 border-t border-gray-200 dark:border-gray-600 flex space-x-3">
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
<h2 class="col-span-full text-lg font-semibold text-gray-700 dark:text-gray-200 mb-2">Détails de l'AG</h2>
|
||||
|
||||
{{ form_row(ag.agDateAt) }}
|
||||
{{ form_row(ag.closedAt) }}
|
||||
{{ form_row(ag.type) }}
|
||||
{# Ce champ est seul sur la deuxième colonne pour l'exemple #}
|
||||
<div class="md:col-span-1"></div>
|
||||
|
||||
@@ -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' }
|
||||
] %}
|
||||
|
||||
115
templates/doc.twig
Normal file
115
templates/doc.twig
Normal file
@@ -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 %}<link rel="canonical" href="{{ url('app_doc') }}" />{% endblock %}
|
||||
{% block breadcrumb_schema %}
|
||||
<script type="application/ld+json">
|
||||
{
|
||||
"@context": "https://schema.org",
|
||||
"@type": "BreadcrumbList",
|
||||
"itemListElement": [
|
||||
{
|
||||
"@type": "ListItem",
|
||||
"position": 1,
|
||||
"name": "{{ 'breadcrumb.home'|trans }}",
|
||||
"item": "{{ app.request.schemeAndHttpHost }}"
|
||||
},
|
||||
{
|
||||
"@type": "ListItem",
|
||||
"position": 2,
|
||||
"name": "{{ 'doc_page.breadcrumb'|trans }}",
|
||||
"item": "{{ app.request.schemeAndHttpHost }}{{ app.request.pathInfo }}"
|
||||
}
|
||||
]
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
{# --- BODY with Tailwind Styling and full i18n --- #}
|
||||
{% block body %}
|
||||
<div class="container mx-auto p-4 md:p-8">
|
||||
{# Main Title #}
|
||||
<h1 class="text-3xl font-bold text-gray-800 mb-6 border-b pb-2">
|
||||
{{ 'doc_list.title'|trans }}
|
||||
</h1>
|
||||
|
||||
{# --- Ordinary General Assembly (AG Ordinaire) --- #}
|
||||
<section class="mb-10">
|
||||
<h2 class="text-2xl font-semibold text-indigo-600 mb-4">
|
||||
{{ 'doc_list.ag_ordinaire_title'|trans }}
|
||||
</h2>
|
||||
<div class="space-y-4">
|
||||
{% for ag in ag_ordinaire %}
|
||||
<div class="bg-white shadow-lg rounded-lg p-5 border border-gray-200 hover:shadow-xl transition duration-300">
|
||||
<div class="flex flex-wrap justify-between items-center mb-2 border-b pb-2">
|
||||
<span class="text-xl font-bold text-gray-700">
|
||||
{{ 'ag.date_at_prefix'|trans }} {{ ag.agDateAt|date('d/m/Y') }}
|
||||
</span>
|
||||
<span class="text-sm font-medium text-gray-500">
|
||||
{{ 'ag.main_members_label'|trans }} : <span class="font-semibold text-gray-700">{{ ag.mainMembers.count }}</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 text-sm text-gray-600 mb-4">
|
||||
<div>
|
||||
<strong class="text-gray-800">{{ 'ag.president_label'|trans }} :</strong>
|
||||
{{ ag.president.civ }} {{ ag.president.name }} {{ ag.president.surname }}
|
||||
</div>
|
||||
<div>
|
||||
<strong class="text-gray-800">{{ 'ag.secretary_label'|trans }} :</strong>
|
||||
{{ ag.secretaire.civ }} {{ ag.secretaire.name }} {{ ag.secretaire.surname }}
|
||||
</div>
|
||||
</div>
|
||||
<a
|
||||
download="ag_normal_ecosplay_{{ ag.agDateAt|date('d-m-Y') }}.pdf"
|
||||
href="{{ vich_uploader_asset(ag,'ag') }}"
|
||||
class="inline-block mt-2 px-4 py-2 bg-indigo-500 text-white font-semibold rounded-lg shadow-md hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 transition ease-in-out duration-150"
|
||||
>
|
||||
<i class="fas fa-file-download mr-2"></i> {{ 'ag.view_document_link'|trans }}
|
||||
</a>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{# --- Extraordinary General Assembly (AG Extraordinaire) --- #}
|
||||
<section>
|
||||
<h2 class="text-2xl font-semibold text-red-600 mb-4">
|
||||
{{ 'doc_list.ag_extraordinaire_title'|trans }}
|
||||
</h2>
|
||||
<div class="space-y-4">
|
||||
{% for ag in ag_extraordinaire %}
|
||||
<div class="bg-white shadow-lg rounded-lg p-5 border border-gray-200 hover:shadow-xl transition duration-300">
|
||||
<div class="flex flex-wrap justify-between items-center mb-2 border-b pb-2">
|
||||
<span class="text-xl font-bold text-gray-700">
|
||||
{{ 'ag.date_at_prefix'|trans }} {{ ag.agDateAt|date('d/m/Y') }}
|
||||
</span>
|
||||
<span class="text-sm font-medium text-gray-500">
|
||||
{{ 'ag.main_members_label'|trans }} : <span class="font-semibold text-gray-700">{{ ag.mainMembers.count }}</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 text-sm text-gray-600 mb-4">
|
||||
<div>
|
||||
<strong class="text-gray-800">{{ 'ag.president_label'|trans }} :</strong>
|
||||
{{ ag.president.civ }} {{ ag.president.name }} {{ ag.president.surname }}
|
||||
</div>
|
||||
<div>
|
||||
<strong class="text-gray-800">{{ 'ag.secretary_label'|trans }} :</strong>
|
||||
{{ ag.secretaire.civ }} {{ ag.secretaire.name }} {{ ag.secretaire.surname }}
|
||||
</div>
|
||||
</div>
|
||||
<a
|
||||
download="ag_normal_ecosplay_{{ ag.agDateAt|date('d-m-Y') }}.pdf"
|
||||
href="{{ vich_uploader_asset(ag,'ag') }}"
|
||||
class="inline-block mt-2 px-4 py-2 bg-red-500 text-white font-semibold rounded-lg shadow-md hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2 transition ease-in-out duration-150"
|
||||
>
|
||||
<i class="fas fa-file-download mr-2"></i> {{ 'ag.view_document_link'|trans }}
|
||||
</a>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
{% endblock %}
|
||||
111
templates/mails/ag_convocation.twig
Normal file
111
templates/mails/ag_convocation.twig
Normal file
@@ -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 %}
|
||||
<mj-section background-color="#ffffff">
|
||||
<mj-column width="100%">
|
||||
{# 1. En-tête (Logo et Titre) #}
|
||||
<mj-text font-size="20px" font-weight="bold" align="center" padding-bottom="0">
|
||||
CONVOCATION À L'ASSEMBLÉE GÉNÉRALE
|
||||
</mj-text>
|
||||
<mj-text font-size="14px" color="#555555" align="center" padding-top="5px">
|
||||
E-Cosplay Association
|
||||
</mj-text>
|
||||
<mj-divider border-color="#e0e0e0" border-width="1px" padding="10px 0"></mj-divider>
|
||||
</mj-column>
|
||||
</mj-section>
|
||||
|
||||
{# 2. Bloc Destinataire (Style Courrier) #}
|
||||
<mj-section background-color="#ffffff" padding-top="10px">
|
||||
<mj-column width="60%"></mj-column>
|
||||
<mj-column width="40%" border="1px solid #e0e0e0" padding="10px">
|
||||
<mj-text font-size="12px" line-height="16px" padding="0">
|
||||
À l'attention de :<br>
|
||||
<span style="font-weight: bold;">{{ datas.member.civ|default('') }} {{ datas.member.name|default('') }} {{ datas.member.surname|default('Cher Membre') }}</span><br>
|
||||
{# Ajout de l'adresse pour une référence complète (bien que ce soit un email) #}
|
||||
{{ datas.member.locate|default('') }}<br>
|
||||
{{ datas.member.locateZipcode|default('') }} {{ datas.member.locateCity|default('') }}
|
||||
</mj-text>
|
||||
</mj-column>
|
||||
</mj-section>
|
||||
|
||||
{# 3. Introduction et Contexte #}
|
||||
<mj-section background-color="#ffffff" padding-top="20px">
|
||||
<mj-column>
|
||||
<mj-text font-size="14px" line-height="22px">
|
||||
Cher(e) membre de l'Association E-Cosplay,
|
||||
</mj-text>
|
||||
<mj-text font-size="14px" line-height="22px" padding-top="10px">
|
||||
Vous êtes cordialement convoqué(e) à l'Assemblée Générale ({{ datas.main.type|default('Type non défini') }}) de notre association.
|
||||
</mj-text>
|
||||
</mj-column>
|
||||
</mj-section>
|
||||
|
||||
{# 4. Bloc des Détails de l'AG (Mise en évidence) #}
|
||||
<mj-section background-color="#f7f7f7" border-radius="5px" padding="20px">
|
||||
<mj-column width="100%">
|
||||
<mj-text font-size="16px" font-weight="bold" color="#333333" align="center" padding-bottom="10px">
|
||||
DÉTAILS DE L'ASSEMBLÉE
|
||||
</mj-text>
|
||||
<mj-table>
|
||||
<tr>
|
||||
<td style="width: 40%; font-weight: bold; padding: 5px 0; color: #555555;">Date :</td>
|
||||
<td style="width: 60%; padding: 5px 0;">{{ datas.main.agDateAt|date('d/m/Y') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="width: 40%; font-weight: bold; padding: 5px 0; color: #555555;">Heure de début :</td>
|
||||
<td style="width: 60%; padding: 5px 0;">{{ datas.main.agDateAt|date('H:i') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="width: 40%; font-weight: bold; padding: 5px 0; color: #555555;">Lieu :</td>
|
||||
<td style="width: 60%; padding: 5px 0;">{{ datas.main.locate }}, {{ datas.main.locateZipCode }} {{ datas.main.locateCity }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="width: 40%; font-weight: bold; padding: 5px 0; color: #555555;">Nature de l'AG :</td>
|
||||
<td style="width: 60%; padding: 5px 0;">{{ datas.main.type|default('Non spécifiée') }}</td>
|
||||
</tr>
|
||||
</mj-table>
|
||||
</mj-column>
|
||||
</mj-section>
|
||||
|
||||
{# 5. Avertissement sur les Droits de Vote (Nouveau bloc en rouge) #}
|
||||
<mj-section background-color="#ffffff" padding-top="15px" padding-bottom="15px">
|
||||
<mj-column>
|
||||
<mj-text font-size="14px" font-weight="bold" color="#FF0000" align="center" padding="0">
|
||||
ATTENTION : Seuls les membres à jour de leurs adhésions pourront voter.
|
||||
</mj-text>
|
||||
</mj-column>
|
||||
</mj-section>
|
||||
|
||||
{# 6. Clôture et Signature #}
|
||||
<mj-section background-color="#ffffff" padding-top="0">
|
||||
<mj-column>
|
||||
<mj-text font-size="14px" line-height="22px">
|
||||
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.
|
||||
</mj-text>
|
||||
<mj-text font-size="14px" line-height="22px" padding-top="10px">
|
||||
Dans l'attente de vous accueillir, veuillez agréer, cher(e) membre, l'expression de nos salutations distinguées.
|
||||
</mj-text>
|
||||
</mj-column>
|
||||
</mj-section>
|
||||
|
||||
<mj-section background-color="#ffffff" padding-top="20px">
|
||||
<mj-column width="40%"></mj-column>
|
||||
<mj-column width="60%">
|
||||
<mj-text font-size="14px" align="right" padding="0">
|
||||
Fait à {{ datas.main.locate_city|default('Lieu non défini') }}, le {{ datas.main.ag_date_at_dmy|default('Date non définie') }}
|
||||
</mj-text>
|
||||
<mj-spacer height="30px" />
|
||||
<mj-text font-size="14px" align="right" padding="0">
|
||||
Le {{ (datas.main.president.civ|default('M.') == 'Mme') ? 'Présidente' : 'Président' }} de l'Association,
|
||||
</mj-text>
|
||||
<mj-text font-size="14px" font-weight="bold" align="right" padding="0">
|
||||
{{ datas.main.president.civ|default('M.') }} {{ datas.main.president.name|default('Nom') }} {{ datas.main.president.surname|default('Prénom') }}
|
||||
</mj-text>
|
||||
</mj-column>
|
||||
</mj-section>
|
||||
|
||||
{% endblock %}
|
||||
43
templates/txt-mails/ag_convocation.twig
Normal file
43
templates/txt-mails/ag_convocation.twig
Normal file
@@ -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 %}
|
||||
20
templates/txt-mails/base.twig
Normal file
20
templates/txt-mails/base.twig
Normal file
@@ -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/
|
||||
26
templates/txt-mails/contact.twig
Normal file
26
templates/txt-mails/contact.twig
Normal file
@@ -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 %}
|
||||
24
templates/txt-mails/cota_validation.twig
Normal file
24
templates/txt-mails/cota_validation.twig
Normal file
@@ -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 %}
|
||||
30
templates/txt-mails/coti_payment.twig
Normal file
30
templates/txt-mails/coti_payment.twig
Normal file
@@ -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 %}
|
||||
39
templates/txt-mails/dons.twig
Normal file
39
templates/txt-mails/dons.twig
Normal file
@@ -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 %}
|
||||
23
templates/txt-mails/dons_new.twig
Normal file
23
templates/txt-mails/dons_new.twig
Normal file
@@ -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 %}
|
||||
25
templates/txt-mails/new_admin.twig
Normal file
25
templates/txt-mails/new_admin.twig
Normal file
@@ -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 %}
|
||||
27
templates/txt-mails/reset.twig
Normal file
27
templates/txt-mails/reset.twig
Normal file
@@ -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 %}
|
||||
@@ -616,3 +616,54 @@ shop.sales_note_main: >
|
||||
shop.sales_note_details: >
|
||||
请注意,针对特定的 Cosplay 写真印品销售,收益分配如下:5% 用于支付协会的银行手续费,95% 直接返还给制作该印品的会员。
|
||||
shop_more: '了解更多'
|
||||
# Used in the <head> section
|
||||
doc_page:
|
||||
title: "官方文件页面"
|
||||
description: "协会的官方文件列表。"
|
||||
breadcrumb: "文件"
|
||||
breadcrumb:
|
||||
home: "首页"
|
||||
|
||||
# Used in the <body> 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: "感谢您的参与!"
|
||||
|
||||
@@ -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 <head> section
|
||||
doc_page:
|
||||
title: "Official Documents Page"
|
||||
description: "List of the association's official documents."
|
||||
breadcrumb: "Documents"
|
||||
breadcrumb:
|
||||
home: "Home"
|
||||
|
||||
# Used in the <body> 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!"
|
||||
|
||||
@@ -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 !"
|
||||
|
||||
Reference in New Issue
Block a user