| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146 |
- import { useTranslation } from 'react-i18next';
- import { Button, Card, Col, Empty, Input, InputNumber, Row, Select, Space } from 'antd';
- import { ArrowDownOutlined, ArrowUpOutlined, DeleteOutlined, PlusOutlined } from '@ant-design/icons';
- import type { FallbackRow } from '@/schemas/forms/inbound-form';
- interface FallbacksCardProps {
- fallbacks: FallbackRow[];
- fallbackChildOptions: { label: string; value: number }[];
- addFallback: () => void;
- updateFallback: (rowKey: string, patch: Partial<FallbackRow>) => void;
- removeFallback: (idx: number) => void;
- moveFallback: (idx: number, direction: -1 | 1) => void;
- addAllFallbacks: () => void;
- }
- export default function FallbacksCard({
- fallbacks,
- fallbackChildOptions,
- addFallback,
- updateFallback,
- removeFallback,
- moveFallback,
- addAllFallbacks,
- }: FallbacksCardProps) {
- const { t } = useTranslation();
- const addButtons = (
- <Space size={8} wrap>
- <Button type="primary" ghost size="small" icon={<PlusOutlined />} onClick={addFallback}>
- {t('pages.inbounds.fallbacks.add') || 'Add fallback'}
- </Button>
- <Button
- size="small"
- onClick={addAllFallbacks}
- disabled={fallbackChildOptions.length === 0 || fallbacks.length >= fallbackChildOptions.length}
- title={t('pages.inbounds.form.addAllFallbackTooltip')}
- >
- {t('pages.inbounds.form.addAll')}
- </Button>
- </Space>
- );
- return (
- <Card
- size="small"
- className="mt-12"
- title={t('pages.inbounds.fallbacks.title') || 'Fallbacks'}
- extra={addButtons}
- >
- {fallbacks.length === 0 ? (
- <Empty
- image={Empty.PRESENTED_IMAGE_SIMPLE}
- styles={{ image: { height: 36 } }}
- description={t('pages.inbounds.fallbacks.empty') || 'No fallbacks yet'}
- style={{ margin: '4px 0 12px' }}
- />
- ) : (
- fallbacks.map((record, idx) => (
- <Card
- key={record.rowKey}
- type="inner"
- size="small"
- style={{ marginBottom: 8 }}
- styles={{ body: { padding: 12 } }}
- >
- <Space.Compact block style={{ marginBottom: 8 }}>
- <Select
- aria-label={t('pages.inbounds.fallbacks.pickInbound')}
- value={record.childId}
- options={fallbackChildOptions}
- placeholder={t('pages.inbounds.fallbacks.pickInbound') || 'Pick an inbound'}
- allowClear
- showSearch={{
- filterOption: (input, option) =>
- ((option?.label as string) || '').toLowerCase().includes(input.toLowerCase()),
- }}
- style={{ width: '100%' }}
- onChange={(v) => updateFallback(record.rowKey, { childId: v ?? null })}
- />
- <Button
- aria-label={t('pages.inbounds.form.moveUp')}
- disabled={idx === 0}
- onClick={() => moveFallback(idx, -1)}
- title={t('pages.inbounds.form.moveUp')}
- icon={<ArrowUpOutlined />}
- />
- <Button
- aria-label={t('pages.inbounds.form.moveDown')}
- disabled={idx === fallbacks.length - 1}
- onClick={() => moveFallback(idx, 1)}
- title={t('pages.inbounds.form.moveDown')}
- icon={<ArrowDownOutlined />}
- />
- <Button aria-label={t('delete')} danger onClick={() => removeFallback(idx)} icon={<DeleteOutlined />} />
- </Space.Compact>
- <Row gutter={[8, 8]}>
- <Col xs={24} sm={12}>
- <Input
- addonBefore="SNI"
- placeholder={t('pages.inbounds.fallbacks.matchAny') || 'any'}
- value={record.name}
- onChange={(e) => updateFallback(record.rowKey, { name: e.target.value })}
- />
- </Col>
- <Col xs={24} sm={12}>
- <Input
- addonBefore="ALPN"
- placeholder={t('pages.inbounds.fallbacks.matchAny') || 'any'}
- value={record.alpn}
- onChange={(e) => updateFallback(record.rowKey, { alpn: e.target.value })}
- />
- </Col>
- <Col xs={24} sm={12}>
- <Input
- addonBefore="Path"
- placeholder="/"
- value={record.path}
- onChange={(e) => updateFallback(record.rowKey, { path: e.target.value })}
- />
- </Col>
- <Col xs={24} sm={12}>
- <Input
- addonBefore="Dest"
- placeholder={t('pages.inbounds.fallbacks.destPlaceholder') || 'auto'}
- value={record.dest}
- onChange={(e) => updateFallback(record.rowKey, { dest: e.target.value })}
- />
- </Col>
- <Col xs={24} sm={12}>
- <InputNumber
- addonBefore="xver"
- min={0}
- max={2}
- style={{ width: '100%' }}
- value={record.xver}
- onChange={(v) => updateFallback(record.rowKey, { xver: Number(v) || 0 })}
- />
- </Col>
- </Row>
- </Card>
- ))
- )}
- </Card>
- );
- }
|