mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-06-05 20:54:14 +00:00
feat(frontend): fallbacks polish — move up/down + Add all button
Two small UX wins on the InboundFormModal Fallbacks card: - Per-row Move up / Move down buttons (ArrowUp/Down icons) that swap adjacent indices. Order survives reloads via sortOrder (rebuilt from index on save). First row's Up button + last row's Down button are disabled. - 'Add all' button next to 'Add fallback' that one-shot inserts a fresh row for every eligible inbound (every option in fallbackChildOptions) not already wired up. Disabled when every eligible inbound is already covered. Convenient for operators running catch-all routing across every host on the panel.
This commit is contained in:
parent
19204f9e04
commit
f4a49862a0
1 changed files with 71 additions and 4 deletions
|
|
@ -19,7 +19,14 @@ import {
|
||||||
Typography,
|
Typography,
|
||||||
message,
|
message,
|
||||||
} from 'antd';
|
} from 'antd';
|
||||||
import { DeleteOutlined, MinusOutlined, PlusOutlined, SyncOutlined } from '@ant-design/icons';
|
import {
|
||||||
|
ArrowDownOutlined,
|
||||||
|
ArrowUpOutlined,
|
||||||
|
DeleteOutlined,
|
||||||
|
MinusOutlined,
|
||||||
|
PlusOutlined,
|
||||||
|
SyncOutlined,
|
||||||
|
} from '@ant-design/icons';
|
||||||
|
|
||||||
import { HttpUtil, NumberFormatter, RandomUtil, SizeFormatter, Wireguard } from '@/utils';
|
import { HttpUtil, NumberFormatter, RandomUtil, SizeFormatter, Wireguard } from '@/utils';
|
||||||
import {
|
import {
|
||||||
|
|
@ -254,6 +261,41 @@ export default function InboundFormModal({
|
||||||
setFallbacks((prev) => prev.filter((_, i) => i !== idx));
|
setFallbacks((prev) => prev.filter((_, i) => i !== idx));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Move a fallback row up/down by swapping adjacent indices. The order
|
||||||
|
// is persisted via the fallback row's sortOrder (rebuilt by index on
|
||||||
|
// save), so reordering survives reloads.
|
||||||
|
const moveFallback = (idx: number, direction: -1 | 1) => {
|
||||||
|
setFallbacks((prev) => {
|
||||||
|
const target = idx + direction;
|
||||||
|
if (target < 0 || target >= prev.length) return prev;
|
||||||
|
const next = prev.slice();
|
||||||
|
[next[idx], next[target]] = [next[target], next[idx]];
|
||||||
|
return next;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// One-shot: add a fresh fallback row for every eligible inbound (i.e.
|
||||||
|
// every option in fallbackChildOptions) that is not already wired up.
|
||||||
|
// Convenient for operators who want catch-all routing to every host
|
||||||
|
// they manage on the panel.
|
||||||
|
const addAllFallbacks = () => {
|
||||||
|
setFallbacks((prev) => {
|
||||||
|
const alreadyHave = new Set(prev.map((r) => r.childId));
|
||||||
|
const additions = fallbackChildOptions
|
||||||
|
.filter((opt) => !alreadyHave.has(opt.value))
|
||||||
|
.map<FallbackRow>((opt) => ({
|
||||||
|
rowKey: `fb-${++fallbackKeyRef.current}`,
|
||||||
|
childId: opt.value,
|
||||||
|
name: '',
|
||||||
|
alpn: '',
|
||||||
|
path: '',
|
||||||
|
xver: 0,
|
||||||
|
}));
|
||||||
|
if (additions.length === 0) return prev;
|
||||||
|
return [...prev, ...additions];
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const genRealityKeypair = async () => {
|
const genRealityKeypair = async () => {
|
||||||
setSaving(true);
|
setSaving(true);
|
||||||
try {
|
try {
|
||||||
|
|
@ -661,6 +703,20 @@ export default function InboundFormModal({
|
||||||
style={{ width: '100%' }}
|
style={{ width: '100%' }}
|
||||||
onChange={(v) => updateFallback(record.rowKey, { childId: v })}
|
onChange={(v) => updateFallback(record.rowKey, { childId: v })}
|
||||||
/>
|
/>
|
||||||
|
<Button
|
||||||
|
disabled={idx === 0}
|
||||||
|
onClick={() => moveFallback(idx, -1)}
|
||||||
|
title="Move up"
|
||||||
|
>
|
||||||
|
<ArrowUpOutlined />
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
disabled={idx === fallbacks.length - 1}
|
||||||
|
onClick={() => moveFallback(idx, 1)}
|
||||||
|
title="Move down"
|
||||||
|
>
|
||||||
|
<ArrowDownOutlined />
|
||||||
|
</Button>
|
||||||
<Button danger onClick={() => removeFallback(idx)}>
|
<Button danger onClick={() => removeFallback(idx)}>
|
||||||
<DeleteOutlined />
|
<DeleteOutlined />
|
||||||
</Button>
|
</Button>
|
||||||
|
|
@ -694,9 +750,20 @@ export default function InboundFormModal({
|
||||||
</Space.Compact>
|
</Space.Compact>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
<Button size="small" onClick={addFallback}>
|
<Space>
|
||||||
<PlusOutlined /> {t('pages.inbounds.fallbacks.add') || 'Add fallback'}
|
<Button size="small" onClick={addFallback}>
|
||||||
</Button>
|
<PlusOutlined /> {t('pages.inbounds.fallbacks.add') || 'Add fallback'}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
size="small"
|
||||||
|
onClick={addAllFallbacks}
|
||||||
|
disabled={fallbackChildOptions.length === 0
|
||||||
|
|| fallbacks.length >= fallbackChildOptions.length}
|
||||||
|
title="Add a fallback row for every eligible inbound not yet wired up"
|
||||||
|
>
|
||||||
|
Add all
|
||||||
|
</Button>
|
||||||
|
</Space>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue