CustomGeoFormModal.tsx 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. import { useEffect, useState } from 'react';
  2. import { useTranslation } from 'react-i18next';
  3. import { Form, Input, message, Modal, Select } from 'antd';
  4. import { HttpUtil } from '@/utils';
  5. import { CustomGeoFormSchema } from '@/schemas/xray';
  6. export interface CustomGeoRecord {
  7. id: number;
  8. type: 'geosite' | 'geoip';
  9. alias: string;
  10. url: string;
  11. }
  12. interface CustomGeoFormModalProps {
  13. open: boolean;
  14. record: CustomGeoRecord | null;
  15. onClose: () => void;
  16. onSaved: () => void;
  17. }
  18. export default function CustomGeoFormModal({
  19. open,
  20. record,
  21. onClose,
  22. onSaved,
  23. }: CustomGeoFormModalProps) {
  24. const { t } = useTranslation();
  25. const [messageApi, messageContextHolder] = message.useMessage();
  26. const [type, setType] = useState<'geosite' | 'geoip'>('geosite');
  27. const [alias, setAlias] = useState('');
  28. const [url, setUrl] = useState('');
  29. const [saving, setSaving] = useState(false);
  30. const editing = record != null;
  31. useEffect(() => {
  32. if (!open) return;
  33. if (record) {
  34. setType(record.type);
  35. setAlias(record.alias);
  36. setUrl(record.url);
  37. } else {
  38. setType('geosite');
  39. setAlias('');
  40. setUrl('');
  41. }
  42. }, [open, record]);
  43. async function submit() {
  44. const validated = CustomGeoFormSchema.safeParse({ type, alias, url });
  45. if (!validated.success) {
  46. messageApi.error(t(validated.error.issues[0]?.message ?? 'somethingWentWrong'));
  47. return;
  48. }
  49. setSaving(true);
  50. try {
  51. const apiUrl = editing
  52. ? `/panel/api/custom-geo/update/${record!.id}`
  53. : '/panel/api/custom-geo/add';
  54. const msg = await HttpUtil.post(apiUrl, validated.data);
  55. if (msg?.success) {
  56. onSaved();
  57. onClose();
  58. }
  59. } finally {
  60. setSaving(false);
  61. }
  62. }
  63. return (
  64. <>
  65. {messageContextHolder}
  66. <Modal
  67. open={open}
  68. title={editing ? t('pages.index.customGeoModalEdit') : t('pages.index.customGeoModalAdd')}
  69. confirmLoading={saving}
  70. okText={t('pages.index.customGeoModalSave')}
  71. cancelText={t('close')}
  72. onOk={submit}
  73. onCancel={onClose}
  74. >
  75. <Form layout="vertical">
  76. <Form.Item label={t('pages.index.customGeoType')}>
  77. <Select
  78. value={type}
  79. disabled={editing}
  80. onChange={(v) => setType(v)}
  81. options={[
  82. { value: 'geosite', label: 'geosite' },
  83. { value: 'geoip', label: 'geoip' },
  84. ]}
  85. />
  86. </Form.Item>
  87. <Form.Item label={t('pages.index.customGeoAlias')}>
  88. <Input
  89. value={alias}
  90. disabled={editing}
  91. placeholder={t('pages.index.customGeoAliasPlaceholder')}
  92. onChange={(e) => setAlias(e.target.value)}
  93. />
  94. </Form.Item>
  95. <Form.Item label={t('pages.index.customGeoUrl')}>
  96. <Input
  97. value={url}
  98. placeholder="https://"
  99. onChange={(e) => setUrl(e.target.value)}
  100. />
  101. </Form.Item>
  102. </Form>
  103. </Modal>
  104. </>
  105. );
  106. }