feat(frontend): add text search to the inbound list

The v2.x panel could filter inbounds but the list page only had the node dropdown. Add a search box next to it matching on remark, port, and protocol, composed with the node filter; the dataset is already client-side, so no API change.

Closes #5267
This commit is contained in:
MHSanaei
2026-07-03 09:40:04 +02:00
parent 323cf09d10
commit 05cb70d8a8
@@ -5,6 +5,7 @@ import {
Card,
Checkbox,
Dropdown,
Input,
Select,
Space,
Switch,
@@ -22,6 +23,7 @@ import {
ReloadOutlined,
InfoCircleOutlined,
DeleteOutlined,
SearchOutlined,
} from '@ant-design/icons';
import { HttpUtil } from '@/utils';
@@ -56,6 +58,7 @@ export default function InboundList({
// Node filter (#4997): 'all' shows everything, 0 is the local-panel
// sentinel (inbounds without a nodeId), otherwise a node id. Session-only.
const [nodeFilter, setNodeFilter] = useState<number | 'all'>('all');
const [searchKey, setSearchKey] = useState('');
const showNodeFilter = useMemo(
() => nodesById.size > 0 || dbInbounds.some((ib) => ib.nodeId != null),
@@ -72,10 +75,17 @@ export default function InboundList({
);
const visibleInbounds = useMemo(() => {
if (nodeFilter === 'all') return dbInbounds;
if (nodeFilter === 0) return dbInbounds.filter((ib) => ib.nodeId == null);
return dbInbounds.filter((ib) => ib.nodeId === nodeFilter);
}, [dbInbounds, nodeFilter]);
let list = dbInbounds;
if (nodeFilter === 0) list = list.filter((ib) => ib.nodeId == null);
else if (nodeFilter !== 'all') list = list.filter((ib) => ib.nodeId === nodeFilter);
const q = searchKey.trim().toLowerCase();
if (!q) return list;
return list.filter((ib) => (
(ib.remark || '').toLowerCase().includes(q)
|| String(ib.port).includes(q)
|| (ib.protocol || '').toLowerCase().includes(q)
));
}, [dbInbounds, nodeFilter, searchKey]);
const onSwitchEnable = useCallback(async (dbInbound: DBInboundRecord, next: boolean) => {
const previous = dbInbound.enable;
@@ -180,6 +190,15 @@ export default function InboundList({
aria-label={t('pages.clients.filters.nodes')}
/>
)}
<Input
value={searchKey}
onChange={(e) => setSearchKey(e.target.value)}
placeholder={t('search')}
allowClear
prefix={<SearchOutlined />}
style={{ maxWidth: isMobile ? 110 : 200 }}
aria-label={t('search')}
/>
{selectedRowKeys.length > 0 && (
<>
<Tag color="blue" closable onClose={() => setSelectedRowKeys([])} style={{ marginInlineEnd: 0 }}>