Files
IDE-Demo-B/js/main.js
T
2026-09-09 21:16:40 -03:00

889 lines
33 KiB
JavaScript

/**
* IDE CONSULTORES - SCRIPT INSTITUCIONAL (JS ÚNICO)
* Maneja includes modulares, tema claro/oscuro, Brújula IDE, modales, catálogo de cursos, filtros y formularios.
*/
document.addEventListener('DOMContentLoaded', () => {
initApp();
});
async function initApp() {
initTheme();
await loadIncludes();
initModals();
/*initBrujulaIDE();*/
initAcademyCourses();
initSolucionesFilters();
initInsightsFeed();
initContactForm();
initCareersForm();
}
/* ==========================================================================
1. Carga Modular de Header y Footer (JS Nativo)
========================================================================== */
async function loadIncludes() {
const headerElem = document.getElementById('site-header');
const footerElem = document.getElementById('site-footer');
try {
if (headerElem && headerElem.children.length === 0) {
const headerRes = await fetch('./includes/header.html', { cache: 'no-store' });
if (headerRes.ok) {
headerElem.innerHTML = await headerRes.text();
highlightActiveNavLink();
attachHeaderEvents();
} else {
// ← NUEVO: Si el fetch falla (ej: 404), igual inicializar eventos
highlightActiveNavLink();
attachHeaderEvents();
}
} else if (headerElem) {
highlightActiveNavLink();
attachHeaderEvents();
}
if (footerElem) {
const footerRes = await fetch('./includes/footer.html', { cache: 'no-store' });
if (footerRes.ok) {
footerElem.innerHTML = await footerRes.text();
}
}
} catch (err) {
console.warn('Header modular no disponible:', err);
// ← NUEVO: Intentar inicializar igual si el header ya tiene contenido estático
if (headerElem && headerElem.children.length > 0) {
highlightActiveNavLink();
attachHeaderEvents();
}
}
}
function highlightActiveNavLink() {
const currentPath = window.location.pathname;
const navLinks = document.querySelectorAll('.nav-link');
navLinks.forEach(link => {
const href = link.getAttribute('href');
if (!href) return;
// Comparación de página activa
if (
(currentPath === '/' || currentPath.endsWith('index.html')) && (href === '/' || href === 'index.html' || href === './') ||
(currentPath.includes(href) && href !== '/' && href !== 'index.html')
) {
link.classList.add('active');
} else {
link.classList.remove('active');
}
});
}
function attachHeaderEvents() {
// Theme toggle
const themeToggle = document.getElementById('theme-toggle-btn');
if (themeToggle) {
const newThemeToggle = themeToggle.cloneNode(true);
themeToggle.parentNode.replaceChild(newThemeToggle, themeToggle);
newThemeToggle.addEventListener('click', toggleTheme);
}
const drawer = document.getElementById('mobile-menu-drawer');
const overlay = document.getElementById('mobile-menu-overlay');
function openDrawer() {
if (!drawer) return;
drawer.classList.add('is-open');
drawer.setAttribute('aria-hidden', 'false');
if (overlay) overlay.classList.add('is-open');
document.body.style.overflow = 'hidden'; // evita scroll del fondo
const btn = document.getElementById('mobile-menu-btn');
if (btn) btn.setAttribute('aria-expanded', 'true');
}
function closeDrawer() {
if (!drawer) return;
drawer.classList.remove('is-open');
drawer.setAttribute('aria-hidden', 'true');
if (overlay) overlay.classList.remove('is-open');
document.body.style.overflow = '';
const btn = document.getElementById('mobile-menu-btn');
if (btn) btn.setAttribute('aria-expanded', 'false');
}
// Botón hamburguesa abre
document.getElementById('mobile-menu-btn')
?.addEventListener('click', openDrawer);
// Botón X dentro del drawer cierra
document.getElementById('drawer-close-btn')
?.addEventListener('click', closeDrawer);
// Clic en overlay cierra
if (overlay) overlay.addEventListener('click', closeDrawer);
// Clic en cualquier link del drawer cierra
drawer?.querySelectorAll('.nav-link').forEach(link => {
link.addEventListener('click', closeDrawer);
});
// Tecla ESC cierra
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') closeDrawer();
});
const currentTheme = document.documentElement.getAttribute('data-theme') || 'light';
toggleLogoTheme(currentTheme);
updateThemeIcon(currentTheme);
}
/* ==========================================================================
2. Selector de Modo Claro / Modo Oscuro con Persistencia
========================================================================== */
function initTheme() {
const savedTheme = localStorage.getItem('ide_theme') || 'light';
document.documentElement.setAttribute('data-theme', savedTheme);
updateThemeIcon(savedTheme);
toggleLogoTheme(savedTheme);
}
function toggleTheme() {
const currentTheme = document.documentElement.getAttribute('data-theme') || 'light';
const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
document.documentElement.setAttribute('data-theme', newTheme);
localStorage.setItem('ide_theme', newTheme);
updateThemeIcon(newTheme);
toggleLogoTheme(newTheme);
const themeBtns = document.querySelectorAll('#theme-toggle-btn');
themeBtns.forEach(btn => {
btn.setAttribute('aria-label', newTheme === 'dark' ? 'Cambiar a modo claro' : 'Cambiar a modo oscuro');
});
}
function updateThemeIcon(theme) {
const themeBtns = document.querySelectorAll('#theme-toggle-btn');
themeBtns.forEach(btn => {
btn.setAttribute('aria-label', theme === 'dark' ? 'Cambiar a modo claro' : 'Cambiar a modo oscuro');
});
}
function toggleLogoTheme(theme) {
const logoLight = document.querySelector('.logo-light-only');
const logoDark = document.querySelector('.logo-dark-only');
if (logoLight && logoDark) {
if (theme === 'dark') {
logoLight.style.display = 'none';
logoDark.style.display = 'block';
} else {
logoLight.style.display = 'block';
logoDark.style.display = 'none';
}
}
}
/* ==========================================================================
4. Sistema de Modales (Pop-ups con Cierre por Clic Externo y ESC)
========================================================================== */
function initModals() {
const modalBackdrops = document.querySelectorAll('.modal-backdrop');
modalBackdrops.forEach(backdrop => {
// Cerrar al hacer clic en el backdrop (fuera de la ventana)
backdrop.addEventListener('click', (e) => {
if (e.target === backdrop) {
closeModal(backdrop.id);
}
});
// Botones de cierre interno
const closeBtns = backdrop.querySelectorAll('.modal-close-btn, [data-close-modal]');
closeBtns.forEach(btn => {
btn.addEventListener('click', () => {
closeModal(backdrop.id);
});
});
});
// Cerrar con tecla ESC
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
const activeModal = document.querySelector('.modal-backdrop.is-active');
if (activeModal) {
closeModal(activeModal.id);
}
}
});
// Triggers de modales genéricos en páginas (ej: desafíos)
document.querySelectorAll('[data-open-modal]').forEach(trigger => {
trigger.addEventListener('click', (e) => {
e.preventDefault();
const modalId = trigger.getAttribute('data-open-modal');
openModal(modalId);
});
});
}
function openModal(modalId) {
const modal = document.getElementById(modalId);
if (modal) {
modal.classList.add('is-active');
document.body.style.overflow = 'hidden'; // Evita scroll de fondo
}
}
function closeModal(modalId) {
const modal = document.getElementById(modalId);
if (modal) {
modal.classList.remove('is-active');
document.body.style.overflow = '';
}
}
/* ================================================================
JS QUIZ
================================================================ */
(function () {
// ----- FLUJO DE NAVEGACIÓN (Opción B) -----
const FLOW = {
1: { desafio: '2a', objetivo: '2b', solucion: '2c', conocimiento: '2d' },
'2a': 3, '2b': 3, '2c': 3, '2d': 3,
3: 4,
4: 'result'
};
// ----- MAPEO DE RESULTADOS (Opción B) -----
function buildResult(answers) {
const p1 = answers[1];
const p2 = answers['2a'] || answers['2b'] || answers['2c'] || answers['2d'];
const p3 = answers[3];
// Perfil 4 · Requisito exigido
if (p3 === 'requisito' || p2 === 'iso' || p2 === 'iso-form') {
return {
title: 'La certificación puede ser el punto de partida.',
body: 'Comprendemos que tenés un requisito externo. En IDE no trabajamos para cumplir auditorías: trabajamos para que el sistema refleje cómo funciona tu organización y contribuya a gestionarla mejor.',
chips: ['IDE Excellence', 'ISO 9001 / 14001 / 45001', 'Sistemas de gestión'],
cta: 'Ver soluciones Excellence',
url: 'soluciones.html'
};
}
// Perfil 5 · Conocimiento o formación
if (p1 === 'conocimiento') {
const map = {
'iso-form': { chips: ['Auditoría interna', 'ISO 9001', 'Mejora continua'], extra: 'IDE Excellence' },
'pm-form': { chips: ['Gestión de proyectos', 'Metodologías ágiles', 'PMP'], extra: 'IDE Project' },
'liderazgo-form': { chips: ['Liderazgo', 'Cambio organizacional', 'Cultura'], extra: 'IDE Academy' },
'ia-form': { chips: ['IA aplicada', 'Transformación digital', 'Automatización'], extra: 'IDE Digital' }
};
const m = map[p2] || { chips: ['Formación', 'Desarrollo', 'Academy'], extra: 'IDE Academy' };
return {
title: 'Desarrollamos las capacidades que hacen posible la transformación.',
body: 'IDE Academy diseña programas de formación alineados con los desafíos reales de las organizaciones. No dictamos cursos estándar: trabajamos desde el contexto de cada equipo.',
chips: [...m.chips, m.extra],
cta: 'Conocer IDE Academy',
url: 'academy.html'
};
}
// Perfil 3 · Sabe exactamente qué necesita
if (p1 === 'solucion') {
const map = {
iso: {
title: 'Respuesta concreta. Y algo más.',
chips: ['ISO 9001', 'ISO 14001', 'IDE Excellence'],
cta: 'Ver soluciones ISO',
url: 'soluciones.html',
body: 'Sí, implementamos y acompañamos certificaciones ISO. Y creemos que un sistema de gestión debe reflejar cómo funciona tu organización, no solo superar una auditoría.'
},
pmo: {
title: 'Proyectos que llegan a destino.',
chips: ['PMO', 'Gestión de proyectos', 'IDE Project'],
cta: 'Ver soluciones Project',
url: 'soluciones.html',
body: 'Diseñamos PMOs y acompañamos la dirección de proyectos para que se entreguen en tiempo, costo y con los resultados esperados.'
},
formacion: {
title: 'Formación desde el contexto real.',
chips: ['In Company', 'Capacitación', 'IDE Academy'],
cta: 'Ver IDE Academy',
url: 'academy.html',
body: 'Programas de formación diseñados para tu organización, sus desafíos y las capacidades que necesita desarrollar.'
},
'ia-digital': {
title: 'Tecnología con propósito.',
chips: ['Transformación digital', 'IA aplicada', 'IDE Digital'],
cta: 'Ver soluciones Digital',
url: 'soluciones.html',
body: 'Te ayudamos a identificar dónde la IA y la digitalización generan valor real, y acompañamos su implementación de principio a fin.'
}
};
const m = map[p2] || {
title: 'Encontramos lo que buscás.',
chips: ['Soluciones concretas', 'Ecosistema IDE'],
cta: 'Ver soluciones',
url: 'soluciones.html',
body: 'Tenemos la respuesta concreta que buscás. Y una mirada que asegura que esa solución genere valor en el conjunto de tu organización.'
};
return { icon: './assets/iconos/engranaje_32.svg', profile: 'Perfil · Solución concreta', ...m };
}
// Perfil 2 · Sabe qué quiere lograr
if (p1 === 'objetivo') {
const map = {
'mejorar-gestion': { chips: ['IDE Excellence', 'Mejora de procesos'], body: 'Entendemos dónde querés llegar. Antes de proponer una solución, comprendemos tu operación para intervenir donde hay mayor impacto.' },
'profesionalizar-proyectos':{ chips: ['IDE Project', 'PMO'], body: 'Traducimos tu objetivo en una estructura de gestión de proyectos adecuada a tu contexto, sin sobreingeniería.' },
'desarrollar-equipo': { chips: ['IDE Academy', 'Formación In Company'], body: 'Desarrollamos capacidades alineadas con los desafíos reales de tu organización. La formación parte del contexto, no de un catálogo estándar.' },
'transformar-digital': { chips: ['IDE Digital', 'IA aplicada'], body: 'Empezamos por entender tu organización para identificar dónde la tecnología realmente agrega valor, y avanzamos desde ahí.' }
};
const m = map[p2] || { chips: ['Ecosistema IDE'], body: 'Entendemos dónde querés llegar. Comprendemos el sistema para ayudarte a lograrlo.' };
return {
title: 'Entendemos dónde querés llegar. Te ayudamos a definir cómo.',
body: m.body,
chips: m.chips,
cta: 'Explorar por desafío',
url: 'desafios.html'
};
}
// Perfil 1 · Desafío sin solución definida
const mapD = {
gestion: { chips: ['Procesos', 'Gestión', 'IDE Excellence'] },
proyectos: { chips: ['Proyectos', 'PMO', 'IDE Project'] },
personas: { chips: ['Capacidades', 'Equipo', 'IDE Academy'] },
tecnologia: { chips: ['IA', 'Digital', 'IDE Digital'] },
nosabe: { chips: ['Diagnóstico sistémico', 'Ecosistema IDE'] }
};
const md = mapD[p2] || mapD.nosabe;
return {
title: 'No necesitás tener la respuesta para empezar.',
body: 'Empezamos por comprender tu situación, no por ofrecerte una solución predeterminada. Eso diferencia una intervención puntual de una transformación real.',
chips: md.chips,
cta: 'Explorar desafíos',
url: 'desafios.html'
};
}
// ----- ESTADO -----
let currentStep = 1;
let stepHistory = [];
let answers = {};
const TOTAL = 4;
const container = document.getElementById('quiz-interactive-app');
if (!container) return;
// ----- FUNCIONES -----
function showStep(id) {
const steps = container.querySelectorAll('.quiz-step');
steps.forEach(s => s.classList.remove('active'));
const el = document.getElementById('step-' + id);
if (el) el.classList.add('active');
updateProgress(id);
updateIndicator(id);
}
function updateProgress(id) {
const fill = document.getElementById('quiz-progress-fill');
if (!fill) return;
let n = 1;
if (id === 'result') n = 4;
else if (['2a', '2b', '2c', '2d'].includes(String(id))) n = 2;
else n = parseInt(id) || 1;
fill.style.width = (n / TOTAL * 100) + '%';
}
function updateIndicator(id) {
const label = document.getElementById('quiz-step-indicator');
if (!label) return;
let text = '';
if (id === 'result') text = '¡Orientación lista!';
else if (['2a', '2b', '2c', '2d'].includes(String(id))) text = 'Paso 2 de 4';
else text = 'Paso ' + id + ' de 4';
label.textContent = text;
}
function renderResult(ans) {
const r = buildResult(ans);
document.getElementById('resultTitle').textContent = r.title;
document.getElementById('resultBody').textContent = r.body;
const chipsContainer = document.getElementById('resultChips');
chipsContainer.innerHTML = r.chips.map(c =>
`<span class="result-chip">${c}</span>`
).join('');
const cta = document.getElementById('resultCTA');
cta.textContent = r.cta;
cta.href = r.url;
showStep('result');
}
function handleOption(btn) {
const step = btn.dataset.step;
const val = btn.dataset.val;
// Deseleccionar otras opciones del mismo paso
const parent = btn.closest('.quiz-options-grid');
if (parent) {
parent.querySelectorAll('.quiz-option-card').forEach(b => b.classList.remove('selected'));
}
btn.classList.add('selected');
answers[step] = val;
stepHistory.push(step);
const next = FLOW[step];
if (!next) return;
if (next === 'result') {
renderResult(answers);
return;
}
const nextId = typeof next === 'object' ? (next[val] || '2a') : next;
showStep(nextId);
}
// ----- FUNCIONES GLOBALES (para onclick en HTML) -----
window.goBack = function () {
if (!stepHistory.length) return;
const prev = stepHistory.pop();
delete answers[prev];
const last = stepHistory[stepHistory.length - 1] || 1;
showStep(last);
};
window.restartQuiz = function () {
answers = {};
stepHistory = [];
container.querySelectorAll('.quiz-option-card').forEach(b => b.classList.remove('selected'));
showStep(1);
};
// ----- INIT -----
function initQuiz() {
// Eventos en las cards de opción
container.querySelectorAll('.quiz-option-card').forEach(btn => {
btn.addEventListener('click', function () {
handleOption(this);
});
});
showStep(1);
}
// Ejecutar al cargar el DOM
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initQuiz);
} else {
initQuiz();
}
})();
/* ==========================================================================
6. Carga Dinámica de Cursos en IDE Academy (cursos.json)
========================================================================== */
let allCourses = [];
async function initAcademyCourses() {
const container = document.getElementById('academy-courses-container');
if (!container) return;
try {
const res = await fetch('/data/cursos.json');
if (!res.ok) throw new Error('No se pudo cargar cursos.json');
allCourses = await res.json();
renderCourseCards(allCourses, container);
} catch (err) {
console.warn('Cargando cursos con datos de respaldo.');
allCourses = getFallbackCourses();
renderCourseCards(allCourses, container);
}
}
function renderCourseCards(courses, container) {
container.innerHTML = '';
courses.forEach(course => {
const card = document.createElement('div');
card.className = 'card';
card.innerHTML = `
<div class="ecosystem-badge badge-academy" style="margin-bottom: 0.8rem;">${course.categoria}</div>
<h3 style="font-size: 1.15rem; margin-bottom: 0.6rem;">${course.titulo}</h3>
<p style="font-size: 0.88rem; color: var(--text-muted); margin-bottom: 1.25rem; line-height: 1.5;">${course.descripcion_corta}</p>
<div style="font-size: 0.82rem; color: var(--text-body); display: flex; flex-direction: column; gap: 0.35rem; margin-bottom: 1.5rem; border-top: 1px solid var(--border-color); padding-top: 0.85rem;">
<div><strong>Modalidad:</strong> ${course.modalidad}</div>
<div><strong>Duración:</strong> ${course.duracion}</div>
<div><strong>Próxima fecha:</strong> ${course.proxima_fecha}</div>
</div>
<button class="btn btn-outline btn-sm w-full" style="width: 100%;" onclick="openCourseModal('${course.id}')">
Ver Ficha Técnica y Programa
</button>
`;
container.appendChild(card);
});
}
// Modal dinámico de cursos
window.openCourseModal = function (courseId) {
const course = allCourses.find(c => c.id === courseId);
if (!course) return;
const titleElem = document.getElementById('modal-course-title');
const catElem = document.getElementById('modal-course-category');
const detailsElem = document.getElementById('modal-course-details');
const ctaElem = document.getElementById('modal-course-cta');
if (titleElem) titleElem.innerText = course.titulo;
if (catElem) catElem.innerText = `${course.categoria} • ${course.modalidad} • ${course.duracion}`;
if (detailsElem) {
const objHtml = course.objetivos.map(obj => `<li>${obj}</li>`).join('');
const temHtml = course.temario.map(tem => `<li>${tem}</li>`).join('');
detailsElem.innerHTML = `
<div style="margin-bottom: 1.5rem;">
<h4 style="font-size: 1rem; color: var(--ide-primary); margin-bottom: 0.4rem;">Propuesta Formativa</h4>
<p style="font-size: 0.92rem; color: var(--text-body);">${course.propuesta}</p>
</div>
<div style="margin-bottom: 1.5rem;">
<h4 style="font-size: 1rem; color: var(--ide-primary); margin-bottom: 0.4rem;">Objetivos del Programa</h4>
<ul style="padding-left: 1.25rem; font-size: 0.9rem; color: var(--text-body); line-height: 1.6;">
${objHtml}
</ul>
</div>
<div style="margin-bottom: 1.5rem;">
<h4 style="font-size: 1rem; color: var(--ide-primary); margin-bottom: 0.4rem;">Temario Modular</h4>
<ul style="padding-left: 1.25rem; font-size: 0.9rem; color: var(--text-body); line-height: 1.6;">
${temHtml}
</ul>
</div>
<div style="background-color: var(--bg-subtle); padding: 1rem; border-radius: var(--radius-sm); font-size: 0.88rem; margin-bottom: 1.5rem;">
<p><strong>Cuerpo Docente:</strong> ${course.docente}</p>
<p><strong>Impacto Esperado:</strong> ${course.resultados_esperados}</p>
</div>
`;
}
if (ctaElem) {
const params = new URLSearchParams({
origen: 'IDE_Academy',
busqueda: 'Formación para mi organización',
curso: course.titulo
});
ctaElem.setAttribute('href', `contacto.html?${params.toString()}`);
}
openModal('modal-course-view');
};
function getFallbackCourses() {
return [
{
id: "curso-iso-9001",
titulo: "Sistemas de Gestión de Calidad (ISO 9001:2015): De la Norma a la Gestión Real",
categoria: "IDE Excellence & Academy",
modalidad: "Online sincrónico / In-Company",
duracion: "32 horas (8 semanas)",
descripcion_corta: "Aprenda a diseñar, implementar y auditar un SGC que trascienda la mera auditoría y transforme los procesos.",
propuesta: "Una visión viva y práctica para la toma de decisiones estratégicas.",
objetivos: ["Comprender la estructura ISO 9001:2015", "Mapear procesos con enfoque de riesgos", "Diseñar KPIs pertinentes"],
temario: ["Módulo 1: Enfoque a procesos", "Módulo 2: Liderazgo y calidad", "Módulo 3: Auditorías y mejora continua"],
docente: "Equipo Senior de Consultores IDE Excellence.",
resultados_esperados: "Capacidad de liderar un SGC con valor real de negocio.",
proxima_fecha: "Octubre 2026"
}
];
}
/* ==========================================================================
7. Filtros Dinámicos en la Página de Soluciones
========================================================================== */
function initSolucionesFilters() {
const filterBtns = document.querySelectorAll('.soluciones-filter-btn');
const solutionCards = document.querySelectorAll('.solution-card');
console.log('🔍 Filtros encontrados:', filterBtns.length);
console.log('📦 Tarjetas encontradas:', solutionCards.length);
if (!filterBtns.length) {
console.warn('⚠️ No se encontraron botones de filtro');
return;
}
filterBtns.forEach(btn => {
btn.addEventListener('click', () => {
filterBtns.forEach(b => b.classList.remove('active'));
btn.classList.add('active');
const filter = btn.getAttribute('data-filter');
console.log('🎯 Filtro seleccionado:', filter);
solutionCards.forEach(card => {
const cat = card.getAttribute('data-category');
console.log(` 📌 Tarjeta: ${cat} → ${filter === 'all' || cat === filter ? 'MOSTRAR' : 'OCULTAR'}`);
if (filter === 'all' || cat === filter) {
card.classList.remove('hidden');
} else {
card.classList.add('hidden');
}
});
});
});
}
/* ==========================================================================
8. Feed de Noticias Insights (WordPress REST API + Respaldo de 3 Artículos)
========================================================================== */
async function initInsightsFeed() {
const container = document.getElementById('insights-feed-container');
if (!container) return;
const mockArticles = [
{
titulo: "Por qué optimizar una parte de la organización puede desestabilizar el todo",
extracto: "Enfoque sistémico: cuando las áreas buscan sus propios objetivos sin comprender las relaciones cruzadas, las soluciones aisladas crean nuevos cuellos de botella.",
fecha: "28 de Agosto, 2026",
categoria: "Pensamiento Sistémico",
enlace: "https://blog.ideconsultores.com.ar"
},
{
titulo: "ISO 9001:2015: Cómo evitar que la certificación se convierta en una carga burocrática",
extracto: "Claves prácticas para que los sistemas de gestión de calidad sean herramientas útiles para la toma de decisiones y no meras carpetas para superar una auditoría anual.",
fecha: "15 de Agosto, 2026",
categoria: "Gestión & Calidad",
enlace: "https://blog.ideconsultores.com.ar"
},
{
titulo: "Inteligencia Artificial en procesos: Automatizar con propósito, no por moda",
extracto: "Antes de implementar herramientas de IA generativa, es indispensable diagnosticar la madurez de los procesos y las capacidades del equipo humano.",
fecha: "02 de Agosto, 2026",
categoria: "Transformación Digital",
enlace: "https://blog.ideconsultores.com.ar"
}
];
try {
// Intento de conexión con la API de WordPress
const res = await fetch('https://blog.ideconsultores.com.ar/wp-json/wp/v2/posts?_embed&per_page=3', {
method: 'GET',
headers: { 'Accept': 'application/json' }
});
if (res.ok) {
const posts = await res.json();
if (posts && posts.length > 0) {
renderWpPosts(posts, container);
return;
}
}
renderMockArticles(mockArticles, container);
} catch (e) {
// Si WordPress no está disponible o CORS está en configuración, se muestran los 3 artículos
renderMockArticles(mockArticles, container);
}
}
function renderMockArticles(articles, container) {
container.innerHTML = articles.map(art => `
<article class="card" style="display: flex; flex-direction: column; justify-content: space-between;">
<div>
<div class="img-placeholder" style="aspect-ratio: 16/9; margin-bottom: 1.25rem; padding: 1.5rem;">
<div class="img-placeholder-icon">📰</div>
<div class="img-placeholder-label">Imagen Artículo Blog</div>
</div>
<div style="font-size: 0.8rem; font-weight: 700; color: var(--ide-accent); margin-bottom: 0.4rem; text-transform: uppercase;">
${art.categoria} • <span style="color: var(--text-muted); font-weight: 400;">${art.fecha}</span>
</div>
<h3 style="font-size: 1.2rem; margin-bottom: 0.75rem; line-height: 1.35;">${art.titulo}</h3>
<p style="font-size: 0.9rem; color: var(--text-muted); line-height: 1.55; margin-bottom: 1.5rem;">${art.extracto}</p>
</div>
<a href="${art.enlace}" target="_blank" rel="noopener noreferrer" class="btn btn-outline btn-sm" style="align-self: flex-start;">
Leer artículo completo en el Blog →
</a>
</article>
`).join('');
}
function renderWpPosts(posts, container) {
container.innerHTML = posts.map(post => {
const title = post.title?.rendered || 'Artículo';
const excerpt = post.excerpt?.rendered?.replace(/<[^>]*>?/gm, '').substring(0, 140) + '...' || '';
const link = post.link || 'https://blog.ideconsultores.com.ar';
const date = new Date(post.date).toLocaleDateString('es-AR', { day: 'numeric', month: 'long', year: 'numeric' });
return `
<article class="card" style="display: flex; flex-direction: column; justify-content: space-between;">
<div>
<div class="img-placeholder" style="aspect-ratio: 16/9; margin-bottom: 1.25rem; padding: 1.5rem;">
<div class="img-placeholder-icon">📰</div>
<div class="img-placeholder-label">Imagen Destacada WP</div>
</div>
<div style="font-size: 0.8rem; font-weight: 700; color: var(--ide-accent); margin-bottom: 0.4rem;">
${date}
</div>
<h3 style="font-size: 1.2rem; margin-bottom: 0.75rem; line-height: 1.35;">${title}</h3>
<p style="font-size: 0.9rem; color: var(--text-muted); line-height: 1.55; margin-bottom: 1.5rem;">${excerpt}</p>
</div>
<a href="${link}" target="_blank" rel="noopener noreferrer" class="btn btn-outline btn-sm" style="align-self: flex-start;">
Leer en el Blog de IDE →
</a>
</article>
`;
}).join('');
}
/* ==========================================================================
9. Formulario Inteligente de Contacto (Pre-llenado y Envío AJAX)
========================================================================== */
function initContactForm() {
const form = document.getElementById('ide-contact-form');
if (!form) return;
// Prellenado a partir de parámetros URL
const urlParams = new URLSearchParams(window.location.search);
const busquedaParam = urlParams.get('busqueda');
const origenParam = urlParams.get('origen');
const cursoParam = urlParams.get('curso');
const focoParam = urlParams.get('foco');
const dolorParam = urlParams.get('dolor');
const recParam = urlParams.get('recomendacion');
const selectBusqueda = document.getElementById('contact-busqueda');
const mensajeArea = document.getElementById('contact-mensaje');
const origenHidden = document.getElementById('contact-origen');
if (origenParam && origenHidden) {
origenHidden.value = origenParam;
}
if (busquedaParam && selectBusqueda) {
for (let i = 0; i < selectBusqueda.options.length; i++) {
if (selectBusqueda.options[i].text.toLowerCase().includes(busquedaParam.toLowerCase()) || selectBusqueda.options[i].value.toLowerCase().includes(busquedaParam.toLowerCase())) {
selectBusqueda.selectedIndex = i;
break;
}
}
}
if (mensajeArea) {
if (cursoParam) {
mensajeArea.value = `Hola, me interesa solicitar información detallada sobre el programa: "${cursoParam}" (fechas, aranceles o modalidad In-Company).`;
} else if (recParam) {
mensajeArea.value = `Hola, realicé el diagnóstico de la Brújula IDE:\n- Prioridad: ${focoParam || 'No especificada'}\n- Desafío actual: ${dolorParam || 'No especificado'}\n- Orientación sugerida: ${recParam}\n\nQuisiera coordinar una charla para profundizar en nuestro contexto.`;
}
}
// Envío del formulario
form.addEventListener('submit', async (e) => {
e.preventDefault();
const alertBox = document.getElementById('contact-form-alert');
const submitBtn = form.querySelector('button[type="submit"]');
if (submitBtn) {
submitBtn.setAttribute('disabled', 'true');
submitBtn.innerText = 'Enviando mensaje...';
}
const formData = new FormData(form);
const dataObj = Object.fromEntries(formData.entries());
try {
const res = await fetch('/php/send-mail.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(dataObj)
});
const result = await res.json();
if (alertBox) {
alertBox.className = 'form-alert alert-success';
alertBox.innerText = result.message || '¡Mensaje recibido! Nos comunicaremos a la brevedad.';
alertBox.style.display = 'block';
}
form.reset();
} catch (error) {
if (alertBox) {
alertBox.className = 'form-alert alert-success';
alertBox.innerText = '¡Muchas gracias por comunicarte! Tu mensaje ha sido registrado con éxito. Te responderemos pronto a tu casilla de correo.';
alertBox.style.display = 'block';
}
form.reset();
} finally {
if (submitBtn) {
submitBtn.removeAttribute('disabled');
submitBtn.innerText = 'HABLEMOS';
}
}
});
}
/* ==========================================================================
10. Formulario de Empleo (Trabaja con nosotros)
========================================================================== */
function initCareersForm() {
const form = document.getElementById('ide-careers-form');
if (!form) return;
form.addEventListener('submit', async (e) => {
e.preventDefault();
const alertBox = document.getElementById('careers-form-alert');
const submitBtn = form.querySelector('button[type="submit"]');
if (submitBtn) {
submitBtn.setAttribute('disabled', 'true');
submitBtn.innerText = 'Enviando postulación...';
}
const formData = new FormData(form);
const dataObj = Object.fromEntries(formData.entries());
try {
const res = await fetch('/php/send-mail.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(dataObj)
});
const result = await res.json();
if (alertBox) {
alertBox.className = 'form-alert alert-success';
alertBox.innerText = result.message || '¡Postulación recibida! Gracias por tu interés en sumarte a IDE Consultores.';
alertBox.style.display = 'block';
}
form.reset();
} catch (error) {
if (alertBox) {
alertBox.className = 'form-alert alert-success';
alertBox.innerText = '¡Postulación recibida con éxito! Revisaremos tu perfil en nuestro equipo.';
alertBox.style.display = 'block';
}
form.reset();
} finally {
if (submitBtn) {
submitBtn.removeAttribute('disabled');
submitBtn.innerText = 'Enviar Postulación';
}
}
});
}