🚑️ Ajout du fonctionnement du formulaire de contact en production, utilisation de PHPMailer.
This commit is contained in:
@@ -1,29 +1,26 @@
|
|||||||
<?php
|
<?php
|
||||||
/**
|
/**
|
||||||
* Endpoint de traitement du formulaire de contact
|
* Endpoint de traitement du formulaire de contact
|
||||||
* Reçoit les données en JSON, valide, vérifie reCAPTCHA, envoie l'email
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
require_once __DIR__ . '/../includes/config.php';
|
require_once __DIR__ . '/../includes/config.php';
|
||||||
require_once __DIR__ . '/../includes/functions.php';
|
require_once __DIR__ . '/../includes/functions.php';
|
||||||
|
|
||||||
// Démarrer la session pour le CSRF
|
ini_set('display_errors', 1);
|
||||||
|
|
||||||
if (session_status() === PHP_SESSION_NONE) {
|
if (session_status() === PHP_SESSION_NONE) {
|
||||||
session_start();
|
session_start();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Headers
|
//header('Content-Type: application/json; charset=utf-8');
|
||||||
header('Content-Type: application/json; charset=utf-8');
|
|
||||||
header('X-Content-Type-Options: nosniff');
|
header('X-Content-Type-Options: nosniff');
|
||||||
|
|
||||||
// Uniquement POST
|
|
||||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||||
http_response_code(405);
|
http_response_code(405);
|
||||||
echo json_encode(['success' => false, 'error' => 'Méthode non autorisée']);
|
echo json_encode(['success' => false, 'error' => 'Méthode non autorisée']);
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Récupérer les données JSON
|
|
||||||
$input = json_decode(file_get_contents('php://input'), true);
|
$input = json_decode(file_get_contents('php://input'), true);
|
||||||
|
|
||||||
if (!$input) {
|
if (!$input) {
|
||||||
@@ -33,29 +30,23 @@ if (!$input) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 1. Valider le token CSRF
|
|
||||||
if (!verifyCsrfToken($input['csrf_token'] ?? '')) {
|
if (!verifyCsrfToken($input['csrf_token'] ?? '')) {
|
||||||
throw new Exception('Token de sécurité invalide. Veuillez rafraîchir la page.');
|
throw new Exception('Token de sécurité invalide. Veuillez rafraîchir la page.');
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Vérifier reCAPTCHA
|
|
||||||
$recaptchaScore = verifyRecaptcha($input['recaptcha_token'] ?? '');
|
$recaptchaScore = verifyRecaptcha($input['recaptcha_token'] ?? '');
|
||||||
if ($recaptchaScore < RECAPTCHA_THRESHOLD) {
|
if ($recaptchaScore < RECAPTCHA_THRESHOLD) {
|
||||||
error_log("reCAPTCHA score trop bas: {$recaptchaScore}");
|
error_log("reCAPTCHA score trop bas: {$recaptchaScore}");
|
||||||
throw new Exception('Vérification anti-spam échouée. Veuillez réessayer.');
|
throw new Exception('Vérification anti-spam échouée. Veuillez réessayer.');
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Valider et nettoyer les données
|
|
||||||
$data = validateContactData($input);
|
$data = validateContactData($input);
|
||||||
|
|
||||||
// 4. Envoyer l'email
|
|
||||||
$sent = sendContactEmail($data);
|
$sent = sendContactEmail($data);
|
||||||
|
|
||||||
if (!$sent) {
|
if (!$sent) {
|
||||||
throw new Exception('Erreur lors de l\'envoi du message. Veuillez réessayer plus tard.');
|
throw new Exception('Erreur lors de l\'envoi du message. Veuillez réessayer plus tard.');
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. Succès
|
|
||||||
echo json_encode([
|
echo json_encode([
|
||||||
'success' => true,
|
'success' => true,
|
||||||
'message' => 'Votre message a bien été envoyé ! Je vous répondrai dans les meilleurs délais.'
|
'message' => 'Votre message a bien été envoyé ! Je vous répondrai dans les meilleurs délais.'
|
||||||
|
|||||||
70
api/contact.php.old
Normal file
70
api/contact.php.old
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Endpoint de traitement du formulaire de contact
|
||||||
|
* Reçoit les données en JSON, valide, vérifie reCAPTCHA, envoie l'email
|
||||||
|
*/
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../includes/config.php';
|
||||||
|
require_once __DIR__ . '/../includes/functions.php';
|
||||||
|
|
||||||
|
// Démarrer la session pour le CSRF
|
||||||
|
if (session_status() === PHP_SESSION_NONE) {
|
||||||
|
session_start();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Headers
|
||||||
|
header('Content-Type: application/json; charset=utf-8');
|
||||||
|
header('X-Content-Type-Options: nosniff');
|
||||||
|
|
||||||
|
// Uniquement POST
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||||
|
http_response_code(405);
|
||||||
|
echo json_encode(['success' => false, 'error' => 'Méthode non autorisée']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Récupérer les données JSON
|
||||||
|
$input = json_decode(file_get_contents('php://input'), true);
|
||||||
|
|
||||||
|
if (!$input) {
|
||||||
|
http_response_code(400);
|
||||||
|
echo json_encode(['success' => false, 'error' => 'Données invalides']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 1. Valider le token CSRF
|
||||||
|
if (!verifyCsrfToken($input['csrf_token'] ?? '')) {
|
||||||
|
throw new Exception('Token de sécurité invalide. Veuillez rafraîchir la page.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Vérifier reCAPTCHA
|
||||||
|
$recaptchaScore = verifyRecaptcha($input['recaptcha_token'] ?? '');
|
||||||
|
if ($recaptchaScore < RECAPTCHA_THRESHOLD) {
|
||||||
|
error_log("reCAPTCHA score trop bas: {$recaptchaScore}");
|
||||||
|
throw new Exception('Vérification anti-spam échouée. Veuillez réessayer.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Valider et nettoyer les données
|
||||||
|
$data = validateContactData($input);
|
||||||
|
|
||||||
|
// 4. Envoyer l'email
|
||||||
|
$sent = sendContactEmail($data);
|
||||||
|
|
||||||
|
if (!$sent) {
|
||||||
|
throw new Exception('Erreur lors de l\'envoi du message. Veuillez réessayer plus tard.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. Succès
|
||||||
|
echo json_encode([
|
||||||
|
'success' => true,
|
||||||
|
'message' => 'Votre message a bien été envoyé ! Je vous répondrai dans les meilleurs délais.'
|
||||||
|
]);
|
||||||
|
|
||||||
|
} catch (Exception $e) {
|
||||||
|
http_response_code(400);
|
||||||
|
echo json_encode([
|
||||||
|
'success' => false,
|
||||||
|
'error' => $e->getMessage()
|
||||||
|
]);
|
||||||
|
}
|
||||||
@@ -99,7 +99,7 @@
|
|||||||
.input {
|
.input {
|
||||||
@apply w-full px-4 py-3
|
@apply w-full px-4 py-3
|
||||||
bg-surface border border-border rounded-lg
|
bg-surface border border-border rounded-lg
|
||||||
text-text-primary placeholder-text-muted
|
text-text-muted placeholder-text-muted
|
||||||
transition-all duration-150
|
transition-all duration-150
|
||||||
focus:outline-none focus:border-primary focus:shadow-input-focus;
|
focus:outline-none focus:border-primary focus:shadow-input-focus;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
"type": "project",
|
"type": "project",
|
||||||
"require": {
|
"require": {
|
||||||
"php": ">=8.0",
|
"php": ">=8.0",
|
||||||
"vlucas/phpdotenv": "^5.6"
|
"vlucas/phpdotenv": "^5.6",
|
||||||
|
"phpmailer/phpmailer": "^7.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
84
composer.lock
generated
84
composer.lock
generated
@@ -4,7 +4,7 @@
|
|||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "adbddd7a48b14ed78896b2d6c5ef28e9",
|
"content-hash": "ef9466a44690e608fe2d148c314ef38c",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "graham-campbell/result-type",
|
"name": "graham-campbell/result-type",
|
||||||
@@ -68,6 +68,88 @@
|
|||||||
],
|
],
|
||||||
"time": "2025-12-27T19:43:20+00:00"
|
"time": "2025-12-27T19:43:20+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "phpmailer/phpmailer",
|
||||||
|
"version": "v7.0.2",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/PHPMailer/PHPMailer.git",
|
||||||
|
"reference": "ebf1655bd5b99b3f97e1a3ec0a69e5f4cd7ea088"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/ebf1655bd5b99b3f97e1a3ec0a69e5f4cd7ea088",
|
||||||
|
"reference": "ebf1655bd5b99b3f97e1a3ec0a69e5f4cd7ea088",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"ext-ctype": "*",
|
||||||
|
"ext-filter": "*",
|
||||||
|
"ext-hash": "*",
|
||||||
|
"php": ">=5.5.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"dealerdirect/phpcodesniffer-composer-installer": "^1.0",
|
||||||
|
"doctrine/annotations": "^1.2.6 || ^1.13.3",
|
||||||
|
"php-parallel-lint/php-console-highlighter": "^1.0.0",
|
||||||
|
"php-parallel-lint/php-parallel-lint": "^1.3.2",
|
||||||
|
"phpcompatibility/php-compatibility": "^10.0.0@dev",
|
||||||
|
"squizlabs/php_codesniffer": "^3.13.5",
|
||||||
|
"yoast/phpunit-polyfills": "^1.0.4"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"decomplexity/SendOauth2": "Adapter for using XOAUTH2 authentication",
|
||||||
|
"directorytree/imapengine": "For uploading sent messages via IMAP, see gmail example",
|
||||||
|
"ext-imap": "Needed to support advanced email address parsing according to RFC822",
|
||||||
|
"ext-mbstring": "Needed to send email in multibyte encoding charset or decode encoded addresses",
|
||||||
|
"ext-openssl": "Needed for secure SMTP sending and DKIM signing",
|
||||||
|
"greew/oauth2-azure-provider": "Needed for Microsoft Azure XOAUTH2 authentication",
|
||||||
|
"hayageek/oauth2-yahoo": "Needed for Yahoo XOAUTH2 authentication",
|
||||||
|
"league/oauth2-google": "Needed for Google XOAUTH2 authentication",
|
||||||
|
"psr/log": "For optional PSR-3 debug logging",
|
||||||
|
"symfony/polyfill-mbstring": "To support UTF-8 if the Mbstring PHP extension is not enabled (^1.2)",
|
||||||
|
"thenetworg/oauth2-azure": "Needed for Microsoft XOAUTH2 authentication"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"PHPMailer\\PHPMailer\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"LGPL-2.1-only"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Marcus Bointon",
|
||||||
|
"email": "phpmailer@synchromedia.co.uk"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Jim Jagielski",
|
||||||
|
"email": "jimjag@gmail.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Andy Prevost",
|
||||||
|
"email": "codeworxtech@users.sourceforge.net"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Brent R. Matzelle"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "PHPMailer is a full-featured email creation and transfer class for PHP",
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/PHPMailer/PHPMailer/issues",
|
||||||
|
"source": "https://github.com/PHPMailer/PHPMailer/tree/v7.0.2"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://github.com/Synchro",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2026-01-09T18:02:33+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "phpoption/phpoption",
|
"name": "phpoption/phpoption",
|
||||||
"version": "1.9.5",
|
"version": "1.9.5",
|
||||||
|
|||||||
@@ -55,5 +55,17 @@ define('RECAPTCHA_THRESHOLD', 0.5); // Score minimum (0.0 à 1.0)
|
|||||||
// Contact
|
// Contact
|
||||||
define('CONTACT_EMAIL', $_ENV['CONTACT_EMAIL'] ?? '');
|
define('CONTACT_EMAIL', $_ENV['CONTACT_EMAIL'] ?? '');
|
||||||
|
|
||||||
|
// SMTP
|
||||||
|
define('SMTP_HOST', $_ENV['SMTP_HOST'] ?? '127.0.0.1');
|
||||||
|
define('SMTP_PORT', (int) $_ENV['SMTP_PORT'] ?? '1025');
|
||||||
|
define('SMTP_USERNAME', $_ENV['SMTP_USERNAME'] ?? '');
|
||||||
|
define('SMTP_PASSWORD', $_ENV['SMTP_PASSWORD'] ?? '');
|
||||||
|
define('SMTP_ENCRYPTION', $_ENV['SMTP_ENCRYPTION'] ?? 'none'); // none|tls|ssl
|
||||||
|
|
||||||
|
define('MAIL_FROM_ADDRESS', $_ENV['MAIL_FROM_ADDRESS'] ?? SMTP_USERNAME);
|
||||||
|
define('MAIL_FROM_NAME', $_ENV['MAIL_FROM_NAME'] ?? 'Portfolio - Contact');
|
||||||
|
|
||||||
// Sécurité
|
// Sécurité
|
||||||
define('APP_SECRET', $_ENV['APP_SECRET'] ?? '');
|
define('APP_SECRET', $_ENV['APP_SECRET'] ?? '');
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,11 @@
|
|||||||
* Fonctions helpers du portfolio
|
* Fonctions helpers du portfolio
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../vendor/autoload.php';
|
||||||
|
|
||||||
|
use PHPMailer\PHPMailer\PHPMailer;
|
||||||
|
use PHPMailer\PHPMailer\Exception as MailerException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inclut un template avec des données
|
* Inclut un template avec des données
|
||||||
* @param string $name Nom du template (sans .php)
|
* @param string $name Nom du template (sans .php)
|
||||||
@@ -410,6 +415,127 @@ function validateContactData(array $input): array
|
|||||||
* @return bool True si envoyé avec succès
|
* @return bool True si envoyé avec succès
|
||||||
*/
|
*/
|
||||||
function sendContactEmail(array $data): bool
|
function sendContactEmail(array $data): bool
|
||||||
|
{
|
||||||
|
// Vérifier config minimale
|
||||||
|
if (!defined('CONTACT_EMAIL') || empty(CONTACT_EMAIL)) {
|
||||||
|
error_log('CONTACT_EMAIL non configuré');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!defined('SMTP_HOST') || !defined('SMTP_PORT')) {
|
||||||
|
error_log('SMTP_HOST/SMTP_PORT non configurés');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Autoload PHPMailer (Composer)
|
||||||
|
$autoload = __DIR__ . '/../vendor/autoload.php';
|
||||||
|
if (!file_exists($autoload)) {
|
||||||
|
error_log('PHPMailer autoload introuvable. As-tu installé Composer ?');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
require_once $autoload;
|
||||||
|
|
||||||
|
$categorieLabels = [
|
||||||
|
'projet' => 'Projet freelance',
|
||||||
|
'poste' => 'Proposition de poste',
|
||||||
|
'autre' => 'Autre demande'
|
||||||
|
];
|
||||||
|
|
||||||
|
$categorie = $categorieLabels[$data['categorie']] ?? 'Autre';
|
||||||
|
$entreprise = $data['entreprise'] ?: 'Non renseignée';
|
||||||
|
|
||||||
|
$subject = "[Portfolio] {$categorie} - {$data['objet']}";
|
||||||
|
|
||||||
|
$body = "═══════════════════════════════════════════\n";
|
||||||
|
$body .= "NOUVEAU MESSAGE - PORTFOLIO\n";
|
||||||
|
$body .= "═══════════════════════════════════════════\n\n";
|
||||||
|
$body .= "DE: {$data['prenom']} {$data['nom']}\n";
|
||||||
|
$body .= "EMAIL: {$data['email']}\n";
|
||||||
|
$body .= "ENTREPRISE: {$entreprise}\n";
|
||||||
|
$body .= "CATÉGORIE: {$categorie}\n\n";
|
||||||
|
$body .= "───────────────────────────────────────────\n";
|
||||||
|
$body .= "OBJET: {$data['objet']}\n";
|
||||||
|
$body .= "───────────────────────────────────────────\n\n";
|
||||||
|
$body .= "MESSAGE:\n\n";
|
||||||
|
$body .= "{$data['message']}\n\n";
|
||||||
|
$body .= "═══════════════════════════════════════════\n";
|
||||||
|
$body .= "Envoyé le {$data['date']}\n";
|
||||||
|
$body .= "IP: {$data['ip']}\n";
|
||||||
|
$body .= "═══════════════════════════════════════════";
|
||||||
|
|
||||||
|
$mail = new PHPMailer(true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Mode SMTP
|
||||||
|
$mail->isSMTP();
|
||||||
|
$mail->Host = SMTP_HOST;
|
||||||
|
$mail->Port = SMTP_PORT;
|
||||||
|
|
||||||
|
// Auth SMTP si configurée
|
||||||
|
$hasAuth = !empty(SMTP_USERNAME) && !empty(SMTP_PASSWORD);
|
||||||
|
$mail->SMTPAuth = $hasAuth;
|
||||||
|
|
||||||
|
if ($hasAuth) {
|
||||||
|
$mail->Username = SMTP_USERNAME;
|
||||||
|
$mail->Password = SMTP_PASSWORD;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encryption
|
||||||
|
$enc = strtolower((string) SMTP_ENCRYPTION);
|
||||||
|
if ($enc === 'tls') {
|
||||||
|
$mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
|
||||||
|
} elseif ($enc === 'ssl') {
|
||||||
|
$mail->SMTPSecure = PHPMailer::ENCRYPTION_SMTPS;
|
||||||
|
} else {
|
||||||
|
// none
|
||||||
|
$mail->SMTPSecure = false;
|
||||||
|
$mail->SMTPAutoTLS = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$mail->CharSet = 'UTF-8';
|
||||||
|
$mail->Encoding = 'base64';
|
||||||
|
|
||||||
|
// Expéditeur (doit être un alias autorisé si Proton)
|
||||||
|
$fromAddress = defined('MAIL_FROM_ADDRESS') ? MAIL_FROM_ADDRESS : SMTP_USERNAME;
|
||||||
|
$fromName = defined('MAIL_FROM_NAME') ? MAIL_FROM_NAME : 'Portfolio - Contact';
|
||||||
|
|
||||||
|
if (empty($fromAddress)) {
|
||||||
|
error_log('MAIL_FROM_ADDRESS/SMTP_USERNAME vide : impossible de définir From');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$mail->setFrom($fromAddress, $fromName);
|
||||||
|
|
||||||
|
// Reply-To = email du visiteur
|
||||||
|
if (!empty($data['email'])) {
|
||||||
|
$mail->addReplyTo($data['email'], trim(($data['prenom'] ?? '') . ' ' . ($data['nom'] ?? '')));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Destinataire
|
||||||
|
$mail->addAddress(CONTACT_EMAIL);
|
||||||
|
|
||||||
|
$mail->Subject = $subject;
|
||||||
|
$mail->Body = $body;
|
||||||
|
$mail->isHTML(false);
|
||||||
|
|
||||||
|
$mail->SMTPDebug = 2;
|
||||||
|
$mail->Debugoutput = function ($str, $level) {
|
||||||
|
error_log("PHPMailer debug($level): $str");
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Timeouts/log (utile en debug)
|
||||||
|
$mail->Timeout = 10;
|
||||||
|
|
||||||
|
$mail->send();
|
||||||
|
return true;
|
||||||
|
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
error_log('Mail send unexpected error: ' . $e->getMessage());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*function sendContactEmail(array $data): bool
|
||||||
{
|
{
|
||||||
$categorieLabels = [
|
$categorieLabels = [
|
||||||
'projet' => 'Projet freelance',
|
'projet' => 'Projet freelance',
|
||||||
@@ -460,4 +586,4 @@ function sendContactEmail(array $data): bool
|
|||||||
}
|
}
|
||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}*/
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ include_template('navbar', compact('currentPage'));
|
|||||||
type="text"
|
type="text"
|
||||||
id="nom"
|
id="nom"
|
||||||
name="nom"
|
name="nom"
|
||||||
class="w-full px-4 py-3 bg-surface-alt border border-border rounded-lg text-text-primary placeholder-text-muted focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary transition-colors"
|
class="w-full px-4 py-3 bg-surface-alt border border-border rounded-lg text-text-muted placeholder-text-muted focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary transition-colors"
|
||||||
required
|
required
|
||||||
maxlength="100"
|
maxlength="100"
|
||||||
autocomplete="family-name"
|
autocomplete="family-name"
|
||||||
@@ -65,7 +65,7 @@ include_template('navbar', compact('currentPage'));
|
|||||||
type="text"
|
type="text"
|
||||||
id="prenom"
|
id="prenom"
|
||||||
name="prenom"
|
name="prenom"
|
||||||
class="w-full px-4 py-3 bg-surface-alt border border-border rounded-lg text-text-primary placeholder-text-muted focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary transition-colors"
|
class="w-full px-4 py-3 bg-surface-alt border border-border rounded-lg text-text-muted placeholder-text-muted focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary transition-colors"
|
||||||
required
|
required
|
||||||
maxlength="100"
|
maxlength="100"
|
||||||
autocomplete="given-name"
|
autocomplete="given-name"
|
||||||
@@ -86,7 +86,7 @@ include_template('navbar', compact('currentPage'));
|
|||||||
type="email"
|
type="email"
|
||||||
id="email"
|
id="email"
|
||||||
name="email"
|
name="email"
|
||||||
class="w-full px-4 py-3 bg-surface-alt border border-border rounded-lg text-text-primary placeholder-text-muted focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary transition-colors"
|
class="w-full px-4 py-3 bg-surface-alt border border-border rounded-lg text-text-muted placeholder-text-muted focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary transition-colors"
|
||||||
required
|
required
|
||||||
maxlength="255"
|
maxlength="255"
|
||||||
autocomplete="email"
|
autocomplete="email"
|
||||||
@@ -104,7 +104,7 @@ include_template('navbar', compact('currentPage'));
|
|||||||
type="text"
|
type="text"
|
||||||
id="entreprise"
|
id="entreprise"
|
||||||
name="entreprise"
|
name="entreprise"
|
||||||
class="w-full px-4 py-3 bg-surface-alt border border-border rounded-lg text-text-primary placeholder-text-muted focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary transition-colors"
|
class="w-full px-4 py-3 bg-surface-alt border border-border rounded-lg text-text-muted placeholder-text-muted focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary transition-colors"
|
||||||
maxlength="200"
|
maxlength="200"
|
||||||
autocomplete="organization"
|
autocomplete="organization"
|
||||||
placeholder="Nom de votre entreprise"
|
placeholder="Nom de votre entreprise"
|
||||||
@@ -120,13 +120,13 @@ include_template('navbar', compact('currentPage'));
|
|||||||
<select
|
<select
|
||||||
id="categorie"
|
id="categorie"
|
||||||
name="categorie"
|
name="categorie"
|
||||||
class="w-full px-4 py-3 bg-surface-alt border border-border rounded-lg text-text-primary focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary transition-colors"
|
class="w-full px-4 py-3 bg-surface-alt border border-border rounded-lg text-text-muted focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary transition-colors"
|
||||||
required
|
required
|
||||||
>
|
>
|
||||||
<option value="" disabled selected>Sélectionnez une catégorie...</option>
|
<option value="" disabled selected>Sélectionnez une catégorie...</option>
|
||||||
<option value="projet">Je souhaite parler de mon projet</option>
|
<option value="projet">Je souhaite parler de mon projet</option>
|
||||||
<option value="poste">Je souhaite vous proposer un poste</option>
|
<option value="poste">Je souhaite vous proposer un poste</option>
|
||||||
<option value="autre">Autre</option>
|
<option value="autre">J'ai une autre idée en tête</option>
|
||||||
</select>
|
</select>
|
||||||
<p class="text-error text-sm mt-1 hidden" data-error="categorie"></p>
|
<p class="text-error text-sm mt-1 hidden" data-error="categorie"></p>
|
||||||
</div>
|
</div>
|
||||||
@@ -140,7 +140,7 @@ include_template('navbar', compact('currentPage'));
|
|||||||
type="text"
|
type="text"
|
||||||
id="objet"
|
id="objet"
|
||||||
name="objet"
|
name="objet"
|
||||||
class="w-full px-4 py-3 bg-surface-alt border border-border rounded-lg text-text-primary placeholder-text-muted focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary transition-colors"
|
class="w-full px-4 py-3 bg-surface-alt border border-border rounded-lg text-text-muted placeholder-text-muted focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary transition-colors"
|
||||||
required
|
required
|
||||||
maxlength="200"
|
maxlength="200"
|
||||||
placeholder="Résumez votre demande en quelques mots"
|
placeholder="Résumez votre demande en quelques mots"
|
||||||
@@ -156,7 +156,7 @@ include_template('navbar', compact('currentPage'));
|
|||||||
<textarea
|
<textarea
|
||||||
id="message"
|
id="message"
|
||||||
name="message"
|
name="message"
|
||||||
class="w-full px-4 py-3 bg-surface-alt border border-border rounded-lg text-text-primary placeholder-text-muted focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary transition-colors resize-y min-h-[150px]"
|
class="w-full px-4 py-3 bg-surface-alt border border-border rounded-lg text-text-muted placeholder-text-muted focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary transition-colors resize-y min-h-[150px]"
|
||||||
required
|
required
|
||||||
maxlength="5000"
|
maxlength="5000"
|
||||||
rows="6"
|
rows="6"
|
||||||
|
|||||||
Reference in New Issue
Block a user