ai-chatbot/components/agents/AgentPresetCard.tsx
Your Name 9a96824e2b
Some checks failed
Lint / build (20) (push) Has been cancelled
Playwright Tests / test (push) Has been cancelled
fix: align agent preset detail search and timezone
2026-03-21 21:14:11 +08:00

136 lines
4.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client";
import { Copy, Pencil, Sparkles, Trash2, Video } from "lucide-react";
import { Badge } from "@/components/ui/badge";
import { cn } from "@/lib/utils";
import type { AgentPreset } from "@/types/agent";
interface AgentPresetCardProps {
preset: AgentPreset;
selected?: boolean;
onSelect: (presetId: string) => void;
onFork?: (preset: AgentPreset) => void;
onEdit?: (preset: AgentPreset) => void;
onDelete?: (preset: AgentPreset) => void;
}
export function AgentPresetCard({
preset,
selected = false,
onSelect,
onFork,
onEdit,
onDelete,
}: AgentPresetCardProps) {
const previewUrl =
preset.avatarUrl ||
preset.referenceAssets?.[0]?.thumbnailUrl ||
preset.referenceAssets?.[0]?.originalUrl ||
"";
const ownerName =
String(preset.owner?.name || "").trim() ||
String(preset.owner?.email || "").trim() ||
"未知发布人";
const publishedAt = (() => {
const raw = preset.createdAt ? new Date(preset.createdAt) : null;
if (!raw || Number.isNaN(raw.getTime())) return "未知时间";
return raw.toLocaleDateString("zh-CN", {
timeZone: "Asia/Shanghai",
year: "numeric",
month: "2-digit",
day: "2-digit",
});
})();
return (
<div
className={cn(
"group overflow-hidden rounded-2xl border bg-background transition-all",
selected
? "border-primary bg-primary/5 shadow-[0_0_0_1px_rgba(59,130,246,0.12)]"
: "border-border hover:border-primary/40 hover:shadow-sm"
)}
>
<button
type="button"
className="block w-full text-left"
onClick={() => onSelect(preset.id)}
>
<div className="relative aspect-[4/3] w-full overflow-hidden bg-muted/40">
{previewUrl ? (
<img
src={previewUrl}
alt={preset.name}
className="h-full w-full object-cover transition-transform duration-300 group-hover:scale-[1.03]"
/>
) : (
<div className="flex h-full w-full items-center justify-center">
{preset.type === "video" ? (
<Video className="h-8 w-8 text-muted-foreground/50" />
) : (
<Sparkles className="h-8 w-8 text-muted-foreground/50" />
)}
</div>
)}
<div className="absolute left-2 top-2 flex flex-wrap gap-1.5">
<Badge variant="secondary" className="bg-white/90 text-black shadow-sm">
{preset.type === "video" ? "视频" : preset.type === "text" ? "生文" : "生图"}
</Badge>
</div>
<div className="absolute right-2 top-2 flex items-center gap-1 opacity-0 transition-opacity group-hover:opacity-100">
{preset.visibility === "private" && onEdit ? (
<button
type="button"
className="inline-flex h-8 w-8 items-center justify-center rounded-full bg-white/90 text-black shadow-sm transition-colors hover:bg-white"
onClick={(event) => {
event.stopPropagation();
onEdit(preset);
}}
title="编辑"
>
<Pencil className="h-4 w-4" />
</button>
) : null}
{preset.visibility === "private" && onDelete ? (
<button
type="button"
className="inline-flex h-8 w-8 items-center justify-center rounded-full bg-white/90 text-destructive shadow-sm transition-colors hover:bg-white"
onClick={(event) => {
event.stopPropagation();
onDelete(preset);
}}
title="删除"
>
<Trash2 className="h-4 w-4" />
</button>
) : null}
{preset.visibility === "public" && onFork ? (
<button
type="button"
className="inline-flex h-8 w-8 items-center justify-center rounded-full bg-white/90 text-black shadow-sm transition-colors hover:bg-white"
onClick={(event) => {
event.stopPropagation();
onFork(preset);
}}
title="另存为我的智能体"
>
<Copy className="h-4 w-4" />
</button>
) : null}
</div>
</div>
<div className="space-y-1.5 p-3">
<div className="line-clamp-1 text-sm font-semibold">{preset.name}</div>
<div className="line-clamp-1 text-xs text-muted-foreground">
发布人:{ownerName}
</div>
<div className="text-xs text-muted-foreground">
发布时间:{publishedAt}
</div>
</div>
</button>
</div>
);
}