mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-06-05 20:54:14 +00:00
feat(frontend): sniffing tab on InboundFormModal.new.tsx (Pattern A)
Second section of the sibling-file rewrite. Wires the six sniffing sub-fields to nested form paths ['sniffing', 'enabled'], ['sniffing', 'destOverride'], etc. Uses Form.useWatch on the enabled flag to drive conditional rendering of the dependent fields — the same gate the legacy modal expressed via `ib.sniffing.enabled &&`. Checkbox.Group renders one Checkbox per SNIFFING_OPTION entry. The two exclusion lists use Select mode="tags" so the user can paste comma- separated IP/CIDR or domain rules. No transient form state, no class methods — every field maps directly to a wire-shape path in InboundFormValues. Protocol tab is next.
This commit is contained in:
parent
bf70743589
commit
74a2813fb4
1 changed files with 67 additions and 2 deletions
|
|
@ -2,6 +2,7 @@ import { useEffect, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import {
|
import {
|
||||||
|
Checkbox,
|
||||||
Form,
|
Form,
|
||||||
Input,
|
Input,
|
||||||
InputNumber,
|
InputNumber,
|
||||||
|
|
@ -25,7 +26,7 @@ import {
|
||||||
type InboundFormValues,
|
type InboundFormValues,
|
||||||
} from '@/schemas/forms/inbound-form';
|
} from '@/schemas/forms/inbound-form';
|
||||||
import { antdRule } from '@/utils/zodForm';
|
import { antdRule } from '@/utils/zodForm';
|
||||||
import { Protocols } from '@/schemas/primitives';
|
import { Protocols, SNIFFING_OPTION } from '@/schemas/primitives';
|
||||||
import DateTimePicker from '@/components/DateTimePicker';
|
import DateTimePicker from '@/components/DateTimePicker';
|
||||||
import type { DBInbound } from '@/models/dbinbound';
|
import type { DBInbound } from '@/models/dbinbound';
|
||||||
import type { NodeRecord } from '@/api/queries/useNodesQuery';
|
import type { NodeRecord } from '@/api/queries/useNodesQuery';
|
||||||
|
|
@ -87,6 +88,7 @@ export default function InboundFormModalNew({
|
||||||
const selectableNodes = (availableNodes || []).filter((n) => n.enable);
|
const selectableNodes = (availableNodes || []).filter((n) => n.enable);
|
||||||
const protocol = Form.useWatch('protocol', form) ?? '';
|
const protocol = Form.useWatch('protocol', form) ?? '';
|
||||||
const isNodeEligible = NODE_ELIGIBLE_PROTOCOLS.has(protocol);
|
const isNodeEligible = NODE_ELIGIBLE_PROTOCOLS.has(protocol);
|
||||||
|
const sniffingEnabled = Form.useWatch(['sniffing', 'enabled'], form) ?? false;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!open) return;
|
if (!open) return;
|
||||||
|
|
@ -271,6 +273,66 @@ export default function InboundFormModalNew({
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const sniffingTab = (
|
||||||
|
<>
|
||||||
|
<Form.Item name={['sniffing', 'enabled']} label={t('enable')} valuePropName="checked">
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
{sniffingEnabled && (
|
||||||
|
<>
|
||||||
|
<Form.Item name={['sniffing', 'destOverride']} wrapperCol={{ span: 24 }}>
|
||||||
|
<Checkbox.Group>
|
||||||
|
{Object.entries(SNIFFING_OPTION).map(([key, value]) => (
|
||||||
|
<Checkbox key={key} value={value}>{key}</Checkbox>
|
||||||
|
))}
|
||||||
|
</Checkbox.Group>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item
|
||||||
|
name={['sniffing', 'metadataOnly']}
|
||||||
|
label={t('pages.inbounds.sniffingMetadataOnly')}
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item
|
||||||
|
name={['sniffing', 'routeOnly']}
|
||||||
|
label={t('pages.inbounds.sniffingRouteOnly')}
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item
|
||||||
|
name={['sniffing', 'ipsExcluded']}
|
||||||
|
label={t('pages.inbounds.sniffingIpsExcluded')}
|
||||||
|
>
|
||||||
|
<Select
|
||||||
|
mode="tags"
|
||||||
|
tokenSeparators={[',']}
|
||||||
|
placeholder="IP/CIDR/geoip:*/ext:*"
|
||||||
|
style={{ width: '100%' }}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item
|
||||||
|
name={['sniffing', 'domainsExcluded']}
|
||||||
|
label={t('pages.inbounds.sniffingDomainsExcluded')}
|
||||||
|
>
|
||||||
|
<Select
|
||||||
|
mode="tags"
|
||||||
|
tokenSeparators={[',']}
|
||||||
|
placeholder="domain:*/ext:*"
|
||||||
|
style={{ width: '100%' }}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{messageContextHolder}
|
{messageContextHolder}
|
||||||
|
|
@ -293,7 +355,10 @@ export default function InboundFormModalNew({
|
||||||
wrapperCol={{ sm: { span: 14 } }}
|
wrapperCol={{ sm: { span: 14 } }}
|
||||||
onValuesChange={onValuesChange}
|
onValuesChange={onValuesChange}
|
||||||
>
|
>
|
||||||
<Tabs items={[{ key: 'basic', label: t('pages.xray.basicTemplate'), children: basicTab }]} />
|
<Tabs items={[
|
||||||
|
{ key: 'basic', label: t('pages.xray.basicTemplate'), children: basicTab },
|
||||||
|
{ key: 'sniffing', label: t('pages.inbounds.sniffingTab'), children: sniffingTab },
|
||||||
|
]} />
|
||||||
</Form>
|
</Form>
|
||||||
</Modal>
|
</Modal>
|
||||||
</>
|
</>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue