From 58025866be25069322b3dc27c47d54940bc1b7ba Mon Sep 17 00:00:00 2001 From: AI Bot Date: Sun, 23 Aug 2026 16:12:25 -0500 Subject: [PATCH] fix API crash and added UI to card --- src/app/api/reports/posts/route.ts | 55 ++++++++++++++++++++---------- src/components/NewsCard.tsx | 54 ++++++++++++++++++----------- 2 files changed, 71 insertions(+), 38 deletions(-) diff --git a/src/app/api/reports/posts/route.ts b/src/app/api/reports/posts/route.ts index 2e37f0d..14e7ed0 100644 --- a/src/app/api/reports/posts/route.ts +++ b/src/app/api/reports/posts/route.ts @@ -5,7 +5,15 @@ import { createAdminClient } from '@/lib/appwrite.server'; import { Query } from 'node-appwrite'; import { APPWRITE_ALMEIRA_CONFIG, endpoint, projectId } from '@/lib/appwrite'; import { collectionMap } from '@/lib/schema'; -import { getAppwriteImageUrl } from '@/components/dashboard/MediaGridSelector'; + +function getAppwriteImageUrl(fileId: string, bucketId?: string, width = 400, height = 250, isVideo = false) { + if (!fileId) return ''; + const finalBucketId = bucketId || APPWRITE_ALMEIRA_CONFIG.mediaBucketId; + if (isVideo) { + return `${endpoint}/storage/buckets/${finalBucketId}/files/${fileId}/view?project=${projectId}`; + } + return `${endpoint}/storage/buckets/${finalBucketId}/files/${fileId}/preview?project=${projectId}&width=${width}&height=${height}&quality=80`; +} export const runtime = 'nodejs'; @@ -109,33 +117,44 @@ export async function GET(request: Request) { const plainTextResumen = (post.resumen_redes || post.contenido || '').replace(/<[^>]*>?/gm, ' ').replace(/\s+/g, ' ').trim(); const safeResumen = plainTextResumen.length > 120 ? plainTextResumen.substring(0, 120) + '...' : plainTextResumen; - // USING @vercel/og syntax to create a React Node inline without JSX in a .ts file ? // WAIT! This file is route.ts, JSX is not supported in .ts unless it's .tsx! // Let's use react.createElement const React = require('react'); const cardNode = React.createElement('div', { style: { - display: 'flex', flexDirection: 'column', width: 600, height: 400, - backgroundColor: '#0f172a', color: 'white', borderRadius: 24, - overflow: 'hidden', border: '2px solid #1e293b', fontFamily: 'sans-serif' + display: 'flex', flexDirection: 'column', width: 600, height: 600, + backgroundColor: '#0f172a', borderRadius: 24, + overflow: 'hidden', border: '1px solid rgba(255,255,255,0.1)', fontFamily: 'sans-serif' } }, - imageUrl - ? React.createElement('img', { src: imageUrl, style: { width: 600, height: 230, objectFit: 'cover' } }) - : React.createElement('div', { style: { width: 600, height: 230, backgroundColor: '#1e293b', display: 'flex', alignItems: 'center', justifyContent: 'center' } }, - React.createElement('span', { style: { color: '#475569', fontSize: 24 } }, 'Sin Imagen') - ), - React.createElement('div', { style: { display: 'flex', flexDirection: 'column', padding: 24, gap: 8 } }, - React.createElement('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center' } }, - React.createElement('span', { style: { color: '#06b6d4', fontSize: 14, fontWeight: 'bold', textTransform: 'uppercase' } }, post.estado || 'Borrador'), - React.createElement('span', { style: { color: '#64748b', fontSize: 14 } }, fechaFormat) - ), - React.createElement('h3', { style: { fontSize: 22, fontWeight: 'bold', margin: 0, lineHeight: 1.2, color: '#f8fafc' } }, post.titulo || 'Sin título'), - React.createElement('p', { style: { fontSize: 16, color: '#94a3b8', margin: 0, lineHeight: 1.4 } }, safeResumen) + React.createElement('div', { + style: { display: 'flex', width: 600, height: 337, backgroundColor: '#1e293b', position: 'relative' } + }, + imageUrl + ? React.createElement('img', { src: imageUrl, style: { width: 600, height: 337, objectFit: 'cover' } }) + : React.createElement('div', { style: { width: 600, height: 337, display: 'flex', alignItems: 'center', justifyContent: 'center' } }, + React.createElement('span', { style: { color: '#475569', fontSize: 24, fontWeight: 900, textTransform: 'uppercase', letterSpacing: 4 } }, 'Almeira Web') + ), + React.createElement('div', { style: { position: 'absolute', top: 16, left: 16, display: 'flex' } }, + React.createElement('span', { + style: { padding: '6px 12px', borderRadius: 9999, fontSize: 12, fontWeight: 'bold', letterSpacing: 1, backgroundColor: 'rgba(0,0,0,0.6)', color: '#67e8f9', border: '1px solid rgba(255,255,255,0.2)' } + }, 'NOTICIA') + ) + ), + React.createElement('div', { style: { display: 'flex', flexDirection: 'column', padding: 32, flexGrow: 1 } }, + React.createElement('h3', { style: { fontSize: 26, fontWeight: 'bold', margin: '0 0 16px 0', lineHeight: 1.3, color: 'white' } }, post.titulo || 'Sin título'), + React.createElement('p', { style: { fontSize: 16, color: '#94a3b8', margin: '0 0 32px 0', lineHeight: 1.6 } }, safeResumen), + React.createElement('div', { style: { marginTop: 'auto', paddingTop: 20, borderTop: '1px solid rgba(255,255,255,0.1)', display: 'flex', justifyContent: 'space-between', alignItems: 'center' } }, + React.createElement('div', { style: { display: 'flex', alignItems: 'center' } }, + React.createElement('span', { style: { color: '#06b6d4', fontSize: 13, fontWeight: 'bold', textTransform: 'uppercase', letterSpacing: 1, marginRight: 8 } }, 'ESTADO:'), + React.createElement('span', { style: { color: '#94a3b8', fontSize: 13, fontWeight: 'bold', textTransform: 'uppercase', letterSpacing: 1 } }, post.estado || 'Borrador') + ), + React.createElement('span', { style: { color: '#06b6d4', fontSize: 13, fontWeight: 'bold', textTransform: 'uppercase', letterSpacing: 1 } }, fechaFormat) + ) ) ); - const cardImage = new ImageResponse(cardNode, { width: 600, height: 400 }); + const cardImage = new ImageResponse(cardNode, { width: 600, height: 600 }); const imgBuffer = await cardImage.arrayBuffer(); docChildren.push( diff --git a/src/components/NewsCard.tsx b/src/components/NewsCard.tsx index d5a7b7e..bacb188 100644 --- a/src/components/NewsCard.tsx +++ b/src/components/NewsCard.tsx @@ -1,5 +1,5 @@ import Link from 'next/link'; -import { ArrowUpRight, Calendar } from 'lucide-react'; +import { ArrowUpRight, Calendar, Edit2, Trash2 } from 'lucide-react'; import { decodeHtmlEntities } from '@/lib/utils'; interface NewsCardProps { @@ -18,13 +18,13 @@ interface NewsCardProps { imageAspect?: string; } -export const NewsCard = ({ - id, - slug, - titulo: rawTitulo, - resumen: rawResumen, - tipo, - fecha, +export const NewsCard = ({ + id, + slug, + titulo: rawTitulo, + resumen: rawResumen, + tipo, + fecha, imagenUrl, showCategory = false, showFooterDate = false, @@ -34,7 +34,7 @@ export const NewsCard = ({ imageAspect = 'aspect-video' }: NewsCardProps) => { const titulo = decodeHtmlEntities(rawTitulo); - + // Strip HTML tags safely for a plain text excerpt const plainTextResumen = (rawResumen || '').replace(/<[^>]*>?/gm, ' ').replace(/\s+/g, ' ').trim(); const resumen = decodeHtmlEntities(plainTextResumen); @@ -50,16 +50,30 @@ export const NewsCard = ({ Ver {titulo} - {/* Edit Button for Admins (Elevado en el stacking context) */} + {/* Admin Controls (Elevado en el stacking context) - Dynamic Island style */} {isAdmin && ( -
- +
+ - Editar + + Editar + +
+ + + + Eliminar + +
)} @@ -79,7 +93,7 @@ export const NewsCard = ({ Almeira Web )} - + {/* Badge/Tag - Habilitable por prop */} {showCategory && (
@@ -95,7 +109,7 @@ export const NewsCard = ({

{titulo}

- +

{resumen}

@@ -105,10 +119,10 @@ export const NewsCard = ({
{showFooterDate ? ( -
+
{fechaFormateada} -
+
) : ( footerText || "" )}