fix API crash and added UI to card
This commit is contained in:
parent
fbc7343730
commit
58025866be
@ -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(
|
||||
|
||||
@ -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 = ({
|
||||
<span className="sr-only">Ver {titulo}</span>
|
||||
</Link>
|
||||
|
||||
{/* Edit Button for Admins (Elevado en el stacking context) */}
|
||||
{/* Admin Controls (Elevado en el stacking context) - Dynamic Island style */}
|
||||
{isAdmin && (
|
||||
<div className="absolute top-4 right-4 z-30 opacity-0 group-hover:opacity-100 transition-opacity pointer-events-auto">
|
||||
<Link
|
||||
href={`/dashboard/posts/${id}`}
|
||||
className="flex items-center gap-2 px-3 py-1.5 rounded-full text-[10px] font-black uppercase tracking-wider bg-slate-950/80 backdrop-blur border border-cyan-500/50 text-cyan-400 hover:bg-cyan-500 hover:text-slate-950 transition-all shadow-lg cursor-pointer hover:scale-105"
|
||||
title="Editar en Dashboard"
|
||||
<div className="absolute top-4 right-4 z-30 flex items-center gap-2 opacity-0 group-hover:opacity-100 transition-opacity translate-y-[-10px] group-hover:translate-y-0 duration-300 pointer-events-auto">
|
||||
<div className="flex items-center bg-slate-950/80 backdrop-blur-md rounded-full shadow-2xl border border-white/10 p-1">
|
||||
<Link
|
||||
href={`/dashboard/posts/${id}`}
|
||||
className="flex items-center justify-center px-4 py-2 rounded-full bg-slate-800/50 hover:bg-cyan-500 text-cyan-400 hover:text-slate-950 transition-all hover:scale-105 tooltip-trigger group/btn"
|
||||
title="Editar Publicación"
|
||||
>
|
||||
<span className="hidden sm:inline pointer-events-none">Editar</span>
|
||||
<Edit2 size={14} className="group-hover/btn:animate-pulse" />
|
||||
<span className="ml-2 text-[10px] font-black uppercase tracking-wider hidden sm:inline">Editar</span>
|
||||
</Link>
|
||||
|
||||
<div className="w-[1px] h-4 bg-white/20 mx-1"></div>
|
||||
|
||||
<Link
|
||||
href={`/dashboard/posts/${id}`}
|
||||
className="flex items-center justify-center w-8 h-8 rounded-full bg-transparent hover:bg-red-500/20 text-red-500 hover:text-red-400 transition-all hover:scale-110 tooltip-trigger"
|
||||
title="Eliminar Publicación"
|
||||
>
|
||||
<Trash2 size={14} />
|
||||
<span className="sr-only">Eliminar</span>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@ -79,7 +93,7 @@ export const NewsCard = ({
|
||||
<span className="text-[10px] font-black uppercase tracking-widest text-slate-700">Almeira Web</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
{/* Badge/Tag - Habilitable por prop */}
|
||||
{showCategory && (
|
||||
<div className="absolute top-4 left-4 z-30">
|
||||
@ -95,7 +109,7 @@ export const NewsCard = ({
|
||||
<h3 className="text-xl font-bold font-outfit text-white leading-snug mb-3 group-hover:text-cyan-400 transition-colors line-clamp-2">
|
||||
{titulo}
|
||||
</h3>
|
||||
|
||||
|
||||
<p className="text-slate-400 text-sm leading-relaxed mb-6 line-clamp-3">
|
||||
{resumen}
|
||||
</p>
|
||||
@ -105,10 +119,10 @@ export const NewsCard = ({
|
||||
<div className="mt-auto pt-5 border-t border-white/10 flex items-center justify-between">
|
||||
<span className="text-xs font-black tracking-widest uppercase text-cyan-500 group-hover:text-cyan-300 transition-colors">
|
||||
{showFooterDate ? (
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<Calendar size={12} />
|
||||
{fechaFormateada}
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
footerText || ""
|
||||
)}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user