feat(ui): improve client form modal UX

- Rename tabs: "Basic" → "Basics", "Config" → "Credentials"
- Move reverseTag field from Credentials tab to Basics tab
- Move IP log button inline with limitIp field (tooltip button, edit mode only)
- Hide random email button when editing an existing client
- Add tooltips to totalGB and limitIp fields with descriptive hints
- Rename labels: "Total Sent/Received (GB)" → "Traffic Limit (GB)", "Duration" → "Duration (days)"
- Add renewDays translation key for auto-renew label with unit hint
- Remove redundant filterOption and width style from AutoComplete group selectors
- Update all 15 locale files with new and renamed translation keys
This commit is contained in:
MHSanaei
2026-06-12 10:38:26 +02:00
parent 253063b785
commit 7ae3ea66d1
16 changed files with 149 additions and 113 deletions
@@ -66,11 +66,7 @@ export default function BulkAddToGroupModal({
placeholder={t('pages.clients.groupName')}
options={groups.map((g) => ({ value: g }))}
onChange={(v) => setValue(v ?? '')}
filterOption={(input, option) =>
String(option?.value ?? '').toLowerCase().includes((input || '').toLowerCase())
}
allowClear
style={{ width: '100%' }}
autoFocus
/>
</Form.Item>
@@ -288,11 +288,7 @@ export default function ClientBulkAddModal({
placeholder={t('pages.clients.groupPlaceholder')}
options={groups.map((g) => ({ value: g }))}
onChange={(v) => update('group', v ?? '')}
filterOption={(input, option) =>
String(option?.value ?? '').toLowerCase().includes((input || '').toLowerCase())
}
allowClear
style={{ width: '100%' }}
/>
</Form.Item>
+45 -40
View File
@@ -14,6 +14,7 @@ import {
Switch,
Tabs,
Tag,
Tooltip,
message,
} from 'antd';
import { EyeOutlined, ReloadOutlined } from '@ant-design/icons';
@@ -428,7 +429,7 @@ export default function ClientFormModal({
items={[
{
key: 'basic',
label: t('pages.clients.tabBasic'),
label: t('pages.clients.tabBasics'),
children: (
<>
<Row gutter={16}>
@@ -441,20 +442,31 @@ export default function ClientFormModal({
style={{ flex: 1 }}
onChange={(e) => update('email', e.target.value)}
/>
<Button icon={<ReloadOutlined />} onClick={() => update('email', RandomUtil.randomLowerAndNum(12))} />
{!isEdit && (
<Button icon={<ReloadOutlined />} onClick={() => update('email', RandomUtil.randomLowerAndNum(12))} />
)}
</Space.Compact>
</Form.Item>
</Col>
<Col xs={24} md={8}>
<Form.Item label={t('pages.clients.totalGB')}>
<Col xs={24} md={6}>
<Form.Item label={t('pages.clients.totalGB')} tooltip={t('pages.clients.totalGBDesc')}>
<InputNumber value={form.totalGB} min={0} step={1} style={{ width: '100%' }}
onChange={(v) => update('totalGB', Number(v) || 0)} />
</Form.Item>
</Col>
<Col xs={24} md={4}>
<Form.Item label={t('pages.clients.limitIp')}>
<InputNumber value={form.limitIp} min={0} style={{ width: '100%' }}
onChange={(v) => update('limitIp', Number(v) || 0)} />
<Col xs={24} md={6}>
<Form.Item label={t('pages.clients.limitIp')} tooltip={t('pages.clients.limitIpDesc')}>
<Space.Compact style={{ display: 'flex' }}>
<InputNumber value={form.limitIp} min={0} style={{ flex: 1 }}
onChange={(v) => update('limitIp', Number(v) || 0)} />
{isEdit && (
<Tooltip title={t('pages.clients.ipLog')}>
<Button icon={<EyeOutlined />} loading={ipsLoading} onClick={openIpsModal}>
{clientIps.length > 0 ? clientIps.length : ''}
</Button>
</Tooltip>
)}
</Space.Compact>
</Form.Item>
</Col>
</Row>
@@ -489,7 +501,7 @@ export default function ClientFormModal({
</Col>
<Col xs={12} md={6}>
<Form.Item
label={t('pages.clients.renew')}
label={t('pages.clients.renewDays')}
tooltip={t('pages.clients.renewDesc')}
>
<InputNumber value={form.reset} min={0} style={{ width: '100%' }}
@@ -499,16 +511,7 @@ export default function ClientFormModal({
</Row>
<Row gutter={16}>
{tgBotEnable && (
<Col xs={24} md={12}>
<Form.Item label={t('pages.clients.telegramId')}>
<InputNumber value={form.tgId} min={0} controls={false}
placeholder={t('pages.clients.telegramIdPlaceholder')} style={{ width: '100%' }}
onChange={(v) => update('tgId', Number(v) || 0)} />
</Form.Item>
</Col>
)}
<Col xs={24} md={tgBotEnable ? 12 : 24}>
<Col xs={24} md={12}>
<Form.Item label={t('pages.clients.comment')}>
<Input value={form.comment} onChange={(e) => update('comment', e.target.value)} />
</Form.Item>
@@ -520,16 +523,34 @@ export default function ClientFormModal({
placeholder={t('pages.clients.groupPlaceholder')}
options={groups.map((g) => ({ value: g }))}
onChange={(v) => update('group', v ?? '')}
filterOption={(input, option) =>
String(option?.value ?? '').toLowerCase().includes((input || '').toLowerCase())
}
allowClear
style={{ width: '100%' }}
/>
</Form.Item>
</Col>
</Row>
{(tgBotEnable || showReverseTag) && (
<Row gutter={16}>
{tgBotEnable && (
<Col xs={24} md={12}>
<Form.Item label={t('pages.clients.telegramId')}>
<InputNumber value={form.tgId} min={0} controls={false}
placeholder={t('pages.clients.telegramIdPlaceholder')} style={{ width: '100%' }}
onChange={(v) => update('tgId', Number(v) || 0)} />
</Form.Item>
</Col>
)}
{showReverseTag && (
<Col xs={24} md={12}>
<Form.Item label={t('pages.clients.reverseTag')}>
<Input value={form.reverseTag} placeholder={t('pages.clients.reverseTagPlaceholder')}
onChange={(e) => update('reverseTag', e.target.value)} />
</Form.Item>
</Col>
)}
</Row>
)}
<Form.Item label={t('pages.clients.attachedInbounds')} required={!isEdit}>
<SelectAllClearButtons
options={inboundOptions}
@@ -555,20 +576,12 @@ export default function ClientFormModal({
<Switch checked={form.enable} onChange={(v) => update('enable', v)} />
<span style={{ marginLeft: 8 }}>{t('enable')}</span>
</Form.Item>
{isEdit && (
<Form.Item label={t('pages.clients.ipLog')}>
<Button icon={<EyeOutlined />} loading={ipsLoading} onClick={openIpsModal}>
{clientIps.length > 0 ? clientIps.length : ''}
</Button>
</Form.Item>
)}
</>
),
},
{
key: 'config',
label: t('pages.clients.tabConfig'),
label: t('pages.clients.tabCredentials'),
children: (
<>
<Row gutter={16}>
@@ -635,14 +648,6 @@ export default function ClientFormModal({
</Form.Item>
</Col>
)}
{showReverseTag && (
<Col xs={24} md={12}>
<Form.Item label={t('pages.clients.reverseTag')}>
<Input value={form.reverseTag} placeholder={t('pages.clients.reverseTagPlaceholder')}
onChange={(e) => update('reverseTag', e.target.value)} />
</Form.Item>
</Col>
)}
</Row>
</>
),