You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
192 lines
5.8 KiB
192 lines
5.8 KiB
"use client";
|
|
|
|
import { TemplateCategory } from "@/types/quickstart";
|
|
|
|
interface TemplateCardProps {
|
|
template: {
|
|
id: string;
|
|
name: string;
|
|
description: string;
|
|
icon: string;
|
|
category: TemplateCategory;
|
|
tags: string[];
|
|
};
|
|
nodeCount: number;
|
|
previewImage?: string;
|
|
hoverImage?: string;
|
|
isLoading?: boolean;
|
|
onUseWorkflow: () => void;
|
|
disabled?: boolean;
|
|
}
|
|
|
|
const CATEGORY_LABELS: Record<TemplateCategory, string> = {
|
|
simple: "Simple",
|
|
advanced: "Advanced",
|
|
community: "Community",
|
|
};
|
|
|
|
const CATEGORY_COLORS: Record<TemplateCategory, string> = {
|
|
simple: "bg-blue-500/20 text-blue-300",
|
|
advanced: "bg-purple-500/20 text-purple-300",
|
|
community: "bg-amber-500/20 text-amber-300",
|
|
};
|
|
|
|
export function TemplateCard({
|
|
template,
|
|
nodeCount,
|
|
previewImage,
|
|
hoverImage,
|
|
isLoading = false,
|
|
onUseWorkflow,
|
|
disabled = false,
|
|
}: TemplateCardProps) {
|
|
return (
|
|
<div
|
|
className={`
|
|
group w-full rounded-lg border p-4 transition-all flex gap-4
|
|
${
|
|
isLoading
|
|
? "bg-blue-600/20 border-blue-500/50"
|
|
: "bg-neutral-900/50 border-neutral-700"
|
|
}
|
|
${disabled && !isLoading ? "opacity-50" : ""}
|
|
`}
|
|
>
|
|
{/* Thumbnail - Left side (square) */}
|
|
<div
|
|
className={`
|
|
w-36 h-36 flex-shrink-0 rounded-lg overflow-hidden relative
|
|
${
|
|
isLoading
|
|
? "bg-blue-500/20"
|
|
: "bg-neutral-800"
|
|
}
|
|
`}
|
|
>
|
|
{previewImage ? (
|
|
<>
|
|
{/* Primary image */}
|
|
{/* eslint-disable-next-line @next/next/no-img-element */}
|
|
<img
|
|
src={previewImage}
|
|
alt={`${template.name} preview`}
|
|
className={`absolute inset-0 w-full h-full object-cover transition-opacity duration-300 ${hoverImage ? "group-hover:opacity-0" : ""}`}
|
|
style={{ imageRendering: "auto" }}
|
|
/>
|
|
{/* Hover image (if provided) */}
|
|
{hoverImage && (
|
|
/* eslint-disable-next-line @next/next/no-img-element */
|
|
<img
|
|
src={hoverImage}
|
|
alt={`${template.name} hover preview`}
|
|
className="absolute inset-0 w-full h-full object-cover opacity-0 group-hover:opacity-100 transition-opacity duration-300"
|
|
style={{ imageRendering: "auto" }}
|
|
/>
|
|
)}
|
|
</>
|
|
) : (
|
|
<div className="absolute inset-0 flex items-center justify-center">
|
|
<svg
|
|
className="w-8 h-8 text-neutral-600"
|
|
fill="none"
|
|
viewBox="0 0 24 24"
|
|
stroke="currentColor"
|
|
strokeWidth={1.5}
|
|
>
|
|
<path strokeLinecap="round" strokeLinejoin="round" d={template.icon} />
|
|
</svg>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* Content - Right side */}
|
|
<div className="flex-1 flex flex-col min-w-0">
|
|
{/* Header row */}
|
|
<div className="flex items-start justify-between gap-2 mb-1">
|
|
<h3 className="text-sm font-medium text-neutral-200 truncate">
|
|
{template.name}
|
|
</h3>
|
|
<span
|
|
className={`
|
|
inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-medium flex-shrink-0
|
|
${CATEGORY_COLORS[template.category]}
|
|
`}
|
|
>
|
|
{CATEGORY_LABELS[template.category]}
|
|
</span>
|
|
</div>
|
|
|
|
{/* Description */}
|
|
<p className="text-xs text-neutral-400 line-clamp-2 flex-1">
|
|
{template.description}
|
|
</p>
|
|
|
|
{/* Provider tags and node count */}
|
|
<div className="flex flex-wrap gap-1 mt-2">
|
|
{template.tags.map((tag) => (
|
|
<span
|
|
key={tag}
|
|
className="inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-medium bg-neutral-700/30 text-neutral-400"
|
|
>
|
|
{tag}
|
|
</span>
|
|
))}
|
|
<span className="inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-medium bg-neutral-700/50 text-neutral-400">
|
|
{nodeCount} nodes
|
|
</span>
|
|
</div>
|
|
|
|
{/* Action row */}
|
|
<div className="flex justify-end mt-2">
|
|
<button
|
|
onClick={onUseWorkflow}
|
|
disabled={disabled || isLoading}
|
|
className="px-3 py-1.5 text-xs font-medium bg-blue-600 hover:bg-blue-500 text-white rounded-md transition-colors disabled:opacity-50 disabled:cursor-not-allowed flex items-center gap-1.5"
|
|
>
|
|
{isLoading ? (
|
|
<>
|
|
<svg
|
|
className="w-3 h-3 animate-spin"
|
|
fill="none"
|
|
viewBox="0 0 24 24"
|
|
>
|
|
<circle
|
|
className="opacity-25"
|
|
cx="12"
|
|
cy="12"
|
|
r="10"
|
|
stroke="currentColor"
|
|
strokeWidth="4"
|
|
/>
|
|
<path
|
|
className="opacity-75"
|
|
fill="currentColor"
|
|
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
|
/>
|
|
</svg>
|
|
Loading...
|
|
</>
|
|
) : (
|
|
<>
|
|
Use workflow
|
|
<svg
|
|
className="w-3 h-3"
|
|
fill="none"
|
|
viewBox="0 0 24 24"
|
|
stroke="currentColor"
|
|
strokeWidth={2}
|
|
>
|
|
<path
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
d="M13.5 4.5L21 12m0 0l-7.5 7.5M21 12H3"
|
|
/>
|
|
</svg>
|
|
</>
|
|
)}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|