141 lines
7.0 KiB
TypeScript
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>
|
|
);
|
|
};
|