feat: newsletter form fonctionnel via Brevo
- Route Kirby `api/newsletter` (proxy vers l'API Brevo) dans site/config/routes/newsletter.php - JS de soumission du formulaire dans assets/js/newsletter-brevo.js - Chargement du script dans le template newsletter.php - Clé API dans config.index.ngo.php (gitignored) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
94065f1ce6
commit
9c9a2fd40a
5 changed files with 217 additions and 2 deletions
5
.gitignore
vendored
5
.gitignore
vendored
|
|
@ -49,6 +49,11 @@ Icon
|
|||
|
||||
/site/config/.license
|
||||
|
||||
# Host-specific config (credentials)
|
||||
# ---------------
|
||||
|
||||
/site/config/config.index.ngo.php
|
||||
|
||||
|
||||
# Content
|
||||
# ---------------
|
||||
|
|
|
|||
89
assets/js/newsletter-brevo.js
Normal file
89
assets/js/newsletter-brevo.js
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
(function () {
|
||||
'use strict';
|
||||
|
||||
const PROXY_URL = '/api/newsletter';
|
||||
|
||||
async function subscribeToNewsletter(email, attributes = {}) {
|
||||
const response = await fetch(PROXY_URL, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ email, attributes }),
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (!response.ok) {
|
||||
const error = new Error(
|
||||
data.user_message || data.message || 'Subscription error'
|
||||
);
|
||||
error.code = data.error;
|
||||
error.data = data;
|
||||
throw error;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
function showMessage(form, text, isError = false) {
|
||||
const oldMessages = form.parentNode.querySelectorAll('.newsletter-message');
|
||||
oldMessages.forEach((msg) => msg.remove());
|
||||
|
||||
const message = document.createElement('p');
|
||||
message.className = 'newsletter-message';
|
||||
message.textContent = text;
|
||||
message.style.marginTop = '0.5rem';
|
||||
message.style.fontSize = '0.9rem';
|
||||
message.style.color = isError
|
||||
? 'var(--color-error, #ef4444)'
|
||||
: 'var(--color-success, #22c55e)';
|
||||
|
||||
form.parentNode.insertBefore(message, form.nextSibling);
|
||||
|
||||
if (!isError) {
|
||||
setTimeout(() => message.remove(), 5000);
|
||||
}
|
||||
}
|
||||
|
||||
async function handleFormSubmit(event) {
|
||||
event.preventDefault();
|
||||
|
||||
const form = event.target;
|
||||
const emailInput = form.querySelector('input[type="email"]');
|
||||
const submitButton = form.querySelector('button[type="submit"]');
|
||||
|
||||
if (!emailInput || !emailInput.value) {
|
||||
const message = document.documentElement.lang.startsWith('en')
|
||||
? 'Please enter a valid email address.'
|
||||
: 'Veuillez entrer une adresse email valide.';
|
||||
showMessage(form, message, true);
|
||||
return;
|
||||
}
|
||||
|
||||
const email = emailInput.value.trim();
|
||||
submitButton.disabled = true;
|
||||
|
||||
try {
|
||||
await subscribeToNewsletter(email);
|
||||
const message = document.documentElement.lang.startsWith('en')
|
||||
? 'Thank you! Your subscription has been confirmed.'
|
||||
: 'Merci, votre inscription est confirmée !';
|
||||
showMessage(form, message, false);
|
||||
form.reset();
|
||||
} catch (error) {
|
||||
const isAlreadySubscribed = error.code === 'email_already_exists';
|
||||
showMessage(form, error.message, !isAlreadySubscribed);
|
||||
} finally {
|
||||
submitButton.disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
function initNewsletterForms() {
|
||||
const forms = document.querySelectorAll('.form__newsletter');
|
||||
forms.forEach((form) => form.addEventListener('submit', handleFormSubmit));
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', initNewsletterForms);
|
||||
})();
|
||||
|
|
@ -76,6 +76,10 @@ return [
|
|||
],
|
||||
'tobimori.seo.canonicalBase' => 'https://www.index.ngo',
|
||||
|
||||
'routes' => [
|
||||
require(__DIR__ . '/routes/newsletter.php'),
|
||||
],
|
||||
|
||||
'hooks' => [
|
||||
'page.update:after' => function ($newPage) {
|
||||
if ($newPage->intendedTemplate()->name() !== 'investigation') {
|
||||
|
|
|
|||
116
site/config/routes/newsletter.php
Normal file
116
site/config/routes/newsletter.php
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'pattern' => 'api/newsletter',
|
||||
'method' => 'POST|OPTIONS',
|
||||
'action' => function () {
|
||||
header('Content-Type: application/json');
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
|
||||
http_response_code(200);
|
||||
die();
|
||||
}
|
||||
|
||||
$config = kirby()->option('brevo');
|
||||
$apiKey = $config['api_key'] ?? '';
|
||||
$listId = (int)($config['list_id'] ?? 2);
|
||||
$apiUrl = $config['api_url'] ?? 'https://api.brevo.com/v3/contacts';
|
||||
|
||||
if (empty($apiKey)) {
|
||||
http_response_code(500);
|
||||
die(json_encode(['error' => 'Server configuration error', 'message' => 'Brevo API key not configured']));
|
||||
}
|
||||
|
||||
$input = file_get_contents('php://input');
|
||||
$data = json_decode($input, true);
|
||||
|
||||
if (!isset($data['email']) || empty($data['email'])) {
|
||||
http_response_code(400);
|
||||
die(json_encode(['error' => 'Email required']));
|
||||
}
|
||||
|
||||
$email = filter_var($data['email'], FILTER_VALIDATE_EMAIL);
|
||||
if ($email === false) {
|
||||
http_response_code(400);
|
||||
die(json_encode(['error' => 'Invalid email']));
|
||||
}
|
||||
|
||||
$brevoData = [
|
||||
'email' => $email,
|
||||
'listIds' => [$listId],
|
||||
'updateEnabled' => true,
|
||||
];
|
||||
|
||||
if (isset($data['attributes']) && is_array($data['attributes']) && !empty($data['attributes'])) {
|
||||
$brevoData['attributes'] = $data['attributes'];
|
||||
}
|
||||
|
||||
$ch = curl_init();
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_URL => $apiUrl,
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_POST => true,
|
||||
CURLOPT_POSTFIELDS => json_encode($brevoData),
|
||||
CURLOPT_HTTPHEADER => [
|
||||
'Content-Type: application/json',
|
||||
'api-key: ' . $apiKey,
|
||||
'User-Agent: Index-NGO-Newsletter',
|
||||
],
|
||||
CURLOPT_TIMEOUT => 10,
|
||||
CURLOPT_SSL_VERIFYPEER => true,
|
||||
]);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
$curlError = curl_error($ch);
|
||||
curl_close($ch);
|
||||
|
||||
if ($response === false) {
|
||||
http_response_code(500);
|
||||
die(json_encode(['error' => 'Connection error', 'details' => $curlError]));
|
||||
}
|
||||
|
||||
$responseData = json_decode($response, true);
|
||||
|
||||
switch ($httpCode) {
|
||||
case 201:
|
||||
case 204:
|
||||
http_response_code(200);
|
||||
die(json_encode(['success' => true, 'message' => 'Successfully subscribed', 'email' => $email]));
|
||||
|
||||
case 400:
|
||||
$isDuplicate = isset($responseData['code']) && $responseData['code'] === 'duplicate_parameter';
|
||||
http_response_code(400);
|
||||
die(json_encode([
|
||||
'error' => $isDuplicate ? 'email_already_exists' : 'invalid_data',
|
||||
'message' => $isDuplicate ? 'You are already subscribed!' : 'Invalid email address.',
|
||||
'user_message' => $isDuplicate ? 'Vous êtes déjà inscrit·e !' : 'Veuillez vérifier votre adresse email.',
|
||||
]));
|
||||
|
||||
case 401:
|
||||
http_response_code(500);
|
||||
die(json_encode([
|
||||
'error' => 'invalid_api_key',
|
||||
'message' => 'Invalid API key',
|
||||
'user_message' => 'Une erreur technique est survenue. Veuillez réessayer plus tard.',
|
||||
]));
|
||||
|
||||
case 404:
|
||||
http_response_code(500);
|
||||
die(json_encode([
|
||||
'error' => 'list_not_found',
|
||||
'message' => 'Contact list not found',
|
||||
'user_message' => 'Une erreur technique est survenue. Veuillez réessayer plus tard.',
|
||||
]));
|
||||
|
||||
default:
|
||||
http_response_code($httpCode);
|
||||
die(json_encode([
|
||||
'error' => 'api_error',
|
||||
'message' => 'Error communicating with subscription service',
|
||||
'user_message' => 'Une erreur est survenue. Veuillez réessayer.',
|
||||
'http_code' => $httpCode,
|
||||
]));
|
||||
}
|
||||
},
|
||||
];
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
<?php snippet('header') ?>
|
||||
<script src="<?= url('assets/js/newsletter-brevo.js') ?>"></script>
|
||||
<main class="main__single">
|
||||
|
||||
<header class="page__header">
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue