feat(clients): restore Auto Renew field in client form

The vue→react rewrite dropped the per-client `reset` (Auto Renew)
input. The backend's autoRenewClients job has always honoured it, but
the form had no way to set or change the value, so existing
auto-renew settings were also invisible during edits.

Reinstate the field as an InputNumber with a tooltip explaining
"0 = disable (unit: day)", placed on the same row as the Reverse tag
field so the form doesn't grow taller for the common cases. Wired
through FormState defaults, edit-mode hydration, the submit payload,
and ClientFormSchema validation.
This commit is contained in:
MHSanaei 2026-05-27 11:22:49 +02:00
parent 3c5e9fa774
commit 313d041db3
No known key found for this signature in database
GPG key ID: 7E4060F2FBE5AB7A
2 changed files with 39 additions and 26 deletions

View file

@ -79,6 +79,7 @@ interface FormState {
expiryDate: Dayjs | null; expiryDate: Dayjs | null;
delayedStart: boolean; delayedStart: boolean;
delayedDays: number; delayedDays: number;
reset: number;
limitIp: number; limitIp: number;
tgId: number; tgId: number;
comment: string; comment: string;
@ -99,6 +100,7 @@ function emptyForm(): FormState {
expiryDate: null, expiryDate: null,
delayedStart: false, delayedStart: false,
delayedDays: 0, delayedDays: 0,
reset: 0,
limitIp: 0, limitIp: 0,
tgId: 0, tgId: 0,
comment: '', comment: '',
@ -157,6 +159,7 @@ export default function ClientFormModal({
flow: client.flow || '', flow: client.flow || '',
reverseTag: client.reverse?.tag || '', reverseTag: client.reverse?.tag || '',
totalGB: bytesToGB(client.totalGB || 0), totalGB: bytesToGB(client.totalGB || 0),
reset: Number(client.reset) || 0,
limitIp: client.limitIp || 0, limitIp: client.limitIp || 0,
tgId: Number(client.tgId) || 0, tgId: Number(client.tgId) || 0,
comment: client.comment || '', comment: client.comment || '',
@ -280,6 +283,7 @@ export default function ClientFormModal({
totalGB: form.totalGB, totalGB: form.totalGB,
delayedStart: form.delayedStart, delayedStart: form.delayedStart,
delayedDays: form.delayedDays, delayedDays: form.delayedDays,
reset: form.reset,
limitIp: form.limitIp, limitIp: form.limitIp,
tgId: form.tgId, tgId: form.tgId,
comment: form.comment, comment: form.comment,
@ -303,6 +307,7 @@ export default function ClientFormModal({
flow: showFlow ? (form.flow || '') : '', flow: showFlow ? (form.flow || '') : '',
totalGB: gbToBytes(form.totalGB), totalGB: gbToBytes(form.totalGB),
expiryTime, expiryTime,
reset: Number(form.reset) || 0,
limitIp: Number(form.limitIp) || 0, limitIp: Number(form.limitIp) || 0,
tgId: Number(form.tgId) || 0, tgId: Number(form.tgId) || 0,
comment: form.comment, comment: form.comment,
@ -452,32 +457,39 @@ export default function ClientFormModal({
</Col> </Col>
</Row> </Row>
{(showFlow || showReverseTag) && ( <Row gutter={16}>
<Row gutter={16}> <Col xs={24} md={12}>
{showFlow && ( <Form.Item
<Col xs={24} md={12}> label={t('pages.clients.renew')}
<Form.Item label={t('pages.clients.flow')}> tooltip={t('pages.clients.renewDesc')}
<Select >
value={form.flow} <InputNumber value={form.reset} min={0} style={{ width: '100%' }}
onChange={(v) => update('flow', v)} onChange={(v) => update('reset', Number(v) || 0)} />
options={[ </Form.Item>
{ value: '', label: t('none') }, </Col>
...FLOW_OPTIONS.map((k) => ({ value: k, label: k })), {showReverseTag && (
]} <Col xs={24} md={12}>
/> <Form.Item label={t('pages.clients.reverseTag')}>
</Form.Item> <Input value={form.reverseTag} placeholder={t('pages.clients.reverseTagPlaceholder')}
</Col> onChange={(e) => update('reverseTag', e.target.value)} />
)} </Form.Item>
{showReverseTag && ( </Col>
<Col xs={24} md={12}> )}
<Form.Item label={t('pages.clients.reverseTag')}> {showFlow && (
<Input value={form.reverseTag} placeholder={t('pages.clients.reverseTagPlaceholder')} <Col xs={24} md={12}>
onChange={(e) => update('reverseTag', e.target.value)} /> <Form.Item label={t('pages.clients.flow')}>
</Form.Item> <Select
</Col> value={form.flow}
)} onChange={(v) => update('flow', v)}
</Row> options={[
)} { value: '', label: t('none') },
...FLOW_OPTIONS.map((k) => ({ value: k, label: k })),
]}
/>
</Form.Item>
</Col>
)}
</Row>
<Row gutter={16}> <Row gutter={16}>
{tgBotEnable && ( {tgBotEnable && (

View file

@ -108,6 +108,7 @@ export const ClientFormSchema = z.object({
totalGB: z.number().min(0), totalGB: z.number().min(0),
delayedStart: z.boolean(), delayedStart: z.boolean(),
delayedDays: z.number().int().min(0), delayedDays: z.number().int().min(0),
reset: z.number().int().min(0),
limitIp: z.number().int().min(0), limitIp: z.number().int().min(0),
tgId: z.number().int().min(0), tgId: z.number().int().min(0),
comment: z.string(), comment: z.string(),