From fb2d292cbff2738307a9650186dea3cfe9c4d3fb Mon Sep 17 00:00:00 2001 From: Fu Diwei Date: Fri, 27 Dec 2024 16:42:07 +0800 Subject: [PATCH] feat(ui): multiple input domains & nameservers in ApplyNodeForm --- .../access/AccessEditFormSSHConfig.tsx | 7 +- .../certificate/CertificateDetailDrawer.tsx | 12 +- ui/src/components/core/DrawerForm.tsx | 99 ++++++++ ui/src/components/core/ModalForm.tsx | 119 +++++++++ ui/src/components/core/MultipleInput.tsx | 62 +++-- .../NotifyChannelEditFormEmailFields.tsx | 3 +- .../workflow/node/ApplyNodeForm.tsx | 228 ++++++++++++++++-- .../workflow/node/NotifyNodeForm.tsx | 2 +- ui/src/hooks/useAntdForm.ts | 2 +- ui/src/i18n/locales/en/nls.workflow.json | 10 +- ui/src/i18n/locales/zh/nls.workflow.json | 10 +- 11 files changed, 481 insertions(+), 73 deletions(-) create mode 100644 ui/src/components/core/DrawerForm.tsx create mode 100644 ui/src/components/core/ModalForm.tsx diff --git a/ui/src/components/access/AccessEditFormSSHConfig.tsx b/ui/src/components/access/AccessEditFormSSHConfig.tsx index bffdc009..e3fe7226 100644 --- a/ui/src/components/access/AccessEditFormSSHConfig.tsx +++ b/ui/src/components/access/AccessEditFormSSHConfig.tsx @@ -40,12 +40,7 @@ const AccessEditFormSSHConfig = ({ form, formName, disabled, initialValues, onVa }, { message: t("common.errmsg.host_invalid") } ), - port: z - .number() - .int() - .gte(1, t("common.errmsg.port_invalid")) - .lte(65535, t("common.errmsg.port_invalid")) - .transform((v) => +v), + port: z.number().int().gte(1, t("common.errmsg.port_invalid")).lte(65535, t("common.errmsg.port_invalid")), username: z .string() .min(1, "access.form.ssh_username.placeholder") diff --git a/ui/src/components/certificate/CertificateDetailDrawer.tsx b/ui/src/components/certificate/CertificateDetailDrawer.tsx index 522f90f5..e65cec51 100644 --- a/ui/src/components/certificate/CertificateDetailDrawer.tsx +++ b/ui/src/components/certificate/CertificateDetailDrawer.tsx @@ -27,7 +27,17 @@ const CertificateDetailDrawer = ({ data, loading, trigger, ...props }: Certifica <> {triggerDom} - setOpen(false)}> + setOpen(false)} + > diff --git a/ui/src/components/core/DrawerForm.tsx b/ui/src/components/core/DrawerForm.tsx new file mode 100644 index 00000000..e59312d8 --- /dev/null +++ b/ui/src/components/core/DrawerForm.tsx @@ -0,0 +1,99 @@ +import { useControllableValue } from "ahooks"; +import { Button, Drawer, Form, Space, type DrawerProps, type FormProps, type ModalProps } from "antd"; + +import { useAntdForm, useTriggerElement } from "@/hooks"; + +export interface DrawerFormProps = any> extends Omit, "title" | "onFinish"> { + className?: string; + style?: React.CSSProperties; + children?: React.ReactNode; + cancelButtonProps?: ModalProps["cancelButtonProps"]; + cancelText?: ModalProps["cancelText"]; + defaultOpen?: boolean; + drawerProps?: Omit; + okButtonProps?: ModalProps["okButtonProps"]; + okText?: ModalProps["okText"]; + open?: boolean; + title?: React.ReactNode; + trigger?: React.ReactNode; + width?: string | number; + onOpenChange?: (open: boolean) => void; + onFinish?: (values: T) => void | Promise; +} + +const DrawerForm = = any>({ + className, + style, + children, + form, + drawerProps, + title, + trigger, + width, + onFinish, + ...props +}: DrawerFormProps) => { + const [open, setOpen] = useControllableValue(props, { + valuePropName: "open", + defaultValuePropName: "defaultOpen", + trigger: "onOpenChange", + }); + + const triggerDom = useTriggerElement(trigger, { onClick: () => setOpen(true) }); + + const { + form: formInst, + formPending, + formProps, + submit, + } = useAntdForm({ + form, + onSubmit: async (values) => { + const ret = await onFinish?.(values); + if (ret != null && !ret) return false; + return true; + }, + }); + const mergedFormProps = { ...formProps, ...props }; + + const handleOkClick = async () => { + const ret = await submit(); + if (ret != null && !ret) return; + + setOpen(false); + }; + + const handleCancelClick = () => { + if (formPending) return; + + setOpen(false); + }; + + return ( + <> + {triggerDom} + + + + + + } + open={open} + title={title} + width={width} + {...drawerProps} + onClose={() => setOpen(false)} + > +
+ {children} +
+
+ + ); +}; + +export default DrawerForm; diff --git a/ui/src/components/core/ModalForm.tsx b/ui/src/components/core/ModalForm.tsx new file mode 100644 index 00000000..5b1586f3 --- /dev/null +++ b/ui/src/components/core/ModalForm.tsx @@ -0,0 +1,119 @@ +import { useControllableValue } from "ahooks"; +import { Form, Modal, type FormProps, type ModalProps } from "antd"; + +import { useAntdForm, useTriggerElement } from "@/hooks"; + +export interface ModalFormProps = any> extends Omit, "title" | "onFinish"> { + className?: string; + style?: React.CSSProperties; + children?: React.ReactNode; + cancelButtonProps?: ModalProps["cancelButtonProps"]; + cancelText?: ModalProps["cancelText"]; + defaultOpen?: boolean; + modalProps?: Omit< + ModalProps, + | "cancelButtonProps" + | "cancelText" + | "confirmLoading" + | "forceRender" + | "okButtonProps" + | "okText" + | "okType" + | "open" + | "title" + | "width" + | "onOk" + | "onCancel" + >; + okButtonProps?: ModalProps["okButtonProps"]; + okText?: ModalProps["okText"]; + open?: boolean; + title?: ModalProps["title"]; + trigger?: React.ReactNode; + width?: ModalProps["width"]; + onOpenChange?: (open: boolean) => void; + onFinish?: (values: T) => void | Promise; +} + +const ModalForm = = any>({ + className, + style, + children, + cancelButtonProps, + cancelText, + form, + modalProps, + okButtonProps, + okText, + title, + trigger, + width, + onFinish, + ...props +}: ModalFormProps) => { + const [open, setOpen] = useControllableValue(props, { + valuePropName: "open", + defaultValuePropName: "defaultOpen", + trigger: "onOpenChange", + }); + + const triggerDom = useTriggerElement(trigger, { onClick: () => setOpen(true) }); + + const { + form: formInst, + formPending, + formProps, + submit, + } = useAntdForm({ + form, + onSubmit: async (values) => { + const ret = await onFinish?.(values); + if (ret != null && !ret) return false; + return true; + }, + }); + const mergedFormProps = { ...formProps, ...props }; + + const handleOkClick = async () => { + const ret = await submit(); + if (ret != null && !ret) return; + + setOpen(false); + }; + + const handleCancelClick = () => { + if (formPending) return; + + setOpen(false); + }; + + return ( + <> + {triggerDom} + + +
+
+ {children} +
+
+
+ + ); +}; + +export default ModalForm; diff --git a/ui/src/components/core/MultipleInput.tsx b/ui/src/components/core/MultipleInput.tsx index 1de5cb83..fd76a5f0 100644 --- a/ui/src/components/core/MultipleInput.tsx +++ b/ui/src/components/core/MultipleInput.tsx @@ -3,10 +3,10 @@ import { useTranslation } from "react-i18next"; import { useControllableValue } from "ahooks"; import { Button, Input, Space, type InputRef, type InputProps } from "antd"; import { - DownOutlined as DownOutlinedIcon, + ArrowDownOutlined as ArrowDownOutlinedIcon, + ArrowUpOutlined as ArrowUpOutlinedIcon, MinusOutlined as MinusOutlinedIcon, PlusOutlined as PlusOutlinedIcon, - UpOutlined as UpOutlinedIcon, } from "@ant-design/icons"; import { produce } from "immer"; @@ -17,11 +17,11 @@ export type MultipleInputProps = Omit) => void; - onCreate?: (index: number) => void; - onRemove?: (index: number) => void; - onSort?: (oldIndex: number, newIndex: number) => void; - onValueChange?: (value: string[]) => void; + onChange?: (value: string[]) => void; + onValueChange?: (index: number, element: string) => void; + onValueCreate?: (index: number) => void; + onValueRemove?: (index: number) => void; + onValueSort?: (oldIndex: number, newIndex: number) => void; }; const MultipleInput = ({ @@ -30,10 +30,10 @@ const MultipleInput = ({ maxCount, minCount, showSortButton = true, - onChange, - onCreate, - onSort, - onRemove, + onValueChange, + onValueCreate, + onValueSort, + onValueRemove, ...props }: MultipleInputProps) => { const { t } = useTranslation(); @@ -44,7 +44,7 @@ const MultipleInput = ({ valuePropName: "value", defaultValue: [], defaultValuePropName: "defaultValue", - trigger: "onValueChange", + trigger: "onChange", }); const handleCreate = () => { @@ -54,16 +54,16 @@ const MultipleInput = ({ setValue(newValue); setTimeout(() => itemRefs.current[newValue.length - 1]?.focus(), 0); - onCreate?.(newValue.length - 1); + onValueCreate?.(newValue.length - 1); }; - const handleInputChange = (index: number, e: ChangeEvent) => { + const handleChange = (index: number, element: string) => { const newValue = produce(value, (draft) => { - draft[index] = e.target.value; + draft[index] = element; }); setValue(newValue); - onChange?.(index, e); + onValueChange?.(index, element); }; const handleInputBlur = (index: number) => { @@ -87,7 +87,7 @@ const MultipleInput = ({ }); setValue(newValue); - onSort?.(index, index - 1); + onValueSort?.(index, index - 1); }; const handleClickDown = (index: number) => { @@ -102,7 +102,7 @@ const MultipleInput = ({ }); setValue(newValue); - onSort?.(index, index + 1); + onValueSort?.(index, index + 1); }; const handleClickAdd = (index: number) => { @@ -112,7 +112,7 @@ const MultipleInput = ({ setValue(newValue); setTimeout(() => itemRefs.current[index + 1]?.focus(), 0); - onCreate?.(index + 1); + onValueCreate?.(index + 1); }; const handleClickRemove = (index: number) => { @@ -121,7 +121,7 @@ const MultipleInput = ({ }); setValue(newValue); - onRemove?.(index); + onValueRemove?.(index); }; return value == null || value.length === 0 ? ( @@ -139,6 +139,7 @@ const MultipleInput = ({ return ( (itemRefs.current[index] = ref!)} allowAdd={allowAdd} allowClear={allowClear} @@ -150,12 +151,11 @@ const MultipleInput = ({ showSortButton={showSortButton} value={element} onBlur={() => handleInputBlur(index)} - onChange={(val) => handleInputChange(index, val)} + onChange={(val) => handleChange(index, val)} onClickAdd={() => handleClickAdd(index)} onClickDown={() => handleClickDown(index)} onClickUp={() => handleClickUp(index)} onClickRemove={() => handleClickRemove(index)} - onValueChange={undefined} /> ); })} @@ -165,7 +165,7 @@ const MultipleInput = ({ type MultipleInputItemProps = Omit< MultipleInputProps, - "defaultValue" | "maxCount" | "minCount" | "preset" | "value" | "onChange" | "onCreate" | "onRemove" | "onSort" | "onValueChange" + "defaultValue" | "maxCount" | "minCount" | "preset" | "value" | "onChange" | "onValueCreate" | "onValueRemove" | "onValueSort" | "onValueChange" > & { allowAdd: boolean; allowRemove: boolean; @@ -173,12 +173,11 @@ type MultipleInputItemProps = Omit< allowDown: boolean; defaultValue?: string; value?: string; - onChange?: (e: ChangeEvent) => void; + onChange?: (value: string) => void; onClickAdd?: () => void; onClickDown?: () => void; onClickUp?: () => void; onClickRemove?: () => void; - onValueChange?: (value: string) => void; }; type MultipleInputItemInstance = { @@ -198,7 +197,6 @@ const MultipleInputItem = forwardRef { if (!showSortButton) return null; - return + } + onFinish={(v) => { + setFieldDomains(v); + formInst.setFieldValue("domain", v); + }} + /> + { rules={[formRule]} tooltip={} > - +