feat(clients): restore reset traffic button in edit client form

This commit is contained in:
MHSanaei
2026-06-12 20:15:31 +02:00
parent 08bc481ae3
commit 4eab37b66c
2 changed files with 89 additions and 47 deletions
+45 -5
View File
@@ -8,6 +8,7 @@ import {
Input,
InputNumber,
Modal,
Popconfirm,
Row,
Select,
Space,
@@ -17,7 +18,7 @@ import {
Tooltip,
message,
} from 'antd';
import { EyeOutlined, ReloadOutlined } from '@ant-design/icons';
import { EyeOutlined, ReloadOutlined, RetweetOutlined } from '@ant-design/icons';
import dayjs from 'dayjs';
import type { Dayjs } from 'dayjs';
import { HttpUtil, RandomUtil } from '@/utils';
@@ -39,6 +40,7 @@ const CLIENT_IP_LOG_MODAL_Z_INDEX = CLIENT_FORM_MODAL_Z_INDEX + 1;
interface ApiMsg<T = unknown> {
success?: boolean;
msg?: string;
obj?: T;
}
@@ -72,6 +74,7 @@ interface ClientFormModalProps {
payload: Record<string, unknown> | SaveCreatePayload,
meta: SaveMetaEdit | SaveMetaCreate,
) => Promise<ApiMsg | null>;
resetTraffic?: (client: ClientRecord) => Promise<ApiMsg | null>;
onOpenChange: (open: boolean) => void;
}
@@ -140,6 +143,7 @@ export default function ClientFormModal({
tgBotEnable = false,
groups = [],
save,
resetTraffic,
onOpenChange,
}: ClientFormModalProps) {
const { t } = useTranslation();
@@ -148,6 +152,7 @@ export default function ClientFormModal({
const [form, setForm] = useState<FormState>(emptyForm);
const [submitting, setSubmitting] = useState(false);
const [resetting, setResetting] = useState(false);
const [clientIps, setClientIps] = useState<string[]>([]);
const [ipsLoading, setIpsLoading] = useState(false);
const [ipsClearing, setIpsClearing] = useState(false);
@@ -328,6 +333,21 @@ export default function ClientFormModal({
onOpenChange(false);
}
async function onResetTraffic() {
if (!isEdit || !client?.email || !resetTraffic) return;
setResetting(true);
try {
const msg = await resetTraffic(client);
if (msg?.success) {
messageApi.success(t('pages.clients.toasts.trafficReset'));
} else {
messageApi.error(msg?.msg || t('somethingWentWrong'));
}
} finally {
setResetting(false);
}
}
async function onSubmit() {
const schema = isEdit ? ClientFormSchema : ClientCreateFormSchema;
const validated = schema.safeParse({
@@ -413,15 +433,35 @@ export default function ClientFormModal({
open={open}
title={isEdit ? t('pages.clients.editClient') : t('pages.clients.addClient')}
destroyOnHidden
okText={isEdit ? t('save') : t('create')}
cancelText={t('cancel')}
okButtonProps={{ loading: submitting }}
width={720}
zIndex={CLIENT_FORM_MODAL_Z_INDEX}
style={{ top: 20 }}
styles={{ body: { maxHeight: 'calc(100vh - 160px)', overflowY: 'auto', overflowX: 'hidden' } }}
onOk={onSubmit}
onCancel={close}
footer={
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
{isEdit && resetTraffic && (
<Popconfirm
title={t('pages.inbounds.resetTraffic')}
description={t('pages.inbounds.resetTrafficContent')}
okText={t('reset')}
cancelText={t('cancel')}
zIndex={CLIENT_IP_LOG_MODAL_Z_INDEX}
onConfirm={onResetTraffic}
>
<Button color="danger" variant="filled" icon={<RetweetOutlined />} loading={resetting}>
{t('pages.inbounds.resetTraffic')}
</Button>
</Popconfirm>
)}
<div style={{ marginInlineStart: 'auto', display: 'flex', gap: 8 }}>
<Button onClick={close}>{t('cancel')}</Button>
<Button type="primary" loading={submitting} onClick={onSubmit}>
{isEdit ? t('save') : t('create')}
</Button>
</div>
</div>
}
>
<Form layout="vertical">
<Tabs
+44 -42
View File
@@ -165,15 +165,15 @@ function gbToBytes(gb: number | undefined): number {
}
const SORT_OPTIONS: { value: string; column: string; order: 'ascend' | 'descend'; labelKey: string }[] = [
{ value: 'createdAt:ascend', column: 'createdAt', order: 'ascend', labelKey: 'pages.clients.sortOldest' },
{ value: 'createdAt:descend', column: 'createdAt', order: 'descend', labelKey: 'pages.clients.sortNewest' },
{ value: 'updatedAt:descend', column: 'updatedAt', order: 'descend', labelKey: 'pages.clients.sortRecentlyUpdated' },
{ value: 'lastOnline:descend', column: 'lastOnline', order: 'descend', labelKey: 'pages.clients.sortRecentlyOnline' },
{ value: 'email:ascend', column: 'email', order: 'ascend', labelKey: 'pages.clients.sortEmailAZ' },
{ value: 'email:descend', column: 'email', order: 'descend', labelKey: 'pages.clients.sortEmailZA' },
{ value: 'traffic:descend', column: 'traffic', order: 'descend', labelKey: 'pages.clients.sortMostTraffic' },
{ value: 'remaining:descend', column: 'remaining', order: 'descend', labelKey: 'pages.clients.sortHighestRemaining' },
{ value: 'expiryTime:ascend', column: 'expiryTime', order: 'ascend', labelKey: 'pages.clients.sortExpiringSoonest' },
{ value: 'createdAt:ascend', column: 'createdAt', order: 'ascend', labelKey: 'pages.clients.sortOldest' },
{ value: 'createdAt:descend', column: 'createdAt', order: 'descend', labelKey: 'pages.clients.sortNewest' },
{ value: 'updatedAt:descend', column: 'updatedAt', order: 'descend', labelKey: 'pages.clients.sortRecentlyUpdated' },
{ value: 'lastOnline:descend', column: 'lastOnline', order: 'descend', labelKey: 'pages.clients.sortRecentlyOnline' },
{ value: 'email:ascend', column: 'email', order: 'ascend', labelKey: 'pages.clients.sortEmailAZ' },
{ value: 'email:descend', column: 'email', order: 'descend', labelKey: 'pages.clients.sortEmailZA' },
{ value: 'traffic:descend', column: 'traffic', order: 'descend', labelKey: 'pages.clients.sortMostTraffic' },
{ value: 'remaining:descend', column: 'remaining', order: 'descend', labelKey: 'pages.clients.sortHighestRemaining' },
{ value: 'expiryTime:ascend', column: 'expiryTime', order: 'ascend', labelKey: 'pages.clients.sortExpiringSoonest' },
];
const DEFAULT_SORT = SORT_OPTIONS[0];
@@ -743,6 +743,7 @@ export default function ClientsPage() {
{
title: t('pages.clients.traffic'),
key: 'traffic',
width: 300,
render: (_v, record) => (
<ClientTrafficCell
up={record.traffic?.up}
@@ -924,40 +925,40 @@ export default function ClientsPage() {
menu={{
items: selectedRowKeys.length > 0
? [
{
key: 'adjust',
icon: <ClockCircleOutlined />,
label: t('pages.clients.adjust'),
onClick: () => setBulkAdjustOpen(true),
},
{
key: 'subLinks',
icon: <LinkOutlined />,
label: t('pages.clients.subLinks'),
onClick: () => setSubLinksOpen(true),
},
]
{
key: 'adjust',
icon: <ClockCircleOutlined />,
label: t('pages.clients.adjust'),
onClick: () => setBulkAdjustOpen(true),
},
{
key: 'subLinks',
icon: <LinkOutlined />,
label: t('pages.clients.subLinks'),
onClick: () => setSubLinksOpen(true),
},
]
: [
{
key: 'bulk',
icon: <UsergroupAddOutlined />,
label: t('pages.clients.bulk'),
onClick: () => setBulkAddOpen(true),
},
{
key: 'resetAll',
icon: <RetweetOutlined />,
label: t('pages.clients.resetAllTraffics'),
onClick: onResetAllTraffics,
},
{
key: 'delDepleted',
icon: <RestOutlined />,
label: t('pages.clients.delDepleted'),
danger: true,
onClick: onDelDepleted,
},
],
{
key: 'bulk',
icon: <UsergroupAddOutlined />,
label: t('pages.clients.bulk'),
onClick: () => setBulkAddOpen(true),
},
{
key: 'resetAll',
icon: <RetweetOutlined />,
label: t('pages.clients.resetAllTraffics'),
onClick: onResetAllTraffics,
},
{
key: 'delDepleted',
icon: <RestOutlined />,
label: t('pages.clients.delDepleted'),
danger: true,
onClick: onDelDepleted,
},
],
}}
>
<Button icon={<MoreOutlined />}>
@@ -1246,6 +1247,7 @@ export default function ClientsPage() {
tgBotEnable={tgBotEnable}
groups={allGroups}
save={onSave}
resetTraffic={resetTraffic}
onOpenChange={setFormOpen}
/>
</LazyMount>