| Current Path : /home/happyrenas/restaurant.myreco.online/public/ |
Linux webd005.cluster105.gra.hosting.ovh.net 5.15.206-ovh-vps-grsec-zfs-classid #1 SMP Fri May 15 02:41:25 UTC 2026 x86_64 |
| Current File : /home/happyrenas/restaurant.myreco.online/public/forms.js |
(() => {
const TEXT = {
fr: {
visibilityTitle: 'Testez votre visibilité MyReco',
visibilityIntro: 'Recherchez votre établissement pour voir la liste des hébergements qui vous recommanderont.',
partnerListTitle: 'Hébergements qui vous recommanderont',
localityLinkText: 'Voir les autres établissements déjà référencés dans votre localité',
localityLinkButton: 'Ouvrir la liste locale',
subscriptionTitle: 'Réserver votre place sur MyReco',
subscriptionIntro: 'Recherchez votre établissement, choisissez une durée, puis renseignez votre URL de réservation.',
searchLabel: 'Nom de votre établissement',
searchPlaceholder: 'Ex. restaurant, brasserie, pizzeria...',
noResults: 'Aucun établissement trouvé. Essayez avec le nom ou la ville.',
loading: 'Recherche en cours...',
select: 'Sélectionner',
selected: 'Établissement sélectionné',
partnerCount: 'hébergements partenaires identifiés',
productLabel: 'Durée de visibilité',
bookingLabel: 'URL de réservation',
bookingPlaceholder: 'https://...',
startDateLabel: 'Date de début souhaitée',
submitVisibility: 'Tester ma visibilité',
submitSubscription: "Je m'abonne",
chooseFirst: 'Sélectionnez d’abord votre établissement.',
chooseProduct: 'Choisissez une durée de visibilité.',
savedVisibility: 'Demande enregistrée. Nous revenons vers vous avec le détail des hébergements partenaires.',
savedSubscription: 'Demande enregistrée. Le lien de paiement Stripe doit encore être configuré côté API.',
error: 'Impossible de joindre le service pour le moment. Réessayez dans un instant.'
},
es: {
visibilityTitle: 'Prueba tu visibilidad MyReco',
visibilityIntro: 'Busca tu establecimiento para ver la lista de alojamientos que te recomendarán.',
partnerListTitle: 'Alojamientos que te recomendarán',
localityLinkText: 'Ver los establecimientos ya referenciados en tu localidad',
localityLinkButton: 'Abrir la lista local',
subscriptionTitle: 'Reserva tu lugar en MyReco',
subscriptionIntro: 'Busca tu establecimiento, elige una duración e indica tu URL de reserva.',
searchLabel: 'Nombre de tu establecimiento',
searchPlaceholder: 'Ej. restaurante, tapas, pizzería...',
noResults: 'No se encontró ningún establecimiento. Prueba con el nombre o la ciudad.',
loading: 'Buscando...',
select: 'Seleccionar',
selected: 'Establecimiento seleccionado',
partnerCount: 'alojamientos asociados identificados',
productLabel: 'Duración de visibilidad',
bookingLabel: 'URL de reserva',
bookingPlaceholder: 'https://...',
startDateLabel: 'Fecha de inicio deseada',
submitVisibility: 'Probar mi visibilidad',
submitSubscription: 'Me suscribo',
chooseFirst: 'Primero selecciona tu establecimiento.',
chooseProduct: 'Elige una duración de visibilidad.',
savedVisibility: 'Solicitud registrada. Te enviaremos el detalle de los alojamientos asociados.',
savedSubscription: 'Solicitud registrada. El enlace de pago Stripe debe configurarse en la API.',
error: 'No se puede contactar el servicio por ahora. Inténtalo de nuevo en un momento.'
},
it: {
visibilityTitle: 'Prova la tua visibilità MyReco',
visibilityIntro: 'Cerca la tua struttura per vedere l\'elenco degli alloggi che ti consiglieranno.',
partnerListTitle: 'Alloggi che ti consiglieranno',
localityLinkText: 'Vedi le strutture già referenziate nella tua località',
localityLinkButton: 'Apri la lista locale',
subscriptionTitle: 'Prenota il tuo posto su MyReco',
subscriptionIntro: 'Cerca la tua struttura, scegli una durata e indica il tuo URL di prenotazione.',
searchLabel: 'Nome della struttura',
searchPlaceholder: 'Es. ristorante, pizzeria, trattoria...',
noResults: 'Nessuna struttura trovata. Prova con il nome o la città.',
loading: 'Ricerca in corso...',
select: 'Seleziona',
selected: 'Struttura selezionata',
partnerCount: 'alloggi partner identificati',
productLabel: 'Durata della visibilità',
bookingLabel: 'URL di prenotazione',
bookingPlaceholder: 'https://...',
startDateLabel: 'Data di inizio desiderata',
submitVisibility: 'Provare la visibilità',
submitSubscription: 'Mi abbono',
chooseFirst: 'Seleziona prima la tua struttura.',
chooseProduct: 'Scegli una durata di visibilità.',
savedVisibility: 'Richiesta registrata. Ti invieremo il dettaglio degli alloggi partner.',
savedSubscription: 'Richiesta registrata. Il link di pagamento Stripe deve essere configurato nell’API.',
error: 'Impossibile contattare il servizio per ora. Riprova tra poco.'
},
en: {
visibilityTitle: 'Test your MyReco visibility',
visibilityIntro: 'Search for your venue to see the list of accommodations that will recommend you.',
partnerListTitle: 'Accommodations that will recommend you',
localityLinkText: 'View the venues already listed in your local area',
localityLinkButton: 'Open local list',
subscriptionTitle: 'Reserve your place on MyReco',
subscriptionIntro: 'Search for your venue, choose a visibility period, then enter your booking URL.',
searchLabel: 'Venue name',
searchPlaceholder: 'E.g. restaurant, bistro, pizzeria...',
noResults: 'No venue found. Try with the name or city.',
loading: 'Searching...',
select: 'Select',
selected: 'Selected venue',
partnerCount: 'partner accommodations identified',
productLabel: 'Visibility period',
bookingLabel: 'Booking URL',
bookingPlaceholder: 'https://...',
startDateLabel: 'Desired start date',
submitVisibility: 'Test my visibility',
submitSubscription: 'Subscribe',
chooseFirst: 'Select your venue first.',
chooseProduct: 'Choose a visibility period.',
savedVisibility: 'Request saved. We will send you the partner accommodation details.',
savedSubscription: 'Request saved. The Stripe payment link still needs to be configured in the API.',
error: 'The service cannot be reached right now. Please try again in a moment.'
}
};
const LANGS = ['fr', 'es', 'it', 'pt', 'en'];
const htmlLang = (document.documentElement.lang || '').slice(0, 2).toLowerCase();
const configuredLang = String(window.MYRECO_LANG || '').slice(0, 2).toLowerCase();
const lang = LANGS.includes(configuredLang) ? configuredLang : (LANGS.includes(htmlLang) ? htmlLang : 'en');
const t = TEXT[lang] || TEXT.en;
const CONTACT_TEXT = {
fr: {
title: 'Une question sur MyReco ?',
intro: 'Utilisez ce formulaire pour nous contacter. Nous vous répondrons dès que possible.',
name: 'Nom de votre établissement',
address: 'Adresse de votre établissement',
addressHint: 'La saisie automatique du navigateur peut aider à remplir ce champ plus vite.',
email: 'Votre adresse e-mail',
subject: 'Sujet de votre question',
message: 'Votre message',
submit: 'Envoyer',
sending: 'Envoi en cours...',
success: 'Merci, votre message a bien été envoyé.',
missingCategory: 'Veuillez sélectionner au moins une catégorie.',
error: 'Impossible d’envoyer le message pour le moment. Réessayez dans un instant.',
categories: ['Demande d’informations', 'Référencement d’un établissement', 'Modification d’une fiche établissement', 'Partenariat', 'Problème technique', 'Facturation / paiement', 'Autre']
},
es: {
title: '¿Una pregunta sobre MyReco?',
intro: 'Utiliza este formulario para contactarnos. Te responderemos lo antes posible.',
name: 'Nombre de tu establecimiento',
address: 'Dirección de tu establecimiento',
addressHint: 'El autocompletado del navegador puede ayudarte a rellenar este campo más rápido.',
email: 'Tu correo electrónico',
subject: 'Tema de tu consulta',
message: 'Tu mensaje',
submit: 'Enviar',
sending: 'Enviando...',
success: 'Gracias, tu mensaje se ha enviado correctamente.',
missingCategory: 'Selecciona al menos una categoría.',
error: 'No se puede enviar el mensaje por ahora. Inténtalo de nuevo en un momento.',
categories: ['Solicitud de información', 'Alta de un establecimiento', 'Modificación de una ficha de establecimiento', 'Colaboración', 'Problema técnico', 'Facturación / pago', 'Otro']
},
it: {
title: 'Una domanda su MyReco?',
intro: 'Usa questo modulo per contattarci. Ti risponderemo il prima possibile.',
name: 'Nome della tua struttura',
address: 'Indirizzo della tua struttura',
addressHint: 'Il completamento automatico del browser può aiutarti a compilare questo campo più rapidamente.',
email: 'Il tuo indirizzo e-mail',
subject: 'Oggetto della richiesta',
message: 'Il tuo messaggio',
submit: 'Invia',
sending: 'Invio in corso...',
success: 'Grazie, il tuo messaggio è stato inviato correttamente.',
missingCategory: 'Seleziona almeno una categoria.',
error: 'Impossibile inviare il messaggio per il momento. Riprova tra poco.',
categories: ['Richiesta di informazioni', 'Inserimento di una struttura', 'Modifica di una scheda struttura', 'Partnership', 'Problema tecnico', 'Fatturazione / pagamento', 'Altro']
},
en: {
title: 'A question about MyReco?',
intro: 'Use this form to contact us. We will reply as soon as possible.',
name: 'Venue name',
address: 'Venue address',
addressHint: 'Browser autocomplete can help fill this field faster.',
email: 'Your email address',
subject: 'Subject of your question',
message: 'Your message',
submit: 'Send',
sending: 'Sending...',
success: 'Thank you, your message has been sent.',
missingCategory: 'Please select at least one category.',
error: 'The message cannot be sent right now. Please try again in a moment.',
categories: ['Information request', 'Venue listing', 'Venue listing update', 'Partnership', 'Technical issue', 'Billing / payment', 'Other']
}
};
const contactT = CONTACT_TEXT[lang] || CONTACT_TEXT.en;
const startedAt = Date.now();
function inferVertical() {
const explicit = new URLSearchParams(location.search).get('vertical');
if (explicit) return explicit;
const host = location.hostname.toLowerCase();
if (host.includes('restaurant') || host.includes('restautant')) return 'restaurant';
if (host.includes('soiree')) return 'soiree';
return 'ludique';
}
function apiBase() {
return (window.MYRECO_API_BASE || 'https://api.myreco.online').replace(/\/$/, '');
}
const vertical = window.MYRECO_VERTICAL || inferVertical();
const country = String(window.MYRECO_COUNTRY || '').trim().toUpperCase();
const hasCountry = /^[A-Z]{2}$/.test(country);
const base = apiBase();
const storageKey = 'myrecoSelectedVenue:' + (hasCountry ? country : 'no-country') + ':' + vertical + ':' + lang;
function escapeHtml(value) {
return String(value || '').replace(/[&<>"']/g, (char) => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[char]));
}
function contactEndpoint() {
return (window.MYRECO_CONTACT_ENDPOINT || (base + '/api/v1/contact')).replace(/\/$/, '');
}
function publicContextMessage() {
return ({
fr: 'Configuration pays manquante pour ce site.',
es: 'Falta la configuracion de pais para este sitio.',
it: 'Configurazione paese mancante per questo sito.',
pt: 'Configuracao de pais ausente para este site.',
en: 'Country configuration is missing for this site.'
}[lang] || 'Country configuration is missing for this site.');
}
function minSearchCharsMessage() {
return ({
fr: 'Saisissez au moins 4 caracteres.',
es: 'Escribe al menos 4 caracteres.',
it: 'Inserisci almeno 4 caratteri.',
pt: 'Digite pelo menos 4 caracteres.',
en: 'Enter at least 4 characters.'
}[lang] || 'Enter at least 4 characters.');
}
function hasPublicApiContext() {
return hasCountry && Boolean(vertical);
}
async function request(path, options = {}) {
const method = String(options.method || 'GET').toUpperCase();
const headers = { ...(options.headers || {}) };
if (!['GET', 'HEAD'].includes(method) && options.body && !headers['Content-Type']) {
headers['Content-Type'] = 'application/json';
}
const response = await fetch(base + path, {
...options,
headers
});
const json = await response.json().catch(() => ({}));
if (!response.ok || json.ok === false) throw new Error(json.error || 'request_failed');
return json;
}
function resultLabel(item) {
return [item.name, [item.postal_code, item.city].filter(Boolean).join(' ')].filter(Boolean).join(' - ');
}
function readStoredSelection() {
try {
const stored = window.localStorage && localStorage.getItem(storageKey);
return stored ? JSON.parse(stored) : null;
} catch (err) {
return null;
}
}
function storeSelection(item) {
try {
if (window.localStorage) localStorage.setItem(storageKey, JSON.stringify(item));
} catch (err) {}
}
function localityUrl(item) {
const raw = String(item.qr_mrx || '').trim();
if (!raw) return '';
try {
const parsed = new URL(raw);
return parsed.searchParams.get('text') || raw;
} catch (err) {
return raw;
}
}
function localityLinkHtml(item) {
const url = localityUrl(item);
const qrImage = String(item.qr_mrx_img || item.qr_mrx || '').trim();
if (!url && !qrImage) return '';
const mobileLink = url ? '<div class="mobile-locality-link"><p>' + escapeHtml(t.localityLinkText || '') + '</p><a class="button" target="_blank" rel="noopener" href="' + escapeHtml(url) + '">' + escapeHtml(t.localityLinkButton || '') + '</a></div>' : '';
const desktopQr = qrImage ? '<div class="desktop-locality-qr"><p>Scannez ce QR code pour visualiser les autres établissements déjà présents dans MyReco</p><img src="' + escapeHtml(qrImage) + '" alt="QR code MyReco"></div>' : '';
return mobileLink + desktopQr;
}
function applySelection(container, item, details, status, options = {}) {
details.innerHTML = detailHtml(item, options);
const results = container.querySelector('[data-role="results"]');
if (results) results.innerHTML = '';
const input = container.querySelector('[data-role="search"]');
if (input) input.value = resultLabel(item);
const locality = container.querySelector('[data-role="locality-link"]');
if (locality) locality.innerHTML = options.showPartnerList ? localityLinkHtml(item) : '';
if (status) status.textContent = '';
}
function partnerListHtml(item) {
const raw = String(item.liste_heberg || '');
const names = raw.split(/\r?\n|;|\|/).map((name) => name.trim()).filter(Boolean).slice(0, 12);
if (!names.length) return '';
return '<div class="partner-list"><p class="partner-list-title">' + escapeHtml(t.partnerListTitle || '') + '</p><ul>' + names.map((name) => '<li>' + escapeHtml(name) + '</li>').join('') + '</ul></div>';
}
function detailHtml(item, options = {}) {
const count = item.total_heberg || '0';
const partners = options.showPartnerList ? partnerListHtml(item) : '';
return '<div class="form-selected"><p class="eyebrow">' + t.selected + '</p><h3>' + escapeHtml(item.name) + '</h3><p>' + escapeHtml([item.postal_code, item.city].filter(Boolean).join(' ')) + '</p><p><strong>' + escapeHtml(count) + '</strong> ' + t.partnerCount + '</p>' + partners + '</div>';
}
function renderResults(target, items, onSelect) {
if (!items.length) {
target.innerHTML = '<p class="form-muted">' + t.noResults + '</p>';
return;
}
target.innerHTML = '<div class="form-results">' + items.map((item, index) => (
'<button type="button" class="form-result" data-index="' + index + '"><span>' + escapeHtml(resultLabel(item)) + '</span><strong>' + t.select + '</strong></button>'
)).join('') + '</div>';
target.querySelectorAll('.form-result').forEach((button) => {
button.addEventListener('click', () => onSelect(items[Number(button.dataset.index)]));
});
}
function setupSearch(container, onSelect) {
const input = container.querySelector('[data-role="search"]');
const results = container.querySelector('[data-role="results"]');
let timer = 0;
let activeSearchController = null;
let searchSequence = 0;
input.addEventListener('input', () => {
clearTimeout(timer);
if (activeSearchController) {
activeSearchController.abort();
activeSearchController = null;
}
const q = input.value.trim();
const sequence = ++searchSequence;
if (q.length < 4) {
results.innerHTML = q ? '<p class="form-muted">' + escapeHtml(minSearchCharsMessage()) + '</p>' : '';
return;
}
results.innerHTML = '<p class="form-muted">' + t.loading + '</p>';
timer = setTimeout(async () => {
if (sequence !== searchSequence) return;
try {
if (!hasPublicApiContext()) {
results.innerHTML = '<p class="form-error">' + escapeHtml(publicContextMessage()) + '</p>';
return;
}
activeSearchController = new AbortController();
const json = await request('/api/v1/search?country=' + encodeURIComponent(country) + '&vertical=' + encodeURIComponent(vertical) + '&lang=' + encodeURIComponent(lang) + '&q=' + encodeURIComponent(q), { signal: activeSearchController.signal });
if (sequence !== searchSequence) return;
renderResults(results, json.items || [], onSelect);
} catch (err) {
if (err && err.name === 'AbortError') return;
if (sequence !== searchSequence) return;
results.innerHTML = '<p class="form-error">' + t.error + '</p>';
} finally {
if (sequence === searchSequence) activeSearchController = null;
}
}, 360);
});
}
async function loadProducts(select) {
try {
if (!hasPublicApiContext()) {
select.innerHTML = '<option value="">' + t.productLabel + '</option>';
return;
}
const json = await request('/api/v1/products?country=' + encodeURIComponent(country) + '&vertical=' + encodeURIComponent(vertical) + '&lang=' + encodeURIComponent(lang));
select.innerHTML = '<option value="">' + t.productLabel + '</option>' + (json.products || []).map((product) => (
'<option value="' + escapeHtml(product.id) + '">' + escapeHtml(product.label + ' - ' + product.priceText) + '</option>'
)).join('');
} catch (err) {
select.innerHTML = '<option value="">' + t.productLabel + '</option>';
}
}
function baseTemplate(kind) {
const isSubscription = kind === 'subscription';
return '<div class="myreco-form-card">' +
'<div class="form-heading"><p class="eyebrow">MyReco</p><h2>' + (isSubscription ? t.subscriptionTitle : t.visibilityTitle) + '</h2><p>' + (isSubscription ? t.subscriptionIntro : t.visibilityIntro) + '</p></div>' +
'<form class="public-form" novalidate>' +
'<label>' + t.searchLabel + '<input type="search" data-role="search" autocomplete="off" placeholder="' + escapeHtml(t.searchPlaceholder) + '"></label>' +
'<div data-role="results"></div>' +
'<div data-role="details"></div>' +
(isSubscription ? '<div class="form-grid"><label>' + t.productLabel + '<select data-role="product"></select></label><label>' + t.bookingLabel + '<input type="url" data-role="booking" placeholder="' + escapeHtml(t.bookingPlaceholder) + '"></label><label>' + t.startDateLabel + '<input type="date" data-role="start"></label></div>' : '') +
'<input type="text" data-role="hp" tabindex="-1" autocomplete="off" class="form-hp" aria-hidden="true">' +
'<button class="button" type="submit">' + (isSubscription ? t.submitSubscription : t.submitVisibility) + '</button>' +
'<p class="form-status" data-role="status"></p>' +
'</form>' +
'</div>' +
'<div data-role="locality-link"></div>';
}
function mount(container) {
const kind = container.dataset.myrecoForm || container.dataset.form || 'visibility';
let selected = null;
container.innerHTML = baseTemplate(kind);
const details = container.querySelector('[data-role="details"]');
const status = container.querySelector('[data-role="status"]');
const form = container.querySelector('form');
const product = container.querySelector('[data-role="product"]');
if (product) loadProducts(product);
const storedSelection = readStoredSelection();
const showPartnerList = kind === 'visibility';
if (storedSelection && storedSelection.id) {
selected = storedSelection;
applySelection(container, selected, details, status, { showPartnerList });
}
setupSearch(container, (item) => {
selected = item;
storeSelection(item);
applySelection(container, item, details, status, { showPartnerList });
});
form.addEventListener('submit', async (event) => {
event.preventDefault();
status.className = 'form-status';
if (!selected) {
status.textContent = t.chooseFirst;
return;
}
if (!hasPublicApiContext()) {
status.textContent = publicContextMessage();
status.classList.add('error');
return;
}
const hp = container.querySelector('[data-role="hp"]').value;
const payload = { country, vertical, lang, activityId: selected.id, hp, elapsedMs: Date.now() - startedAt };
try {
if (kind === 'subscription') {
const productId = product.value;
if (!productId) {
status.textContent = t.chooseProduct;
return;
}
const json = await request('/api/v1/subscription/start', {
method: 'POST',
body: JSON.stringify({
...payload,
productId,
urlBooking: container.querySelector('[data-role="booking"]').value.trim(),
startDateISO: container.querySelector('[data-role="start"]').value
})
});
if (json.paymentUrl) {
window.location.href = json.paymentUrl;
return;
}
status.textContent = t.savedSubscription;
status.classList.add('success');
return;
}
await request('/api/v1/visibility/request', { method: 'POST', body: JSON.stringify(payload) });
status.textContent = t.savedVisibility;
status.classList.add('success');
} catch (err) {
status.textContent = t.error;
status.classList.add('error');
}
});
}
function contactTemplate() {
return '<div class="myreco-form-card contact-public-card">' +
'<div class="form-heading"><p class="eyebrow">MyReco</p><h2>' + escapeHtml(contactT.title) + '</h2><p>' + escapeHtml(contactT.intro) + '</p></div>' +
'<form class="public-form contact-public-form" novalidate>' +
'<input type="text" data-role="contact-website" tabindex="-1" autocomplete="off" class="form-hp" aria-hidden="true">' +
'<div class="field"><label>' + escapeHtml(contactT.name) + ' <span class="required">*</span><input type="text" data-role="contact-name" required autocomplete="organization"></label></div>' +
'<div class="field"><label>' + escapeHtml(contactT.address) + ' <span class="required">*</span><input type="text" data-role="contact-address" required autocomplete="street-address"></label><p class="form-hint">' + escapeHtml(contactT.addressHint) + '</p></div>' +
'<div class="field"><label>' + escapeHtml(contactT.email) + ' <span class="required">*</span><input type="email" data-role="contact-email" required autocomplete="email" inputmode="email"></label></div>' +
'<div class="field"><p class="field-label">' + escapeHtml(contactT.subject) + ' <span class="required">*</span></p><div class="checkbox-group">' + contactT.categories.map((category) => '<label class="checkbox-item"><input type="checkbox" name="contact-category" value="' + escapeHtml(category) + '"><span>' + escapeHtml(category) + '</span></label>').join('') + '</div></div>' +
'<div class="field"><label>' + escapeHtml(contactT.message) + ' <span class="required">*</span><textarea data-role="contact-message" required></textarea></label></div>' +
'<button class="button" type="submit">' + escapeHtml(contactT.submit) + '</button>' +
'<p class="form-status" data-role="contact-status"></p>' +
'</form>' +
'</div>';
}
function mountContact(container) {
container.innerHTML = contactTemplate();
const form = container.querySelector('form');
const status = container.querySelector('[data-role="contact-status"]');
const button = form.querySelector('button[type="submit"]');
form.addEventListener('submit', async (event) => {
event.preventDefault();
status.className = 'form-status';
const categories = Array.from(form.querySelectorAll('input[name="contact-category"]:checked')).map((input) => input.value);
if (!categories.length) {
status.textContent = contactT.missingCategory;
status.classList.add('error');
return;
}
if (!hasPublicApiContext()) {
status.textContent = publicContextMessage();
status.classList.add('error');
return;
}
const payload = {
nomEtablissement: form.querySelector('[data-role="contact-name"]').value.trim(),
adresseEtablissement: form.querySelector('[data-role="contact-address"]').value.trim(),
email: form.querySelector('[data-role="contact-email"]').value.trim(),
categories,
message: form.querySelector('[data-role="contact-message"]').value.trim(),
website: form.querySelector('[data-role="contact-website"]').value.trim(),
country,
lang,
vertical,
elapsedMs: Date.now() - startedAt,
sourceHost: location.host,
sourcePath: location.pathname
};
button.disabled = true;
button.textContent = contactT.sending;
status.textContent = '';
try {
const response = await fetch(contactEndpoint(), {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
});
const json = await response.json().catch(() => ({}));
if (!response.ok || json.ok === false) throw new Error(json.error || 'contact_failed');
status.textContent = contactT.success;
status.classList.add('success');
form.reset();
} catch (err) {
status.textContent = contactT.error;
status.classList.add('error');
} finally {
button.disabled = false;
button.textContent = contactT.submit;
}
});
}
function mountTariffImage(container) {
const tariffCountry = country;
if (!hasCountry) {
container.innerHTML = '';
return;
}
const family = vertical === 'ludique' ? 'activite' : 'resto';
const src = '../assets/tarif/tarif_' + family + '_' + tariffCountry + '.png';
const alt = {
fr: 'Tarifs MyReco',
es: 'Tarifas MyReco',
it: 'Tariffe MyReco',
en: 'MyReco pricing'
}[lang] || 'MyReco pricing';
container.innerHTML = '<figure class="tariff-visual-card"><img src="' + src + '" alt="' + alt + '" loading="lazy"></figure>';
}
function bindPlanLinks() {
document.querySelectorAll('[data-plan]').forEach((link) => {
link.addEventListener('click', () => {
window.setTimeout(() => {
const select = document.querySelector('[data-role="product"]');
if (select && link.dataset.plan) {
select.value = link.dataset.plan;
}
}, 250);
});
});
}
document.querySelectorAll('[data-tariff-image]').forEach(mountTariffImage);
document.querySelectorAll('[data-myreco-contact]').forEach(mountContact);
document.querySelectorAll('[data-myreco-form], [data-form]').forEach(mount);
bindPlanLinks();
})();