diff --git a/blog.json b/blog.json new file mode 100644 index 0000000..3a05610 --- /dev/null +++ b/blog.json @@ -0,0 +1,44 @@ +[ + { + "area": "Grant compliance", + "title": "Grant-ready NGO ERP checklist", + "teaser": "

Map every grant condition to automated workflows and never miss a reporting milestone again.

", + "content": "

Map every grant condition to automated workflows and never miss a reporting milestone again. Start by linking each donor requirement to a workflow stage, add automated reminders for supporting documents, and use a shared KPI dashboard so finance and program leads see the same data.

", + "date": "2026-03-20" + }, + { + "area": "Field operations", + "title": "Digitize volunteer onboarding", + "teaser": "

Standardize approvals, ID checks, and training with low-code flows tied directly to your ERP workspace.

", + "content": "

Standardize approvals, ID checks, and training with low-code flows tied directly to your ERP workspace. Issue dynamic checklists to each volunteer, collect signatures on mobile devices, and sync completion data back to HR and program rosters instantly.

", + "date": "2026-03-18" + }, + { + "area": "Impact storytelling", + "title": "From KPI to donor story", + "teaser": "

Turn ERP data into visual narratives that inspire board members and keep major donors engaged.

", + "content": "

Turn ERP data into visual narratives that inspire board members and keep major donors engaged. Pair outcome metrics with qualitative anecdotes, embed automated charts directly into newsletters, and reuse the same narrative blocks across social, email, and board decks.

", + "date": "2026-03-15" + }, + { + "area": "Finance automation", + "title": "Close monthly books in 48 hours", + "teaser": "

Use consolidated ledgers, smart reconciliations, and approval routing to shrink month-end crunch time.

", + "content": "

Use consolidated ledgers, smart reconciliations, and approval routing to shrink month-end crunch time. Automate multi-currency FX adjustments, surface outlier transactions for manual review, and share a read-only dashboard with leadership the moment the books are closed.

", + "date": "2026-03-12" + }, + { + "area": "Localisation", + "title": "Deliver parity across EN/ES/DE", + "teaser": "

Keep every landing page, email sequence, and CTA synchronized across languages without copy drift.

", + "content": "

Keep every landing page, email sequence, and CTA synchronized across languages without copy drift. Store shared messaging pillars in your ERP knowledge base, tag assets by language, and schedule automated QA passes before campaigns launch.

", + "date": "2026-03-10" + }, + { + "area": "Data strategy", + "title": "Build an NGO impact warehouse", + "teaser": "

Consolidate donor, program, and finance data so every report starts from a trustworthy baseline.

", + "content": "

Consolidate donor, program, and finance data so every report starts from a trustworthy baseline. Use ELT pipelines into an affordable warehouse, enforce a shared taxonomy for indicators, and expose curated models to analysts and program teams alike.

", + "date": "2026-03-08" + } +] diff --git a/index.html b/index.html index e4984d8..08dc623 100644 --- a/index.html +++ b/index.html @@ -144,28 +144,10 @@

- - -
-
-

-

-

- -
-
-

-

-

- -
-
-

-

-

- -
+
+
+ @@ -264,21 +246,10 @@ 'blog-title': 'Blog & resource hub', 'blog-subtitle': 'Stay ahead with tutorials, case studies, and playbooks for NGO ERP rollouts.', 'blog-link': 'Browse all articles →', - 'blog1-tag': 'Grant compliance', - 'blog1-title': 'Grant-ready NGO ERP checklist', - 'blog1-desc': 'Map every grant condition to automated workflows and never miss a reporting milestone again.', - 'blog1-link': 'Read the guide', - 'blog1-aria': 'Read the grant-ready NGO ERP checklist', - 'blog2-tag': 'Field operations', - 'blog2-title': 'Digitize volunteer onboarding', - 'blog2-desc': 'Standardize approvals, ID checks, and training with low-code workflows tied to your ERP.', - 'blog2-link': 'View playbook', - 'blog2-aria': 'Read the volunteer onboarding playbook', - 'blog3-tag': 'Impact storytelling', - 'blog3-title': 'From KPI to donor story', - 'blog3-desc': 'Turn ERP data into visual narratives that inspire donors and board members.', - 'blog3-link': 'See examples', - 'blog3-aria': 'See donor storytelling examples', + 'blog-all-open': 'Showing all articles', + 'blog-read-more': 'Read more', + 'blog-show-less': 'Show less', + 'blog-error': 'Unable to load articles right now.', 'trial-tagline': 'Sign up free trial', 'trial-heading': 'Launch your NGO ERP sandbox in under 5 minutes', 'trial-body': 'Experience localized NGO templates, donor CRM, and automation recipes. No credit card required—just choose your focus area and invite teammates.', @@ -335,21 +306,10 @@ 'blog-title': 'Blog y centro de recursos', 'blog-subtitle': 'Aprende con tutoriales, casos prácticos y playbooks para implementar un ERP en tu ONG.', 'blog-link': 'Ver todos los artículos →', - 'blog1-tag': 'Cumplimiento', - 'blog1-title': 'Checklist ERP listo para subvenciones', - 'blog1-desc': 'Relaciona cada requisito de la subvención con flujos automatizados y evita retrasos en los reportes.', - 'blog1-link': 'Leer la guía', - 'blog1-aria': 'Leer la guía ERP para subvenciones', - 'blog2-tag': 'Operaciones de campo', - 'blog2-title': 'Digitaliza la incorporación de voluntarios', - 'blog2-desc': 'Estandariza aprobaciones, verificaciones e inducciones con flujos low-code conectados a tu ERP.', - 'blog2-link': 'Ver playbook', - 'blog2-aria': 'Ver el playbook de voluntariado', - 'blog3-tag': 'Narrativas de impacto', - 'blog3-title': 'Del KPI a la historia del donante', - 'blog3-desc': 'Convierte datos del ERP en historias visuales que inspiran a donantes y juntas directivas.', - 'blog3-link': 'Ver ejemplos', - 'blog3-aria': 'Ver ejemplos de historias para donantes', + 'blog-all-open': 'Mostrando todos los artículos', + 'blog-read-more': 'Leer más', + 'blog-show-less': 'Mostrar menos', + 'blog-error': 'No pudimos cargar los artículos en este momento.', 'trial-tagline': 'Activa tu prueba', 'trial-heading': 'Lanza tu sandbox ERP para ONG en menos de 5 minutos', 'trial-body': 'Explora plantillas localizadas, CRM de donantes y recetas de automatización. Sin tarjeta; solo elige tu prioridad e invita al equipo.', @@ -406,21 +366,10 @@ 'blog-title': 'Blog & Wissenshub', 'blog-subtitle': 'Tutorials, Fallstudien und Playbooks für erfolgreiche NGO-ERP-Rollouts.', 'blog-link': 'Alle Artikel ansehen →', - 'blog1-tag': 'Förder-Compliance', - 'blog1-title': 'ERP-Checkliste für Fördermittel', - 'blog1-desc': 'Verknüpfe jede Auflage mit automatisierten Workflows und vermeide verspätete Berichte.', - 'blog1-link': 'Zur Anleitung', - 'blog1-aria': 'ERP-Checkliste für Fördermittel lesen', - 'blog2-tag': 'Feldeinsätze', - 'blog2-title': 'Ehrenamtliche digital onboarden', - 'blog2-desc': 'Standardisiere Freigaben, ID-Prüfungen und Schulungen mit Low-Code-Flows im ERP.', - 'blog2-link': 'Playbook ansehen', - 'blog2-aria': 'Playbook zur Ehrenamtlichen-Onboarding ansehen', - 'blog3-tag': 'Impact Storytelling', - 'blog3-title': 'Vom KPI zur Spenderstory', - 'blog3-desc': 'Mach aus ERP-Daten visuelle Geschichten, die Spender und Boards überzeugen.', - 'blog3-link': 'Beispiele ansehen', - 'blog3-aria': 'Beispiele für Spenderstorys ansehen', + 'blog-all-open': 'Alle Artikel werden angezeigt', + 'blog-read-more': 'Mehr lesen', + 'blog-show-less': 'Weniger anzeigen', + 'blog-error': 'Artikel konnten gerade nicht geladen werden.', 'trial-tagline': 'Kostenlos testen', 'trial-heading': 'Starte dein NGO-ERP-Sandbox in unter 5 Minuten', 'trial-body': 'Teste lokalisierte Templates, Donor-CRM und Automatisierungen. Keine Kreditkarte – wähle den Fokus und lade dein Team ein.', @@ -455,6 +404,140 @@ } }; + const BLOG_LIMIT = 3; + const blogGrid = document.getElementById('blog-grid'); + const blogToggle = document.getElementById('blog-toggle'); + const blogError = document.getElementById('blog-error'); + let blogPosts = []; + let blogExpanded = false; + + if (blogGrid) { + fetch('blog.json') + .then(response => response.json()) + .then(data => { + blogPosts = Array.isArray(data) ? data : []; + renderBlogCards(); + }) + .catch(error => { + if (blogError) { + blogError.classList.remove('hidden'); + blogError.textContent = getTranslation(document.documentElement.lang, 'blog-error', 'Unable to load articles right now.'); + } + console.error('blog.json load failed', error); + }); + } + + if (blogToggle) { + blogToggle.addEventListener('click', () => { + if (blogExpanded || blogPosts.length <= BLOG_LIMIT) return; + blogExpanded = true; + renderBlogCards(); + }); + } + + function getTranslation(locale, key, fallback) { + return (translations[locale] && translations[locale][key]) ?? translations.en[key] ?? fallback; + } + + function renderBlogCards() { + if (!blogGrid) { + return; + } + if (!blogPosts.length) { + blogGrid.innerHTML = ''; + updateBlogToggleLabel(); + return; + } + const limit = blogExpanded ? blogPosts.length : Math.min(BLOG_LIMIT, blogPosts.length); + blogGrid.innerHTML = ''; + blogPosts.slice(0, limit).forEach(post => { + blogGrid.appendChild(createBlogCard(post)); + }); + updateBlogToggleLabel(); + } + + function createBlogCard(post) { + const card = document.createElement('article'); + card.className = 'border border-brand-smoke rounded-3xl p-6 h-full flex flex-col'; + + const meta = document.createElement('div'); + meta.className = 'flex items-center justify-between mb-3'; + const area = document.createElement('p'); + area.className = 'text-xs uppercase tracking-widest text-brand-primary'; + area.textContent = post.area || ''; + const dateEl = document.createElement('time'); + dateEl.className = 'text-xs text-brand-ink/60'; + if (post.date) { + dateEl.dateTime = post.date; + } + dateEl.textContent = formatBlogDate(post.date); + meta.append(area, dateEl); + card.appendChild(meta); + + const title = document.createElement('h3'); + title.className = 'text-xl font-semibold mb-3'; + title.textContent = post.title || ''; + card.appendChild(title); + + const teaser = document.createElement('div'); + teaser.className = 'text-sm text-brand-ink/80 space-y-3 mb-4'; + teaser.innerHTML = post.teaser || ''; + card.appendChild(teaser); + + let contentBlock = null; + const hasMore = post.content && post.content.trim() !== (post.teaser || '').trim(); + 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'); + btn.type = 'button'; + btn.className = 'text-sm font-semibold text-brand-accent underline-offset-4 hover:underline self-start'; + btn.textContent = getTranslation(document.documentElement.lang, 'blog-read-more', 'Read more'); + btn.addEventListener('click', () => { + 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); + } + + return card; + } + + function formatBlogDate(dateString) { + if (!dateString) return ''; + try { + const locale = document.documentElement.lang || 'en'; + return new Intl.DateTimeFormat(locale, { year: 'numeric', month: 'short', day: 'numeric' }).format(new Date(dateString)); + } catch (error) { + return dateString; + } + } + + function updateBlogToggleLabel() { + if (!blogToggle) return; + if (blogPosts.length <= BLOG_LIMIT) { + blogToggle.classList.add('hidden'); + return; + } + blogToggle.classList.remove('hidden'); + blogToggle.disabled = blogExpanded; + const locale = document.documentElement.lang || 'en'; + blogToggle.textContent = blogExpanded + ? getTranslation(locale, 'blog-all-open', 'Showing all articles') + : getTranslation(locale, 'blog-link', 'Browse all articles →'); + } + function setLanguage(lang) { const locale = translations[lang] ? lang : 'en'; document.documentElement.lang = locale; @@ -478,6 +561,8 @@ const value = translations[locale][key] ?? translations.en[key] ?? ''; if (value !== undefined) el.setAttribute('aria-label', value); }); + renderBlogCards(); + updateBlogToggleLabel(); } document.getElementById('lang-selector').addEventListener('change', (event) => {