import { useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Alert, Modal, Select, Typography, message } from 'antd'; import type { InboundOption } from '@/hooks/useClients'; import type { BulkAttachResult } from '@/schemas/client'; const MULTI_USER_PROTOCOLS = new Set(['vmess', 'vless', 'trojan', 'hysteria', 'shadowsocks']); interface BulkAttachInboundsModalProps { open: boolean; count: number; inbounds: InboundOption[]; onOpenChange: (open: boolean) => void; onSubmit: (inboundIds: number[]) => Promise; } export default function BulkAttachInboundsModal({ open, count, inbounds, onOpenChange, onSubmit, }: BulkAttachInboundsModalProps) { const { t } = useTranslation(); const [messageApi, messageContextHolder] = message.useMessage(); const [targetIds, setTargetIds] = useState([]); const [submitting, setSubmitting] = useState(false); useEffect(() => { if (open) setTargetIds([]); }, [open]); const targetOptions = useMemo(() => { return (inbounds || []) .filter((ib) => MULTI_USER_PROTOCOLS.has((ib.protocol || '').toLowerCase())) .map((ib) => ({ value: ib.id, label: `${ib.remark ?? ''} (${ib.protocol ?? ''}@${ib.port ?? ''})`, })); }, [inbounds]); async function submit() { if (targetIds.length === 0 || count === 0) return; setSubmitting(true); try { const result = await onSubmit(targetIds); if (!result) return; const attached = result.attached?.length ?? 0; const skipped = result.skipped?.length ?? 0; const errors = result.errors?.length ?? 0; if (errors > 0) { messageApi.warning( t('pages.inbounds.attachClientsResultMixed', { attached, skipped, errors }), ); } else { messageApi.success(t('pages.inbounds.attachClientsResult', { attached, skipped })); } onOpenChange(false); } finally { setSubmitting(false); } } return ( <> {messageContextHolder} onOpenChange(false)} onOk={submit} destroyOnHidden > {t('pages.clients.attachToInboundsDesc', { count })} {targetOptions.length === 0 ? ( ) : (