Merge pull request #917 from fudiwei/dev

This commit is contained in:
RHQYZ 2025-08-13 11:29:17 +08:00 committed by GitHub
commit cbbabc42ac
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
224 changed files with 2290 additions and 2579 deletions

1259
ui/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -9,26 +9,24 @@
"preview": "vite preview"
},
"dependencies": {
"@ant-design/icons": "^6.0.0",
"@ant-design/pro-components": "^2.8.10",
"@codemirror/lang-json": "^6.0.2",
"@codemirror/lang-yaml": "^6.1.2",
"@codemirror/language": "^6.11.2",
"@codemirror/legacy-modes": "^6.5.1",
"@flowgram.ai/document": "^0.3.4",
"@flowgram.ai/fixed-layout-editor": "^0.3.4",
"@flowgram.ai/minimap-plugin": "^0.3.4",
"@flowgram.ai/document": "^0.3.5",
"@flowgram.ai/fixed-layout-editor": "^0.3.5",
"@flowgram.ai/minimap-plugin": "^0.3.5",
"@tabler/icons-react": "^3.34.1",
"@uiw/codemirror-extensions-basic-setup": "^4.24.2",
"@uiw/codemirror-theme-vscode": "^4.24.2",
"@uiw/react-codemirror": "^4.24.2",
"ahooks": "^3.9.0",
"antd": "^5.26.7",
"antd": "^5.27.0",
"antd-zod": "^7.0.0",
"clsx": "^2.1.1",
"cron-parser": "^5.3.0",
"file-saver": "^2.0.5",
"i18next": "^25.3.2",
"i18next": "^25.3.4",
"i18next-browser-languagedetector": "^8.2.0",
"immer": "^10.1.1",
"nanoid": "^5.1.5",
@ -40,7 +38,7 @@
"react-i18next": "^15.6.1",
"react-router-dom": "^7.8.0",
"tailwind-merge": "^3.3.1",
"zod": "^4.0.16",
"zod": "^4.0.17",
"zustand": "^5.0.7"
},
"devDependencies": {
@ -67,7 +65,7 @@
"prettier": "^3.6.2",
"tailwindcss": "^4.1.11",
"typescript": "^5.8.3",
"typescript-eslint": "^8.39.0",
"typescript-eslint": "^8.39.1",
"vite": "^6.3.5"
}
}

View File

