From a446a708546682251c83b64cf1a6fd32ac7a62ed Mon Sep 17 00:00:00 2001 From: "Carlos (Almeira)" Date: Thu, 26 Mar 2026 14:47:02 -0500 Subject: [PATCH] Fix: Featured images resolution, NewsCard redesign, and settings configurability --- src/app/clasificados/page.tsx | 41 +++++++++++++++------ src/app/eventos/page.tsx | 41 +++++++++++++++------ src/app/noticia/[slug]/page.tsx | 12 +++++-- src/app/page.tsx | 46 ++++++++++++++++++------ src/components/NewsCard.tsx | 55 ++++++++++++++++++++++------ src/components/NewsTicker.tsx | 64 +++++++++++++++++++++++---------- 6 files changed, 196 insertions(+), 63 deletions(-) diff --git a/src/app/clasificados/page.tsx b/src/app/clasificados/page.tsx index 39d034b..74b7082 100644 --- a/src/app/clasificados/page.tsx +++ b/src/app/clasificados/page.tsx @@ -2,15 +2,22 @@ import { APPWRITE_ALMEIRA_CONFIG } from '@/lib/appwrite'; import { createAdminClient } from '@/lib/appwrite.server'; import { Query } from 'node-appwrite'; import { NewsCard } from '@/components/NewsCard'; -import { Loader2, Sparkles } from 'lucide-react'; +import { Sparkles } from 'lucide-react'; async function fetchClasificadosData() { try { const client = await createAdminClient(); - // 1. Settings for count + // 1. Settings const settingsRes = await client.databases.listDocuments(APPWRITE_ALMEIRA_CONFIG.databaseId, 'settings', []); - const postsCount = settingsRes.documents.length > 0 ? (settingsRes.documents[0].classifieds_count || 9) : 9; + const settings = settingsRes.documents.length > 0 ? settingsRes.documents[0] : {}; + + const postsCount = settings.classifieds_count || 9; + const cardSettings = { + showCategory: settings.news_card_show_category ?? false, + showFooterDate: settings.news_card_show_footer_date ?? true, + footerText: settings.news_card_footer_text || "" + }; // 2. Get CLASIFICADO type ID const typeRes = await client.databases.listDocuments(APPWRITE_ALMEIRA_CONFIG.databaseId, 'publication_types', [ @@ -27,17 +34,27 @@ async function fetchClasificadosData() { Query.limit(postsCount) ]); posts = postsRes.documents; + + // 3. Resolve Media + const mediaIds = posts.map(p => p.imagen_destacada_id).filter(id => id && typeof id === 'string'); + if (mediaIds.length > 0) { + const mediaRes = await client.databases.listDocuments(APPWRITE_ALMEIRA_CONFIG.databaseId, 'media', [ + Query.equal('$id', mediaIds) + ]); + const mediaMap = new Map(mediaRes.documents.map(m => [m.$id, m])); + posts = posts.map(p => ({ ...p, resolved_media: mediaMap.get(p.imagen_destacada_id) })); + } } - return { posts }; + return { posts, cardSettings }; } catch (e) { console.error("Clasificados fetch error", e); - return { posts: [] }; + return { posts: [], cardSettings: { showCategory: false, showFooterDate: true, footerText: "" } }; } } export default async function ClasificadosPage() { - const { posts } = await fetchClasificadosData(); + const { posts, cardSettings } = await fetchClasificadosData(); return (
@@ -90,9 +107,10 @@ export default async function ClasificadosPage() { {posts.map((post) => { const PROJECT_ID = process.env.NEXT_PUBLIC_APPWRITE_PROJECT_ID || '69b84df5003bd8957c5c'; const ENDPOINT = process.env.NEXT_PUBLIC_APPWRITE_ENDPOINT || 'https://appwrite.almeira.com.co/v1'; - let imagenUrl = null; - if (post.imagen_destacada_id) { - imagenUrl = `${ENDPOINT}/storage/buckets/${post.imagen_destacada_id.bucket_id}/files/${post.imagen_destacada_id.file_id}/preview?project=${PROJECT_ID}&width=800&height=500&quality=85`; + let imagenUrl = post.og_image_url || null; + + if (post.resolved_media) { + imagenUrl = `${ENDPOINT}/storage/buckets/${post.resolved_media.bucket_id}/files/${post.resolved_media.file_id}/preview?project=${PROJECT_ID}&width=800&height=500&quality=85`; } return ( @@ -101,10 +119,13 @@ export default async function ClasificadosPage() { id={post.$id} slug={post.slug || post.$id} titulo={post.titulo} - resumen={post.resumen_redes || post.contenido?.substring(0, 100).replace(/<[^>]*>?/gm, '') + '...'} + resumen={post.resumen_redes || post.contenido?.replace(/<[^>]*>?/gm, '').substring(0, 150) + '...'} tipo="Aviso" fecha={post.fecha_publicacion || post.$createdAt} imagenUrl={imagenUrl} + showCategory={cardSettings.showCategory} + showFooterDate={cardSettings.showFooterDate} + footerText={cardSettings.footerText} /> ) })} diff --git a/src/app/eventos/page.tsx b/src/app/eventos/page.tsx index 6ed18c4..a8ec1e0 100644 --- a/src/app/eventos/page.tsx +++ b/src/app/eventos/page.tsx @@ -2,15 +2,22 @@ import { APPWRITE_ALMEIRA_CONFIG } from '@/lib/appwrite'; import { createAdminClient } from '@/lib/appwrite.server'; import { Query } from 'node-appwrite'; import { NewsCard } from '@/components/NewsCard'; -import { Loader2, Calendar } from 'lucide-react'; +import { Calendar } from 'lucide-react'; async function fetchEventosData() { try { const client = await createAdminClient(); - // 1. Settings for count + // 1. Settings const settingsRes = await client.databases.listDocuments(APPWRITE_ALMEIRA_CONFIG.databaseId, 'settings', []); - const postsCount = settingsRes.documents.length > 0 ? (settingsRes.documents[0].events_count || 9) : 9; + const settings = settingsRes.documents.length > 0 ? settingsRes.documents[0] : {}; + + const postsCount = settings.events_count || 9; + const cardSettings = { + showCategory: settings.news_card_show_category ?? false, + showFooterDate: settings.news_card_show_footer_date ?? true, + footerText: settings.news_card_footer_text || "" + }; // 2. Get EVENTO type ID const typeRes = await client.databases.listDocuments(APPWRITE_ALMEIRA_CONFIG.databaseId, 'publication_types', [ @@ -27,17 +34,27 @@ async function fetchEventosData() { Query.limit(postsCount) ]); posts = postsRes.documents; + + // 3. Resolve Media + const mediaIds = posts.map(p => p.imagen_destacada_id).filter(id => id && typeof id === 'string'); + if (mediaIds.length > 0) { + const mediaRes = await client.databases.listDocuments(APPWRITE_ALMEIRA_CONFIG.databaseId, 'media', [ + Query.equal('$id', mediaIds) + ]); + const mediaMap = new Map(mediaRes.documents.map(m => [m.$id, m])); + posts = posts.map(p => ({ ...p, resolved_media: mediaMap.get(p.imagen_destacada_id) })); + } } - return { posts }; + return { posts, cardSettings }; } catch (e) { console.error("Eventos fetch error", e); - return { posts: [] }; + return { posts: [], cardSettings: { showCategory: false, showFooterDate: true, footerText: "" } }; } } export default async function EventosPage() { - const { posts } = await fetchEventosData(); + const { posts, cardSettings } = await fetchEventosData(); return (
@@ -90,9 +107,10 @@ export default async function EventosPage() { {posts.map((post) => { const PROJECT_ID = process.env.NEXT_PUBLIC_APPWRITE_PROJECT_ID || '69b84df5003bd8957c5c'; const ENDPOINT = process.env.NEXT_PUBLIC_APPWRITE_ENDPOINT || 'https://appwrite.almeira.com.co/v1'; - let imagenUrl = null; - if (post.imagen_destacada_id) { - imagenUrl = `${ENDPOINT}/storage/buckets/${post.imagen_destacada_id.bucket_id}/files/${post.imagen_destacada_id.file_id}/preview?project=${PROJECT_ID}&width=800&height=500&quality=85`; + let imagenUrl = post.og_image_url || null; + + if (post.resolved_media) { + imagenUrl = `${ENDPOINT}/storage/buckets/${post.resolved_media.bucket_id}/files/${post.resolved_media.file_id}/preview?project=${PROJECT_ID}&width=800&height=500&quality=85`; } return ( @@ -101,10 +119,13 @@ export default async function EventosPage() { id={post.$id} slug={post.slug || post.$id} titulo={post.titulo} - resumen={post.resumen_redes || post.contenido?.substring(0, 100).replace(/<[^>]*>?/gm, '') + '...'} + resumen={post.resumen_redes || post.contenido?.replace(/<[^>]*>?/gm, '').substring(0, 150) + '...'} tipo="Evento" fecha={post.fecha_publicacion || post.$createdAt} imagenUrl={imagenUrl} + showCategory={cardSettings.showCategory} + showFooterDate={cardSettings.showFooterDate} + footerText={cardSettings.footerText} /> ) })} diff --git a/src/app/noticia/[slug]/page.tsx b/src/app/noticia/[slug]/page.tsx index bde37d1..a718dd4 100644 --- a/src/app/noticia/[slug]/page.tsx +++ b/src/app/noticia/[slug]/page.tsx @@ -37,9 +37,17 @@ export default async function NoticiaDetail({ params }: { params: { slug: string const PROJECT_ID = process.env.NEXT_PUBLIC_APPWRITE_PROJECT_ID || '69b84df5003bd8957c5c'; const ENDPOINT = process.env.NEXT_PUBLIC_APPWRITE_ENDPOINT || 'https://appwrite.almeira.com.co/v1'; - let imagenUrl = null; + let imagenUrl = post.og_image_url || null; + if (post.imagen_destacada_id) { - imagenUrl = `${ENDPOINT}/storage/buckets/${post.imagen_destacada_id.bucket_id}/files/${post.imagen_destacada_id.file_id}/preview?project=${PROJECT_ID}&width=1200&height=600&quality=90`; + // En vista de detalle, resolvemos el documento de media + try { + const client = await createAdminClient(); + const mediaRes = await client.databases.getDocument(APPWRITE_ALMEIRA_CONFIG.databaseId, 'media', post.imagen_destacada_id); + if (mediaRes) { + imagenUrl = `${ENDPOINT}/storage/buckets/${mediaRes.bucket_id}/files/${mediaRes.file_id}/preview?project=${PROJECT_ID}&width=1200&height=600&quality=90`; + } + } catch(e) {} } const fechaObj = new Date(post.fecha_publicacion || post.$createdAt); diff --git a/src/app/page.tsx b/src/app/page.tsx index 498f536..5728d79 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -3,16 +3,22 @@ import { createAdminClient } from '@/lib/appwrite.server'; import { Query } from 'node-appwrite'; import { NewsCard } from '@/components/NewsCard'; import { GlobalSidebar } from '@/components/AdWrappers'; -import { RadioTower, Sparkles, ChevronDown, Loader2 } from 'lucide-react'; -import { Suspense } from 'react'; +import { RadioTower, Sparkles, ChevronDown } from 'lucide-react'; async function fetchHomeData() { try { const client = await createAdminClient(); - // 1. Settings for count + // 1. Settings const settingsRes = await client.databases.listDocuments(APPWRITE_ALMEIRA_CONFIG.databaseId, 'settings', []); - const postsCount = settingsRes.documents.length > 0 ? (settingsRes.documents[0].feed_count || settingsRes.documents[0].post_count || 9) : 9; + const settings = settingsRes.documents.length > 0 ? settingsRes.documents[0] : {}; + + const postsCount = settings.feed_count || settings.post_count || 9; + const cardSettings = { + showCategory: settings.news_card_show_category ?? false, + showFooterDate: settings.news_card_show_footer_date ?? true, + footerText: settings.news_card_footer_text || "" + }; // 2. Get NOTICIA type ID const typeRes = await client.databases.listDocuments(APPWRITE_ALMEIRA_CONFIG.databaseId, 'publication_types', [ @@ -29,17 +35,31 @@ async function fetchHomeData() { Query.limit(postsCount) ]); posts = postsRes.documents; + + // 3. Resolve Media documents for images + const mediaIds = posts.map(p => p.imagen_destacada_id).filter(id => id && typeof id === 'string'); + if (mediaIds.length > 0) { + const mediaRes = await client.databases.listDocuments(APPWRITE_ALMEIRA_CONFIG.databaseId, 'media', [ + Query.equal('$id', mediaIds) + ]); + const mediaMap = new Map(mediaRes.documents.map(m => [m.$id, m])); + + posts = posts.map(p => ({ + ...p, + resolved_media: mediaMap.get(p.imagen_destacada_id) + })); + } } - return { posts }; + return { posts, cardSettings }; } catch (e) { console.error("Home fetch error", e); - return { posts: [] }; + return { posts: [], cardSettings: { showCategory: false, showFooterDate: true, footerText: "" } }; } } export default async function Home() { - const { posts } = await fetchHomeData(); + const { posts, cardSettings } = await fetchHomeData(); return (
@@ -110,9 +130,10 @@ export default async function Home() { {posts.map((post) => { const PROJECT_ID = process.env.NEXT_PUBLIC_APPWRITE_PROJECT_ID || '69b84df5003bd8957c5c'; const ENDPOINT = process.env.NEXT_PUBLIC_APPWRITE_ENDPOINT || 'https://appwrite.almeira.com.co/v1'; - let imagenUrl = null; - if (post.imagen_destacada_id) { - imagenUrl = `${ENDPOINT}/storage/buckets/${post.imagen_destacada_id.bucket_id}/files/${post.imagen_destacada_id.file_id}/preview?project=${PROJECT_ID}&width=800&height=500&quality=85`; + let imagenUrl = post.og_image_url || null; + + if (post.resolved_media) { + imagenUrl = `${ENDPOINT}/storage/buckets/${post.resolved_media.bucket_id}/files/${post.resolved_media.file_id}/preview?project=${PROJECT_ID}&width=800&height=500&quality=85`; } return ( @@ -121,10 +142,13 @@ export default async function Home() { id={post.$id} slug={post.slug || post.$id} titulo={post.titulo} - resumen={post.resumen_redes || post.contenido?.substring(0, 100).replace(/<[^>]*>?/gm, '') + '...'} + resumen={post.resumen_redes || post.contenido?.replace(/<[^>]*>?/gm, '').substring(0, 150) + '...'} tipo="Noticia" fecha={post.fecha_publicacion || post.$createdAt} imagenUrl={imagenUrl} + showCategory={cardSettings.showCategory} + showFooterDate={cardSettings.showFooterDate} + footerText={cardSettings.footerText} /> ) })} diff --git a/src/components/NewsCard.tsx b/src/components/NewsCard.tsx index 74ac516..10e8ad9 100644 --- a/src/components/NewsCard.tsx +++ b/src/components/NewsCard.tsx @@ -1,7 +1,31 @@ import Link from 'next/link'; import { ArrowUpRight, Calendar } from 'lucide-react'; -export const NewsCard = ({ id, slug, titulo, resumen, tipo, fecha, imagenUrl }: any) => { +interface NewsCardProps { + id: string; + slug: string; + titulo: string; + resumen: string; + tipo?: string; + fecha: string; + imagenUrl?: string | null; + showCategory?: boolean; + showFooterDate?: boolean; + footerText?: string; +} + +export const NewsCard = ({ + id, + slug, + titulo, + resumen, + tipo, + fecha, + imagenUrl, + showCategory = false, + showFooterDate = true, + footerText +}: NewsCardProps) => { // Formatear fecha const fechaObj = new Date(fecha); const fechaFormateada = fechaObj.toLocaleDateString('es-ES', { day: '2-digit', month: 'short', year: 'numeric' }); @@ -14,22 +38,24 @@ export const NewsCard = ({ id, slug, titulo, resumen, tipo, fecha, imagenUrl }: {imagenUrl ? ( {titulo} ) : ( -
- Almeira Media +
+ {/* No placeholder text as per user request */}
)} - {/* Badge/Tag */} -
- - {tipo?.toUpperCase() || 'NOTICIA'} - -
+ {/* Badge/Tag - Habilitable por prop */} + {showCategory && ( +
+ + {tipo?.toUpperCase() || 'NOTICIA'} + +
+ )}
{/* Contenido */}
-
+
{fechaFormateada}
@@ -45,7 +71,14 @@ export const NewsCard = ({ id, slug, titulo, resumen, tipo, fecha, imagenUrl }: {/* Footer del card */}
- Expandir Registro + {showFooterDate ? ( +
+ + {fechaFormateada} +
+ ) : ( + footerText || "Ver más" + )}
diff --git a/src/components/NewsTicker.tsx b/src/components/NewsTicker.tsx index 1f22ff4..056d5ce 100644 --- a/src/components/NewsTicker.tsx +++ b/src/components/NewsTicker.tsx @@ -20,26 +20,52 @@ export default async function NewsTicker() { // Since 'createSessionClient' uses the session cookie, we might have issues on public incognito pages. // The user previously used Admin API key for everything or a local backdoor. - // Settings - const settingsRes = await sessionClient.databases.listDocuments(APPWRITE_ALMEIRA_CONFIG.databaseId, 'settings', []); - const tickerCount = settingsRes.documents.length > 0 ? settingsRes.documents[0].ticker_count : 10; + // Settings const settingsRes = await client.databases.listDocuments(APPWRITE_ALMEIRA_CONFIG.databaseId, 'settings', []); + const settings: any = settingsRes.documents.length > 0 ? settingsRes.documents[0] : {}; + const tickerCount = settings.ticker_count || 10; - // Get Type ID for "Noticia" - const typeRes = await sessionClient.databases.listDocuments(APPWRITE_ALMEIRA_CONFIG.databaseId, 'publication_types', [ - Query.equal('codigo', 'NOTICIA') - ]); - - if (typeRes.documents.length > 0) { - const typeId = typeRes.documents[0].$id; - - const postsRes = await sessionClient.databases.listDocuments(APPWRITE_ALMEIRA_CONFIG.databaseId, 'posts', [ - Query.equal('tipo_publicacion_id', typeId), - Query.equal('estado', 'Publicado'), - Query.orderDesc('fecha_publicacion'), - Query.limit(tickerCount) - ]); - noticias = postsRes.documents; - } + // 2. Get NOTICIA type ID + const typeRes = await client.databases.listDocuments(APPWRITE_ALMEIRA_CONFIG.databaseId, 'publication_types', [ + Query.equal('codigo', 'NOTICIA') + ]); + + let newsItems: any[] = []; + if (typeRes.documents.length > 0) { + const typeId = typeRes.documents[0].$id; + + const postsRes = await client.databases.listDocuments(APPWRITE_ALMEIRA_CONFIG.databaseId, 'posts', [ + Query.equal('tipo_publicacion_id', typeId), + Query.equal('estado', 'Publicado'), + Query.orderDesc('fecha_publicacion'), + Query.limit(tickerCount) + ]); + + // Resolve Media + const mediaIds = postsRes.documents.map(p => p.imagen_destacada_id).filter(id => id && typeof id === 'string'); + const mediaMap = new Map(); + if (mediaIds.length > 0) { + const mediaRes = await client.databases.listDocuments(APPWRITE_ALMEIRA_CONFIG.databaseId, 'media', [ + Query.equal('$id', mediaIds) + ]); + mediaRes.documents.forEach(m => mediaMap.set(m.$id, m)); + } + + newsItems = postsRes.documents.map(post => { + const media = mediaMap.get(post.imagen_destacada_id); + let imagenUrl = post.og_image_url || null; + if (media) { + imagenUrl = `${process.env.NEXT_PUBLIC_APPWRITE_ENDPOINT || 'https://appwrite.almeira.com.co/v1'}/storage/buckets/${media.bucket_id}/files/${media.file_id}/preview?project=${process.env.NEXT_PUBLIC_APPWRITE_PROJECT_ID || '69b84df5003bd8957c5c'}`; + } + + return { + id: post.$id, + titulo: post.titulo, + slug: post.slug || post.$id, + imagenUrl + }; + }); + } + noticias = newsItems; // Assign resolved news items to 'noticias' } catch (e) { console.error("Ticker fetch error", e); }