Browse Source

refactor(25-02): simplify to single thumbnail image

- Remove carousel animation, use single static preview image
- Simplify TemplateCard props from previewImages[] to previewImage
- Ready for future GIF replacement
handoff-20260429-1057
shrimbly 6 months ago
parent
commit
aab4c96677
  1. 58
      src/components/quickstart/TemplateCard.tsx
  2. 11
      src/components/quickstart/TemplateExplorerView.tsx

58
src/components/quickstart/TemplateCard.tsx

@ -1,6 +1,5 @@
"use client";
import { useState, useEffect } from "react";
import { TemplateCategory } from "@/types/quickstart";
interface TemplateCardProps {
@ -13,7 +12,7 @@ interface TemplateCardProps {
tags: string[];
};
nodeCount: number;
previewImages?: string[];
previewImage?: string;
isLoading: boolean;
onClick: () => void;
disabled: boolean;
@ -36,31 +35,11 @@ const CATEGORY_COLORS: Record<TemplateCategory, string> = {
export function TemplateCard({
template,
nodeCount,
previewImages = [],
previewImage,
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}
@ -108,32 +87,13 @@ export function TemplateCard({
/>
</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>
)}
</>
) : previewImage ? (
/* eslint-disable-next-line @next/next/no-img-element */
<img
src={previewImage}
alt={`${template.name} preview`}
className="w-full h-full object-cover"
/>
) : (
<div className="absolute inset-0 flex items-center justify-center">
<svg

11
src/components/quickstart/TemplateExplorerView.tsx

@ -69,15 +69,14 @@ export function TemplateExplorerView({
return metadata;
}, []);
// Get preview images for each template (from the "full" content level)
// Get preview image for each template (first image from "full" content level)
const previewImages = useMemo(() => {
const images: Record<string, string[]> = {};
const images: Record<string, string | undefined> = {};
PRESET_TEMPLATES.forEach((template) => {
const content = getTemplateContent(template.id, "full");
if (content?.images) {
images[template.id] = Object.values(content.images).map((img) => img.url);
} else {
images[template.id] = [];
const imageUrls = Object.values(content.images).map((img) => img.url);
images[template.id] = imageUrls[0];
}
});
return images;
@ -406,7 +405,7 @@ export function TemplateExplorerView({
key={preset.id}
template={preset}
nodeCount={presetMetadata[preset.id]?.nodeCount ?? 0}
previewImages={previewImages[preset.id]}
previewImage={previewImages[preset.id]}
isLoading={loadingWorkflowId === preset.id}
onClick={() => handlePresetSelect(preset.id)}
disabled={isLoading}

Loading…
Cancel
Save