Merge branch 'main' into feat/condition

This commit is contained in:
Fu Diwei
2025-05-27 05:36:42 +08:00
155 changed files with 3533 additions and 1659 deletions

View File

@@ -89,13 +89,6 @@ const ModalForm = <T extends NonNullable<unknown> = any>({
modalProps?.afterClose?.();
},
onClose: async (e) => {
if (formPending) return;
// 关闭 Modal 时 Promise.reject 阻止关闭
await modalProps?.onClose?.(e as React.MouseEvent | React.KeyboardEvent);
setOpen(false);
},
};
const handleOkClick = async () => {

View File

@@ -0,0 +1,108 @@
import { type ChangeEvent, useEffect } from "react";
import { FormOutlined as FormOutlinedIcon } from "@ant-design/icons";
import { nanoid } from "@ant-design/pro-components";
import { useControllableValue } from "ahooks";
import { Button, Form, Input, type InputProps, Space } from "antd";
import { useAntdForm } from "@/hooks";
import ModalForm from "./ModalForm";
import MultipleInput from "./MultipleInput";
type SplitOptions = {
removeEmpty?: boolean;
trim?: boolean;
};
export type MultipleSplitValueInputProps = Omit<InputProps, "count" | "defaultValue" | "showCount" | "value" | "onChange"> & {
defaultValue?: string;
delimiter?: string;
maxCount?: number;
minCount?: number;
modalTitle?: string;
modalWidth?: number;
placeholderInModal?: string;
showSortButton?: boolean;
splitOptions?: SplitOptions;
value?: string[];
onChange?: (value: string) => void;
};
const DEFAULT_DELIMITER = ";";
const MultipleSplitValueInput = ({
className,
style,
delimiter = DEFAULT_DELIMITER,
disabled,
maxCount,
minCount,
modalTitle,
modalWidth = 480,
placeholder,
placeholderInModal,
showSortButton = true,
splitOptions = {},
onClear,
...props
}: MultipleSplitValueInputProps) => {
const [value, setValue] = useControllableValue<string>(props, {
valuePropName: "value",
defaultValuePropName: "defaultValue",
trigger: "onChange",
});
const { form: formInst, formProps } = useAntdForm({
name: "componentMultipleSplitValueInput_" + nanoid(),
initialValues: { value: value?.split(delimiter) },
onSubmit: (values) => {
const temp = values.value ?? [];
if (splitOptions.trim) {
temp.map((e) => e.trim());
}
if (splitOptions.removeEmpty) {
temp.filter((e) => !!e);
}
setValue(temp.join(delimiter));
},
});
useEffect(() => {
formInst.setFieldValue("value", value?.split(delimiter));
}, [delimiter, value]);
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
setValue(e.target.value);
};
const handleClear = () => {
setValue("");
onClear?.();
};
return (
<Space.Compact className={className} style={{ width: "100%", ...style }}>
<Input {...props} disabled={disabled} placeholder={placeholder} value={value} onChange={handleChange} onClear={handleClear} />
<ModalForm
{...formProps}
layout="vertical"
form={formInst}
modalProps={{ destroyOnHidden: true }}
title={modalTitle}
trigger={
<Button disabled={disabled}>
<FormOutlinedIcon />
</Button>
}
validateTrigger="onSubmit"
width={modalWidth}
>
<Form.Item name="value" noStyle>
<MultipleInput minCount={minCount} maxCount={maxCount} placeholder={placeholderInModal ?? placeholder} showSortButton={showSortButton} />
</Form.Item>
</ModalForm>
</Space.Compact>
);
};
export default MultipleSplitValueInput;

View File

@@ -100,6 +100,7 @@ const AccessEditModal = ({ data, loading, trigger, scene, usage, afterSubmit, ..
}}
afterClose={() => setOpen(false)}
cancelButtonProps={{ disabled: formPending }}
cancelText={t("common.button.cancel")}
closable
confirmLoading={formPending}
destroyOnHidden

View File

