mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-09-01 07:57:14 +00:00
feat(clients): selective bulk attach + new bulk detach
Inbounds page: - AttachClientsModal now shows a per-client selection table (email, comment, enabled tag) with search and a live "selected of total" counter; all clients are pre-selected so the old "attach all" workflow stays a single OK click. - New DetachClientsModal on the inbound row menu lets you pick which clients to remove from that inbound (records are kept so they can be re-attached later; for full removal use Delete). Clients page: - New "Attach (N)" bulk-action button + BulkAttachInboundsModal that attaches selected clients to one or more multi-user inbounds. - New "Detach (N)" bulk-action button + BulkDetachInboundsModal that removes selected clients from chosen inbounds; (email, inbound) pairs where the client isn't attached are silently skipped. Backend adds POST /panel/api/clients/bulkDetach, wrapping the existing Detach service for each email and reporting per-email detached/skipped/errors. ClientRecord rows are kept on detach to match the single-client endpoint; bulkDel remains the path for full removal.
This commit is contained in:
@@ -39,6 +39,7 @@ const InboundFormModal = lazy(() => import('./InboundFormModal'));
|
||||
const InboundInfoModal = lazy(() => import('./InboundInfoModal'));
|
||||
const QrCodeModal = lazy(() => import('./QrCodeModal'));
|
||||
const AttachClientsModal = lazy(() => import('./AttachClientsModal'));
|
||||
const DetachClientsModal = lazy(() => import('./DetachClientsModal'));
|
||||
const AssignClientsGroupModal = lazy(() => import('./AssignClientsGroupModal'));
|
||||
|
||||
type RowAction =
|
||||
@@ -52,6 +53,7 @@ type RowAction =
|
||||
| 'resetTraffic'
|
||||
| 'delAllClients'
|
||||
| 'attachClients'
|
||||
| 'detachClients'
|
||||
| 'assignGroup'
|
||||
| 'clone';
|
||||
|
||||
@@ -127,6 +129,8 @@ export default function InboundsPage() {
|
||||
|
||||
const [attachOpen, setAttachOpen] = useState(false);
|
||||
const [attachSource, setAttachSource] = useState<DBInbound | null>(null);
|
||||
const [detachOpen, setDetachOpen] = useState(false);
|
||||
const [detachSource, setDetachSource] = useState<DBInbound | null>(null);
|
||||
|
||||
const [groupOpen, setGroupOpen] = useState(false);
|
||||
const [groupSource, setGroupSource] = useState<DBInbound | null>(null);
|
||||
@@ -489,6 +493,10 @@ export default function InboundsPage() {
|
||||
setAttachSource(target);
|
||||
setAttachOpen(true);
|
||||
break;
|
||||
case 'detachClients':
|
||||
setDetachSource(target);
|
||||
setDetachOpen(true);
|
||||
break;
|
||||
case 'assignGroup':
|
||||
setGroupSource(target);
|
||||
setGroupOpen(true);
|
||||
@@ -614,6 +622,14 @@ export default function InboundsPage() {
|
||||
dbInbounds={dbInbounds}
|
||||
/>
|
||||
</LazyMount>
|
||||
<LazyMount when={detachOpen}>
|
||||
<DetachClientsModal
|
||||
open={detachOpen}
|
||||
onClose={() => setDetachOpen(false)}
|
||||
onDetached={refresh}
|
||||
source={detachSource}
|
||||
/>
|
||||
</LazyMount>
|
||||
<LazyMount when={groupOpen}>
|
||||
<AssignClientsGroupModal
|
||||
open={groupOpen}
|
||||
|
||||
Reference in New Issue
Block a user