mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-07-16 09:36:07 +00:00
feat(clients,inbound): Auto Renew in Bulk Add + cleaner inbound wire payload
Bulk Add now exposes the same Auto Renew (`reset`, days) input as the single-client form, applied to every client the batch produces. The field was already on ClientBulkAddFormSchema's siblings; just wire it into the schema, the empty-form defaults, the UI, and the bulkCreate payload. Also relabel "Subscription info" to "Subscription ID" by switching to the canonical pages.clients.subId key and modernise the SyncOutlined-in-label random affordance on the same row. On the inbound submit path, two payload-shape cleanups in dropLegacyOptionalEmpties: - streamSettings.hysteriaSettings.auth is a holdover slot whose real per-client value lives in settings.clients[*].auth; drop the field entirely when empty instead of shipping `"auth": ""`. - finalmask's `tcp` / `udp` arrays were already dropped together when both were empty, but a UDP-only setup still emitted a stray `"tcp": []`. Drop each sub-array on its own when empty so a Hysteria-style "salamander on udp only" config no longer carries the empty tcp sibling.
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Form, Input, InputNumber, Modal, Select, Switch, message } from 'antd';
|
||||
import { SyncOutlined } from '@ant-design/icons';
|
||||
import { Button, Form, Input, InputNumber, Modal, Select, Space, Switch, message } from 'antd';
|
||||
import { ReloadOutlined } from '@ant-design/icons';
|
||||
import dayjs from 'dayjs';
|
||||
import type { Dayjs } from 'dayjs';
|
||||
|
||||
@@ -41,6 +41,7 @@ function emptyForm(): FormState {
|
||||
limitIp: 0,
|
||||
totalGB: 0,
|
||||
expiryTime: 0,
|
||||
reset: 0,
|
||||
inboundIds: [],
|
||||
};
|
||||
}
|
||||
@@ -154,6 +155,7 @@ export default function ClientBulkAddModal({
|
||||
flow: showFlow ? (form.flow || '') : '',
|
||||
totalGB: Math.round((form.totalGB || 0) * SizeFormatter.ONE_GB),
|
||||
expiryTime: form.expiryTime,
|
||||
reset: Number(form.reset) || 0,
|
||||
limitIp: Number(form.limitIp) || 0,
|
||||
comment: form.comment,
|
||||
enable: true,
|
||||
@@ -247,16 +249,18 @@ export default function ClientBulkAddModal({
|
||||
</Form.Item>
|
||||
)}
|
||||
|
||||
<Form.Item label={
|
||||
<>
|
||||
{t('subscription.title')}
|
||||
<SyncOutlined
|
||||
className="random-icon"
|
||||
<Form.Item label={t('pages.clients.subId')}>
|
||||
<Space.Compact style={{ display: 'flex' }}>
|
||||
<Input
|
||||
value={form.subId}
|
||||
onChange={(e) => update('subId', e.target.value)}
|
||||
style={{ flex: 1 }}
|
||||
/>
|
||||
<Button
|
||||
icon={<ReloadOutlined />}
|
||||
onClick={() => update('subId', RandomUtil.randomLowerAndNum(16))}
|
||||
/>
|
||||
</>
|
||||
}>
|
||||
<Input value={form.subId} onChange={(e) => update('subId', e.target.value)} />
|
||||
</Space.Compact>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label={t('comment')}>
|
||||
@@ -310,6 +314,17 @@ export default function ClientBulkAddModal({
|
||||
/>
|
||||
</Form.Item>
|
||||
)}
|
||||
|
||||
<Form.Item
|
||||
label={t('pages.clients.renew')}
|
||||
tooltip={t('pages.clients.renewDesc')}
|
||||
>
|
||||
<InputNumber
|
||||
value={form.reset}
|
||||
min={0}
|
||||
onChange={(v) => update('reset', Number(v) || 0)}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
</>
|
||||
|
||||
Reference in New Issue
Block a user