From 4d0f7c2e02df9237ebc570954fbd56b591bb34e9 Mon Sep 17 00:00:00 2001 From: Fu Diwei Date: Wed, 25 Dec 2024 14:51:32 +0800 Subject: [PATCH] refactor(ui): useAntdForm --- ui/src/components/access/AccessEditForm.tsx | 44 ++- .../AccessEditFormACMEHttpReqConfig.tsx | 28 +- .../access/AccessEditFormAWSConfig.tsx | 28 +- .../access/AccessEditFormAliyunConfig.tsx | 28 +- .../access/AccessEditFormBaiduCloudConfig.tsx | 28 +- .../access/AccessEditFormBytePlusConfig.tsx | 28 +- .../access/AccessEditFormCloudflareConfig.tsx | 28 +- .../access/AccessEditFormDogeCloudConfig.tsx | 28 +- .../access/AccessEditFormGoDaddyConfig.tsx | 28 +- .../AccessEditFormHuaweiCloudConfig.tsx | 28 +- .../access/AccessEditFormKubernetesConfig.tsx | 31 +- .../access/AccessEditFormLocalConfig.tsx | 29 +- .../access/AccessEditFormNameSiloConfig.tsx | 28 +- .../access/AccessEditFormPowerDNSConfig.tsx | 28 +- .../access/AccessEditFormQiniuConfig.tsx | 28 +- .../access/AccessEditFormSSHConfig.tsx | 31 +- .../AccessEditFormTencentCloudConfig.tsx | 28 +- .../access/AccessEditFormVolcEngineConfig.tsx | 28 +- .../access/AccessEditFormWebhookConfig.tsx | 27 +- ui/src/components/access/AccessEditModal.tsx | 4 +- .../components/{ui => core}/MultipleInput.tsx | 0 ui/src/components/{ui => core}/Version.tsx | 0 .../notification/NotifyChannelEditForm.tsx | 40 ++- .../NotifyChannelEditFormEmailFields.tsx | 4 +- .../notification/NotifyChannels.tsx | 6 +- .../notification/NotifyTemplate.tsx | 59 ++-- ui/src/components/workflow/ApplyForm.tsx | 267 +++++++++--------- ui/src/components/workflow/Panel.tsx | 4 +- .../workflow/node/StartNodeForm.tsx | 14 +- ui/src/hooks/index.ts | 5 +- ui/src/hooks/useAntdForm.ts | 106 +++++++ ui/src/hooks/useBrowserTheme.ts | 12 +- ui/src/hooks/useZustandShallowSelector.ts | 27 +- ui/src/i18n/locales/en/nls.settings.json | 4 +- ui/src/i18n/locales/zh/nls.settings.json | 4 +- ui/src/pages/AuthLayout.tsx | 2 +- ui/src/pages/ConsoleLayout.tsx | 2 +- ui/src/pages/login/Login.tsx | 33 ++- ui/src/pages/settings/SettingsAccount.tsx | 59 ++-- ui/src/pages/settings/SettingsPassword.tsx | 62 ++-- ui/src/pages/settings/SettingsSSLProvider.tsx | 177 ++++++------ ui/src/pages/workflows/WorkflowDetail.tsx | 7 +- ui/src/stores/workflow/index.ts | 4 +- 43 files changed, 779 insertions(+), 677 deletions(-) rename ui/src/components/{ui => core}/MultipleInput.tsx (100%) rename ui/src/components/{ui => core}/Version.tsx (100%) create mode 100644 ui/src/hooks/useAntdForm.ts diff --git a/ui/src/components/access/AccessEditForm.tsx b/ui/src/components/access/AccessEditForm.tsx index e8c5a3b8..01f182d1 100644 --- a/ui/src/components/access/AccessEditForm.tsx +++ b/ui/src/components/access/AccessEditForm.tsx @@ -1,10 +1,11 @@ import { forwardRef, useEffect, useImperativeHandle, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; -import { useCreation, useDeepCompareEffect } from "ahooks"; +import { useCreation } from "ahooks"; import { Form, Input } from "antd"; import { createSchemaFieldRule } from "antd-zod"; import { z } from "zod"; +import { useAntdForm } from "@/hooks"; import { ACCESS_PROVIDER_TYPES, type AccessModel } from "@/domain/access"; import AccessTypeSelect from "./AccessTypeSelect"; import AccessEditFormACMEHttpReqConfig from "./AccessEditFormACMEHttpReqConfig"; @@ -26,22 +27,22 @@ import AccessEditFormTencentCloudConfig from "./AccessEditFormTencentCloudConfig import AccessEditFormVolcEngineConfig from "./AccessEditFormVolcEngineConfig"; import AccessEditFormWebhookConfig from "./AccessEditFormWebhookConfig"; -type AccessEditFormModelType = Partial>; +type AccessEditFormModelValues = Partial>; type AccessEditFormPresets = "add" | "edit"; export type AccessEditFormProps = { className?: string; style?: React.CSSProperties; disabled?: boolean; - model?: AccessEditFormModelType; + model?: AccessEditFormModelValues; preset: AccessEditFormPresets; - onModelChange?: (model: AccessEditFormModelType) => void; + onModelChange?: (model: AccessEditFormModelValues) => void; }; export type AccessEditFormInstance = { - getFieldsValue: () => AccessEditFormModelType; + getFieldsValue: () => AccessEditFormModelValues; resetFields: () => void; - validateFields: () => Promise; + validateFields: () => Promise; }; const AccessEditForm = forwardRef(({ className, style, disabled, model, preset, onModelChange }, ref) => { @@ -57,12 +58,9 @@ const AccessEditForm = forwardRef(( config: z.any(), }); const formRule = createSchemaFieldRule(formSchema); - const [form] = Form.useForm>(); - - const [initialValues, setInitialValues] = useState>>(model as Partial>); - useDeepCompareEffect(() => { - setInitialValues(model as Partial>); - }, [model]); + const { form: formInst, formProps } = useAntdForm>({ + initialValues: model as Partial>, + }); const [configType, setConfigType] = useState(model?.configType); useEffect(() => { @@ -115,32 +113,32 @@ const AccessEditForm = forwardRef(( case ACCESS_PROVIDER_TYPES.WEBHOOK: return ; } - }, [model, configType, configFormInst]); + }, [disabled, model, configType, configFormInst, configFormName]); const handleFormProviderChange = (name: string) => { if (name === configFormName) { - form.setFieldValue("config", configFormInst.getFieldsValue()); - onModelChange?.(form.getFieldsValue(true)); + formInst.setFieldValue("config", configFormInst.getFieldsValue()); + onModelChange?.(formInst.getFieldsValue(true)); } }; - const handleFormChange = (_: unknown, fields: AccessEditFormModelType) => { - if (fields.configType !== configType) { - setConfigType(fields.configType); + const handleFormChange = (_: unknown, values: AccessEditFormModelValues) => { + if (values.configType !== configType) { + setConfigType(values.configType); } - onModelChange?.(fields); + onModelChange?.(values); }; useImperativeHandle(ref, () => ({ getFieldsValue: () => { - return form.getFieldsValue(true); + return formInst.getFieldsValue(true); }, resetFields: () => { - return form.resetFields(); + return formInst.resetFields(); }, validateFields: () => { - const t1 = form.validateFields(); + const t1 = formInst.validateFields(); const t2 = configFormInst.validateFields(); return Promise.all([t1, t2]).then(() => t1); }, @@ -149,7 +147,7 @@ const AccessEditForm = forwardRef(( return (
-
+ diff --git a/ui/src/components/access/AccessEditFormACMEHttpReqConfig.tsx b/ui/src/components/access/AccessEditFormACMEHttpReqConfig.tsx index a48c66d1..b03f6e30 100644 --- a/ui/src/components/access/AccessEditFormACMEHttpReqConfig.tsx +++ b/ui/src/components/access/AccessEditFormACMEHttpReqConfig.tsx @@ -1,27 +1,26 @@ -import { useState } from "react"; import { useTranslation } from "react-i18next"; -import { useDeepCompareEffect } from "ahooks"; import { Form, Input, Select, type FormInstance } from "antd"; import { createSchemaFieldRule } from "antd-zod"; import { z } from "zod"; +import { useAntdForm } from "@/hooks"; import { type ACMEHttpReqAccessConfig } from "@/domain/access"; -type AccessEditFormACMEHttpReqConfigModelType = Partial; +type AccessEditFormACMEHttpReqConfigModelValues = Partial; export type AccessEditFormACMEHttpReqConfigProps = { form: FormInstance; formName: string; disabled?: boolean; - model?: AccessEditFormACMEHttpReqConfigModelType; - onModelChange?: (model: AccessEditFormACMEHttpReqConfigModelType) => void; + model?: AccessEditFormACMEHttpReqConfigModelValues; + onModelChange?: (model: AccessEditFormACMEHttpReqConfigModelValues) => void; }; -const initModel = () => { +const initFormModel = (): AccessEditFormACMEHttpReqConfigModelValues => { return { endpoint: "https://example.com/api/", mode: "", - } as AccessEditFormACMEHttpReqConfigModelType; + }; }; const AccessEditFormACMEHttpReqConfig = ({ form, formName, disabled, model, onModelChange }: AccessEditFormACMEHttpReqConfigProps) => { @@ -44,18 +43,17 @@ const AccessEditFormACMEHttpReqConfig = ({ form, formName, disabled, model, onMo .nullish(), }); const formRule = createSchemaFieldRule(formSchema); + const { form: formInst, formProps } = useAntdForm>({ + form: form, + initialValues: model ?? initFormModel(), + }); - const [initialValues, setInitialValues] = useState>>(model ?? initModel()); - useDeepCompareEffect(() => { - setInitialValues(model ?? initModel()); - }, [model]); - - const handleFormChange = (_: unknown, fields: AccessEditFormACMEHttpReqConfigModelType) => { - onModelChange?.(fields); + const handleFormChange = (_: unknown, values: z.infer) => { + onModelChange?.(values as AccessEditFormACMEHttpReqConfigModelValues); }; return ( - + ; +type AccessEditFormAWSConfigModelValues = Partial; export type AccessEditFormAWSConfigProps = { form: FormInstance; formName: string; disabled?: boolean; - model?: AccessEditFormAWSConfigModelType; - onModelChange?: (model: AccessEditFormAWSConfigModelType) => void; + model?: AccessEditFormAWSConfigModelValues; + onModelChange?: (model: AccessEditFormAWSConfigModelValues) => void; }; -const initModel = () => { +const initFormModel = (): AccessEditFormAWSConfigModelValues => { return { accessKeyId: "", secretAccessKey: "", region: "us-east-1", hostedZoneId: "", - } as AccessEditFormAWSConfigModelType; + }; }; const AccessEditFormAWSConfig = ({ form, formName, disabled, model, onModelChange }: AccessEditFormAWSConfigProps) => { @@ -56,18 +55,17 @@ const AccessEditFormAWSConfig = ({ form, formName, disabled, model, onModelChang .nullish(), }); const formRule = createSchemaFieldRule(formSchema); + const { form: formInst, formProps } = useAntdForm>({ + form: form, + initialValues: model ?? initFormModel(), + }); - const [initialValues, setInitialValues] = useState>>(model ?? initModel()); - useDeepCompareEffect(() => { - setInitialValues(model ?? initModel()); - }, [model]); - - const handleFormChange = (_: unknown, fields: AccessEditFormAWSConfigModelType) => { - onModelChange?.(fields); + const handleFormChange = (_: unknown, values: z.infer) => { + onModelChange?.(values as AccessEditFormAWSConfigModelValues); }; return ( - + ; +type AccessEditFormAliyunConfigModelValues = Partial; export type AccessEditFormAliyunConfigProps = { form: FormInstance; formName: string; disabled?: boolean; - model?: AccessEditFormAliyunConfigModelType; - onModelChange?: (model: AccessEditFormAliyunConfigModelType) => void; + model?: AccessEditFormAliyunConfigModelValues; + onModelChange?: (model: AccessEditFormAliyunConfigModelValues) => void; }; -const initModel = () => { +const initFormModel = (): AccessEditFormAliyunConfigModelValues => { return { accessKeyId: "", accessKeySecret: "", - } as AccessEditFormAliyunConfigModelType; + }; }; const AccessEditFormAliyunConfig = ({ form, formName, disabled, model, onModelChange }: AccessEditFormAliyunConfigProps) => { @@ -40,18 +39,17 @@ const AccessEditFormAliyunConfig = ({ form, formName, disabled, model, onModelCh .max(64, t("common.errmsg.string_max", { max: 64 })), }); const formRule = createSchemaFieldRule(formSchema); + const { form: formInst, formProps } = useAntdForm>({ + form: form, + initialValues: model ?? initFormModel(), + }); - const [initialValues, setInitialValues] = useState>>(model ?? initModel()); - useDeepCompareEffect(() => { - setInitialValues(model ?? initModel()); - }, [model]); - - const handleFormChange = (_: unknown, fields: AccessEditFormAliyunConfigModelType) => { - onModelChange?.(fields); + const handleFormChange = (_: unknown, values: z.infer) => { + onModelChange?.(values as AccessEditFormAliyunConfigModelValues); }; return ( - + ; +type AccessEditFormBaiduCloudConfigModelValues = Partial; export type AccessEditFormBaiduCloudConfigProps = { form: FormInstance; formName: string; disabled?: boolean; - model?: AccessEditFormBaiduCloudConfigModelType; - onModelChange?: (model: AccessEditFormBaiduCloudConfigModelType) => void; + model?: AccessEditFormBaiduCloudConfigModelValues; + onModelChange?: (model: AccessEditFormBaiduCloudConfigModelValues) => void; }; -const initModel = () => { +const initFormModel = (): AccessEditFormBaiduCloudConfigModelValues => { return { accessKeyId: "", secretAccessKey: "", - } as AccessEditFormBaiduCloudConfigModelType; + }; }; const AccessEditFormBaiduCloudConfig = ({ form, formName, disabled, model, onModelChange }: AccessEditFormBaiduCloudConfigProps) => { @@ -40,18 +39,17 @@ const AccessEditFormBaiduCloudConfig = ({ form, formName, disabled, model, onMod .max(64, t("common.errmsg.string_max", { max: 64 })), }); const formRule = createSchemaFieldRule(formSchema); + const { form: formInst, formProps } = useAntdForm>({ + form: form, + initialValues: model ?? initFormModel(), + }); - const [initialValues, setInitialValues] = useState>>(model ?? initModel()); - useDeepCompareEffect(() => { - setInitialValues(model ?? initModel()); - }, [model]); - - const handleFormChange = (_: unknown, fields: AccessEditFormBaiduCloudConfigModelType) => { - onModelChange?.(fields); + const handleFormChange = (_: unknown, values: z.infer) => { + onModelChange?.(values as AccessEditFormBaiduCloudConfigModelValues); }; return ( - + ; +type AccessEditFormBytePlusConfigModelValues = Partial; export type AccessEditFormBytePlusConfigProps = { form: FormInstance; formName: string; disabled?: boolean; - model?: AccessEditFormBytePlusConfigModelType; - onModelChange?: (model: AccessEditFormBytePlusConfigModelType) => void; + model?: AccessEditFormBytePlusConfigModelValues; + onModelChange?: (model: AccessEditFormBytePlusConfigModelValues) => void; }; -const initModel = () => { +const initFormModel = (): AccessEditFormBytePlusConfigModelValues => { return { accessKey: "", secretKey: "", - } as AccessEditFormBytePlusConfigModelType; + }; }; const AccessEditFormBytePlusConfig = ({ form, formName, disabled, model, onModelChange }: AccessEditFormBytePlusConfigProps) => { @@ -40,18 +39,17 @@ const AccessEditFormBytePlusConfig = ({ form, formName, disabled, model, onModel .max(64, t("common.errmsg.string_max", { max: 64 })), }); const formRule = createSchemaFieldRule(formSchema); + const { form: formInst, formProps } = useAntdForm>({ + form: form, + initialValues: model ?? initFormModel(), + }); - const [initialValues, setInitialValues] = useState>>(model ?? initModel()); - useDeepCompareEffect(() => { - setInitialValues(model ?? initModel()); - }, [model]); - - const handleFormChange = (_: unknown, fields: AccessEditFormBytePlusConfigModelType) => { - onModelChange?.(fields); + const handleFormChange = (_: unknown, values: z.infer) => { + onModelChange?.(values as AccessEditFormBytePlusConfigModelValues); }; return ( - + ; +type AccessEditFormCloudflareConfigModelValues = Partial; export type AccessEditFormCloudflareConfigProps = { form: FormInstance; formName: string; disabled?: boolean; - model?: AccessEditFormCloudflareConfigModelType; - onModelChange?: (model: AccessEditFormCloudflareConfigModelType) => void; + model?: AccessEditFormCloudflareConfigModelValues; + onModelChange?: (model: AccessEditFormCloudflareConfigModelValues) => void; }; -const initModel = () => { +const initFormModel = (): AccessEditFormCloudflareConfigModelValues => { return { dnsApiToken: "", - } as AccessEditFormCloudflareConfigModelType; + }; }; const AccessEditFormCloudflareConfig = ({ form, formName, disabled, model, onModelChange }: AccessEditFormCloudflareConfigProps) => { @@ -34,18 +33,17 @@ const AccessEditFormCloudflareConfig = ({ form, formName, disabled, model, onMod .max(64, t("common.errmsg.string_max", { max: 64 })), }); const formRule = createSchemaFieldRule(formSchema); + const { form: formInst, formProps } = useAntdForm>({ + form: form, + initialValues: model ?? initFormModel(), + }); - const [initialValues, setInitialValues] = useState>>(model ?? initModel()); - useDeepCompareEffect(() => { - setInitialValues(model ?? initModel()); - }, [model]); - - const handleFormChange = (_: unknown, fields: AccessEditFormCloudflareConfigModelType) => { - onModelChange?.(fields); + const handleFormChange = (_: unknown, values: z.infer) => { + onModelChange?.(values as AccessEditFormCloudflareConfigModelValues); }; return ( - + ; +type AccessEditFormDogeCloudConfigModelValues = Partial; export type AccessEditFormDogeCloudConfigProps = { form: FormInstance; formName: string; disabled?: boolean; - model?: AccessEditFormDogeCloudConfigModelType; - onModelChange?: (model: AccessEditFormDogeCloudConfigModelType) => void; + model?: AccessEditFormDogeCloudConfigModelValues; + onModelChange?: (model: AccessEditFormDogeCloudConfigModelValues) => void; }; -const initModel = () => { +const initFormModel = (): AccessEditFormDogeCloudConfigModelValues => { return { accessKey: "", secretKey: "", - } as AccessEditFormDogeCloudConfigModelType; + }; }; const AccessEditFormDogeCloudConfig = ({ form, formName, disabled, model, onModelChange }: AccessEditFormDogeCloudConfigProps) => { @@ -40,18 +39,17 @@ const AccessEditFormDogeCloudConfig = ({ form, formName, disabled, model, onMode .max(64, t("common.errmsg.string_max", { max: 64 })), }); const formRule = createSchemaFieldRule(formSchema); + const { form: formInst, formProps } = useAntdForm>({ + form: form, + initialValues: model ?? initFormModel(), + }); - const [initialValues, setInitialValues] = useState>>(model ?? initModel()); - useDeepCompareEffect(() => { - setInitialValues(model ?? initModel()); - }, [model]); - - const handleFormChange = (_: unknown, fields: AccessEditFormDogeCloudConfigModelType) => { - onModelChange?.(fields); + const handleFormChange = (_: unknown, values: z.infer) => { + onModelChange?.(values as AccessEditFormDogeCloudConfigModelValues); }; return ( - + ; +type AccessEditFormGoDaddyConfigModelValues = Partial; export type AccessEditFormGoDaddyConfigProps = { form: FormInstance; formName: string; disabled?: boolean; - model?: AccessEditFormGoDaddyConfigModelType; - onModelChange?: (model: AccessEditFormGoDaddyConfigModelType) => void; + model?: AccessEditFormGoDaddyConfigModelValues; + onModelChange?: (model: AccessEditFormGoDaddyConfigModelValues) => void; }; -const initModel = () => { +const initFormModel = (): AccessEditFormGoDaddyConfigModelValues => { return { apiKey: "", apiSecret: "", - } as AccessEditFormGoDaddyConfigModelType; + }; }; const AccessEditFormGoDaddyConfig = ({ form, formName, disabled, model, onModelChange }: AccessEditFormGoDaddyConfigProps) => { @@ -40,18 +39,17 @@ const AccessEditFormGoDaddyConfig = ({ form, formName, disabled, model, onModelC .max(64, t("common.errmsg.string_max", { max: 64 })), }); const formRule = createSchemaFieldRule(formSchema); + const { form: formInst, formProps } = useAntdForm>({ + form: form, + initialValues: model ?? initFormModel(), + }); - const [initialValues, setInitialValues] = useState>>(model ?? initModel()); - useDeepCompareEffect(() => { - setInitialValues(model ?? initModel()); - }, [model]); - - const handleFormChange = (_: unknown, fields: AccessEditFormGoDaddyConfigModelType) => { - onModelChange?.(fields); + const handleFormChange = (_: unknown, values: z.infer) => { + onModelChange?.(values as AccessEditFormGoDaddyConfigModelValues); }; return ( - + ; +type AccessEditFormHuaweiCloudConfigModelValues = Partial; export type AccessEditFormHuaweiCloudConfigProps = { form: FormInstance; formName: string; disabled?: boolean; - model?: AccessEditFormHuaweiCloudConfigModelType; - onModelChange?: (model: AccessEditFormHuaweiCloudConfigModelType) => void; + model?: AccessEditFormHuaweiCloudConfigModelValues; + onModelChange?: (model: AccessEditFormHuaweiCloudConfigModelValues) => void; }; -const initModel = () => { +const initFormModel = (): AccessEditFormHuaweiCloudConfigModelValues => { return { accessKeyId: "", secretAccessKey: "", region: "cn-north-1", - } as AccessEditFormHuaweiCloudConfigModelType; + }; }; const AccessEditFormHuaweiCloudConfig = ({ form, formName, disabled, model, onModelChange }: AccessEditFormHuaweiCloudConfigProps) => { @@ -48,18 +47,17 @@ const AccessEditFormHuaweiCloudConfig = ({ form, formName, disabled, model, onMo .nullish(), }); const formRule = createSchemaFieldRule(formSchema); + const { form: formInst, formProps } = useAntdForm>({ + form: form, + initialValues: model ?? initFormModel(), + }); - const [initialValues, setInitialValues] = useState>>(model ?? initModel()); - useDeepCompareEffect(() => { - setInitialValues(model ?? initModel()); - }, [model]); - - const handleFormChange = (_: unknown, fields: AccessEditFormHuaweiCloudConfigModelType) => { - onModelChange?.(fields); + const handleFormChange = (_: unknown, values: z.infer) => { + onModelChange?.(values as AccessEditFormHuaweiCloudConfigModelValues); }; return ( - + ; +type AccessEditFormKubernetesConfigModelValues = Partial; export type AccessEditFormKubernetesConfigProps = { form: FormInstance; formName: string; disabled?: boolean; - model?: AccessEditFormKubernetesConfigModelType; - onModelChange?: (model: AccessEditFormKubernetesConfigModelType) => void; + model?: AccessEditFormKubernetesConfigModelValues; + onModelChange?: (model: AccessEditFormKubernetesConfigModelValues) => void; }; -const initModel = () => { - return {} as AccessEditFormKubernetesConfigModelType; +const initFormModel = (): AccessEditFormKubernetesConfigModelValues => { + return {}; }; const AccessEditFormKubernetesConfig = ({ form, formName, disabled, model, onModelChange }: AccessEditFormKubernetesConfigProps) => { @@ -36,21 +37,21 @@ const AccessEditFormKubernetesConfig = ({ form, formName, disabled, model, onMod .nullish(), }); const formRule = createSchemaFieldRule(formSchema); - const formInst = form as FormInstance>; + const { form: formInst, formProps } = useAntdForm>({ + form: form, + initialValues: model ?? initFormModel(), + }); - const [initialValues, setInitialValues] = useState>>(model ?? initModel()); + const [kubeFileList, setKubeFileList] = useState([]); useDeepCompareEffect(() => { - setInitialValues(model ?? initModel()); setKubeFileList(model?.kubeConfig?.trim() ? [{ uid: "-1", name: "kubeconfig", status: "done" }] : []); }, [model]); - const [kubeFileList, setKubeFileList] = useState([]); - - const handleFormChange = (_: unknown, fields: AccessEditFormKubernetesConfigModelType) => { - onModelChange?.(fields); + const handleFormChange = (_: unknown, values: z.infer) => { + onModelChange?.(values as AccessEditFormKubernetesConfigModelValues); }; - const handleUploadChange: UploadProps["onChange"] = async ({ file }) => { + const handleKubeFileChange: UploadProps["onChange"] = async ({ file }) => { if (file && file.status !== "removed") { formInst.setFieldValue("kubeConfig", (await readFileContent(file.originFileObj ?? (file as unknown as File))).trim()); setKubeFileList([file]); @@ -63,7 +64,7 @@ const AccessEditFormKubernetesConfig = ({ form, formName, disabled, model, onMod }; return ( - + } > - false} fileList={kubeFileList} maxCount={1} onChange={handleUploadChange}> + false} fileList={kubeFileList} maxCount={1} onChange={handleKubeFileChange}> diff --git a/ui/src/components/access/AccessEditFormLocalConfig.tsx b/ui/src/components/access/AccessEditFormLocalConfig.tsx index ae9ac79c..406b18b7 100644 --- a/ui/src/components/access/AccessEditFormLocalConfig.tsx +++ b/ui/src/components/access/AccessEditFormLocalConfig.tsx @@ -1,30 +1,33 @@ -import { useState } from "react"; -import { useDeepCompareEffect } from "ahooks"; import { Form, type FormInstance } from "antd"; +import { useAntdForm } from "@/hooks"; import { type LocalAccessConfig } from "@/domain/access"; -type AccessEditFormLocalConfigModelType = Partial; +type AccessEditFormLocalConfigModelValues = Partial; export type AccessEditFormLocalConfigProps = { form: FormInstance; formName: string; disabled?: boolean; - model?: AccessEditFormLocalConfigModelType; - onModelChange?: (model: AccessEditFormLocalConfigModelType) => void; + model?: AccessEditFormLocalConfigModelValues; + onModelChange?: (model: AccessEditFormLocalConfigModelValues) => void; }; -const initModel = () => { - return {} as AccessEditFormLocalConfigModelType; +const initFormModel = (): AccessEditFormLocalConfigModelValues => { + return {}; }; -const AccessEditFormLocalConfig = ({ form, formName, disabled, model }: AccessEditFormLocalConfigProps) => { - const [initialValues, setInitialValues] = useState(model ?? initModel()); - useDeepCompareEffect(() => { - setInitialValues(model ?? initModel()); - }, [model]); +const AccessEditFormLocalConfig = ({ form, formName, disabled, model, onModelChange }: AccessEditFormLocalConfigProps) => { + const { form: formInst, formProps } = useAntdForm({ + form: form, + initialValues: model ?? initFormModel(), + }); - return ; + const handleFormChange = (_: unknown, values: unknown) => { + onModelChange?.(values as AccessEditFormLocalConfigModelValues); + }; + + return
; }; export default AccessEditFormLocalConfig; diff --git a/ui/src/components/access/AccessEditFormNameSiloConfig.tsx b/ui/src/components/access/AccessEditFormNameSiloConfig.tsx index 19ca5d92..29633f4c 100644 --- a/ui/src/components/access/AccessEditFormNameSiloConfig.tsx +++ b/ui/src/components/access/AccessEditFormNameSiloConfig.tsx @@ -1,26 +1,25 @@ -import { useState } from "react"; import { useTranslation } from "react-i18next"; -import { useDeepCompareEffect } from "ahooks"; import { Form, Input, type FormInstance } from "antd"; import { createSchemaFieldRule } from "antd-zod"; import { z } from "zod"; +import { useAntdForm } from "@/hooks"; import { type NameSiloAccessConfig } from "@/domain/access"; -type AccessEditFormNameSiloConfigModelType = Partial; +type AccessEditFormNameSiloConfigModelValues = Partial; export type AccessEditFormNameSiloConfigProps = { form: FormInstance; formName: string; disabled?: boolean; - model?: AccessEditFormNameSiloConfigModelType; - onModelChange?: (model: AccessEditFormNameSiloConfigModelType) => void; + model?: AccessEditFormNameSiloConfigModelValues; + onModelChange?: (model: AccessEditFormNameSiloConfigModelValues) => void; }; -const initModel = () => { +const initFormModel = (): AccessEditFormNameSiloConfigModelValues => { return { apiKey: "", - } as AccessEditFormNameSiloConfigModelType; + }; }; const AccessEditFormNameSiloConfig = ({ form, formName, disabled, model, onModelChange }: AccessEditFormNameSiloConfigProps) => { @@ -34,18 +33,17 @@ const AccessEditFormNameSiloConfig = ({ form, formName, disabled, model, onModel .max(64, t("common.errmsg.string_max", { max: 64 })), }); const formRule = createSchemaFieldRule(formSchema); + const { form: formInst, formProps } = useAntdForm>({ + form: form, + initialValues: model ?? initFormModel(), + }); - const [initialValues, setInitialValues] = useState>>(model ?? initModel()); - useDeepCompareEffect(() => { - setInitialValues(model ?? initModel()); - }, [model]); - - const handleFormChange = (_: unknown, fields: AccessEditFormNameSiloConfigModelType) => { - onModelChange?.(fields); + const handleFormChange = (_: unknown, values: z.infer) => { + onModelChange?.(values as AccessEditFormNameSiloConfigModelValues); }; return ( -
+ ; +type AccessEditFormPowerDNSConfigModelValues = Partial; export type AccessEditFormPowerDNSConfigProps = { form: FormInstance; formName: string; disabled?: boolean; - model?: AccessEditFormPowerDNSConfigModelType; - onModelChange?: (model: AccessEditFormPowerDNSConfigModelType) => void; + model?: AccessEditFormPowerDNSConfigModelValues; + onModelChange?: (model: AccessEditFormPowerDNSConfigModelValues) => void; }; -const initModel = () => { +const initFormModel = (): AccessEditFormPowerDNSConfigModelValues => { return { apiUrl: "", apiKey: "", - } as AccessEditFormPowerDNSConfigModelType; + }; }; const AccessEditFormPowerDNSConfig = ({ form, formName, disabled, model, onModelChange }: AccessEditFormPowerDNSConfigProps) => { @@ -36,18 +35,17 @@ const AccessEditFormPowerDNSConfig = ({ form, formName, disabled, model, onModel .max(64, t("common.errmsg.string_max", { max: 64 })), }); const formRule = createSchemaFieldRule(formSchema); + const { form: formInst, formProps } = useAntdForm>({ + form: form, + initialValues: model ?? initFormModel(), + }); - const [initialValues, setInitialValues] = useState>>(model ?? initModel()); - useDeepCompareEffect(() => { - setInitialValues(model ?? initModel()); - }, [model]); - - const handleFormChange = (_: unknown, fields: AccessEditFormPowerDNSConfigModelType) => { - onModelChange?.(fields); + const handleFormChange = (_: unknown, values: z.infer) => { + onModelChange?.(values as AccessEditFormPowerDNSConfigModelValues); }; return ( - + ; +type AccessEditFormQiniuConfigModelValues = Partial; export type AccessEditFormQiniuConfigProps = { form: FormInstance; formName: string; disabled?: boolean; - model?: AccessEditFormQiniuConfigModelType; - onModelChange?: (model: AccessEditFormQiniuConfigModelType) => void; + model?: AccessEditFormQiniuConfigModelValues; + onModelChange?: (model: AccessEditFormQiniuConfigModelValues) => void; }; -const initModel = () => { +const initFormModel = (): AccessEditFormQiniuConfigModelValues => { return { accessKey: "", secretKey: "", - } as AccessEditFormQiniuConfigModelType; + }; }; const AccessEditFormQiniuConfig = ({ form, formName, disabled, model, onModelChange }: AccessEditFormQiniuConfigProps) => { @@ -40,18 +39,17 @@ const AccessEditFormQiniuConfig = ({ form, formName, disabled, model, onModelCha .max(64, t("common.errmsg.string_max", { max: 64 })), }); const formRule = createSchemaFieldRule(formSchema); + const { form: formInst, formProps } = useAntdForm>({ + form: form, + initialValues: model ?? initFormModel(), + }); - const [initialValues, setInitialValues] = useState>>(model ?? initModel()); - useDeepCompareEffect(() => { - setInitialValues(model ?? initModel()); - }, [model]); - - const handleFormChange = (_: unknown, fields: AccessEditFormQiniuConfigModelType) => { - onModelChange?.(fields); + const handleFormChange = (_: unknown, values: z.infer) => { + onModelChange?.(values as AccessEditFormQiniuConfigModelValues); }; return ( - + ; +type AccessEditFormSSHConfigModelValues = Partial; export type AccessEditFormSSHConfigProps = { form: FormInstance; formName: string; disabled?: boolean; - model?: AccessEditFormSSHConfigModelType; - onModelChange?: (model: AccessEditFormSSHConfigModelType) => void; + model?: AccessEditFormSSHConfigModelValues; + onModelChange?: (model: AccessEditFormSSHConfigModelValues) => void; }; -const initModel = () => { +const initFormModel = (): AccessEditFormSSHConfigModelValues => { return { host: "127.0.0.1", port: 22, username: "root", - } as AccessEditFormSSHConfigModelType; + }; }; const AccessEditFormSSHConfig = ({ form, formName, disabled, model, onModelChange }: AccessEditFormSSHConfigProps) => { @@ -71,21 +72,21 @@ const AccessEditFormSSHConfig = ({ form, formName, disabled, model, onModelChang .and(z.string().refine((v) => !v || form.getFieldValue("key"), { message: t("access.form.ssh_key.placeholder") })), }); const formRule = createSchemaFieldRule(formSchema); - const formInst = form as FormInstance>; + const { form: formInst, formProps } = useAntdForm>({ + form: form, + initialValues: model ?? initFormModel(), + }); - const [initialValues, setInitialValues] = useState>>(model ?? initModel()); + const [keyFileList, setKeyFileList] = useState([]); useDeepCompareEffect(() => { - setInitialValues(model ?? initModel()); setKeyFileList(model?.key?.trim() ? [{ uid: "-1", name: "sshkey", status: "done" }] : []); }, [model]); - const [keyFileList, setKeyFileList] = useState([]); - - const handleFormChange = (_: unknown, fields: AccessEditFormSSHConfigModelType) => { - onModelChange?.(fields); + const handleFormChange = (_: unknown, values: z.infer) => { + onModelChange?.(values as AccessEditFormSSHConfigModelValues); }; - const handleUploadChange: UploadProps["onChange"] = async ({ file }) => { + const handleKeyFileChange: UploadProps["onChange"] = async ({ file }) => { if (file && file.status !== "removed") { formInst.setFieldValue("key", (await readFileContent(file.originFileObj ?? (file as unknown as File))).trim()); setKeyFileList([file]); @@ -98,7 +99,7 @@ const AccessEditFormSSHConfig = ({ form, formName, disabled, model, onModelChang }; return ( - +
@@ -138,7 +139,7 @@ const AccessEditFormSSHConfig = ({ form, formName, disabled, model, onModelChang }> - false} fileList={keyFileList} maxCount={1} onChange={handleUploadChange}> + false} fileList={keyFileList} maxCount={1} onChange={handleKeyFileChange}> diff --git a/ui/src/components/access/AccessEditFormTencentCloudConfig.tsx b/ui/src/components/access/AccessEditFormTencentCloudConfig.tsx index 01238439..3f2322d9 100644 --- a/ui/src/components/access/AccessEditFormTencentCloudConfig.tsx +++ b/ui/src/components/access/AccessEditFormTencentCloudConfig.tsx @@ -1,27 +1,26 @@ -import { useState } from "react"; import { useTranslation } from "react-i18next"; -import { useDeepCompareEffect } from "ahooks"; import { Form, Input, type FormInstance } from "antd"; import { createSchemaFieldRule } from "antd-zod"; import { z } from "zod"; +import { useAntdForm } from "@/hooks"; import { type TencentCloudAccessConfig } from "@/domain/access"; -type AccessEditFormTencentCloudConfigModelType = Partial; +type AccessEditFormTencentCloudConfigModelValues = Partial; export type AccessEditFormTencentCloudConfigProps = { form: FormInstance; formName: string; disabled?: boolean; - model?: AccessEditFormTencentCloudConfigModelType; - onModelChange?: (model: AccessEditFormTencentCloudConfigModelType) => void; + model?: AccessEditFormTencentCloudConfigModelValues; + onModelChange?: (model: AccessEditFormTencentCloudConfigModelValues) => void; }; -const initModel = () => { +const initFormModel = (): AccessEditFormTencentCloudConfigModelValues => { return { secretId: "", secretKey: "", - } as AccessEditFormTencentCloudConfigModelType; + }; }; const AccessEditFormTencentCloudConfig = ({ form, formName, disabled, model, onModelChange }: AccessEditFormTencentCloudConfigProps) => { @@ -40,18 +39,17 @@ const AccessEditFormTencentCloudConfig = ({ form, formName, disabled, model, onM .max(64, t("common.errmsg.string_max", { max: 64 })), }); const formRule = createSchemaFieldRule(formSchema); + const { form: formInst, formProps } = useAntdForm>({ + form: form, + initialValues: model ?? initFormModel(), + }); - const [initialValues, setInitialValues] = useState>>(model ?? initModel()); - useDeepCompareEffect(() => { - setInitialValues(model ?? initModel()); - }, [model]); - - const handleFormChange = (_: unknown, fields: AccessEditFormTencentCloudConfigModelType) => { - onModelChange?.(fields); + const handleFormChange = (_: unknown, values: z.infer) => { + onModelChange?.(values as AccessEditFormTencentCloudConfigModelValues); }; return ( - + ; +type AccessEditFormVolcEngineConfigModelValues = Partial; export type AccessEditFormVolcEngineConfigProps = { form: FormInstance; formName: string; disabled?: boolean; - model?: AccessEditFormVolcEngineConfigModelType; - onModelChange?: (model: AccessEditFormVolcEngineConfigModelType) => void; + model?: AccessEditFormVolcEngineConfigModelValues; + onModelChange?: (model: AccessEditFormVolcEngineConfigModelValues) => void; }; -const initModel = () => { +const initFormModel = (): AccessEditFormVolcEngineConfigModelValues => { return { accessKeyId: "", secretAccessKey: "", - } as AccessEditFormVolcEngineConfigModelType; + }; }; const AccessEditFormVolcEngineConfig = ({ form, formName, disabled, model, onModelChange }: AccessEditFormVolcEngineConfigProps) => { @@ -40,18 +39,17 @@ const AccessEditFormVolcEngineConfig = ({ form, formName, disabled, model, onMod .max(64, t("common.errmsg.string_max", { max: 64 })), }); const formRule = createSchemaFieldRule(formSchema); + const { form: formInst, formProps } = useAntdForm>({ + form: form, + initialValues: model ?? initFormModel(), + }); - const [initialValues, setInitialValues] = useState>>(model ?? initModel()); - useDeepCompareEffect(() => { - setInitialValues(model ?? initModel()); - }, [model]); - - const handleFormChange = (_: unknown, fields: AccessEditFormVolcEngineConfigModelType) => { - onModelChange?.(fields); + const handleFormChange = (_: unknown, values: z.infer) => { + onModelChange?.(values as AccessEditFormVolcEngineConfigModelValues); }; return ( - + ; +type AccessEditFormWebhookConfigModelValues = Partial; export type AccessEditFormWebhookConfigProps = { form: FormInstance; formName: string; disabled?: boolean; - model?: AccessEditFormWebhookConfigModelType; - onModelChange?: (model: AccessEditFormWebhookConfigModelType) => void; + model?: AccessEditFormWebhookConfigModelValues; + onModelChange?: (model: AccessEditFormWebhookConfigModelValues) => void; }; -const initModel = () => { +const initFormModel = (): AccessEditFormWebhookConfigModelValues => { return { url: "", - } as AccessEditFormWebhookConfigModelType; + }; }; const AccessEditFormWebhookConfig = ({ form, formName, disabled, model, onModelChange }: AccessEditFormWebhookConfigProps) => { @@ -32,18 +32,17 @@ const AccessEditFormWebhookConfig = ({ form, formName, disabled, model, onModelC .url({ message: t("common.errmsg.url_invalid") }), }); const formRule = createSchemaFieldRule(formSchema); + const { form: formInst, formProps } = useAntdForm>({ + form: form, + initialValues: model ?? initFormModel(), + }); - const [initialValues, setInitialValues] = useState>>(model ?? initModel()); - useEffect(() => { - setInitialValues(model ?? initModel()); - }, [model]); - - const handleFormChange = (_: unknown, fields: AccessEditFormWebhookConfigModelType) => { - onModelChange?.(fields); + const handleFormChange = (_: unknown, values: z.infer) => { + onModelChange?.(values as AccessEditFormWebhookConfigModelValues); }; return ( - + diff --git a/ui/src/components/access/AccessEditModal.tsx b/ui/src/components/access/AccessEditModal.tsx index f2d24c9e..38c10831 100644 --- a/ui/src/components/access/AccessEditModal.tsx +++ b/ui/src/components/access/AccessEditModal.tsx @@ -53,7 +53,7 @@ const AccessEditModal = ({ data, loading, trigger, preset, ...props }: AccessEdi await formRef.current!.validateFields(); } catch (err) { setFormPending(false); - return Promise.reject(); + return Promise.reject(err); } try { @@ -74,8 +74,6 @@ const AccessEditModal = ({ data, loading, trigger, preset, ...props }: AccessEdi setOpen(false); } catch (err) { notificationApi.error({ message: t("common.text.request_error"), description: getErrMsg(err) }); - - throw err; } finally { setFormPending(false); } diff --git a/ui/src/components/ui/MultipleInput.tsx b/ui/src/components/core/MultipleInput.tsx similarity index 100% rename from ui/src/components/ui/MultipleInput.tsx rename to ui/src/components/core/MultipleInput.tsx diff --git a/ui/src/components/ui/Version.tsx b/ui/src/components/core/Version.tsx similarity index 100% rename from ui/src/components/ui/Version.tsx rename to ui/src/components/core/Version.tsx diff --git a/ui/src/components/notification/NotifyChannelEditForm.tsx b/ui/src/components/notification/NotifyChannelEditForm.tsx index 6b7f1ddc..a5e87324 100644 --- a/ui/src/components/notification/NotifyChannelEditForm.tsx +++ b/ui/src/components/notification/NotifyChannelEditForm.tsx @@ -1,7 +1,8 @@ -import { forwardRef, useImperativeHandle, useMemo, useState } from "react"; -import { useCreation, useDeepCompareEffect } from "ahooks"; +import { forwardRef, useImperativeHandle, useMemo } from "react"; +import { useCreation } from "ahooks"; import { Form } from "antd"; +import { useAntdForm } from "@/hooks"; import { NOTIFY_CHANNELS, type NotifyChannelsSettingsContent } from "@/domain/settings"; import NotifyChannelEditFormBarkFields from "./NotifyChannelEditFormBarkFields"; import NotifyChannelEditFormDingTalkFields from "./NotifyChannelEditFormDingTalkFields"; @@ -12,26 +13,28 @@ import NotifyChannelEditFormTelegramFields from "./NotifyChannelEditFormTelegram import NotifyChannelEditFormWebhookFields from "./NotifyChannelEditFormWebhookFields"; import NotifyChannelEditFormWeComFields from "./NotifyChannelEditFormWeComFields"; -type NotifyChannelEditFormModelType = NotifyChannelsSettingsContent[keyof NotifyChannelsSettingsContent]; +type NotifyChannelEditFormModelValues = NotifyChannelsSettingsContent[keyof NotifyChannelsSettingsContent]; export type NotifyChannelEditFormProps = { className?: string; style?: React.CSSProperties; channel: string; disabled?: boolean; - model?: NotifyChannelEditFormModelType; - onModelChange?: (model: NotifyChannelEditFormModelType) => void; + model?: NotifyChannelEditFormModelValues; + onModelChange?: (model: NotifyChannelEditFormModelValues) => void; }; export type NotifyChannelEditFormInstance = { - getFieldsValue: () => NotifyChannelEditFormModelType; + getFieldsValue: () => NotifyChannelEditFormModelValues; resetFields: () => void; - validateFields: () => Promise; + validateFields: () => Promise; }; const NotifyChannelEditForm = forwardRef( ({ className, style, channel, disabled, model, onModelChange }, ref) => { - const [form] = Form.useForm(); + const { form: formInst, formProps } = useAntdForm({ + initialValues: model, + }); const formName = useCreation(() => `notifyChannelEditForm_${Math.random().toString(36).substring(2, 10)}${new Date().getTime()}`, []); const formFieldsComponent = useMemo(() => { /* @@ -58,36 +61,31 @@ const NotifyChannelEditForm = forwardRef { - setInitialValues(model); - }, [model]); - - const handleFormChange = (_: unknown, fields: NotifyChannelEditFormModelType) => { - onModelChange?.(fields); + const handleFormChange = (_: unknown, values: NotifyChannelEditFormModelValues) => { + onModelChange?.(values); }; useImperativeHandle(ref, () => ({ getFieldsValue: () => { - return form.getFieldsValue(true); + return formInst.getFieldsValue(true); }, resetFields: () => { - return form.resetFields(); + return formInst.resetFields(); }, validateFields: () => { - return form.validateFields(); + return formInst.validateFields(); }, })); return ( {formFieldsComponent} diff --git a/ui/src/components/notification/NotifyChannelEditFormEmailFields.tsx b/ui/src/components/notification/NotifyChannelEditFormEmailFields.tsx index 2d043d0c..2b54526a 100644 --- a/ui/src/components/notification/NotifyChannelEditFormEmailFields.tsx +++ b/ui/src/components/notification/NotifyChannelEditFormEmailFields.tsx @@ -56,13 +56,13 @@ const NotifyChannelEditFormEmailFields = () => {
- +
- +
diff --git a/ui/src/components/notification/NotifyChannels.tsx b/ui/src/components/notification/NotifyChannels.tsx index d382e207..2d1b33e3 100644 --- a/ui/src/components/notification/NotifyChannels.tsx +++ b/ui/src/components/notification/NotifyChannels.tsx @@ -25,10 +25,10 @@ const NotifyChannel = ({ className, style, channel }: NotifyChannelProps) => { const { channels, setChannel } = useNotifyChannelStore(); const channelConfig = useDeepCompareMemo(() => channels[channel], [channels, channel]); - const [channelFormChanged, setChannelFormChanged] = useState(false); const channelFormRef = useRef(null); + const [channelFormChanged, setChannelFormChanged] = useState(false); - const handleClickSubmit = async () => { + const handleSubmit = async () => { await channelFormRef.current!.validateFields(); try { @@ -49,7 +49,7 @@ const NotifyChannel = ({ className, style, channel }: NotifyChannelProps) => { setChannelFormChanged(true)} /> - diff --git a/ui/src/components/notification/NotifyTemplate.tsx b/ui/src/components/notification/NotifyTemplate.tsx index 506743ce..8ebde44b 100644 --- a/ui/src/components/notification/NotifyTemplate.tsx +++ b/ui/src/components/notification/NotifyTemplate.tsx @@ -7,6 +7,7 @@ import { z } from "zod"; import { ClientResponseError } from "pocketbase"; import Show from "@/components/Show"; +import { useAntdForm } from "@/hooks"; import { defaultNotifyTemplate, SETTINGS_NAMES, type NotifyTemplatesSettingsContent } from "@/domain/settings"; import { get as getSettings, save as saveSettings } from "@/repository/settings"; import { getErrMsg } from "@/utils/error"; @@ -35,11 +36,29 @@ const NotifyTemplateForm = ({ className, style }: NotifyTemplateFormProps) => { .max(1000, t("common.errmsg.string_max", { max: 1000 })), }); const formRule = createSchemaFieldRule(formSchema); - const [form] = Form.useForm>(); - const [formPending, setFormPending] = useState(false); + const { + form: formInst, + formPending, + formProps, + } = useAntdForm>({ + initialValues: defaultNotifyTemplate, + onSubmit: async (values) => { + try { + const settings = await getSettings(SETTINGS_NAMES.NOTIFY_TEMPLATES); + await saveSettings({ + ...settings, + content: { + notifyTemplates: [values], + }, + }); - const [initialValues, setInitialValues] = useState>>(); - const [initialChanged, setInitialChanged] = useState(false); + messageApi.success(t("common.text.operation_succeeded")); + } catch (err) { + notificationApi.error({ message: t("common.text.request_error"), description: getErrMsg(err) }); + } + }, + }); + const [formChanged, setFormChanged] = useState(false); const { loading } = useRequest( () => { @@ -55,33 +74,13 @@ const NotifyTemplateForm = ({ className, style }: NotifyTemplateFormProps) => { }, onFinally: (_, resp) => { const template = resp?.content?.notifyTemplates?.[0] ?? defaultNotifyTemplate; - setInitialValues(template); + formInst.setFieldsValue(template); }, } ); const handleInputChange = () => { - setInitialChanged(true); - }; - - const handleFormFinish = async (fields: z.infer) => { - setFormPending(true); - - try { - const settings = await getSettings(SETTINGS_NAMES.NOTIFY_TEMPLATES); - await saveSettings({ - ...settings, - content: { - notifyTemplates: [fields], - }, - }); - - messageApi.success(t("common.text.operation_succeeded")); - } catch (err) { - notificationApi.error({ message: t("common.text.request_error"), description: getErrMsg(err) }); - } finally { - setFormPending(false); - } + setFormChanged(true); }; return ( @@ -90,11 +89,11 @@ const NotifyTemplateForm = ({ className, style }: NotifyTemplateFormProps) => { {NotificationContextHolder} }> - + @@ -103,7 +102,7 @@ const NotifyTemplateForm = ({ className, style }: NotifyTemplateFormProps) => { { - diff --git a/ui/src/components/workflow/ApplyForm.tsx b/ui/src/components/workflow/ApplyForm.tsx index 399ea33a..fd3ec882 100644 --- a/ui/src/components/workflow/ApplyForm.tsx +++ b/ui/src/components/workflow/ApplyForm.tsx @@ -1,7 +1,7 @@ import { memo, useEffect } from "react"; import { useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; -import { Collapse, Switch, Tooltip } from "antd"; +import { Collapse, Divider, Switch, Tooltip, Typography } from "antd"; import z from "zod"; import { zodResolver } from "@hookform/resolvers/zod"; import { ChevronsUpDown as ChevronsUpDownIcon, Plus as PlusIcon, CircleHelp as CircleHelpIcon } from "lucide-react"; @@ -204,146 +204,145 @@ const ApplyForm = ({ data }: ApplyFormProps) => { )} /> -
-
- {t("domain.application.form.advanced_settings.label")}, - children: ( -
- {/* 证书算法 */} - ( - - {t("domain.application.form.key_algorithm.label")} - { + form.setValue("keyAlgorithm", value); + }} + > + + + + + + RSA2048 + RSA3072 + RSA4096 + RSA8192 + EC256 + EC384 + + + + + )} + /> + + {/* DNS */} + ( + + { + form.setValue("nameservers", val); + }} + valueType="dns" + > + + + + )} + /> + + {/* DNS 超时时间 */} + ( + + {t("domain.application.form.timeout.label")} + + { - form.setValue("keyAlgorithm", value); + onChange={(e) => { + form.setValue("timeout", parseInt(e.target.value)); }} - > - - - - - - RSA2048 - RSA3072 - RSA4096 - RSA8192 - EC256 - EC384 - - - - - )} - /> + /> + - {/* DNS */} - ( - - { - form.setValue("nameservers", val); - }} - valueType="dns" - > + + + )} + /> - - - )} - /> - - {/* DNS 超时时间 */} - ( - - {t("domain.application.form.timeout.label")} - - { - form.setValue("timeout", parseInt(e.target.value)); + {/* 禁用 CNAME 跟随 */} + ( + + +
+ {t("domain.application.form.disable_follow_cname.label")} + + {t("domain.application.form.disable_follow_cname.tips")} + + {t("domain.application.form.disable_follow_cname.tips_link")} + +

+ } + > + +
+
+
+ +
+ { + form.setValue(field.name, value); }} /> - - - - - )} - /> - - {/* 禁用 CNAME 跟随 */} - ( - - -
- {t("domain.application.form.disable_follow_cname.label")} - - {t("domain.application.form.disable_follow_cname.tips")} - - {t("domain.application.form.disable_follow_cname.tips_link")} - -

- } - > - -
-
-
- -
- { - form.setValue(field.name, value); - }} - /> -
-
- -
- )} - /> -
- ), - extra: , - forceRender: true, - showArrow: false, - }, - ]} - /> -
+
+ + + + )} + /> +
+ ), + extra: , + forceRender: true, + showArrow: false, + }, + ]} + />
diff --git a/ui/src/components/workflow/Panel.tsx b/ui/src/components/workflow/Panel.tsx index ac6e20c6..c603e8c0 100644 --- a/ui/src/components/workflow/Panel.tsx +++ b/ui/src/components/workflow/Panel.tsx @@ -13,9 +13,9 @@ const Panel = ({ open, onOpenChange, children, name }: AddNodePanelProps) => { return ( - {name} + {name} - {children} + {children} ); diff --git a/ui/src/components/workflow/node/StartNodeForm.tsx b/ui/src/components/workflow/node/StartNodeForm.tsx index 1c0e7bb9..80bdfad1 100644 --- a/ui/src/components/workflow/node/StartNodeForm.tsx +++ b/ui/src/components/workflow/node/StartNodeForm.tsx @@ -16,7 +16,7 @@ export type StartNodeFormProps = { data: WorkflowNode; }; -const initModel = () => { +const initFormModel = () => { return { executionMethod: "auto", crontab: "0 0 * * *", @@ -51,9 +51,11 @@ const StartNodeForm = ({ data }: StartNodeFormProps) => { const [form] = Form.useForm>(); const [formPending, setFormPending] = useState(false); - const [initialValues, setInitialValues] = useState>>((data?.config as Partial>) ?? initModel()); + const [initialValues, setInitialValues] = useState>>( + (data?.config as Partial>) ?? initFormModel() + ); useDeepCompareEffect(() => { - setInitialValues((data?.config as Partial>) ?? initModel()); + setInitialValues((data?.config as Partial>) ?? initFormModel()); }, [data?.config]); const [triggerType, setTriggerType] = useState(data?.config?.executionMethod); @@ -67,7 +69,7 @@ const StartNodeForm = ({ data }: StartNodeFormProps) => { setTriggerType(value); if (value === "auto") { - form.setFieldValue("crontab", form.getFieldValue("crontab") || initModel().crontab); + form.setFieldValue("crontab", form.getFieldValue("crontab") || initFormModel().crontab); } }; @@ -75,11 +77,11 @@ const StartNodeForm = ({ data }: StartNodeFormProps) => { setTriggerCronExecutions(getNextCronExecutions(value, 5)); }; - const handleFormFinish = async (fields: z.infer) => { + const handleFormFinish = async (values: z.infer) => { setFormPending(true); try { - await updateNode({ ...data, config: { ...fields }, validated: true }); + await updateNode({ ...data, config: { ...values }, validated: true }); hidePanel(); } finally { diff --git a/ui/src/hooks/index.ts b/ui/src/hooks/index.ts index 69e1b33c..6aa77f23 100644 --- a/ui/src/hooks/index.ts +++ b/ui/src/hooks/index.ts @@ -1,4 +1,5 @@ -import useBrowserTheme from "./useBrowserTheme"; +import useAntdForm from "./useAntdForm"; +import useBrowserTheme from "./useBrowserTheme"; import useZustandShallowSelector from "./useZustandShallowSelector"; -export { useBrowserTheme, useZustandShallowSelector }; +export { useAntdForm, useBrowserTheme, useZustandShallowSelector }; diff --git a/ui/src/hooks/useAntdForm.ts b/ui/src/hooks/useAntdForm.ts new file mode 100644 index 00000000..a87dd284 --- /dev/null +++ b/ui/src/hooks/useAntdForm.ts @@ -0,0 +1,106 @@ +import { useState } from "react"; +import { Form, type FormInstance, type FormProps } from "antd"; +import { useDeepCompareEffect } from "ahooks"; + +export interface UseAntdFormOptions = any> { + form?: FormInstance; + initialValues?: Partial | (() => Partial | Promise>); + onSubmit?: (values: T) => void | Promise; +} + +export interface UseAntdFormReturns = any> { + form: FormInstance; + formProps: Omit, "children">; + formPending: boolean; + submit: (values?: T) => Promise; +} + +/** + * + * @param {UseAntdFormOptions} options + * @returns {UseAntdFormReturns} + */ +const useAntdForm = = any>({ initialValues, form, onSubmit }: UseAntdFormOptions): UseAntdFormReturns => { + const formInst = form ?? Form["useForm"]()[0]; + const [formInitialValues, setFormInitialValues] = useState>(); + const [formPending, setFormPending] = useState(false); + + useDeepCompareEffect(() => { + let unmounted = false; + + if (!initialValues) { + return; + } + + let temp: Promise>; + if (typeof initialValues === "function") { + temp = Promise.resolve(initialValues()); + } else { + temp = Promise.resolve(initialValues); + } + + temp.then((temp) => { + if (!unmounted) { + type FieldName = Parameters["getFieldValue"]>[0]; + type FieldsValue = Parameters["setFieldsValue"]>[0]; + + const obj = { ...temp }; + Object.keys(temp).forEach((key) => { + obj[key as keyof T] = formInst!.isFieldTouched(key as FieldName) ? formInst!.getFieldValue(key as FieldName) : temp[key as keyof T]; + }); + + setFormInitialValues(temp); + formInst!.setFieldsValue(obj as FieldsValue); + } + }); + + return () => { + unmounted = true; + }; + }, [formInst, initialValues]); + + const onFinish = (values: T) => { + if (formPending) return Promise.reject(new Error("Form is pending")); + + setFormPending(true); + + return new Promise((resolve, reject) => { + formInst + .validateFields() + .then(() => { + resolve( + Promise.resolve(onSubmit?.(values)) + .then((data) => { + setFormPending(false); + return data; + }) + .catch((err) => { + setFormPending(false); + throw err; + }) + ); + }) + .catch((err) => { + setFormPending(false); + reject(err); + }); + }); + }; + + const formProps: FormProps = { + form: formInst, + initialValues: formInitialValues, + onFinish, + }; + + return { + form: formInst, + formProps: formProps, + formPending: formPending, + submit: () => { + return onFinish(formInst.getFieldsValue(true)); + }, + }; +}; + +export default useAntdForm; diff --git a/ui/src/hooks/useBrowserTheme.ts b/ui/src/hooks/useBrowserTheme.ts index 09b83d59..d23e49a4 100644 --- a/ui/src/hooks/useBrowserTheme.ts +++ b/ui/src/hooks/useBrowserTheme.ts @@ -1,5 +1,13 @@ import { useTheme } from "ahooks"; -export default function () { +export type UseBrowserThemeReturns = ReturnType; + +/** + * 获取并设置当前浏览器系统主题。 + * @returns {UseBrowserThemeReturns} + */ +const useBrowserTheme = (): UseBrowserThemeReturns => { return useTheme({ localStorageKey: "certimate-ui-theme" }); -} +}; + +export default useBrowserTheme; diff --git a/ui/src/hooks/useZustandShallowSelector.ts b/ui/src/hooks/useZustandShallowSelector.ts index 92bf83ca..590a3eec 100644 --- a/ui/src/hooks/useZustandShallowSelector.ts +++ b/ui/src/hooks/useZustandShallowSelector.ts @@ -5,7 +5,28 @@ import { shallow } from "zustand/shallow"; type MaybeMany = T | readonly T[]; -export default function (paths: MaybeMany): (state: T) => Pick { +export type UseZustandShallowSelectorReturns = (state: T) => Pick; + +/** + * 选择并获取指定的状态。 + * 基于 `zustand.useShallow` 二次封装,以减少样板代码。 + * @param {Array} paths 要选择的状态键名。 + * @returns {UseZustandShallowSelectorReturns} + * + * @example + * ```js + * // 使用示例: + * const { foo, bar, baz } = useStore(useZustandShallowSelector(["foo", "bar", "baz"])); + * + * // 以上代码等效于: + * const { foo, bar, baz } = useStore((state) => ({ + * foo: state.foo, + * bar: state.bar, + * baz: state.baz, + * })); + * ``` + */ +const useZustandShallowSelector = (paths: MaybeMany): UseZustandShallowSelectorReturns => { const prev = useRef>({} as Pick); return (state: T) => { @@ -15,4 +36,6 @@ export default function (paths: MaybeMa } return prev.current; }; -} +}; + +export default useZustandShallowSelector; diff --git a/ui/src/i18n/locales/en/nls.settings.json b/ui/src/i18n/locales/en/nls.settings.json index 0bf5ea24..d1c7cfe7 100644 --- a/ui/src/i18n/locales/en/nls.settings.json +++ b/ui/src/i18n/locales/en/nls.settings.json @@ -19,10 +19,10 @@ "settings.notification.template.card.title": "Template", "settings.notification.template.form.subject.label": "Subject", "settings.notification.template.form.subject.placeholder": "Please enter notification subject", - "settings.notification.template.form.subject.tooltip": "Optional variables (${COUNT}: number of expiring soon)", + "settings.notification.template.form.subject.extra": "Optional variables (${COUNT}: number of expiring soon)", "settings.notification.template.form.message.label": "Message", "settings.notification.template.form.message.placeholder": "Please enter notification message", - "settings.notification.template.form.message.tooltip": "Optional variables (${COUNT}: number of expiring soon. ${DOMAINS}: Domain list)", + "settings.notification.template.form.message.extra": "Optional variables (${COUNT}: number of expiring soon. ${DOMAINS}: Domain list)", "settings.notification.channels.card.title": "Channels", "settings.notification.channel.enabled.on": "On", "settings.notification.channel.enabled.off": "Off", diff --git a/ui/src/i18n/locales/zh/nls.settings.json b/ui/src/i18n/locales/zh/nls.settings.json index a57d621f..601188a0 100644 --- a/ui/src/i18n/locales/zh/nls.settings.json +++ b/ui/src/i18n/locales/zh/nls.settings.json @@ -19,10 +19,10 @@ "settings.notification.template.card.title": "通知模板", "settings.notification.template.form.subject.label": "通知主题", "settings.notification.template.form.subject.placeholder": "请输入通知主题", - "settings.notification.template.form.subject.tooltip": "可选的变量(${COUNT}: 即将过期张数)", + "settings.notification.template.form.subject.extra": "可选的变量(${COUNT}: 即将过期张数)", "settings.notification.template.form.message.label": "通知内容", "settings.notification.template.form.message.placeholder": "请输入通知内容", - "settings.notification.template.form.message.tooltip": "可选的变量(${COUNT}: 即将过期张数;${DOMAINS}: 域名列表)", + "settings.notification.template.form.message.extra": "可选的变量(${COUNT}: 即将过期张数;${DOMAINS}: 域名列表)", "settings.notification.channels.card.title": "通知渠道", "settings.notification.channel.enabled.on": "启用", "settings.notification.channel.enabled.off": "未启用", diff --git a/ui/src/pages/AuthLayout.tsx b/ui/src/pages/AuthLayout.tsx index 84a009dd..10f84c38 100644 --- a/ui/src/pages/AuthLayout.tsx +++ b/ui/src/pages/AuthLayout.tsx @@ -1,6 +1,6 @@ import { Navigate, Outlet } from "react-router-dom"; -import Version from "@/components/ui/Version"; +import Version from "@/components/core/Version"; import { getPocketBase } from "@/repository/pocketbase"; const AuthLayout = () => { diff --git a/ui/src/pages/ConsoleLayout.tsx b/ui/src/pages/ConsoleLayout.tsx index 93595a05..f59f47eb 100644 --- a/ui/src/pages/ConsoleLayout.tsx +++ b/ui/src/pages/ConsoleLayout.tsx @@ -15,7 +15,7 @@ import { Workflow as WorkflowIcon, } from "lucide-react"; -import Version from "@/components/ui/Version"; +import Version from "@/components/core/Version"; import { useBrowserTheme } from "@/hooks"; import { getPocketBase } from "@/repository/pocketbase"; diff --git a/ui/src/pages/login/Login.tsx b/ui/src/pages/login/Login.tsx index ed40fc84..c6e885ff 100644 --- a/ui/src/pages/login/Login.tsx +++ b/ui/src/pages/login/Login.tsx @@ -1,10 +1,10 @@ -import { useState } from "react"; import { useNavigate } from "react-router-dom"; import { useTranslation } from "react-i18next"; import { Button, Card, Form, Input, notification } from "antd"; import { createSchemaFieldRule } from "antd-zod"; import { z } from "zod"; +import { useAntdForm } from "@/hooks"; import { getPocketBase } from "@/repository/pocketbase"; import { getErrMsg } from "@/utils/error"; @@ -20,21 +20,20 @@ const Login = () => { password: z.string().min(10, t("login.password.errmsg.invalid")), }); const formRule = createSchemaFieldRule(formSchema); - const [form] = Form.useForm>(); - const [formPending, setFormPending] = useState(false); - - const handleFormFinish = async (fields: z.infer) => { - setFormPending(true); - - try { - await getPocketBase().admins.authWithPassword(fields.username, fields.password); - navigage("/"); - } catch (err) { - notificationApi.error({ message: t("common.text.request_error"), description: getErrMsg(err) }); - } finally { - setFormPending(false); - } - }; + const { + form: formInst, + formPending, + formProps, + } = useAntdForm>({ + onSubmit: async (values) => { + try { + await getPocketBase().admins.authWithPassword(values.username, values.password); + navigage("/"); + } catch (err) { + notificationApi.error({ message: t("common.text.request_error"), description: getErrMsg(err) }); + } + }, + }); return ( <> @@ -45,7 +44,7 @@ const Login = () => {
- + diff --git a/ui/src/pages/settings/SettingsAccount.tsx b/ui/src/pages/settings/SettingsAccount.tsx index 3a2a8ce0..afbe4719 100644 --- a/ui/src/pages/settings/SettingsAccount.tsx +++ b/ui/src/pages/settings/SettingsAccount.tsx @@ -5,8 +5,9 @@ import { Button, Form, Input, message, notification } from "antd"; import { createSchemaFieldRule } from "antd-zod"; import { z } from "zod"; -import { getErrMsg } from "@/utils/error"; +import { useAntdForm } from "@/hooks"; import { getPocketBase } from "@/repository/pocketbase"; +import { getErrMsg } from "@/utils/error"; const SettingsAccount = () => { const navigate = useNavigate(); @@ -20,37 +21,35 @@ const SettingsAccount = () => { username: z.string({ message: "settings.account.form.email.placeholder" }).email({ message: t("common.errmsg.email_invalid") }), }); const formRule = createSchemaFieldRule(formSchema); - const [form] = Form.useForm>(); - const [formPending, setFormPending] = useState(false); + const { + form: formInst, + formPending, + formProps, + } = useAntdForm>({ + initialValues: { + username: getPocketBase().authStore.model?.email, + }, + onSubmit: async (values) => { + try { + await getPocketBase().admins.update(getPocketBase().authStore.model?.id, { + email: values.username, + }); - const [initialValues] = useState>>({ - username: getPocketBase().authStore.model?.email, + messageApi.success(t("common.text.operation_succeeded")); + + setTimeout(() => { + getPocketBase().authStore.clear(); + navigate("/login"); + }, 500); + } catch (err) { + notificationApi.error({ message: t("common.text.request_error"), description: getErrMsg(err) }); + } + }, }); - const [initialChanged, setInitialChanged] = useState(false); + const [formChanged, setFormChanged] = useState(false); const handleInputChange = () => { - setInitialChanged(form.getFieldValue("username") !== initialValues.username); - }; - - const handleFormFinish = async (fields: z.infer) => { - setFormPending(true); - - try { - await getPocketBase().admins.update(getPocketBase().authStore.model?.id, { - email: fields.username, - }); - - messageApi.success(t("common.text.operation_succeeded")); - - setTimeout(() => { - getPocketBase().authStore.clear(); - navigate("/login"); - }, 500); - } catch (err) { - notificationApi.error({ message: t("common.text.request_error"), description: getErrMsg(err) }); - } finally { - setFormPending(false); - } + setFormChanged(formInst.getFieldValue("username") !== formProps.initialValues?.username); }; return ( @@ -59,13 +58,13 @@ const SettingsAccount = () => { {NotificationContextHolder}
- + - diff --git a/ui/src/pages/settings/SettingsPassword.tsx b/ui/src/pages/settings/SettingsPassword.tsx index 39dfa84b..234e7b5d 100644 --- a/ui/src/pages/settings/SettingsPassword.tsx +++ b/ui/src/pages/settings/SettingsPassword.tsx @@ -5,8 +5,9 @@ import { Button, Form, Input, message, notification } from "antd"; import { createSchemaFieldRule } from "antd-zod"; import { z } from "zod"; -import { getErrMsg } from "@/utils/error"; +import { useAntdForm } from "@/hooks"; import { getPocketBase } from "@/repository/pocketbase"; +import { getErrMsg } from "@/utils/error"; const SettingsPassword = () => { const navigate = useNavigate(); @@ -33,37 +34,36 @@ const SettingsPassword = () => { path: ["confirmPassword"], }); const formRule = createSchemaFieldRule(formSchema); - const [form] = Form.useForm>(); - const [formPending, setFormPending] = useState(false); + const { + form: formInst, + formPending, + formProps, + } = useAntdForm>({ + onSubmit: async (values) => { + try { + await getPocketBase().admins.authWithPassword(getPocketBase().authStore.model?.email, values.oldPassword); + await getPocketBase().admins.update(getPocketBase().authStore.model?.id, { + password: values.newPassword, + passwordConfirm: values.confirmPassword, + }); - const [initialChanged, setInitialChanged] = useState(false); + messageApi.success(t("common.text.operation_succeeded")); + + setTimeout(() => { + getPocketBase().authStore.clear(); + navigate("/login"); + }, 500); + } catch (err) { + notificationApi.error({ message: t("common.text.request_error"), description: getErrMsg(err) }); + } + }, + }); + + const [formChanged, setFormChanged] = useState(false); const handleInputChange = () => { - const fields = form.getFieldsValue(); - setInitialChanged(!!fields.oldPassword && !!fields.newPassword && !!fields.confirmPassword); - }; - - const handleFormFinish = async (fields: z.infer) => { - setFormPending(true); - - try { - await getPocketBase().admins.authWithPassword(getPocketBase().authStore.model?.email, fields.oldPassword); - await getPocketBase().admins.update(getPocketBase().authStore.model?.id, { - password: fields.newPassword, - passwordConfirm: fields.confirmPassword, - }); - - messageApi.success(t("common.text.operation_succeeded")); - - setTimeout(() => { - getPocketBase().authStore.clear(); - navigate("/login"); - }, 500); - } catch (err) { - notificationApi.error({ message: t("common.text.request_error"), description: getErrMsg(err) }); - } finally { - setFormPending(false); - } + const values = formInst.getFieldsValue(); + setFormChanged(!!values.oldPassword && !!values.newPassword && !!values.confirmPassword); }; return ( @@ -72,7 +72,7 @@ const SettingsPassword = () => { {NotificationContextHolder}
- + @@ -86,7 +86,7 @@ const SettingsPassword = () => { - diff --git a/ui/src/pages/settings/SettingsSSLProvider.tsx b/ui/src/pages/settings/SettingsSSLProvider.tsx index d0ce1110..a4cfed09 100644 --- a/ui/src/pages/settings/SettingsSSLProvider.tsx +++ b/ui/src/pages/settings/SettingsSSLProvider.tsx @@ -1,15 +1,17 @@ import { createContext, useContext, useEffect, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; +import { useDeepCompareEffect } from "ahooks"; import { Button, Form, Input, message, notification, Skeleton } from "antd"; import { CheckCard } from "@ant-design/pro-components"; import { createSchemaFieldRule } from "antd-zod"; import { produce } from "immer"; import { z } from "zod"; +import Show from "@/components/Show"; +import { useAntdForm } from "@/hooks"; import { SETTINGS_NAMES, SSLPROVIDERS, type SettingsModel, type SSLProviderSettingsContent, type SSLProviders } from "@/domain/settings"; import { get as getSettings, save as saveSettings } from "@/repository/settings"; import { getErrMsg } from "@/utils/error"; -import { useDeepCompareEffect } from "ahooks"; const SSLProviderContext = createContext( {} as { @@ -24,36 +26,35 @@ const SSLProviderEditFormLetsEncryptConfig = () => { const { pending, settings, updateSettings } = useContext(SSLProviderContext); - const [form] = Form.useForm>(); + const { form: formInst, formProps } = useAntdForm>({ + initialValues: settings?.content?.config?.[SSLPROVIDERS.LETS_ENCRYPT], + onSubmit: async (values) => { + const newSettings = produce(settings, (draft) => { + draft.content ??= {} as SSLProviderSettingsContent; + draft.content.provider = SSLPROVIDERS.LETS_ENCRYPT; - const [initialValues, setInitialValues] = useState(settings?.content?.config?.[SSLPROVIDERS.LETS_ENCRYPT]); - const [initialChanged, setInitialChanged] = useState(false); + draft.content.config ??= {} as SSLProviderSettingsContent["config"]; + draft.content.config[SSLPROVIDERS.LETS_ENCRYPT] = values; + }); + await updateSettings(newSettings); + + setFormChanged(false); + }, + }); + + const [formChanged, setFormChanged] = useState(false); useDeepCompareEffect(() => { - setInitialValues(settings?.content?.config?.[SSLPROVIDERS.LETS_ENCRYPT]); - setInitialChanged(settings?.content?.provider !== SSLPROVIDERS.LETS_ENCRYPT); + setFormChanged(settings?.content?.provider !== SSLPROVIDERS.LETS_ENCRYPT); }, [settings]); const handleFormChange = () => { - setInitialChanged(true); - }; - - const handleFormFinish = async (fields: NonNullable) => { - const newSettings = produce(settings, (draft) => { - draft.content ??= {} as SSLProviderSettingsContent; - draft.content.provider = SSLPROVIDERS.LETS_ENCRYPT; - - draft.content.config ??= {} as SSLProviderSettingsContent["config"]; - draft.content.config[SSLPROVIDERS.LETS_ENCRYPT] = fields; - }); - await updateSettings(newSettings); - - setInitialChanged(false); + setFormChanged(true); }; return ( - + - @@ -77,34 +78,33 @@ const SSLProviderEditFormZeroSSLConfig = () => { .max(256, t("common.errmsg.string_max", { max: 256 })), }); const formRule = createSchemaFieldRule(formSchema); - const [form] = Form.useForm>(); + const { form: formInst, formProps } = useAntdForm>({ + initialValues: settings?.content?.config?.[SSLPROVIDERS.ZERO_SSL], + onSubmit: async (values) => { + const newSettings = produce(settings, (draft) => { + draft.content ??= {} as SSLProviderSettingsContent; + draft.content.provider = SSLPROVIDERS.ZERO_SSL; - const [initialValues, setInitialValues] = useState(settings?.content?.config?.[SSLPROVIDERS.ZERO_SSL]); - const [initialChanged, setInitialChanged] = useState(false); + draft.content.config ??= {} as SSLProviderSettingsContent["config"]; + draft.content.config[SSLPROVIDERS.ZERO_SSL] = values; + }); + await updateSettings(newSettings); + + setFormChanged(false); + }, + }); + + const [formChanged, setFormChanged] = useState(false); useDeepCompareEffect(() => { - setInitialValues(settings?.content?.config?.[SSLPROVIDERS.ZERO_SSL]); - setInitialChanged(settings?.content?.provider !== SSLPROVIDERS.ZERO_SSL); + setFormChanged(settings?.content?.provider !== SSLPROVIDERS.ZERO_SSL); }, [settings]); const handleFormChange = () => { - setInitialChanged(true); - }; - - const handleFormFinish = async (fields: z.infer) => { - const newSettings = produce(settings, (draft) => { - draft.content ??= {} as SSLProviderSettingsContent; - draft.content.provider = SSLPROVIDERS.ZERO_SSL; - - draft.content.config ??= {} as SSLProviderSettingsContent["config"]; - draft.content.config[SSLPROVIDERS.ZERO_SSL] = fields; - }); - await updateSettings(newSettings); - - setInitialChanged(false); + setFormChanged(true); }; return ( - + { - @@ -148,34 +148,33 @@ const SSLProviderEditFormGoogleTrustServicesConfig = () => { .max(256, t("common.errmsg.string_max", { max: 256 })), }); const formRule = createSchemaFieldRule(formSchema); - const [form] = Form.useForm>(); + const { form: formInst, formProps } = useAntdForm>({ + initialValues: settings?.content?.config?.[SSLPROVIDERS.GOOGLE_TRUST_SERVICES], + onSubmit: async (values) => { + const newSettings = produce(settings, (draft) => { + draft.content ??= {} as SSLProviderSettingsContent; + draft.content.provider = SSLPROVIDERS.GOOGLE_TRUST_SERVICES; - const [initialValues, setInitialValues] = useState(settings?.content?.config?.[SSLPROVIDERS.GOOGLE_TRUST_SERVICES]); - const [initialChanged, setInitialChanged] = useState(false); + draft.content.config ??= {} as SSLProviderSettingsContent["config"]; + draft.content.config[SSLPROVIDERS.GOOGLE_TRUST_SERVICES] = values; + }); + await updateSettings(newSettings); + + setFormChanged(false); + }, + }); + + const [formChanged, setFormChanged] = useState(false); useDeepCompareEffect(() => { - setInitialValues(settings?.content?.config?.[SSLPROVIDERS.GOOGLE_TRUST_SERVICES]); - setInitialChanged(settings?.content?.provider !== SSLPROVIDERS.GOOGLE_TRUST_SERVICES); + setFormChanged(settings?.content?.provider !== SSLPROVIDERS.GOOGLE_TRUST_SERVICES); }, [settings]); const handleFormChange = () => { - setInitialChanged(true); - }; - - const handleFormFinish = async (fields: z.infer) => { - const newSettings = produce(settings, (draft) => { - draft.content ??= {} as SSLProviderSettingsContent; - draft.content.provider = SSLPROVIDERS.GOOGLE_TRUST_SERVICES; - - draft.content.config ??= {} as SSLProviderSettingsContent["config"]; - draft.content.config[SSLPROVIDERS.GOOGLE_TRUST_SERVICES] = fields; - }); - await updateSettings(newSettings); - - setInitialChanged(false); + setFormChanged(true); }; return ( - + { - @@ -228,7 +227,7 @@ const SettingsSSLProvider = () => { fetchData(); }, []); - const [providerType, setFormProviderType] = useState(); + const [providerType, setFormProviderType] = useState(SSLPROVIDERS.LETS_ENCRYPT); const providerFormComponent = useMemo(() => { switch (providerType) { case SSLPROVIDERS.LETS_ENCRYPT: @@ -267,33 +266,29 @@ const SettingsSSLProvider = () => { {MessageContextHolder} {NotificationContextHolder} - {loading ? ( - - ) : ( - <> - - - setFormProviderType(value as SSLProviders)}> - } - size="small" - title="Let's Encrypt" - value={SSLPROVIDERS.LETS_ENCRYPT} - /> - } size="small" title="ZeroSSL" value={SSLPROVIDERS.ZERO_SSL} /> - } - size="small" - title="Google Trust Services" - value={SSLPROVIDERS.GOOGLE_TRUST_SERVICES} - /> - - - + }> +
+ + setFormProviderType(value as SSLProviders)}> + } + size="small" + title="Let's Encrypt" + value={SSLPROVIDERS.LETS_ENCRYPT} + /> + } size="small" title="ZeroSSL" value={SSLPROVIDERS.ZERO_SSL} /> + } + size="small" + title="Google Trust Services" + value={SSLPROVIDERS.GOOGLE_TRUST_SERVICES} + /> + + +
-
{providerFormComponent}
- - )} +
{providerFormComponent}
+
); }; diff --git a/ui/src/pages/workflows/WorkflowDetail.tsx b/ui/src/pages/workflows/WorkflowDetail.tsx index 0ae2df91..3ffc7330 100644 --- a/ui/src/pages/workflows/WorkflowDetail.tsx +++ b/ui/src/pages/workflows/WorkflowDetail.tsx @@ -57,9 +57,9 @@ const WorkflowDetail = () => { return elements; }, [workflow]); - const handleBaseInfoFormFinish = async (fields: Pick) => { + const handleBaseInfoFormFinish = async (values: Pick) => { try { - await setBaseInfo(fields.name!, fields.description!); + await setBaseInfo(values.name!, values.description!); } catch (err) { console.error(err); notificationApi.error({ message: t("common.text.request_error"), description: getErrMsg(err) }); @@ -93,6 +93,7 @@ const WorkflowDetail = () => { }); }; + // TODO: 发布更改 撤销更改 立即执行 // const handleWorkflowSaveClick = () => { // if (!allNodesValidated(workflow.draft as WorkflowNode)) { // messageApi.warning(t("workflow.detail.action.save.failed.uncompleted")); @@ -192,7 +193,7 @@ const WorkflowBaseInfoModalForm = memo( }: { model: Pick; trigger?: React.ReactElement; - onFinish?: (fields: Pick) => Promise; + onFinish?: (values: Pick) => Promise; }) => { const { t } = useTranslation(); diff --git a/ui/src/stores/workflow/index.ts b/ui/src/stores/workflow/index.ts index 432adcf4..21f59ff0 100644 --- a/ui/src/stores/workflow/index.ts +++ b/ui/src/stores/workflow/index.ts @@ -34,14 +34,14 @@ export type WorkflowState = { export const useWorkflowStore = create((set, get) => ({ workflow: { id: "", - name: "placeholder", + name: "", type: WorkflowNodeType.Start, } as WorkflowModel, initialized: false, init: async (id?: string) => { let data = { id: "", - name: "placeholder", + name: "", type: "auto", } as WorkflowModel;