Add contact page
This commit is contained in:
@@ -1,6 +1,4 @@
|
||||
@import "tailwindcss";
|
||||
@plugin "@tailwindcss/forms";
|
||||
@source not "../../public";
|
||||
@theme {
|
||||
--font-sans: InterVariable, sans-serif;
|
||||
--font-sans--font-feature-settings: "cv02", "cv03", "cv04", "cv11";
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
twig:
|
||||
file_name_pattern: "*.twig"
|
||||
form_themes:
|
||||
- "form/custom_tailwind_theme.html.twig"
|
||||
|
||||
when@test:
|
||||
twig:
|
||||
|
||||
31
migrations/Version20260117031003.php
Normal file
31
migrations/Version20260117031003.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?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 Version20260117031003 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 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');
|
||||
}
|
||||
}
|
||||
31
migrations/Version20260117033945.php
Normal file
31
migrations/Version20260117033945.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?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 Version20260117033945 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 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');
|
||||
}
|
||||
}
|
||||
43
src/Controller/ConfigurationController.php
Normal file
43
src/Controller/ConfigurationController.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use App\Entity\Configuration;
|
||||
use App\Form\ConfigurationType;
|
||||
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;
|
||||
|
||||
final class ConfigurationController extends AbstractController
|
||||
{
|
||||
private EntityManagerInterface $entityManager;
|
||||
|
||||
public function __construct(EntityManagerInterface $entityManager)
|
||||
{
|
||||
$this->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(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
|
||||
35
src/Entity/Configuration.php
Normal file
35
src/Entity/Configuration.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use App\Repository\ConfigurationRepository;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
#[ORM\Entity(repositoryClass: ConfigurationRepository::class)]
|
||||
class Configuration
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\Column(length: 255, nullable: true)]
|
||||
private ?string $ownerMail = null;
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getOwnerMail(): ?string
|
||||
{
|
||||
return $this->ownerMail;
|
||||
}
|
||||
|
||||
public function setOwnerMail(?string $ownerMail): static
|
||||
{
|
||||
$this->ownerMail = $ownerMail;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
50
src/Entity/Contact.php
Normal file
50
src/Entity/Contact.php
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use App\Repository\ContactRepository;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
#[ORM\Entity(repositoryClass: ContactRepository::class)]
|
||||
class Contact
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\Column]
|
||||
private ?\DateTimeImmutable $sendAt = null;
|
||||
|
||||
#[ORM\Column]
|
||||
private ?bool $isValid = null;
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->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;
|
||||
}
|
||||
}
|
||||
44
src/Form/ConfigurationType.php
Normal file
44
src/Form/ConfigurationType.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace App\Form;
|
||||
|
||||
use App\Entity\Configuration;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\EmailType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
class ConfigurationType extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder
|
||||
->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,
|
||||
]);
|
||||
}
|
||||
}
|
||||
47
src/Form/ContactType.php
Normal file
47
src/Form/ContactType.php
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace App\Form;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\EmailType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
|
||||
class ContactType extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder
|
||||
->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()],
|
||||
])
|
||||
;
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
43
src/Repository/ConfigurationRepository.php
Normal file
43
src/Repository/ConfigurationRepository.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace App\Repository;
|
||||
|
||||
use App\Entity\Configuration;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
/**
|
||||
* @extends ServiceEntityRepository<Configuration>
|
||||
*/
|
||||
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()
|
||||
// ;
|
||||
// }
|
||||
}
|
||||
43
src/Repository/ContactRepository.php
Normal file
43
src/Repository/ContactRepository.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace App\Repository;
|
||||
|
||||
use App\Entity\Contact;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
/**
|
||||
* @extends ServiceEntityRepository<Contact>
|
||||
*/
|
||||
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()
|
||||
// ;
|
||||
// }
|
||||
}
|
||||
36
src/Service/MailService.php
Normal file
36
src/Service/MailService.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace App\Service;
|
||||
use App\Entity\Configuration;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Bridge\Twig\Mime\TemplatedEmail;
|
||||
use Symfony\Component\Mailer\MailerInterface;
|
||||
use Symfony\Component\Mime\Email;
|
||||
|
||||
class MailService
|
||||
{
|
||||
private MailerInterface $mailer;
|
||||
private EntityManagerInterface $entityManager;
|
||||
|
||||
public function __construct(MailerInterface $mailer, EntityManagerInterface $entityManager)
|
||||
{
|
||||
$this->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);
|
||||
}
|
||||
}
|
||||
@@ -315,6 +315,19 @@
|
||||
<span class="truncate">Fonctionnalité</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="{{ path('admin_configuration_index') }}"
|
||||
class="group flex gap-x-3 rounded-md p-2 text-sm/6 font-semibold hover:bg-gray-50 hover:text-amber-600{% if path starts with '/admin/configuration' %} bg-gray-50 text-amber-600 {% else %} text-gray-700 hover:bg-gray-50 hover:text-amber-600{% endif %}"
|
||||
>
|
||||
<span
|
||||
class="flex size-6 shrink-0 items-center justify-center rounded-lg border border-gray-200 bg-white text-[0.625rem] font-medium {% if path starts with '/admin/configuration' %} border-amber-600 text-amber-600 {% else %} text-gray-400 group-hover:border-amber-600 group-hover:text-amber-600{% endif %}"
|
||||
>
|
||||
C
|
||||
</span>
|
||||
<span class="truncate">Configuration</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
@@ -64,8 +64,8 @@
|
||||
Me connaître
|
||||
</a>
|
||||
<a
|
||||
href="tel:+33672701956"
|
||||
class="text-sm/6 font-semibold text-gray-900 bg-amber-300 rounded-2xl px-4 py-2 hover:bg-amber-400 transition-colors"
|
||||
href="{{ path('contact') }}"
|
||||
class="text-sm/6 font-semibold text-gray-900"
|
||||
>
|
||||
Me contacter
|
||||
</a>
|
||||
|
||||
27
templates/configuration/index.html.twig
Normal file
27
templates/configuration/index.html.twig
Normal file
@@ -0,0 +1,27 @@
|
||||
{% extends 'base_admin.html.twig' %}
|
||||
|
||||
{% block title %}
|
||||
Configuration
|
||||
{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<main class="lg:pl-72">
|
||||
<div class="xl:pr-96">
|
||||
<div class="px-4 py-10 sm:px-6 lg:px-8 lg:py-6">
|
||||
{{ form_start(form) }}
|
||||
{{ form_widget(form) }}
|
||||
{{ form_end(form) }}
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
<aside
|
||||
class="fixed inset-y-0 right-0 hidden w-96 overflow-y-auto border-l border-gray-200 px-4 py-6 sm:px-6 lg:px-8 xl:block dark:border-white/10"
|
||||
>
|
||||
<a
|
||||
href="#"
|
||||
class="rounded-md bg-amber-600 px-4 py-2.5 text-sm font-semibold text-white shadow-xs hover:bg-amber-500 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-amber-600 dark:bg-amber-500 dark:shadow-none dark:hover:bg-amber-400 dark:focus-visible:outline-amber-500"
|
||||
>
|
||||
Modifier la configuration
|
||||
</a>
|
||||
</aside>
|
||||
{% endblock %}
|
||||
@@ -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 -%}
|
||||
238
templates/mail/contact.html.twig
Normal file
238
templates/mail/contact.html.twig
Normal file
@@ -0,0 +1,238 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<title>
|
||||
Nouveau message de contact
|
||||
</title>
|
||||
<style media="all" type="text/css">
|
||||
/* ------------------------------------- GLOBAL RESETS
|
||||
------------------------------------- */
|
||||
|
||||
body { font-family: Helvetica, sans-serif; -webkit-font-smoothing:
|
||||
antialiased; font-size: 16px; line-height: 1.3; -ms-text-size-adjust:
|
||||
100%; -webkit-text-size-adjust: 100%; }
|
||||
|
||||
table { border-collapse: separate; mso-table-lspace: 0pt;
|
||||
mso-table-rspace: 0pt; width: 100%; }
|
||||
|
||||
table td { font-family: Helvetica, sans-serif; font-size: 16px;
|
||||
vertical-align: top; } /* ------------------------------------- BODY &
|
||||
CONTAINER ------------------------------------- */
|
||||
|
||||
body { background-color: #f4f5f6; margin: 0; padding: 0; }
|
||||
|
||||
.body { background-color: #f4f5f6; width: 100%; }
|
||||
|
||||
.container { margin: 0 auto !important; max-width: 600px; padding: 0;
|
||||
padding-top: 24px; width: 600px; }
|
||||
|
||||
.content { box-sizing: border-box; display: block; margin: 0 auto;
|
||||
max-width: 600px; padding: 0; } /* -------------------------------------
|
||||
HEADER, FOOTER, MAIN ------------------------------------- */
|
||||
|
||||
.main { background: #ffffff; border: 1px solid #eaebed; border-radius:
|
||||
16px; width: 100%; }
|
||||
|
||||
.wrapper { box-sizing: border-box; padding: 24px; }
|
||||
|
||||
.footer { clear: both; padding-top: 24px; text-align: center; width: 100%;
|
||||
}
|
||||
|
||||
.footer td, .footer p, .footer span, .footer a { color: #9a9ea6;
|
||||
font-size: 16px; text-align: center; } /*
|
||||
------------------------------------- TYPOGRAPHY
|
||||
------------------------------------- */
|
||||
|
||||
p { font-family: Helvetica, sans-serif; font-size: 16px; font-weight:
|
||||
normal; margin: 0; margin-bottom: 16px; }
|
||||
|
||||
a { color: #0867ec; text-decoration: underline; } /*
|
||||
------------------------------------- BUTTONS
|
||||
------------------------------------- */
|
||||
|
||||
.btn { box-sizing: border-box; min-width: 100% !important; width: 100%; }
|
||||
|
||||
.btn > tbody > tr > td { padding-bottom: 16px; }
|
||||
|
||||
.btn table { width: auto; }
|
||||
|
||||
.btn table td { background-color: #ffffff; border-radius: 4px; text-align:
|
||||
center; }
|
||||
|
||||
.btn a { background-color: #ffffff; border: solid 2px #0867ec;
|
||||
border-radius: 4px; box-sizing: border-box; color: #0867ec; cursor:
|
||||
pointer; display: inline-block; font-size: 16px; font-weight: bold;
|
||||
margin: 0; padding: 12px 24px; text-decoration: none; text-transform:
|
||||
capitalize; }
|
||||
|
||||
.btn-primary table td { background-color: #0867ec; }
|
||||
|
||||
.btn-primary a { background-color: #0867ec; border-color: #0867ec; color:
|
||||
#ffffff; }
|
||||
|
||||
@media all { .btn-primary table td:hover { background-color: #ec0867
|
||||
!important; } .btn-primary a:hover { background-color: #ec0867 !important;
|
||||
border-color: #ec0867 !important; } }
|
||||
|
||||
/* ------------------------------------- OTHER STYLES THAT MIGHT BE USEFUL
|
||||
------------------------------------- */
|
||||
|
||||
.last { margin-bottom: 0; }
|
||||
|
||||
.first { margin-top: 0; }
|
||||
|
||||
.align-center { text-align: center; }
|
||||
|
||||
.align-right { text-align: right; }
|
||||
|
||||
.align-left { text-align: left; }
|
||||
|
||||
.text-link { color: #0867ec !important; text-decoration: underline
|
||||
!important; }
|
||||
|
||||
.clear { clear: both; }
|
||||
|
||||
.mt0 { margin-top: 0; }
|
||||
|
||||
.mb0 { margin-bottom: 0; }
|
||||
|
||||
.preheader { color: transparent; display: none; height: 0; max-height: 0;
|
||||
max-width: 0; opacity: 0; overflow: hidden; mso-hide: all; visibility:
|
||||
hidden; width: 0; }
|
||||
|
||||
.powered-by a { text-decoration: none; }
|
||||
|
||||
/* ------------------------------------- RESPONSIVE AND MOBILE FRIENDLY
|
||||
STYLES ------------------------------------- */
|
||||
|
||||
@media only screen and (max-width: 640px) { .main p, .main td, .main span
|
||||
{ font-size: 16px !important; } .wrapper { padding: 8px !important; }
|
||||
.content { padding: 0 !important; } .container { padding: 0 !important;
|
||||
padding-top: 8px !important; width: 100% !important; } .main {
|
||||
border-left-width: 0 !important; border-radius: 0 !important;
|
||||
border-right-width: 0 !important; } .btn table { max-width: 100%
|
||||
!important; width: 100% !important; } .btn a { font-size: 16px !important;
|
||||
max-width: 100% !important; width: 100% !important; } } /*
|
||||
------------------------------------- PRESERVE THESE STYLES IN THE HEAD
|
||||
------------------------------------- */
|
||||
|
||||
@media all { .ExternalClass { width: 100%; } .ExternalClass,
|
||||
.ExternalClass p, .ExternalClass span, .ExternalClass font, .ExternalClass
|
||||
td, .ExternalClass div { line-height: 100%; } .apple-link a { color:
|
||||
inherit !important; font-family: inherit !important; font-size: inherit
|
||||
!important; font-weight: inherit !important; line-height: inherit
|
||||
!important; text-decoration: none !important; } #MessageViewBody a {
|
||||
color: inherit; text-decoration: none; font-size: inherit; font-family:
|
||||
inherit; font-weight: inherit; line-height: inherit; } }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<table
|
||||
role="presentation"
|
||||
border="0"
|
||||
cellpadding="0"
|
||||
cellspacing="0"
|
||||
class="body"
|
||||
>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td class="container">
|
||||
<div class="content">
|
||||
<!-- START CENTERED WHITE CONTAINER -->
|
||||
<span class="preheader">Nouveau message de contact</span>
|
||||
<table
|
||||
role="presentation"
|
||||
border="0"
|
||||
cellpadding="0"
|
||||
cellspacing="0"
|
||||
class="main"
|
||||
>
|
||||
<!-- START MAIN CONTENT AREA -->
|
||||
<tr>
|
||||
<td class="wrapper">
|
||||
<p>
|
||||
{{ data.lastName }} {{ data.firstName }}
|
||||
</p>
|
||||
{% if data.company %}
|
||||
<p>
|
||||
{{ data.company }}
|
||||
</p>
|
||||
{% endif %}
|
||||
<p>
|
||||
{{ data.message }}
|
||||
</p>
|
||||
<table
|
||||
role="presentation"
|
||||
border="0"
|
||||
cellpadding="0"
|
||||
cellspacing="0"
|
||||
class="btn btn-primary"
|
||||
>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="left">
|
||||
<table
|
||||
role="presentation"
|
||||
border="0"
|
||||
cellpadding="0"
|
||||
cellspacing="0"
|
||||
>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<a
|
||||
href="https://arts-ticule.fr"
|
||||
target="_blank"
|
||||
>
|
||||
Voir le site
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p>
|
||||
Mon adresse électronique : {{ data.email }}
|
||||
</p>
|
||||
{% if data.phoneNumber %}
|
||||
<p>
|
||||
Mes coordonnées téléphoniques : {{ data.phoneNumber }}
|
||||
</p>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- END MAIN CONTENT AREA -->
|
||||
</table>
|
||||
|
||||
<!-- START FOOTER -->
|
||||
<div class="footer">
|
||||
<table
|
||||
role="presentation"
|
||||
border="0"
|
||||
cellpadding="0"
|
||||
cellspacing="0"
|
||||
>
|
||||
<tr>
|
||||
<td class="content-block">
|
||||
<span class="apple-link">Arts-Ticule</span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- END FOOTER -->
|
||||
|
||||
<!-- END CENTERED WHITE CONTAINER -->
|
||||
</div>
|
||||
</td>
|
||||
<td> </td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
311
templates/page/contact.html.twig
Normal file
311
templates/page/contact.html.twig
Normal file
@@ -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' %}
|
||||
|
||||
<title>
|
||||
{{ meta_title }}
|
||||
</title>
|
||||
|
||||
<meta name="description" content="{{ meta_description }}" />
|
||||
<meta name="robots" content="index, follow" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta charset="utf-8" />
|
||||
|
||||
<link rel="canonical" href="{{ meta_url }}" />
|
||||
|
||||
{# ============================
|
||||
OPEN GRAPH (Facebook, LinkedIn…)
|
||||
============================ #}
|
||||
|
||||
<meta property="og:title" content="{{ meta_title }}" />
|
||||
<meta property="og:description" content="{{ meta_description }}" />
|
||||
<meta property="og:type" content="{{ meta_type }}" />
|
||||
<meta property="og:url" content="{{ meta_url }}" />
|
||||
<meta property="og:image" content="{{ meta_image }}" />
|
||||
<meta property="og:site_name" content="{{ meta_site_name }}" />
|
||||
<meta property="og:locale" content="fr_FR" />
|
||||
|
||||
{# Optionnel mais recommandé #}
|
||||
<meta property="og:image:width" content="1200" />
|
||||
<meta property="og:image:height" content="630" />
|
||||
|
||||
{# ============================
|
||||
TWITTER CARDS
|
||||
============================ #}
|
||||
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<meta name="twitter:title" content="{{ meta_title }}" />
|
||||
<meta name="twitter:description" content="{{ meta_description }}" />
|
||||
<meta name="twitter:image" content="{{ meta_image }}" />
|
||||
{% endblock %}
|
||||
{% block body %}
|
||||
<div class="isolate bg-white px-6 py-24 sm:py-32 lg:px-8">
|
||||
<div
|
||||
aria-hidden="true"
|
||||
class="absolute inset-x-0 -top-40 -z-10 transform-gpu overflow-hidden blur-3xl sm:-top-80"
|
||||
>
|
||||
<div
|
||||
style="clip-path: polygon(74.1% 44.1%, 100% 61.6%, 97.5% 26.9%, 85.5% 0.1%, 80.7% 2%, 72.5% 32.5%, 60.2% 62.4%, 52.4% 68.1%, 47.5% 58.3%, 45.2% 34.5%, 27.5% 76.7%, 0.1% 64.9%, 17.9% 100%, 27.6% 76.8%, 76.1% 97.7%, 74.1% 44.1%)"
|
||||
class="relative left-1/2 -z-10 aspect-1155/678 w-144.5 max-w-none -translate-x-1/2 rotate-30 bg-linear-to-tr from-[#ff80b5] to-[#F59E0B] opacity-30 sm:left-[calc(50%-40rem)] sm:w-288.75"
|
||||
></div>
|
||||
</div>
|
||||
<div class="mx-auto max-w-2xl text-center">
|
||||
<h2
|
||||
class="text-4xl font-semibold tracking-tight text-balance text-gray-900 sm:text-5xl"
|
||||
>
|
||||
Contactez moi
|
||||
</h2>
|
||||
<p class="mt-2 text-lg/8 text-gray-600">
|
||||
Vous pouvez me contacter via le formulaire ci-dessous et je vous
|
||||
répondrai rapidement.
|
||||
</p>
|
||||
{% for label, messages in app.flashes %}
|
||||
{% for message in messages %}
|
||||
<div
|
||||
class="mb-6 rounded-md p-4 text-sm font-medium {% if label == 'success' %} bg-green-50 text-green-800 {% elseif label == 'error' %} bg-red-50 text-red-800 {% elseif label == 'warning' %} bg-yellow-50 text-yellow-800 {% endif %}"
|
||||
>
|
||||
{{ message }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{{
|
||||
form_start(
|
||||
form,
|
||||
{
|
||||
attr: {
|
||||
class: 'mx-auto mt-16 max-w-xl sm:mt-20'
|
||||
}
|
||||
}
|
||||
)
|
||||
}}
|
||||
<div class="grid grid-cols-1 gap-x-8 gap-y-6 sm:grid-cols-2">
|
||||
<div>
|
||||
<label
|
||||
for="{{ form.lastName.vars.id }}"
|
||||
class="block text-sm/6 font-semibold text-gray-900"
|
||||
>
|
||||
Nom
|
||||
</label>
|
||||
<div class="mt-2.5">
|
||||
{{
|
||||
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'
|
||||
}
|
||||
}
|
||||
)
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label
|
||||
for="{{ form.firstName.vars.id }}"
|
||||
class="block text-sm/6 font-semibold text-gray-900"
|
||||
>
|
||||
Prénom
|
||||
</label>
|
||||
<div class="mt-2.5">
|
||||
{{
|
||||
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'
|
||||
}
|
||||
}
|
||||
)
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sm:col-span-2">
|
||||
<label
|
||||
for="{{ form.company.vars.id }}"
|
||||
class="block text-sm/6 font-semibold text-gray-900"
|
||||
>
|
||||
Entreprise
|
||||
</label>
|
||||
<div class="mt-2.5">
|
||||
{{
|
||||
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'
|
||||
}
|
||||
}
|
||||
)
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sm:col-span-2">
|
||||
<label
|
||||
for="{{ form.email.vars.id }}"
|
||||
class="block text-sm/6 font-semibold text-gray-900"
|
||||
>
|
||||
Email
|
||||
</label>
|
||||
<div class="mt-2.5">
|
||||
{{
|
||||
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'
|
||||
}
|
||||
}
|
||||
)
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sm:col-span-2">
|
||||
<label
|
||||
for="{{ form.phoneNumber.vars.id }}"
|
||||
class="block text-sm/6 font-semibold text-gray-900"
|
||||
>
|
||||
Numéro de téléphone
|
||||
</label>
|
||||
<div class="mt-2.5">
|
||||
{{
|
||||
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'
|
||||
}
|
||||
}
|
||||
)
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sm:col-span-2 hidden">
|
||||
<label
|
||||
for="{{ form.phoneNumber.vars.id }}"
|
||||
class="block text-sm/6 font-semibold text-gray-900"
|
||||
>
|
||||
Numéro de téléphone mobile
|
||||
</label>
|
||||
<div class="mt-2.5">
|
||||
{{
|
||||
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'
|
||||
}
|
||||
}
|
||||
)
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sm:col-span-2">
|
||||
<label
|
||||
for="{{ form.message.vars.id }}"
|
||||
class="block text-sm/6 font-semibold text-gray-900"
|
||||
>
|
||||
Message
|
||||
</label>
|
||||
<div class="mt-2.5">
|
||||
{{
|
||||
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'
|
||||
}
|
||||
}
|
||||
)
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-x-4 sm:col-span-2">
|
||||
<div class="flex h-6 items-center">
|
||||
<div
|
||||
class="group relative inline-flex w-8 shrink-0 rounded-full bg-gray-200 p-px inset-ring inset-ring-gray-900/5 outline-offset-2 outline-amber-600 transition-colors duration-200 ease-in-out has-checked:bg-amber-600 has-focus-visible:outline-2"
|
||||
>
|
||||
<span
|
||||
class="size-4 rounded-full bg-white shadow-xs ring-1 ring-gray-900/5 transition-transform duration-200 ease-in-out group-has-checked:translate-x-3.5"
|
||||
>
|
||||
|
||||
</span>
|
||||
|
||||
{{
|
||||
form_widget(
|
||||
form.agreeToPolicies,
|
||||
{
|
||||
attr: {
|
||||
class: 'absolute inset-0 size-full appearance-none focus:outline-hidden'
|
||||
}
|
||||
}
|
||||
)
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<label
|
||||
for="{{ form.agreeToPolicies.vars.id }}"
|
||||
class="text-sm/6 text-gray-600"
|
||||
>
|
||||
En cochant cette case, vous acceptez notre
|
||||
<a
|
||||
href="{{ path('confidentialitypolicy') }}"
|
||||
class="font-semibold whitespace-nowrap text-amber-600"
|
||||
>
|
||||
politique de confidentialité
|
||||
</a>.
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-10">
|
||||
<button
|
||||
type="submit"
|
||||
class="block w-full rounded-md bg-amber-600 px-3.5 py-2.5 text-center text-sm font-semibold text-white shadow-xs hover:bg-amber-500 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-amber-600"
|
||||
>
|
||||
Envoyer ma demande
|
||||
</button>
|
||||
</div>
|
||||
{{ form_end(form) }}
|
||||
</div>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user