Add post-confirmation page that triggers welcome email after double opt-in
Flux complet du double opt-in : 1. User soumet le formulaire → contact créé en HubSpot avec sa référence 2. HubSpot envoie un email 'Confirmez votre inscription' 3. User clique 'Confirmer' → HubSpot le marque 'subscribed' 4. HubSpot redirige vers confirmation.html?email=... 5. La page lit l'email, appelle le Worker Cloudflare pour récupérer la référence du contact, et déclenche l'envoi de l'email de bienvenue via EmailJS (avec la référence dedans) 6. Affiche succès + référence à l'écran Idempotence via localStorage pour éviter de spammer l'email à chaque rechargement de la page. À configurer dans HubSpot Settings > Marketing > Email > Confirmation d'inscription : URL de redirection après confirmation = https://mva-global-fret.github.io/site-mva-global-fret/confirmation.html?email={{contact.email}} Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
c713d40946
commit
f534376f90
180
confirmation.html
Normal file
180
confirmation.html
Normal file
@ -0,0 +1,180 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Inscription confirmée — MVA Global Fret</title>
|
||||
<meta name="description" content="Votre inscription chez MVA Global Fret est confirmée. Votre numéro de référence client vient d'être envoyé par email.">
|
||||
<meta name="robots" content="noindex, nofollow">
|
||||
<link rel="icon" type="image/png" href="PNG MVA GLOBAL FRET.png">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=Poppins:wght@600;700&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css">
|
||||
<link rel="stylesheet" href="css/style.css">
|
||||
|
||||
<!-- EmailJS -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/@emailjs/browser@4/dist/email.min.js"></script>
|
||||
|
||||
<style>
|
||||
.confirmation-shell {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.confirmation-main {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 80px 24px 40px;
|
||||
background: linear-gradient(135deg, var(--navy) 0%, #2a2a5e 100%);
|
||||
}
|
||||
.confirmation-card {
|
||||
max-width: 560px;
|
||||
width: 100%;
|
||||
background: var(--white);
|
||||
border-radius: 20px;
|
||||
padding: 48px 36px;
|
||||
text-align: center;
|
||||
box-shadow: 0 30px 80px rgba(0, 0, 0, 0.25);
|
||||
border: 2px solid var(--gold);
|
||||
}
|
||||
.confirmation-card .icon-circle {
|
||||
width: 88px;
|
||||
height: 88px;
|
||||
margin: 0 auto 24px;
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(135deg, var(--gold), #e0c98a);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 12px 30px rgba(197, 165, 90, 0.45);
|
||||
}
|
||||
.confirmation-card .icon-circle i {
|
||||
font-size: 2.4rem;
|
||||
color: var(--navy);
|
||||
}
|
||||
.confirmation-card h1 {
|
||||
color: var(--navy);
|
||||
margin-bottom: 16px;
|
||||
font-size: clamp(1.6rem, 4vw, 2.2rem);
|
||||
}
|
||||
.confirmation-card p {
|
||||
color: var(--text-light);
|
||||
margin-bottom: 14px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
.confirmation-card .ref-block {
|
||||
margin-top: 28px;
|
||||
padding: 18px 24px;
|
||||
background: var(--navy);
|
||||
border-radius: 12px;
|
||||
border: 2px solid var(--gold);
|
||||
text-align: center;
|
||||
}
|
||||
.confirmation-card .ref-block small {
|
||||
display: block;
|
||||
color: var(--gold);
|
||||
font-size: 0.8rem;
|
||||
font-weight: 600;
|
||||
letter-spacing: 1px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
.confirmation-card .ref-block strong {
|
||||
color: var(--white);
|
||||
font-size: 1.6rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: 2px;
|
||||
}
|
||||
.confirmation-card .actions {
|
||||
margin-top: 32px;
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
}
|
||||
.confirmation-card .actions .btn {
|
||||
flex: 1 1 200px;
|
||||
}
|
||||
.confirmation-card .loader {
|
||||
display: inline-block;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
border: 2px solid rgba(26,26,62,0.2);
|
||||
border-top-color: var(--navy);
|
||||
border-radius: 50%;
|
||||
animation: spin 0.7s linear infinite;
|
||||
vertical-align: middle;
|
||||
margin-right: 8px;
|
||||
}
|
||||
@keyframes spin { to { transform: rotate(360deg); } }
|
||||
.confirmation-card.is-error .icon-circle {
|
||||
background: linear-gradient(135deg, #ff5a5a, #ff8a8a);
|
||||
box-shadow: 0 12px 30px rgba(255, 90, 90, 0.45);
|
||||
}
|
||||
.confirmation-card.is-error .icon-circle i { color: var(--white); }
|
||||
.confirmation-card.is-error { border-color: #ff5a5a; }
|
||||
</style>
|
||||
</head>
|
||||
<body class="confirmation-shell">
|
||||
|
||||
<header class="header" id="header" style="background: rgba(26,26,62,0.95);">
|
||||
<div class="container header-inner">
|
||||
<a href="accueil.html" class="logo"><img src="PNG MVA GLOBAL FRET.png" alt="MVA Global Fret"></a>
|
||||
<div class="header-right">
|
||||
<div class="lang-switcher" role="group" aria-label="Choisir la langue">
|
||||
<button data-lang="fr" class="active">FR</button>
|
||||
<button data-lang="en">EN</button>
|
||||
<button data-lang="mg">MG</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="confirmation-main">
|
||||
<!-- État : chargement (par défaut) -->
|
||||
<div class="confirmation-card" id="cardLoading">
|
||||
<div class="icon-circle"><i class="fa-solid fa-envelope-open-text"></i></div>
|
||||
<h1 data-i18n="confirmation.loadingTitle">Confirmation en cours…</h1>
|
||||
<p data-i18n="confirmation.loadingDesc">Nous finalisons votre inscription et préparons votre numéro de référence client.</p>
|
||||
<p style="margin-top:18px;"><span class="loader"></span><span data-i18n="confirmation.loadingHint">Quelques secondes…</span></p>
|
||||
</div>
|
||||
|
||||
<!-- État : succès -->
|
||||
<div class="confirmation-card" id="cardSuccess" style="display:none;">
|
||||
<div class="icon-circle"><i class="fa-solid fa-circle-check"></i></div>
|
||||
<h1 data-i18n="confirmation.successTitle">Inscription confirmée !</h1>
|
||||
<p data-i18n="confirmation.successDesc">Bienvenue chez MVA Global Fret. Votre numéro de référence client vous a été envoyé par email.</p>
|
||||
<div class="ref-block" id="refBlock" style="display:none;">
|
||||
<small data-i18n="confirmation.refLabel">VOTRE NUMÉRO DE RÉFÉRENCE CLIENT</small>
|
||||
<strong id="refDisplay"></strong>
|
||||
</div>
|
||||
<p style="margin-top:18px; font-size:0.9rem;" data-i18n="confirmation.successHint">📧 Pensez aussi à vérifier vos spams.</p>
|
||||
<div class="actions">
|
||||
<a href="accueil.html" class="btn btn-primary" data-i18n="confirmation.backHome">Retour à l'accueil</a>
|
||||
<a href="guide-envoi.html" class="btn btn-secondary" data-i18n="confirmation.guide">Guide d'envoi</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- État : erreur -->
|
||||
<div class="confirmation-card is-error" id="cardError" style="display:none;">
|
||||
<div class="icon-circle"><i class="fa-solid fa-circle-exclamation"></i></div>
|
||||
<h1 data-i18n="confirmation.errorTitle">Confirmation reçue</h1>
|
||||
<p data-i18n="confirmation.errorDesc">Votre confirmation est bien enregistrée chez MVA Global Fret. En cas de question sur votre numéro de référence, contactez-nous directement.</p>
|
||||
<div class="actions">
|
||||
<a href="contact.html" class="btn btn-primary" data-i18n="confirmation.contactUs">Nous contacter</a>
|
||||
<a href="accueil.html" class="btn btn-secondary" data-i18n="confirmation.backHome">Retour à l'accueil</a>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<footer style="background: var(--navy-dark, #050518); color: rgba(255,255,255,0.6); padding: 18px 24px; text-align:center; font-size: 0.85rem;">
|
||||
© 2026 MVA Global Fret. Tous droits réservés.
|
||||
</footer>
|
||||
|
||||
<script src="js/translations.js"></script>
|
||||
<script src="js/main.js"></script>
|
||||
<script src="js/confirmation.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
110
js/confirmation.js
Normal file
110
js/confirmation.js
Normal file
@ -0,0 +1,110 @@
|
||||
// ============================================================
|
||||
// MVA Global Fret — Page de confirmation post-double-opt-in
|
||||
// ============================================================
|
||||
// Cette page est la cible de redirection après que l'utilisateur
|
||||
// a cliqué sur "Confirmer" dans l'email de validation HubSpot.
|
||||
//
|
||||
// HubSpot redirige vers :
|
||||
// https://mva-global-fret.github.io/site-mva-global-fret/confirmation.html?email={contact.email}
|
||||
//
|
||||
// Étapes :
|
||||
// 1. Lire l'email depuis l'URL
|
||||
// 2. Demander au Cloudflare Worker la fiche du contact (incluant reference_client)
|
||||
// 3. Envoyer un email de bienvenue via EmailJS contenant la référence
|
||||
// 4. Afficher la référence à l'écran
|
||||
//
|
||||
// Si une étape échoue, on affiche un fallback poli (l'inscription
|
||||
// reste valide côté HubSpot, c'est juste l'email qui ne part pas).
|
||||
// ============================================================
|
||||
|
||||
const WORKER_PROXY_URL = 'https://mva-hubspot-proxy.mvaglobalfret.workers.dev';
|
||||
const EMAILJS_PUBLIC_KEY = '8KUlaQ7BDVIbkZRyP';
|
||||
const EMAILJS_SERVICE_ID = 'service_aeamo3x';
|
||||
const EMAILJS_TEMPLATE_ID = 'template_s1kr2et';
|
||||
|
||||
// Marqueur localStorage : empêche de relancer l'envoi si l'utilisateur
|
||||
// recharge la page après confirmation (par sécurité ET pour ne pas
|
||||
// renvoyer 3 fois le même email).
|
||||
const STORAGE_KEY_PREFIX = 'mva-confirm-sent-';
|
||||
|
||||
document.addEventListener('DOMContentLoaded', async () => {
|
||||
if (typeof emailjs !== 'undefined') {
|
||||
emailjs.init({ publicKey: EMAILJS_PUBLIC_KEY });
|
||||
}
|
||||
|
||||
const email = getEmailFromUrl();
|
||||
|
||||
if (!email) {
|
||||
// Pas d'email dans l'URL : on affiche quand même un succès générique
|
||||
// (l'utilisateur a bien confirmé puisqu'il atterrit ici depuis HubSpot)
|
||||
showSuccess(null);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const contact = await fetchContact(email);
|
||||
|
||||
if (!contact) {
|
||||
// Contact non trouvé via Worker — on affiche succès quand même
|
||||
showSuccess(null);
|
||||
return;
|
||||
}
|
||||
|
||||
const ref = contact.reference_client || null;
|
||||
|
||||
// Idempotence : un seul email par confirmation
|
||||
const storageKey = STORAGE_KEY_PREFIX + email.toLowerCase();
|
||||
if (!localStorage.getItem(storageKey)) {
|
||||
await sendWelcomeEmail({
|
||||
firstname: contact.firstname || '',
|
||||
email: email,
|
||||
reference_client: ref || '',
|
||||
});
|
||||
localStorage.setItem(storageKey, String(Date.now()));
|
||||
}
|
||||
|
||||
showSuccess(ref);
|
||||
} catch (err) {
|
||||
console.warn('[confirmation] flow failed:', err);
|
||||
showError();
|
||||
}
|
||||
});
|
||||
|
||||
function getEmailFromUrl() {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const raw = params.get('email');
|
||||
if (!raw) return null;
|
||||
const email = raw.trim().toLowerCase();
|
||||
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email) ? email : null;
|
||||
}
|
||||
|
||||
async function fetchContact(email) {
|
||||
const res = await fetch(WORKER_PROXY_URL, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ email }),
|
||||
});
|
||||
if (!res.ok) throw new Error('Worker error: ' + res.status);
|
||||
const data = await res.json();
|
||||
if (!data.results || data.results.length === 0) return null;
|
||||
return data.results[0].properties;
|
||||
}
|
||||
|
||||
async function sendWelcomeEmail(payload) {
|
||||
if (typeof emailjs === 'undefined') return;
|
||||
await emailjs.send(EMAILJS_SERVICE_ID, EMAILJS_TEMPLATE_ID, payload);
|
||||
}
|
||||
|
||||
function showSuccess(ref) {
|
||||
document.getElementById('cardLoading').style.display = 'none';
|
||||
document.getElementById('cardSuccess').style.display = '';
|
||||
if (ref) {
|
||||
document.getElementById('refDisplay').textContent = ref;
|
||||
document.getElementById('refBlock').style.display = '';
|
||||
}
|
||||
}
|
||||
|
||||
function showError() {
|
||||
document.getElementById('cardLoading').style.display = 'none';
|
||||
document.getElementById('cardError').style.display = '';
|
||||
}
|
||||
@ -333,6 +333,21 @@ const translations = {
|
||||
ctaSubtitle: "Contactez-nous avec votre lien produit, nous vous établissons un devis sous 24h.",
|
||||
ctaContact: "Nous contacter",
|
||||
ctaMessenger: "Messenger"
|
||||
},
|
||||
|
||||
confirmation: {
|
||||
loadingTitle: "Confirmation en cours…",
|
||||
loadingDesc: "Nous finalisons votre inscription et préparons votre numéro de référence client.",
|
||||
loadingHint: "Quelques secondes…",
|
||||
successTitle: "Inscription confirmée !",
|
||||
successDesc: "Bienvenue chez MVA Global Fret. Votre numéro de référence client vous a été envoyé par email.",
|
||||
successHint: "📧 Pensez aussi à vérifier vos spams.",
|
||||
refLabel: "VOTRE NUMÉRO DE RÉFÉRENCE CLIENT",
|
||||
errorTitle: "Confirmation reçue",
|
||||
errorDesc: "Votre confirmation est bien enregistrée chez MVA Global Fret. En cas de question sur votre numéro de référence, contactez-nous directement.",
|
||||
backHome: "Retour à l'accueil",
|
||||
guide: "Guide d'envoi",
|
||||
contactUs: "Nous contacter"
|
||||
}
|
||||
},
|
||||
|
||||
@ -670,6 +685,21 @@ const translations = {
|
||||
ctaSubtitle: "Contact us with your product link, we'll send you a quote within 24h.",
|
||||
ctaContact: "Contact us",
|
||||
ctaMessenger: "Messenger"
|
||||
},
|
||||
|
||||
confirmation: {
|
||||
loadingTitle: "Confirming…",
|
||||
loadingDesc: "We're finalising your registration and preparing your client reference number.",
|
||||
loadingHint: "A few seconds…",
|
||||
successTitle: "Registration confirmed!",
|
||||
successDesc: "Welcome to MVA Global Fret. Your client reference number has been sent to you by email.",
|
||||
successHint: "📧 Don't forget to check your spam folder.",
|
||||
refLabel: "YOUR CLIENT REFERENCE NUMBER",
|
||||
errorTitle: "Confirmation received",
|
||||
errorDesc: "Your confirmation has been recorded with MVA Global Fret. If you have any question about your reference number, please contact us directly.",
|
||||
backHome: "Back to home",
|
||||
guide: "Shipping guide",
|
||||
contactUs: "Contact us"
|
||||
}
|
||||
},
|
||||
|
||||
@ -1007,6 +1037,21 @@ const translations = {
|
||||
ctaSubtitle: "Mifandraisa aminay miaraka amin'ny rohin'ny vokatra, hanome tombam-bidy izahay ao anatin'ny 24 ora.",
|
||||
ctaContact: "Mifandraisa aminay",
|
||||
ctaMessenger: "Messenger"
|
||||
},
|
||||
|
||||
confirmation: {
|
||||
loadingTitle: "Eo am-panamafisana…",
|
||||
loadingDesc: "Mamarana ny fisoratana anaranareo izahay ary manomana ny laharanao mpanjifa.",
|
||||
loadingHint: "Segondra vitsivitsy…",
|
||||
successTitle: "Voamarina ny fisoratana anarana!",
|
||||
successDesc: "Tongasoa eto amin'ny MVA Global Fret. Nalefa tao amin'ny mailakao ny laharanao mpanjifa.",
|
||||
successHint: "📧 Jereo koa ao amin'ny spam.",
|
||||
refLabel: "NY LAHARANAO MPANJIFA",
|
||||
errorTitle: "Voaray ny fanamafisana",
|
||||
errorDesc: "Voarakitra tsara eto amin'ny MVA Global Fret ny fanamafisanareo. Raha manana fanontaniana mikasika ny laharana, mifandraisa aminay.",
|
||||
backHome: "Hiverina any am-pandraisana",
|
||||
guide: "Tari-dàlana fandefasana",
|
||||
contactUs: "Mifandraisa aminay"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Loading…
Reference in New Issue
Block a user