almeira-web/src/components/NewsCard.tsx
2026-08-23 16:12:25 -05:00

141 lines
7.0 KiB
TypeScript

import Link from 'next/link';
import { ArrowUpRight, Calendar, Edit2, Trash2 } from 'lucide-react';
import { decodeHtmlEntities } from '@/lib/utils';
interface NewsCardProps {
id: string;
slug: string;
titulo: string;
resumen: string;
tipo?: string;
fecha: string;
imagenUrl?: string | null;
showCategory?: boolean;
showFooterDate?: boolean;
showFooterButton?: boolean;
footerText?: string;
isAdmin?: boolean;
imageAspect?: string;
}
export const NewsCard = ({
id,
slug,
titulo: rawTitulo,
resumen: rawResumen,
tipo,
fecha,
imagenUrl,
showCategory = false,
showFooterDate = false,
showFooterButton = false,
footerText,
isAdmin = false,
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);
// Formatear fecha
const fechaObj = new Date(fecha);
const fechaFormateada = fechaObj.toLocaleDateString('es-ES', { day: '2-digit', month: 'short', year: 'numeric' });
return (
<div className="group relative flex flex-col h-full bg-slate-900/40 backdrop-blur-xl border border-white/10 rounded-3xl overflow-hidden transition-all duration-500 hover:-translate-y-2 hover:shadow-[0_20px_40px_-15px_rgba(6,182,212,0.3)] hover:border-cyan-500/50">
{/* Click Frontal */}
<Link href={`/${slug}`} className="absolute inset-0 z-10" prefetch={false}>
<span className="sr-only">Ver {titulo}</span>
</Link>
{/* Admin Controls (Elevado en el stacking context) - Dynamic Island style */}
{isAdmin && (
<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"
>
<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>
)}
{/* Imagen Destacada */}
<div className={`relative ${imageAspect} w-full overflow-hidden bg-slate-800 z-0`}>
<div className="absolute inset-0 bg-gradient-to-t from-slate-950 via-slate-950/20 to-transparent z-20 opacity-90 pointer-events-none"></div>
{imagenUrl ? (
<>
{/* Blurred Background to fill empty spaces nicely */}
<img src={imagenUrl} alt="" className="absolute inset-0 w-full h-full object-cover blur-2xl opacity-40 scale-110" aria-hidden="true" />
{/* Main Image contained properly without crop */}
<img src={imagenUrl} alt={titulo} className="relative z-10 w-full h-full object-contain transition-transform duration-700 group-hover:scale-105" />
</>
) : (
<div className="w-full h-full flex items-center justify-center bg-gradient-to-br from-slate-900 to-slate-950 relative">
<div className="absolute inset-0 bg-[radial-gradient(circle_at_center,_var(--accent-glow))] opacity-5 pointer-events-none"></div>
<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">
<span className="px-3 py-1.5 rounded-full text-xs font-bold uppercase tracking-wider backdrop-blur-md border border-white/20 bg-black/40 text-cyan-300 shadow-[0_0_10px_rgba(6,182,212,0.2)]">
{tipo?.toUpperCase() || 'NOTICIA'}
</span>
</div>
)}
</div>
{/* Contenido */}
<div className="flex flex-col flex-grow p-6 relative z-20">
<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>
{/* Footer del card */}
{(showFooterDate || footerText) && (
<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">
<Calendar size={12} />
{fechaFormateada}
</div>
) : (
footerText || ""
)}
</span>
{showFooterButton && footerText && (
<div className="w-8 h-8 rounded-full bg-white/5 flex items-center justify-center border border-white/10 group-hover:bg-cyan-500 group-hover:border-cyan-400 group-hover:text-slate-950 transition-all">
<ArrowUpRight size={16} className="transition-transform group-hover:translate-x-0.5 group-hover:-translate-y-0.5" />
</div>
)}
</div>
)}
</div>
</div>
);
};