✨ feat(app.scss): Ajoute style pour fond semi-transparent avec flou.
✨ feat(base.twig): Ajoute panier latéral et icônes sur l'en-tête.
This commit is contained in:
@@ -2,10 +2,13 @@
|
||||
|
||||
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\Service\Mailer\Mailer;
|
||||
use App\Service\ResetPassword\Event\ResetPasswordConfirmEvent;
|
||||
use App\Service\ResetPassword\Event\ResetPasswordEvent;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
@@ -22,9 +25,21 @@ use Twig\Environment;
|
||||
class ContactController extends AbstractController
|
||||
{
|
||||
|
||||
#[Route(path: '/contact', name: 'app_contact', options: ['sitemap' => true], methods: ['GET'])]
|
||||
public function index(): Response
|
||||
#[Route(path: '/contact', name: 'app_contact', options: ['sitemap' => true], methods: ['GET','POST'])]
|
||||
public function index(Request $request,Mailer $mailer): Response
|
||||
{
|
||||
return $this->render('home.twig');
|
||||
$dto = new DtoContact();
|
||||
$form = $this->createForm(ContactType::class,$dto);
|
||||
$form->handleRequest($request);
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
$mailer->send('contact@e-cosplay.fr','E-Cosplat',"[E-Cosplay] - Demande de contact","mails/contact.twig", [
|
||||
'dto' => $dto
|
||||
]);
|
||||
$this->addFlash("success","Votre message à été envoyée");
|
||||
return $this->redirectToRoute('app_contact');
|
||||
}
|
||||
return $this->render('contact.twig',[
|
||||
'form' => $form->createView(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
63
src/Dto/Contact/ContactType.php
Normal file
63
src/Dto/Contact/ContactType.php
Normal file
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
namespace App\Dto\Contact;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
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\OptionsResolver\OptionsResolver;
|
||||
|
||||
class ContactType extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$builder
|
||||
->add('name', TextType::class, [
|
||||
'label' => 'Prénom',
|
||||
'attr' => [
|
||||
'placeholder' => 'Votre prénom',
|
||||
],
|
||||
])
|
||||
->add('surname', TextType::class, [
|
||||
'label' => 'Nom de famille',
|
||||
'attr' => [
|
||||
'placeholder' => 'Votre nom de famille',
|
||||
],
|
||||
])
|
||||
->add('email', EmailType::class, [
|
||||
'label' => 'Adresse e-mail',
|
||||
'attr' => [
|
||||
'placeholder' => 'votre.email@exemple.com',
|
||||
],
|
||||
])
|
||||
// NOUVEAU : Ajout du champ 'subject'
|
||||
->add('subject', TextType::class, [
|
||||
'label' => 'Sujet',
|
||||
'attr' => [
|
||||
'placeholder' => 'Objet de votre message',
|
||||
],
|
||||
])
|
||||
->add('tel', TextType::class, [
|
||||
'label' => 'Téléphone (facultatif)',
|
||||
'required' => false,
|
||||
'attr' => [
|
||||
'placeholder' => 'Ex: 06 01 02 03 04',
|
||||
],
|
||||
])
|
||||
->add('message', TextareaType::class, [
|
||||
'label' => 'Votre message',
|
||||
'attr' => [
|
||||
'placeholder' => 'Saisissez votre question ou votre demande...',
|
||||
'rows' => 6,
|
||||
],
|
||||
])
|
||||
;
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$resolver->setDefault('data_class', DtoContact::class);
|
||||
}
|
||||
}
|
||||
154
src/Dto/Contact/DtoContact.php
Normal file
154
src/Dto/Contact/DtoContact.php
Normal file
@@ -0,0 +1,154 @@
|
||||
<?php
|
||||
|
||||
namespace App\Dto\Contact;
|
||||
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
|
||||
/**
|
||||
* DTO (Data Transfer Object) pour le formulaire de contact.
|
||||
* Utilise des propriétés publiques sans type pour la compatibilité avec les anciennes versions de PHP/Symfony.
|
||||
* Les getters et setters sont conservés pour l'approche traditionnelle.
|
||||
*/
|
||||
class DtoContact
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
#[Assert\NotBlank(message: 'Veuillez renseigner votre prénom.')]
|
||||
#[Assert\Length(max: 50, maxMessage: 'Le prénom ne peut dépasser {{ limit }} caractères.')]
|
||||
public $name;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
#[Assert\NotBlank(message: 'Veuillez renseigner votre nom de famille.')]
|
||||
#[Assert\Length(max: 50, maxMessage: 'Le nom de famille ne peut dépasser {{ limit }} caractères.')]
|
||||
public $surname;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
#[Assert\Email(message: 'L\'adresse e-mail n\'est pas valide.')]
|
||||
#[Assert\NotBlank(message: 'Veuillez renseigner votre adresse e-mail.')]
|
||||
public $email;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
#[Assert\NotBlank(message: 'Veuillez renseigner un sujet.')]
|
||||
#[Assert\Length(max: 100, maxMessage: 'Le sujet ne peut dépasser {{ limit }} caractères.')]
|
||||
public $subject;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
#[Assert\NotBlank(message: 'Veuillez saisir votre message.')]
|
||||
#[Assert\Length(min: 5, minMessage: 'Votre message doit contenir au moins {{ limit }} caractères.')]
|
||||
public $message;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
#[Assert\Regex(
|
||||
pattern: '/^[0-9\s\.\-()+]{8,20}$/',
|
||||
message: 'Le numéro de téléphone n\'est pas valide.',
|
||||
htmlPattern: '^[0-9\s\.\-()+]{8,20}$'
|
||||
)]
|
||||
public $tel = null;
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
*/
|
||||
public function setName(string $name): void
|
||||
{
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getSurname(): string
|
||||
{
|
||||
return $this->surname;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $surname
|
||||
*/
|
||||
public function setSurname(string $surname): void
|
||||
{
|
||||
$this->surname = $surname;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getEmail(): string
|
||||
{
|
||||
return $this->email;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $email
|
||||
*/
|
||||
public function setEmail(string $email): void
|
||||
{
|
||||
$this->email = $email;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getSubject(): string
|
||||
{
|
||||
return $this->subject;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $subject
|
||||
*/
|
||||
public function setSubject(string $subject): void
|
||||
{
|
||||
$this->subject = $subject;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getMessage(): string
|
||||
{
|
||||
return $this->message;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $message
|
||||
*/
|
||||
public function setMessage(string $message): void
|
||||
{
|
||||
$this->message = $message;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function getTel(): ?string
|
||||
{
|
||||
return $this->tel;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $tel
|
||||
*/
|
||||
public function setTel(?string $tel): void
|
||||
{
|
||||
$this->tel = $tel;
|
||||
}
|
||||
}
|
||||
@@ -49,43 +49,6 @@ class Mailer
|
||||
}
|
||||
}
|
||||
|
||||
public function sendTest(): void
|
||||
{
|
||||
$dest = new Address("jovann@siteconseil.fr", "Test Dev");
|
||||
$src = new Address("mainframe@esy-web.dev", "Mainframe EsyWeb");
|
||||
|
||||
$mail = (new Email())
|
||||
->subject("Test de configuration")
|
||||
->to($dest)
|
||||
->from($src);
|
||||
|
||||
$messageId = $mail->generateMessageId();
|
||||
$mail->getHeaders()->add(new IdentificationHeader("Message-Id", $messageId));
|
||||
$datas = $this->generateTracking($mail);
|
||||
/** @var Mail $object */
|
||||
$object = $datas['object'];
|
||||
|
||||
$mjmlGenerator = $this->environment->render('mails/test.twig', [
|
||||
'system' => [
|
||||
'subject' => 'Test de configuration',
|
||||
'tracking_url' => $datas['url'],
|
||||
],
|
||||
]);
|
||||
$htmlContent = $this->convertMjmlToHtml($mjmlGenerator);
|
||||
$object->setContent($htmlContent);
|
||||
|
||||
$mail->html($htmlContent);
|
||||
|
||||
try {
|
||||
$this->mailer->send($mail);
|
||||
$object->setStatus("sent");
|
||||
} catch (TransportExceptionInterface $exception) {
|
||||
$object->setStatus("error - ".$exception->getMessage());
|
||||
}
|
||||
|
||||
$this->entityManager->persist($object);
|
||||
$this->entityManager->flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $address
|
||||
@@ -93,7 +56,7 @@ class Mailer
|
||||
* @param string $subject
|
||||
* @param string $template
|
||||
* @param array<string, mixed> $data
|
||||
* @param array<\Symfony\Component\Mime\Part\Part> $files
|
||||
* @param array<\Symfony\Component\Mime\Part\DataPart> $files
|
||||
*/
|
||||
public function send(
|
||||
string $address,
|
||||
@@ -104,115 +67,27 @@ class Mailer
|
||||
array $files = []
|
||||
): void {
|
||||
$dest = new Address($address, $addressName);
|
||||
$src = new Address("mainframe@esy-web.dev", "Mainframe EsyWeb");
|
||||
$src = new Address("contact@e-cosplay", "E-Cosplay");
|
||||
|
||||
$mail = (new Email())
|
||||
->subject($subject)
|
||||
->to($dest)
|
||||
->from($src);
|
||||
|
||||
$messageId = $mail->generateMessageId();
|
||||
$mail->getHeaders()->add(new IdentificationHeader("Message-Id", $messageId));
|
||||
$datasSign = $this->generateTracking($mail);
|
||||
/** @var Mail $object */
|
||||
$object = $datasSign['object'];
|
||||
|
||||
$mjmlGenerator = $this->environment->render($template, [
|
||||
'system' => [
|
||||
'subject' => $subject,
|
||||
'tracking_url' => $datasSign['url'],
|
||||
],
|
||||
'datas' => $data,
|
||||
]);
|
||||
|
||||
$htmlContent = $this->convertMjmlToHtml($mjmlGenerator);
|
||||
$object->setContent($htmlContent);
|
||||
|
||||
foreach ($files as $file) {
|
||||
$mail->addPart($file);
|
||||
}
|
||||
|
||||
$mail->html($htmlContent);
|
||||
|
||||
try {
|
||||
$this->mailer->send($mail);
|
||||
$object->setStatus("sent");
|
||||
} catch (TransportExceptionInterface $exception) {
|
||||
$object->setStatus("error - ".$exception->getMessage());
|
||||
}
|
||||
|
||||
$this->entityManager->persist($object);
|
||||
$this->entityManager->flush();
|
||||
$this->mailer->send($mail);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Email $email
|
||||
* @return array{object: Mail, url: string}
|
||||
*/
|
||||
private function generateTracking(Email $email): array
|
||||
{
|
||||
$messageIdHeader = $email->getHeaders()->get('message-id');
|
||||
$messageFormat = $messageIdHeader ? $messageIdHeader->getBody()[0] : '';
|
||||
$messageFormat = str_replace("@esy-web.dev", "", $messageFormat);
|
||||
|
||||
$mailData = new Mail();
|
||||
$mailData->setDest($email->getTo()[0]->getAddress());
|
||||
$mailData->setSubject($email->getSubject());
|
||||
$mailData->setMessageId($messageFormat);
|
||||
$mailData->setStatus("draft");
|
||||
|
||||
return [
|
||||
'object' => $mailData,
|
||||
'url' => "https://mainframe.esy-web.dev" . $this->urlGenerator->generate('app_tracking', ['slug' => $messageFormat]),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $addressList
|
||||
* @param string $subject
|
||||
* @param string $template
|
||||
* @param array<string, mixed> $data
|
||||
*/
|
||||
public function sendMulti(array $addressList, string $subject, string $template, array $data): void
|
||||
{
|
||||
$src = new Address("mainframe@esy-web.dev", "Mainframe EsyWeb");
|
||||
$mail = (new Email())->subject($subject);
|
||||
|
||||
foreach ($addressList as $address) {
|
||||
$dest = new Address($address);
|
||||
$mail->addTo($dest);
|
||||
}
|
||||
|
||||
$mail->from($src);
|
||||
|
||||
$messageId = $mail->generateMessageId();
|
||||
$mail->getHeaders()->add(new IdentificationHeader("Message-Id", $messageId));
|
||||
|
||||
$datasSign = $this->generateTracking($mail);
|
||||
/** @var Mail $object */
|
||||
$object = $datasSign['object'];
|
||||
|
||||
$mjmlGenerator = $this->environment->render($template, [
|
||||
'system' => [
|
||||
'subject' => $subject,
|
||||
'tracking_url' => $datasSign['url'],
|
||||
],
|
||||
'datas' => $data,
|
||||
]);
|
||||
|
||||
$htmlContent = $this->convertMjmlToHtml($mjmlGenerator);
|
||||
$object->setContent($htmlContent);
|
||||
|
||||
$mail->html($htmlContent);
|
||||
|
||||
try {
|
||||
$this->mailer->send($mail);
|
||||
$object->setStatus("sent");
|
||||
} catch (TransportExceptionInterface) {
|
||||
$object->setStatus("error");
|
||||
}
|
||||
|
||||
$this->entityManager->persist($object);
|
||||
$this->entityManager->flush();
|
||||
}
|
||||
}
|
||||
|
||||
146
templates/contact.twig
Normal file
146
templates/contact.twig
Normal file
@@ -0,0 +1,146 @@
|
||||
{% extends 'base.twig' %}
|
||||
|
||||
{% block title %}Contactez Nous{% endblock %}
|
||||
|
||||
{% block canonical_url %}<link rel="canonical" href="{{ url('app_contact') }}" />{% endblock %}
|
||||
{% block breadcrumb_schema %}
|
||||
<script type="application/ld+json">
|
||||
{
|
||||
"@context": "https://schema.org",
|
||||
"@type": "BreadcrumbList",
|
||||
"itemListElement": [
|
||||
{
|
||||
"@type": "ListItem",
|
||||
"position": 1,
|
||||
"name": "Accueil",
|
||||
"item": "{{ app.request.schemeAndHttpHost }}"
|
||||
},
|
||||
{
|
||||
"@type": "ListItem",
|
||||
"position": 2,
|
||||
"name": "Contactez Nous",
|
||||
"item": "{{ app.request.schemeAndHttpHost }}{{ app.request.pathInfo }}"
|
||||
}
|
||||
]
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<div class="container mx-auto p-4 md:p-8 pt-12">
|
||||
|
||||
{# --- FLASH MESSAGE BLOCK --- #}
|
||||
{% for message in app.flashes('success') %}
|
||||
<div class="max-w-4xl mx-auto mb-8 p-4 rounded-lg bg-green-100 border border-green-300 text-green-800 flex items-start shadow-md" role="alert">
|
||||
<svg class="w-6 h-6 mr-3 mt-0.5 text-green-600 flex-shrink-0" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"></path>
|
||||
</svg>
|
||||
<div>
|
||||
<strong class="font-semibold">Message envoyé !</strong>
|
||||
<p class="text-sm">{{ message }}</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% for message in app.flashes('error') %}
|
||||
<div class="max-w-4xl mx-auto mb-8 p-4 rounded-lg bg-green-100 border border-green-300 text-green-800 flex items-start shadow-md" role="alert">
|
||||
<svg class="w-6 h-6 mr-3 mt-0.5 text-green-600 flex-shrink-0" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"></path>
|
||||
</svg>
|
||||
<div>
|
||||
<strong class="font-semibold">Message Erreur !</strong>
|
||||
<p class="text-sm">{{ message }}</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
<h1 class="text-4xl font-extrabold text-center mb-10 text-gray-800">
|
||||
Contactez Nous
|
||||
</h1>
|
||||
|
||||
<div class="max-w-4xl mx-auto grid grid-cols-1 lg:grid-cols-3 gap-10">
|
||||
|
||||
{# Colonne 1 : Informations de Contact (1/3 sur grand écran) #}
|
||||
<div class="lg:col-span-1 bg-white p-6 rounded-xl shadow-lg border border-gray-100 h-fit">
|
||||
<h2 class="text-2xl font-bold mb-4 text-indigo-700">Restons Connectés</h2>
|
||||
<p class="mb-6 text-gray-600 border-b pb-4">
|
||||
Que ce soit pour une question technique, une opportunité de partenariat ou une simple salutation, nous sommes là pour vous.
|
||||
</p>
|
||||
|
||||
{# Coordonnées #}
|
||||
<div class="space-y-6">
|
||||
|
||||
<div>
|
||||
<h3 class="font-semibold text-gray-800 flex items-center mb-1">
|
||||
<svg class="w-5 h-5 text-indigo-500 mr-2" 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="M3 8l7.89 5.26a2 2 0 002.22 0L21 8m-1 10a2 2 0 01-2 2H6a2 2 0 01-2-2V8a2 2 0 012-2h12a2 2 0 012 2v10z"></path></svg>
|
||||
E-mail
|
||||
</h3>
|
||||
{# Nouvelle adresse email #}
|
||||
<p class="text-indigo-600 hover:text-indigo-800 transition duration-150">
|
||||
<a href="mailto:contact@e-cosplay.fr">contact@e-cosplay.fr</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 class="font-semibold text-gray-800 flex items-center mb-1">
|
||||
<svg class="w-5 h-5 text-indigo-500 mr-2" 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="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.828 0l-4.243-4.243m12.44-1.296a8 8 0 11-14.04-4.832"></path></svg>
|
||||
Adresse Postale
|
||||
</h3>
|
||||
{# Nouvelle adresse postale #}
|
||||
<p class="text-gray-600">
|
||||
42 rue de Saint-Quentin<br>
|
||||
02800 Beautor, FRANCE
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{# Bloc pour l'adhésion #}
|
||||
<div class="pt-4 border-t mt-6 border-gray-200">
|
||||
<h3 class="text-xl font-bold mb-3 text-green-700 flex items-center">
|
||||
<svg class="w-6 h-6 text-green-500 mr-2" 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="M18 9v3m0 0v3m0-3h3m-3 0h-3m-2-5a4 4 0 11-8 0 4 4 0 018 0zM12 18H5a2 2 0 01-2-2v-2c0-.55.22-1.07.61-1.45l2-2.02a1 1 0 011.41 0l2.5 2.5a1 1 0 001.41 0l2-2.02a1 1 0 011.41 0l2 2.02c.39.38.61.9.61 1.45v2a2 2 0 01-2 2z"></path></svg>
|
||||
Rejoindre l'association
|
||||
</h3>
|
||||
<p class="text-gray-600">
|
||||
Vous souhaitez nous rejoindre ou obtenir plus d'informations sur les modalités d'adhésion ? Veuillez nous écrire directement à :
|
||||
</p>
|
||||
<p class="mt-2 font-bold text-indigo-600">
|
||||
<a href="mailto:contact@e-cosplay.fr">contact@e-cosplay.fr</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{# Colonne 2 : Formulaire de Contact (2/3 sur grand écran) #}
|
||||
<div class="lg:col-span-2 bg-white p-8 rounded-xl shadow-lg border border-gray-100">
|
||||
<h2 class="text-2xl font-bold mb-6 text-gray-800">Envoyez-nous un Message</h2>
|
||||
|
||||
{{ form_start(form, {'attr': {'class': 'space-y-5'}}) }}
|
||||
|
||||
{# Ligne 1 : Nom et Prénom #}
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
{{ form_row(form.name) }}
|
||||
{{ form_row(form.surname) }}
|
||||
</div>
|
||||
|
||||
{# Ligne 2 : Email et Sujet #}
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
{{ form_row(form.email, {'label': 'Votre adresse e-mail'}) }}
|
||||
{{ form_row(form.subject) }}
|
||||
</div>
|
||||
|
||||
{# Ligne 3 : Téléphone (optionnel) #}
|
||||
{{ form_row(form.tel) }}
|
||||
|
||||
{# Ligne 4 : Message #}
|
||||
{{ form_row(form.message) }}
|
||||
|
||||
<div class="pt-4">
|
||||
<button type="submit" class="w-full py-3 px-4 bg-indigo-600 hover:bg-indigo-700 text-white font-semibold rounded-lg transition duration-150 ease-in-out shadow-lg transform hover:scale-[1.01]">
|
||||
<svg class="w-5 h-5 inline-block mr-2" 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="M3 8l7.89 5.26a2 2 0 002.22 0L21 8m-1 10a2 2 0 01-2 2H6a2 2 0 01-2-2V8a2 2 0 012-2h12a2 2 0 012 2v10z"></path></svg>
|
||||
Envoyer le message
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{{ form_end(form) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -16,8 +16,10 @@
|
||||
{{ form_widget(form) }}
|
||||
</div>
|
||||
{% if not compound and not form.vars.valid %}
|
||||
<p class="text-sm text-red-500 mt-1">{{ form_errors(form) }}</p>
|
||||
{# Affiche l'erreur en bas du champ simple #}
|
||||
<p class="text-sm text-red-600 mt-1">{{ form_errors(form) }}</p>
|
||||
{% else %}
|
||||
{# Affiche l'erreur pour les champs composés (si form_errors n'est pas déjà dans le widget) #}
|
||||
{{ form_errors(form) }}
|
||||
{% endif %}
|
||||
</div>
|
||||
@@ -26,10 +28,10 @@
|
||||
{# ---------- LABEL ---------- #}
|
||||
{% block form_label %}
|
||||
{% if label is not same as(false) %}
|
||||
<label for="{{ id }}" class="block text-sm font-medium text-gray-200 dark:text-gray-300">
|
||||
<label for="{{ id }}" class="block text-sm font-medium text-gray-700">
|
||||
{{ label|trans({}, translation_domain) }}
|
||||
{% if required %}
|
||||
<span class="text-red-400">*</span>
|
||||
<span class="text-red-500">*</span>
|
||||
{% endif %}
|
||||
</label>
|
||||
{% endif %}
|
||||
@@ -38,7 +40,7 @@
|
||||
{# ---------- ERRORS ---------- #}
|
||||
{% block form_errors %}
|
||||
{% if errors|length > 0 %}
|
||||
<ul class="mt-1 text-sm text-red-500">
|
||||
<ul class="mt-1 text-sm text-red-600 list-disc list-inside">
|
||||
{% for error in errors %}
|
||||
<li>{{ error.message }}</li>
|
||||
{% endfor %}
|
||||
@@ -55,6 +57,8 @@
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{# --- STYLE COMMUN POUR WIDGETS (Light Mode) --- #}
|
||||
|
||||
{# ---------- SIMPLE INPUTS (text, email, number...) ---------- #}
|
||||
{% block form_widget_simple %}
|
||||
{% set type = type|default('text') %}
|
||||
@@ -62,7 +66,7 @@
|
||||
type="{{ type }}"
|
||||
{{ block('widget_attributes') }}
|
||||
value="{{ value }}"
|
||||
class="form-input mt-1 block w-full px-3 py-2 bg-gray-800 border border-gray-700 text-white rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm"
|
||||
class="form-input mt-1 block w-full px-3 py-2 bg-white border border-gray-300 text-gray-900 placeholder-gray-400 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm transition duration-150"
|
||||
/>
|
||||
{% endblock %}
|
||||
|
||||
@@ -70,7 +74,7 @@
|
||||
{% block textarea_widget %}
|
||||
<textarea
|
||||
{{ block('widget_attributes') }}
|
||||
class="form-textarea mt-1 block w-full px-3 py-2 bg-gray-800 border border-gray-700 text-white rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm"
|
||||
class="form-textarea form-input mt-1 block w-full px-3 py-2 bg-white border border-gray-300 text-gray-900 placeholder-gray-400 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm transition duration-150"
|
||||
>{{ value }}</textarea>
|
||||
{% endblock %}
|
||||
|
||||
@@ -78,7 +82,7 @@
|
||||
{% block choice_widget_collapsed %}
|
||||
<select
|
||||
{{ block('widget_attributes') }}
|
||||
class="form-select mt-1 block w-full px-3 py-2 bg-gray-800 border border-gray-700 text-white rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm"
|
||||
class="form-select form-input mt-1 block w-full px-3 py-2 bg-white border border-gray-300 text-gray-900 placeholder-gray-400 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm transition duration-150"
|
||||
>
|
||||
{% if placeholder is not none %}
|
||||
<option value="" {% if required and value is empty %}selected{% endif %}>
|
||||
@@ -105,12 +109,12 @@
|
||||
|
||||
{# ---------- CHECKBOX ---------- #}
|
||||
{% block checkbox_widget %}
|
||||
<div class="flex items-center space-x-2">
|
||||
<div class="flex items-center">
|
||||
<input type="checkbox"
|
||||
{{ block('widget_attributes') }}
|
||||
{% if value not in ['', null] %} value="{{ value }}"{% endif %}
|
||||
{% if checked %}checked="checked"{% endif %}
|
||||
class="form-checkbox h-5 w-5 text-indigo-500 bg-gray-800 border-gray-700 rounded focus:ring-indigo-500">
|
||||
class="form-checkbox h-5 w-5 text-indigo-600 border-gray-300 rounded focus:ring-indigo-500">
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
@@ -120,17 +124,17 @@
|
||||
{{ block('widget_attributes') }}
|
||||
value="{{ value }}"
|
||||
{% if checked %}checked="checked"{% endif %}
|
||||
class="form-radio h-5 w-5 text-indigo-500 bg-gray-800 border-gray-700 focus:ring-indigo-500">
|
||||
class="form-radio h-5 w-5 text-indigo-600 border-gray-300 focus:ring-indigo-500">
|
||||
{% endblock %}
|
||||
|
||||
{# ---------- FILE ---------- #}
|
||||
{% block file_widget %}
|
||||
<input type="file"
|
||||
{{ block('widget_attributes') }}
|
||||
class="block w-full text-sm text-gray-300 file:mr-4 file:py-2 file:px-4
|
||||
class="block w-full text-sm text-gray-800 file:mr-4 file:py-2 file:px-4
|
||||
file:rounded-md file:border-0
|
||||
file:text-sm file:font-semibold
|
||||
file:bg-indigo-600 file:text-white
|
||||
hover:file:bg-indigo-700
|
||||
bg-gray-800 border border-gray-700 rounded-md">
|
||||
bg-white border border-gray-300 rounded-md shadow-sm">
|
||||
{% endblock %}
|
||||
|
||||
66
templates/mails/base.twig
Normal file
66
templates/mails/base.twig
Normal file
@@ -0,0 +1,66 @@
|
||||
{# base.twig - Modèle d'e-mail MJML #}
|
||||
<mjml>
|
||||
<mj-head>
|
||||
<mj-title>{{ system.subject }}</mj-title>
|
||||
<mj-attributes>
|
||||
<mj-all font-family="Inter, Helvetica, Arial, sans-serif"></mj-all>
|
||||
<mj-text font-size="16px" line-height="24px" color="#333333"></mj-text>
|
||||
<mj-button background-color="#4A90E2" color="#ffffff" border-radius="4px" font-size="16px" padding="10px 25px"></mj-button>
|
||||
</mj-attributes>
|
||||
<mj-style inline="inline">
|
||||
.link-style {
|
||||
color: #4A90E2;
|
||||
text-decoration: none;
|
||||
}
|
||||
.footer-text {
|
||||
font-size: 12px;
|
||||
color: #888888;
|
||||
}
|
||||
</mj-style>
|
||||
</mj-head>
|
||||
<mj-body background-color="#F2F2F2">
|
||||
{# Section d'en-tête #}
|
||||
<mj-section background-color="#ffffff" padding-bottom="0px">
|
||||
<mj-column>
|
||||
{# Logo mis à jour pour SARL SITECONSEIL #}
|
||||
<mj-image src="https://mainframe.esy-web.dev/assets/logo_siteconseil.png" alt="Logo SARL SITECONSEIL" align="center" width="150px" padding-bottom="20px"></mj-image>
|
||||
</mj-column>
|
||||
</mj-section>
|
||||
|
||||
{# Section de contenu #}
|
||||
<mj-section background-color="#ffffff" padding-top="0px" padding-bottom="0px">
|
||||
{# Titre dynamique ajouté avant le bloc de contenu, directement dans la section #}
|
||||
<mj-text font-size="20px" font-weight="bold" align="center" padding-bottom="20px">{{ system.subject }}</mj-text>
|
||||
<mj-column width="100%">
|
||||
{% block content %}
|
||||
{% endblock %}
|
||||
</mj-column>
|
||||
</mj-section>
|
||||
|
||||
{# Section d'espacement #}
|
||||
<mj-section background-color="#ffffff" padding-top="0px" padding-bottom="20px">
|
||||
<mj-column>
|
||||
<mj-spacer height="20px"></mj-spacer>
|
||||
</mj-column>
|
||||
</mj-section>
|
||||
|
||||
{# Section de pied de page #}
|
||||
<mj-section background-color="#F2F2F2" padding-top="20px" padding-bottom="20px">
|
||||
<mj-column>
|
||||
<mj-text align="center" css-class="footer-text">
|
||||
© {{ "now"|date("Y") }} SARL SITECONSEIL. Tous droits réservés.
|
||||
</mj-text>
|
||||
{# Adresse et contact de l'entreprise mis à jour pour SARL SITECONSEIL #}
|
||||
<mj-text align="center" css-class="footer-text" padding-top="10px">
|
||||
SARL SITECONSEIL, 27 Rue Le Serurier, 02100 Saint-Quentin, France
|
||||
<br/>
|
||||
Téléphone : 03 23 62 73 60
|
||||
</mj-text>
|
||||
<mj-social font-size="15px" icon-size="24px" mode="horizontal" padding-top="10px">
|
||||
<mj-social-element name="facebook" href="#" background-color="#3B5998"></mj-social-element>
|
||||
<mj-social-element name="linkedin" href="#" background-color="#0077B5"></mj-social-element>
|
||||
</mj-social>
|
||||
</mj-column>
|
||||
</mj-section>
|
||||
</mj-body>
|
||||
</mjml>
|
||||
58
templates/mails/contact.twig
Normal file
58
templates/mails/contact.twig
Normal file
@@ -0,0 +1,58 @@
|
||||
{% extends 'mails/base.twig' %}
|
||||
|
||||
{% block content %}
|
||||
<mj-section background-color="#ffffff" padding="20px 0">
|
||||
<mj-column>
|
||||
<mj-text font-size="24px" font-weight="700" color="#1E3A8A" align="center" padding-bottom="10px">
|
||||
Nouveau Message de Contact
|
||||
</mj-text>
|
||||
<mj-text font-size="16px" color="#4B5563" align="center" padding-bottom="30px">
|
||||
Vous avez reçu un nouveau message via le formulaire de contact du site.
|
||||
</mj-text>
|
||||
</mj-column>
|
||||
</mj-section>
|
||||
|
||||
{# Section des informations personnelles (Tout sur une seule ligne) #}
|
||||
<mj-section background-color="#F3F4F6" padding="20px 20px" border-radius="8px">
|
||||
<mj-column width="100%">
|
||||
<mj-text font-size="18px" font-weight="600" color="#10B981" padding-bottom="15px">
|
||||
Détails de l'Expéditeur
|
||||
</mj-text>
|
||||
|
||||
{# Bloc de texte ultra-condensé #}
|
||||
<mj-text font-size="14px" color="#1F2937" line-height="24px">
|
||||
<strong style="font-weight: 500; color: #374151;">Expéditeur:</strong> {{ datas.dto.surname }} {{ datas.dto.name }} |
|
||||
<strong style="font-weight: 500; color: #374151;">E-mail:</strong> <a href="mailto:{{ datas.dto.email }}" style="color:#4C51BF; text-decoration:none;">{{ datas.dto.email }}</a>
|
||||
{% if datas.dto.tel is not empty %} |
|
||||
<strong style="font-weight: 500; color: #374151;">Tél:</strong> <a href="tel:{{ datas.dto.tel }}" style="color:#4C51BF; text-decoration:none;">{{ datas.dto.tel }}</a>
|
||||
{% endif %}
|
||||
</mj-text>
|
||||
|
||||
</mj-column>
|
||||
</mj-section>
|
||||
|
||||
{# Section du message #}
|
||||
<mj-section background-color="#ffffff" padding="30px 20px 20px 20px">
|
||||
<mj-column width="100%">
|
||||
{# Objet #}
|
||||
<mj-text font-size="18px" font-weight="600" color="#1E3A8A" padding-bottom="10px" border-bottom="1px solid #D1D5DB">
|
||||
Objet: {{ datas.dto.subject }}
|
||||
</mj-text>
|
||||
|
||||
{# En-tête du contenu #}
|
||||
<mj-text font-size="16px" font-weight="600" color="#1F2937" padding-top="20px" padding-bottom="10px">
|
||||
Contenu du Message:
|
||||
</mj-text>
|
||||
|
||||
{# Contenu du message (avec gestion des sauts de ligne) #}
|
||||
<mj-text font-size="14px" color="#374151" line-height="24px" background-color="#F9FAFB" padding="15px" border-radius="4px">
|
||||
{{ datas.dto.message | raw | nl2br }}
|
||||
</mj-text>
|
||||
|
||||
{# Bouton d'action pour répondre #}
|
||||
<mj-button href="mailto:{{ datas.dto.email }}" background-color="#4C51BF" color="#ffffff" font-size="14px" padding-top="30px" border-radius="6px">
|
||||
Répondre à l'Expéditeur
|
||||
</mj-button>
|
||||
</mj-column>
|
||||
</mj-section>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user