|
@@ -25,6 +25,7 @@ import {
|
|
|
} from 'antd';
|
|
} from 'antd';
|
|
|
import type { ColumnsType, TableProps } from 'antd/es/table';
|
|
import type { ColumnsType, TableProps } from 'antd/es/table';
|
|
|
import {
|
|
import {
|
|
|
|
|
+ ClockCircleOutlined,
|
|
|
DeleteOutlined,
|
|
DeleteOutlined,
|
|
|
EditOutlined,
|
|
EditOutlined,
|
|
|
FilterOutlined,
|
|
FilterOutlined,
|
|
@@ -54,6 +55,7 @@ import ClientFormModal from './ClientFormModal';
|
|
|
import ClientInfoModal from './ClientInfoModal';
|
|
import ClientInfoModal from './ClientInfoModal';
|
|
|
import ClientQrModal from './ClientQrModal';
|
|
import ClientQrModal from './ClientQrModal';
|
|
|
import ClientBulkAddModal from './ClientBulkAddModal';
|
|
import ClientBulkAddModal from './ClientBulkAddModal';
|
|
|
|
|
+import ClientBulkAdjustModal from './ClientBulkAdjustModal';
|
|
|
import '@/styles/page-cards.css';
|
|
import '@/styles/page-cards.css';
|
|
|
import './ClientsPage.css';
|
|
import './ClientsPage.css';
|
|
|
|
|
|
|
@@ -96,7 +98,7 @@ export default function ClientsPage() {
|
|
|
const {
|
|
const {
|
|
|
clients, inbounds, onlines, loading, fetched, subSettings,
|
|
clients, inbounds, onlines, loading, fetched, subSettings,
|
|
|
ipLimitEnable, tgBotEnable, expireDiff, trafficDiff, pageSize,
|
|
ipLimitEnable, tgBotEnable, expireDiff, trafficDiff, pageSize,
|
|
|
- create, update, remove, removeMany, attach, detach,
|
|
|
|
|
|
|
+ create, update, remove, removeMany, bulkAdjust, attach, detach,
|
|
|
resetTraffic, resetAllTraffics, delDepleted, setEnable,
|
|
resetTraffic, resetAllTraffics, delDepleted, setEnable,
|
|
|
applyTrafficEvent, applyClientStatsEvent, applyInvalidate,
|
|
applyTrafficEvent, applyClientStatsEvent, applyInvalidate,
|
|
|
} = useClients();
|
|
} = useClients();
|
|
@@ -117,6 +119,7 @@ export default function ClientsPage() {
|
|
|
const [qrOpen, setQrOpen] = useState(false);
|
|
const [qrOpen, setQrOpen] = useState(false);
|
|
|
const [qrClient, setQrClient] = useState<ClientRecord | null>(null);
|
|
const [qrClient, setQrClient] = useState<ClientRecord | null>(null);
|
|
|
const [bulkAddOpen, setBulkAddOpen] = useState(false);
|
|
const [bulkAddOpen, setBulkAddOpen] = useState(false);
|
|
|
|
|
+ const [bulkAdjustOpen, setBulkAdjustOpen] = useState(false);
|
|
|
const [selectedRowKeys, setSelectedRowKeys] = useState<string[]>([]);
|
|
const [selectedRowKeys, setSelectedRowKeys] = useState<string[]>([]);
|
|
|
|
|
|
|
|
const initial = readFilterState();
|
|
const initial = readFilterState();
|
|
@@ -587,7 +590,7 @@ export default function ClientsPage() {
|
|
|
}, 'expiryTime'),
|
|
}, 'expiryTime'),
|
|
|
];
|
|
];
|
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
|
- }, [t, togglingEmail, sortColumn, sortOrder, clientBucket, isOnline]);
|
|
|
|
|
|
|
+ }, [t, togglingEmail, sortColumn, sortOrder, clientBucket, isOnline, inboundsById]);
|
|
|
|
|
|
|
|
const tablePagination = {
|
|
const tablePagination = {
|
|
|
current: currentPage,
|
|
current: currentPage,
|
|
@@ -700,9 +703,14 @@ export default function ClientsPage() {
|
|
|
{!isMobile && t('pages.clients.bulk')}
|
|
{!isMobile && t('pages.clients.bulk')}
|
|
|
</Button>
|
|
</Button>
|
|
|
{selectedRowKeys.length > 0 && (
|
|
{selectedRowKeys.length > 0 && (
|
|
|
- <Button danger size="small" icon={<DeleteOutlined />} onClick={onBulkDelete}>
|
|
|
|
|
- {t('pages.clients.deleteSelected', { count: selectedRowKeys.length })}
|
|
|
|
|
- </Button>
|
|
|
|
|
|
|
+ <>
|
|
|
|
|
+ <Button size="small" icon={<ClockCircleOutlined />} onClick={() => setBulkAdjustOpen(true)}>
|
|
|
|
|
+ {t('pages.clients.adjustSelected', { count: selectedRowKeys.length })}
|
|
|
|
|
+ </Button>
|
|
|
|
|
+ <Button danger size="small" icon={<DeleteOutlined />} onClick={onBulkDelete}>
|
|
|
|
|
+ {t('pages.clients.deleteSelected', { count: selectedRowKeys.length })}
|
|
|
|
|
+ </Button>
|
|
|
|
|
+ </>
|
|
|
)}
|
|
)}
|
|
|
<Button size="small" icon={<RetweetOutlined />} onClick={onResetAllTraffics}>
|
|
<Button size="small" icon={<RetweetOutlined />} onClick={onResetAllTraffics}>
|
|
|
{!isMobile && t('pages.clients.resetAllTraffics')}
|
|
{!isMobile && t('pages.clients.resetAllTraffics')}
|
|
@@ -902,6 +910,19 @@ export default function ClientsPage() {
|
|
|
onOpenChange={setBulkAddOpen}
|
|
onOpenChange={setBulkAddOpen}
|
|
|
onSaved={() => setBulkAddOpen(false)}
|
|
onSaved={() => setBulkAddOpen(false)}
|
|
|
/>
|
|
/>
|
|
|
|
|
+ <ClientBulkAdjustModal
|
|
|
|
|
+ open={bulkAdjustOpen}
|
|
|
|
|
+ count={selectedRowKeys.length}
|
|
|
|
|
+ onOpenChange={setBulkAdjustOpen}
|
|
|
|
|
+ onSubmit={async (addDays, addBytes) => {
|
|
|
|
|
+ const msg = await bulkAdjust([...selectedRowKeys], addDays, addBytes);
|
|
|
|
|
+ if (msg?.success) {
|
|
|
|
|
+ setSelectedRowKeys([]);
|
|
|
|
|
+ return msg.obj ?? { adjusted: 0 };
|
|
|
|
|
+ }
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }}
|
|
|
|
|
+ />
|
|
|
</Layout>
|
|
</Layout>
|
|
|
</ConfigProvider>
|
|
</ConfigProvider>
|
|
|
);
|
|
);
|