@ -10,9 +10,7 @@ export interface AppDocumentLinkButtonProps {
showIcon?: boolean;
}
const AppDocumentLinkButton = (props: AppDocumentLinkButtonProps) => {
const { className, style, showIcon = true } = props;
const AppDocumentLinkButton = ({ className, style, showIcon = true }: AppDocumentLinkButtonProps) => {
const { i18n, t } = useTranslation();
const handleDocumentClick = () => {

View File

@ -30,9 +30,7 @@ export interface AppLocaleDropdownProps {
trigger?: DropdownProps["trigger"];
}
const AppLocaleDropdown = (props: AppLocaleDropdownProps) => {
const { children, trigger = ["click"] } = props;
const AppLocaleDropdown = ({ children, trigger = ["click"] }: AppLocaleDropdownProps) => {
const items = useAppLocaleMenuItems();
return (
@ -62,9 +60,7 @@ export interface AppLocaleLinkButtonProps {
showIcon?: boolean;
}
const AppLocaleLinkButton = (props: AppLocaleLinkButtonProps) => {
const { className, style, showIcon = true } = props;
const AppLocaleLinkButton = ({ className, style, showIcon = true }: AppLocaleLinkButtonProps) => {
const { t } = useTranslation();
const { i18n } = useTranslation();

View File

@ -39,9 +39,7 @@ export interface AppThemeDropdownProps {
trigger?: DropdownProps["trigger"];
}
const AppThemeDropdown = (props: AppThemeDropdownProps) => {
const { children, trigger = ["click"] } = props;
const AppThemeDropdown = ({ children, trigger = ["click"] }: AppThemeDropdownProps) => {
const items = useAppThemeMenuItems();
return (
@ -65,9 +63,7 @@ export interface AppThemeLinkButtonProps {
showIcon?: boolean;
}
const AppThemeLinkButton = (props: AppThemeLinkButtonProps) => {
const { className, style, showIcon = true } = props;
const AppThemeLinkButton = ({ className, style, showIcon = true }: AppThemeLinkButtonProps) => {
const { t } = useTranslation();
const { themeMode } = useBrowserTheme();

View File

@ -25,7 +25,7 @@ export interface AppVersionBadgeProps {
}
const AppVersionBadge = ({ className, style, children }: AppVersionBadgeProps) => {
const { hasNewVersion } = useVersionChecker();
const { hasUpdate } = useVersionChecker();
return (
<Badge
@ -34,7 +34,7 @@ const AppVersionBadge = ({ className, style, children }: AppVersionBadgeProps) =
styles={{
indicator: { transform: "scale(0.75) translate(50%, -50%)" },
}}
count={hasNewVersion ? "NEW" : void 0}
count={hasUpdate ? "NEW" : void 0}
>
{children}
</Badge>

View File

@ -16,10 +16,12 @@ const Tips = ({ className, style, message }: TipsProps) => {
style={style}
message={
<Flex gap="small">
<div>
<IconBulb size="1.5em" color={themeToken.colorInfo} />
<div style={{ marginTop: "1px" }}>
<IconBulb size={18} color={themeToken.colorInfo} />
</div>
<div style={{ flex: 1 }}>
<Typography.Text>{message}</Typography.Text>
</div>
<Typography.Text>{message}</Typography.Text>
</Flex>
}
type="info"

View File

@ -139,12 +139,6 @@ const AccessForm = forwardRef<AccessFormInstance, AccessFormProps>(({ className,
console.warn(`[certimate] unsupported provider usage: '${usage}'`);
}
}, [usage]);
const providerTooltip = useMemo(() => {
switch (usage) {
case "dns-hosting":
return <span dangerouslySetInnerHTML={{ __html: t("access.form.provider.tooltip") }}></span>;
}
}, [usage]);
const fieldProvider = Form.useWatch<z.infer<typeof formSchema>["provider"]>("provider", formInst);
const [fieldProviderPicked, setFieldProviderPicked] = useState<string>(initialValues?.provider); // bugfix: Form.useWatch 在条件渲染下不生效,这里用单独的变量存放 Picker 组件选择的值
@ -378,7 +372,12 @@ const AccessForm = forwardRef<AccessFormInstance, AccessFormProps>(({ className,
<Input placeholder={t("access.form.name.placeholder")} />
</Form.Item>
<Form.Item name="provider" label={t("access.form.provider.label")} rules={[formRule]} tooltip={providerTooltip}>
<Form.Item
name="provider"
label={t("access.form.provider.label")}
extra={usage === "dns-hosting" ? <span dangerouslySetInnerHTML={{ __html: t("access.form.provider.help") }}></span> : null}
rules={[formRule]}
>
<AccessProviderSelect
disabled={mode !== "create"}
placeholder={t("access.form.provider.placeholder")}

View File

@ -58,6 +58,7 @@ const AccessFormDiscordBotConfig = ({ form: formInst, formName, disabled, initia
<Form.Item
name="channelId"
label={t("access.form.discordbot_channel_id.label")}
extra={t("access.form.discordbot_channel_id.help")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.discordbot_channel_id.tooltip") }}></span>}
>

View File

@ -117,7 +117,12 @@ const AccessFormEmailConfig = ({ form: formInst, formName, disabled, initialValu
<Input allowClear placeholder={t("access.form.email_sender_name.placeholder")} />
</Form.Item>
<Form.Item name="receiverAddress" label={t("access.form.email_receiver_address.label")} rules={[formRule]}>
<Form.Item
name="receiverAddress"
label={t("access.form.email_receiver_address.label")}
extra={t("access.form.email_receiver_address.help")}
rules={[formRule]}
>
<Input type="email" allowClear placeholder={t("access.form.email_receiver_address.placeholder")} />
</Form.Item>
</Form>

View File

@ -67,6 +67,7 @@ const AccessFormMattermostConfig = ({ form: formInst, formName, disabled, initia
<Form.Item
name="channelId"
label={t("access.form.mattermost_channel_id.label")}
extra={t("access.form.mattermost_channel_id.help")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.mattermost_channel_id.tooltip") }}></span>}
>

View File

@ -135,17 +135,14 @@ const AccessFormSSHConfig = ({ form: formInst, formName, disabled, initialValues
</div>
<Form.Item name="authMethod" label={t("access.form.ssh_auth_method.label")} rules={[formRule]}>
<Select placeholder={t("access.form.ssh_auth_method.placeholder")}>
<Select.Option key={AUTH_METHOD_NONE} value={AUTH_METHOD_NONE}>
{t("access.form.ssh_auth_method.option.none.label")}
</Select.Option>
<Select.Option key={AUTH_METHOD_PASSWORD} value={AUTH_METHOD_PASSWORD}>
{t("access.form.ssh_auth_method.option.password.label")}
</Select.Option>
<Select.Option key={AUTH_METHOD_KEY} value={AUTH_METHOD_KEY}>
{t("access.form.ssh_auth_method.option.key.label")}
</Select.Option>
</Select>
<Select
options={[AUTH_METHOD_NONE, AUTH_METHOD_PASSWORD, AUTH_METHOD_KEY].map((s) => ({
key: s,
label: t(`access.form.ssh_auth_method.option.${s}.label`),
value: s,
}))}
placeholder={t("access.form.ssh_auth_method.placeholder")}
/>
</Form.Item>
<Form.Item name="username" label={t("access.form.ssh_username.label")} rules={[formRule]}>

View File

@ -58,6 +58,7 @@ const AccessFormSlackBotConfig = ({ form: formInst, formName, disabled, initialV
<Form.Item
name="channelId"
label={t("access.form.slackbot_channel_id.label")}
extra={t("access.form.slackbot_channel_id.help")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.slackbot_channel_id.tooltip") }}></span>}
>

View File

@ -65,6 +65,7 @@ const AccessFormTelegramBotConfig = ({ form: formInst, formName, disabled, initi
<Form.Item
name="chatId"
label={t("access.form.telegrambot_chat_id.label")}
extra={t("access.form.telegrambot_chat_id.help")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.telegrambot_chat_id.tooltip") }}></span>}
>

View File

@ -305,76 +305,62 @@ const AccessFormWebhookConfig = ({ form: formInst, formName, disabled, initialVa
</Form.Item>
<Show when={!usage || usage === "deployment"}>
<Form.Item noStyle>
<label className="mb-1 block">
<div className="flex w-full items-center justify-between gap-4">
<div className="max-w-full grow truncate">
<span>{t("access.form.webhook_data_for_deployment.label")}</span>
</div>
<div className="text-right">
<Button size="small" type="link" onClick={handlePresetDataForDeploymentClick}>
{t("access.form.webhook_preset_data.button")}
</Button>
</div>
</div>
</label>
<Form.Item name="dataForDeployment" rules={[formRule]}>
<Form.Item className="relative" label={t("access.form.webhook_data.label")} extra={t("access.form.webhook_data.help")}>
<div className="absolute -top-[6px] right-0 -translate-y-full">
<Button size="small" type="link" onClick={handlePresetDataForDeploymentClick}>
{t("access.form.webhook_preset_data.button")}
</Button>
</div>
<Form.Item name="dataForDeployment" noStyle rules={[formRule]}>
<CodeInput
height="auto"
minHeight="64px"
maxHeight="256px"
language="json"
placeholder={t("access.form.webhook_data_for_deployment.placeholder")}
placeholder={t("access.form.webhook_data.placeholder")}
onBlur={handleWebhookDataForDeploymentBlur}
/>
</Form.Item>
</Form.Item>
<Form.Item>
<Tips message={<span dangerouslySetInnerHTML={{ __html: t("access.form.webhook_data_for_deployment.guide") }}></span>} />
<Tips message={<span dangerouslySetInnerHTML={{ __html: t("access.form.webhook_data.guide_for_deployment") }}></span>} />
</Form.Item>
</Show>
<Show when={!usage || usage === "notification"}>
<Form.Item noStyle>
<label className="mb-1 block">
<div className="flex w-full items-center justify-between gap-4">
<div className="max-w-full grow truncate">
<span>{t("access.form.webhook_data_for_notification.label")}</span>
</div>
<div className="text-right">
<Dropdown
menu={{
items: ["bark", "ntfy", "gotify", "pushover", "pushplus", "serverchan3", "serverchanturbo", "common"].map((key) => ({
key,
label: <span dangerouslySetInnerHTML={{ __html: t(`access.form.webhook_preset_data.option.${key}.label`) }}></span>,
onClick: () => handlePresetDataForNotificationClick(key),
})),
}}
trigger={["click"]}
>
<Button size="small" type="link">
{t("access.form.webhook_preset_data.button")}
<IconChevronDown size="1.25em" />
</Button>
</Dropdown>
</div>
</div>
</label>
<Form.Item name="dataForNotification" rules={[formRule]}>
<Form.Item className="relative" label={t("access.form.webhook_data.label")} extra={t("access.form.webhook_data.help")}>
<div className="absolute -top-[6px] right-0 -translate-y-full">
<Dropdown
menu={{
items: ["bark", "ntfy", "gotify", "pushover", "pushplus", "serverchan3", "serverchanturbo", "common"].map((key) => ({
key,
label: <span dangerouslySetInnerHTML={{ __html: t(`access.form.webhook_preset_data.option.${key}.label`) }}></span>,
onClick: () => handlePresetDataForNotificationClick(key),
})),
}}
trigger={["click"]}
>
<Button size="small" type="link">
{t("access.form.webhook_preset_data.button")}
<IconChevronDown size="1.25em" />
</Button>
</Dropdown>
</div>
<Form.Item name="dataForNotification" noStyle rules={[formRule]}>
<CodeInput
height="auto"
minHeight="64px"
maxHeight="256px"
language="json"
placeholder={t("access.form.webhook_data_for_notification.placeholder")}
placeholder={t("access.form.webhook_data.placeholder")}
onBlur={handleWebhookDataForNotificationBlur}
/>
</Form.Item>
</Form.Item>
<Form.Item>
<Tips message={<span dangerouslySetInnerHTML={{ __html: t("access.form.webhook_data_for_notification.guide") }}></span>} />
<Tips message={<span dangerouslySetInnerHTML={{ __html: t("access.form.webhook_data.guide_for_notification") }}></span>} />
</Form.Item>
</Show>

View File

@ -16,7 +16,7 @@ const AccessSelect = ({ onFilter, ...props }: AccessTypeSelectProps) => {
const { accesses, loadedAtOnce, fetchAccesses } = useAccessesStore(useZustandShallowSelector(["accesses", "loadedAtOnce", "fetchAccesses"]));
useEffect(() => {
fetchAccesses();
fetchAccesses(false);
}, []);
const [options, setOptions] = useState<Array<{ key: string; value: string; label: string; data: AccessModel }>>([]);

View File

@ -59,9 +59,8 @@ const CertificateDetail = ({ data, ...props }: CertificateDetailProps) => {
<Input value={data.keyAlgorithm} variant="filled" placeholder="" />
</Form.Item>
<Form.Item>
<div className="mb-2 flex w-full items-center justify-between">
<label>{t("certificate.props.certificate")}</label>
<Form.Item label={t("certificate.props.certificate")}>
<div className="absolute -top-[6px] right-0 -translate-y-full">
<Tooltip title={t("common.button.copy")}>
<CopyToClipboard
text={data.certificate}
@ -76,9 +75,8 @@ const CertificateDetail = ({ data, ...props }: CertificateDetailProps) => {
<Input.TextArea value={data.certificate} variant="filled" autoSize={{ minRows: 5, maxRows: 5 }} readOnly />
</Form.Item>
<Form.Item>
<div className="mb-2 flex w-full items-center justify-between">
<label>{t("certificate.props.private_key")}</label>
<Form.Item label={t("certificate.props.private_key")}>
<div className="absolute -top-[6px] right-0 -translate-y-full">
<Tooltip title={t("common.button.copy")}>
<CopyToClipboard
text={data.privateKey}

View File

@ -1,7 +1,7 @@
import { useEffect, useMemo, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import { useSize } from "ahooks";
import { Avatar, Card, Empty, Input, type InputRef, Tag, Typography } from "antd";
import { Avatar, Card, Empty, Input, type InputRef, Tag, Tooltip, Typography } from "antd";
import Show from "@/components/Show";
import { ACCESS_USAGES, type AccessProvider, type AccessUsageType, accessProvidersMap } from "@/domain/provider";
@ -18,9 +18,7 @@ export interface AccessProviderPickerProps {
onSelect?: (value: string) => void;
}
const AccessProviderPicker = ({ className, style, autoFocus, placeholder, showOptionTags, onFilter, onSelect, ...props }: AccessProviderPickerProps) => {
const { gap = "middle" } = props;
const AccessProviderPicker = ({ className, style, autoFocus, gap = "middle", placeholder, showOptionTags, onFilter, onSelect }: AccessProviderPickerProps) => {
const { t } = useTranslation();
const wrapperRef = useRef<HTMLDivElement>(null);
@ -99,7 +97,7 @@ const AccessProviderPicker = ({ className, style, autoFocus, placeholder, showOp
return (
<div key={provider.type}>
<Card
className={mergeCls("h-20 w-full overflow-hidden shadow", provider.builtin ? " cursor-not-allowed" : "")}
className={mergeCls("w-full overflow-hidden shadow", provider.builtin ? " cursor-not-allowed" : "", showOptionTagAnyhow ? "h-32" : "h-28")}
styles={{ body: { height: "100%", padding: "0.5rem 1rem" } }}
hoverable
onClick={() => {
@ -110,14 +108,18 @@ const AccessProviderPicker = ({ className, style, autoFocus, placeholder, showOp
handleProviderTypeSelect(provider.type);
}}
>
<div className="flex size-full items-center gap-4 overflow-hidden">
<Avatar className="bg-stone-100" icon={<img src={provider.icon} />} shape="square" size={28} />
<div className="flex-1 overflow-hidden">
<div className={mergeCls("max-w-full", showOptionTagAnyhow ? "mb-1 truncate" : "line-clamp-2")}>
<Typography.Text type={provider.builtin ? "secondary" : void 0}>{t(provider.name) || "\u00A0"}</Typography.Text>
<div className="flex size-full flex-col items-center justify-center gap-3 overflow-hidden p-2">
<div className="flex items-center justify-center">
<Avatar className="bg-stone-100" icon={<img src={provider.icon} />} shape="square" size={32} />
</div>
<div className="w-full overflow-hidden text-center">
<div className={mergeCls("w-full truncate", { "mb-1": showOptionTagAnyhow })}>
<Tooltip title={t(provider.name)} mouseEnterDelay={1}>
<Typography.Text type={provider.builtin ? "secondary" : void 0}>{t(provider.name) || "\u00A0"}</Typography.Text>
</Tooltip>
</div>
<Show when={showOptionTagAnyhow}>
<div className="origin-left scale-80 whitespace-nowrap">
<div className="origin-top scale-80 whitespace-nowrap" style={{ marginInlineEnd: "-8px" }}>
<Show when={provider.builtin}>
<Tag>{t("access.props.provider.builtin")}</Tag>
</Show>

View File

@ -33,7 +33,7 @@ const CAProviderSelect = ({ onFilter, ...props }: CAProviderSelectProps) => {
temp.unshift({
key: "",
value: "",
label: t("provider.text.default_ca_provider.label"),
label: t("provider.text.default_ca_provider"),
data: {} as CAProvider,
});
@ -45,7 +45,7 @@ const CAProviderSelect = ({ onFilter, ...props }: CAProviderSelectProps) => {
return (
<div className="flex items-center gap-2 truncate overflow-hidden">
<Typography.Text className="italic" ellipsis italic>
{t("provider.text.default_ca_provider.label")}
{t("provider.text.default_ca_provider")}
</Typography.Text>
</div>
);

View File

@ -1,10 +1,12 @@
import { useEffect, useMemo, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import { useSize } from "ahooks";
import { Avatar, Card, Empty, Flex, Input, type InputRef, Tabs, Tooltip, Typography } from "antd";
import { Avatar, Card, Checkbox, Empty, Flex, Input, type InputRef, Tabs, Tooltip, Typography } from "antd";
import Show from "@/components/Show";
import { DEPLOYMENT_CATEGORIES, type DeploymentProvider, deploymentProvidersMap } from "@/domain/provider";
import { useZustandShallowSelector } from "@/hooks";
import { useAccessesStore } from "@/stores/access";
import { mergeCls } from "@/utils/css";
export interface DeploymentProviderPickerProps {
@ -17,14 +19,19 @@ export interface DeploymentProviderPickerProps {
onSelect?: (value: string) => void;
}
const DeploymentProviderPicker = ({ className, style, autoFocus, onFilter, placeholder, onSelect, ...props }: DeploymentProviderPickerProps) => {
const { gap = "middle" } = props;
const DeploymentProviderPicker = ({ className, style, autoFocus, gap = "middle", placeholder, onFilter, onSelect }: DeploymentProviderPickerProps) => {
const { t } = useTranslation();
const { accesses, fetchAccesses } = useAccessesStore(useZustandShallowSelector(["accesses", "fetchAccesses"]));
useEffect(() => {
fetchAccesses(false);
}, []);
const wrapperRef = useRef<HTMLDivElement>(null);
const wrapperSize = useSize(wrapperRef);
const [isAvailableOnly, setIsAvailableOnly] = useState(true);
const [category, setCategory] = useState<string>(DEPLOYMENT_CATEGORIES.ALL);
const [keyword, setKeyword] = useState<string>();
@ -44,6 +51,13 @@ const DeploymentProviderPicker = ({ className, style, autoFocus, onFilter, place
return true;
})
.filter((provider) => {
if (isAvailableOnly) {
return provider.builtin || accesses.some((access) => access.provider === provider.provider);
}
return true;
})
.filter((provider) => {
if (category && category !== DEPLOYMENT_CATEGORIES.ALL) {
return provider.category === category;
@ -59,7 +73,7 @@ const DeploymentProviderPicker = ({ className, style, autoFocus, onFilter, place
return true;
});
}, [onFilter, category, keyword]);
}, [onFilter, accesses, isAvailableOnly, category, keyword]);
const providerCols = useMemo(() => {
if (!wrapperSize) {
return 1;
@ -77,6 +91,14 @@ const DeploymentProviderPicker = ({ className, style, autoFocus, onFilter, place
<div className={className} style={style} ref={wrapperRef}>
<Input.Search ref={keywordInputRef} placeholder={placeholder ?? t("common.text.search")} onChange={(e) => setKeyword(e.target.value.trim())} />
<div className="mt-4">
<Flex justify="end">
<Checkbox checked={isAvailableOnly} onClick={() => setIsAvailableOnly(!isAvailableOnly)}>
{t("provider.text.show_available_hosting_provider_only")}
</Checkbox>
</Flex>
</div>
<div className="mt-4">
<Flex>
<Tabs
@ -126,16 +148,16 @@ const DeploymentProviderPicker = ({ className, style, autoFocus, onFilter, place
handleProviderTypeSelect(provider.type);
}}
>
<Tooltip title={t(provider.name)} mouseEnterDelay={1}>
<div className="flex size-full items-center gap-4 overflow-hidden">
<Avatar className="bg-stone-100" icon={<img src={provider.icon} />} shape="square" size={28} />
<div className="flex-1 overflow-hidden">
<div className="line-clamp-2 max-w-full">
<div className="flex size-full items-center gap-4 overflow-hidden">
<Avatar className="bg-stone-100" icon={<img src={provider.icon} />} shape="square" size={28} />
<div className="flex-1 overflow-hidden">
<div className="line-clamp-2 max-w-full">
<Tooltip title={t(provider.name)} mouseEnterDelay={1}>
<Typography.Text>{t(provider.name) || "\u00A0"}</Typography.Text>
</div>
</Tooltip>
</div>
</div>
</Tooltip>
</div>
</Card>
</div>
);

View File

@ -15,6 +15,10 @@ import StartNode from "./node/StartNode";
import UnknownNode from "./node/UnknownNode";
import UploadNode from "./node/UploadNode";
/**
*
* @deprecated
*/
export interface WorkflowElementProps {
node: WorkflowNode;
disabled?: boolean;

View File

@ -5,6 +5,10 @@ import { WorkflowNodeType, newNode } from "@/domain/workflow";
import { useZustandShallowSelector } from "@/hooks";
import { useWorkflowStore } from "@/stores/workflow";
/**
*
* @deprecated
*/
export interface WorkflowElementsProps {
className?: string;
style?: React.CSSProperties;

View File

@ -5,6 +5,10 @@ import { Button, Card, Typography } from "antd";
import WorkflowElements from "@/components/workflow/WorkflowElements";
import { mergeCls } from "@/utils/css";
/**
*
* @deprecated
*/
export interface WorkflowElementsProps {
className?: string;
style?: React.CSSProperties;

View File

@ -17,7 +17,7 @@ import { listByWorkflowRunId as listLogsByWorkflowRunId } from "@/repository/wor
import { mergeCls } from "@/utils/css";
import { getErrMsg } from "@/utils/error";
import WorkflowStatusIcon from "./WorkflowStatusIcon";
import WorkflowStatus from "./WorkflowStatus";
export interface WorkflowRunDetailProps {
className?: string;
@ -88,34 +88,6 @@ const WorkflowRunLogs = ({ runId, runStatus }: { runId: string; runStatus: strin
const [showTimestamp, setShowTimestamp] = useState(true);
const [showWhitespace, setShowWhitespace] = useState(true);
const renderBadge = () => {
let color: string | undefined;
switch (runStatus) {
case WORKFLOW_RUN_STATUSES.PENDING:
break;
case WORKFLOW_RUN_STATUSES.RUNNING:
color = themeToken.colorInfo;
break;
case WORKFLOW_RUN_STATUSES.SUCCEEDED:
color = themeToken.colorSuccess;
break;
case WORKFLOW_RUN_STATUSES.FAILED:
color = themeToken.colorError;
break;
case WORKFLOW_RUN_STATUSES.CANCELED:
color = themeToken.colorWarning;
break;
}
return (
<Flex gap="small" style={{ color: color }}>
<WorkflowStatusIcon size="1.25em" status={runStatus} />
{t(`workflow_run.props.status.${runStatus}`)}
</Flex>
);
};
const renderRecord = (record: Log) => {
let message = <>{record.message}</>;
if (record.data != null && Object.keys(record.data).length > 0) {
@ -189,7 +161,9 @@ const WorkflowRunLogs = ({ runId, runStatus }: { runId: string; runStatus: strin
<Typography.Title level={5}>{t("workflow_run.logs")}</Typography.Title>
<div className="rounded-md bg-black text-stone-200">
<div className="flex items-center gap-2 p-4">
<div className="grow overflow-hidden">{renderBadge()}</div>
<div className="grow overflow-hidden">
<WorkflowStatus value={runStatus} />
</div>
<div>
<Dropdown
menu={{

View File

@ -0,0 +1,153 @@
import { useTranslation } from "react-i18next";
import {
IconCircleCheck,
IconCircleCheckFilled,
IconCircleDashed,
IconCircleOff,
IconCircleX,
IconCircleXFilled,
IconClock,
IconClockFilled,
IconLoader3,
} from "@tabler/icons-react";
import { Typography, theme } from "antd";
import { WORKFLOW_RUN_STATUSES, type WorkflorRunStatusType } from "@/domain/workflowRun";
import { mergeCls } from "@/utils/css";
const useColor = (value: WorkflorRunStatusType | string, defaultColor?: string | false) => {
const { token: themeToken } = theme.useToken();
switch (value) {
case WORKFLOW_RUN_STATUSES.PENDING:
if (defaultColor == null || !defaultColor) {
return themeToken.colorTextSecondary;
}
break;
case WORKFLOW_RUN_STATUSES.RUNNING:
if (defaultColor == null || !defaultColor) {
return themeToken.colorInfo;
}
break;
case WORKFLOW_RUN_STATUSES.SUCCEEDED:
if (defaultColor == null || !defaultColor) {
return themeToken.colorSuccess;
}
break;
case WORKFLOW_RUN_STATUSES.FAILED:
if (defaultColor == null || !defaultColor) {
return themeToken.colorError;
}
break;
case WORKFLOW_RUN_STATUSES.CANCELED:
if (defaultColor == null || !defaultColor) {
return themeToken.colorWarning;
}
break;
default:
if (defaultColor == null || !defaultColor) {
return themeToken.colorTextSecondary;
}
break;
}
return defaultColor;
};
export interface WorkflowStatusIconProps {
className?: string;
style?: React.CSSProperties;
color?: string | false;
size?: number | string;
type?: "filled" | "outlined";
value: WorkflorRunStatusType | string;
}
const WorkflowStatusIcon = ({ className, style, size = "1.25em", type = "outlined", value, ...props }: WorkflowStatusIconProps) => {
const color = useColor(value, props.color);
switch (value) {
case WORKFLOW_RUN_STATUSES.PENDING:
return (
<span className={mergeCls("anticon", className)} style={style} role="img">
{type === "filled" ? <IconClockFilled color={color} size={size} /> : <IconClock color={color} size={size} />}
</span>
);
case WORKFLOW_RUN_STATUSES.RUNNING:
return (
<span className={mergeCls("anticon", "animate-spin", className)} style={style} role="img">
<IconLoader3 color={color} size={size} />
</span>
);
case WORKFLOW_RUN_STATUSES.SUCCEEDED:
return (
<span className={mergeCls("anticon", className)} style={style} role="img">
{type === "filled" ? <IconCircleCheckFilled color={color} size={size} /> : <IconCircleCheck color={color} size={size} />}
</span>
);
case WORKFLOW_RUN_STATUSES.FAILED:
return (
<span className={mergeCls("anticon", className)} style={style} role="img">
{type === "filled" ? <IconCircleXFilled color={color} size={size} /> : <IconCircleX color={color} size={size} />}
</span>
);
case WORKFLOW_RUN_STATUSES.CANCELED:
return (
<span className={mergeCls("anticon", className)} style={style} role="img">
<IconCircleOff color={color} size={size} />
</span>
);
default:
return (
<span className={mergeCls("anticon", className)} style={style} role="img">
<IconCircleDashed color={color} size={size} />
</span>
);
}
};
export interface WorkflowStatusProps {
className?: string;
style?: React.CSSProperties;
children?: React.ReactNode;
color?: string | false;
showIcon?: boolean;
type?: WorkflowStatusIconProps["type"];
value: WorkflorRunStatusType | string;
}
const WorkflowStatus = ({ className, style, children, showIcon = true, type, value, ...props }: WorkflowStatusProps) => {
const { t } = useTranslation();
const color = useColor(value, props.color);
const renderIcon = () => (showIcon ? <WorkflowStatusIcon type={type} value={value} /> : null);
switch (value) {
case WORKFLOW_RUN_STATUSES.PENDING:
case WORKFLOW_RUN_STATUSES.RUNNING:
case WORKFLOW_RUN_STATUSES.SUCCEEDED:
case WORKFLOW_RUN_STATUSES.FAILED:
case WORKFLOW_RUN_STATUSES.CANCELED:
return (
<Typography.Text className={className} style={style}>
<div className="flex items-center gap-2">
{renderIcon()}
{children != null ? children : <span style={{ color: color }}>{t(`workflow_run.props.status.${value.toLowerCase()}`)}</span>}
</div>
</Typography.Text>
);
default:
return (
<Typography.Text className={className} style={style}>
<div className="flex items-center gap-2">{children != null ? children : <></>}</div>
</Typography.Text>
);
}
};
const _default = Object.assign(WorkflowStatus, {
Icon: WorkflowStatusIcon,
});
export default _default;

View File

@ -1,78 +0,0 @@
import { IconCircleCheck, IconCircleDashed, IconCircleOff, IconCircleX, IconClock, IconLoader3 } from "@tabler/icons-react";
import { theme } from "antd";
import { WORKFLOW_RUN_STATUSES } from "@/domain/workflowRun";
import { mergeCls } from "@/utils/css";
export interface WorkflowStatusIconProps {
className?: string;
style?: React.CSSProperties;
color?: string | true;
size?: number | string;
status: string;
}
const WorkflowStatusIcon = ({ className, style, color, status, ...props }: WorkflowStatusIconProps) => {
const { size = "1em" } = props;
const { token: themeToken } = theme.useToken();
switch (status) {
case WORKFLOW_RUN_STATUSES.PENDING:
if (color === true) {
color = themeToken.colorTextSecondary;
}
return (
<span className={mergeCls("anticon", className)} style={style} role="img">
<IconClock color={color} size={size} />
</span>
);
case WORKFLOW_RUN_STATUSES.RUNNING:
if (color === true) {
color = themeToken.colorInfo;
}
return (
<span className={mergeCls("anticon", "animate-spin", className)} style={style} role="img">
<IconLoader3 color={color} size={size} />
</span>
);
case WORKFLOW_RUN_STATUSES.SUCCEEDED:
if (color === true) {
color = themeToken.colorSuccess;
}
return (
<span className={mergeCls("anticon", className)} style={style} role="img">
<IconCircleCheck color={color} size={size} />
</span>
);
case WORKFLOW_RUN_STATUSES.FAILED:
if (color === true) {
color = themeToken.colorError;
}
return (
<span className={mergeCls("anticon", className)} style={style} role="img">
<IconCircleX color={color} size={size} />
</span>
);
case WORKFLOW_RUN_STATUSES.CANCELED:
if (color === true) {
color = themeToken.colorWarning;
}
return (
<span className={mergeCls("anticon", className)} style={style} role="img">
<IconCircleOff color={color} size={size} />
</span>
);
default:
if (color === true) {
color = themeToken.colorTextSecondary;
}
return (
<span className={mergeCls("anticon", className)} style={style} role="img">
<IconCircleDashed color={color} size={size} />
</span>
);
}
};
export default WorkflowStatusIcon;

View File

@ -1,55 +0,0 @@
import { useTranslation } from "react-i18next";
import { Tag } from "antd";
import { WORKFLOW_RUN_STATUSES } from "@/domain/workflowRun";
import WorkflowStatusIcon from "./WorkflowStatusIcon";
export interface WorkflowStatusTagProps {
className?: string;
style?: React.CSSProperties;
status: string;
}
const WorkflowStatusTag = ({ className, style, status }: WorkflowStatusTagProps) => {
const { t } = useTranslation();
const Icon = <WorkflowStatusIcon status={status} />;
switch (status) {
case WORKFLOW_RUN_STATUSES.PENDING:
return (
<Tag className={className} style={style} icon={Icon}>
{t("workflow_run.props.status.pending")}
</Tag>
);
case WORKFLOW_RUN_STATUSES.RUNNING:
return (
<Tag className={className} style={style} color="var(--color-info)" icon={Icon}>
{t("workflow_run.props.status.running")}
</Tag>
);
case WORKFLOW_RUN_STATUSES.SUCCEEDED:
return (
<Tag className={className} style={style} color="var(--color-success)" icon={Icon}>
{t("workflow_run.props.status.succeeded")}
</Tag>
);
case WORKFLOW_RUN_STATUSES.FAILED:
return (
<Tag className={className} style={style} color="var(--color-error)" icon={Icon}>
{t("workflow_run.props.status.failed")}
</Tag>
);
case WORKFLOW_RUN_STATUSES.CANCELED:
return (
<Tag className={className} style={style} color="var(--color-warning)" icon={Icon}>
{t("workflow_run.props.status.canceled")}
</Tag>
);
default:
return <></>;
}
};
export default WorkflowStatusTag;

View File

@ -14,14 +14,14 @@ import { createMinimapPlugin } from "@flowgram.ai/minimap-plugin";
import "@flowgram.ai/fixed-layout-editor/index.css";
import { theme } from "antd";
import { getFlowComponents } from "./components";
import { EditorContextProvider } from "./EditorContext";
import { DegisnerContextProvider } from "./DesignerContext";
import { getAllElements } from "./elements";
import NodeRender from "./NodeRender";
import { getFlowNodeRegistries } from "./nodes";
import { getAllNodeRegistries } from "./nodes";
import { BranchNode } from "./nodes/_shared";
import "./flowgram.css";
export interface EditorProps {
export interface DesignerProps {
className?: string;
style?: React.CSSProperties;
children?: React.ReactNode;
@ -30,12 +30,12 @@ export interface EditorProps {
onNodeClick?: (ctx: FixedLayoutPluginContext, node: FlowNodeEntity) => void;
}
export interface EditorInstance extends FixedLayoutPluginContext {
export interface DesignerInstance extends FixedLayoutPluginContext {
validateNode(node: string | FlowNodeEntity): Promise<boolean>;
validateAllNodes(): Promise<boolean>;
}
const Editor = forwardRef<EditorInstance, EditorProps>(({ className, style, children, initialData, readonly, onNodeClick }, ref) => {
const Designer = forwardRef<DesignerInstance, DesignerProps>(({ className, style, children, initialData, readonly, onNodeClick }, ref) => {
const { token: themeToken } = theme.useToken();
const flowgramEditorRef = useRef<FixedLayoutPluginContext>(null);
@ -80,16 +80,16 @@ const Editor = forwardRef<EditorInstance, EditorProps>(({ className, style, chil
},
materials: {
components: getFlowComponents(),
components: getAllElements(),
renderTexts: {
[FlowTextKey.TRY_START_TEXT]: "Try",
[FlowTextKey.TRY_END_TEXT]: "Finally",
[FlowTextKey.TRY_END_TEXT]: "Then",
[FlowTextKey.CATCH_TEXT]: "Catch",
},
renderDefaultNode: NodeRender,
},
nodeRegistries: getFlowNodeRegistries(),
nodeRegistries: getAllNodeRegistries(),
getNodeDefaultRegistry(type) {
return {
@ -172,12 +172,12 @@ const Editor = forwardRef<EditorInstance, EditorProps>(({ className, style, chil
return (
<FixedLayoutEditorProvider ref={flowgramEditorRef} {...flowgramEditorProps}>
<EditorContextProvider value={{ onNodeClick: (node) => onNodeClick?.(flowgramEditorRef.current!, node) }}>
<DegisnerContextProvider value={{ onNodeClick: (node) => onNodeClick?.(flowgramEditorRef.current!, node) }}>
<EditorRenderer className={className} style={style} />
{children}
</EditorContextProvider>
</DegisnerContextProvider>
</FixedLayoutEditorProvider>
);
});
export default Editor;
export default Designer;

View File

@ -0,0 +1,20 @@
import { createContext, useContext } from "react";
import { type FlowNodeEntity } from "@flowgram.ai/fixed-layout-editor";
export type DesignerContextType = {
onNodeClick: (node: FlowNodeEntity) => void;
};
export const DesignerContext = createContext<DesignerContextType>({
onNodeClick: () => {},
});
export const DegisnerContextProvider = DesignerContext.Provider;
export const useDesignerContext = () => {
const context = useContext(DesignerContext);
if (!context) {
throw new Error("`DesignerContext` must be used within a `DesignerContextProvider`");
}
return context;
};

View File

@ -1,20 +0,0 @@
import { createContext, useContext } from "react";
import { type FlowNodeEntity } from "@flowgram.ai/fixed-layout-editor";
export type NodeRenderContextType = {
onNodeClick: (node: FlowNodeEntity) => void;
};
export const EditorContext = createContext<NodeRenderContextType>({
onNodeClick: () => {},
});
export const EditorContextProvider = EditorContext.Provider;
export const useEditorContext = () => {
const context = useContext(EditorContext);
if (!context) {
throw new Error("`EditorContext` must be used within a `EditorContextProvider`");
}
return context;
};

View File

@ -22,9 +22,7 @@ export interface NodeDrawerProps {
onOpenChange?: (open: boolean) => void;
}
const NodeDrawer = (_: NodeDrawerProps) => {
const { node, trigger, ...props } = _;
const NodeDrawer = ({ node, trigger, ...props }: NodeDrawerProps) => {
const [open, setOpen] = useControllableValue<boolean>(props, {
valuePropName: "open",
defaultValuePropName: "defaultOpen",

View File

@ -1,7 +1,7 @@
import { useEffect } from "react";
import { type NodeRenderProps, useClientContext, useNodeRender, useRefresh } from "@flowgram.ai/fixed-layout-editor";
import { useEditorContext } from "./EditorContext";
import { useDesignerContext } from "./DesignerContext";
import { NodeRenderContextProvider } from "./NodeRenderContext";
import { type NodeRegistry } from "./nodes/typings";
@ -14,6 +14,8 @@ const Node = (_: NodeProps) => {
const nodeRender = useNodeRender();
const designer = useDesignerContext();
useEffect(() => {
const d = ctx.document.originTree.onTreeChange(() => refresh());
return () => d.dispose();
@ -28,11 +30,10 @@ const Node = (_: NodeProps) => {
};
}, [nodeRender.form]);
const { onNodeClick } = useEditorContext();
const handleNodeClick = () => {
const node = nodeRender.node;
if (node.getNodeRegistry<NodeRegistry>().meta?.clickable) {
onNodeClick?.(node);
designer.onNodeClick?.(node);
}
};

View File

@ -1,60 +0,0 @@
import { type AdderProps as FlowgramAdderProps, useClientContext } from "@flowgram.ai/fixed-layout-editor";
import { IconPlus } from "@tabler/icons-react";
import { Button, Dropdown } from "antd";
import { getFlowNodeRegistries } from "../nodes";
export interface AdderProps extends FlowgramAdderProps {}
const Adder = ({ from, hoverActivated }: AdderProps) => {
const ctx = useClientContext();
const { operation, playground } = ctx;
const menuItems = getFlowNodeRegistries()
.filter((registry) => {
if (registry.meta?.addDisable != null) {
return !registry.meta.addDisable;
}
return true;
})
.filter((registry) => {
if (registry.canAdd != null) {
return registry.canAdd(ctx, from);
}
return true;
})
.map((registry) => {
const Icon = registry.meta?.icon;
return {
key: registry.type,
label: registry.meta?.labelText ?? registry.type,
icon: <span className="anticon scale-125">{Icon && <Icon size="1em" />}</span>,
onClick: () => {
const block = operation.addFromNode(from, registry.onAdd!(ctx, from));
setTimeout(() => {
playground.scrollToView({
bounds: block.bounds,
scrollToCenter: true,
});
}, 1);
},
};
});
return playground.config.readonlyOrDisabled ? null : (
<div className="relative">
<Dropdown menu={{ items: menuItems }} placement="bottomRight" trigger={["click"]}>
{hoverActivated ? (
<Button icon={<IconPlus size="1em" stroke={3} />} shape="circle" size="small" type="primary" />
) : (
<div className="size-2 rounded-full bg-primary opacity-75"></div>
)}
</Dropdown>
</div>
);
};
export default Adder;

View File

@ -0,0 +1,82 @@
import { useState } from "react";
import { useTranslation } from "react-i18next";
import { type AdderProps as FlowgramAdderProps, useClientContext } from "@flowgram.ai/fixed-layout-editor";
import { IconPlus } from "@tabler/icons-react";
import { Button, Dropdown, type MenuProps } from "antd";
import { getAllNodeRegistries } from "../nodes";
export interface AdderProps extends FlowgramAdderProps {}
const Adder = ({ from, hoverActivated }: AdderProps) => {
const { t } = useTranslation();
const ctx = useClientContext();
const { operation, playground } = ctx;
const [menuOpen, setMenuOpen] = useState(false); // 使用受控组件,避免下拉菜单展开时鼠标移出而产生的布局抖动
const menuItems = getAllNodeRegistries()
.filter((registry) => {
if (registry.meta?.addDisable != null) {
return !registry.meta.addDisable;
}
return true;
})
.filter((registry) => {
if (registry.canAdd != null) {
return registry.canAdd(ctx, from);
}
return true;
})
.reduce(
(acc, registry) => {
let group = acc.find((item) => item!.key === registry.kindType);
if (!group) {
group = {
key: registry.kindType,
type: "group",
label: registry.kindType ? t(`workflow_node.kind.${registry.kindType}`) : null,
children: [],
};
acc.push(group);
}
if (group.type === "group") {
const NodeIcon = registry.meta?.icon;
group.children!.push({
key: registry.type,
label: registry.meta?.labelText ?? registry.type,
icon: <span className="anticon scale-125">{NodeIcon && <NodeIcon size="1em" />}</span>,
onClick: () => {
const block = operation.addFromNode(from, registry.onAdd!(ctx, from));
setTimeout(() => {
playground.scrollToView({
bounds: block.bounds,
scrollToCenter: true,
});
}, 1);
},
});
}
return acc;
},
[] as Required<MenuProps>["items"]
);
return playground.config.readonlyOrDisabled ? null : (
<div className="relative">
<Dropdown menu={{ items: menuItems }} placement="bottomRight" trigger={["click"]} open={menuOpen} onOpenChange={setMenuOpen}>
{hoverActivated || menuOpen ? (
<Button icon={<IconPlus size="1em" stroke={3} />} shape="circle" size="small" type="primary" />
) : (
<div className="size-2 rounded-full bg-primary opacity-75"></div>
)}
</Dropdown>
</div>
);
};
export default Adder;

View File

@ -9,7 +9,7 @@ import DragNode from "./DragNode";
import Null from "./Null";
import TryCatchCollapse from "./TryCatchCollapse";
export const getFlowComponents = () => {
export const getAllElements = () => {
return {
[FlowRendererKey.ADDER]: Adder,
[FlowRendererKey.BRANCH_ADDER]: BranchAdder,

View File

@ -13,8 +13,7 @@ export interface BizApplyNodeConfigDrawerProps {
onOpenChange?: (open: boolean) => void;
}
const BizApplyNodeConfigDrawer = (_: BizApplyNodeConfigDrawerProps) => {
const { node, ...props } = _;
const BizApplyNodeConfigDrawer = ({ node, ...props }: BizApplyNodeConfigDrawerProps) => {
if (node.flowNodeType !== NodeType.BizApply) {
console.warn(`[certimate] current workflow node type is not: ${NodeType.BizApply}`);
}

View File

@ -1,11 +1,10 @@
import { memo, useEffect, useMemo, useState } from "react";
import { getI18n, useTranslation } from "react-i18next";
import { Link } from "react-router";
import { QuestionCircleOutlined as IconQuestionCircleOutlined } from "@ant-design/icons";
import { type FlowNodeEntity, getNodeForm } from "@flowgram.ai/fixed-layout-editor";
import { IconChevronRight, IconCircleMinus, IconPlus } from "@tabler/icons-react";
import { useControllableValue } from "ahooks";
import { type AnchorProps, AutoComplete, Button, Divider, Flex, Form, type FormInstance, Input, InputNumber, Select, Switch, Tooltip, Typography } from "antd";
import { type AnchorProps, AutoComplete, Button, Divider, Flex, Form, type FormInstance, Input, InputNumber, Select, Switch, Typography } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
@ -150,12 +149,7 @@ const BizApplyNodeConfigForm = ({ node, ...props }: BizApplyNodeConfigFormProps)
<NodeFormContextProvider value={{ node }}>
<Form {...formProps} clearOnDestroy={true} form={formInst} layout="vertical" preserve={false} scrollToFirstError>
<div id="parameters" data-anchor="parameters">
<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>}
>
<Form.Item name="domains" label={t("workflow_node.apply.form.domains.label")} extra={t("workflow_node.apply.form.domains.help")} rules={[formRule]}>
<MultipleSplitValueInput
modalTitle={t("workflow_node.apply.form.domains.multiple_input_modal.title")}
placeholder={t("workflow_node.apply.form.domains.placeholder")}
@ -171,7 +165,7 @@ const BizApplyNodeConfigForm = ({ node, ...props }: BizApplyNodeConfigFormProps)
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.apply.form.contact_email.tooltip") }}></span>}
>
<EmailInput placeholder={t("workflow_node.apply.form.contact_email.placeholder")} />
<InternalEmailInput placeholder={t("workflow_node.apply.form.contact_email.placeholder")} />
</Form.Item>
<Form.Item name="challengeType" label={t("workflow_node.apply.form.challenge_type.label")} rules={[formRule]} hidden>
@ -200,39 +194,31 @@ const BizApplyNodeConfigForm = ({ node, ...props }: BizApplyNodeConfigFormProps)
/>
</Form.Item>
<Form.Item noStyle>
<label className="mb-1 block">
<div className="flex w-full items-center justify-between gap-4">
<div className="max-w-full grow truncate">
<span>{t("workflow_node.apply.form.provider_access.label")}</span>
<Tooltip title={t("workflow_node.apply.form.provider_access.tooltip")}>
<Typography.Text className="ms-1" type="secondary">
<IconQuestionCircleOutlined />
</Typography.Text>
</Tooltip>
</div>
<div className="text-right">
<AccessEditDrawer
mode="create"
trigger={
<Button size="small" type="link">
{t("workflow_node.apply.form.provider_access.button")}
<IconPlus size="1.25em" />
</Button>
}
usage="dns"
afterSubmit={(record) => {
const provider = accessProvidersMap.get(record.provider);
if (provider?.usages?.includes(ACCESS_USAGES.DNS)) {
formInst.setFieldValue("providerAccessId", record.id);
handleProviderAccessSelect(record.id);
}
}}
/>
</div>
</div>
</label>
<Form.Item name="providerAccessId" rules={[formRule]}>
<Form.Item
className="relative"
label={t("workflow_node.apply.form.provider_access.label")}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.apply.form.provider_access.tooltip") }}></span>}
>
<div className="absolute -top-[6px] right-0 -translate-y-full">
<AccessEditDrawer
mode="create"
trigger={
<Button size="small" type="link">
{t("workflow_node.apply.form.provider_access.button")}
<IconPlus size="1.25em" />
</Button>
}
usage="dns"
afterSubmit={(record) => {
const provider = accessProvidersMap.get(record.provider);
if (provider?.usages?.includes(ACCESS_USAGES.DNS)) {
formInst.setFieldValue("providerAccessId", record.id);
handleProviderAccessSelect(record.id);
}
}}
/>
</div>
<Form.Item name="providerAccessId" rules={[formRule]} noStyle>
<AccessSelect
placeholder={t("workflow_node.apply.form.provider_access.placeholder")}
showSearch
@ -259,25 +245,18 @@ const BizApplyNodeConfigForm = ({ node, ...props }: BizApplyNodeConfigFormProps)
</Typography.Text>
</Divider>
<Form.Item noStyle>
<label className="mb-1 block">
<div className="flex w-full items-center justify-between gap-4">
<div className="max-w-full grow truncate">
<span>{t("workflow_node.apply.form.ca_provider.label")}</span>
</div>
<div className="text-right">
<Show when={!fieldCAProvider}>
<Link className="ant-typography" to="/settings/ssl-provider" target="_blank">
<Button size="small" type="link">
{t("workflow_node.apply.form.ca_provider.button")}
<IconChevronRight size="1.25em" />
</Button>
</Link>
</Show>
</div>
</div>
</label>
<Form.Item name="caProvider" rules={[formRule]}>
<Form.Item className="relative" label={t("workflow_node.apply.form.ca_provider.label")}>
<div className="absolute -top-[6px] right-0 -translate-y-full">
<Show when={!fieldCAProvider}>
<Link className="ant-typography" to="/settings/ssl-provider" target="_blank">
<Button size="small" type="link">
{t("workflow_node.apply.form.ca_provider.button")}
<IconChevronRight size="1.25em" />
</Button>
</Link>
</Show>
</div>
<Form.Item name="caProvider" noStyle rules={[formRule]}>
<CAProviderSelect
allowClear
placeholder={t("workflow_node.apply.form.ca_provider.placeholder")}
@ -288,34 +267,27 @@ const BizApplyNodeConfigForm = ({ node, ...props }: BizApplyNodeConfigFormProps)
</Form.Item>
</Form.Item>
<Form.Item hidden={!showCAProviderAccess} noStyle>
<label className="mb-1 block">
<div className="flex w-full items-center justify-between gap-4">
<div className="max-w-full grow truncate">
<span>{t("workflow_node.apply.form.ca_provider_access.label")}</span>
</div>
<div className="text-right">
<AccessEditDrawer
data={{ provider: caProvidersMap.get(fieldCAProvider!)?.provider }}
mode="create"
trigger={
<Button size="small" type="link">
{t("workflow_node.apply.form.ca_provider_access.button")}
<IconChevronRight size="1.25em" />
</Button>
}
usage="ca"
afterSubmit={(record) => {
const provider = accessProvidersMap.get(record.provider);
if (provider?.usages?.includes(ACCESS_USAGES.CA)) {
formInst.setFieldValue("caProviderAccessId", record.id);
}
}}
/>
</div>
</div>
</label>
<Form.Item name="caProviderAccessId" rules={[formRule]}>
<Form.Item label={t("workflow_node.apply.form.ca_provider_access.label")} hidden={!showCAProviderAccess}>
<div className="absolute -top-[6px] right-0 -translate-y-full">
<AccessEditDrawer
data={{ provider: caProvidersMap.get(fieldCAProvider!)?.provider }}
mode="create"
trigger={
<Button size="small" type="link">
{t("workflow_node.apply.form.ca_provider_access.button")}
<IconChevronRight size="1.25em" />
</Button>
}
usage="ca"
afterSubmit={(record) => {
const provider = accessProvidersMap.get(record.provider);
if (provider?.usages?.includes(ACCESS_USAGES.CA)) {
formInst.setFieldValue("caProviderAccessId", record.id);
}
}}
/>
</div>
<Form.Item name="caProviderAccessId" noStyle rules={[formRule]}>
<AccessSelect
placeholder={t("workflow_node.apply.form.ca_provider_access.placeholder")}
showSearch
@ -412,6 +384,7 @@ const BizApplyNodeConfigForm = ({ node, ...props }: BizApplyNodeConfigFormProps)
<Form.Item
name="dnsTTL"
label={t("workflow_node.apply.form.dns_ttl.label")}
extra={t("workflow_node.apply.form.dns_ttl.help")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.apply.form.dns_ttl.tooltip") }}></span>}
>
@ -475,11 +448,11 @@ const BizApplyNodeConfigForm = ({ node, ...props }: BizApplyNodeConfigFormProps)
);
};
const EmailInput = memo(
const InternalEmailInput = memo(
({ disabled, placeholder, ...props }: { disabled?: boolean; placeholder?: string; value?: string; onChange?: (value: string) => void }) => {
const { emails, fetchEmails, removeEmail } = useContactEmailsStore();
useEffect(() => {
fetchEmails();
fetchEmails(false);
}, []);
const [value, setValue] = useControllableValue<string>(props, {
@ -550,7 +523,7 @@ const EmailInput = memo(
}
);
const getAnchorItems = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }): Required<AnchorProps>["items"] => {
const getAnchorItems = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }): Required<AnchorProps>["items"] => {
const { t } = i18n;
return ["parameters", "certificate", "advanced", "strategy"].map((key) => ({
@ -569,7 +542,7 @@ const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z

View File

@ -47,7 +47,7 @@ const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z.object({

View File

@ -36,7 +36,7 @@ const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z.object({

View File

@ -36,7 +36,7 @@ const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z.object({

View File

@ -36,7 +36,7 @@ const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z.object({

View File

@ -36,7 +36,7 @@ const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z.object({

View File

@ -13,8 +13,7 @@ export interface BizDeployNodeConfigDrawerProps {
onOpenChange?: (open: boolean) => void;
}
const BizDeployNodeConfigDrawer = (_: BizDeployNodeConfigDrawerProps) => {
const { node, ...props } = _;
const BizDeployNodeConfigDrawer = ({ node, ...props }: BizDeployNodeConfigDrawerProps) => {
if (node.flowNodeType !== NodeType.BizDeploy) {
console.warn(`[certimate] current workflow node type is not: ${NodeType.BizDeploy}`);
}

View File

@ -1,9 +1,8 @@
import { useEffect, useMemo, useState } from "react";
import { getI18n, useTranslation } from "react-i18next";
import { QuestionCircleOutlined as IconQuestionCircleOutlined } from "@ant-design/icons";
import { type FlowNodeEntity, getNodeForm } from "@flowgram.ai/fixed-layout-editor";
import { IconPlus } from "@tabler/icons-react";
import { type AnchorProps, Button, Divider, Flex, Form, type FormInstance, Select, Switch, Tooltip, Typography, theme } from "antd";
import { type AnchorProps, Button, Divider, Flex, Form, type FormInstance, Select, Switch, Typography, theme } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
@ -415,39 +414,32 @@ const BizDeployNodeConfigForm = ({ node, ...props }: BizDeployNodeConfigFormProp
/>
</Form.Item>
<Form.Item hidden={!showProviderAccess} noStyle>
<label className="mb-1 block">
<div className="flex w-full items-center justify-between gap-4">
<div className="max-w-full grow truncate">
<span>{t("workflow_node.deploy.form.provider_access.label")}</span>
<Tooltip title={t("workflow_node.deploy.form.provider_access.tooltip")}>
<Typography.Text className="ms-1" type="secondary">
<IconQuestionCircleOutlined />
</Typography.Text>
</Tooltip>
</div>
<div className="text-right">
<AccessEditDrawer
data={{ provider: deploymentProvidersMap.get(fieldProvider!)?.provider }}
mode="create"
trigger={
<Button size="small" type="link">
{t("workflow_node.deploy.form.provider_access.button")}
<IconPlus size="1.25em" />
</Button>
}
usage="hosting"
afterSubmit={(record) => {
const provider = accessProvidersMap.get(record.provider);
if (provider?.usages?.includes(ACCESS_USAGES.HOSTING)) {
formInst.setFieldValue("providerAccessId", record.id);
}
}}
/>
</div>
</div>
</label>
<Form.Item name="providerAccessId" rules={[formRule]}>
<Form.Item
className="relative"
hidden={!showProviderAccess}
label={t("workflow_node.deploy.form.provider_access.label")}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.provider_access.tooltip") }}></span>}
>
<div className="absolute -top-[6px] right-0 -translate-y-full">
<AccessEditDrawer
data={{ provider: deploymentProvidersMap.get(fieldProvider!)?.provider }}
mode="create"
trigger={
<Button size="small" type="link">
{t("workflow_node.deploy.form.provider_access.button")}
<IconPlus size="1.25em" />
</Button>
}
usage="hosting"
afterSubmit={(record) => {
const provider = accessProvidersMap.get(record.provider);
if (provider?.usages?.includes(ACCESS_USAGES.HOSTING)) {
formInst.setFieldValue("providerAccessId", record.id);
}
}}
/>
</div>
<Form.Item name="providerAccessId" rules={[formRule]} noStyle>
<AccessSelect
placeholder={t("workflow_node.deploy.form.provider_access.placeholder")}
showSearch
@ -465,8 +457,8 @@ const BizDeployNodeConfigForm = ({ node, ...props }: BizDeployNodeConfigFormProp
<Form.Item
name="certificate"
label={t("workflow_node.deploy.form.certificate.label")}
extra={t("workflow_node.deploy.form.certificate.help")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.certificate.tooltip") }}></span>}
>
<Select
labelRender={({ label, value }) => {
@ -525,7 +517,7 @@ const BizDeployNodeConfigForm = ({ node, ...props }: BizDeployNodeConfigFormProp
);
};
const getAnchorItems = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }): Required<AnchorProps>["items"] => {
const getAnchorItems = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }): Required<AnchorProps>["items"] => {
const { t } = i18n;
return ["parameters", "deployment", "strategy"].map((key) => ({
@ -543,7 +535,7 @@ const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z

View File

@ -35,7 +35,7 @@ const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
const { t: _ } = i18n;
return z.object({

View File

@ -29,6 +29,7 @@ const BizDeployNodeConfigFormProvider1PanelSite = () => {
name={[parentNamePath, "nodeName"]}
initialValue={initialValues.nodeName}
label={t("workflow_node.deploy.form.1panel_site_node_name.label")}
extra={t("workflow_node.deploy.form.1panel_site_node_name.help")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.1panel_site_node_name.tooltip") }}></span>}
>
@ -84,7 +85,7 @@ const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z

View File

@ -58,7 +58,7 @@ const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z

View File

@ -31,8 +31,8 @@ const BizDeployNodeConfigFormProviderAWSACM = () => {
name={[parentNamePath, "certificateArn"]}
initialValue={initialValues.certificateArn}
label={t("workflow_node.deploy.form.aws_acm_certificate_arn.label")}
extra={t("workflow_node.deploy.form.aws_acm_certificate_arn.help")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aws_acm_certificate_arn.tooltip") }}></span>}
>
<Input allowClear placeholder={t("workflow_node.deploy.form.aws_acm_certificate_arn.placeholder")} />
</Form.Item>
@ -46,7 +46,7 @@ const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z.object({

View File

@ -62,7 +62,7 @@ const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z.object({

View File

@ -47,7 +47,7 @@ const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z.object({

View File

@ -81,6 +81,7 @@ const BizDeployNodeConfigFormProviderAliyunALB = () => {
name={[parentNamePath, "domain"]}
initialValue={initialValues.domain}
label={t("workflow_node.deploy.form.aliyun_alb_snidomain.label")}
extra={t("workflow_node.deploy.form.aliyun_alb_snidomain.help")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aliyun_alb_snidomain.tooltip") }}></span>}
>
@ -98,7 +99,7 @@ const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z

View File

@ -95,7 +95,7 @@ const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z

View File

@ -36,7 +36,7 @@ const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z.object({

View File

@ -1,9 +1,10 @@
import { getI18n, useTranslation } from "react-i18next";
import { Alert, Form, Input } from "antd";
import { Form, Input } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import MultipleSplitValueInput from "@/components/MultipleSplitValueInput";
import Tips from "@/components/Tips";
import { useFormNestedFieldsContext } from "./_context";
@ -22,30 +23,31 @@ const BizDeployNodeConfigFormProviderAliyunCASDeploy = () => {
return (
<>
<Form.Item>
<Alert type="info" message={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aliyun_cas_deploy.guide") }}></span>} />
<Tips message={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aliyun_casdeploy.guide") }}></span>} />
</Form.Item>
<Form.Item
name={[parentNamePath, "region"]}
initialValue={initialValues.region}
label={t("workflow_node.deploy.form.aliyun_cas_deploy_region.label")}
label={t("workflow_node.deploy.form.aliyun_casdeploy_region.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aliyun_cas_deploy_region.tooltip") }}></span>}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aliyun_casdeploy_region.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.aliyun_cas_deploy_region.placeholder")} />
<Input placeholder={t("workflow_node.deploy.form.aliyun_casdeploy_region.placeholder")} />
</Form.Item>
<Form.Item
name={[parentNamePath, "resourceIds"]}
initialValue={initialValues.resourceIds}
label={t("workflow_node.deploy.form.aliyun_cas_deploy_resource_ids.label")}
label={t("workflow_node.deploy.form.aliyun_casdeploy_resource_ids.label")}
extra={t("workflow_node.deploy.form.aliyun_casdeploy_resource_ids.help")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aliyun_cas_deploy_resource_ids.tooltip") }}></span>}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aliyun_casdeploy_resource_ids.tooltip") }}></span>}
>
<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")}
modalTitle={t("workflow_node.deploy.form.aliyun_casdeploy_resource_ids.multiple_input_modal.title")}
placeholder={t("workflow_node.deploy.form.aliyun_casdeploy_resource_ids.placeholder")}
placeholderInModal={t("workflow_node.deploy.form.aliyun_casdeploy_resource_ids.multiple_input_modal.placeholder")}
separator={MULTIPLE_INPUT_SEPARATOR}
splitOptions={{ removeEmpty: true, trimSpace: true }}
/>
@ -54,14 +56,15 @@ const BizDeployNodeConfigFormProviderAliyunCASDeploy = () => {
<Form.Item
name={[parentNamePath, "contactIds"]}
initialValue={initialValues.contactIds}
label={t("workflow_node.deploy.form.aliyun_cas_deploy_contact_ids.label")}
label={t("workflow_node.deploy.form.aliyun_casdeploy_contact_ids.label")}
extra={t("workflow_node.deploy.form.aliyun_casdeploy_contact_ids.help")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aliyun_cas_deploy_contact_ids.tooltip") }}></span>}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aliyun_casdeploy_contact_ids.tooltip") }}></span>}
>
<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")}
modalTitle={t("workflow_node.deploy.form.aliyun_casdeploy_contact_ids.multiple_input_modal.title")}
placeholder={t("workflow_node.deploy.form.aliyun_casdeploy_contact_ids.placeholder")}
placeholderInModal={t("workflow_node.deploy.form.aliyun_casdeploy_contact_ids.multiple_input_modal.placeholder")}
separator={MULTIPLE_INPUT_SEPARATOR}
splitOptions={{ removeEmpty: true, trimSpace: true }}
/>
@ -77,17 +80,17 @@ const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z.object({
region: z.string().nonempty(t("workflow_node.deploy.form.aliyun_cas_deploy_region.placeholder")),
region: z.string().nonempty(t("workflow_node.deploy.form.aliyun_casdeploy_region.placeholder")),
resourceIds: z.string().refine((v) => {
if (!v) return false;
return String(v)
.split(MULTIPLE_INPUT_SEPARATOR)
.every((e) => /^[1-9]\d*$/.test(e));
}, t("workflow_node.deploy.form.aliyun_cas_deploy_resource_ids.errmsg.invalid")),
}, t("workflow_node.deploy.form.aliyun_casdeploy_resource_ids.errmsg.invalid")),
contactIds: z
.string()
.nullish()
@ -96,7 +99,7 @@ const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) =
return String(v)
.split(MULTIPLE_INPUT_SEPARATOR)
.every((e) => /^[1-9]\d*$/.test(e));
}, t("workflow_node.deploy.form.aliyun_cas_deploy_contact_ids.errmsg.invalid")),
}, t("workflow_node.deploy.form.aliyun_casdeploy_contact_ids.errmsg.invalid")),
});
};

View File

@ -38,7 +38,7 @@ const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z.object({

View File

@ -79,6 +79,7 @@ const BizDeployNodeConfigFormProviderAliyunCLB = () => {
name={[parentNamePath, "domain"]}
initialValue={initialValues.domain}
label={t("workflow_node.deploy.form.aliyun_clb_snidomain.label")}
extra={t("workflow_node.deploy.form.aliyun_clb_snidomain.help")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aliyun_clb_snidomain.tooltip") }}></span>}
>
@ -98,7 +99,7 @@ const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z

View File

@ -38,7 +38,7 @@ const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z.object({

View File

@ -49,7 +49,7 @@ const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z.object({

View File

@ -47,7 +47,7 @@ const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z.object({

View File

@ -66,7 +66,7 @@ const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z.object({

View File

@ -69,6 +69,7 @@ const BizDeployNodeConfigFormProviderAliyunGA = () => {
name={[parentNamePath, "domain"]}
initialValue={initialValues.domain}
label={t("workflow_node.deploy.form.aliyun_ga_snidomain.label")}
extra={t("workflow_node.deploy.form.aliyun_ga_snidomain.help")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aliyun_ga_snidomain.tooltip") }}></span>}
>
@ -86,7 +87,7 @@ const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z

View File

@ -49,7 +49,7 @@ const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z.object({

View File

@ -85,7 +85,7 @@ const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z

View File

@ -60,7 +60,7 @@ const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z.object({

View File

@ -49,7 +49,7 @@ const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z.object({

View File

@ -56,6 +56,7 @@ const BizDeployNodeConfigFormProviderAliyunWAF = () => {
name={[parentNamePath, "domain"]}
initialValue={initialValues.domain}
label={t("workflow_node.deploy.form.aliyun_waf_domain.label")}
extra={t("workflow_node.deploy.form.aliyun_waf_domain.help")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aliyun_waf_domain.tooltip") }}></span>}
>
@ -73,7 +74,7 @@ const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z.object({

View File

@ -31,8 +31,8 @@ const BizDeployNodeConfigFormProviderAzureKeyVault = () => {
name={[parentNamePath, "certificateName"]}
initialValue={initialValues.certificateName}
label={t("workflow_node.deploy.form.azure_keyvault_certificate_name.label")}
extra={t("workflow_node.deploy.form.azure_keyvault_certificate_name.help")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.azure_keyvault_certificate_name.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.azure_keyvault_certificate_name.placeholder")} />
</Form.Item>
@ -46,7 +46,7 @@ const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z.object({

View File

@ -79,6 +79,7 @@ const BizDeployNodeConfigFormProviderBaiduCloudAppBLB = () => {
name={[parentNamePath, "domain"]}
initialValue={initialValues.domain}
label={t("workflow_node.deploy.form.baiducloud_appblb_snidomain.label")}
extra={t("workflow_node.deploy.form.baiducloud_appblb_snidomain.help")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.baiducloud_appblb_snidomain.tooltip") }}></span>}
>
@ -98,7 +99,7 @@ const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z

View File

@ -79,6 +79,7 @@ const BizDeployNodeConfigFormProviderBaiduCloudBLB = () => {
name={[parentNamePath, "domain"]}
initialValue={initialValues.domain}
label={t("workflow_node.deploy.form.baiducloud_blb_snidomain.label")}
extra={t("workflow_node.deploy.form.baiducloud_blb_snidomain.help")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.baiducloud_blb_snidomain.tooltip") }}></span>}
>
@ -98,7 +99,7 @@ const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z

View File

@ -38,7 +38,7 @@ const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z.object({

View File

@ -33,6 +33,7 @@ const BizDeployNodeConfigFormProviderBaishanCDN = () => {
name={[parentNamePath, "certificateId"]}
initialValue={initialValues.certificateId}
label={t("workflow_node.deploy.form.baishan_cdn_certificate_id.label")}
extra={t("workflow_node.deploy.form.baishan_cdn_certificate_id.help")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.baishan_cdn_certificate_id.tooltip") }}></span>}
>
@ -48,7 +49,7 @@ const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z.object({

View File

@ -35,7 +35,7 @@ const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
const { t: _ } = i18n;
return z.object({

View File

@ -61,6 +61,7 @@ const BizDeployNodeConfigFormProviderBaotaPanelSite = () => {
name={[parentNamePath, "siteNames"]}
initialValue={initialValues.siteNames}
label={t("workflow_node.deploy.form.baotapanel_site_names.label")}
extra={t("workflow_node.deploy.form.baotapanel_site_names.help")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.baotapanel_site_names.tooltip") }}></span>}
>
@ -83,7 +84,7 @@ const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z

View File

@ -48,7 +48,7 @@ const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z.object({

View File

@ -48,7 +48,7 @@ const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z.object({

View File

@ -38,7 +38,7 @@ const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z.object({

View File

@ -38,7 +38,7 @@ const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z.object({

View File

@ -38,7 +38,7 @@ const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z.object({

View File

@ -85,7 +85,7 @@ const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z

View File

@ -38,7 +38,7 @@ const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z.object({

View File

@ -38,7 +38,7 @@ const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z.object({

View File

@ -74,7 +74,7 @@ const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z

View File

@ -38,7 +38,7 @@ const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z.object({

View File

@ -36,7 +36,7 @@ const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z.object({

View File

@ -58,7 +58,7 @@ const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z

View File

@ -31,6 +31,7 @@ const BizDeployNodeConfigFormProviderGcoreCDN = () => {
name={[parentNamePath, "certificateId"]}
initialValue={initialValues.certificateId}
label={t("workflow_node.deploy.form.gcore_cdn_certificate_id.label")}
extra={t("workflow_node.deploy.form.gcore_cdn_certificate_id.help")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.gcore_cdn_certificate_id.tooltip") }}></span>}
>
@ -46,7 +47,7 @@ const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z.object({

View File

@ -58,7 +58,7 @@ const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z

View File

@ -49,7 +49,7 @@ const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z.object({

View File

@ -101,7 +101,7 @@ const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z

View File

@ -89,7 +89,7 @@ const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z

View File

@ -81,6 +81,7 @@ const BizDeployNodeConfigFormProviderJDCloudALB = () => {
name={[parentNamePath, "domain"]}
initialValue={initialValues.domain}
label={t("workflow_node.deploy.form.jdcloud_alb_snidomain.label")}
extra={t("workflow_node.deploy.form.jdcloud_alb_snidomain.help")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.jdcloud_alb_snidomain.tooltip") }}></span>}
>
@ -98,7 +99,7 @@ const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z

View File

@ -38,7 +38,7 @@ const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z.object({

View File

@ -38,7 +38,7 @@ const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z.object({

Some files were not shown because too many files have changed in this diff Show More