mejoras en el blog
This commit is contained in:
+232
-90
@@ -1,110 +1,252 @@
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const searchInput = document.getElementById('blog-search');
|
||||
const searchClear = document.getElementById('search-clear');
|
||||
const categoryBtns = document.querySelectorAll('.category-btn');
|
||||
const filterBtns = document.querySelectorAll('.filter-btn');
|
||||
const resetBtn = document.getElementById('reset-filters');
|
||||
document.addEventListener('DOMContentLoaded', async () => {
|
||||
const GHOST_API_URL = 'https://blog.amoryroma.com.ar';
|
||||
const GHOST_API_KEY = '82450ba91ca6b5b9628006eec4';
|
||||
|
||||
// Elementos del DOM
|
||||
const featuredSection = document.getElementById('featured-posts');
|
||||
const featuredGrid = document.getElementById('featured-grid');
|
||||
const blogGrid = document.getElementById('blog-grid');
|
||||
const loadMoreBtn = document.getElementById('load-more');
|
||||
const resultsCount = document.getElementById('results-count');
|
||||
const emptyState = document.getElementById('empty-state');
|
||||
|
||||
const allCards = document.querySelectorAll('.blog-card, .featured-card');
|
||||
let activeCategory = 'all';
|
||||
let activeFilter = 'all';
|
||||
let searchQuery = '';
|
||||
const gridSectionTitle = document.getElementById('grid-section-title');
|
||||
const gridSectionSubtitle = document.getElementById('grid-section-subtitle');
|
||||
|
||||
function filterArticles() {
|
||||
let visibleCount = 0;
|
||||
// Estado de los filtros
|
||||
let posts = [];
|
||||
let state = {
|
||||
category: 'all',
|
||||
filter: 'all', // 'all' o 'destacados'
|
||||
search: '',
|
||||
sort: 'recent'
|
||||
};
|
||||
|
||||
allCards.forEach(card => {
|
||||
const categories = card.dataset.categories || '';
|
||||
const isFeatured = card.dataset.featured === 'true';
|
||||
const title = card.querySelector('.featured-title, .card-title')?.textContent.toLowerCase() || '';
|
||||
const excerpt = card.querySelector('.featured-excerpt, .card-excerpt')?.textContent.toLowerCase() || '';
|
||||
|
||||
// 1. Filtro de categoría
|
||||
const matchCategory = activeCategory === 'all' || categories.includes(activeCategory);
|
||||
// Cargar artículos desde Ghost API
|
||||
async function loadPosts() {
|
||||
try {
|
||||
const response = await fetch(`${GHOST_API_URL}/ghost/api/content/posts/?key=${GHOST_API_KEY}&limit=all&include=tags,authors`);
|
||||
const data = await response.json();
|
||||
|
||||
// 2. Filtro de tipo (Todos / Destacados)
|
||||
const matchFilter = activeFilter === 'all' || (activeFilter === 'destacados' && isFeatured);
|
||||
|
||||
// 3. Filtro de búsqueda
|
||||
const matchSearch = searchQuery === '' || title.includes(searchQuery) || excerpt.includes(searchQuery);
|
||||
posts = data.posts.map(post => ({
|
||||
id: post.id,
|
||||
slug: post.slug,
|
||||
titulo: post.title,
|
||||
extracto: post.excerpt,
|
||||
categoria: post.primary_tag ? post.primary_tag.name.toLowerCase() : 'general',
|
||||
tags: post.tags ? post.tags.map(t => t.name.toLowerCase()) : [],
|
||||
imagen: post.feature_image || 'assets/blog/default.jpg',
|
||||
fecha: post.published_at,
|
||||
tiempo_lectura: Math.ceil(post.reading_time) || 5,
|
||||
destacado: post.featured
|
||||
}));
|
||||
|
||||
if (matchCategory && matchFilter && matchSearch) {
|
||||
card.style.display = 'flex';
|
||||
visibleCount++;
|
||||
} else {
|
||||
card.style.display = 'none';
|
||||
renderPosts();
|
||||
setupEventListeners();
|
||||
} catch (error) {
|
||||
console.error('Error cargando posts de Ghost:', error);
|
||||
if(emptyState) {
|
||||
emptyState.style.display = 'block';
|
||||
emptyState.querySelector('h3').textContent = 'Error al cargar el blog';
|
||||
emptyState.querySelector('p').textContent = 'Por favor, recarga la página.';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function formatDate(dateString) {
|
||||
const options = { day: '2-digit', month: 'short', year: 'numeric' };
|
||||
return new Date(dateString).toLocaleDateString('es-AR', options);
|
||||
}
|
||||
|
||||
function createCard(post, isFeatured = false) {
|
||||
const catFormatted = post.categoria.charAt(0).toUpperCase() + post.categoria.slice(1);
|
||||
const cardClass = isFeatured ? 'featured-card' : 'blog-card';
|
||||
const badge = isFeatured && post.destacado ? '<span class="featured-badge">Destacado</span>' : '';
|
||||
const metaClass = isFeatured ? 'featured-meta' : 'card-meta';
|
||||
const titleClass = isFeatured ? 'featured-title' : 'card-title';
|
||||
const excerptClass = isFeatured ? 'featured-excerpt' : 'card-excerpt';
|
||||
const linkClass = isFeatured ? 'btn-read' : 'card-link';
|
||||
const linkText = isFeatured ? 'Leer artículo →' : 'Leer artículo';
|
||||
|
||||
return `
|
||||
<article class="${cardClass}" data-categories="${post.categoria}" data-featured="${post.destacado}">
|
||||
<div class="${isFeatured ? 'featured-image' : 'card-image'}">
|
||||
<img src="${post.imagen}" alt="${post.titulo}" loading="lazy">
|
||||
${!isFeatured ? `<span class="card-category ${post.categoria}">${catFormatted}</span>` : ''}
|
||||
${badge}
|
||||
</div>
|
||||
<div class="${isFeatured ? 'featured-content' : 'card-content'}">
|
||||
<div class="${metaClass}">
|
||||
<span class="category-tag ${post.categoria}">${catFormatted}</span>
|
||||
<time datetime="${post.fecha}">${formatDate(post.fecha)}</time>
|
||||
</div>
|
||||
<h3 class="${titleClass}">${post.titulo}</h3>
|
||||
<p class="${excerptClass}">${post.extracto}</p>
|
||||
<div class="${isFeatured ? 'featured-footer' : ''}">
|
||||
${isFeatured ? `<span class="reading-time">⏱ ${post.tiempo_lectura} min</span>` : `<span class="reading-time-mini">${post.tiempo_lectura} min</span>`}
|
||||
<a href="${GHOST_API_URL}/${post.slug}/" class="${linkClass}" target="_blank">${linkText}</a>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
`;
|
||||
}
|
||||
|
||||
function renderPosts() {
|
||||
// 1. FILTRAR
|
||||
let filtered = posts.filter(post => {
|
||||
const matchCategory = state.category === 'all' || post.categoria === state.category;
|
||||
const matchFilter = state.filter === 'all' || (state.filter === 'destacados' && post.destacado);
|
||||
const q = state.search.toLowerCase();
|
||||
const matchSearch = !q ||
|
||||
post.titulo.toLowerCase().includes(q) ||
|
||||
post.extracto.toLowerCase().includes(q) ||
|
||||
post.tags.some(tag => tag.includes(q));
|
||||
|
||||
return matchCategory && matchFilter && matchSearch;
|
||||
});
|
||||
|
||||
// Actualizar contador y estado vacío
|
||||
if (resultsCount) {
|
||||
resultsCount.innerHTML = `Mostrando <strong>${visibleCount}</strong> artículo${visibleCount !== 1 ? 's' : ''}`;
|
||||
// 2. ORDENAR
|
||||
if (state.sort === 'recent') {
|
||||
filtered.sort((a, b) => new Date(b.fecha) - new Date(a.fecha));
|
||||
} else if (state.sort === 'popular') {
|
||||
// Como la API pública de Ghost no expone "vistas", simulamos popularidad poniendo los destacados primero
|
||||
filtered.sort((a, b) => (b.destacado === a.destacado) ? 0 : b.destacado ? 1 : -1);
|
||||
}
|
||||
|
||||
// 3. DECIDIR VISTA: ¿Es la vista por defecto (sin filtros)?
|
||||
const isDefaultView = state.category === 'all' && state.filter === 'all' && state.search === '';
|
||||
|
||||
if (isDefaultView) {
|
||||
// VISTA POR DEFECTO: Mostrar Destacados + Últimos 3
|
||||
if (featuredSection) featuredSection.style.display = 'block';
|
||||
if (gridSectionTitle) gridSectionTitle.textContent = 'Últimos artículos';
|
||||
if (gridSectionSubtitle) gridSectionSubtitle.textContent = 'Lo más nuevo y a la vanguardia.';
|
||||
if (loadMoreBtn) {
|
||||
loadMoreBtn.style.display = 'inline-block';
|
||||
loadMoreBtn.textContent = 'Ver todos los artículos en el Blog';
|
||||
loadMoreBtn.onclick = () => window.location.href = `${GHOST_API_URL}/`;
|
||||
}
|
||||
|
||||
const featuredPosts = filtered.filter(p => p.destacado).slice(0, 3);
|
||||
const latestPosts = filtered.slice(0, 3);
|
||||
|
||||
if (featuredGrid) featuredGrid.innerHTML = featuredPosts.map(p => createCard(p, true)).join('');
|
||||
if (blogGrid) blogGrid.innerHTML = latestPosts.map(p => createCard(p, false)).join('');
|
||||
|
||||
if (resultsCount) resultsCount.innerHTML = `Mostrando los <strong>últimos 3</strong> artículos`;
|
||||
if (emptyState) emptyState.style.display = 'none';
|
||||
|
||||
} else {
|
||||
// VISTA FILTRADA: Ocultar Destacados, mostrar TODOS los resultados en la grilla principal
|
||||
if (featuredSection) featuredSection.style.display = 'none';
|
||||
if (loadMoreBtn) loadMoreBtn.style.display = 'none'; // Ocultamos "cargar más" porque ya mostramos todos los filtrados
|
||||
|
||||
// Actualizar títulos según el filtro
|
||||
if (gridSectionTitle) {
|
||||
if (state.search) gridSectionTitle.textContent = `Resultados de búsqueda`;
|
||||
else if (state.category !== 'all') gridSectionTitle.textContent = `Artículos de ${state.category.charAt(0).toUpperCase() + state.category.slice(1)}`;
|
||||
else if (state.filter === 'destacados') gridSectionTitle.textContent = `Artículos Destacados`;
|
||||
}
|
||||
if (gridSectionSubtitle) gridSectionSubtitle.textContent = `Se encontraron ${filtered.length} artículo${filtered.length !== 1 ? 's' : ''}`;
|
||||
|
||||
if (blogGrid) {
|
||||
if (filtered.length === 0) {
|
||||
blogGrid.innerHTML = '';
|
||||
if (emptyState) {
|
||||
emptyState.style.display = 'block';
|
||||
emptyState.querySelector('h3').textContent = 'No encontramos artículos con esos filtros';
|
||||
}
|
||||
} else {
|
||||
if (emptyState) emptyState.style.display = 'none';
|
||||
// Mostramos TODOS los artículos que coinciden con el filtro, no solo 3
|
||||
blogGrid.innerHTML = filtered.map(p => createCard(p, false)).join('');
|
||||
}
|
||||
}
|
||||
|
||||
if (resultsCount) {
|
||||
resultsCount.innerHTML = `Mostrando <strong>${filtered.length}</strong> artículo${filtered.length !== 1 ? 's' : ''}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function setupEventListeners() {
|
||||
// 1. Botones de Categoría
|
||||
document.querySelectorAll('.category-btn').forEach(btn => {
|
||||
btn.addEventListener('click', (e) => {
|
||||
document.querySelectorAll('.category-btn').forEach(b => b.classList.remove('active'));
|
||||
e.target.classList.add('active');
|
||||
state.category = e.target.dataset.category;
|
||||
renderPosts();
|
||||
});
|
||||
});
|
||||
|
||||
// 2. Botones de Filtro (Todos / Destacados)
|
||||
document.querySelectorAll('.filter-btn').forEach(btn => {
|
||||
btn.addEventListener('click', (e) => {
|
||||
document.querySelectorAll('.filter-btn').forEach(b => b.classList.remove('active'));
|
||||
e.target.classList.add('active');
|
||||
state.filter = e.target.dataset.filter;
|
||||
renderPosts();
|
||||
});
|
||||
});
|
||||
|
||||
// 3. Buscador
|
||||
const searchInput = document.getElementById('blog-search');
|
||||
const searchClear = document.getElementById('search-clear');
|
||||
|
||||
if (searchInput) {
|
||||
searchInput.addEventListener('input', (e) => {
|
||||
state.search = e.target.value;
|
||||
if (searchClear) searchClear.classList.toggle('visible', state.search.length > 0);
|
||||
renderPosts();
|
||||
});
|
||||
}
|
||||
|
||||
if (emptyState) {
|
||||
emptyState.style.display = visibleCount === 0 ? 'block' : 'none';
|
||||
}
|
||||
|
||||
// Mostrar/ocultar botón de limpiar búsqueda
|
||||
if (searchClear) {
|
||||
searchClear.classList.toggle('visible', searchQuery.length > 0);
|
||||
searchClear.addEventListener('click', () => {
|
||||
if (searchInput) {
|
||||
searchInput.value = '';
|
||||
state.search = '';
|
||||
searchClear.classList.remove('visible');
|
||||
searchInput.focus();
|
||||
renderPosts();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Event Listeners
|
||||
if (searchInput) {
|
||||
searchInput.addEventListener('input', (e) => {
|
||||
searchQuery = e.target.value.toLowerCase().trim();
|
||||
filterArticles();
|
||||
});
|
||||
}
|
||||
// 4. Ordenamiento
|
||||
const sortSelect = document.getElementById('sort-select');
|
||||
if (sortSelect) {
|
||||
sortSelect.addEventListener('change', (e) => {
|
||||
state.sort = e.target.value;
|
||||
renderPosts();
|
||||
});
|
||||
}
|
||||
|
||||
if (searchClear) {
|
||||
searchClear.addEventListener('click', () => {
|
||||
searchInput.value = '';
|
||||
searchQuery = '';
|
||||
searchInput.focus();
|
||||
filterArticles();
|
||||
});
|
||||
}
|
||||
|
||||
categoryBtns.forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
categoryBtns.forEach(b => b.classList.remove('active'));
|
||||
btn.classList.add('active');
|
||||
activeCategory = btn.dataset.category;
|
||||
filterArticles();
|
||||
});
|
||||
});
|
||||
|
||||
filterBtns.forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
filterBtns.forEach(b => b.classList.remove('active'));
|
||||
btn.classList.add('active');
|
||||
activeFilter = btn.dataset.filter;
|
||||
filterArticles();
|
||||
});
|
||||
});
|
||||
|
||||
if (resetBtn) {
|
||||
resetBtn.addEventListener('click', () => {
|
||||
activeCategory = 'all';
|
||||
activeFilter = 'all';
|
||||
searchQuery = '';
|
||||
// 5. Botón Limpiar Filtros
|
||||
const resetBtn = document.getElementById('reset-filters');
|
||||
const emptyReset = document.getElementById('empty-reset');
|
||||
|
||||
function resetAll() {
|
||||
state = { category: 'all', filter: 'all', search: '', sort: 'recent' };
|
||||
|
||||
// Resetear UI
|
||||
document.querySelectorAll('.category-btn').forEach(b => b.classList.remove('active'));
|
||||
document.querySelector('.category-btn[data-category="all"]').classList.add('active');
|
||||
|
||||
document.querySelectorAll('.filter-btn').forEach(b => b.classList.remove('active'));
|
||||
document.querySelector('.filter-btn[data-filter="all"]').classList.add('active');
|
||||
|
||||
if (searchInput) searchInput.value = '';
|
||||
if (searchClear) searchClear.classList.remove('visible');
|
||||
if (sortSelect) sortSelect.value = 'recent';
|
||||
|
||||
categoryBtns.forEach(b => b.classList.remove('active'));
|
||||
document.querySelector('.category-btn[data-category="all"]')?.classList.add('active');
|
||||
|
||||
filterBtns.forEach(b => b.classList.remove('active'));
|
||||
document.querySelector('.filter-btn[data-filter="all"]')?.classList.add('active');
|
||||
|
||||
filterArticles();
|
||||
});
|
||||
renderPosts();
|
||||
}
|
||||
|
||||
if (resetBtn) resetBtn.addEventListener('click', resetAll);
|
||||
if (emptyReset) emptyReset.addEventListener('click', resetAll);
|
||||
}
|
||||
|
||||
// Ejecutar al inicio
|
||||
filterArticles();
|
||||
// Iniciar
|
||||
await loadPosts();
|
||||
});
|
||||
Reference in New Issue
Block a user