import { useState, useCallback } from "react";
import type { ChainSummary, ChainStatus } from "../../lib/api";
interface ChainListProps {
chains: ChainSummary[];
loading: boolean;
onSelect: (chainId: string) => void;
onCreate: () => void;
selectedId?: string;
onArchive: (chain: ChainSummary) => void;
}
export function ChainList({
chains,
loading,
onSelect,
onCreate,
selectedId,
onArchive,
}: ChainListProps) {
const [statusFilter, setStatusFilter] = useState<ChainStatus | "all">("all");
const [contextMenu, setContextMenu] = useState<{
chain: ChainSummary;
x: number;
y: number;
} | null>(null);
const filteredChains = chains.filter((chain) =>
statusFilter === "all" ? true : chain.status === statusFilter
);
const handleContextMenu = useCallback(
(e: React.MouseEvent, chain: ChainSummary) => {
e.preventDefault();
setContextMenu({ chain, x: e.clientX, y: e.clientY });
},
[]
);
const closeContextMenu = useCallback(() => {
setContextMenu(null);
}, []);
const handleArchive = useCallback(() => {
if (contextMenu) {
onArchive(contextMenu.chain);
setContextMenu(null);
}
}, [contextMenu, onArchive]);
const getStatusColor = (status: ChainStatus) => {
switch (status) {
case "active":
return "text-[#4ade80] bg-[#4ade80]/10";
case "completed":
return "text-[#60a5fa] bg-[#60a5fa]/10";
case "archived":
return "text-[#6b7280] bg-[#6b7280]/10";
default:
return "text-[#8b949e] bg-[#8b949e]/10";
}
};
const getStatusIcon = (status: ChainStatus) => {
switch (status) {
case "active":
return (
<svg className="w-3 h-3" viewBox="0 0 24 24" fill="currentColor">
<circle cx="12" cy="12" r="4" />
</svg>
);
case "completed":
return (
<svg className="w-3 h-3" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3">
<polyline points="20 6 9 17 4 12" />
</svg>
);
case "archived":
return (
<svg className="w-3 h-3" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<path d="M21 8v13H3V8" />
<path d="M1 3h22v5H1z" />
<path d="M10 12h4" />
</svg>
);
default:
return null;
}
};
return (
<div className="panel h-full flex flex-col" onClick={closeContextMenu}>
{/* Header */}
<div className="p-3 border-b border-[rgba(117,170,252,0.2)]">
<div className="flex items-center justify-between mb-3">
<h2 className="font-mono text-sm text-[#75aafc] uppercase">Chains</h2>
<button
onClick={onCreate}
className="px-3 py-1 font-mono text-xs text-[#dbe7ff] bg-[#0f3c78] border border-[#3f6fb3] hover:bg-[#153667] transition-colors"
>
+ New
</button>
</div>
{/* Status filter */}
<div className="flex gap-1">
{(["all", "active", "completed", "archived"] as const).map((status) => (
<button
key={status}
onClick={() => setStatusFilter(status)}
className={`px-2 py-1 font-mono text-[10px] uppercase transition-colors ${
statusFilter === status
? "bg-[#0f3c78] text-[#dbe7ff] border border-[#75aafc]"
: "bg-[#0d1b2d] text-[#8b949e] border border-[#3f6fb3] hover:border-[#75aafc]"
}`}
>
{status}
</button>
))}
</div>
</div>
{/* Chain list */}
<div className="flex-1 overflow-y-auto">
{loading ? (
<div className="flex items-center justify-center h-32">
<p className="font-mono text-xs text-[#8b949e]">Loading chains...</p>
</div>
) : filteredChains.length === 0 ? (
<div className="flex items-center justify-center h-32">
<p className="font-mono text-xs text-[#8b949e]">
{statusFilter === "all" ? "No chains yet" : `No ${statusFilter} chains`}
</p>
</div>
) : (
<div className="divide-y divide-[rgba(117,170,252,0.1)]">
{filteredChains.map((chain) => (
<div
key={chain.id}
onClick={() => onSelect(chain.id)}
onContextMenu={(e) => handleContextMenu(e, chain)}
className={`p-3 cursor-pointer transition-colors ${
selectedId === chain.id
? "bg-[rgba(117,170,252,0.15)]"
: "hover:bg-[rgba(117,170,252,0.05)]"
}`}
>
<div className="flex items-center justify-between mb-1">
<span className="font-mono text-sm text-[#dbe7ff] truncate">
{chain.name}
</span>
<span
className={`flex items-center gap-1 px-1.5 py-0.5 font-mono text-[10px] uppercase rounded ${getStatusColor(
chain.status
)}`}
>
{getStatusIcon(chain.status)}
{chain.status}
</span>
</div>
{chain.description && (
<p className="font-mono text-xs text-[#8b949e] truncate mb-1">
{chain.description}
</p>
)}
<div className="flex items-center gap-3 font-mono text-[10px] text-[#556677]">
<span>{chain.contractCount} contracts</span>
<span>
{new Date(chain.updatedAt).toLocaleDateString()}
</span>
</div>
</div>
))}
</div>
)}
</div>
{/* Context menu */}
{contextMenu && (
<div
className="fixed z-50 bg-[#0a1628] border border-[rgba(117,170,252,0.3)] shadow-lg py-1"
style={{ top: contextMenu.y, left: contextMenu.x }}
>
<button
onClick={() => {
onSelect(contextMenu.chain.id);
setContextMenu(null);
}}
className="w-full px-4 py-2 text-left font-mono text-xs text-[#dbe7ff] hover:bg-[rgba(117,170,252,0.1)]"
>
View Details
</button>
{contextMenu.chain.status !== "archived" && (
<button
onClick={handleArchive}
className="w-full px-4 py-2 text-left font-mono text-xs text-red-400 hover:bg-red-400/10"
>
Archive
</button>
)}
</div>
)}
</div>
);
}