diff --git a/assets/styles/app.css b/assets/styles/app.css
index 35d653e..02f0d8b 100644
--- a/assets/styles/app.css
+++ b/assets/styles/app.css
@@ -1,9 +1,7 @@
@import "tailwindcss";
-@plugin "@tailwindcss/forms";
-@source not "../../public";
@theme {
- --font-sans: InterVariable, sans-serif;
- --font-sans--font-feature-settings: "cv02", "cv03", "cv04", "cv11";
+ --font-sans: InterVariable, sans-serif;
+ --font-sans--font-feature-settings: "cv02", "cv03", "cv04", "cv11";
}
@import "./css/swiper.css";
diff --git a/config/packages/twig.yaml b/config/packages/twig.yaml
index 71bce7d..b454750 100644
--- a/config/packages/twig.yaml
+++ b/config/packages/twig.yaml
@@ -1,8 +1,6 @@
twig:
- file_name_pattern: "*.twig"
- form_themes:
- - "form/custom_tailwind_theme.html.twig"
+ file_name_pattern: "*.twig"
when@test:
- twig:
- strict_variables: true
+ twig:
+ strict_variables: true
diff --git a/migrations/Version20260117031003.php b/migrations/Version20260117031003.php
new file mode 100644
index 0000000..6e53ff7
--- /dev/null
+++ b/migrations/Version20260117031003.php
@@ -0,0 +1,31 @@
+addSql('CREATE TABLE configuration (id INT GENERATED BY DEFAULT AS IDENTITY NOT NULL, owner_mail VARCHAR(255) DEFAULT NULL, PRIMARY KEY (id))');
+ }
+
+ public function down(Schema $schema): void
+ {
+ // this down() migration is auto-generated, please modify it to your needs
+ $this->addSql('DROP TABLE configuration');
+ }
+}
diff --git a/migrations/Version20260117033945.php b/migrations/Version20260117033945.php
new file mode 100644
index 0000000..79914e6
--- /dev/null
+++ b/migrations/Version20260117033945.php
@@ -0,0 +1,31 @@
+addSql('CREATE TABLE contact (id INT GENERATED BY DEFAULT AS IDENTITY NOT NULL, send_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, is_valid BOOLEAN NOT NULL, PRIMARY KEY (id))');
+ }
+
+ public function down(Schema $schema): void
+ {
+ // this down() migration is auto-generated, please modify it to your needs
+ $this->addSql('DROP TABLE contact');
+ }
+}
diff --git a/src/Controller/ConfigurationController.php b/src/Controller/ConfigurationController.php
new file mode 100644
index 0000000..69264b6
--- /dev/null
+++ b/src/Controller/ConfigurationController.php
@@ -0,0 +1,43 @@
+entityManager = $entityManager;
+ }
+
+ #[Route('/admin/configuration', name: 'admin_configuration_index')]
+ public function index(Request $request): Response
+ {
+ $configuration = $this->entityManager->getRepository(Configuration::class)->findOneBy(['id' => 1]);
+ $form = $this->createForm(ConfigurationType::class, $configuration);
+ $form->handleRequest($request);
+ if ($form->isSubmitted() && $form->isValid()) {
+ $isConfiguration = $this->entityManager->getRepository(Configuration::class)->findOneBy(['id' => 1]);
+ if ($isConfiguration) {
+ $isConfiguration->setOwnerMail($form->getData()->getOwnerMail());
+ } else {
+ $configuration = new Configuration();
+ $configuration->setOwnerMail($form->getData()->getOwnerMail());
+ $this->entityManager->persist($configuration);
+ }
+ $this->entityManager->flush();
+ }
+ return $this->render('configuration/index.html.twig', [
+ 'form' => $form->createView(),
+ ]);
+ }
+}
diff --git a/src/Controller/PageController.php b/src/Controller/PageController.php
index eadddcc..fa13bc3 100644
--- a/src/Controller/PageController.php
+++ b/src/Controller/PageController.php
@@ -3,18 +3,26 @@
namespace App\Controller;
use App\Entity\News;
+use App\Entity\Contact;
+use App\Form\ContactType;
+use App\Service\MailService;
+use DateTimeImmutable;
use Doctrine\ORM\EntityManagerInterface;
+use Exception;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
+use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
final class PageController extends AbstractController
{
private EntityManagerInterface $entityManager;
+ private MailService $mailService;
- public function __construct(EntityManagerInterface $entityManager)
+ public function __construct(EntityManagerInterface $entityManager, MailService $mailService)
{
$this->entityManager = $entityManager;
+ $this->mailService = $mailService;
}
#[Route('/', name: 'home')]
@@ -47,6 +55,43 @@ final class PageController extends AbstractController
return $this->render('page/show.html.twig', []);
}
+ #[Route('/contact', name: 'contact')]
+ public function contact(Request $request): Response
+ {
+ $form = $this->createForm(ContactType::class);
+ $form->handleRequest($request);
+
+ if ($form->isSubmitted() && $form->isValid()) {
+ $contact = new Contact();
+ $contact->setSendAt(new DateTimeImmutable());
+ $data = $form->getData();
+
+ $honeyPot = $form->getData()['mobilePhoneNumber'];
+ if ($honeyPot !== NULL) {
+ $contact->setIsValid(false);
+ $this->addFlash('success', 'Votre message a été envoyé avec succès.');
+ $this->entityManager->persist($contact);
+ $this->entityManager->flush();
+ return $this->redirectToRoute('contact');
+ }
+ try {
+ $this->mailService->sendContactMail($data);
+ $contact->setIsValid(true);
+ $this->addFlash('success', 'Votre message a été envoyé avec succès.');
+ } catch (Exception $e) {
+ $contact->setIsValid(false);
+ $this->addFlash('error', 'Une erreur est survenue lors de l’envoi du message.');
+ }
+ $this->entityManager->persist($contact);
+ $this->entityManager->flush();
+ return $this->redirectToRoute('contact');
+ }
+
+ return $this->render('page/contact.html.twig', [
+ 'form' => $form->createView(),
+ ]);
+ }
+
#[Route('/mentions-legales', name: 'legalmentions')]
public function legalmentions(): Response
{
diff --git a/src/Entity/Configuration.php b/src/Entity/Configuration.php
new file mode 100644
index 0000000..2b2327b
--- /dev/null
+++ b/src/Entity/Configuration.php
@@ -0,0 +1,35 @@
+id;
+ }
+
+ public function getOwnerMail(): ?string
+ {
+ return $this->ownerMail;
+ }
+
+ public function setOwnerMail(?string $ownerMail): static
+ {
+ $this->ownerMail = $ownerMail;
+
+ return $this;
+ }
+}
diff --git a/src/Entity/Contact.php b/src/Entity/Contact.php
new file mode 100644
index 0000000..229773a
--- /dev/null
+++ b/src/Entity/Contact.php
@@ -0,0 +1,50 @@
+id;
+ }
+
+ public function getSendAt(): ?\DateTimeImmutable
+ {
+ return $this->sendAt;
+ }
+
+ public function setSendAt(\DateTimeImmutable $sendAt): static
+ {
+ $this->sendAt = $sendAt;
+
+ return $this;
+ }
+
+ public function isValid(): ?bool
+ {
+ return $this->isValid;
+ }
+
+ public function setIsValid(bool $isValid): static
+ {
+ $this->isValid = $isValid;
+
+ return $this;
+ }
+}
diff --git a/src/Form/ConfigurationType.php b/src/Form/ConfigurationType.php
new file mode 100644
index 0000000..4352f40
--- /dev/null
+++ b/src/Form/ConfigurationType.php
@@ -0,0 +1,44 @@
+add('ownerMail', EmailType::class, [
+ 'label' => 'Email du propriétaire',
+ 'label_attr' => [
+ 'class' => 'block text-sm font-semibold text-gray-900',
+ ],
+ 'attr' => [
+ 'class' => 'block w-full rounded-md bg-white px-3.5 py-2 text-base text-gray-900 border border-gray-300 placeholder:text-gray-400 focus:border-amber-600 focus:ring-2 focus:ring-amber-600/30 focus:outline-none',
+ ],
+ 'row_attr' => [
+ 'class' => 'space-y-1',
+ ],
+ ])
+ ->add('submit', SubmitType::class, [
+ 'label' => 'Enregistrer',
+ 'attr' => [
+ 'class' => 'mt-2 inline-flex items-center justify-center rounded-md bg-amber-600 px-3.5 py-2.5 text-base font-semibold text-white shadow-sm hover:bg-amber-700 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-amber-600',
+ ],
+ ])
+ ;
+ }
+
+ public function configureOptions(OptionsResolver $resolver): void
+ {
+ $resolver->setDefaults([
+ 'data_class' => Configuration::class,
+ ]);
+ }
+}
diff --git a/src/Form/ContactType.php b/src/Form/ContactType.php
new file mode 100644
index 0000000..c8da7f0
--- /dev/null
+++ b/src/Form/ContactType.php
@@ -0,0 +1,47 @@
+add('lastName', TextType::class, [
+ 'constraints' => [new Assert\NotBlank()],
+ ])
+ ->add('firstName', TextType::class, [
+ 'constraints' => [new Assert\NotBlank()],
+ ])
+ ->add('company', TextType::class, [
+ 'required' => false,
+ ])
+ ->add('email', EmailType::class, [
+ 'constraints' => [
+ new Assert\NotBlank(),
+ new Assert\Email(),
+ ],
+ ])
+ ->add('phoneNumber', TextType::class, [
+ 'required' => false,
+ ])
+ ->add('mobilePhoneNumber', TextType::class, [
+ 'required' => false,
+ ])
+ ->add('message', TextareaType::class, [
+ 'constraints' => [new Assert\NotBlank()],
+ ])
+ ->add('agreeToPolicies', CheckboxType::class, [
+ 'constraints' => [new Assert\IsTrue()],
+ ])
+ ;
+ }
+}
diff --git a/src/Form/NewsType.php b/src/Form/NewsType.php
index 53b5014..71aa0c3 100644
--- a/src/Form/NewsType.php
+++ b/src/Form/NewsType.php
@@ -19,6 +19,15 @@ class NewsType extends AbstractType
$builder
->add('title', TextType::class, [
'label' => 'Titre',
+ 'label_attr' => [
+ 'class' => 'block text-sm font-semibold text-gray-900',
+ ],
+ 'attr' => [
+ 'class' => 'block w-full rounded-md bg-white px-3.5 py-2 text-base text-gray-900 border border-gray-300 placeholder:text-gray-400 focus:border-amber-600 focus:ring-2 focus:ring-amber-600/30 focus:outline-none',
+ ],
+ 'row_attr' => [
+ 'class' => 'space-y-1',
+ ],
])
->add('description', CKEditor5Type::class, [
'required' => false,
diff --git a/src/Repository/ConfigurationRepository.php b/src/Repository/ConfigurationRepository.php
new file mode 100644
index 0000000..48b36d1
--- /dev/null
+++ b/src/Repository/ConfigurationRepository.php
@@ -0,0 +1,43 @@
+
+ */
+class ConfigurationRepository extends ServiceEntityRepository
+{
+ public function __construct(ManagerRegistry $registry)
+ {
+ parent::__construct($registry, Configuration::class);
+ }
+
+ // /**
+ // * @return Configuration[] Returns an array of Configuration objects
+ // */
+ // public function findByExampleField($value): array
+ // {
+ // return $this->createQueryBuilder('c')
+ // ->andWhere('c.exampleField = :val')
+ // ->setParameter('val', $value)
+ // ->orderBy('c.id', 'ASC')
+ // ->setMaxResults(10)
+ // ->getQuery()
+ // ->getResult()
+ // ;
+ // }
+
+ // public function findOneBySomeField($value): ?Configuration
+ // {
+ // return $this->createQueryBuilder('c')
+ // ->andWhere('c.exampleField = :val')
+ // ->setParameter('val', $value)
+ // ->getQuery()
+ // ->getOneOrNullResult()
+ // ;
+ // }
+}
diff --git a/src/Repository/ContactRepository.php b/src/Repository/ContactRepository.php
new file mode 100644
index 0000000..4944392
--- /dev/null
+++ b/src/Repository/ContactRepository.php
@@ -0,0 +1,43 @@
+
+ */
+class ContactRepository extends ServiceEntityRepository
+{
+ public function __construct(ManagerRegistry $registry)
+ {
+ parent::__construct($registry, Contact::class);
+ }
+
+ // /**
+ // * @return Contact[] Returns an array of Contact objects
+ // */
+ // public function findByExampleField($value): array
+ // {
+ // return $this->createQueryBuilder('c')
+ // ->andWhere('c.exampleField = :val')
+ // ->setParameter('val', $value)
+ // ->orderBy('c.id', 'ASC')
+ // ->setMaxResults(10)
+ // ->getQuery()
+ // ->getResult()
+ // ;
+ // }
+
+ // public function findOneBySomeField($value): ?Contact
+ // {
+ // return $this->createQueryBuilder('c')
+ // ->andWhere('c.exampleField = :val')
+ // ->setParameter('val', $value)
+ // ->getQuery()
+ // ->getOneOrNullResult()
+ // ;
+ // }
+}
diff --git a/src/Service/MailService.php b/src/Service/MailService.php
new file mode 100644
index 0000000..d28ebbe
--- /dev/null
+++ b/src/Service/MailService.php
@@ -0,0 +1,36 @@
+mailer = $mailer;
+ $this->entityManager = $entityManager;
+ }
+
+ public function sendContactMail(array $data): void
+ {
+ $configuration = $this->entityManager->getRepository(Configuration::class)->findOneBy(['id' => 1]);
+ $email = (new TemplatedEmail())
+ ->from($configuration->getOwnerMail())
+ ->to($configuration->getOwnerMail())
+ ->replyTo($data["email"])
+ ->subject('Nouvelle demande de contact')
+ ->htmlTemplate('mail/contact.html.twig')
+ ->context([
+ 'data' => $data,
+ ]);
+
+ $this->mailer->send($email);
+ }
+}
diff --git a/templates/base-admin/sidebar.html.twig b/templates/base-admin/sidebar.html.twig
index 88ca0e7..84430a9 100644
--- a/templates/base-admin/sidebar.html.twig
+++ b/templates/base-admin/sidebar.html.twig
@@ -315,6 +315,19 @@
Fonctionnalité
+
+
+
+ C
+
+ Configuration
+
+
{% endif %}
diff --git a/templates/base/header.html.twig b/templates/base/header.html.twig
index d465ae0..5223c29 100644
--- a/templates/base/header.html.twig
+++ b/templates/base/header.html.twig
@@ -64,8 +64,8 @@
Me connaître
Me contacter
diff --git a/templates/configuration/index.html.twig b/templates/configuration/index.html.twig
new file mode 100644
index 0000000..618b58d
--- /dev/null
+++ b/templates/configuration/index.html.twig
@@ -0,0 +1,27 @@
+{% extends 'base_admin.html.twig' %}
+
+{% block title %}
+ Configuration
+{% endblock %}
+
+{% block body %}
+
+
+
+ {{ form_start(form) }}
+ {{ form_widget(form) }}
+ {{ form_end(form) }}
+
+
+
+
+{% endblock %}
diff --git a/templates/form/custom_tailwind_theme.html.twig b/templates/form/custom_tailwind_theme.html.twig
deleted file mode 100644
index 20ed0bd..0000000
--- a/templates/form/custom_tailwind_theme.html.twig
+++ /dev/null
@@ -1,5 +0,0 @@
-{% use 'tailwind_2_layout.html.twig' %}
-
-{%- block form_row -%}
-{%- set row_class = row_class|default('mb-4 flex flex-col space-y-1') -%}
-{{- parent() -}}{%- endblock form_row -%}{%- block widget_attributes -%}{%- set widget_class = widget_class|default('block w-full rounded-lg border-gray-300 focus:border-amber-500 focus:ring-amber-500') -%}{%- set widget_disabled_class = widget_disabled_class|default('opacity-50 cursor-not-allowed') -%}{%- set widget_errors_class = widget_errors_class|default('border-red-500 ring-red-500') -%}{{- parent() -}}{%- endblock widget_attributes -%}{%- block form_label -%}{%- set label_class = label_class|default('font-medium text-gray-700') -%}{{- parent() -}}{%- endblock form_label -%}{%- block form_help -%}{%- set help_class = help_class|default('text-sm text-gray-500') -%}{{- parent() -}}{%- endblock form_help -%}{%- block form_errors -%}{%- set error_item_class = error_item_class|default('text-sm text-red-600') -%}{{- parent() -}}{%- endblock form_errors -%}
diff --git a/templates/mail/contact.html.twig b/templates/mail/contact.html.twig
new file mode 100644
index 0000000..1238f20
--- /dev/null
+++ b/templates/mail/contact.html.twig
@@ -0,0 +1,238 @@
+
+
+
+
+
+
+ Nouveau message de contact
+
+
+
+
+
+
+ | |
+
+
+
+
+
+
+
+ |
+
+ {{ data.lastName }} {{ data.firstName }}
+
+ {% if data.company %}
+
+ {{ data.company }}
+
+ {% endif %}
+
+ {{ data.message }}
+
+
+
+ Mon adresse électronique : {{ data.email }}
+
+ {% if data.phoneNumber %}
+
+ Mes coordonnées téléphoniques : {{ data.phoneNumber }}
+
+ {% endif %}
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+ |
+ |
+
+
+
+
diff --git a/templates/page/contact.html.twig b/templates/page/contact.html.twig
new file mode 100644
index 0000000..d20fed6
--- /dev/null
+++ b/templates/page/contact.html.twig
@@ -0,0 +1,311 @@
+{% extends 'base.html.twig' %}
+{% block metadata %}
+ {# ============================
+ METADATA / SEO
+ ============================ #}
+
+ {% set meta_title = meta_title is defined
+ ? meta_title
+ : 'Contact – Spectacles vivants et artisanat pour collectivités'
+ %}
+ {% set meta_description = meta_description is defined
+ ? meta_description
+ : 'Contactez-nous pour vos projets de spectacles vivants et de créations artisanales destinés aux collectivités, écoles et lieux culturels.'
+ %}
+ {% set meta_image = meta_image is defined
+ ? meta_image
+ : asset('images/logo.jpg')
+ %}
+ {% set meta_url = meta_url is defined ? meta_url : app.request.uri %}
+ {% set meta_site_name = meta_site_name is defined
+ ? meta_site_name
+ : 'Nom du site'
+ %}
+ {% set meta_type = meta_type is defined ? meta_type : 'website' %}
+
+
+ {{ meta_title }}
+
+
+
+
+
+
+
+
+
+ {# ============================
+ OPEN GRAPH (Facebook, LinkedIn…)
+ ============================ #}
+
+
+
+
+
+
+
+
+
+ {# Optionnel mais recommandé #}
+
+
+
+ {# ============================
+ TWITTER CARDS
+ ============================ #}
+
+
+
+
+
+{% endblock %}
+{% block body %}
+
+
+
+
+ Contactez moi
+
+
+ Vous pouvez me contacter via le formulaire ci-dessous et je vous
+ répondrai rapidement.
+
+ {% for label, messages in app.flashes %}
+ {% for message in messages %}
+
+ {{ message }}
+
+ {% endfor %}
+ {% endfor %}
+
+ {{
+ form_start(
+ form,
+ {
+ attr: {
+ class: 'mx-auto mt-16 max-w-xl sm:mt-20'
+ }
+ }
+ )
+ }}
+
+
+
+
+ {{
+ form_widget(
+ form.lastName,
+ {
+ attr: {
+ placeholder: 'Votre nom',
+ autocomplete: 'family-name',
+ class: 'block w-full rounded-md bg-white px-3.5 py-2 text-base text-gray-900 outline-1 -outline-offset-1 outline-gray-300 placeholder:text-gray-400 focus:outline-2 focus:-outline-offset-2 focus:outline-amber-600'
+ }
+ }
+ )
+ }}
+
+
+
+
+
+ {{
+ form_widget(
+ form.firstName,
+ {
+ attr: {
+ placeholder: 'Votre prénom',
+ autocomplete: 'given-name',
+ class: 'block w-full rounded-md bg-white px-3.5 py-2 text-base text-gray-900 outline-1 -outline-offset-1 outline-gray-300 placeholder:text-gray-400 focus:outline-2 focus:-outline-offset-2 focus:outline-amber-600'
+ }
+ }
+ )
+ }}
+
+
+
+
+
+
+ {{
+ form_widget(
+ form.company,
+ {
+ attr: {
+ placeholder: 'Raison sociale',
+ autocomplete: 'organization',
+ class: 'block w-full rounded-md bg-white px-3.5 py-2 text-base text-gray-900 outline-1 -outline-offset-1 outline-gray-300 placeholder:text-gray-400 focus:outline-2 focus:-outline-offset-2 focus:outline-amber-600'
+ }
+ }
+ )
+ }}
+
+
+
+
+
+
+ {{
+ form_widget(
+ form.email,
+ {
+ attr: {
+ placeholder: 'Votre adresse email',
+ autocomplete: 'email',
+ class: 'block w-full rounded-md bg-white px-3.5 py-2 text-base text-gray-900 outline-1 -outline-offset-1 outline-gray-300 placeholder:text-gray-400 focus:outline-2 focus:-outline-offset-2 focus:outline-amber-600'
+ }
+ }
+ )
+ }}
+
+
+
+
+
+
+ {{
+ form_widget(
+ form.phoneNumber,
+ {
+ attr: {
+ placeholder: 'Votre numéro de téléphone',
+ autocomplete: 'tel',
+ class: 'block w-full rounded-md bg-white px-3.5 py-2 text-base text-gray-900 outline-1 -outline-offset-1 outline-gray-300 placeholder:text-gray-400 focus:outline-2 focus:-outline-offset-2 focus:outline-amber-600'
+ }
+ }
+ )
+ }}
+
+
+
+
+
+
+ {{
+ form_widget(
+ form.mobilePhoneNumber,
+ {
+ attr: {
+ placeholder: 'Votre numéro de téléphone',
+ class: 'block w-full rounded-md bg-white px-3.5 py-2 text-base text-gray-900 outline-1 -outline-offset-1 outline-gray-300 placeholder:text-gray-400 focus:outline-2 focus:-outline-offset-2 focus:outline-amber-600'
+ }
+ }
+ )
+ }}
+
+
+
+
+
+
+ {{
+ form_widget(
+ form.message,
+ {
+ attr: {
+ rows: 4,
+ placeholder: 'Votre message',
+ class: 'block w-full rounded-md bg-white px-3.5 py-2 text-base text-gray-900 outline-1 -outline-offset-1 outline-gray-300 placeholder:text-gray-400 focus:outline-2 focus:-outline-offset-2 focus:outline-amber-600'
+ }
+ }
+ )
+ }}
+
+
+
+
+
+
+
+
+
+
+ {{
+ form_widget(
+ form.agreeToPolicies,
+ {
+ attr: {
+ class: 'absolute inset-0 size-full appearance-none focus:outline-hidden'
+ }
+ }
+ )
+ }}
+
+
+
+
+
+
+
+
+
+ {{ form_end(form) }}
+
+{% endblock %}