@@ -29,9 +29,12 @@ import AccessFormCloudflareConfig from "./AccessFormCloudflareConfig";
import AccessFormClouDNSConfig from "./AccessFormClouDNSConfig";
import AccessFormCMCCCloudConfig from "./AccessFormCMCCCloudConfig";
import AccessFormDeSECConfig from "./AccessFormDeSECConfig";
import AccessFormDigitalOceanConfig from "./AccessFormDigitalOceanConfig";
import AccessFormDingTalkBotConfig from "./AccessFormDingTalkBotConfig";
import AccessFormDiscordBotConfig from "./AccessFormDiscordBotConfig";
import AccessFormDNSLAConfig from "./AccessFormDNSLAConfig";
import AccessFormDogeCloudConfig from "./AccessFormDogeCloudConfig";
import AccessFormDuckDNSConfig from "./AccessFormDuckDNSConfig";
import AccessFormDynv6Config from "./AccessFormDynv6Config";
import AccessFormEdgioConfig from "./AccessFormEdgioConfig";
import AccessFormEmailConfig from "./AccessFormEmailConfig";
@@ -41,6 +44,7 @@ import AccessFormGnameConfig from "./AccessFormGnameConfig";
import AccessFormGoDaddyConfig from "./AccessFormGoDaddyConfig";
import AccessFormGoEdgeConfig from "./AccessFormGoEdgeConfig";
import AccessFormGoogleTrustServicesConfig from "./AccessFormGoogleTrustServicesConfig";
import AccessFormHetznerConfig from "./AccessFormHetznerConfig";
import AccessFormHuaweiCloudConfig from "./AccessFormHuaweiCloudConfig";
import AccessFormJDCloudConfig from "./AccessFormJDCloudConfig";
import AccessFormKubernetesConfig from "./AccessFormKubernetesConfig";
@@ -60,11 +64,13 @@ import AccessFormQiniuConfig from "./AccessFormQiniuConfig";
import AccessFormRainYunConfig from "./AccessFormRainYunConfig";
import AccessFormRatPanelConfig from "./AccessFormRatPanelConfig";
import AccessFormSafeLineConfig from "./AccessFormSafeLineConfig";
import AccessFormSlackBotConfig from "./AccessFormSlackBotConfig";
import AccessFormSSHConfig from "./AccessFormSSHConfig";
import AccessFormSSLComConfig from "./AccessFormSSLComConfig";
import AccessFormTelegramBotConfig from "./AccessFormTelegramBotConfig";
import AccessFormTencentCloudConfig from "./AccessFormTencentCloudConfig";
import AccessFormUCloudConfig from "./AccessFormUCloudConfig";
import AccessFormUniCloudConfig from "./AccessFormUniCloudConfig";
import AccessFormUpyunConfig from "./AccessFormUpyunConfig";
import AccessFormVercelConfig from "./AccessFormVercelConfig";
import AccessFormVolcEngineConfig from "./AccessFormVolcEngineConfig";
@@ -100,9 +106,9 @@ const AccessForm = forwardRef<AccessFormInstance, AccessFormProps>(({ className,
const formSchema = z.object({
name: z
.string({ message: t("access.form.name.placeholder") })
.trim()
.min(1, t("access.form.name.placeholder"))
.max(64, t("common.errmsg.string_max", { max: 64 }))
.trim(),
.max(64, t("common.errmsg.string_max", { max: 64 })),
provider: z.nativeEnum(ACCESS_PROVIDERS, {
message:
usage === "ca-only"
@@ -215,12 +221,18 @@ const AccessForm = forwardRef<AccessFormInstance, AccessFormProps>(({ className,
return <AccessFormCMCCCloudConfig {...nestedFormProps} />;
case ACCESS_PROVIDERS.DESEC:
return <AccessFormDeSECConfig {...nestedFormProps} />;
case ACCESS_PROVIDERS.DIGITALOCEAN:
return <AccessFormDigitalOceanConfig {...nestedFormProps} />;
case ACCESS_PROVIDERS.DINGTALKBOT:
return <AccessFormDingTalkBotConfig {...nestedFormProps} />;
case ACCESS_PROVIDERS.DISCORDBOT:
return <AccessFormDiscordBotConfig {...nestedFormProps} />;
case ACCESS_PROVIDERS.DNSLA:
return <AccessFormDNSLAConfig {...nestedFormProps} />;
case ACCESS_PROVIDERS.DOGECLOUD:
return <AccessFormDogeCloudConfig {...nestedFormProps} />;
case ACCESS_PROVIDERS.DUCKDNS:
return <AccessFormDuckDNSConfig {...nestedFormProps} />;
case ACCESS_PROVIDERS.DYNV6:
return <AccessFormDynv6Config {...nestedFormProps} />;
case ACCESS_PROVIDERS.EDGIO:
@@ -239,6 +251,8 @@ const AccessForm = forwardRef<AccessFormInstance, AccessFormProps>(({ className,
return <AccessFormGoEdgeConfig {...nestedFormProps} />;
case ACCESS_PROVIDERS.GOOGLETRUSTSERVICES:
return <AccessFormGoogleTrustServicesConfig {...nestedFormProps} />;
case ACCESS_PROVIDERS.HETZNER:
return <AccessFormHetznerConfig {...nestedFormProps} />;
case ACCESS_PROVIDERS.HUAWEICLOUD:
return <AccessFormHuaweiCloudConfig {...nestedFormProps} />;
case ACCESS_PROVIDERS.JDCLOUD:
@@ -277,6 +291,8 @@ const AccessForm = forwardRef<AccessFormInstance, AccessFormProps>(({ className,
return <AccessFormRatPanelConfig {...nestedFormProps} />;
case ACCESS_PROVIDERS.SAFELINE:
return <AccessFormSafeLineConfig {...nestedFormProps} />;
case ACCESS_PROVIDERS.SLACKBOT:
return <AccessFormSlackBotConfig {...nestedFormProps} />;
case ACCESS_PROVIDERS.SSH:
return <AccessFormSSHConfig {...nestedFormProps} />;
case ACCESS_PROVIDERS.TELEGRAMBOT:
@@ -287,6 +303,8 @@ const AccessForm = forwardRef<AccessFormInstance, AccessFormProps>(({ className,
return <AccessFormTencentCloudConfig {...nestedFormProps} />;
case ACCESS_PROVIDERS.UCLOUD:
return <AccessFormUCloudConfig {...nestedFormProps} />;
case ACCESS_PROVIDERS.UNICLOUD:
return <AccessFormUniCloudConfig {...nestedFormProps} />;
case ACCESS_PROVIDERS.UPYUN:
return <AccessFormUpyunConfig {...nestedFormProps} />;
case ACCESS_PROVIDERS.VERCEL:

View File

@@ -17,7 +17,7 @@ export type AccessForm1PanelConfigProps = {
const initFormModel = (): AccessForm1PanelConfigFieldValues => {
return {
apiUrl: "http://<your-host-addr>:20410/",
serverUrl: "http://<your-host-addr>:20410/",
apiVersion: "v1",
apiKey: "",
};
@@ -27,7 +27,7 @@ const AccessForm1PanelConfig = ({ form: formInst, formName, disabled, initialVal
const { t } = useTranslation();
const formSchema = z.object({
apiUrl: z.string().url(t("common.errmsg.url_invalid")),
serverUrl: z.string().url(t("common.errmsg.url_invalid")),
apiVersion: z.string().nonempty(t("access.form.1panel_api_version.placeholder")),
apiKey: z
.string()
@@ -51,8 +51,8 @@ const AccessForm1PanelConfig = ({ form: formInst, formName, disabled, initialVal
name={formName}
onValuesChange={handleFormChange}
>
<Form.Item name="apiUrl" label={t("access.form.1panel_api_url.label")} rules={[formRule]}>
<Input placeholder={t("access.form.1panel_api_url.placeholder")} />
<Form.Item name="serverUrl" label={t("access.form.1panel_server_url.label")} rules={[formRule]}>
<Input placeholder={t("access.form.1panel_server_url.placeholder")} />
</Form.Item>
<Form.Item name="apiVersion" label={t("access.form.1panel_api_version.label")} rules={[formRule]}>
@@ -68,10 +68,10 @@ const AccessForm1PanelConfig = ({ form: formInst, formName, disabled, initialVal
<Input.Password autoComplete="new-password" placeholder={t("access.form.1panel_api_key.placeholder")} />
</Form.Item>
<Form.Item name="allowInsecureConnections" label={t("access.form.1panel_allow_insecure_conns.label")} rules={[formRule]}>
<Form.Item name="allowInsecureConnections" label={t("access.form.common_allow_insecure_conns.label")} rules={[formRule]}>
<Switch
checkedChildren={t("access.form.1panel_allow_insecure_conns.switch.on")}
unCheckedChildren={t("access.form.1panel_allow_insecure_conns.switch.off")}
checkedChildren={t("access.form.common_allow_insecure_conns.switch.on")}
unCheckedChildren={t("access.form.common_allow_insecure_conns.switch.off")}
/>
</Form.Item>
</Form>

View File

@@ -17,7 +17,7 @@ export type AccessFormBaotaPanelConfigProps = {
const initFormModel = (): AccessFormBaotaPanelConfigFieldValues => {
return {
apiUrl: "http://<your-host-addr>:8888/",
serverUrl: "http://<your-host-addr>:8888/",
apiKey: "",
};
};
@@ -26,7 +26,7 @@ const AccessFormBaotaPanelConfig = ({ form: formInst, formName, disabled, initia
const { t } = useTranslation();
const formSchema = z.object({
apiUrl: z.string().url(t("common.errmsg.url_invalid")),
serverUrl: z.string().url(t("common.errmsg.url_invalid")),
apiKey: z.string().nonempty(t("access.form.baotapanel_api_key.placeholder")).trim(),
allowInsecureConnections: z.boolean().nullish(),
});
@@ -45,8 +45,8 @@ const AccessFormBaotaPanelConfig = ({ form: formInst, formName, disabled, initia
name={formName}
onValuesChange={handleFormChange}
>
<Form.Item name="apiUrl" label={t("access.form.baotapanel_api_url.label")} rules={[formRule]}>
<Input placeholder={t("access.form.baotapanel_api_url.placeholder")} />
<Form.Item name="serverUrl" label={t("access.form.baotapanel_server_url.label")} rules={[formRule]}>
<Input placeholder={t("access.form.baotapanel_server_url.placeholder")} />
</Form.Item>
<Form.Item
@@ -58,10 +58,10 @@ const AccessFormBaotaPanelConfig = ({ form: formInst, formName, disabled, initia
<Input.Password autoComplete="new-password" placeholder={t("access.form.baotapanel_api_key.placeholder")} />
</Form.Item>
<Form.Item name="allowInsecureConnections" label={t("access.form.baotapanel_allow_insecure_conns.label")} rules={[formRule]}>
<Form.Item name="allowInsecureConnections" label={t("access.form.common_allow_insecure_conns.label")} rules={[formRule]}>
<Switch
checkedChildren={t("access.form.baotapanel_allow_insecure_conns.switch.on")}
unCheckedChildren={t("access.form.baotapanel_allow_insecure_conns.switch.off")}
checkedChildren={t("access.form.common_allow_insecure_conns.switch.on")}
unCheckedChildren={t("access.form.common_allow_insecure_conns.switch.off")}
/>
</Form.Item>
</Form>

View File

@@ -17,7 +17,7 @@ export type AccessFormBaotaWAFConfigProps = {
const initFormModel = (): AccessFormBaotaWAFConfigFieldValues => {
return {
apiUrl: "http://<your-host-addr>:8379/",
serverUrl: "http://<your-host-addr>:8379/",
apiKey: "",
};
};
@@ -26,7 +26,7 @@ const AccessFormBaotaWAFConfig = ({ form: formInst, formName, disabled, initialV
const { t } = useTranslation();
const formSchema = z.object({
apiUrl: z.string().url(t("common.errmsg.url_invalid")),
serverUrl: z.string().url(t("common.errmsg.url_invalid")),
apiKey: z.string().nonempty(t("access.form.baotawaf_api_key.placeholder")).trim(),
allowInsecureConnections: z.boolean().nullish(),
});
@@ -45,8 +45,8 @@ const AccessFormBaotaWAFConfig = ({ form: formInst, formName, disabled, initialV
name={formName}
onValuesChange={handleFormChange}
>
<Form.Item name="apiUrl" label={t("access.form.baotawaf_api_url.label")} rules={[formRule]}>
<Input placeholder={t("access.form.baotawaf_api_url.placeholder")} />
<Form.Item name="serverUrl" label={t("access.form.baotawaf_server_url.label")} rules={[formRule]}>
<Input placeholder={t("access.form.baotawaf_server_url.placeholder")} />
</Form.Item>
<Form.Item
@@ -58,10 +58,10 @@ const AccessFormBaotaWAFConfig = ({ form: formInst, formName, disabled, initialV
<Input.Password autoComplete="new-password" placeholder={t("access.form.baotawaf_api_key.placeholder")} />
</Form.Item>
<Form.Item name="allowInsecureConnections" label={t("access.form.baotawaf_allow_insecure_conns.label")} rules={[formRule]}>
<Form.Item name="allowInsecureConnections" label={t("access.form.common_allow_insecure_conns.label")} rules={[formRule]}>
<Switch
checkedChildren={t("access.form.baotawaf_allow_insecure_conns.switch.on")}
unCheckedChildren={t("access.form.baotawaf_allow_insecure_conns.switch.off")}
checkedChildren={t("access.form.common_allow_insecure_conns.switch.on")}
unCheckedChildren={t("access.form.common_allow_insecure_conns.switch.off")}
/>
</Form.Item>
</Form>

View File

@@ -17,7 +17,7 @@ export type AccessFormCdnflyConfigProps = {
const initFormModel = (): AccessFormCdnflyConfigFieldValues => {
return {
apiUrl: "http://<your-host-addr>:88/",
serverUrl: "http://<your-host-addr>:88/",
apiKey: "",
apiSecret: "",
};
@@ -27,7 +27,7 @@ const AccessFormCdnflyConfig = ({ form: formInst, formName, disabled, initialVal
const { t } = useTranslation();
const formSchema = z.object({
apiUrl: z.string().url(t("common.errmsg.url_invalid")),
serverUrl: z.string().url(t("common.errmsg.url_invalid")),
apiKey: z
.string()
.min(1, t("access.form.cdnfly_api_key.placeholder"))
@@ -55,8 +55,8 @@ const AccessFormCdnflyConfig = ({ form: formInst, formName, disabled, initialVal
name={formName}
onValuesChange={handleFormChange}
>
<Form.Item name="apiUrl" label={t("access.form.cdnfly_api_url.label")} rules={[formRule]}>
<Input placeholder={t("access.form.cdnfly_api_url.placeholder")} />
<Form.Item name="serverUrl" label={t("access.form.cdnfly_server_url.label")} rules={[formRule]}>
<Input placeholder={t("access.form.cdnfly_server_url.placeholder")} />
</Form.Item>
<Form.Item
@@ -77,10 +77,10 @@ const AccessFormCdnflyConfig = ({ form: formInst, formName, disabled, initialVal
<Input.Password autoComplete="new-password" placeholder={t("access.form.cdnfly_api_secret.placeholder")} />
</Form.Item>
<Form.Item name="allowInsecureConnections" label={t("access.form.cdnfly_allow_insecure_conns.label")} rules={[formRule]}>
<Form.Item name="allowInsecureConnections" label={t("access.form.common_allow_insecure_conns.label")} rules={[formRule]}>
<Switch
checkedChildren={t("access.form.cdnfly_allow_insecure_conns.switch.on")}
unCheckedChildren={t("access.form.cdnfly_allow_insecure_conns.switch.off")}
checkedChildren={t("access.form.common_allow_insecure_conns.switch.on")}
unCheckedChildren={t("access.form.common_allow_insecure_conns.switch.off")}
/>
</Form.Item>
</Form>

View File

@@ -0,0 +1,57 @@
import { useTranslation } from "react-i18next";
import { Form, type FormInstance, Input } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import { type AccessConfigForDigitalOcean } from "@/domain/access";
type AccessFormDigitalOceanConfigFieldValues = Nullish<AccessConfigForDigitalOcean>;
export type AccessFormDigitalOceanConfigProps = {
form: FormInstance;
formName: string;
disabled?: boolean;
initialValues?: AccessFormDigitalOceanConfigFieldValues;
onValuesChange?: (values: AccessFormDigitalOceanConfigFieldValues) => void;
};
const initFormModel = (): AccessFormDigitalOceanConfigFieldValues => {
return {
accessToken: "",
};
};
const AccessFormDigitalOceanConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: AccessFormDigitalOceanConfigProps) => {
const { t } = useTranslation();
const formSchema = z.object({
accessToken: z.string().nonempty(t("access.form.digitalocean_access_token.placeholder")).trim(),
});
const formRule = createSchemaFieldRule(formSchema);
const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => {
onValuesChange?.(values);
};
return (
<Form
form={formInst}
disabled={disabled}
initialValues={initialValues ?? initFormModel()}
layout="vertical"
name={formName}
onValuesChange={handleFormChange}
>
<Form.Item
name="accessToken"
label={t("access.form.digitalocean_access_token.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.digitalocean_access_token.tooltip") }}></span>}
>
<Input.Password autoComplete="new-password" placeholder={t("access.form.digitalocean_access_token.placeholder")} />
</Form.Item>
</Form>
);
};
export default AccessFormDigitalOceanConfig;

View File

@@ -0,0 +1,71 @@
import { useTranslation } from "react-i18next";
import { Form, type FormInstance, Input } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import { type AccessConfigForDiscordBot } from "@/domain/access";
type AccessFormDiscordBotConfigFieldValues = Nullish<AccessConfigForDiscordBot>;
export type AccessFormDiscordBotConfigProps = {
form: FormInstance;
formName: string;
disabled?: boolean;
initialValues?: AccessFormDiscordBotConfigFieldValues;
onValuesChange?: (values: AccessFormDiscordBotConfigFieldValues) => void;
};
const initFormModel = (): AccessFormDiscordBotConfigFieldValues => {
return {
botToken: "",
};
};
const AccessFormDiscordBotConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: AccessFormDiscordBotConfigProps) => {
const { t } = useTranslation();
const formSchema = z.object({
botToken: z
.string({ message: t("access.form.discordbot_token.placeholder") })
.min(1, t("access.form.discordbot_token.placeholder"))
.max(256, t("common.errmsg.string_max", { max: 256 }))
.trim(),
defaultChannelId: z.string().nullish(),
});
const formRule = createSchemaFieldRule(formSchema);
const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => {
onValuesChange?.(values);
};
return (
<Form
form={formInst}
disabled={disabled}
initialValues={initialValues ?? initFormModel()}
layout="vertical"
name={formName}
onValuesChange={handleFormChange}
>
<Form.Item
name="botToken"
label={t("access.form.discordbot_token.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.discordbot_token.tooltip") }}></span>}
>
<Input.Password autoComplete="new-password" placeholder={t("access.form.discordbot_token.placeholder")} />
</Form.Item>
<Form.Item
name="defaultChannelId"
label={t("access.form.discordbot_default_channel_id.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.discordbot_default_channel_id.tooltip") }}></span>}
>
<Input allowClear placeholder={t("access.form.discordbot_default_channel_id.placeholder")} />
</Form.Item>
</Form>
);
};
export default AccessFormDiscordBotConfig;

View File

@@ -0,0 +1,57 @@
import { useTranslation } from "react-i18next";
import { Form, type FormInstance, Input } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import { type AccessConfigForDuckDNS } from "@/domain/access";
type AccessFormDuckDNSConfigFieldValues = Nullish<AccessConfigForDuckDNS>;
export type AccessFormDuckDNSConfigProps = {
form: FormInstance;
formName: string;
disabled?: boolean;
initialValues?: AccessFormDuckDNSConfigFieldValues;
onValuesChange?: (values: AccessFormDuckDNSConfigFieldValues) => void;
};
const initFormModel = (): AccessFormDuckDNSConfigFieldValues => {
return {
token: "",
};
};
const AccessFormDuckDNSConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: AccessFormDuckDNSConfigProps) => {
const { t } = useTranslation();
const formSchema = z.object({
token: z.string().nonempty(t("access.form.duckdns_token.placeholder")).trim(),
});
const formRule = createSchemaFieldRule(formSchema);
const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => {
onValuesChange?.(values);
};
return (
<Form
form={formInst}
disabled={disabled}
initialValues={initialValues ?? initFormModel()}
layout="vertical"
name={formName}
onValuesChange={handleFormChange}
>
<Form.Item
name="token"
label={t("access.form.duckdns_token.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.duckdns_token.tooltip") }}></span>}
>
<Input.Password autoComplete="new-password" placeholder={t("access.form.duckdns_token.placeholder")} />
</Form.Item>
</Form>
);
};
export default AccessFormDuckDNSConfig;

View File

@@ -17,7 +17,7 @@ export type AccessFormFlexCDNConfigProps = {
const initFormModel = (): AccessFormFlexCDNConfigFieldValues => {
return {
apiUrl: "http://<your-host-addr>:8000/",
serverUrl: "http://<your-host-addr>:8000/",
apiRole: "user",
accessKeyId: "",
accessKey: "",
@@ -28,7 +28,7 @@ const AccessFormFlexCDNConfig = ({ form: formInst, formName, disabled, initialVa
const { t } = useTranslation();
const formSchema = z.object({
apiUrl: z.string().url(t("common.errmsg.url_invalid")),
serverUrl: z.string().url(t("common.errmsg.url_invalid")),
role: z.union([z.literal("user"), z.literal("admin")], {
message: t("access.form.flexcdn_api_role.placeholder"),
}),
@@ -51,8 +51,8 @@ const AccessFormFlexCDNConfig = ({ form: formInst, formName, disabled, initialVa
name={formName}
onValuesChange={handleFormChange}
>
<Form.Item name="apiUrl" label={t("access.form.flexcdn_api_url.label")} rules={[formRule]}>
<Input placeholder={t("access.form.flexcdn_api_url.placeholder")} />
<Form.Item name="serverUrl" label={t("access.form.flexcdn_server_url.label")} rules={[formRule]}>
<Input placeholder={t("access.form.flexcdn_server_url.placeholder")} />
</Form.Item>
<Form.Item name="apiRole" label={t("access.form.flexcdn_api_role.label")} rules={[formRule]}>
@@ -77,10 +77,10 @@ const AccessFormFlexCDNConfig = ({ form: formInst, formName, disabled, initialVa
<Input.Password autoComplete="new-password" placeholder={t("access.form.flexcdn_access_key.placeholder")} />
</Form.Item>
<Form.Item name="allowInsecureConnections" label={t("access.form.flexcdn_allow_insecure_conns.label")} rules={[formRule]}>
<Form.Item name="allowInsecureConnections" label={t("access.form.common_allow_insecure_conns.label")} rules={[formRule]}>
<Switch
checkedChildren={t("access.form.flexcdn_allow_insecure_conns.switch.on")}
unCheckedChildren={t("access.form.flexcdn_allow_insecure_conns.switch.off")}
checkedChildren={t("access.form.common_allow_insecure_conns.switch.on")}
unCheckedChildren={t("access.form.common_allow_insecure_conns.switch.off")}
/>
</Form.Item>
</Form>

View File

@@ -17,7 +17,7 @@ export type AccessFormGoEdgeConfigProps = {
const initFormModel = (): AccessFormGoEdgeConfigFieldValues => {
return {
apiUrl: "http://<your-host-addr>:7788/",
serverUrl: "http://<your-host-addr>:7788/",
apiRole: "user",
accessKeyId: "",
accessKey: "",
@@ -28,7 +28,7 @@ const AccessFormGoEdgeConfig = ({ form: formInst, formName, disabled, initialVal
const { t } = useTranslation();
const formSchema = z.object({
apiUrl: z.string().url(t("common.errmsg.url_invalid")),
serverUrl: z.string().url(t("common.errmsg.url_invalid")),
role: z.union([z.literal("user"), z.literal("admin")], {
message: t("access.form.goedge_api_role.placeholder"),
}),
@@ -51,8 +51,8 @@ const AccessFormGoEdgeConfig = ({ form: formInst, formName, disabled, initialVal
name={formName}
onValuesChange={handleFormChange}
>
<Form.Item name="apiUrl" label={t("access.form.goedge_api_url.label")} rules={[formRule]}>
<Input placeholder={t("access.form.goedge_api_url.placeholder")} />
<Form.Item name="serverUrl" label={t("access.form.goedge_server_url.label")} rules={[formRule]}>
<Input placeholder={t("access.form.goedge_server_url.placeholder")} />
</Form.Item>
<Form.Item name="apiRole" label={t("access.form.goedge_api_role.label")} rules={[formRule]}>
@@ -77,10 +77,10 @@ const AccessFormGoEdgeConfig = ({ form: formInst, formName, disabled, initialVal
<Input.Password autoComplete="new-password" placeholder={t("access.form.goedge_access_key.placeholder")} />
</Form.Item>
<Form.Item name="allowInsecureConnections" label={t("access.form.goedge_allow_insecure_conns.label")} rules={[formRule]}>
<Form.Item name="allowInsecureConnections" label={t("access.form.common_allow_insecure_conns.label")} rules={[formRule]}>
<Switch
checkedChildren={t("access.form.goedge_allow_insecure_conns.switch.on")}
unCheckedChildren={t("access.form.goedge_allow_insecure_conns.switch.off")}
checkedChildren={t("access.form.common_allow_insecure_conns.switch.on")}
unCheckedChildren={t("access.form.common_allow_insecure_conns.switch.off")}
/>
</Form.Item>
</Form>

View File

@@ -0,0 +1,57 @@
import { useTranslation } from "react-i18next";
import { Form, type FormInstance, Input } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import { type AccessConfigForHetzner } from "@/domain/access";
type AccessFormHetznerConfigFieldValues = Nullish<AccessConfigForHetzner>;
export type AccessFormHetznerConfigProps = {
form: FormInstance;
formName: string;
disabled?: boolean;
initialValues?: AccessFormHetznerConfigFieldValues;
onValuesChange?: (values: AccessFormHetznerConfigFieldValues) => void;
};
const initFormModel = (): AccessFormHetznerConfigFieldValues => {
return {
apiToken: "",
};
};
const AccessFormHetznerConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: AccessFormHetznerConfigProps) => {
const { t } = useTranslation();
const formSchema = z.object({
apiToken: z.string().nonempty(t("access.form.hetzner_api_token.placeholder")).trim(),
});
const formRule = createSchemaFieldRule(formSchema);
const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => {
onValuesChange?.(values);
};
return (
<Form
form={formInst}
disabled={disabled}
initialValues={initialValues ?? initFormModel()}
layout="vertical"
name={formName}
onValuesChange={handleFormChange}
>
<Form.Item
name="apiToken"
label={t("access.form.hetzner_api_token.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.hetzner_api_token.tooltip") }}></span>}
>
<Input.Password autoComplete="new-password" placeholder={t("access.form.hetzner_api_token.placeholder")} />
</Form.Item>
</Form>
);
};
export default AccessFormHetznerConfig;

View File

@@ -17,7 +17,7 @@ export type AccessFormLeCDNConfigProps = {
const initFormModel = (): AccessFormLeCDNConfigFieldValues => {
return {
apiUrl: "http://<your-host-addr>:5090/",
serverUrl: "http://<your-host-addr>:5090/",
apiVersion: "v3",
apiRole: "user",
username: "",
@@ -29,7 +29,7 @@ const AccessFormLeCDNConfig = ({ form: formInst, formName, disabled, initialValu
const { t } = useTranslation();
const formSchema = z.object({
apiUrl: z.string().url(t("common.errmsg.url_invalid")),
serverUrl: z.string().url(t("common.errmsg.url_invalid")),
role: z.union([z.literal("client"), z.literal("master")], {
message: t("access.form.lecdn_api_role.placeholder"),
}),
@@ -52,8 +52,8 @@ const AccessFormLeCDNConfig = ({ form: formInst, formName, disabled, initialValu
name={formName}
onValuesChange={handleFormChange}
>
<Form.Item name="apiUrl" label={t("access.form.lecdn_api_url.label")} rules={[formRule]}>
<Input placeholder={t("access.form.lecdn_api_url.placeholder")} />
<Form.Item name="serverUrl" label={t("access.form.lecdn_server_url.label")} rules={[formRule]}>
<Input placeholder={t("access.form.lecdn_server_url.placeholder")} />
</Form.Item>
<Form.Item name="apiVersion" label={t("access.form.lecdn_api_version.label")} rules={[formRule]}>
@@ -72,10 +72,10 @@ const AccessFormLeCDNConfig = ({ form: formInst, formName, disabled, initialValu
<Input.Password autoComplete="new-password" placeholder={t("access.form.lecdn_password.placeholder")} />
</Form.Item>
<Form.Item name="allowInsecureConnections" label={t("access.form.lecdn_allow_insecure_conns.label")} rules={[formRule]}>
<Form.Item name="allowInsecureConnections" label={t("access.form.common_allow_insecure_conns.label")} rules={[formRule]}>
<Switch
checkedChildren={t("access.form.lecdn_allow_insecure_conns.switch.on")}
unCheckedChildren={t("access.form.lecdn_allow_insecure_conns.switch.off")}
checkedChildren={t("access.form.common_allow_insecure_conns.switch.on")}
unCheckedChildren={t("access.form.common_allow_insecure_conns.switch.off")}
/>
</Form.Item>
</Form>

View File

@@ -17,7 +17,7 @@ export type AccessFormPowerDNSConfigProps = {
const initFormModel = (): AccessFormPowerDNSConfigFieldValues => {
return {
apiUrl: "http://<your-host-addr>:8082/",
serverUrl: "http://<your-host-addr>:8082/",
apiKey: "",
};
};
@@ -26,7 +26,7 @@ const AccessFormPowerDNSConfig = ({ form: formInst, formName, disabled, initialV
const { t } = useTranslation();
const formSchema = z.object({
apiUrl: z.string().url(t("common.errmsg.url_invalid")),
serverUrl: z.string().url(t("common.errmsg.url_invalid")),
apiKey: z
.string()
.min(1, t("access.form.powerdns_api_key.placeholder"))
@@ -49,8 +49,8 @@ const AccessFormPowerDNSConfig = ({ form: formInst, formName, disabled, initialV
name={formName}
onValuesChange={handleFormChange}
>
<Form.Item name="apiUrl" label={t("access.form.powerdns_api_url.label")} rules={[formRule]}>
<Input placeholder={t("access.form.powerdns_api_url.placeholder")} />
<Form.Item name="serverUrl" label={t("access.form.powerdns_server_url.label")} rules={[formRule]}>
<Input placeholder={t("access.form.powerdns_server_url.placeholder")} />
</Form.Item>
<Form.Item
@@ -62,10 +62,10 @@ const AccessFormPowerDNSConfig = ({ form: formInst, formName, disabled, initialV
<Input.Password autoComplete="new-password" placeholder={t("access.form.powerdns_api_key.placeholder")} />
</Form.Item>
<Form.Item name="allowInsecureConnections" label={t("access.form.powerdns_allow_insecure_conns.label")} rules={[formRule]}>
<Form.Item name="allowInsecureConnections" label={t("access.form.common_allow_insecure_conns.label")} rules={[formRule]}>
<Switch
checkedChildren={t("access.form.powerdns_allow_insecure_conns.switch.on")}
unCheckedChildren={t("access.form.powerdns_allow_insecure_conns.switch.off")}
checkedChildren={t("access.form.common_allow_insecure_conns.switch.on")}
unCheckedChildren={t("access.form.common_allow_insecure_conns.switch.off")}
/>
</Form.Item>
</Form>

View File

@@ -17,7 +17,7 @@ export type AccessFormProxmoxVEConfigProps = {
const initFormModel = (): AccessFormProxmoxVEConfigFieldValues => {
return {
apiUrl: "http://<your-host-addr>:8006/",
serverUrl: "http://<your-host-addr>:8006/",
apiToken: "",
};
};
@@ -26,7 +26,7 @@ const AccessFormProxmoxVEConfig = ({ form: formInst, formName, disabled, initial
const { t } = useTranslation();
const formSchema = z.object({
apiUrl: z.string().url(t("common.errmsg.url_invalid")),
serverUrl: z.string().url(t("common.errmsg.url_invalid")),
apiToken: z.string().nonempty(t("access.form.proxmoxve_api_token.placeholder")).trim(),
apiTokenSecret: z.string().nullish(),
allowInsecureConnections: z.boolean().nullish(),
@@ -46,8 +46,8 @@ const AccessFormProxmoxVEConfig = ({ form: formInst, formName, disabled, initial
name={formName}
onValuesChange={handleFormChange}
>
<Form.Item name="apiUrl" label={t("access.form.proxmoxve_api_url.label")} rules={[formRule]}>
<Input placeholder={t("access.form.proxmoxve_api_url.placeholder")} />
<Form.Item name="serverUrl" label={t("access.form.proxmoxve_server_url.label")} rules={[formRule]}>
<Input placeholder={t("access.form.proxmoxve_server_url.placeholder")} />
</Form.Item>
<Form.Item
@@ -68,10 +68,10 @@ const AccessFormProxmoxVEConfig = ({ form: formInst, formName, disabled, initial
<Input.Password allowClear autoComplete="new-password" placeholder={t("access.form.proxmoxve_api_token_secret.placeholder")} />
</Form.Item>
<Form.Item name="allowInsecureConnections" label={t("access.form.proxmoxve_allow_insecure_conns.label")} rules={[formRule]}>
<Form.Item name="allowInsecureConnections" label={t("access.form.common_allow_insecure_conns.label")} rules={[formRule]}>
<Switch
checkedChildren={t("access.form.proxmoxve_allow_insecure_conns.switch.on")}
unCheckedChildren={t("access.form.proxmoxve_allow_insecure_conns.switch.off")}
checkedChildren={t("access.form.common_allow_insecure_conns.switch.on")}
unCheckedChildren={t("access.form.common_allow_insecure_conns.switch.off")}
/>
</Form.Item>
</Form>

View File

@@ -17,7 +17,7 @@ export type AccessFormRatPanelConfigProps = {
const initFormModel = (): AccessFormRatPanelConfigFieldValues => {
return {
apiUrl: "http://<your-host-addr>:8888/",
serverUrl: "http://<your-host-addr>:8888/",
accessTokenId: 1,
accessToken: "",
};
@@ -27,7 +27,7 @@ const AccessFormRatPanelConfig = ({ form: formInst, formName, disabled, initialV
const { t } = useTranslation();
const formSchema = z.object({
apiUrl: z.string().url(t("common.errmsg.url_invalid")),
serverUrl: z.string().url(t("common.errmsg.url_invalid")),
accessTokenId: z.preprocess((v) => Number(v), z.number().positive(t("access.form.ratpanel_access_token_id.placeholder"))),
accessToken: z.string().nonempty(t("access.form.ratpanel_access_token.placeholder")).trim(),
allowInsecureConnections: z.boolean().nullish(),
@@ -47,8 +47,8 @@ const AccessFormRatPanelConfig = ({ form: formInst, formName, disabled, initialV
name={formName}
onValuesChange={handleFormChange}
>
<Form.Item name="apiUrl" label={t("access.form.ratpanel_api_url.label")} rules={[formRule]}>
<Input placeholder={t("access.form.ratpanel_api_url.placeholder")} />
<Form.Item name="serverUrl" label={t("access.form.ratpanel_server_url.label")} rules={[formRule]}>
<Input placeholder={t("access.form.ratpanel_server_url.placeholder")} />
</Form.Item>
<Form.Item
@@ -69,10 +69,10 @@ const AccessFormRatPanelConfig = ({ form: formInst, formName, disabled, initialV
<Input.Password autoComplete="new-password" placeholder={t("access.form.ratpanel_access_token.placeholder")} />
</Form.Item>
<Form.Item name="allowInsecureConnections" label={t("access.form.ratpanel_allow_insecure_conns.label")} rules={[formRule]}>
<Form.Item name="allowInsecureConnections" label={t("access.form.common_allow_insecure_conns.label")} rules={[formRule]}>
<Switch
checkedChildren={t("access.form.ratpanel_allow_insecure_conns.switch.on")}
unCheckedChildren={t("access.form.ratpanel_allow_insecure_conns.switch.off")}
checkedChildren={t("access.form.common_allow_insecure_conns.switch.on")}
unCheckedChildren={t("access.form.common_allow_insecure_conns.switch.off")}
/>
</Form.Item>
</Form>

View File

@@ -17,7 +17,7 @@ export type AccessFormSafeLineConfigProps = {
const initFormModel = (): AccessFormSafeLineConfigFieldValues => {
return {
apiUrl: "http://<your-host-addr>:9443/",
serverUrl: "http://<your-host-addr>:9443/",
apiToken: "",
};
};
@@ -26,7 +26,7 @@ const AccessFormSafeLineConfig = ({ form: formInst, formName, disabled, initialV
const { t } = useTranslation();
const formSchema = z.object({
apiUrl: z.string().url(t("common.errmsg.url_invalid")),
serverUrl: z.string().url(t("common.errmsg.url_invalid")),
apiToken: z
.string()
.min(1, t("access.form.safeline_api_token.placeholder"))
@@ -49,8 +49,8 @@ const AccessFormSafeLineConfig = ({ form: formInst, formName, disabled, initialV
name={formName}
onValuesChange={handleFormChange}
>
<Form.Item name="apiUrl" label={t("access.form.safeline_api_url.label")} rules={[formRule]}>
<Input placeholder={t("access.form.safeline_api_url.placeholder")} />
<Form.Item name="serverUrl" label={t("access.form.safeline_server_url.label")} rules={[formRule]}>
<Input placeholder={t("access.form.safeline_server_url.placeholder")} />
</Form.Item>
<Form.Item
@@ -62,10 +62,10 @@ const AccessFormSafeLineConfig = ({ form: formInst, formName, disabled, initialV
<Input.Password autoComplete="new-password" placeholder={t("access.form.safeline_api_token.placeholder")} />
</Form.Item>
<Form.Item name="allowInsecureConnections" label={t("access.form.safeline_allow_insecure_conns.label")} rules={[formRule]}>
<Form.Item name="allowInsecureConnections" label={t("access.form.common_allow_insecure_conns.label")} rules={[formRule]}>
<Switch
checkedChildren={t("access.form.safeline_allow_insecure_conns.switch.on")}
unCheckedChildren={t("access.form.safeline_allow_insecure_conns.switch.off")}
checkedChildren={t("access.form.common_allow_insecure_conns.switch.on")}
unCheckedChildren={t("access.form.common_allow_insecure_conns.switch.off")}
/>
</Form.Item>
</Form>

View File

@@ -0,0 +1,71 @@
import { useTranslation } from "react-i18next";
import { Form, type FormInstance, Input } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import { type AccessConfigForSlackBot } from "@/domain/access";
type AccessFormSlackBotConfigFieldValues = Nullish<AccessConfigForSlackBot>;
export type AccessFormSlackBotConfigProps = {
form: FormInstance;
formName: string;
disabled?: boolean;
initialValues?: AccessFormSlackBotConfigFieldValues;
onValuesChange?: (values: AccessFormSlackBotConfigFieldValues) => void;
};
const initFormModel = (): AccessFormSlackBotConfigFieldValues => {
return {
botToken: "",
};
};
const AccessFormSlackBotConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: AccessFormSlackBotConfigProps) => {
const { t } = useTranslation();
const formSchema = z.object({
botToken: z
.string({ message: t("access.form.slackbot_token.placeholder") })
.min(1, t("access.form.slackbot_token.placeholder"))
.max(256, t("common.errmsg.string_max", { max: 256 }))
.trim(),
defaultChannelId: z.string().nullish(),
});
const formRule = createSchemaFieldRule(formSchema);
const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => {
onValuesChange?.(values);
};
return (
<Form
form={formInst}
disabled={disabled}
initialValues={initialValues ?? initFormModel()}
layout="vertical"
name={formName}
onValuesChange={handleFormChange}
>
<Form.Item
name="botToken"
label={t("access.form.slackbot_token.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.slackbot_token.tooltip") }}></span>}
>
<Input.Password autoComplete="new-password" placeholder={t("access.form.slackbot_token.placeholder")} />
</Form.Item>
<Form.Item
name="defaultChannelId"
label={t("access.form.slackbot_default_channel_id.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.slackbot_default_channel_id.tooltip") }}></span>}
>
<Input allowClear placeholder={t("access.form.slackbot_default_channel_id.placeholder")} />
</Form.Item>
</Form>
);
};
export default AccessFormSlackBotConfig;

View File

@@ -26,9 +26,10 @@ const AccessFormTelegramBotConfig = ({ form: formInst, formName, disabled, initi
const formSchema = z.object({
botToken: z
.string({ message: t("access.form.telegram_bot_token.placeholder") })
.min(1, t("access.form.telegram_bot_token.placeholder"))
.max(256, t("common.errmsg.string_max", { max: 256 })),
.string({ message: t("access.form.telegrambot_token.placeholder") })
.min(1, t("access.form.telegrambot_token.placeholder"))
.max(256, t("common.errmsg.string_max", { max: 256 }))
.trim(),
defaultChatId: z
.preprocess(
(v) => (v == null || v === "" ? undefined : Number(v)),
@@ -38,7 +39,7 @@ const AccessFormTelegramBotConfig = ({ form: formInst, formName, disabled, initi
.refine((v) => {
if (v == null || v + "" === "") return true;
return !Number.isNaN(+v!) && +v! !== 0;
}, t("access.form.telegram_bot_default_chat_id.placeholder"))
}, t("access.form.telegrambot_default_chat_id.placeholder"))
)
.nullish(),
});
@@ -59,20 +60,20 @@ const AccessFormTelegramBotConfig = ({ form: formInst, formName, disabled, initi
>
<Form.Item
name="botToken"
label={t("access.form.telegram_bot_token.label")}
label={t("access.form.telegrambot_token.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.telegram_bot_token.tooltip") }}></span>}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.telegrambot_token.tooltip") }}></span>}
>
<Input.Password autoComplete="new-password" placeholder={t("access.form.telegram_bot_token.placeholder")} />
<Input.Password autoComplete="new-password" placeholder={t("access.form.telegrambot_token.placeholder")} />
</Form.Item>
<Form.Item
name="defaultChatId"
label={t("access.form.telegram_bot_default_chat_id.label")}
label={t("access.form.telegrambot_default_chat_id.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.telegram_bot_default_chat_id.tooltip") }}></span>}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.telegrambot_default_chat_id.tooltip") }}></span>}
>
<Input allowClear placeholder={t("access.form.telegram_bot_default_chat_id.placeholder")} />
<Input allowClear placeholder={t("access.form.telegrambot_default_chat_id.placeholder")} />
</Form.Item>
</Form>
);

View File

@@ -0,0 +1,68 @@
import { useTranslation } from "react-i18next";
import { Form, type FormInstance, Input } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import { type AccessConfigForUniCloud } from "@/domain/access";
type AccessFormUniCloudConfigFieldValues = Nullish<AccessConfigForUniCloud>;
export type AccessFormUniCloudConfigProps = {
form: FormInstance;
formName: string;
disabled?: boolean;
initialValues?: AccessFormUniCloudConfigFieldValues;
onValuesChange?: (values: AccessFormUniCloudConfigFieldValues) => void;
};
const initFormModel = (): AccessFormUniCloudConfigFieldValues => {
return {
username: "",
password: "",
};
};
const AccessFormUniCloudConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: AccessFormUniCloudConfigProps) => {
const { t } = useTranslation();
const formSchema = z.object({
username: z.string().trim().nonempty(t("access.form.unicloud_username.placeholder")),
password: z.string().trim().nonempty(t("access.form.unicloud_password.placeholder")),
});
const formRule = createSchemaFieldRule(formSchema);
const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => {
onValuesChange?.(values);
};
return (
<Form
form={formInst}
disabled={disabled}
initialValues={initialValues ?? initFormModel()}
layout="vertical"
name={formName}
onValuesChange={handleFormChange}
>
<Form.Item
name="username"
label={t("access.form.unicloud_username.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.unicloud_username.tooltip") }}></span>}
>
<Input autoComplete="new-password" placeholder={t("access.form.unicloud_username.placeholder")} />
</Form.Item>
<Form.Item
name="password"
label={t("access.form.unicloud_password.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.unicloud_password.tooltip") }}></span>}
>
<Input.Password autoComplete="new-password" placeholder={t("access.form.unicloud_password.placeholder")} />
</Form.Item>
</Form>
);
};
export default AccessFormUniCloudConfig;

View File

@@ -33,9 +33,9 @@ const AccessFormUpyunConfig = ({ form: formInst, formName, disabled, initialValu
.max(64, t("common.errmsg.string_max", { max: 64 })),
password: z
.string()
.trim()
.min(1, t("access.form.upyun_password.placeholder"))
.max(64, t("common.errmsg.string_max", { max: 64 }))
.trim(),
.max(64, t("common.errmsg.string_max", { max: 64 })),
});
const formRule = createSchemaFieldRule(formSchema);

View File

@@ -362,10 +362,10 @@ const AccessFormWebhookConfig = ({ form: formInst, formName, disabled, initialVa
</Form.Item>
</Show>
<Form.Item name="allowInsecureConnections" label={t("access.form.webhook_allow_insecure_conns.label")} rules={[formRule]}>
<Form.Item name="allowInsecureConnections" label={t("access.form.common_allow_insecure_conns.label")} rules={[formRule]}>
<Switch
checkedChildren={t("access.form.webhook_allow_insecure_conns.switch.on")}
unCheckedChildren={t("access.form.webhook_allow_insecure_conns.switch.off")}
checkedChildren={t("access.form.common_allow_insecure_conns.switch.on")}
unCheckedChildren={t("access.form.common_allow_insecure_conns.switch.off")}
/>
</Form.Item>
</Form>

View File

@@ -1,12 +1,7 @@
import { forwardRef, memo, useEffect, useImperativeHandle, useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
import { Link } from "react-router";
import {
FormOutlined as FormOutlinedIcon,
PlusOutlined as PlusOutlinedIcon,
QuestionCircleOutlined as QuestionCircleOutlinedIcon,
RightOutlined as RightOutlinedIcon,
} from "@ant-design/icons";
import { PlusOutlined as PlusOutlinedIcon, QuestionCircleOutlined as QuestionCircleOutlinedIcon, RightOutlined as RightOutlinedIcon } from "@ant-design/icons";
import { useControllableValue } from "ahooks";
import {
AutoComplete,
@@ -19,7 +14,6 @@ import {
Input,
InputNumber,
Select,
Space,
Switch,
Tooltip,
Typography,
@@ -29,8 +23,7 @@ import { z } from "zod";
import AccessEditModal from "@/components/access/AccessEditModal";
import AccessSelect from "@/components/access/AccessSelect";
import ModalForm from "@/components/ModalForm";
import MultipleInput from "@/components/MultipleInput";
import MultipleSplitValueInput from "@/components/MultipleSplitValueInput";
import ACMEDns01ProviderSelect from "@/components/provider/ACMEDns01ProviderSelect";
import CAProviderSelect from "@/components/provider/CAProviderSelect";
import Show from "@/components/Show";
@@ -152,11 +145,9 @@ const ApplyNodeConfigForm = forwardRef<ApplyNodeConfigFormInstance, ApplyNodeCon
initialValues: initialValues ?? initFormModel(),
});
const fieldDomains = Form.useWatch<string>("domains", formInst);
const fieldProvider = Form.useWatch<string>("provider", { form: formInst, preserve: true });
const fieldProviderAccessId = Form.useWatch<string>("providerAccessId", formInst);
const fieldCAProvider = Form.useWatch<string>("caProvider", formInst);
const fieldNameservers = Form.useWatch<string>("nameservers", formInst);
const [showProvider, setShowProvider] = useState(false);
useEffect(() => {
@@ -294,25 +285,17 @@ const ApplyNodeConfigForm = forwardRef<ApplyNodeConfigFormInstance, ApplyNodeCon
<Form.Provider onFormChange={handleFormProviderChange}>
<Form className={className} style={style} {...formProps} disabled={disabled} layout="vertical" scrollToFirstError onValuesChange={handleFormChange}>
<Form.Item
name="domains"
label={t("workflow_node.apply.form.domains.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.apply.form.domains.tooltip") }}></span>}
>
<Space.Compact style={{ width: "100%" }}>
<Form.Item name="domains" noStyle rules={[formRule]}>
<Input placeholder={t("workflow_node.apply.form.domains.placeholder")} />
</Form.Item>
<DomainsModalInput
value={fieldDomains}
trigger={
<Button disabled={disabled}>
<FormOutlinedIcon />
</Button>
}
onChange={(v) => {
formInst.setFieldValue("domains", v);
}}
/>
</Space.Compact>
<MultipleSplitValueInput
modalTitle={t("workflow_node.apply.form.domains.multiple_input_modal.title")}
placeholder={t("workflow_node.apply.form.domains.placeholder")}
placeholderInModal={t("workflow_node.apply.form.domains.multiple_input_modal.placeholder")}
splitOptions={{ trim: true, removeEmpty: true }}
/>
</Form.Item>
<Form.Item
@@ -497,36 +480,17 @@ const ApplyNodeConfigForm = forwardRef<ApplyNodeConfigFormInstance, ApplyNodeCon
<Form className={className} style={style} {...formProps} disabled={disabled} layout="vertical" scrollToFirstError onValuesChange={handleFormChange}>
<Form.Item
name="nameservers"
label={t("workflow_node.apply.form.nameservers.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.apply.form.nameservers.tooltip") }}></span>}
>
<Space.Compact style={{ width: "100%" }}>
<Form.Item name="nameservers" noStyle rules={[formRule]}>
<Input
allowClear
disabled={disabled}
value={fieldNameservers}
placeholder={t("workflow_node.apply.form.nameservers.placeholder")}
onChange={(e) => {
formInst.setFieldValue("nameservers", e.target.value);
}}
onClear={() => {
formInst.setFieldValue("nameservers", undefined);
}}
/>
</Form.Item>
<NameserversModalInput
value={fieldNameservers}
trigger={
<Button disabled={disabled}>
<FormOutlinedIcon />
</Button>
}
onChange={(value) => {
formInst.setFieldValue("nameservers", value);
}}
/>
</Space.Compact>
<MultipleSplitValueInput
modalTitle={t("workflow_node.apply.form.nameservers.multiple_input_modal.title")}
placeholder={t("workflow_node.apply.form.nameservers.placeholder")}
placeholderInModal={t("workflow_node.apply.form.nameservers.multiple_input_modal.placeholder")}
splitOptions={{ trim: true, removeEmpty: true }}
/>
</Form.Item>
<Form.Item
@@ -678,84 +642,4 @@ const EmailInput = memo(
}
);
const DomainsModalInput = memo(({ value, trigger, onChange }: { value?: string; trigger?: React.ReactNode; onChange?: (value: string) => void }) => {
const { t } = useTranslation();
const formSchema = z.object({
domains: z.array(z.string()).refine((v) => {
return v.every((e) => !e?.trim() || validDomainName(e.trim(), { allowWildcard: true }));
}, t("common.errmsg.domain_invalid")),
});
const formRule = createSchemaFieldRule(formSchema);
const { form: formInst, formProps } = useAntdForm({
name: "workflowNodeApplyConfigFormDomainsModalInput",
initialValues: { domains: value?.split(MULTIPLE_INPUT_DELIMITER) },
onSubmit: (values) => {
onChange?.(
values.domains
.map((e) => e.trim())
.filter((e) => !!e)
.join(MULTIPLE_INPUT_DELIMITER)
);
},
});
return (
<ModalForm
{...formProps}
layout="vertical"
form={formInst}
modalProps={{ destroyOnHidden: true }}
title={t("workflow_node.apply.form.domains.multiple_input_modal.title")}
trigger={trigger}
validateTrigger="onSubmit"
width={480}
>
<Form.Item name="domains" rules={[formRule]}>
<MultipleInput placeholder={t("workflow_node.apply.form.domains.multiple_input_modal.placeholder")} />
</Form.Item>
</ModalForm>
);
});
const NameserversModalInput = memo(({ trigger, value, onChange }: { trigger?: React.ReactNode; value?: string; onChange?: (value: string) => void }) => {
const { t } = useTranslation();
const formSchema = z.object({
nameservers: z.array(z.string()).refine((v) => {
return v.every((e) => !e?.trim() || validIPv4Address(e) || validIPv6Address(e) || validDomainName(e));
}, t("common.errmsg.domain_invalid")),
});
const formRule = createSchemaFieldRule(formSchema);
const { form: formInst, formProps } = useAntdForm({
name: "workflowNodeApplyConfigFormNameserversModalInput",
initialValues: { nameservers: value?.split(MULTIPLE_INPUT_DELIMITER) },
onSubmit: (values) => {
onChange?.(
values.nameservers
.map((e) => e.trim())
.filter((e) => !!e)
.join(MULTIPLE_INPUT_DELIMITER)
);
},
});
return (
<ModalForm
{...formProps}
layout="vertical"
form={formInst}
modalProps={{ destroyOnHidden: true }}
title={t("workflow_node.apply.form.nameservers.multiple_input_modal.title")}
trigger={trigger}
validateTrigger="onSubmit"
width={480}
>
<Form.Item name="nameservers" rules={[formRule]}>
<MultipleInput placeholder={t("workflow_node.apply.form.nameservers.multiple_input_modal.placeholder")} />
</Form.Item>
</ModalForm>
);
});
export default memo(ApplyNodeConfigForm);

View File

@@ -82,6 +82,7 @@ import DeployNodeConfigFormTencentCloudVODConfig from "./DeployNodeConfigFormTen
import DeployNodeConfigFormTencentCloudWAFConfig from "./DeployNodeConfigFormTencentCloudWAFConfig";
import DeployNodeConfigFormUCloudUCDNConfig from "./DeployNodeConfigFormUCloudUCDNConfig.tsx";
import DeployNodeConfigFormUCloudUS3Config from "./DeployNodeConfigFormUCloudUS3Config.tsx";
import DeployNodeConfigFormUniCloudWebHostConfig from "./DeployNodeConfigFormUniCloudWebHostConfig.tsx";
import DeployNodeConfigFormUpyunCDNConfig from "./DeployNodeConfigFormUpyunCDNConfig.tsx";
import DeployNodeConfigFormUpyunFileConfig from "./DeployNodeConfigFormUpyunFileConfig.tsx";
import DeployNodeConfigFormVolcEngineALBConfig from "./DeployNodeConfigFormVolcEngineALBConfig.tsx";
@@ -318,6 +319,8 @@ const DeployNodeConfigForm = forwardRef<DeployNodeConfigFormInstance, DeployNode
return <DeployNodeConfigFormUCloudUCDNConfig {...nestedFormProps} />;
case DEPLOYMENT_PROVIDERS.UCLOUD_US3:
return <DeployNodeConfigFormUCloudUS3Config {...nestedFormProps} />;
case DEPLOYMENT_PROVIDERS.UNICLOUD_WEBHOST:
return <DeployNodeConfigFormUniCloudWebHostConfig {...nestedFormProps} />;
case DEPLOYMENT_PROVIDERS.UPYUN_CDN:
return <DeployNodeConfigFormUpyunCDNConfig {...nestedFormProps} />;
case DEPLOYMENT_PROVIDERS.UPYUN_FILE:

View File

@@ -1,13 +1,9 @@
import { memo } from "react";
import { useTranslation } from "react-i18next";
import { FormOutlined as FormOutlinedIcon } from "@ant-design/icons";
import { Alert, Button, Form, type FormInstance, Input, Space } from "antd";
import { Alert, Form, type FormInstance, Input } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import ModalForm from "@/components/ModalForm";
import MultipleInput from "@/components/MultipleInput";
import { useAntdForm } from "@/hooks";
import MultipleSplitValueInput from "@/components/MultipleSplitValueInput";
type DeployNodeConfigFormAliyunCASDeployConfigFieldValues = Nullish<{
region: string;
@@ -61,9 +57,6 @@ const DeployNodeConfigFormAliyunCASDeployConfig = ({
});
const formRule = createSchemaFieldRule(formSchema);
const fieldResourceIds = Form.useWatch<string>("resourceIds", formInst);
const fieldContactIds = Form.useWatch<string>("contactIds", formInst);
const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => {
onValuesChange?.(values);
};
@@ -87,69 +80,31 @@ const DeployNodeConfigFormAliyunCASDeployConfig = ({
</Form.Item>
<Form.Item
name="resourceIds"
label={t("workflow_node.deploy.form.aliyun_cas_deploy_resource_ids.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aliyun_cas_deploy_resource_ids.tooltip") }}></span>}
>
<Space.Compact style={{ width: "100%" }}>
<Form.Item name="resourceIds" noStyle rules={[formRule]}>
<Input
allowClear
disabled={disabled}
value={fieldResourceIds}
placeholder={t("workflow_node.deploy.form.aliyun_cas_deploy_resource_ids.placeholder")}
onChange={(e) => {
formInst.setFieldValue("resourceIds", e.target.value);
}}
onClear={() => {
formInst.setFieldValue("resourceIds", "");
}}
/>
</Form.Item>
<ResourceIdsModalInput
value={fieldResourceIds}
trigger={
<Button disabled={disabled}>
<FormOutlinedIcon />
</Button>
}
onChange={(value) => {
formInst.setFieldValue("resourceIds", value);
}}
/>
</Space.Compact>
<MultipleSplitValueInput
modalTitle={t("workflow_node.deploy.form.aliyun_cas_deploy_resource_ids.multiple_input_modal.title")}
placeholder={t("workflow_node.deploy.form.aliyun_cas_deploy_resource_ids.placeholder")}
placeholderInModal={t("workflow_node.deploy.form.aliyun_cas_deploy_resource_ids.multiple_input_modal.placeholder")}
splitOptions={{ trim: true, removeEmpty: true }}
/>
</Form.Item>
<Form.Item
name="contactIds"
label={t("workflow_node.deploy.form.aliyun_cas_deploy_contact_ids.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aliyun_cas_deploy_contact_ids.tooltip") }}></span>}
>
<Space.Compact style={{ width: "100%" }}>
<Form.Item name="contactIds" noStyle rules={[formRule]}>
<Input
allowClear
disabled={disabled}
value={fieldContactIds}
placeholder={t("workflow_node.deploy.form.aliyun_cas_deploy_contact_ids.placeholder")}
onChange={(e) => {
formInst.setFieldValue("contactIds", e.target.value);
}}
onClear={() => {
formInst.setFieldValue("contactIds", "");
}}
/>
</Form.Item>
<ContactIdsModalInput
value={fieldContactIds}
trigger={
<Button disabled={disabled}>
<FormOutlinedIcon />
</Button>
}
onChange={(value) => {
formInst.setFieldValue("contactIds", value);
}}
/>
</Space.Compact>
<MultipleSplitValueInput
modalTitle={t("workflow_node.deploy.form.aliyun_cas_deploy_contact_ids.multiple_input_modal.title")}
placeholder={t("workflow_node.deploy.form.aliyun_cas_deploy_contact_ids.placeholder")}
placeholderInModal={t("workflow_node.deploy.form.aliyun_cas_deploy_contact_ids.multiple_input_modal.placeholder")}
splitOptions={{ trim: true, removeEmpty: true }}
/>
</Form.Item>
<Form.Item>
@@ -159,84 +114,4 @@ const DeployNodeConfigFormAliyunCASDeployConfig = ({
);
};
const ResourceIdsModalInput = memo(({ value, trigger, onChange }: { value?: string; trigger?: React.ReactNode; onChange?: (value: string) => void }) => {
const { t } = useTranslation();
const formSchema = z.object({
resourceIds: z.array(z.string()).refine((v) => {
return v.every((e) => !e?.trim() || /^[1-9]\d*$/.test(e));
}, t("workflow_node.deploy.form.aliyun_cas_deploy_resource_ids.errmsg.invalid")),
});
const formRule = createSchemaFieldRule(formSchema);
const { form: formInst, formProps } = useAntdForm({
name: "workflowNodeDeployConfigFormAliyunCASResourceIdsModalInput",
initialValues: { resourceIds: value?.split(MULTIPLE_INPUT_DELIMITER) },
onSubmit: (values) => {
onChange?.(
values.resourceIds
.map((e) => e.trim())
.filter((e) => !!e)
.join(MULTIPLE_INPUT_DELIMITER)
);
},
});
return (
<ModalForm
{...formProps}
layout="vertical"
form={formInst}
modalProps={{ destroyOnHidden: true }}
title={t("workflow_node.deploy.form.aliyun_cas_deploy_resource_ids.multiple_input_modal.title")}
trigger={trigger}
validateTrigger="onSubmit"
width={480}
>
<Form.Item name="resourceIds" rules={[formRule]}>
<MultipleInput placeholder={t("workflow_node.deploy.form.aliyun_cas_deploy_resource_ids.multiple_input_modal.placeholder")} />
</Form.Item>
</ModalForm>
);
});
const ContactIdsModalInput = memo(({ value, trigger, onChange }: { value?: string; trigger?: React.ReactNode; onChange?: (value: string) => void }) => {
const { t } = useTranslation();
const formSchema = z.object({
contactIds: z.array(z.string()).refine((v) => {
return v.every((e) => !e?.trim() || /^[1-9]\d*$/.test(e));
}, t("workflow_node.deploy.form.aliyun_cas_deploy_contact_ids.errmsg.invalid")),
});
const formRule = createSchemaFieldRule(formSchema);
const { form: formInst, formProps } = useAntdForm({
name: "workflowNodeDeployConfigFormAliyunCASDeploymentJobContactIdsModalInput",
initialValues: { contactIds: value?.split(MULTIPLE_INPUT_DELIMITER) },
onSubmit: (values) => {
onChange?.(
values.contactIds
.map((e) => e.trim())
.filter((e) => !!e)
.join(MULTIPLE_INPUT_DELIMITER)
);
},
});
return (
<ModalForm
{...formProps}
layout="vertical"
form={formInst}
modalProps={{ destroyOnHidden: true }}
title={t("workflow_node.deploy.form.aliyun_cas_deploy_contact_ids.multiple_input_modal.title")}
trigger={trigger}
validateTrigger="onSubmit"
width={480}
>
<Form.Item name="contactIds" rules={[formRule]}>
<MultipleInput placeholder={t("workflow_node.deploy.form.aliyun_cas_deploy_contact_ids.multiple_input_modal.placeholder")} />
</Form.Item>
</ModalForm>
);
});
export default DeployNodeConfigFormAliyunCASDeployConfig;

View File

@@ -1,14 +1,10 @@
import { memo } from "react";
import { useTranslation } from "react-i18next";
import { FormOutlined as FormOutlinedIcon } from "@ant-design/icons";
import { Button, Form, type FormInstance, Input, Select, Space } from "antd";
import { Form, type FormInstance, Input, Select } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import ModalForm from "@/components/ModalForm";
import MultipleInput from "@/components/MultipleInput";
import MultipleSplitValueInput from "@/components/MultipleSplitValueInput";
import Show from "@/components/Show";
import { useAntdForm } from "@/hooks";
type DeployNodeConfigFormBaotaPanelSiteConfigFieldValues = Nullish<{
siteType: string;
@@ -71,7 +67,6 @@ const DeployNodeConfigFormBaotaPanelSiteConfig = ({
const formRule = createSchemaFieldRule(formSchema);
const fieldSiteType = Form.useWatch<string>("siteType", formInst);
const fieldSiteNames = Form.useWatch<string>("siteNames", formInst);
const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => {
onValuesChange?.(values);
@@ -110,80 +105,21 @@ const DeployNodeConfigFormBaotaPanelSiteConfig = ({
<Show when={fieldSiteType === SITE_TYPE_OTHER}>
<Form.Item
name="siteNames"
label={t("workflow_node.deploy.form.baotapanel_site_names.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.baotapanel_site_names.tooltip") }}></span>}
>
<Space.Compact style={{ width: "100%" }}>
<Form.Item name="siteNames" noStyle rules={[formRule]}>
<Input
allowClear
disabled={disabled}
value={fieldSiteNames}
placeholder={t("workflow_node.deploy.form.baotapanel_site_names.placeholder")}
onChange={(e) => {
formInst.setFieldValue("siteNames", e.target.value);
}}
onClear={() => {
formInst.setFieldValue("siteNames", "");
}}
/>
</Form.Item>
<SiteNamesModalInput
value={fieldSiteNames}
trigger={
<Button disabled={disabled}>
<FormOutlinedIcon />
</Button>
}
onChange={(value) => {
formInst.setFieldValue("siteNames", value);
}}
/>
</Space.Compact>
<MultipleSplitValueInput
modalTitle={t("workflow_node.deploy.form.baotapanel_site_names.multiple_input_modal.title")}
placeholder={t("workflow_node.deploy.form.baotapanel_site_names.placeholder")}
placeholderInModal={t("workflow_node.deploy.form.baotapanel_site_names.multiple_input_modal.placeholder")}
splitOptions={{ trim: true, removeEmpty: true }}
/>
</Form.Item>
</Show>
</Form>
);
};
const SiteNamesModalInput = memo(({ value, trigger, onChange }: { value?: string; trigger?: React.ReactNode; onChange?: (value: string) => void }) => {
const { t } = useTranslation();
const formSchema = z.object({
siteNames: z.array(z.string()).refine((v) => {
return v.every((e) => !!e?.trim());
}, t("workflow_node.deploy.form.baotapanel_site_names.errmsg.invalid")),
});
const formRule = createSchemaFieldRule(formSchema);
const { form: formInst, formProps } = useAntdForm({
name: "workflowNodeDeployConfigFormBaotaPanelSiteNamesModalInput",
initialValues: { siteNames: value?.split(MULTIPLE_INPUT_DELIMITER) },
onSubmit: (values) => {
onChange?.(
values.siteNames
.map((e) => e.trim())
.filter((e) => !!e)
.join(MULTIPLE_INPUT_DELIMITER)
);
},
});
return (
<ModalForm
{...formProps}
layout="vertical"
form={formInst}
modalProps={{ destroyOnHidden: true }}
title={t("workflow_node.deploy.form.baotapanel_site_names.multiple_input_modal.title")}
trigger={trigger}
validateTrigger="onSubmit"
width={480}
>
<Form.Item name="siteNames" rules={[formRule]}>
<MultipleInput placeholder={t("workflow_node.deploy.form.baotapanel_site_names.multiple_input_modal.placeholder")} />
</Form.Item>
</ModalForm>
);
});
export default DeployNodeConfigFormBaotaPanelSiteConfig;

View File

@@ -1,13 +1,9 @@
import { memo } from "react";
import { useTranslation } from "react-i18next";
import { FormOutlined as FormOutlinedIcon } from "@ant-design/icons";
import { Alert, AutoComplete, Button, Form, type FormInstance, Input, Space } from "antd";
import { Alert, AutoComplete, Form, type FormInstance, Input } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import ModalForm from "@/components/ModalForm";
import MultipleInput from "@/components/MultipleInput";
import { useAntdForm } from "@/hooks";
import MultipleSplitValueInput from "@/components/MultipleSplitValueInput";
type DeployNodeConfigFormTencentCloudSSLDeployConfigFieldValues = Nullish<{
region: string;
@@ -56,8 +52,6 @@ const DeployNodeConfigFormTencentCloudSSLDeployConfig = ({
});
const formRule = createSchemaFieldRule(formSchema);
const fieldResourceIds = Form.useWatch<string>("resourceIds", formInst);
const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => {
onValuesChange?.(values);
};
@@ -94,36 +88,17 @@ const DeployNodeConfigFormTencentCloudSSLDeployConfig = ({
</Form.Item>
<Form.Item
name="resourceIds"
label={t("workflow_node.deploy.form.tencentcloud_ssl_deploy_resource_ids.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.tencentcloud_ssl_deploy_resource_ids.tooltip") }}></span>}
>
<Space.Compact style={{ width: "100%" }}>
<Form.Item name="resourceIds" noStyle rules={[formRule]}>
<Input
allowClear
disabled={disabled}
value={fieldResourceIds}
placeholder={t("workflow_node.deploy.form.tencentcloud_ssl_deploy_resource_ids.placeholder")}
onChange={(e) => {
formInst.setFieldValue("resourceIds", e.target.value);
}}
onClear={() => {
formInst.setFieldValue("resourceIds", "");
}}
/>
</Form.Item>
<ResourceIdsModalInput
value={fieldResourceIds}
trigger={
<Button disabled={disabled}>
<FormOutlinedIcon />
</Button>
}
onChange={(value) => {
formInst.setFieldValue("resourceIds", value);
}}
/>
</Space.Compact>
<MultipleSplitValueInput
modalTitle={t("workflow_node.deploy.form.tencentcloud_ssl_deploy_resource_ids.multiple_input_modal.title")}
placeholder={t("workflow_node.deploy.form.tencentcloud_ssl_deploy_resource_ids.placeholder")}
placeholderInModal={t("workflow_node.deploy.form.tencentcloud_ssl_deploy_resource_ids.multiple_input_modal.placeholder")}
splitOptions={{ trim: true, removeEmpty: true }}
/>
</Form.Item>
<Form.Item>
@@ -133,44 +108,4 @@ const DeployNodeConfigFormTencentCloudSSLDeployConfig = ({
);
};
const ResourceIdsModalInput = memo(({ value, trigger, onChange }: { value?: string; trigger?: React.ReactNode; onChange?: (value: string) => void }) => {
const { t } = useTranslation();
const formSchema = z.object({
resourceIds: z.array(z.string()).refine((v) => {
return v.every((e) => !e?.trim() || /^[A-Za-z0-9*._-|]+$/.test(e));
}, t("workflow_node.deploy.form.tencentcloud_ssl_deploy_resource_ids.errmsg.invalid")),
});
const formRule = createSchemaFieldRule(formSchema);
const { form: formInst, formProps } = useAntdForm({
name: "workflowNodeDeployConfigFormTencentCloudSSLDeployResourceIdsModalInput",
initialValues: { resourceIds: value?.split(MULTIPLE_INPUT_DELIMITER) },
onSubmit: (values) => {
onChange?.(
values.resourceIds
.map((e) => e.trim())
.filter((e) => !!e)
.join(MULTIPLE_INPUT_DELIMITER)
);
},
});
return (
<ModalForm
{...formProps}
layout="vertical"
form={formInst}
modalProps={{ destroyOnHidden: true }}
title={t("workflow_node.deploy.form.tencentcloud_ssl_deploy_resource_ids.multiple_input_modal.title")}
trigger={trigger}
validateTrigger="onSubmit"
width={480}
>
<Form.Item name="resourceIds" rules={[formRule]}>
<MultipleInput placeholder={t("workflow_node.deploy.form.tencentcloud_ssl_deploy_resource_ids.multiple_input_modal.placeholder")} />
</Form.Item>
</ModalForm>
);
});
export default DeployNodeConfigFormTencentCloudSSLDeployConfig;

View File

@@ -0,0 +1,85 @@
import { useTranslation } from "react-i18next";
import { Form, type FormInstance, Input, Select } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import { validDomainName } from "@/utils/validators";
type DeployNodeConfigFormUniCloudWebHostConfigFieldValues = Nullish<{
spaceProvider: string;
spaceId: string;
domain: string;
}>;
export type DeployNodeConfigFormUniCloudWebHostConfigProps = {
form: FormInstance;
formName: string;
disabled?: boolean;
initialValues?: DeployNodeConfigFormUniCloudWebHostConfigFieldValues;
onValuesChange?: (values: DeployNodeConfigFormUniCloudWebHostConfigFieldValues) => void;
};
const initFormModel = (): DeployNodeConfigFormUniCloudWebHostConfigFieldValues => {
return {
spaceProvider: "tencent",
spaceId: "",
domain: "",
};
};
const DeployNodeConfigFormUniCloudWebHostConfig = ({
form: formInst,
formName,
disabled,
initialValues,
onValuesChange,
}: DeployNodeConfigFormUniCloudWebHostConfigProps) => {
const { t } = useTranslation();
const formSchema = z.object({
spaceProvider: z.string().trim().nonempty(t("workflow_node.deploy.form.unicloud_webhost_space_provider.placeholder")),
spaceId: z.string().trim().nonempty(t("workflow_node.deploy.form.unicloud_webhost_space_id.placeholder")),
domain: z.string().refine((v) => validDomainName(v), t("common.errmsg.domain_invalid")),
});
const formRule = createSchemaFieldRule(formSchema);
const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => {
onValuesChange?.(values);
};
return (
<Form
form={formInst}
disabled={disabled}
initialValues={initialValues ?? initFormModel()}
layout="vertical"
name={formName}
onValuesChange={handleFormChange}
>
<Form.Item name="spaceProvider" label={t("workflow_node.deploy.form.unicloud_webhost_space_provider.label")} rules={[formRule]}>
<Select
options={["aliyun", "tencent"].map((s) => ({
label: t(`workflow_node.deploy.form.unicloud_webhost_space_provider.option.${s}.label`),
value: s,
}))}
placeholder={t("workflow_node.deploy.form.unicloud_webhost_space_provider.placeholder")}
/>
</Form.Item>
<Form.Item
name="spaceId"
label={t("workflow_node.deploy.form.unicloud_webhost_space_id.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.unicloud_webhost_space_id.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.unicloud_webhost_space_id.placeholder")} />
</Form.Item>
<Form.Item name="domain" label={t("workflow_node.deploy.form.unicloud_webhost_domain.label")} rules={[formRule]}>
<Input placeholder={t("workflow_node.deploy.form.unicloud_webhost_domain.placeholder")} />
</Form.Item>
</Form>
);
};
export default DeployNodeConfigFormUniCloudWebHostConfig;

View File

@@ -1,5 +1,5 @@
import { useTranslation } from "react-i18next";
import { Form, type FormInstance, Input } from "antd";
import { Alert, Form, type FormInstance, Input } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
@@ -44,6 +44,10 @@ const DeployNodeConfigFormUpyunCDNConfig = ({ form: formInst, formName, disabled
name={formName}
onValuesChange={handleFormChange}
>
<Form.Item>
<Alert type="info" message={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.upyun_cdn.guide") }}></span>} />
</Form.Item>
<Form.Item
name="domain"
label={t("workflow_node.deploy.form.upyun_cdn_domain.label")}

View File

@@ -1,5 +1,5 @@
import { useTranslation } from "react-i18next";
import { Form, type FormInstance, Input } from "antd";
import { Alert, Form, type FormInstance, Input } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
@@ -50,6 +50,10 @@ const DeployNodeConfigFormUpyunFileConfig = ({
name={formName}
onValuesChange={handleFormChange}
>
<Form.Item>
<Alert type="info" message={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.upyun_file.guide") }}></span>} />
</Form.Item>
<Form.Item
name="domain"
label={t("workflow_node.deploy.form.upyun_file_domain.label")}

View File

@@ -1,13 +1,9 @@
import { memo } from "react";
import { useTranslation } from "react-i18next";
import { FormOutlined as FormOutlinedIcon } from "@ant-design/icons";
import { Button, Form, type FormInstance, Input, Space } from "antd";
import { Form, type FormInstance } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import ModalForm from "@/components/ModalForm";
import MultipleInput from "@/components/MultipleInput";
import { useAntdForm } from "@/hooks";
import MultipleSplitValueInput from "@/components/MultipleSplitValueInput";
import { validDomainName } from "@/utils/validators";
type DeployNodeConfigFormWangsuCDNConfigFieldValues = Nullish<{
@@ -52,8 +48,6 @@ const DeployNodeConfigFormWangsuCDNConfig = ({
});
const formRule = createSchemaFieldRule(formSchema);
const fieldDomains = Form.useWatch<string>("domains", formInst);
const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => {
onValuesChange?.(values);
};
@@ -68,79 +62,20 @@ const DeployNodeConfigFormWangsuCDNConfig = ({
onValuesChange={handleFormChange}
>
<Form.Item
name="domains"
label={t("workflow_node.deploy.form.wangsu_cdn_domains.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.wangsu_cdn_domains.tooltip") }}></span>}
>
<Space.Compact style={{ width: "100%" }}>
<Form.Item name="domains" noStyle rules={[formRule]}>
<Input
allowClear
disabled={disabled}
value={fieldDomains}
placeholder={t("workflow_node.deploy.form.wangsu_cdn_domains.placeholder")}
onChange={(e) => {
formInst.setFieldValue("domains", e.target.value);
}}
onClear={() => {
formInst.setFieldValue("domains", "");
}}
/>
</Form.Item>
<SiteNamesModalInput
value={fieldDomains}
trigger={
<Button disabled={disabled}>
<FormOutlinedIcon />
</Button>
}
onChange={(value) => {
formInst.setFieldValue("domains", value);
}}
/>
</Space.Compact>
<MultipleSplitValueInput
modalTitle={t("workflow_node.deploy.form.wangsu_cdn_domains.multiple_input_modal.title")}
placeholder={t("workflow_node.deploy.form.wangsu_cdn_domains.placeholder")}
placeholderInModal={t("workflow_node.deploy.form.wangsu_cdn_domains.multiple_input_modal.placeholder")}
splitOptions={{ trim: true, removeEmpty: true }}
/>
</Form.Item>
</Form>
);
};
const SiteNamesModalInput = memo(({ value, trigger, onChange }: { value?: string; trigger?: React.ReactNode; onChange?: (value: string) => void }) => {
const { t } = useTranslation();
const formSchema = z.object({
domains: z.array(z.string()).refine((v) => {
return v.every((e) => validDomainName(e));
}, t("workflow_node.deploy.form.wangsu_cdn_domains.errmsg.invalid")),
});
const formRule = createSchemaFieldRule(formSchema);
const { form: formInst, formProps } = useAntdForm({
name: "workflowNodeDeployConfigFormWangsuCDNNamesModalInput",
initialValues: { domains: value?.split(MULTIPLE_INPUT_DELIMITER) },
onSubmit: (values) => {
onChange?.(
values.domains
.map((e) => e.trim())
.filter((e) => !!e)
.join(MULTIPLE_INPUT_DELIMITER)
);
},
});
return (
<ModalForm
{...formProps}
layout="vertical"
form={formInst}
modalProps={{ destroyOnHidden: true }}
title={t("workflow_node.deploy.form.wangsu_cdn_domains.multiple_input_modal.title")}
trigger={trigger}
validateTrigger="onSubmit"
width={480}
>
<Form.Item name="domains" rules={[formRule]}>
<MultipleInput placeholder={t("workflow_node.deploy.form.wangsu_cdn_domains.multiple_input_modal.placeholder")} />
</Form.Item>
</ModalForm>
);
});
export default DeployNodeConfigFormWangsuCDNConfig;

View File

@@ -17,8 +17,10 @@ import { useAntdForm, useAntdFormName, useZustandShallowSelector } from "@/hooks
import { useAccessesStore } from "@/stores/access";
import { useNotifyChannelsStore } from "@/stores/notify";
import NotifyNodeConfigFormDiscordBotConfig from "./NotifyNodeConfigFormDiscordBotConfig";
import NotifyNodeConfigFormEmailConfig from "./NotifyNodeConfigFormEmailConfig";
import NotifyNodeConfigFormMattermostConfig from "./NotifyNodeConfigFormMattermostConfig";
import NotifyNodeConfigFormSlackBotConfig from "./NotifyNodeConfigFormSlackBotConfig";
import NotifyNodeConfigFormTelegramBotConfig from "./NotifyNodeConfigFormTelegramBotConfig";
import NotifyNodeConfigFormWebhookConfig from "./NotifyNodeConfigFormWebhookConfig";
@@ -110,10 +112,14 @@ const NotifyNodeConfigForm = forwardRef<NotifyNodeConfigFormInstance, NotifyNode
NOTICE: If you add new child component, please keep ASCII order.
*/
switch (fieldProvider) {
case NOTIFICATION_PROVIDERS.DISCORDBOT:
return <NotifyNodeConfigFormDiscordBotConfig {...nestedFormProps} />;
case NOTIFICATION_PROVIDERS.EMAIL:
return <NotifyNodeConfigFormEmailConfig {...nestedFormProps} />;
case NOTIFICATION_PROVIDERS.MATTERMOST:
return <NotifyNodeConfigFormMattermostConfig {...nestedFormProps} />;
case NOTIFICATION_PROVIDERS.SLACKBOT:
return <NotifyNodeConfigFormSlackBotConfig {...nestedFormProps} />;
case NOTIFICATION_PROVIDERS.TELEGRAMBOT:
return <NotifyNodeConfigFormTelegramBotConfig {...nestedFormProps} />;
case NOTIFICATION_PROVIDERS.WEBHOOK:

View File

@@ -0,0 +1,61 @@
import { useTranslation } from "react-i18next";
import { Form, type FormInstance, Input } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
type NotifyNodeConfigFormDiscordBotConfigFieldValues = Nullish<{
channelId?: string;
}>;
export type NotifyNodeConfigFormDiscordBotConfigProps = {
form: FormInstance;
formName: string;
disabled?: boolean;
initialValues?: NotifyNodeConfigFormDiscordBotConfigFieldValues;
onValuesChange?: (values: NotifyNodeConfigFormDiscordBotConfigFieldValues) => void;
};
const initFormModel = (): NotifyNodeConfigFormDiscordBotConfigFieldValues => {
return {};
};
const NotifyNodeConfigFormDiscordBotConfig = ({
form: formInst,
formName,
disabled,
initialValues,
onValuesChange,
}: NotifyNodeConfigFormDiscordBotConfigProps) => {
const { t } = useTranslation();
const formSchema = z.object({
channelId: z.string().nullish(),
});
const formRule = createSchemaFieldRule(formSchema);
const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => {
onValuesChange?.(values);
};
return (
<Form
form={formInst}
disabled={disabled}
initialValues={initialValues ?? initFormModel()}
layout="vertical"
name={formName}
onValuesChange={handleFormChange}
>
<Form.Item
name="channelId"
label={t("workflow_node.notify.form.discordbot_channel_id.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.notify.form.discordbot_channel_id.tooltip") }}></span>}
>
<Input allowClear placeholder={t("workflow_node.notify.form.discordbot_channel_id.placeholder")} />
</Form.Item>
</Form>
);
};
export default NotifyNodeConfigFormDiscordBotConfig;

View File

@@ -0,0 +1,55 @@
import { useTranslation } from "react-i18next";
import { Form, type FormInstance, Input } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
type NotifyNodeConfigFormSlackBotConfigFieldValues = Nullish<{
channelId?: string;
}>;
export type NotifyNodeConfigFormSlackBotConfigProps = {
form: FormInstance;
formName: string;
disabled?: boolean;
initialValues?: NotifyNodeConfigFormSlackBotConfigFieldValues;
onValuesChange?: (values: NotifyNodeConfigFormSlackBotConfigFieldValues) => void;
};
const initFormModel = (): NotifyNodeConfigFormSlackBotConfigFieldValues => {
return {};
};
const NotifyNodeConfigFormSlackBotConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: NotifyNodeConfigFormSlackBotConfigProps) => {
const { t } = useTranslation();
const formSchema = z.object({
channelId: z.string().nullish(),
});
const formRule = createSchemaFieldRule(formSchema);
const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => {
onValuesChange?.(values);
};
return (
<Form
form={formInst}
disabled={disabled}
initialValues={initialValues ?? initFormModel()}
layout="vertical"
name={formName}
onValuesChange={handleFormChange}
>
<Form.Item
name="channelId"
label={t("workflow_node.notify.form.slackbot_channel_id.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.notify.form.slackbot_channel_id.tooltip") }}></span>}
>
<Input allowClear placeholder={t("workflow_node.notify.form.slackbot_channel_id.placeholder")} />
</Form.Item>
</Form>
);
};
export default NotifyNodeConfigFormSlackBotConfig;

View File

@@ -38,7 +38,7 @@ const NotifyNodeConfigFormTelegramBotConfig = ({
.refine((v) => {
if (v == null || v + "" === "") return true;
return !Number.isNaN(+v!) && +v! !== 0;
}, t("workflow_node.notify.form.telegram_bot_chat_id.placeholder"))
}, t("workflow_node.notify.form.telegrambot_chat_id.placeholder"))
)
.nullish(),
});
@@ -59,11 +59,11 @@ const NotifyNodeConfigFormTelegramBotConfig = ({
>
<Form.Item
name="chatId"
label={t("workflow_node.notify.form.telegram_bot_chat_id.label")}
label={t("workflow_node.notify.form.telegrambot_chat_id.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.notify.form.telegram_bot_chat_id.tooltip") }}></span>}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.notify.form.telegrambot_chat_id.tooltip") }}></span>}
>
<Input allowClear placeholder={t("workflow_node.notify.form.telegram_bot_chat_id.placeholder")} />
<Input allowClear placeholder={t("workflow_node.notify.form.telegrambot_chat_id.placeholder")} />
</Form.Item>
</Form>
);

View File

@@ -1,4 +1,4 @@
import { memo, useRef } from "react";
import { memo, useMemo, useRef } from "react";
import { useTranslation } from "react-i18next";
import {
CloseCircleOutlined as CloseCircleOutlinedIcon,
@@ -7,7 +7,7 @@ import {
MoreOutlined as MoreOutlinedIcon,
} from "@ant-design/icons";
import { useControllableValue } from "ahooks";
import { Button, Card, Drawer, Dropdown, Input, type InputRef, Modal, Popover, Space } from "antd";
import { Button, Card, Drawer, Dropdown, Input, type InputRef, type MenuProps, Modal, Popover, Space } from "antd";
import { produce } from "immer";
import { isEqual } from "radash";
@@ -59,12 +59,13 @@ const SharedNodeTitle = ({ className, style, node, disabled }: SharedNodeTitlePr
type SharedNodeMenuProps = SharedNodeProps & {
branchId?: string;
branchIndex?: number;
menus?: Array<"rename" | "duplicate" | "remove">;
trigger: React.ReactNode;
afterUpdate?: () => void;
afterDelete?: () => void;
};
const isBranchingNode = (node: WorkflowNode) => {
const isNodeBranchLike = (node: WorkflowNode) => {
return (
node.type === WorkflowNodeType.Branch ||
node.type === WorkflowNodeType.Condition ||
@@ -74,7 +75,11 @@ const isBranchingNode = (node: WorkflowNode) => {
);
};
const SharedNodeMenu = ({ trigger, node, disabled, branchId, branchIndex, afterUpdate, afterDelete }: SharedNodeMenuProps) => {
const isNodeReadOnly = (node: WorkflowNode) => {
return node.type === WorkflowNodeType.Start || node.type === WorkflowNodeType.End;
};
const SharedNodeMenu = ({ menus, trigger, node, disabled, branchId, branchIndex, afterUpdate, afterDelete }: SharedNodeMenuProps) => {
const { t } = useTranslation();
const { updateNode, removeNode, removeBranch } = useWorkflowStore(useZustandShallowSelector(["updateNode", "removeNode", "removeBranch"]));
@@ -101,7 +106,7 @@ const SharedNodeMenu = ({ trigger, node, disabled, branchId, branchIndex, afterU
};
const handleDeleteClick = async () => {
if (isBranchingNode(node)) {
if (isNodeBranchLike(node)) {
await removeBranch(branchId!, branchIndex!);
} else {
await removeNode(node.id);
@@ -110,56 +115,76 @@ const SharedNodeMenu = ({ trigger, node, disabled, branchId, branchIndex, afterU
afterDelete?.();
};
const menuItems = useMemo(() => {
let temp = [
{
key: "rename",
disabled: disabled,
label: isNodeBranchLike(node) ? t("workflow_node.action.rename_branch") : t("workflow_node.action.rename_node"),
icon: <FormOutlinedIcon />,
onClick: () => {
nameRef.current = node.name;
const dialog = modalApi.confirm({
title: isNodeBranchLike(node) ? t("workflow_node.action.rename_branch") : t("workflow_node.action.rename_node"),
content: (
<div className="pb-2 pt-4">
<Input
ref={nameInputRef}
autoFocus
defaultValue={node.name}
onChange={(e) => (nameRef.current = e.target.value)}
onPressEnter={async () => {
await handleRenameConfirm();
dialog.destroy();
}}
/>
</div>
),
icon: null,
okText: t("common.button.save"),
onOk: handleRenameConfirm,
});
setTimeout(() => nameInputRef.current?.focus(), 1);
},
},
{
type: "divider",
},
{
key: "remove",
disabled: disabled || isNodeReadOnly(node),
label: isNodeBranchLike(node) ? t("workflow_node.action.remove_branch") : t("workflow_node.action.remove_node"),
icon: <CloseCircleOutlinedIcon />,
danger: true,
onClick: handleDeleteClick,
},
] satisfies MenuProps["items"];
if (menus) {
temp = temp.filter((item) => item.type === "divider" || menus.includes(item.key as "rename" | "remove"));
temp = temp.filter((item, index, array) => {
if (item.type !== "divider") return true;
return index === 0 || array[index - 1].type !== "divider";
});
if (temp[0]?.type === "divider") {
temp.shift();
}
if (temp[temp.length - 1]?.type === "divider") {
temp.pop();
}
}
return temp;
}, [disabled, node]);
return (
<>
{ModelContextHolder}
<Dropdown
menu={{
items: [
{
key: "rename",
disabled: disabled,
label: isBranchingNode(node) ? t("workflow_node.action.rename_branch") : t("workflow_node.action.rename_node"),
icon: <FormOutlinedIcon />,
onClick: () => {
nameRef.current = node.name;
const dialog = modalApi.confirm({
title: isBranchingNode(node) ? t("workflow_node.action.rename_branch") : t("workflow_node.action.rename_node"),
content: (
<div className="pb-2 pt-4">
<Input
ref={nameInputRef}
autoFocus
defaultValue={node.name}
onChange={(e) => (nameRef.current = e.target.value)}
onPressEnter={async () => {
await handleRenameConfirm();
dialog.destroy();
}}
/>
</div>
),
icon: null,
okText: t("common.button.save"),
onOk: handleRenameConfirm,
});
setTimeout(() => nameInputRef.current?.focus(), 1);
},
},
{
type: "divider",
},
{
key: "remove",
disabled: disabled || node.type === WorkflowNodeType.Start,
label: isBranchingNode(node) ? t("workflow_node.action.remove_branch") : t("workflow_node.action.remove_node"),
icon: <CloseCircleOutlinedIcon />,
danger: true,
onClick: handleDeleteClick,
},
],
items: menuItems,
}}
trigger={["click"]}
>
@@ -264,7 +289,6 @@ const SharedNodeConfigDrawer = ({
const { promise, resolve, reject } = Promise.withResolvers();
if (changed) {
console.log(oldValues, newValues);
modalApi.confirm({
title: t("common.text.operation_confirm"),
content: t("workflow_node.unsaved_changes.confirm"),
@@ -288,6 +312,7 @@ const SharedNodeConfigDrawer = ({
destroyOnHidden
extra={
<SharedNodeMenu
menus={["rename", "remove"]}
node={node}
disabled={disabled}
trigger={<Button icon={<EllipsisOutlinedIcon />} type="text" />}