From bf1b488a63b3613dc69e4b3e08c4c102c6a5dd9f Mon Sep 17 00:00:00 2001 From: MHSanaei Date: Thu, 28 May 2026 11:24:21 +0200 Subject: [PATCH] feat(clients): tidier bulk action toolbar MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When at least one client is selected, the toolbar now collapses to a small selection indicator plus the three most-used actions instead of spreading six count-suffixed buttons across the row: - Replaces every per-button "(N)" with a single closable "{N} selected" tag on the left — one click on its × clears the selection. - Hides "+ Add Clients" while a selection is active (focus mode). - Keeps Attach, Detach, and Delete as visible buttons; Delete is pushed to the right with auto margin so it doesn't sit flush against the non-destructive actions. - Folds Adjust, Group, and Sub links into the existing "more" dropdown, which is now context-aware: selection-scoped overflow when rows are picked, global actions (Add Bulk / Reset all / Del depleted) otherwise. On mobile the new buttons collapse to icon-only the same way as the rest of the toolbar. --- frontend/src/pages/clients/ClientsPage.tsx | 106 +++++++++++++-------- web/translation/en-US.json | 4 + web/translation/fa-IR.json | 4 + 3 files changed, 75 insertions(+), 39 deletions(-) diff --git a/frontend/src/pages/clients/ClientsPage.tsx b/frontend/src/pages/clients/ClientsPage.tsx index 42e21bef..dfb155a3 100644 --- a/frontend/src/pages/clients/ClientsPage.tsx +++ b/frontend/src/pages/clients/ClientsPage.tsx @@ -783,28 +783,25 @@ export default function ClientsPage() { hoverable title={
- - {selectedRowKeys.length > 0 && ( + {selectedRowKeys.length === 0 ? ( + + ) : ( <> - - + setSelectedRowKeys([])} + style={{ marginInlineEnd: 0, padding: '4px 8px', fontSize: 13 }} + > + {t('pages.clients.selectedCount', { count: selectedRowKeys.length })} + - - )} @@ -812,33 +809,64 @@ export default function ClientsPage() { trigger={['click']} placement="bottomRight" menu={{ - items: [ - { - key: 'bulk', - icon: , - label: t('pages.clients.bulk'), - onClick: () => setBulkAddOpen(true), - }, - { - key: 'resetAll', - icon: , - label: t('pages.clients.resetAllTraffics'), - onClick: onResetAllTraffics, - }, - { - key: 'delDepleted', - icon: , - label: t('pages.clients.delDepleted'), - danger: true, - onClick: onDelDepleted, - }, - ], + items: selectedRowKeys.length > 0 + ? [ + { + key: 'adjust', + icon: , + label: t('pages.clients.adjust'), + onClick: () => setBulkAdjustOpen(true), + }, + { + key: 'group', + icon: , + label: t('pages.clients.group'), + onClick: () => setBulkGroupOpen(true), + }, + { + key: 'subLinks', + icon: , + label: t('pages.clients.subLinks'), + onClick: () => setSubLinksOpen(true), + }, + ] + : [ + { + key: 'bulk', + icon: , + label: t('pages.clients.bulk'), + onClick: () => setBulkAddOpen(true), + }, + { + key: 'resetAll', + icon: , + label: t('pages.clients.resetAllTraffics'), + onClick: onResetAllTraffics, + }, + { + key: 'delDepleted', + icon: , + label: t('pages.clients.delDepleted'), + danger: true, + onClick: onDelDepleted, + }, + ], }} > + {selectedRowKeys.length > 0 && ( + + )}
} > diff --git a/web/translation/en-US.json b/web/translation/en-US.json index db2db99e..1f1ac320 100644 --- a/web/translation/en-US.json +++ b/web/translation/en-US.json @@ -542,6 +542,10 @@ "assignGroupPlaceholder": "Group name (leave blank to clear)", "assignGroupAssignedToast": "Assigned {count} client(s) to {group}", "assignGroupClearedToast": "Cleared group from {count} client(s)", + "attach": "Attach", + "adjust": "Adjust", + "subLinks": "Sub links", + "selectedCount": "{count} selected", "attachSelected": "Attach ({count})", "attachToInboundsTitle": "Attach {count} client(s) to inbound(s)", "attachToInboundsDesc": "Attaches the selected {count} client(s) (same UUID/password and shared traffic) to the chosen inbound(s). They keep their existing attachments too.", diff --git a/web/translation/fa-IR.json b/web/translation/fa-IR.json index 23779bab..e4e60c4f 100644 --- a/web/translation/fa-IR.json +++ b/web/translation/fa-IR.json @@ -513,6 +513,10 @@ "deleteConfirmContent": "این کلاینت از تمام اینباندهای متصل حذف و سابقه ترافیک آن پاک می‌شود. این عمل غیرقابل بازگشت است.", "deleteSelected": "حذف ({count})", "adjustSelected": "تنظیم ({count})", + "attach": "اتصال", + "adjust": "تنظیم", + "subLinks": "لینک‌های ساب", + "selectedCount": "{count} انتخاب‌شده", "attachSelected": "اتصال ({count})", "attachToInboundsTitle": "اتصال {count} کلاینت به اینباند(ها)", "attachToInboundsDesc": "{count} کلاینت انتخاب‌شده (با همان UUID/پسورد و ترافیک مشترک) به اینباند(های) انتخابی متصل می‌شوند. روی اینباندهای فعلی هم باقی می‌مانند.",