import { useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { Button, Card, Space, Switch, Table, Tag, Tooltip } from 'antd'; import type { ColumnsType } from 'antd/es/table'; import { ArrowDownOutlined, ArrowUpOutlined, DeleteOutlined, EditOutlined, GlobalOutlined, PlusOutlined, } from '@ant-design/icons'; import type { HostRecord } from '@/api/queries/useHostsQuery'; import type { InboundOption } from '@/schemas/client'; import './HostList.css'; interface HostListProps { hosts: HostRecord[]; inboundOptions: InboundOption[]; loading?: boolean; isMobile?: boolean; selectedIds: number[]; onSelectionChange: (ids: number[]) => void; onAdd: () => void; onEdit: (host: HostRecord) => void; onDelete: (host: HostRecord) => void; onToggleEnable: (host: HostRecord, next: boolean) => void; onMove: (host: HostRecord, dir: 'up' | 'down') => void; onBulkEnable: (enable: boolean) => void; onBulkDelete: () => void; } // Sorted by inbound then sort_order then id — the same order the subscription // renderer uses, so the list mirrors the emitted link order. function sortHosts(hosts: HostRecord[]): HostRecord[] { return [...hosts].sort((a, b) => { if (a.inboundId !== b.inboundId) return a.inboundId - b.inboundId; const sa = a.sortOrder ?? 0; const sb = b.sortOrder ?? 0; if (sa !== sb) return sa - sb; return a.id - b.id; }); } export default function HostList(props: HostListProps) { const { t } = useTranslation(); const { hosts, inboundOptions, loading, isMobile, selectedIds, onSelectionChange, onAdd, onEdit, onDelete, onToggleEnable, onMove, onBulkEnable, onBulkDelete, } = props; const inboundLabel = useMemo(() => { const map = new Map(); for (const ib of inboundOptions) map.set(ib.id, ib.remark || ib.tag || `#${ib.id}`); return map; }, [inboundOptions]); const sorted = useMemo(() => sortHosts(hosts), [hosts]); // Move is bounded to neighbours within the same inbound (sort_order is per-inbound). const movable = useMemo(() => { const byInbound = new Map(); const idxInGroup = new Map(); const counters = new Map(); for (const h of sorted) byInbound.set(h.inboundId, (byInbound.get(h.inboundId) ?? 0) + 1); for (const h of sorted) { const c = counters.get(h.inboundId) ?? 0; idxInGroup.set(h.id, c); counters.set(h.inboundId, c + 1); } return { byInbound, idxInGroup }; }, [sorted]); // Column order requested: Actions, Enable, then the rest. const columns: ColumnsType = [ { title: t('pages.hosts.fields.actions'), key: 'actions', width: 168, render: (_, h) => { const idx = movable.idxInGroup.get(h.id) ?? 0; const count = movable.byInbound.get(h.inboundId) ?? 1; return ( ) : ( <> onSelectionChange([])} style={{ marginInlineEnd: 0, padding: '4px 8px', fontSize: 13 }} > {t('pages.hosts.selectedCount', { count: selectedIds.length })} )} ); return ( rowKey="id" size="small" loading={loading} columns={columns} dataSource={sorted} pagination={false} scroll={{ x: 'max-content' }} rowSelection={{ selectedRowKeys: selectedIds, onChange: (keys) => onSelectionChange(keys as number[]), }} locale={{ emptyText: (
{t('noData')}
), }} />
); }