feat: blog modal with animation, replace trial form with product/location/webhook widget
This commit is contained in:
+1
-1
File diff suppressed because one or more lines are too long
+148
-58
@@ -140,28 +140,46 @@
|
|||||||
<li class="flex gap-3"><span class="text-brand-emerald" aria-hidden="true">•</span><span data-i18n="trial-bullet3"></span></li>
|
<li class="flex gap-3"><span class="text-brand-emerald" aria-hidden="true">•</span><span data-i18n="trial-bullet3"></span></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<form class="bg-white text-brand-ink rounded-3xl p-8 shadow-brand space-y-5" data-event="form:trial">
|
<div id="trial-form-wrapper">
|
||||||
<label class="block text-sm font-medium">
|
<form id="trial-form" class="bg-white text-brand-ink rounded-3xl p-8 shadow-brand space-y-5">
|
||||||
<span data-i18n="trial-label-email"></span>
|
<fieldset class="space-y-2">
|
||||||
<input type="email" required class="mt-2 w-full rounded-2xl border border-brand-smoke px-4 py-3 focus:outline-none focus:ring-2 focus:ring-brand-accent" data-i18n-placeholder="trial-placeholder-email" />
|
<legend class="block text-sm font-medium mb-2" data-i18n="trial-label-product">Product</legend>
|
||||||
|
<div class="flex gap-3">
|
||||||
|
<label class="flex items-center gap-2 flex-1 border border-brand-smoke rounded-2xl px-4 py-3 cursor-pointer hover:border-brand-accent transition">
|
||||||
|
<input type="radio" name="product" value="odoo_19" checked class="accent-brand-accent">
|
||||||
|
<span class="text-sm font-medium">ODOO</span>
|
||||||
</label>
|
</label>
|
||||||
|
<label class="flex items-center gap-2 flex-1 border border-brand-smoke rounded-2xl px-4 py-3 cursor-pointer hover:border-brand-accent transition">
|
||||||
|
<input type="radio" name="product" value="N8N" class="accent-brand-accent">
|
||||||
|
<span class="text-sm font-medium">N8N</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
<label class="block text-sm font-medium">
|
<label class="block text-sm font-medium">
|
||||||
<span data-i18n="trial-label-priority"></span>
|
<span data-i18n="trial-label-location">Server region</span>
|
||||||
<select class="mt-2 w-full rounded-2xl border border-brand-smoke px-4 py-3 focus:outline-none focus:ring-2 focus:ring-brand-accent">
|
<select name="location" required class="mt-2 w-full rounded-2xl border border-brand-smoke px-4 py-3 focus:outline-none focus:ring-2 focus:ring-brand-accent bg-white">
|
||||||
<option data-i18n-option="priority-1"></option>
|
<option value="" disabled selected data-i18n-option="trial-location-placeholder">Select a region</option>
|
||||||
<option data-i18n-option="priority-2"></option>
|
<option value="manchester">🇬🇧 UK, Manchester</option>
|
||||||
<option data-i18n-option="priority-3"></option>
|
<option value="boston">🇺🇸 US, Boston</option>
|
||||||
<option data-i18n-option="priority-4"></option>
|
<option value="mumbai">🇮🇳 IN, Mumbai</option>
|
||||||
|
<option value="saopaulo">🇧🇷 BR, São Paulo</option>
|
||||||
|
<option value="Meppel">🇳🇱 NL, Meppel</option>
|
||||||
</select>
|
</select>
|
||||||
</label>
|
</label>
|
||||||
<label class="block text-sm font-medium">
|
<label class="block text-sm font-medium">
|
||||||
<span data-i18n="trial-label-region"></span>
|
<span data-i18n="trial-label-email"></span>
|
||||||
<input type="text" class="mt-2 w-full rounded-2xl border border-brand-smoke px-4 py-3 focus:outline-none focus:ring-2 focus:ring-brand-accent" data-i18n-placeholder="trial-placeholder-region" />
|
<input type="email" name="email" required class="mt-2 w-full rounded-2xl border border-brand-smoke px-4 py-3 focus:outline-none focus:ring-2 focus:ring-brand-accent" data-i18n-placeholder="trial-placeholder-email" />
|
||||||
</label>
|
</label>
|
||||||
|
<input type="hidden" id="trial-affiliate" name="affiliate_code" value="NGO">
|
||||||
<p class="text-xs text-brand-ink/70" data-i18n="trial-microcopy"></p>
|
<p class="text-xs text-brand-ink/70" data-i18n="trial-microcopy"></p>
|
||||||
<button type="submit" class="w-full py-3 rounded-2xl bg-brand-accent text-brand-ink font-semibold" data-i18n="cta-trial"></button>
|
<button type="submit" class="w-full py-3 rounded-2xl bg-brand-accent hover:bg-brand-sunrise text-brand-ink font-semibold transition plausible-event-name=Trial" data-i18n="cta-trial"></button>
|
||||||
<p class="text-xs text-brand-ink/60 text-center" data-i18n="trial-help"></p>
|
<p class="text-xs text-brand-ink/60 text-center" data-i18n="trial-help"></p>
|
||||||
</form>
|
</form>
|
||||||
|
<div id="trial-status" class="hidden bg-white text-brand-ink rounded-3xl p-8 shadow-brand text-center space-y-4">
|
||||||
|
<p class="text-brand-emerald text-4xl" aria-hidden="true">✓</p>
|
||||||
|
<p id="trial-status-msg" class="text-sm text-brand-ink/80"></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
@@ -197,6 +215,27 @@
|
|||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
|
<!-- Blog post modal -->
|
||||||
|
<div id="blog-modal" role="dialog" aria-modal="true" aria-labelledby="blog-modal-title" style="display:none" class="fixed inset-0 z-50 items-center justify-center p-4">
|
||||||
|
<div id="blog-modal-backdrop" class="absolute inset-0 bg-brand-ink/80 backdrop-blur cursor-pointer"></div>
|
||||||
|
<article id="blog-modal-box" class="relative bg-white rounded-3xl shadow-brand w-full max-w-2xl max-h-[90vh] overflow-y-auto flex flex-col">
|
||||||
|
<div class="sticky top-0 bg-white rounded-t-3xl px-8 pt-6 pb-4 flex items-start justify-between gap-4 border-b border-brand-smoke z-10">
|
||||||
|
<div class="flex-1 min-w-0">
|
||||||
|
<p id="blog-modal-area" class="text-xs uppercase tracking-widest text-brand-primary mb-1"></p>
|
||||||
|
<h2 id="blog-modal-title" class="text-2xl font-semibold leading-tight text-brand-ink pr-4"></h2>
|
||||||
|
</div>
|
||||||
|
<button id="blog-modal-close-x" type="button" aria-label="Close" class="flex-shrink-0 w-12 h-12 rounded-full bg-brand-smoke flex items-center justify-center text-2xl font-bold text-brand-ink hover:bg-brand-accent hover:text-white transition">✕</button>
|
||||||
|
</div>
|
||||||
|
<div class="px-8 py-6 flex-1">
|
||||||
|
<time id="blog-modal-date" class="text-xs text-brand-ink/60 block mb-4"></time>
|
||||||
|
<div id="blog-modal-content" class="text-sm text-brand-ink/80 space-y-3"></div>
|
||||||
|
</div>
|
||||||
|
<div class="px-8 pb-8 flex justify-center">
|
||||||
|
<button id="blog-modal-close-bottom" type="button" class="px-10 py-3 rounded-full bg-brand-ink text-white font-semibold hover:bg-brand-primary transition">Close</button>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const translations = {
|
const translations = {
|
||||||
en: {
|
en: {
|
||||||
@@ -235,13 +274,10 @@
|
|||||||
'trial-bullet3': 'Guided checklists for go-live within 30 days',
|
'trial-bullet3': 'Guided checklists for go-live within 30 days',
|
||||||
'trial-label-email': 'Work email',
|
'trial-label-email': 'Work email',
|
||||||
'trial-placeholder-email': 'impact@yourngo.org',
|
'trial-placeholder-email': 'impact@yourngo.org',
|
||||||
'trial-label-priority': 'Primary priority',
|
'trial-label-product': 'Product',
|
||||||
'priority-1': 'Grant compliance reporting',
|
'trial-label-location': 'Server region',
|
||||||
'priority-2': 'Volunteer management',
|
'trial-location-placeholder': 'Select a region',
|
||||||
'priority-3': 'Donor CRM & fundraising',
|
'modal-close': 'Close',
|
||||||
'priority-4': 'Finance & procurement',
|
|
||||||
'trial-label-region': 'Country / region',
|
|
||||||
'trial-placeholder-region': 'e.g., Paraguay, LATAM',
|
|
||||||
'trial-microcopy': 'No credit card required. EU hosting & GDPR-ready.',
|
'trial-microcopy': 'No credit card required. EU hosting & GDPR-ready.',
|
||||||
'trial-help': 'Need help? Use the Schedule a Meeting button in the header to book a guided call.',
|
'trial-help': 'Need help? Use the Schedule a Meeting button in the header to book a guided call.',
|
||||||
'meeting-tagline': 'Have a meeting',
|
'meeting-tagline': 'Have a meeting',
|
||||||
@@ -295,13 +331,10 @@
|
|||||||
'trial-bullet3': 'Checklists guiados para salir a producción en 30 días',
|
'trial-bullet3': 'Checklists guiados para salir a producción en 30 días',
|
||||||
'trial-label-email': 'Correo laboral',
|
'trial-label-email': 'Correo laboral',
|
||||||
'trial-placeholder-email': 'impacto@tuong.org',
|
'trial-placeholder-email': 'impacto@tuong.org',
|
||||||
'trial-label-priority': 'Prioridad principal',
|
'trial-label-product': 'Producto',
|
||||||
'priority-1': 'Reportes de subvenciones',
|
'trial-label-location': 'Región de servidor',
|
||||||
'priority-2': 'Gestión de voluntarios',
|
'trial-location-placeholder': 'Selecciona una región',
|
||||||
'priority-3': 'CRM y recaudación',
|
'modal-close': 'Cerrar',
|
||||||
'priority-4': 'Finanzas y compras',
|
|
||||||
'trial-label-region': 'País / región',
|
|
||||||
'trial-placeholder-region': 'ej. Paraguay, LATAM',
|
|
||||||
'trial-microcopy': 'Sin tarjeta. Hosting en la UE y cumplimiento GDPR.',
|
'trial-microcopy': 'Sin tarjeta. Hosting en la UE y cumplimiento GDPR.',
|
||||||
'trial-help': '¿Dudas? Usa el botón “Agendar una reunión” en el encabezado para reservar una llamada guiada.',
|
'trial-help': '¿Dudas? Usa el botón “Agendar una reunión” en el encabezado para reservar una llamada guiada.',
|
||||||
'meeting-tagline': 'Agenda una reunión',
|
'meeting-tagline': 'Agenda una reunión',
|
||||||
@@ -355,13 +388,10 @@
|
|||||||
'trial-bullet3': 'Geführte Checklisten für Go-Live in 30 Tagen',
|
'trial-bullet3': 'Geführte Checklisten für Go-Live in 30 Tagen',
|
||||||
'trial-label-email': 'Arbeits-E-Mail',
|
'trial-label-email': 'Arbeits-E-Mail',
|
||||||
'trial-placeholder-email': 'impact@deinengo.org',
|
'trial-placeholder-email': 'impact@deinengo.org',
|
||||||
'trial-label-priority': 'Höchste Priorität',
|
'trial-label-product': 'Produkt',
|
||||||
'priority-1': 'Förder-Reporting',
|
'trial-label-location': 'Serverstandort',
|
||||||
'priority-2': 'Ehrenamt-Management',
|
'trial-location-placeholder': 'Region auswählen',
|
||||||
'priority-3': 'Spender-CRM & Fundraising',
|
'modal-close': 'Schließen',
|
||||||
'priority-4': 'Finanzen & Beschaffung',
|
|
||||||
'trial-label-region': 'Land / Region',
|
|
||||||
'trial-placeholder-region': 'z. B. Paraguay, LATAM',
|
|
||||||
'trial-microcopy': 'Keine Kreditkarte. EU-Hosting & DSGVO-konform.',
|
'trial-microcopy': 'Keine Kreditkarte. EU-Hosting & DSGVO-konform.',
|
||||||
'trial-help': 'Brauchen Sie Hilfe? Nutzen Sie den Button „Meeting planen“ im Header für einen geführten Call.',
|
'trial-help': 'Brauchen Sie Hilfe? Nutzen Sie den Button „Meeting planen“ im Header für einen geführten Call.',
|
||||||
'meeting-tagline': 'Meeting vereinbaren',
|
'meeting-tagline': 'Meeting vereinbaren',
|
||||||
@@ -459,9 +489,7 @@
|
|||||||
area.textContent = post.area || '';
|
area.textContent = post.area || '';
|
||||||
const dateEl = document.createElement('time');
|
const dateEl = document.createElement('time');
|
||||||
dateEl.className = 'text-xs text-brand-ink/60';
|
dateEl.className = 'text-xs text-brand-ink/60';
|
||||||
if (post.date) {
|
if (post.date) dateEl.dateTime = post.date;
|
||||||
dateEl.dateTime = post.date;
|
|
||||||
}
|
|
||||||
dateEl.textContent = formatBlogDate(post.date);
|
dateEl.textContent = formatBlogDate(post.date);
|
||||||
meta.append(area, dateEl);
|
meta.append(area, dateEl);
|
||||||
card.appendChild(meta);
|
card.appendChild(meta);
|
||||||
@@ -472,40 +500,57 @@
|
|||||||
card.appendChild(title);
|
card.appendChild(title);
|
||||||
|
|
||||||
const teaser = document.createElement('div');
|
const teaser = document.createElement('div');
|
||||||
teaser.className = 'text-sm text-brand-ink/80 space-y-3 mb-4';
|
teaser.className = 'text-sm text-brand-ink/80 space-y-3 mb-4 flex-1';
|
||||||
teaser.innerHTML = post.teaser || '';
|
teaser.innerHTML = post.teaser || '';
|
||||||
card.appendChild(teaser);
|
card.appendChild(teaser);
|
||||||
|
|
||||||
let contentBlock = null;
|
|
||||||
const hasMore = post.content && post.content.trim() !== (post.teaser || '').trim();
|
|
||||||
if (post.content) {
|
if (post.content) {
|
||||||
contentBlock = document.createElement('div');
|
|
||||||
contentBlock.className = 'text-sm text-brand-ink/80 space-y-3 mb-4 hidden';
|
|
||||||
contentBlock.innerHTML = post.content;
|
|
||||||
card.appendChild(contentBlock);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasMore && contentBlock) {
|
|
||||||
const btn = document.createElement('button');
|
const btn = document.createElement('button');
|
||||||
btn.type = 'button';
|
btn.type = 'button';
|
||||||
btn.className = 'text-sm font-semibold text-brand-accent underline-offset-4 hover:underline self-start';
|
btn.className = 'text-sm font-semibold text-brand-accent underline-offset-4 hover:underline self-start mt-auto';
|
||||||
btn.textContent = getTranslation(document.documentElement.lang, 'blog-read-more', 'Read more');
|
btn.textContent = getTranslation(document.documentElement.lang, 'blog-read-more', 'Read more');
|
||||||
btn.addEventListener('click', () => {
|
btn.addEventListener('click', function () { openBlogModal(post); });
|
||||||
const isHidden = contentBlock.classList.contains('hidden');
|
|
||||||
if (isHidden) {
|
|
||||||
contentBlock.classList.remove('hidden');
|
|
||||||
btn.textContent = getTranslation(document.documentElement.lang, 'blog-show-less', 'Show less');
|
|
||||||
} else {
|
|
||||||
contentBlock.classList.add('hidden');
|
|
||||||
btn.textContent = getTranslation(document.documentElement.lang, 'blog-read-more', 'Read more');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
card.appendChild(btn);
|
card.appendChild(btn);
|
||||||
}
|
}
|
||||||
|
|
||||||
return card;
|
return card;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function openBlogModal(post) {
|
||||||
|
var modal = document.getElementById('blog-modal');
|
||||||
|
var modalBox = document.getElementById('blog-modal-box');
|
||||||
|
var backdrop = document.getElementById('blog-modal-backdrop');
|
||||||
|
var locale = document.documentElement.lang || 'en';
|
||||||
|
|
||||||
|
document.getElementById('blog-modal-area').textContent = post.area || '';
|
||||||
|
|
||||||
|
var modalDateEl = document.getElementById('blog-modal-date');
|
||||||
|
modalDateEl.textContent = formatBlogDate(post.date);
|
||||||
|
if (post.date) modalDateEl.dateTime = post.date;
|
||||||
|
|
||||||
|
document.getElementById('blog-modal-title').textContent = post.title || '';
|
||||||
|
document.getElementById('blog-modal-content').innerHTML = post.content || post.teaser || '';
|
||||||
|
document.getElementById('blog-modal-close-bottom').textContent = getTranslation(locale, 'modal-close', 'Close');
|
||||||
|
|
||||||
|
modalBox.classList.remove('modal-animate-box');
|
||||||
|
backdrop.classList.remove('modal-animate-backdrop');
|
||||||
|
modal.style.display = 'flex';
|
||||||
|
document.body.style.overflow = 'hidden';
|
||||||
|
|
||||||
|
requestAnimationFrame(function () {
|
||||||
|
modalBox.classList.add('modal-animate-box');
|
||||||
|
backdrop.classList.add('modal-animate-backdrop');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeBlogModal() {
|
||||||
|
var modal = document.getElementById('blog-modal');
|
||||||
|
modal.style.display = 'none';
|
||||||
|
document.body.style.overflow = '';
|
||||||
|
document.getElementById('blog-modal-box').classList.remove('modal-animate-box');
|
||||||
|
document.getElementById('blog-modal-backdrop').classList.remove('modal-animate-backdrop');
|
||||||
|
}
|
||||||
|
|
||||||
function formatBlogDate(dateString) {
|
function formatBlogDate(dateString) {
|
||||||
if (!dateString) return '';
|
if (!dateString) return '';
|
||||||
try {
|
try {
|
||||||
@@ -563,6 +608,51 @@
|
|||||||
|
|
||||||
setLanguage('en');
|
setLanguage('en');
|
||||||
document.getElementById('year').textContent = new Date().getFullYear();
|
document.getElementById('year').textContent = new Date().getFullYear();
|
||||||
|
|
||||||
|
// Blog modal event listeners
|
||||||
|
var blogModal = document.getElementById('blog-modal');
|
||||||
|
if (blogModal) {
|
||||||
|
document.getElementById('blog-modal-close-x').addEventListener('click', closeBlogModal);
|
||||||
|
document.getElementById('blog-modal-close-bottom').addEventListener('click', closeBlogModal);
|
||||||
|
document.getElementById('blog-modal-backdrop').addEventListener('click', closeBlogModal);
|
||||||
|
document.addEventListener('keydown', function (e) {
|
||||||
|
if (e.key === 'Escape' && blogModal.style.display !== 'none') closeBlogModal();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trial form — affiliate code + webhook submission
|
||||||
|
var affiliateCode = (new URLSearchParams(window.location.search)).get('affiliate') || 'NGO';
|
||||||
|
var trialAffiliateInput = document.getElementById('trial-affiliate');
|
||||||
|
if (trialAffiliateInput) trialAffiliateInput.value = affiliateCode;
|
||||||
|
|
||||||
|
var trialForm = document.getElementById('trial-form');
|
||||||
|
var trialStatusEl = document.getElementById('trial-status');
|
||||||
|
var trialStatusMsg = document.getElementById('trial-status-msg');
|
||||||
|
|
||||||
|
if (trialForm) {
|
||||||
|
trialForm.addEventListener('submit', async function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
var btn = trialForm.querySelector('button[type="submit"]');
|
||||||
|
var origText = btn.textContent;
|
||||||
|
btn.disabled = true;
|
||||||
|
btn.textContent = '…';
|
||||||
|
try {
|
||||||
|
var res = await fetch('https://002-001-5dd6e535-4d1c-46bc-9bd9-42ad4bc5f082.odoo4projects.com/webhook/c25169c6-4234-4b47-8e74-612b9539da0a', {
|
||||||
|
method: 'POST',
|
||||||
|
body: new FormData(trialForm)
|
||||||
|
});
|
||||||
|
var text = await res.text();
|
||||||
|
trialForm.style.display = 'none';
|
||||||
|
trialStatusEl.classList.remove('hidden');
|
||||||
|
trialStatusMsg.textContent = text;
|
||||||
|
} catch (err) {
|
||||||
|
btn.disabled = false;
|
||||||
|
btn.textContent = origText;
|
||||||
|
trialStatusEl.classList.remove('hidden');
|
||||||
|
trialStatusMsg.textContent = '❌ Submission failed. Please try again.';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -15,3 +15,33 @@ body {
|
|||||||
color: #201824;
|
color: #201824;
|
||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ── Blog post modal animations ───────────────────────── */
|
||||||
|
|
||||||
|
@keyframes modal-slide-in {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: scale(0.95) translateY(20px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: scale(1) translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes modal-fade-in {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-animate-box {
|
||||||
|
animation: modal-slide-in 0.3s cubic-bezier(0.16, 1, 0.3, 1) both;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-animate-backdrop {
|
||||||
|
animation: modal-fade-in 0.2s ease both;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user