Переглянути джерело

fix(inbounds): bulk-delete keeps last client to satisfy backend constraint

DelClient rejects the removal that would leave an inbound with zero
clients (the constraint exists because Xray protocols need at least
one client to keep the inbound functional). The bulk-delete flow
fired one DelClient call per picked client in a loop, so picking
every client meant the final iteration always errored out with
"no client remained in Inbound" and surfaced as a red toast even
though N-1 deletions had already gone through.

Now confirmBulkDelete detects the "all selected" case up front,
drops the last client from the request, and surfaces the partial
operation in the confirm dialog ("N-1 / N — last selected will
remain. Delete the inbound to remove all."). The pre-existing
single-row delete path and partial-selection bulk delete paths are
untouched. If the only client in the inbound is selected, a
Modal.warning explains the constraint instead of asking for confirm.
MHSanaei 19 годин тому
батько
коміт
d8aedcdde4
1 змінених файлів з 19 додано та 3 видалено
  1. 19 3
      frontend/src/pages/inbounds/ClientRowTable.vue

+ 19 - 3
frontend/src/pages/inbounds/ClientRowTable.vue

@@ -219,14 +219,30 @@ watch(clients, (list) => {
 function confirmBulkDelete() {
   const picked = clients.value.filter((c) => selected.value.has(rowKey(c)));
   if (picked.length === 0) return;
+
+  const total = clients.value.length;
+  const keepLast = picked.length === total;
+  const toDelete = keepLast ? picked.slice(0, -1) : picked;
+
+  if (toDelete.length === 0) {
+    Modal.warning({
+      title: t('pages.inbounds.deleteClient'),
+      content: 'Inbound must keep at least one client — delete the inbound to remove all.',
+      okText: t('confirm'),
+    });
+    return;
+  }
+
   Modal.confirm({
-    title: t('pages.inbounds.deleteClient') + ` — ${picked.length}`,
-    content: t('pages.inbounds.deleteClientContent'),
+    title: `${t('pages.inbounds.deleteClient')} — ${toDelete.length}${keepLast ? ` / ${total}` : ''}`,
+    content: keepLast
+      ? 'Inbound must keep at least one client — the last selected will remain. Delete the inbound to remove all.'
+      : t('pages.inbounds.deleteClientContent'),
     okText: t('delete'),
     okType: 'danger',
     cancelText: t('cancel'),
     onOk: () => {
-      emit('delete-clients', { dbInbound: props.dbInbound, clients: picked });
+      emit('delete-clients', { dbInbound: props.dbInbound, clients: toDelete });
       clearSelection();
     },
   });