"use client" import type React from "react" import { useEffect, useState } from "react" import { Download, Trash2, RefreshCw, ExternalLink, Copy, CheckCircle, Tag, Check } from "lucide-react" interface PackageCardProps { id: number name: string description: string version: string status: string isInstalled: boolean installedVersion?: string onInstall: (pluginId: number) => void onUninstall: (pluginId: number) => void onUpdate: (pluginId: number) => void } const PackageCard: React.FC = ({ id, name, description, version, status, isInstalled, installedVersion, onInstall, onUninstall, onUpdate, }) => { const isUpdateAvailable = isInstalled && installedVersion ? version > installedVersion : false type Snippet = { type: string; target_file: string; content: string } const [snippets, setSnippets] = useState(null) const [loadingSnippets, setLoadingSnippets] = useState(false) const [errorSnippets, setErrorSnippets] = useState(null) // Track expanded/collapsed state per snippet kind for the preview blocks const [expandedSnippets, setExpandedSnippets] = useState<{ [k: string]: boolean }>({}) useEffect(() => { let cancelled = false setLoadingSnippets(true) fetch(`http://localhost:3001/api/plugins/${id}/snippets`) .then(async (res) => { const data = await res.json() if (!res.ok) throw new Error(data?.error || `Failed to load snippets (${res.status})`) return data.data as Snippet[] }) .then((snips) => { if (!cancelled) setSnippets(snips) }) .catch((err) => { if (!cancelled) setErrorSnippets(err.message) }) .finally(() => { if (!cancelled) setLoadingSnippets(false) }) return () => { cancelled = true } }, [id]) const ActionButton: React.FC = () => { if (isUpdateAvailable) { return ( ) } if (isInstalled) { return ( ) } return ( ) } return (

{name}

{isInstalled && }

{description}

Available: {version} {isInstalled && ( Installed: {installedVersion} )} {status}
{/* Snippets area (list view only component) */}

Step definitions snippets Usage:

{loadingSnippets && (
Loading snippets…
)} {errorSnippets && (
{errorSnippets}
)} {snippets && snippets.length > 0 && (
{["step_definition", "pom_function"].map((kind) => { const snip = snippets.find((s) => s.type === kind) if (!snip) return null const title = kind === "step_definition" ? "stepDefinition" : "Source code" // Determine if we should truncate based on length or lines const lineCount = snip.content.split('\n').length const needTruncate = lineCount > 12 || snip.content.length > 600 const isExpanded = !!expandedSnippets[kind] const toggle = () => setExpandedSnippets((prev) => ({ ...prev, [kind]: !prev[kind] })) return (
{kind === "pom_function" && (

Source code snippets Usage:

)}
{title} · {snip.target_file}
{snip.content}
{!isExpanded && needTruncate && (
)}
{needTruncate && (
)}
) })}
)}
) } export default PackageCard