- generateRefNumber() basé sur timestamp : références jamais identiques - checkExistingContact() : lecture HubSpot via clé de service (read-only) - Si email déjà connu : affiche message 'déjà client' + référence existante, AUCUNE soumission envoyée (référence existante jamais modifiée) - Notification interne Formspree si tentative double inscription - Traductions FR/EN/MG pour les nouveaux messages
303 lines
11 KiB
JavaScript
303 lines
11 KiB
JavaScript
// ============================================
|
|
// MVA Global Fret — Form Handler
|
|
// HubSpot Portal ID : 148163754
|
|
// HubSpot Form GUID : 1d9b75c9-8b60-4966-aa18-4bf503452e9a
|
|
// ============================================
|
|
|
|
const HUBSPOT_PORTAL_ID = '148163754';
|
|
const HUBSPOT_FORM_GUID = '1d9b75c9-8b60-4966-aa18-4bf503452e9a';
|
|
// Clé de service HubSpot — lecture seule des contacts (crm.objects.contacts.read)
|
|
const HUBSPOT_SERVICE_KEY = 'pat-eu1-e3c92146-bb17-45fe-8d77-0c665fc4df3b';
|
|
const FORMSPREE_ID = 'mojrvokp';
|
|
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
const form = document.getElementById('contactForm');
|
|
if (form) setupContactForm(form);
|
|
});
|
|
|
|
// Génération basée sur le timestamp : impossible d'avoir deux fois le même numéro
|
|
function generateRefNumber() {
|
|
const year = new Date().getFullYear();
|
|
// Timestamp milliseconde → 6 derniers chiffres, toujours entre 100000 et 999999
|
|
const ts = (Date.now() % 900000) + 100000;
|
|
return `MVA-${year}-${ts}`;
|
|
}
|
|
|
|
// Vérifie si l'email existe déjà dans HubSpot (lecture seule — ne modifie rien)
|
|
async function checkExistingContact(email) {
|
|
try {
|
|
const res = await fetch(
|
|
'https://api.hubapi.com/crm/v3/objects/contacts/search',
|
|
{
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'Authorization': `Bearer ${HUBSPOT_SERVICE_KEY}`
|
|
},
|
|
body: JSON.stringify({
|
|
filterGroups: [{
|
|
filters: [{
|
|
propertyName: 'email',
|
|
operator: 'EQ',
|
|
value: email
|
|
}]
|
|
}],
|
|
properties: ['firstname', 'lastname', 'email', 'reference_client']
|
|
})
|
|
}
|
|
);
|
|
if (!res.ok) return null;
|
|
const data = await res.json();
|
|
return data.total > 0 ? data.results[0].properties : null;
|
|
} catch {
|
|
// En cas d'erreur réseau : on laisse passer (ne pas bloquer l'inscription)
|
|
return null;
|
|
}
|
|
}
|
|
|
|
function setupContactForm(form) {
|
|
form.addEventListener('submit', async (e) => {
|
|
e.preventDefault();
|
|
if (!validateForm(form)) return;
|
|
|
|
setLoading(true);
|
|
|
|
const email = form.email.value.trim();
|
|
|
|
// ── VÉRIFICATION DOUBLON ──────────────────────────────────────────────────
|
|
// Si le client existe déjà, on affiche uniquement un message informatif.
|
|
// On ne soumet RIEN à HubSpot — la référence existante n'est JAMAIS modifiée.
|
|
const existing = await checkExistingContact(email);
|
|
if (existing) {
|
|
setLoading(false);
|
|
showAlreadyRegistered(existing);
|
|
return; // ← arrêt complet, aucune soumission
|
|
}
|
|
// ─────────────────────────────────────────────────────────────────────────
|
|
|
|
// Nouveau client : génération du numéro de référence unique
|
|
const refNumber = generateRefNumber();
|
|
|
|
const data = {
|
|
firstname: form.firstname.value.trim(),
|
|
lastname: form.lastname.value.trim(),
|
|
phone: form.phone.value.trim(),
|
|
email: email,
|
|
address: form.address.value.trim(),
|
|
reference_client: refNumber,
|
|
};
|
|
|
|
const results = await Promise.allSettled([
|
|
submitToHubSpot(data),
|
|
submitToFormspree(data),
|
|
]);
|
|
|
|
const hubspotOk = results[0].status === 'fulfilled';
|
|
const formspreeOk = results[1].status === 'fulfilled';
|
|
|
|
setLoading(false);
|
|
|
|
if (hubspotOk || formspreeOk) {
|
|
showSuccess(refNumber);
|
|
} else {
|
|
showError();
|
|
}
|
|
});
|
|
}
|
|
|
|
// ── SOUMISSION HUBSPOT ────────────────────────────────────────────────────────
|
|
async function submitToHubSpot(data) {
|
|
const payload = {
|
|
fields: [
|
|
{ name: 'firstname', value: data.firstname },
|
|
{ name: 'lastname', value: data.lastname },
|
|
{ name: 'phone', value: data.phone },
|
|
{ name: 'email', value: data.email },
|
|
{ name: 'address', value: data.address },
|
|
{ name: 'reference_client', value: data.reference_client },
|
|
],
|
|
context: {
|
|
pageUri: window.location.href,
|
|
pageName: document.title,
|
|
},
|
|
};
|
|
|
|
const res = await fetch(
|
|
`https://api.hsforms.com/submissions/v3/integration/submit/${HUBSPOT_PORTAL_ID}/${HUBSPOT_FORM_GUID}`,
|
|
{
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(payload),
|
|
}
|
|
);
|
|
|
|
if (!res.ok) throw new Error(`HubSpot error: ${res.status}`);
|
|
return res.json();
|
|
}
|
|
|
|
// ── SOUMISSION FORMSPREE (email de backup) ────────────────────────────────────
|
|
async function submitToFormspree(data) {
|
|
if (FORMSPREE_ID === 'YOUR_FORMSPREE_ID') return;
|
|
|
|
const res = await fetch(`https://formspree.io/f/${FORMSPREE_ID}`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
|
|
body: JSON.stringify({
|
|
nom: data.lastname,
|
|
prenom: data.firstname,
|
|
telephone: data.phone,
|
|
email: data.email,
|
|
adresse_livraison: data.address,
|
|
reference_client: data.reference_client,
|
|
}),
|
|
});
|
|
|
|
if (!res.ok) throw new Error(`Formspree error: ${res.status}`);
|
|
return res.json();
|
|
}
|
|
|
|
// ── NOTIFICATION DOUBLON (email interne seulement, sans toucher aux données) ──
|
|
async function notifyDuplicateViaFormspree(contact) {
|
|
if (FORMSPREE_ID === 'YOUR_FORMSPREE_ID') return;
|
|
try {
|
|
await fetch(`https://formspree.io/f/${FORMSPREE_ID}`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
|
|
body: JSON.stringify({
|
|
_subject: `[MVA] Tentative double inscription — ${contact.firstname || ''} ${contact.lastname || ''}`,
|
|
message: `Le client ${contact.firstname || ''} ${contact.lastname || ''} (${contact.email}) a tenté de s'inscrire à nouveau. Référence existante : ${contact.reference_client || 'non définie'}.`,
|
|
}),
|
|
});
|
|
} catch { /* Ne pas bloquer l'interface si la notification échoue */ }
|
|
}
|
|
|
|
// ── VALIDATION ────────────────────────────────────────────────────────────────
|
|
function validateForm(form) {
|
|
let valid = true;
|
|
const lang = localStorage.getItem('mva-lang') || 'fr';
|
|
const t = translations?.[lang]?.contact || {};
|
|
|
|
const requiredMsg = t.required || 'Ce champ est obligatoire';
|
|
const invalidEmail = t.invalidEmail || 'Adresse email invalide';
|
|
const invalidPhone = t.invalidPhone || 'Numéro de téléphone invalide';
|
|
|
|
const fields = ['firstname', 'lastname', 'phone', 'email', 'address'];
|
|
fields.forEach(name => clearError(name));
|
|
clearError('cgv');
|
|
|
|
fields.forEach(name => {
|
|
const el = form[name];
|
|
if (!el.value.trim()) {
|
|
showFieldError(name, requiredMsg);
|
|
valid = false;
|
|
}
|
|
});
|
|
|
|
const cgvBox = form['cgv'];
|
|
if (cgvBox && !cgvBox.checked) {
|
|
showFieldError('cgv', t.cgvRequired || 'Vous devez accepter les Conditions Générales de Vente.');
|
|
valid = false;
|
|
}
|
|
|
|
if (form.email.value.trim() && !isValidEmail(form.email.value.trim())) {
|
|
showFieldError('email', invalidEmail);
|
|
valid = false;
|
|
}
|
|
|
|
if (form.phone.value.trim() && !isValidPhone(form.phone.value.trim())) {
|
|
showFieldError('phone', invalidPhone);
|
|
valid = false;
|
|
}
|
|
|
|
return valid;
|
|
}
|
|
|
|
function isValidEmail(email) {
|
|
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
|
|
}
|
|
|
|
function isValidPhone(phone) {
|
|
return /^[+\d][\d\s\-().]{6,20}$/.test(phone);
|
|
}
|
|
|
|
// ── AFFICHAGE ─────────────────────────────────────────────────────────────────
|
|
function showFieldError(name, msg) {
|
|
const el = document.getElementById(`error-${name}`);
|
|
const input = document.getElementById(name) || document.querySelector(`[name="${name}"]`);
|
|
if (el) el.textContent = msg;
|
|
if (input) input.classList.add('error');
|
|
}
|
|
|
|
function clearError(name) {
|
|
const el = document.getElementById(`error-${name}`);
|
|
const input = document.getElementById(name) || document.querySelector(`[name="${name}"]`);
|
|
if (el) el.textContent = '';
|
|
if (input) input.classList.remove('error');
|
|
}
|
|
|
|
function setLoading(isLoading) {
|
|
const btn = document.getElementById('submitBtn');
|
|
const txt = document.getElementById('submitText');
|
|
const form = document.getElementById('contactForm');
|
|
const lang = localStorage.getItem('mva-lang') || 'fr';
|
|
const t = translations?.[lang]?.contact || {};
|
|
|
|
if (!btn) return;
|
|
btn.disabled = isLoading;
|
|
if (txt) {
|
|
txt.textContent = isLoading
|
|
? (t.submitLoading || 'Envoi en cours...')
|
|
: (t.submitBtn || "S'inscrire");
|
|
}
|
|
form?.classList.toggle('form-loading', isLoading);
|
|
}
|
|
|
|
function showSuccess(refNumber) {
|
|
const successEl = document.getElementById('formSuccess');
|
|
const form = document.getElementById('contactForm');
|
|
const refDisplay = document.getElementById('refNumberDisplay');
|
|
if (refDisplay && refNumber) refDisplay.textContent = refNumber;
|
|
if (successEl) {
|
|
successEl.classList.add('show');
|
|
successEl.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
}
|
|
if (form) form.style.display = 'none';
|
|
}
|
|
|
|
// Affiche le message "déjà client" — ne modifie AUCUNE donnée HubSpot
|
|
function showAlreadyRegistered(contact) {
|
|
const lang = localStorage.getItem('mva-lang') || 'fr';
|
|
const t = translations?.[lang]?.contact || {};
|
|
|
|
const alreadyEl = document.getElementById('alreadyRegistered');
|
|
const form = document.getElementById('contactForm');
|
|
const refDisplay = document.getElementById('existingRefDisplay');
|
|
|
|
// Affiche la référence existante si disponible, sinon message de contact
|
|
if (refDisplay) {
|
|
refDisplay.textContent = contact.reference_client
|
|
? contact.reference_client
|
|
: (t.alreadyRefUnknown || 'Contactez-nous pour retrouver votre référence.');
|
|
}
|
|
|
|
if (alreadyEl) {
|
|
alreadyEl.classList.add('show');
|
|
alreadyEl.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
}
|
|
|
|
if (form) form.style.display = 'none';
|
|
|
|
// Envoi d'une notification interne à MVA (sans modifier les données du client)
|
|
notifyDuplicateViaFormspree(contact);
|
|
}
|
|
|
|
function showError() {
|
|
const errEl = document.getElementById('formErrorGlobal');
|
|
const lang = localStorage.getItem('mva-lang') || 'fr';
|
|
const t = translations?.[lang]?.contact || {};
|
|
if (errEl) {
|
|
errEl.style.display = 'block';
|
|
errEl.textContent = t.errorMsg || 'Une erreur est survenue. Veuillez réessayer ou nous contacter directement.';
|
|
}
|
|
}
|