85 lines
3.4 KiB
TypeScript
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/edit?id=${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>
|
|
);
|
|
}
|