haixunMaster/app/(dashboard)/published/page.tsx

112 lines
3.8 KiB
TypeScript

"use client";
import { useEffect, useState } from "react";
import { ExternalLink, Send } from "lucide-react";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
import { EmptyState } from "@/components/layout/empty-state";
import { PageHeader } from "@/components/layout/page-header";
import { notify } from "@/lib/notifications/store";
interface PublishedItem {
id: string;
text: string;
angle?: string | null;
hook?: string | null;
permalink?: string | null;
publishedAt: string;
views?: number | null;
likes?: number | null;
replies?: number | null;
}
export default function PublishedPage() {
const [items, setItems] = useState<PublishedItem[]>([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch("/api/published")
.then(async (r) => {
const data = await r.json();
if (!r.ok) {
throw new Error(data.error ?? "無法載入已發布貼文");
}
setItems(data.published ?? []);
})
.catch((error) => {
notify({
type: "error",
title: "載入失敗",
message: error instanceof Error ? error.message : "請稍後再試",
});
})
.finally(() => setLoading(false));
}, []);
return (
<div>
<PageHeader
eyebrow="SYSTEM"
title="成效紀錄"
description="查看已發布到 Threads 的貼文,追蹤瀏覽與互動數據。"
/>
{loading ? (
<div className="space-y-4">
{[0, 1].map((i) => (
<div key={i} className="skeleton h-32 animate-pulse" />
))}
</div>
) : items.length === 0 ? (
<EmptyState
icon={Send}
title="尚無已發布貼文"
description="完成海巡與草稿審核後,發布的貼文會出現在這裡。"
/>
) : (
<div className="space-y-4">
{items.map((item, i) => (
<Card
key={item.id}
className="animate-fade-in-up"
style={{ animationDelay: `${i * 50}ms` }}
>
<CardHeader>
<CardTitle>{item.angle ?? "已發布貼文"}</CardTitle>
<CardDescription className="flex flex-wrap items-center gap-3">
<span>{new Date(item.publishedAt).toLocaleString("zh-TW")}</span>
{item.permalink && (
<a
href={item.permalink}
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center gap-1 text-muted-foreground transition-colors hover:text-foreground"
>
<ExternalLink className="h-3 w-3" />
</a>
)}
</CardDescription>
</CardHeader>
<CardContent>
{item.hook && (
<p className="mb-2 text-[14px] font-medium leading-snug">{item.hook}</p>
)}
<p className="whitespace-pre-wrap text-[15px] leading-[1.7] text-foreground/90">
{item.text}
</p>
{(item.views != null || item.likes != null || item.replies != null) && (
<div className="mt-4 flex flex-wrap gap-4 border-t border-border pt-3 text-sm text-muted-foreground">
{item.views != null && <span> {item.views}</span>}
{item.likes != null && <span> {item.likes}</span>}
{item.replies != null && <span> {item.replies}</span>}
</div>
)}
</CardContent>
</Card>
))}
</div>
)}
</div>
);
}