|
|
|
@ -1,5 +1,6 @@ |
|
|
|
"use client"; |
|
|
|
|
|
|
|
import { useState, useEffect } from "react"; |
|
|
|
import { TemplateCategory } from "@/types/quickstart"; |
|
|
|
|
|
|
|
interface TemplateCardProps { |
|
|
|
@ -12,6 +13,7 @@ interface TemplateCardProps { |
|
|
|
tags: string[]; |
|
|
|
}; |
|
|
|
nodeCount: number; |
|
|
|
previewImages?: string[]; |
|
|
|
isLoading: boolean; |
|
|
|
onClick: () => void; |
|
|
|
disabled: boolean; |
|
|
|
@ -34,10 +36,31 @@ const CATEGORY_COLORS: Record<TemplateCategory, string> = { |
|
|
|
export function TemplateCard({ |
|
|
|
template, |
|
|
|
nodeCount, |
|
|
|
previewImages = [], |
|
|
|
isLoading, |
|
|
|
onClick, |
|
|
|
disabled, |
|
|
|
}: TemplateCardProps) { |
|
|
|
const [currentImageIndex, setCurrentImageIndex] = useState(0); |
|
|
|
const [isTransitioning, setIsTransitioning] = useState(false); |
|
|
|
|
|
|
|
// Cycle through preview images
|
|
|
|
useEffect(() => { |
|
|
|
if (previewImages.length <= 1) return; |
|
|
|
|
|
|
|
const interval = setInterval(() => { |
|
|
|
setIsTransitioning(true); |
|
|
|
setTimeout(() => { |
|
|
|
setCurrentImageIndex((prev) => (prev + 1) % previewImages.length); |
|
|
|
setIsTransitioning(false); |
|
|
|
}, 300); // Fade out duration
|
|
|
|
}, 2500); // Time between transitions
|
|
|
|
|
|
|
|
return () => clearInterval(interval); |
|
|
|
}, [previewImages.length]); |
|
|
|
|
|
|
|
const hasImages = previewImages.length > 0; |
|
|
|
|
|
|
|
return ( |
|
|
|
<button |
|
|
|
onClick={onClick} |
|
|
|
@ -47,25 +70,26 @@ export function TemplateCard({ |
|
|
|
${ |
|
|
|
isLoading |
|
|
|
? "bg-blue-600/20 border-blue-500/50" |
|
|
|
: "bg-neutral-800/50 border-neutral-700 hover:border-neutral-600 hover:bg-neutral-800/70" |
|
|
|
: "bg-neutral-900/50 border-neutral-700 hover:border-neutral-500 hover:bg-neutral-900/70" |
|
|
|
} |
|
|
|
${disabled && !isLoading ? "opacity-50 cursor-not-allowed" : "cursor-pointer"} |
|
|
|
`}
|
|
|
|
> |
|
|
|
{/* Icon */} |
|
|
|
{/* Thumbnail Area */} |
|
|
|
<div |
|
|
|
className={` |
|
|
|
w-12 h-12 rounded-lg flex items-center justify-center mb-3 |
|
|
|
w-full aspect-[4/3] rounded-lg mb-3 overflow-hidden relative |
|
|
|
${ |
|
|
|
isLoading |
|
|
|
? "bg-blue-500/30" |
|
|
|
: "bg-neutral-700/50 group-hover:bg-neutral-700" |
|
|
|
? "bg-blue-500/20" |
|
|
|
: "bg-neutral-800 group-hover:bg-neutral-700/80" |
|
|
|
} |
|
|
|
`}
|
|
|
|
> |
|
|
|
{isLoading ? ( |
|
|
|
<div className="absolute inset-0 flex items-center justify-center"> |
|
|
|
<svg |
|
|
|
className="w-5 h-5 text-blue-400 animate-spin" |
|
|
|
className="w-6 h-6 text-blue-400 animate-spin" |
|
|
|
fill="none" |
|
|
|
viewBox="0 0 24 24" |
|
|
|
> |
|
|
|
@ -83,9 +107,37 @@ export function TemplateCard({ |
|
|
|
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> |
|
|
|
</div> |
|
|
|
) : hasImages ? ( |
|
|
|
<> |
|
|
|
{/* eslint-disable-next-line @next/next/no-img-element */} |
|
|
|
<img |
|
|
|
src={previewImages[currentImageIndex]} |
|
|
|
alt={`${template.name} preview`} |
|
|
|
className={` |
|
|
|
w-full h-full object-cover transition-opacity duration-300 |
|
|
|
${isTransitioning ? "opacity-0" : "opacity-100"} |
|
|
|
`}
|
|
|
|
/> |
|
|
|
{/* Image indicator dots */} |
|
|
|
{previewImages.length > 1 && ( |
|
|
|
<div className="absolute bottom-2 left-1/2 -translate-x-1/2 flex gap-1"> |
|
|
|
{previewImages.map((_, idx) => ( |
|
|
|
<div |
|
|
|
key={idx} |
|
|
|
className={` |
|
|
|
w-1.5 h-1.5 rounded-full transition-colors |
|
|
|
${idx === currentImageIndex ? "bg-white" : "bg-white/40"} |
|
|
|
`}
|
|
|
|
/> |
|
|
|
))} |
|
|
|
</div> |
|
|
|
)} |
|
|
|
</> |
|
|
|
) : ( |
|
|
|
<div className="absolute inset-0 flex items-center justify-center"> |
|
|
|
<svg |
|
|
|
className="w-6 h-6 text-neutral-400 group-hover:text-neutral-300 transition-colors" |
|
|
|
className="w-8 h-8 text-neutral-600 group-hover:text-neutral-500 transition-colors" |
|
|
|
fill="none" |
|
|
|
viewBox="0 0 24 24" |
|
|
|
stroke="currentColor" |
|
|
|
@ -93,6 +145,7 @@ export function TemplateCard({ |
|
|
|
> |
|
|
|
<path strokeLinecap="round" strokeLinejoin="round" d={template.icon} /> |
|
|
|
</svg> |
|
|
|
</div> |
|
|
|
)} |
|
|
|
</div> |
|
|
|
|
|
|
|
|