Files
desktop-app/components/blog-card.tsx
T

85 lines
3.4 KiB
TypeScript

"use client";
import { Blog, HomelabApp } from "@/types";
import { useEffect, useState } from "react";
import { invoke } from "@tauri-apps/api/core";
import { useRouter } from "next/navigation";
interface BlogCardProps {
blog: Blog & { linked_app_ids?: string[] };
}
export default function BlogCard({ blog }: BlogCardProps) {
const [referencedApps, setReferencedApps] = useState<HomelabApp[]>([]);
const appIds = blog.linked_app_ids;
const router = useRouter();
useEffect(() => {
if (appIds && appIds.length > 0) {
const fetchReferences = async () => {
try {
const apps = await invoke<HomelabApp[]>("get_apps_by_id", {
ids: appIds,
});
setReferencedApps(apps);
} catch (err) {
console.error("Failed to fetch referenced apps:", err);
}
};
fetchReferences();
}
}, [appIds]);
return (
<div
className="group flex flex-col justify-between p-6 border border-slate-200/80 rounded-2xl bg-gradient-to-b from-white to-slate-50/50 dark:from-slate-900 dark:to-slate-950 text-card-foreground shadow-sm hover:shadow-xl hover:-translate-y-1 hover:border-emerald-600/40 transition-all duration-300 h-64 relative overflow-hidden"
onClick={() => router.push(`/write/${blog.id}`)}
>
{/* Decorative background glow that triggers on hover */}
<div className="absolute -right-10 -top-10 w-24 h-24 bg-emerald-500/10 rounded-full blur-2xl group-hover:bg-emerald-500/20 transition-all duration-500" />
<div>
{/* Category / Meta indicator */}
<span className="text-[10px] font-bold tracking-wider uppercase text-emerald-700/80 dark:text-emerald-400 mb-1 block">
Homelab Log
</span>
{/* Title */}
<h3 className="text-xl font-bold tracking-tight line-clamp-1 mb-2 text-slate-800 dark:text-slate-100 group-hover:text-emerald-700 dark:group-hover:text-emerald-400 transition-colors duration-200">
{blog.title}
</h3>
{/* Truncated Tiptap HTML Content */}
<div
className="prose prose-sm dark:prose-invert line-clamp-3 text-slate-500 dark:text-slate-400 overflow-hidden max-w-none leading-relaxed"
dangerouslySetInnerHTML={{ __html: blog.content }}
/>
</div>
{/* Footer / References */}
<div className="mt-4 pt-4 border-t border-slate-100 dark:border-slate-800/80 flex flex-wrap gap-2 items-center overflow-hidden max-h-12">
{!appIds || appIds.length === 0 ? (
<span className="text-xs text-slate-400 italic">
No linked applications
</span>
) : (
referencedApps.map((app) => (
<span
key={app.id}
title={app.address}
className="inline-flex items-center px-2.5 py-1 rounded-lg text-xs font-semibold bg-emerald-500/10 text-emerald-700 dark:text-emerald-400 border border-emerald-500/20 hover:bg-emerald-500/20 transition-colors"
>
{/* App Avatar Placeholder (Uses first letter of app name) */}
<span className="w-4 h-4 rounded bg-emerald-700 text-white dark:bg-emerald-500 dark:text-slate-950 font-bold text-[9px] flex items-center justify-center mr-1.5 shadow-sm uppercase shrink-0">
{app.name.charAt(0)}
</span>
{app.name}
</span>
))
)}
</div>
</div>
);
}