mirror of
https://github.com/certimate-go/certimate.git
synced 2026-09-03 06:23:54 +08:00
Merge pull request #1440 from certimate-go/feat/form-input-trim
feat: form input trim
This commit is contained in:
@@ -56,12 +56,12 @@ const MultipleSplitValueInput = ({
|
||||
name: "componentMultipleSplitValueInput_" + nanoid(),
|
||||
initialValues: { value: value?.split(delimiter) },
|
||||
onSubmit: (values) => {
|
||||
const temp = (values.value ?? []) as string[];
|
||||
let temp = (values.value ?? []) as string[];
|
||||
if (splitOptions.trimSpace) {
|
||||
temp.map((e) => e.trim());
|
||||
temp = temp.map((e) => e.trim());
|
||||
}
|
||||
if (splitOptions.removeEmpty) {
|
||||
temp.filter((e) => !!e);
|
||||
temp = temp.filter((e) => !!e);
|
||||
}
|
||||
|
||||
setValue(temp.join(delimiter));
|
||||
|
||||
@@ -12,6 +12,7 @@ import { ACCESS_USAGES } from "@/domain/provider";
|
||||
import { useTriggerElement, useZustandShallowSelector } from "@/hooks";
|
||||
import { useAccessesStore } from "@/stores/access";
|
||||
import { unwrapErrMsg } from "@/utils/error";
|
||||
import { applyTrimmedFormValues } from "@/utils/form";
|
||||
|
||||
import AccessForm, { type AccessFormModes, type AccessFormProps, type AccessFormUsages } from "./AccessForm";
|
||||
|
||||
@@ -60,6 +61,7 @@ const AccessEditDrawer = ({ afterSubmit, mode, data, loading, trigger, usage, ..
|
||||
|
||||
setFormPending(true);
|
||||
try {
|
||||
applyTrimmedFormValues(formInst);
|
||||
formValues = await formInst.validateFields();
|
||||
formValues.reserve = usage === "ca" ? "ca" : usage === "notification" ? "notif" : void 0;
|
||||
} catch (err) {
|
||||
|
||||
@@ -8,6 +8,7 @@ import { saveAs } from "file-saver";
|
||||
import { download as downloadCertificate } from "@/api/certificates";
|
||||
import { CERTIFICATE_FORMATS, type CertificateFormatType, type CertificateModel } from "@/domain/certificate";
|
||||
import { useAntdForm, useTriggerElement } from "@/hooks";
|
||||
import { applyTrimmedFormValues } from "@/utils/form";
|
||||
|
||||
export interface CertificateDownloadModalProps {
|
||||
className?: string;
|
||||
@@ -153,10 +154,11 @@ const CertificateDownloadModal = ({ afterClose, data, trigger, ...props }: Certi
|
||||
};
|
||||
|
||||
const handleDownloadClick = async (format: CertificateFormatType) => {
|
||||
await formInst.validateFields();
|
||||
applyTrimmedFormValues(formInst);
|
||||
const formValues = await formInst.validateFields();
|
||||
|
||||
try {
|
||||
const res = await downloadCertificate(data.id, format, formInst.getFieldsValue());
|
||||
const res = await downloadCertificate(data.id, format, formValues);
|
||||
const bstr = atob(res.data.zipBytes);
|
||||
const u8arr = Uint8Array.from(bstr, (ch) => ch.charCodeAt(0));
|
||||
const blob = new Blob([u8arr], { type: "application/zip" });
|
||||
|
||||
@@ -8,6 +8,7 @@ import { z } from "zod";
|
||||
import CodeTextInput from "@/components/CodeTextInput";
|
||||
import { WORKFLOW_NODE_TYPES, type WorkflowGraph, type WorkflowNode, type WorkflowNodeType } from "@/domain/workflow";
|
||||
import { useAntdForm } from "@/hooks";
|
||||
import { applyTrimmedFormValues } from "@/utils/form";
|
||||
|
||||
export type WorkflowGraphImportInputBoxFormats = "json" | "yaml";
|
||||
|
||||
@@ -220,6 +221,7 @@ const WorkflowGraphImportInputBox = forwardRef<WorkflowGraphImportInputBoxInstan
|
||||
useImperativeHandle(ref, () => {
|
||||
return {
|
||||
validate: async () => {
|
||||
applyTrimmedFormValues(formInst);
|
||||
const formValues = await formInst.validateFields();
|
||||
return deserialize(formValues.content, formValues.format);
|
||||
},
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
type ExprValueType,
|
||||
} from "@/domain/workflow";
|
||||
import { useAntdFormName } from "@/hooks";
|
||||
import { applyTrimmedFormValues } from "@/utils/form";
|
||||
|
||||
import { useNodeFormContext } from "./_context";
|
||||
import { getAllPreviousNodes } from "../_util";
|
||||
@@ -261,6 +262,7 @@ const BranchBlockNodeConfigExprInputBox = forwardRef<BranchBlockNodeConfigExprIn
|
||||
useImperativeHandle(ref, () => {
|
||||
return {
|
||||
validate: async () => {
|
||||
applyTrimmedFormValues(formInst);
|
||||
const formValues = await formInst.validateFields();
|
||||
return formValuesToExpr(formValues);
|
||||
},
|
||||
|
||||
@@ -8,6 +8,7 @@ import { isEqual } from "radash";
|
||||
|
||||
import Show from "@/components/Show";
|
||||
import { unwrapErrMsg } from "@/utils/error";
|
||||
import { applyTrimmedFormValues } from "@/utils/form";
|
||||
|
||||
import { type NodeRegistry } from "../nodes/typings";
|
||||
|
||||
@@ -48,6 +49,7 @@ export const NodeConfigDrawer = ({ children, afterClose, anchor, footer = true,
|
||||
|
||||
setFormPending(true);
|
||||
try {
|
||||
applyTrimmedFormValues(formInst);
|
||||
formValues = await formInst.validateFields();
|
||||
} catch (err) {
|
||||
message.warning(t("common.errmsg.form_invalid"));
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { useState } from "react";
|
||||
import { useMemo, useState } from "react";
|
||||
import { useDeepCompareEffect } from "ahooks";
|
||||
import { Form, type FormInstance, type FormProps } from "antd";
|
||||
|
||||
import { applyTrimmedFormValues, trimFormValues } from "@/utils/form";
|
||||
|
||||
import useAntdFormName from "./useAntdFormName";
|
||||
|
||||
export interface UseAntdFormOptions<T extends NonNullable<unknown> = any> {
|
||||
@@ -64,11 +66,24 @@ const useAntdForm = <T extends NonNullable<unknown> = any>({ form, initialValues
|
||||
};
|
||||
}, [formInst, initialValues]);
|
||||
|
||||
const onFinish = (values: T) => {
|
||||
const wrappedFormInst = useMemo(() => {
|
||||
const originalSubmit = formInst.submit;
|
||||
return {
|
||||
...formInst,
|
||||
submit: () => {
|
||||
applyTrimmedFormValues(formInst);
|
||||
return originalSubmit();
|
||||
},
|
||||
};
|
||||
}, [formInst]);
|
||||
|
||||
const doFinish = () => {
|
||||
if (formPending) return Promise.reject(new Error("Form is pending"));
|
||||
|
||||
setFormPending(true);
|
||||
|
||||
const values = trimFormValues(formInst.getFieldsValue(true));
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
formInst
|
||||
.validateFields()
|
||||
@@ -93,18 +108,19 @@ const useAntdForm = <T extends NonNullable<unknown> = any>({ form, initialValues
|
||||
};
|
||||
|
||||
const formProps: FormProps = {
|
||||
form: formInst,
|
||||
form: wrappedFormInst,
|
||||
initialValues: formInitialValues,
|
||||
name: formName,
|
||||
onFinish,
|
||||
onFinish: doFinish,
|
||||
};
|
||||
|
||||
return {
|
||||
form: formInst,
|
||||
form: wrappedFormInst,
|
||||
formProps: formProps,
|
||||
formPending: formPending,
|
||||
submit: () => {
|
||||
return onFinish(formInst.getFieldsValue(true));
|
||||
applyTrimmedFormValues(formInst);
|
||||
return doFinish();
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
@@ -12,6 +12,7 @@ import { ACCESS_USAGES } from "@/domain/provider";
|
||||
import { useZustandShallowSelector } from "@/hooks";
|
||||
import { useAccessesStore } from "@/stores/access";
|
||||
import { unwrapErrMsg } from "@/utils/error";
|
||||
import { applyTrimmedFormValues } from "@/utils/form";
|
||||
|
||||
const AccessNew = () => {
|
||||
const navigate = useNavigate();
|
||||
@@ -47,6 +48,7 @@ const AccessNew = () => {
|
||||
|
||||
setFormPending(true);
|
||||
try {
|
||||
applyTrimmedFormValues(formInst);
|
||||
formValues = await formInst.validateFields();
|
||||
formValues.reserve = providerUsage === "ca" ? "ca" : providerUsage === "notification" ? "notif" : void 0;
|
||||
} catch (err) {
|
||||
|
||||
91
ui/src/utils/form.ts
Normal file
91
ui/src/utils/form.ts
Normal file
@@ -0,0 +1,91 @@
|
||||
import type { FormInstance } from "antd";
|
||||
|
||||
export const TRIM_EXEMPT_FIELD_NAMES = new Set([
|
||||
"accesskey",
|
||||
"accesskeyid",
|
||||
"accesskeypassword",
|
||||
"accesskeysecret",
|
||||
"accesstoken",
|
||||
"accesstokenid",
|
||||
"apiaccesstoken",
|
||||
"apikey",
|
||||
"apikeysecret",
|
||||
"apipassword",
|
||||
"apisecret",
|
||||
"apitoken",
|
||||
"apitokenforzone",
|
||||
"apitokensecret",
|
||||
"appkey",
|
||||
"applicationkey",
|
||||
"applicationsecret",
|
||||
"applicationtoken",
|
||||
"authpassword",
|
||||
"bottoken",
|
||||
"clientsecret",
|
||||
"clienttoken",
|
||||
"confirmpassword",
|
||||
"consumerkey",
|
||||
"credentials",
|
||||
"eabhmackey",
|
||||
"httptoken",
|
||||
"jkskeypass",
|
||||
"jksstorepass",
|
||||
"key",
|
||||
"keypassphrase",
|
||||
"mtlsprivatekey",
|
||||
"newpassword",
|
||||
"oldpassword",
|
||||
"password",
|
||||
"personalaccesstoken",
|
||||
"pfxpassword",
|
||||
"privatekey",
|
||||
"privatekeypassphrase",
|
||||
"secret",
|
||||
"secretaccesskey",
|
||||
"secretapikey",
|
||||
"secretid",
|
||||
"secretkey",
|
||||
"serviceaccountkey",
|
||||
"token",
|
||||
"totpsecret",
|
||||
"tsiggsspassword",
|
||||
"tsigkey",
|
||||
"tsigsecret",
|
||||
]);
|
||||
|
||||
const isPlainObject = (value: unknown): value is Record<string, unknown> => {
|
||||
if (typeof value !== "object" || value === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const proto = Object.getPrototypeOf(value);
|
||||
return proto === Object.prototype || proto === null;
|
||||
};
|
||||
|
||||
const trimValue = (value: unknown, key?: string): unknown => {
|
||||
if (typeof value === "string") {
|
||||
return key != null && TRIM_EXEMPT_FIELD_NAMES.has(key.toLowerCase()) ? value : value.trim();
|
||||
}
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
return value.map((item) => trimValue(item));
|
||||
}
|
||||
|
||||
if (isPlainObject(value)) {
|
||||
const obj: Record<string, unknown> = {};
|
||||
Object.keys(value).forEach((k) => {
|
||||
obj[k] = trimValue(value[k], k);
|
||||
});
|
||||
return obj;
|
||||
}
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
export const trimFormValues = <T>(values: T): T => {
|
||||
return trimValue(values) as T;
|
||||
};
|
||||
|
||||
export const applyTrimmedFormValues = (formInst: FormInstance): void => {
|
||||
formInst.setFieldsValue(trimFormValues(formInst.getFieldsValue(true)));
|
||||
};
|
||||
Reference in New Issue
Block a user