mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2026-02-12 07:50:25 +00:00
feat: 新版webui
This commit is contained in:
172
napcat.webui/src/components/network_edit/generic_form.tsx
Normal file
172
napcat.webui/src/components/network_edit/generic_form.tsx
Normal file
@@ -0,0 +1,172 @@
|
||||
import { Button } from '@heroui/button'
|
||||
import { Input } from '@heroui/input'
|
||||
import { ModalBody, ModalFooter } from '@heroui/modal'
|
||||
import { Select, SelectItem } from '@heroui/select'
|
||||
import { ReactElement, useEffect } from 'react'
|
||||
import { Controller, useForm } from 'react-hook-form'
|
||||
import type {
|
||||
DefaultValues,
|
||||
Path,
|
||||
PathValue,
|
||||
SubmitHandler
|
||||
} from 'react-hook-form'
|
||||
import toast from 'react-hot-toast'
|
||||
|
||||
import SwitchCard from '../switch_card'
|
||||
|
||||
export type FieldTypes = 'input' | 'select' | 'switch'
|
||||
|
||||
type NetworkConfigType = OneBotConfig['network']
|
||||
|
||||
export interface Field<T extends keyof OneBotConfig['network']> {
|
||||
name: keyof NetworkConfigType[T][0]
|
||||
label: string
|
||||
type: FieldTypes
|
||||
options?: Array<{ key: string; value: string }>
|
||||
placeholder?: string
|
||||
isRequired?: boolean
|
||||
isDisabled?: boolean
|
||||
description?: string
|
||||
colSpan?: 1 | 2
|
||||
}
|
||||
|
||||
export interface GenericFormProps<T extends keyof NetworkConfigType> {
|
||||
data?: NetworkConfigType[T][0]
|
||||
defaultValues: DefaultValues<NetworkConfigType[T][0]>
|
||||
onClose: () => void
|
||||
onSubmit: (data: NetworkConfigType[T][0]) => Promise<void>
|
||||
fields: Array<Field<T>>
|
||||
}
|
||||
|
||||
const GenericForm = <T extends keyof NetworkConfigType>({
|
||||
data,
|
||||
defaultValues,
|
||||
onClose,
|
||||
onSubmit,
|
||||
fields
|
||||
}: GenericFormProps<T>): ReactElement => {
|
||||
const { control, handleSubmit, formState, setValue, reset } = useForm<
|
||||
NetworkConfigType[T][0]
|
||||
>({
|
||||
defaultValues
|
||||
})
|
||||
|
||||
const submitAction: SubmitHandler<NetworkConfigType[T][0]> = async (data) => {
|
||||
await onSubmit(data)
|
||||
onClose()
|
||||
}
|
||||
|
||||
const _onSubmit = handleSubmit(submitAction, (e) => {
|
||||
for (const error in e) {
|
||||
toast.error(e[error]?.message as string)
|
||||
return
|
||||
}
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
if (data) {
|
||||
const keys = Object.keys(data) as Path<NetworkConfig[T][0]>[]
|
||||
for (const key of keys) {
|
||||
const value = data[key] as PathValue<
|
||||
NetworkConfig[T][0],
|
||||
Path<NetworkConfig[T][0]>
|
||||
>
|
||||
setValue(key, value)
|
||||
}
|
||||
} else {
|
||||
reset()
|
||||
}
|
||||
}, [data, reset, setValue])
|
||||
|
||||
return (
|
||||
<>
|
||||
<ModalBody>
|
||||
<div className="grid grid-cols-2 gap-y-4 gap-x-2 w-full">
|
||||
{fields.map((field) => (
|
||||
<div
|
||||
key={field.name as string}
|
||||
className={field.colSpan === 1 ? 'col-span-1' : 'col-span-2'}
|
||||
>
|
||||
<Controller
|
||||
control={control}
|
||||
name={field.name as Path<NetworkConfig[T][0]>}
|
||||
rules={
|
||||
field.isRequired
|
||||
? {
|
||||
required: `请填写${field.label}`
|
||||
}
|
||||
: void 0
|
||||
}
|
||||
render={({ field: controllerField }) => {
|
||||
switch (field.type) {
|
||||
case 'input':
|
||||
return (
|
||||
<Input
|
||||
value={controllerField.value as string}
|
||||
onChange={controllerField.onChange}
|
||||
onBlur={controllerField.onBlur}
|
||||
ref={controllerField.ref}
|
||||
isRequired={field.isRequired}
|
||||
isDisabled={field.isDisabled}
|
||||
label={field.label}
|
||||
placeholder={field.placeholder}
|
||||
/>
|
||||
)
|
||||
case 'select':
|
||||
return (
|
||||
<Select
|
||||
{...controllerField}
|
||||
ref={controllerField.ref}
|
||||
isRequired={field.isRequired}
|
||||
label={field.label}
|
||||
placeholder={field.placeholder}
|
||||
selectedKeys={[controllerField.value as string]}
|
||||
value={controllerField.value.toString()}
|
||||
>
|
||||
{field.options?.map((option) => (
|
||||
<SelectItem key={option.key} value={option.value}>
|
||||
{option.value}
|
||||
</SelectItem>
|
||||
)) || <></>}
|
||||
</Select>
|
||||
)
|
||||
case 'switch':
|
||||
return (
|
||||
<SwitchCard
|
||||
{...controllerField}
|
||||
value={controllerField.value as boolean}
|
||||
description={field.description}
|
||||
label={field.label}
|
||||
/>
|
||||
)
|
||||
default:
|
||||
return <></>
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button
|
||||
color="danger"
|
||||
isDisabled={formState.isSubmitting}
|
||||
variant="light"
|
||||
onPress={onClose}
|
||||
>
|
||||
关闭
|
||||
</Button>
|
||||
<Button
|
||||
color="primary"
|
||||
isLoading={formState.isSubmitting}
|
||||
onPress={() => _onSubmit()}
|
||||
>
|
||||
保存
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default GenericForm
|
||||
95
napcat.webui/src/components/network_edit/http_client.tsx
Normal file
95
napcat.webui/src/components/network_edit/http_client.tsx
Normal file
@@ -0,0 +1,95 @@
|
||||
import GenericForm from './generic_form'
|
||||
import type { Field } from './generic_form'
|
||||
|
||||
export interface HTTPClientFormProps {
|
||||
data?: OneBotConfig['network']['httpClients'][0]
|
||||
onClose: () => void
|
||||
onSubmit: (data: OneBotConfig['network']['httpClients'][0]) => Promise<void>
|
||||
}
|
||||
|
||||
type HTTPClientFormType = OneBotConfig['network']['httpClients']
|
||||
|
||||
const HTTPClientForm: React.FC<HTTPClientFormProps> = ({
|
||||
data,
|
||||
onClose,
|
||||
onSubmit
|
||||
}) => {
|
||||
const defaultValues: HTTPClientFormType[0] = {
|
||||
enable: false,
|
||||
name: '',
|
||||
url: 'http://localhost:8080',
|
||||
reportSelfMessage: false,
|
||||
messagePostFormat: 'array',
|
||||
token: '',
|
||||
debug: false
|
||||
}
|
||||
|
||||
const fields: Field<'httpClients'>[] = [
|
||||
{
|
||||
name: 'enable',
|
||||
label: '启用',
|
||||
type: 'switch',
|
||||
description: '保存后启用此配置',
|
||||
colSpan: 1
|
||||
},
|
||||
{
|
||||
name: 'debug',
|
||||
label: '开启Debug',
|
||||
type: 'switch',
|
||||
description: '是否开启调试模式',
|
||||
colSpan: 1
|
||||
},
|
||||
{
|
||||
name: 'name',
|
||||
label: '名称',
|
||||
type: 'input',
|
||||
placeholder: '请输入名称',
|
||||
isRequired: true,
|
||||
isDisabled: !!data
|
||||
},
|
||||
{
|
||||
name: 'url',
|
||||
label: 'URL',
|
||||
type: 'input',
|
||||
placeholder: '请输入URL',
|
||||
isRequired: true
|
||||
},
|
||||
{
|
||||
name: 'reportSelfMessage',
|
||||
label: '上报自身消息',
|
||||
type: 'switch',
|
||||
description: '是否上报自身消息',
|
||||
colSpan: 1
|
||||
},
|
||||
{
|
||||
name: 'messagePostFormat',
|
||||
label: '消息格式',
|
||||
type: 'select',
|
||||
placeholder: '请选择消息格式',
|
||||
isRequired: true,
|
||||
options: [
|
||||
{ key: 'array', value: 'Array' },
|
||||
{ key: 'string', value: 'String' }
|
||||
],
|
||||
colSpan: 1
|
||||
},
|
||||
{
|
||||
name: 'token',
|
||||
label: 'Token',
|
||||
type: 'input',
|
||||
placeholder: '请输入Token'
|
||||
}
|
||||
]
|
||||
|
||||
return (
|
||||
<GenericForm
|
||||
data={data}
|
||||
defaultValues={defaultValues}
|
||||
onClose={onClose}
|
||||
onSubmit={onSubmit}
|
||||
fields={fields}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export default HTTPClientForm
|
||||
110
napcat.webui/src/components/network_edit/http_server.tsx
Normal file
110
napcat.webui/src/components/network_edit/http_server.tsx
Normal file
@@ -0,0 +1,110 @@
|
||||
import GenericForm from './generic_form'
|
||||
import type { Field } from './generic_form'
|
||||
|
||||
export interface HTTPServerFormProps {
|
||||
data?: OneBotConfig['network']['httpServers'][0]
|
||||
onClose: () => void
|
||||
onSubmit: (data: OneBotConfig['network']['httpServers'][0]) => Promise<void>
|
||||
}
|
||||
|
||||
type HTTPServerFormType = OneBotConfig['network']['httpServers']
|
||||
|
||||
const HTTPServerForm: React.FC<HTTPServerFormProps> = ({
|
||||
data,
|
||||
onClose,
|
||||
onSubmit
|
||||
}) => {
|
||||
const defaultValues: HTTPServerFormType[0] = {
|
||||
enable: false,
|
||||
name: '',
|
||||
host: '0.0.0.0',
|
||||
port: 3000,
|
||||
enableCors: true,
|
||||
enableWebsocket: true,
|
||||
messagePostFormat: 'array',
|
||||
token: '',
|
||||
debug: false
|
||||
}
|
||||
|
||||
const fields: Field<'httpServers'>[] = [
|
||||
{
|
||||
name: 'enable',
|
||||
label: '启用',
|
||||
type: 'switch',
|
||||
description: '保存后启用此配置',
|
||||
colSpan: 1
|
||||
},
|
||||
{
|
||||
name: 'debug',
|
||||
label: '开启Debug',
|
||||
type: 'switch',
|
||||
description: '是否开启调试模式',
|
||||
colSpan: 1
|
||||
},
|
||||
{
|
||||
name: 'name',
|
||||
label: '名称',
|
||||
type: 'input',
|
||||
placeholder: '请输入名称',
|
||||
isRequired: true,
|
||||
isDisabled: !!data
|
||||
},
|
||||
{
|
||||
name: 'host',
|
||||
label: 'Host',
|
||||
type: 'input',
|
||||
placeholder: '请输入主机地址',
|
||||
isRequired: true
|
||||
},
|
||||
{
|
||||
name: 'port',
|
||||
label: 'Port',
|
||||
type: 'input',
|
||||
placeholder: '请输入端口',
|
||||
isRequired: true
|
||||
},
|
||||
{
|
||||
name: 'enableCors',
|
||||
label: '启用CORS',
|
||||
type: 'switch',
|
||||
description: '是否启用CORS跨域',
|
||||
colSpan: 1
|
||||
},
|
||||
{
|
||||
name: 'enableWebsocket',
|
||||
label: '启用Websocket',
|
||||
type: 'switch',
|
||||
description: '是否启用Websocket',
|
||||
colSpan: 1
|
||||
},
|
||||
{
|
||||
name: 'messagePostFormat',
|
||||
label: '消息格式',
|
||||
type: 'select',
|
||||
placeholder: '请选择消息格式',
|
||||
isRequired: true,
|
||||
options: [
|
||||
{ key: 'array', value: 'Array' },
|
||||
{ key: 'string', value: 'String' }
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'token',
|
||||
label: 'Token',
|
||||
type: 'input',
|
||||
placeholder: '请输入Token'
|
||||
}
|
||||
]
|
||||
|
||||
return (
|
||||
<GenericForm
|
||||
data={data}
|
||||
defaultValues={defaultValues}
|
||||
onClose={onClose}
|
||||
onSubmit={onSubmit}
|
||||
fields={fields}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export default HTTPServerForm
|
||||
113
napcat.webui/src/components/network_edit/modal.tsx
Normal file
113
napcat.webui/src/components/network_edit/modal.tsx
Normal file
@@ -0,0 +1,113 @@
|
||||
import { Modal, ModalContent, ModalHeader } from '@heroui/modal'
|
||||
import toast from 'react-hot-toast'
|
||||
|
||||
import useConfig from '@/hooks/use-config'
|
||||
|
||||
import HTTPClientForm from './http_client'
|
||||
import HTTPServerForm from './http_server'
|
||||
import WebsocketClientForm from './ws_client'
|
||||
import WebsocketServerForm from './ws_server'
|
||||
|
||||
const modalTitle = {
|
||||
httpServers: 'HTTP Server',
|
||||
httpClients: 'HTTP Client',
|
||||
websocketServers: 'Websocket Server',
|
||||
websocketClients: 'Websocket Client'
|
||||
}
|
||||
|
||||
export interface NetworkFormModalProps<
|
||||
T extends keyof OneBotConfig['network']
|
||||
> {
|
||||
isOpen: boolean
|
||||
field: T
|
||||
data?: OneBotConfig['network'][T][0]
|
||||
onOpenChange: (isOpen: boolean) => void
|
||||
}
|
||||
|
||||
const NetworkFormModal = <T extends keyof OneBotConfig['network']>(
|
||||
props: NetworkFormModalProps<T>
|
||||
) => {
|
||||
const { isOpen, onOpenChange, field, data } = props
|
||||
const { createNetworkConfig, updateNetworkConfig } = useConfig()
|
||||
const isCreate = !data
|
||||
|
||||
const onSubmit = async (data: OneBotConfig['network'][typeof field][0]) => {
|
||||
try {
|
||||
if (isCreate) {
|
||||
await createNetworkConfig(field, data)
|
||||
} else {
|
||||
await updateNetworkConfig(field, data)
|
||||
}
|
||||
toast.success('保存配置成功')
|
||||
} catch (error) {
|
||||
const msg = (error as Error).message
|
||||
|
||||
toast.error(`保存配置失败: ${msg}`)
|
||||
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
const renderFormComponent = (onClose: () => void) => {
|
||||
switch (field) {
|
||||
case 'httpServers':
|
||||
return (
|
||||
<HTTPServerForm
|
||||
data={data as OneBotConfig['network']['httpServers'][0]}
|
||||
onClose={onClose}
|
||||
onSubmit={onSubmit}
|
||||
/>
|
||||
)
|
||||
case 'httpClients':
|
||||
return (
|
||||
<HTTPClientForm
|
||||
data={data as OneBotConfig['network']['httpClients'][0]}
|
||||
onClose={onClose}
|
||||
onSubmit={onSubmit}
|
||||
/>
|
||||
)
|
||||
case 'websocketServers':
|
||||
return (
|
||||
<WebsocketServerForm
|
||||
data={data as OneBotConfig['network']['websocketServers'][0]}
|
||||
onClose={onClose}
|
||||
onSubmit={onSubmit}
|
||||
/>
|
||||
)
|
||||
case 'websocketClients':
|
||||
return (
|
||||
<WebsocketClientForm
|
||||
data={data as OneBotConfig['network']['websocketClients'][0]}
|
||||
onClose={onClose}
|
||||
onSubmit={onSubmit}
|
||||
/>
|
||||
)
|
||||
default:
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
backdrop="blur"
|
||||
isDismissable={false}
|
||||
isOpen={isOpen}
|
||||
size="lg"
|
||||
scrollBehavior="outside"
|
||||
onOpenChange={onOpenChange}
|
||||
>
|
||||
<ModalContent>
|
||||
{(onClose) => (
|
||||
<>
|
||||
<ModalHeader className="flex flex-col gap-1">
|
||||
{modalTitle[field]}
|
||||
</ModalHeader>
|
||||
{renderFormComponent(onClose)}
|
||||
</>
|
||||
)}
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
||||
export default NetworkFormModal
|
||||
115
napcat.webui/src/components/network_edit/ws_client.tsx
Normal file
115
napcat.webui/src/components/network_edit/ws_client.tsx
Normal file
@@ -0,0 +1,115 @@
|
||||
import GenericForm from './generic_form'
|
||||
import type { Field } from './generic_form'
|
||||
|
||||
export interface WebsocketClientFormProps {
|
||||
data?: OneBotConfig['network']['websocketClients'][0]
|
||||
onClose: () => void
|
||||
onSubmit: (
|
||||
data: OneBotConfig['network']['websocketClients'][0]
|
||||
) => Promise<void>
|
||||
}
|
||||
|
||||
type WebsocketClientFormType = OneBotConfig['network']['websocketClients']
|
||||
|
||||
const WebsocketClientForm: React.FC<WebsocketClientFormProps> = ({
|
||||
data,
|
||||
onClose,
|
||||
onSubmit
|
||||
}) => {
|
||||
const defaultValues: WebsocketClientFormType[0] = {
|
||||
enable: false,
|
||||
name: '',
|
||||
url: 'ws://localhost:8082',
|
||||
reportSelfMessage: false,
|
||||
messagePostFormat: 'array',
|
||||
token: '',
|
||||
debug: false,
|
||||
heartInterval: 30000,
|
||||
reconnectInterval: 30000
|
||||
}
|
||||
|
||||
const fields: Field<'websocketClients'>[] = [
|
||||
{
|
||||
name: 'enable',
|
||||
label: '启用',
|
||||
type: 'switch',
|
||||
description: '保存后启用此配置',
|
||||
colSpan: 1
|
||||
},
|
||||
{
|
||||
name: 'debug',
|
||||
label: '开启Debug',
|
||||
type: 'switch',
|
||||
description: '是否开启调试模式',
|
||||
colSpan: 1
|
||||
},
|
||||
{
|
||||
name: 'name',
|
||||
label: '名称',
|
||||
type: 'input',
|
||||
placeholder: '请输入名称',
|
||||
isRequired: true,
|
||||
isDisabled: !!data
|
||||
},
|
||||
{
|
||||
name: 'url',
|
||||
label: 'URL',
|
||||
type: 'input',
|
||||
placeholder: '请输入URL',
|
||||
isRequired: true
|
||||
},
|
||||
{
|
||||
name: 'reportSelfMessage',
|
||||
label: '上报自身消息',
|
||||
type: 'switch',
|
||||
description: '是否上报自身消息',
|
||||
colSpan: 1
|
||||
},
|
||||
{
|
||||
name: 'messagePostFormat',
|
||||
label: '消息格式',
|
||||
type: 'select',
|
||||
placeholder: '请选择消息格式',
|
||||
isRequired: true,
|
||||
options: [
|
||||
{ key: 'array', value: 'Array' },
|
||||
{ key: 'string', value: 'String' }
|
||||
],
|
||||
colSpan: 1
|
||||
},
|
||||
{
|
||||
name: 'token',
|
||||
label: 'Token',
|
||||
type: 'input',
|
||||
placeholder: '请输入Token'
|
||||
},
|
||||
{
|
||||
name: 'heartInterval',
|
||||
label: '心跳间隔',
|
||||
type: 'input',
|
||||
placeholder: '请输入心跳间隔',
|
||||
isRequired: true,
|
||||
colSpan: 1
|
||||
},
|
||||
{
|
||||
name: 'reconnectInterval',
|
||||
label: '重连间隔',
|
||||
type: 'input',
|
||||
placeholder: '请输入重连间隔',
|
||||
isRequired: true,
|
||||
colSpan: 1
|
||||
}
|
||||
]
|
||||
|
||||
return (
|
||||
<GenericForm
|
||||
data={data}
|
||||
defaultValues={defaultValues}
|
||||
onClose={onClose}
|
||||
onSubmit={onSubmit}
|
||||
fields={fields}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export default WebsocketClientForm
|
||||
122
napcat.webui/src/components/network_edit/ws_server.tsx
Normal file
122
napcat.webui/src/components/network_edit/ws_server.tsx
Normal file
@@ -0,0 +1,122 @@
|
||||
import GenericForm from './generic_form'
|
||||
import type { Field } from './generic_form'
|
||||
|
||||
export interface WebsocketServerFormProps {
|
||||
data?: OneBotConfig['network']['websocketServers'][0]
|
||||
onClose: () => void
|
||||
onSubmit: (
|
||||
data: OneBotConfig['network']['websocketServers'][0]
|
||||
) => Promise<void>
|
||||
}
|
||||
|
||||
type WebsocketServerFormType = OneBotConfig['network']['websocketServers']
|
||||
|
||||
const WebsocketServerForm: React.FC<WebsocketServerFormProps> = ({
|
||||
data,
|
||||
onClose,
|
||||
onSubmit
|
||||
}) => {
|
||||
const defaultValues: WebsocketServerFormType[0] = {
|
||||
enable: false,
|
||||
name: '',
|
||||
host: '0.0.0.0',
|
||||
port: 3000,
|
||||
reportSelfMessage: false,
|
||||
enableForcePushEvent: true,
|
||||
messagePostFormat: 'array',
|
||||
token: '',
|
||||
debug: false,
|
||||
heartInterval: 30000
|
||||
}
|
||||
|
||||
const fields: Field<'websocketServers'>[] = [
|
||||
{
|
||||
name: 'enable',
|
||||
label: '启用',
|
||||
type: 'switch',
|
||||
description: '保存后启用此配置',
|
||||
colSpan: 1
|
||||
},
|
||||
{
|
||||
name: 'debug',
|
||||
label: '开启Debug',
|
||||
type: 'switch',
|
||||
description: '是否开启调试模式',
|
||||
colSpan: 1
|
||||
},
|
||||
{
|
||||
name: 'name',
|
||||
label: '名称',
|
||||
type: 'input',
|
||||
placeholder: '请输入名称',
|
||||
isRequired: true,
|
||||
isDisabled: !!data
|
||||
},
|
||||
{
|
||||
name: 'host',
|
||||
label: 'Host',
|
||||
type: 'input',
|
||||
placeholder: '请输入主机地址',
|
||||
isRequired: true
|
||||
},
|
||||
{
|
||||
name: 'port',
|
||||
label: 'Port',
|
||||
type: 'input',
|
||||
placeholder: '请输入端口',
|
||||
isRequired: true,
|
||||
colSpan: 1
|
||||
},
|
||||
{
|
||||
name: 'messagePostFormat',
|
||||
label: '消息格式',
|
||||
type: 'select',
|
||||
placeholder: '请选择消息格式',
|
||||
isRequired: true,
|
||||
options: [
|
||||
{ key: 'array', value: 'Array' },
|
||||
{ key: 'string', value: 'String' }
|
||||
],
|
||||
colSpan: 1
|
||||
},
|
||||
{
|
||||
name: 'reportSelfMessage',
|
||||
label: '上报自身消息',
|
||||
type: 'switch',
|
||||
description: '是否上报自身消息',
|
||||
colSpan: 1
|
||||
},
|
||||
{
|
||||
name: 'enableForcePushEvent',
|
||||
label: '强制推送事件',
|
||||
type: 'switch',
|
||||
description: '是否强制推送事件',
|
||||
colSpan: 1
|
||||
},
|
||||
{
|
||||
name: 'token',
|
||||
label: 'Token',
|
||||
type: 'input',
|
||||
placeholder: '请输入Token'
|
||||
},
|
||||
{
|
||||
name: 'heartInterval',
|
||||
label: '心跳间隔',
|
||||
type: 'input',
|
||||
placeholder: '请输入心跳间隔',
|
||||
isRequired: true
|
||||
}
|
||||
]
|
||||
|
||||
return (
|
||||
<GenericForm
|
||||
data={data}
|
||||
defaultValues={defaultValues}
|
||||
onClose={onClose}
|
||||
onSubmit={onSubmit}
|
||||
fields={fields}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export default WebsocketServerForm
|
||||
Reference in New Issue
Block a user