diff --git a/.env b/.env index 7479cb1..87f4f4c 100644 --- a/.env +++ b/.env @@ -69,3 +69,12 @@ GOOGLE_APPLICATION_CREDENTIALS=%kernel.project_dir%/google.json ###> sentry/sentry-symfony ### SENTRY_DSN="https://4f43769e7c483f14da26e05824a482d0@o4509563601092608.ingest.de.sentry.io/4510392636473424" ###< sentry/sentry-symfony ### + +###> pixelopen/cloudflare-turnstile-bundle ### +TURNSTILE_KEY=3x00000000000000000000FF +TURNSTILE_SECRET=1x0000000000000000000000000000000AA +###< pixelopen/cloudflare-turnstile-bundle ### + +###> stripe/stripe-php ### +STRIPE_SECRET_KEY=sk_test_*** +###< stripe/stripe-php ### diff --git a/ansible/playbook.yml b/ansible/playbook.yml index 284ee82..9e6730a 100644 --- a/ansible/playbook.yml +++ b/ansible/playbook.yml @@ -74,7 +74,9 @@ STRIPE_SK=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 STRIPE_WEBHOOKS_SIGN=whsec_wNHtgjypqbfP7erAqifCOzZvW8kW9oB7 MAILER_DSN=ses+smtp://AKIAWTT2T22CWBRBBDYN:BBdgb6KxRQ8mNcpWFJsZCJxbSGNdgLhKFiITMErfBlQP@default?region=eu-west-3 - SENTRY_DSN="https://4f43769e7c483f14da26e05824a482d0@o4509563601092608.ingest.de.sentry.io/4510392636473424" + SENTRY_DSN="https://375cf73e411fb1aa515202b7922cbaeb@sentry.esy-web.dev/6" + TURNSTILE_KEY=0x4AAAAAACI84gZ0CLCEZY5i + TURNSTILE_SECRET=0x4AAAAAACI84k8G11ODrOwCNAaWyWQ_Vzk dest: "{{ path }}/.env.local" when: ansible_os_family == "Debian" diff --git a/ansible/templates/caddy.j2 b/ansible/templates/caddy.j2 index 3f82587..bee8ffe 100644 --- a/ansible/templates/caddy.j2 +++ b/ansible/templates/caddy.j2 @@ -8,6 +8,9 @@ www.e-cosplay.fr { request_body { max_size 100MB } + handle_path /sc.js { + redir https://sentry.esy-web.dev/js-sdk-loader/49feadbadd9b443832205c4eeac1c4f5.min.js + } handle_path /ts.js { redir https://widget.trustpilot.com/bootstrap/v5/tp.widget.bootstrap.min.js } diff --git a/assets/app.js b/assets/app.js index ca1782a..0650a34 100644 --- a/assets/app.js +++ b/assets/app.js @@ -1,7 +1,8 @@ import './app.scss' import * as Turbo from "@hotwired/turbo" - +import '@grafikart/drop-files-element' import {PaymentForm} from './PaymentForm' +import * as Sentry from "@sentry/browser"; // --- CLÉS DE STOCKAGE ET VAPID --- const VAPID_PUBLIC_KEY = "BKz0kdcsG6kk9KxciPpkfP8kEDAd408inZecij5kBDbQ1ZGZSNwS4KZ8FerC28LFXvgSqpDXtor3ePo0zBCdNqo"; @@ -482,6 +483,7 @@ document.addEventListener('turbo:load', () => { initializeUI(); handleNotificationBanner(); handleCookieBanner(); // Doit être appelé à chaque chargement Turbo + }); diff --git a/assets/app.scss b/assets/app.scss index 5e06ad0..b061f7d 100644 --- a/assets/app.scss +++ b/assets/app.scss @@ -1,5 +1,6 @@ @import "tailwindcss"; @import url('https://fonts.googleapis.com/css2?family=Intel+One+Mono:ital,wght@0,300..700;1,300..700&display=swap'); + .bg-op { background: rgba(0,0,0,0.5); backdrop-filter: blur(5px); diff --git a/composer.json b/composer.json index 456ff32..6f47784 100644 --- a/composer.json +++ b/composer.json @@ -40,6 +40,7 @@ "phpdocumentor/reflection-docblock": "^5.6.4", "phpoffice/phpspreadsheet": ">=5.3", "phpstan/phpdoc-parser": "^2.3", + "pixelopen/cloudflare-turnstile-bundle": "^0.4.1", "presta/sitemap-bundle": "^4.2", "sentry/sentry-symfony": "^5.6", "setasign/fpdi": "^2.6.4", diff --git a/composer.lock b/composer.lock index ce303f1..f61df05 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "f27062872645f8d1a8553efdd75c2aef", + "content-hash": "ed26fccb39be0438a7def45a9b987dc9", "packages": [ { "name": "async-aws/core", @@ -6899,6 +6899,65 @@ }, "time": "2025-08-30T15:50:23+00:00" }, + { + "name": "pixelopen/cloudflare-turnstile-bundle", + "version": "0.4.1", + "source": { + "type": "git", + "url": "https://github.com/Pixel-Open/cloudflare-turnstile-bundle.git", + "reference": "d000eb4b37c48f9421fb603742e7e23611204182" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Pixel-Open/cloudflare-turnstile-bundle/zipball/d000eb4b37c48f9421fb603742e7e23611204182", + "reference": "d000eb4b37c48f9421fb603742e7e23611204182", + "shasum": "" + }, + "require": { + "php": ">=7.4", + "symfony/form": "^5.0|^6.0|^7.0", + "symfony/framework-bundle": "^5.0|^6.0|^7.0", + "symfony/http-client": "^5.0|^6.0|^7.0", + "symfony/twig-bundle": "^5.0|^6.0|^7.0", + "symfony/validator": "^5.0|^6.0|^7.0", + "symfony/yaml": "^5.0|^6.0|^7.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-symfony": "^1.2", + "phpunit/phpunit": "^9.6", + "rector/rector": "^0.14.5", + "symplify/easy-coding-standard": "^11.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "PixelOpen\\CloudflareTurnstileBundle\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Pixel Open Team", + "homepage": "https://pixel-open.org/projects/symfony-bundle-cloudflare-turnstile/" + } + ], + "description": "A simple package to help integrate Cloudflare Turnstile on Symfony.", + "keywords": [ + "Symfony Bundle", + "captcha", + "cloudflare-turnstile", + "form security" + ], + "support": { + "issues": "https://github.com/Pixel-Open/cloudflare-turnstile-bundle/issues", + "source": "https://github.com/Pixel-Open/cloudflare-turnstile-bundle/tree/0.4.1" + }, + "time": "2024-09-30T15:42:13+00:00" + }, { "name": "presta/sitemap-bundle", "version": "v4.2.0", diff --git a/config/bundles.php b/config/bundles.php index 5e1b6aa..d7bd546 100644 --- a/config/bundles.php +++ b/config/bundles.php @@ -18,4 +18,5 @@ return [ Presta\SitemapBundle\PrestaSitemapBundle::class => ['all' => true], Vich\UploaderBundle\VichUploaderBundle::class => ['all' => true], Sentry\SentryBundle\SentryBundle::class => ['prod' => true], + PixelOpen\CloudflareTurnstileBundle\PixelOpenCloudflareTurnstileBundle::class => ['all' => true], ]; diff --git a/config/packages/vich_uploader.yaml b/config/packages/vich_uploader.yaml index 4d763c2..31d5460 100644 --- a/config/packages/vich_uploader.yaml +++ b/config/packages/vich_uploader.yaml @@ -74,6 +74,14 @@ vich_uploader: inject_on_load: true delete_on_update: true delete_on_remove: true + epage_avatar: + uri_prefix: /epage_avatar/events + upload_destination: '%kernel.project_dir%/public/storage/epage_avatar' + namer: App\VichUploader\Namer\Epage\AvatarNamer # Replaced namer + directory_namer: App\VichUploader\DirectoryNamer\Epage\DirectoryNamer + inject_on_load: true + delete_on_update: true + delete_on_remove: true #mappings: # products: # uri_prefix: /images/products diff --git a/docker/caddy/Caddyfile b/docker/caddy/Caddyfile index 12741ca..32a39f4 100644 --- a/docker/caddy/Caddyfile +++ b/docker/caddy/Caddyfile @@ -1,4 +1,4 @@ -# ./docker/caddy/Caddyfile + # ./docker/caddy/Caddyfile # Nous écoutons sur le port 80 (qui est mappé depuis le port 8000 de l'hôte) :80 { @@ -30,6 +30,9 @@ output stdout format json } + handle_path /sc.js { + redir https://sentry.esy-web.dev/js-sdk-loader/49feadbadd9b443832205c4eeac1c4f5.min.js + } handle_path /ts.js { redir https://widget.trustpilot.com/bootstrap/v5/tp.widget.bootstrap.min.js } diff --git a/package.json b/package.json index 0253a89..d9ddda3 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "@grafikart/drop-files-element": "^1.0.9", "@hotwired/turbo": "^8.0.20", "@preact/preset-vite": "^2.10.2", + "@sentry/browser": "^10.32.1", "@tailwindcss/vite": "^4.1.17", "autoprefixer": "^10.4.22", "body-scroll-lock": "^4.0.0-beta.0", diff --git a/src/Controller/Admin/AdminController.php b/src/Controller/Admin/AdminController.php index 9e03cd1..d201b40 100644 --- a/src/Controller/Admin/AdminController.php +++ b/src/Controller/Admin/AdminController.php @@ -15,6 +15,7 @@ use App\Entity\Ag\MainOrder; use App\Entity\Ag\MainSigned; use App\Entity\Ag\MainVote; use App\Entity\Event; +use App\Entity\InviteEPage; use App\Entity\Members; use App\Entity\MembersCotisations; use App\Entity\Products; @@ -25,6 +26,7 @@ use App\Form\RequestPasswordConfirmType; use App\Form\RequestPasswordRequestType; use App\Repository\Ag\MainRepository; use App\Repository\EventRepository; +use App\Repository\InviteEPageRepository; use App\Repository\MembersCotisationsRepository; use App\Repository\MembersRepository; use App\Repository\ProductsRepository; @@ -286,7 +288,13 @@ class AdminController extends AbstractController ]); } - + #[Route(path: '/admin/epage/invitation', name: 'admin_epage_invite', options: ['sitemap' => false], methods: ['GET'])] + public function adminEpageInvitation(InviteEPageRepository $inviteEPageRepository): Response + { + return $this->render('admin/epage/invitation.twig', [ + 'invitations' => $inviteEPageRepository->findBy([],['id' => 'DESC']), + ]); + } #[Route(path: '/admin/ag', name: 'admin_ag', options: ['sitemap' => false], methods: ['GET'])] public function adminAg(Mailer $mailer,UploaderHelper $uploaderHelper,KernelInterface $kernel,EntityManagerInterface $entityManager,Request $request,MainRepository $mainRepository): Response { diff --git a/src/Controller/HomeController.php b/src/Controller/HomeController.php index 723d5c0..17234a8 100644 --- a/src/Controller/HomeController.php +++ b/src/Controller/HomeController.php @@ -53,8 +53,8 @@ class HomeController extends AbstractController 'city' => $city, ]); } - const SENTRY_HOST = 'o4509563601092608.ingest.de.sentry.io'; - const SENTRY_PROJECT_IDS = ['4510392640340048']; + const SENTRY_HOST = 'sentry.esy-web.dev'; + const SENTRY_PROJECT_IDS = ['7']; #[Route('/uptime',name: 'app_uptime',options: ['sitemap' => false], methods: ['GET'])] public function app_uptime(Request $request,HttpClientInterface $httpClient): Response diff --git a/src/Controller/JoinController.php b/src/Controller/JoinController.php new file mode 100644 index 0000000..58164ff --- /dev/null +++ b/src/Controller/JoinController.php @@ -0,0 +1,43 @@ + false], methods: ['GET','POST'])] + public function join(Request $request,Mailer $mailer): Response + { + $j= new Join(); + $form = $this->createForm(JoinType::class,$j); + $form->handleRequest($request); + if($form->isSubmitted() && $form->isValid()){ + + } + return $this->render('join.twig',[ + 'form' => $form->createView(), + ]); + } +} diff --git a/src/Controller/PagesController.php b/src/Controller/PagesController.php index 86c7e17..c052c77 100644 --- a/src/Controller/PagesController.php +++ b/src/Controller/PagesController.php @@ -2,9 +2,16 @@ namespace App\Controller; +use App\Entity\OnBoaringEpage; +use App\Form\EPageOnboard; +use App\Repository\AbonementsRepository; +use Cocur\Slugify\Slugify; +use Doctrine\ORM\EntityManagerInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; +use Symfony\Component\Uid\Uuid; class PagesController extends AbstractController { @@ -21,5 +28,88 @@ class PagesController extends AbstractController return $this->render('pages/prestation.twig',[ ]); } + #[Route(path: '/pages/onboaring', name: 'app_pages_onboaring', options: ['sitemap' => false], methods: ['GET','POST'])] + public function onboaring(Request $request,AbonementsRepository $abonementsRepository,EntityManagerInterface $entityManager): Response + { + // Définir la liste des abonnements valides. + $validAboDurations = ['1M', '2M', '3M', '6M', '12M']; + // 1. Vérifier si le paramètre 'abo' est présent dans la requête. + if (!$request->query->has('abo')) { + // Si 'abo' n'est pas présent, rediriger vers la page de découverte ou de tarification. + // J'utilise 'app_pages_discover' comme dans votre exemple initial. + return $this->redirectToRoute('app_pages_discover'); + } + + $selectedAbo = $request->query->get('abo'); + + // 2. Vérifier si la valeur de 'abo' est valide. + if (!in_array($selectedAbo, $validAboDurations, true)) { + // Si la valeur n'est pas valide, rediriger vers la page de découverte. + return $this->redirectToRoute('app_pages_discover'); + } + + // --- DÉBUT DE LA LOGIQUE D'AJOUT EN SESSION --- + $session = $request->getSession(); + + // Vérifier si un UUID de panier (cartId) existe déjà en session + if (!$session->has('cart_uuid')) { + // Si non, générer un nouvel UUID V4 et le stocker + $cartUuid = Uuid::v4()->toString(); + $session->set('cart_uuid', $cartUuid); + + // Initialiser le panier en session avec l'abonnement sélectionné + $session->set('cart_items', [ + 'subscription' => $selectedAbo, + 'uuid' => $cartUuid, + // Vous pouvez ajouter d'autres informations ici (timestamp, etc.) + ]); + + } else { + // Si l'UUID existe déjà, on met juste à jour l'abonnement sélectionné + // Si l'on suppose qu'il s'agit d'une mise à jour du choix dans le panier existant. + $cartItems = $session->get('cart_items', []); + $cartItems['subscription'] = $selectedAbo; + $session->set('cart_items', $cartItems); + } + // --- FIN DE LA LOGIQUE D'AJOUT EN SESSION --- + + $onBoard = new OnBoaringEpage(); + $onBoard->setState("created"); + $onBoard->setUuid($session->get('cart_items')['uuid']); + if($request->query->get('abo')== "1M") + $onBoard->setAbos($abonementsRepository->findOneBy(['name'=>'Epage 1 Mois'])); + if($request->query->get('abo')== "2M") + $onBoard->setAbos($abonementsRepository->findOneBy(['name'=>'Epage 2 Mois'])); + if($request->query->get('abo')== "3M") + $onBoard->setAbos($abonementsRepository->findOneBy(['name'=>'Epage 3 Mois'])); + if($request->query->get('abo')== "6M") + $onBoard->setAbos($abonementsRepository->findOneBy(['name'=>'Epage 6 Mois'])); + if($request->query->get('abo')== "12M") + $onBoard->setAbos($abonementsRepository->findOneBy(['name'=>'Epage 12 Mois'])); + $onBoard->setUseDomain(false); + // Exemple simple : afficher une page de confirmation avec l'abonnement sélectionné. + $form = $this->createForm(EPageOnboard::class,$onBoard); + $form->handleRequest($request); + if($form->isSubmitted() && $form->isValid()){ + $onBoard->setBirdth($_POST['e_page_onboard']['birdth']); + $s = new Slugify(); + $onBoard->setPageUrl($s->slugify($onBoard->getNameCosplayer()).".e-cosplay.fr"); + $avatar = $onBoard->getEpage()->getMimeType(); + if(str_contains($avatar, 'image')){ + $entityManager->persist($onBoard); + $entityManager->flush(); + + dd($onBoard); + } else { + return $this->redirectToRoute('app_pages_onboaring',['type'=>$request->get('type'),'errorFile'=>1]); + } + } + return $this->render('pages/onboarding.twig', [ + 'no_index' => true, + 'abo' => $selectedAbo, + 'onBoard' => $onBoard, + 'form' => $form->createView() + ]); + } } diff --git a/src/Dto/Join/JoinType.php b/src/Dto/Join/JoinType.php new file mode 100644 index 0000000..a232596 --- /dev/null +++ b/src/Dto/Join/JoinType.php @@ -0,0 +1,133 @@ +add('name', TextType::class, [ + 'label' => 'form.label.name', + 'required' => true, + ]) + ->add('surname', TextType::class, [ + 'label' => 'form.label.surname', + 'required' => true, + ]) + ->add('email', EmailType::class, [ + 'label' => 'form.label.email', + 'required' => true, + ]) + ->add('phone', TelType::class, [ + 'label' => 'form.label.phone', + 'required' => true, + ]) + ->add('dateBirth', DateTimeType::class, [ + 'label' => 'form.label.birthdate', + 'widget' => 'single_text', + 'required' => true, + ]) + ->add('address', TextType::class, [ + 'label' => 'form.label.address', + 'required' => true, + ]) + ->add('zipCode', TextType::class, [ + 'label' => 'form.label.zipcode', + 'required' => true, + ]) + ->add('city', TextType::class, [ + 'label' => 'form.label.city', + 'required' => true, + ]) + ->add('sexe', ChoiceType::class, [ + 'label' => 'form.label.gender', + 'choices' => [ + 'not_specified' => 'not_specified', + 'asexual' => 'asexual', + 'bisexual' => 'bisexual', + 'demisexual' => 'demisexual', + 'gay' => 'gay', + 'heterosexual' => 'heterosexual', + 'lesbian' => 'lesbian', + 'pansexual' => 'pansexual', + 'queer' => 'queer', + 'questioning' => 'questioning', + 'other' => 'other', + ], + 'choice_label' => function ($choice) { + return 'form.choices.gender.' . $choice; + }, + ]) + ->add('pronom', ChoiceType::class, [ + 'label' => 'form.label.pronouns', + 'choices' => [ + 'il' => 'il', + 'elle' => 'elle', + 'iel' => 'iel', + 'autre' => 'autre', + ], + 'choice_label' => function ($choice) { + return 'form.choices.pronouns.' . $choice; + }, + ]) + ->add('discordAccount', TextType::class, [ + 'label' => 'form.label.discord', + 'required' => false, + ]) + ->add('facebookLink', UrlType::class, [ + 'label' => 'form.label.facebook', + 'required' => false, + ]) + ->add('instaLink', UrlType::class, [ + 'label' => 'form.label.insta', + 'required' => false, + ]) + ->add('tiktokLink', UrlType::class, [ + 'label' => 'form.label.tiktok', + 'required' => false, + ]) + ->add('role', ChoiceType::class, [ + 'label' => 'form.label.role', + 'choices' => [ + 'cosplay' => 'cosplay', + 'helper' => 'helper', + 'photographe' => 'photographer', + 'autre' => 'other', + ], + 'choice_label' => function ($choice) { + return 'form.choices.role.' . $choice; + }, + 'expanded' => true, + 'multiple' => true, + ]) + ->add('who', TextareaType::class, [ + 'label' => 'form.label.who', + 'required' => true, + ]) + ->add('security', TurnstileType::class, ['attr' => ['data-action' => 'contact', 'data-theme' => 'dark'], 'label' => false]) + ; + } + + public function configureOptions(OptionsResolver $resolver) + { + $resolver->setDefaults([ + 'data_class' => Join::class, + // Permet de s'assurer que les labels sont traduits + 'translation_domain' => 'messages', + ]); + } +} diff --git a/src/Entity/Abonements.php b/src/Entity/Abonements.php index a895f9c..14dcf59 100644 --- a/src/Entity/Abonements.php +++ b/src/Entity/Abonements.php @@ -3,6 +3,8 @@ namespace App\Entity; use App\Repository\AbonementsRepository; +use Doctrine\Common\Collections\ArrayCollection; +use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; #[ORM\Entity(repositoryClass: AbonementsRepository::class)] @@ -40,6 +42,17 @@ class Abonements #[ORM\Column] private ?int $duration = null; + /** + * @var Collection + */ + #[ORM\OneToMany(targetEntity: OnBoaringEpage::class, mappedBy: 'abos')] + private Collection $onBoaringEpages; + + public function __construct() + { + $this->onBoaringEpages = new ArrayCollection(); + } + public function getId(): ?int { return $this->id; @@ -152,4 +165,34 @@ class Abonements return $this; } + + /** + * @return Collection + */ + public function getOnBoaringEpages(): Collection + { + return $this->onBoaringEpages; + } + + public function addOnBoaringEpage(OnBoaringEpage $onBoaringEpage): static + { + if (!$this->onBoaringEpages->contains($onBoaringEpage)) { + $this->onBoaringEpages->add($onBoaringEpage); + $onBoaringEpage->setAbos($this); + } + + return $this; + } + + public function removeOnBoaringEpage(OnBoaringEpage $onBoaringEpage): static + { + if ($this->onBoaringEpages->removeElement($onBoaringEpage)) { + // set the owning side to null (unless already changed) + if ($onBoaringEpage->getAbos() === $this) { + $onBoaringEpage->setAbos(null); + } + } + + return $this; + } } diff --git a/src/Entity/Account.php b/src/Entity/Account.php index f8a9fe3..51875c2 100644 --- a/src/Entity/Account.php +++ b/src/Entity/Account.php @@ -73,10 +73,17 @@ class Account implements UserInterface, PasswordAuthenticatedUserInterface, \Ser #[ORM\Column(nullable: true)] private ?bool $isActif = null; + /** + * @var Collection + */ + #[ORM\OneToMany(targetEntity: OnBoaringEpage::class, mappedBy: 'account')] + private Collection $onBoaringEpages; + public function __construct() { $this->accountLoginRegisters = new ArrayCollection(); + $this->onBoaringEpages = new ArrayCollection(); } public function getId(): ?int @@ -332,4 +339,34 @@ class Account implements UserInterface, PasswordAuthenticatedUserInterface, \Ser return $this; } + + /** + * @return Collection + */ + public function getOnBoaringEpages(): Collection + { + return $this->onBoaringEpages; + } + + public function addOnBoaringEpage(OnBoaringEpage $onBoaringEpage): static + { + if (!$this->onBoaringEpages->contains($onBoaringEpage)) { + $this->onBoaringEpages->add($onBoaringEpage); + $onBoaringEpage->setAccount($this); + } + + return $this; + } + + public function removeOnBoaringEpage(OnBoaringEpage $onBoaringEpage): static + { + if ($this->onBoaringEpages->removeElement($onBoaringEpage)) { + // set the owning side to null (unless already changed) + if ($onBoaringEpage->getAccount() === $this) { + $onBoaringEpage->setAccount(null); + } + } + + return $this; + } } diff --git a/src/EventSubscriber/SitemapSubscriber.php b/src/EventSubscriber/SitemapSubscriber.php index 7a33fca..e0f0ce8 100644 --- a/src/EventSubscriber/SitemapSubscriber.php +++ b/src/EventSubscriber/SitemapSubscriber.php @@ -51,6 +51,15 @@ class SitemapSubscriber } $urlContainer->addUrl($decoratedUrlMembers, 'default'); + $urlMembers = new UrlConcrete($urlGenerator->generate('app_recruit', [], UrlGeneratorInterface::ABSOLUTE_URL)); + $decoratedUrlMembers = new GoogleImageUrlDecorator($urlMembers); + $decoratedUrlMembers->addImage(new GoogleImage($this->cacheManager->resolve('assets/images/logo.jpg','webp'))); + $decoratedUrlMembers = new GoogleMultilangUrlDecorator($decoratedUrlMembers); + foreach ($langs as $lang) { + $decoratedUrlMembers->addLink($urlGenerator->generate('app_recruit',['lang'=>$lang], UrlGeneratorInterface::ABSOLUTE_URL), $lang); + } + $urlContainer->addUrl($decoratedUrlMembers, 'default'); + $urlMembers = new UrlConcrete($urlGenerator->generate('app_pages', [], UrlGeneratorInterface::ABSOLUTE_URL)); $decoratedUrlMembers = new GoogleImageUrlDecorator($urlMembers); $decoratedUrlMembers->addImage(new GoogleImage($this->cacheManager->resolve('assets/images/logo.jpg','webp'))); diff --git a/src/Form/EPageOnboard.php b/src/Form/EPageOnboard.php new file mode 100644 index 0000000..eab0dbc --- /dev/null +++ b/src/Form/EPageOnboard.php @@ -0,0 +1,99 @@ +add('name',TextType::class,[ + 'label' => 'epage_onboard.name', + 'required' => true, + ]) + ->add('surname',TextType::class,[ + 'label' => 'epage_onboard.surname', + 'required' => true, + ]) + ->add('email',EmailType::class,[ + 'label' => 'epage_onboard.email', + 'required' => true, + ]) + ->add('birdth',DateType::class,[ + 'label' =>'epage_onboard.birdth', + 'required' => true, + 'widget' =>'single_text', + 'mapped' => false, + ]) + ->add('description',TextareaType::class,[ + 'label' => 'epage_onboard.description', + 'required' => true, + 'attr' => [ + 'rows' => 5, + ] + ]) + + ->add('nameCosplayer',TextType::class,[ + 'label' => 'epage_onboard.nameCosplayer', + 'required' => true, + ]) + ->add('linkInstagram',UrlType::class,[ + 'label' => 'epage_onboard.linkInstagram', + 'required' => false, + ]) + ->add('linkFacebook',UrlType::class,[ + 'label' => 'epage_onboard.linkFacebook', + 'required' => false, + ]) + ->add('linkTiktok',UrlType::class,[ + 'label' => 'epage_onboard.linkTiktok', + 'required' => false, + ]) + ->add('linkX',UrlType::class,[ + 'label' => 'epage_onboard.linkX', + 'required' => false, + ]) + ->add('useDomain',CheckboxType::class,[ + 'label' => 'epage_onboard.useDomain', + 'required' => false, + ]) + ->add('epage',FileType::class,[ + 'required' => true, + 'label' => 'epage_onboard.avatar', + 'attr' =>[ + 'label' => $this->translator->trans('epage_onboard.avatar_label'), + 'multiple' => false, + 'accept' => 'image/*', + 'is' => 'drop-files' + ] + ]) + ->add('domain',TextType::class,[ + 'label' => 'epage_onboard.domain', + 'required' => true, + ]) + ; + } + + public function configureOptions(OptionsResolver $resolver) + { + $resolver->setDefault('data_class',OnBoaringEpage::class); + } +} diff --git a/src/VichUploader/DirectoryNamer/Epage/DirectoryNamer.php b/src/VichUploader/DirectoryNamer/Epage/DirectoryNamer.php new file mode 100644 index 0000000..e20f43a --- /dev/null +++ b/src/VichUploader/DirectoryNamer/Epage/DirectoryNamer.php @@ -0,0 +1,19 @@ +getUuid(); + } +} diff --git a/src/VichUploader/Namer/Epage/AvatarNamer.php b/src/VichUploader/Namer/Epage/AvatarNamer.php new file mode 100644 index 0000000..a996f40 --- /dev/null +++ b/src/VichUploader/Namer/Epage/AvatarNamer.php @@ -0,0 +1,21 @@ +getFile($object); + $extension = pathinfo($file->getClientOriginalName(), PATHINFO_EXTENSION); + return "avatar.".$extension; + } +} diff --git a/symfony.lock b/symfony.lock index 57ff09d..e9d5459 100644 --- a/symfony.lock +++ b/symfony.lock @@ -115,6 +115,18 @@ "bin/phpunit" ] }, + "pixelopen/cloudflare-turnstile-bundle": { + "version": "0.4", + "recipe": { + "repo": "github.com/symfony/recipes-contrib", + "branch": "main", + "version": "0.1", + "ref": "6173b9aff0056dd99ca70a2caa940cc7bcac0b4b" + }, + "files": [ + "config/packages/pixel_open_cloudflare_turnstile.yaml" + ] + }, "presta/sitemap-bundle": { "version": "v4.1.3" }, @@ -130,6 +142,18 @@ "config/packages/sentry.yaml" ] }, + "stripe/stripe-php": { + "version": "19.0", + "recipe": { + "repo": "github.com/symfony/recipes-contrib", + "branch": "main", + "version": "19.0", + "ref": "d6829c693e3927a8972c7671d74a1a5c505712b0" + }, + "files": [ + "config/packages/stripe.yaml" + ] + }, "symfony/amazon-mailer": { "version": "7.3", "recipe": { diff --git a/templates/admin/base.twig b/templates/admin/base.twig index 4799b14..d3322aa 100644 --- a/templates/admin/base.twig +++ b/templates/admin/base.twig @@ -60,6 +60,18 @@ L'overflow-y-auto n'est plus nécessaire ici car c'est le qui gère le sc AG (Assemblée Générale) + + + EPage - Validation + + + + EPage - Cosplayeur(s) + + + + EPage - Invitation + diff --git a/templates/admin/epage/invitation.twig b/templates/admin/epage/invitation.twig new file mode 100644 index 0000000..eebc776 --- /dev/null +++ b/templates/admin/epage/invitation.twig @@ -0,0 +1,93 @@ +{% extends 'admin/base.twig' %} + +{% block title %}Ajouter/Éditer un événement{% endblock %} +{% block page_title %} + Epage - Invitation +{% endblock %} + +{% block body %} + + + +
+ + +
+ {% if invitations is empty %} + +
+ Aucun invitation trouvé. Commencez par en créer un ! +
+ {% else %} +
+ + + + + + + + + + + + + + + + {% for event in events %} + + + + + + + + + + + + + {% endfor %} + +
+ Titre + + Début + + Actions +
+ {{ event.title }} + + {{ event.startAt|date('d/m/Y') }} + + + Modifier + + +
+ + +
+
+
+ {% endif %} +
+ + +
+{% endblock %} diff --git a/templates/base.twig b/templates/base.twig index fc3125c..f5f2489 100644 --- a/templates/base.twig +++ b/templates/base.twig @@ -115,7 +115,19 @@ {% if app.environment == "prod" %} {% endif %} - + + {# Le corps aura un fond gris clair pour correspondre au fond du logo #} @@ -128,6 +140,7 @@ { 'name': 'Boutiques'|trans, 'route': 'app_shop' }, { 'name': 'Documents'|trans, 'route': 'app_doc' }, { 'name': 'Dons'|trans, 'route': 'app_dons' }, + { 'name': 'Nous rejoindre'|trans, 'route': 'app_recruit' }, { 'name': 'Contact'|trans, 'route': 'app_contact' } ] %}
@@ -420,7 +433,7 @@ {# 2. Réseaux Sociaux #} -
gestion commercial +

{{ 'footer_follow_us_title'|trans }}

{% endblock %} + +{% block breadcrumb_schema %} + + + +{% endblock %} + +{% block body %} +
+ + {# --- HERO SECTION --- #} +
+
+
+

+ {{ 'hero.join.text'|trans }} {{ 'hero.brand.name'|trans }} +

+
+ {{ 'hero.fee.label'|trans }} +
{{ 'hero.fee.amount'|trans }}
+
+
+
+ + {# --- SECTION PROCESSUS D'ADHÉSION --- #} +
+
+

{{ 'process.title'|trans }}

+ +
+
+
{{ 'process.unanimous.percent'|trans }}
+

{{ 'process.unanimous.title'|trans }}

+

+ {{ 'process.unanimous.description'|trans|raw }} +

+
+ +
+
{{ 'process.feedback.icon'|trans }}
+

{{ 'process.feedback.title'|trans }}

+

+ {{ 'process.feedback.description'|trans|raw }} +

+
+
+
+
+ + {# --- GOUVERNANCE PARTAGÉE --- #} +
+
+
+

{{ 'governance.title'|trans }}

+
+ {% set steps = { + '💡': 'governance.step.propose'|trans, + '👂': 'governance.step.listen'|trans, + '🗳️': 'governance.step.vote'|trans, + '🚀': 'governance.step.apply'|trans + } %} + {% for icon, title in steps %} +
+
{{ icon }}
+

{{ title }}

+
+ {% endfor %} +
+

+ {{ 'governance.footer'|trans }} +

+
+
+
+ + {# --- SERVICES & HANDICAP --- #} +
+
+
+ {# Portfolio #} +
+
01
+
+

{{ 'services.portfolio.title'|trans }}

+

{{ 'services.portfolio.description'|trans }}

+
+
+ {# Handicap #} +
+
+
+

{{ 'services.inclusion.title'|trans }}

+

{{ 'services.inclusion.description'|trans }}

+
+
+
+
+
+ + {# --- SAFE SPACE --- #} +
+

{{ 'safespace.title'|trans }}

+

{{ 'safespace.subtitle'|trans }}

+
+ + {# --- FORMULAIRE --- #} + + +
+{% endblock %} diff --git a/templates/pages/onboarding.twig b/templates/pages/onboarding.twig new file mode 100644 index 0000000..14d7bad --- /dev/null +++ b/templates/pages/onboarding.twig @@ -0,0 +1,149 @@ +{% extends 'base.twig' %} + +{% block title %}{{'page.onboarding.title'|trans}}{% endblock %} +{% block meta_description %}{{'page.onboarding.description'|trans}}{% endblock %} +{% block canonical_url %}{% endblock %} + +{% block body %} + {{ form_start(form) }} + +
+ +

{{ 'onboarding.form.title'|trans }}

+ + +
+ + +
+
+

+ + {{ 'onboarding.form.section1.title'|trans }} +

+

{{ 'onboarding.form.section1.description'|trans }}

+ +
+ +
+ {{ form_row(form.name) }} +
+ +
+ {{ form_row(form.surname) }} +
+ +
+ {{ form_row(form.email) }} +
+ +
+ {{ form_row(form.birdth) }} +
+
+
+
+ + +
+

+ + {{ 'onboarding.form.section2.title'|trans }} +

+

{{ 'onboarding.form.section2.description'|trans }}

+ +
+
+ {{ form_row(form.nameCosplayer) }} +
+
+ {{ form_row(form.description)}} +
+ {{ form_row(form.epage) }} +
+
+ + +
+

+ + {{ 'onboarding.form.section3.title'|trans }} +

+

{{ 'onboarding.form.section3.description'|trans }}

+ +
+
+ {{ form_row(form.linkFacebook) }} +
+
+ {{ form_row(form.linkInstagram) }} +
+
+ {{ form_row(form.linkTiktok) }} +
+
+ {{ form_row(form.linkX) }} +
+
+
+ + +
+

+ + {{ 'onboarding.form.section4.title'|trans }} +

+
+ + +
+ {{ form_row(form.useDomain) }} +
+ + + +
+
+
+ + +
+ +
+ +
+ {{ form_end(form) }} + + +{% endblock %} diff --git a/templates/pages/prestation.twig b/templates/pages/prestation.twig index 78f26aa..1f5b671 100644 --- a/templates/pages/prestation.twig +++ b/templates/pages/prestation.twig @@ -285,7 +285,7 @@
{# BLOC DE TARIFICATION EPage (CARTE UNIQUE AVEC SÉLECTEUR) - CENTRÉ ET RESPONSIVE #} -