/** * Host Chain Sub-Panel * Panel for configuring SSH jump host chain */ import { ArrowDown,Plus,Search,X } from 'lucide-react'; import React, { useMemo, useState } from 'react'; import { useI18n } from '../../application/i18n/I18nProvider'; import { Host } from '../../types'; import { DistroAvatar } from '../DistroAvatar'; import { AsidePanel, type AsidePanelLayout, type AsidePanelResizeProps } from '../ui/aside-panel'; import { Button } from '../ui/button'; import { Card } from '../ui/card'; import { FixedSizeVirtualList } from '../ui/FixedSizeVirtualList'; import { Input } from '../ui/input'; import { ScrollArea } from '../ui/scroll-area'; const CHAIN_HOST_ROW_HEIGHT = 52; const CHAIN_HOST_VIEWPORT_HEIGHT = 256; export interface ChainPanelProps { formLabel: string; formHostname: string; form: Host; chainedHosts: Host[]; availableHostsForChain: Host[]; onAddHost: (hostId: string) => void; onRemoveHost: (index: number) => void; onClearChain: () => void; onBack: () => void; onCancel: () => void; layout?: AsidePanelLayout; } export type ChainPanelPropsWithResize = ChainPanelProps & AsidePanelResizeProps; export const ChainPanel: React.FC = ({ formLabel, formHostname, form, chainedHosts, availableHostsForChain, onAddHost, onRemoveHost, onClearChain, onBack, onCancel, layout = 'overlay', resizable, persistWidthStorageKey, resizeAriaLabel, }) => { const { t } = useI18n(); const [searchQuery, setSearchQuery] = useState(''); const filteredHosts = useMemo(() => { if (!searchQuery.trim()) return availableHostsForChain; const q = searchQuery.toLowerCase(); return availableHostsForChain.filter( (host) => host.label.toLowerCase().includes(q) || host.hostname.toLowerCase().includes(q) ); }, [availableHostsForChain, searchQuery]); return ( {t('common.save')} } >
{/* Chain visualization */}
{chainedHosts.map((host, index) => ( {index > 0 && (
)}
{host.label || host.hostname}
))} {chainedHosts.length > 0 && (
)} {/* Target host (current) */}
{formLabel || formHostname || t('hostDetails.chain.target')}
{t('hostDetails.chain.target')}
{/* Available hosts to add */} {availableHostsForChain.length > 0 && (

{t('hostDetails.chain.availableHosts')}

setSearchQuery(e.target.value)} placeholder={t('common.searchPlaceholder')} className="h-8 pl-8 text-sm" />
host.id} renderItem={(host, index) => ( )} />
)} {chainedHosts.length > 0 && ( )}
); }; export default ChainPanel;