`;
}
if (ctaElem) {
const params = new URLSearchParams({
origen: 'IDE_Academy',
busqueda: 'Formación para mi organización',
curso: `${course.titulo} (${course.id})`
});
ctaElem.setAttribute('href', `contacto.html?${params.toString()}`);
}
openModal('modal-course-view');
};
function getFallbackCourses() {
return [
{
id: "ACA-GC-001",
estado: "Próximamente",
titulo: "Calidad que genera valor: De la norma a la gestión real",
area_tematica: "Gestión & Calidad",
tipo: "Curso",
modalidad: "Online sincrónico",
duracion: "12 horas – 4 encuentros",
nivel: "Intermedio",
destinatarios: "Responsables de calidad, líderes de procesos y mandos medios.",
propuesta_formativa: "Una experiencia práctica y estratégica para transformar el sistema de calidad en una herramienta real de gestión.",
que_vas_a_desarrollar: [
"Capacidad para diagnosticar cuellos de botella reales.",
"Diseño de indicadores con impacto operativo.",
"Liderazgo en auditorías con enfoque de valor."
],
recorrido_aprendizaje: [
"1. De cumplir normas a gestionar resultados.",
"2. Mapeo y rediseño de procesos críticos.",
"3. Indicadores clave y tableros de gestión.",
"4. Auditoría interna con valor agregado."
],
equipo_docente: "Equipo IDE Excellence",
proxima_edicion: "A confirmar",
informacion_comercial: "Próxima edición con cupos limitados. Consultar por ediciones In-Company."
}
];
}
/* ==========================================================================
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 => `
📰
Imagen Artículo Blog
${art.categoria} • ${art.fecha}
${art.titulo}
${art.extracto}
Leer artículo completo en el Blog →
`).join('');
}
function extractFirstImage(post) {
// 1. Intentar obtener la imagen destacada oficial gracias al ?_embed
if (post._embedded && post._embedded['wp:featuredmedia'] && post._embedded['wp:featuredmedia'][0]) {
const featuredMedia = post._embedded['wp:featuredmedia'][0];
// Intentamos usar un tamaño mediano-grande para que cargue rápido, si no, la original
const url = featuredMedia.media_details?.sizes?.medium_large?.source_url || featuredMedia.source_url;
if (url) return url;
}
// 2. Respaldo: Si el artículo no tiene imagen destacada, busca la primera dentro del texto
const contentHtml = post.content?.rendered || '';
const match = contentHtml.match(/]+src=["']([^"']+)["']/i);
if (match && match[1]) {
let url = match[1];
if (url.startsWith('/')) {
url = 'https://blog.ideconsultores.com.ar' + url;
}
return url;
}
return null;
}
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' });
const imageUrl = extractFirstImage(post);
const imageHtml = imageUrl
? `
`
: `
📰
Sin imagen destacada
`;
return `
${imageHtml}
${date}
${title}
${excerpt}
Leer en el Blog de IDE →
`;
}).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';
}
}
});
}
/* ==========================================================================
11. Video de Insights
========================================================================== */
function initHeroVideoSpeed() {
const video = document.getElementById('hero-bg-video');
if (!video) return;
const targetSpeed = 0.5; // 50% de la velocidad normal
// 1. Forzar mute por propiedad nativa JS (garantiza el autoplay sin bloqueo)
video.muted = true;
video.defaultMuted = true;
video.playsInline = true;
const applySpeedAndPlay = () => {
video.playbackRate = targetSpeed;
// Si quedó en pausa, disparar play explícito atrapando la promesa
if (video.paused) {
const playPromise = video.play();
if (playPromise !== undefined) {
playPromise.catch(err => {
console.warn('El navegador requirió interacción previa:', err);
});
}
}
};
// 2. Si el video ya está listo o ya empezó a correr
if (video.readyState >= 2) {
applySpeedAndPlay();
} else {
video.addEventListener('canplay', applySpeedAndPlay, { once: true });
video.addEventListener('loadeddata', applySpeedAndPlay, { once: true });
}
// 3. Garantizar que tras el loop o reanudación conserve la velocidad
video.addEventListener('play', () => {
video.playbackRate = targetSpeed;
});
}
/* ==========================================================================
12. BÚSQUEDA EN EL SITIO — Página buscar.html
========================================================================== */
const SEARCH_INDEX_URL = 'data/search-index.json';
async function initSearchPage() {
const form = document.getElementById('search-page-form');
const input = document.getElementById('search-page-input');
const results = document.getElementById('search-results');
const status = document.getElementById('search-status');
const suggestions = document.getElementById('search-suggestions');
// Si no estamos en buscar.html, salir silenciosamente
if (!form || !input || !results) return;
let index = [];
// Cargar índice
try {
const res = await fetch(SEARCH_INDEX_URL, { cache: 'no-store' });
if (res.ok) {
index = await res.json();
} else {
console.warn('No se pudo cargar search-index.json:', res.status);
}
} catch (e) {
console.warn('Error cargando search-index.json:', e);
}
// Normalizar texto (sin acentos, minúsculas)
const normalize = (str) =>
(str || '')
.toString()
.toLowerCase()
.normalize('NFD')
.replace(/[\u0300-\u036f]/g, '');
// Escapar HTML
const escapeHtml = (str) =>
(str || '').replace(/[&<>"']/g, (c) => ({
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": '''
}[c]));
// Scoring por relevancia
function scoreItem(item, terms) {
const title = normalize(item.titulo);
const desc = normalize(item.descripcion);
const kw = normalize(item.keywords);
let score = 0;
terms.forEach(term => {
if (!term) return;
if (title.includes(term)) score += 10;
if (kw.includes(term)) score += 6;
if (desc.includes(term)) score += 3;
});
// Bonus si la frase completa aparece en el título
const fullPhrase = terms.join(' ');
if (fullPhrase && title.includes(fullPhrase)) score += 5;
return score;
}
// Resaltar coincidencias
function highlight(text, terms) {
let safe = escapeHtml(text);
terms.forEach(term => {
if (term.length < 2) return;
const escaped = term.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
const regex = new RegExp(`(${escaped})`, 'gi');
safe = safe.replace(regex, '$1');
});
return safe;
}
// Render de resultados
function render(term) {
results.innerHTML = '';
const cleanTerm = (term || '').trim();
if (!cleanTerm) {
if (status) status.textContent = '';
if (suggestions) suggestions.hidden = false;
return;
}
if (suggestions) suggestions.hidden = true;
const terms = normalize(cleanTerm).split(/\s+/).filter(Boolean);
const scored = index
.map(item => ({ item, score: scoreItem(item, terms) }))
.filter(r => r.score > 0)
.sort((a, b) => b.score - a.score);
if (scored.length === 0) {
if (status) status.textContent = '';
results.innerHTML = `
No encontramos resultados para ${escapeHtml(cleanTerm)}.