{t("access.props.provider.builtin")}
diff --git a/ui/src/components/provider/CAProviderSelect.tsx b/ui/src/components/provider/CAProviderSelect.tsx
index c74059c6..ab89bd06 100644
--- a/ui/src/components/provider/CAProviderSelect.tsx
+++ b/ui/src/components/provider/CAProviderSelect.tsx
@@ -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 (
- {t("provider.text.default_ca_provider.label")}
+ {t("provider.text.default_ca_provider")}
);
diff --git a/ui/src/components/provider/DeploymentProviderPicker.tsx b/ui/src/components/provider/DeploymentProviderPicker.tsx
index 8b91a187..5e3dddcc 100644
--- a/ui/src/components/provider/DeploymentProviderPicker.tsx
+++ b/ui/src/components/provider/DeploymentProviderPicker.tsx
@@ -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
(null);
const wrapperSize = useSize(wrapperRef);
+ const [isAvailableOnly, setIsAvailableOnly] = useState(true);
+
const [category, setCategory] = useState(DEPLOYMENT_CATEGORIES.ALL);
const [keyword, setKeyword] = useState();
@@ -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
setKeyword(e.target.value.trim())} />
+
+
+ setIsAvailableOnly(!isAvailableOnly)}>
+ {t("provider.text.show_available_hosting_provider_only")}
+
+
+
+
-
-
-
} shape="square" size={28} />
-
-
+
+
} shape="square" size={28} />
+
+
+
{t(provider.name) || "\u00A0"}
-
+
-
+
);
diff --git a/ui/src/components/workflow/WorkflowElement.tsx b/ui/src/components/workflow/WorkflowElement.tsx
index 4e827f8d..7b1325a2 100644
--- a/ui/src/components/workflow/WorkflowElement.tsx
+++ b/ui/src/components/workflow/WorkflowElement.tsx
@@ -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;
diff --git a/ui/src/components/workflow/WorkflowElements.tsx b/ui/src/components/workflow/WorkflowElements.tsx
index d3e772e3..c9d64876 100644
--- a/ui/src/components/workflow/WorkflowElements.tsx
+++ b/ui/src/components/workflow/WorkflowElements.tsx
@@ -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;
diff --git a/ui/src/components/workflow/WorkflowElementsContainer.tsx b/ui/src/components/workflow/WorkflowElementsContainer.tsx
index 5cd0f29e..339315bc 100644
--- a/ui/src/components/workflow/WorkflowElementsContainer.tsx
+++ b/ui/src/components/workflow/WorkflowElementsContainer.tsx
@@ -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;
diff --git a/ui/src/components/workflow/WorkflowRunDetail.tsx b/ui/src/components/workflow/WorkflowRunDetail.tsx
index 87bbef85..73efbedc 100644
--- a/ui/src/components/workflow/WorkflowRunDetail.tsx
+++ b/ui/src/components/workflow/WorkflowRunDetail.tsx
@@ -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 (
-
-
- {t(`workflow_run.props.status.${runStatus}`)}
-
- );
- };
-
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
{t("workflow_run.logs")}
-
{renderBadge()}
+
+
+
{
+ 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 (
+
+ {type === "filled" ? : }
+
+ );
+ case WORKFLOW_RUN_STATUSES.RUNNING:
+ return (
+
+
+
+ );
+ case WORKFLOW_RUN_STATUSES.SUCCEEDED:
+ return (
+
+ {type === "filled" ? : }
+
+ );
+ case WORKFLOW_RUN_STATUSES.FAILED:
+ return (
+
+ {type === "filled" ? : }
+
+ );
+ case WORKFLOW_RUN_STATUSES.CANCELED:
+ return (
+
+
+
+ );
+ default:
+ return (
+
+
+
+ );
+ }
+};
+
+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 ? : 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 (
+
+
+ {renderIcon()}
+ {children != null ? children : {t(`workflow_run.props.status.${value.toLowerCase()}`)}}
+
+
+ );
+ default:
+ return (
+
+ {children != null ? children : <>>}
+
+ );
+ }
+};
+
+const _default = Object.assign(WorkflowStatus, {
+ Icon: WorkflowStatusIcon,
+});
+
+export default _default;
diff --git a/ui/src/components/workflow/WorkflowStatusIcon.tsx b/ui/src/components/workflow/WorkflowStatusIcon.tsx
deleted file mode 100644
index 04d4d2d3..00000000
--- a/ui/src/components/workflow/WorkflowStatusIcon.tsx
+++ /dev/null
@@ -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 (
-
-
-
- );
- case WORKFLOW_RUN_STATUSES.RUNNING:
- if (color === true) {
- color = themeToken.colorInfo;
- }
- return (
-
-
-
- );
- case WORKFLOW_RUN_STATUSES.SUCCEEDED:
- if (color === true) {
- color = themeToken.colorSuccess;
- }
- return (
-
-
-
- );
- case WORKFLOW_RUN_STATUSES.FAILED:
- if (color === true) {
- color = themeToken.colorError;
- }
- return (
-
-
-
- );
- case WORKFLOW_RUN_STATUSES.CANCELED:
- if (color === true) {
- color = themeToken.colorWarning;
- }
- return (
-
-
-
- );
- default:
- if (color === true) {
- color = themeToken.colorTextSecondary;
- }
- return (
-
-
-
- );
- }
-};
-
-export default WorkflowStatusIcon;
diff --git a/ui/src/components/workflow/WorkflowStatusTag.tsx b/ui/src/components/workflow/WorkflowStatusTag.tsx
deleted file mode 100644
index 12a645d9..00000000
--- a/ui/src/components/workflow/WorkflowStatusTag.tsx
+++ /dev/null
@@ -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 = ;
-
- switch (status) {
- case WORKFLOW_RUN_STATUSES.PENDING:
- return (
-
- {t("workflow_run.props.status.pending")}
-
- );
- case WORKFLOW_RUN_STATUSES.RUNNING:
- return (
-
- {t("workflow_run.props.status.running")}
-
- );
- case WORKFLOW_RUN_STATUSES.SUCCEEDED:
- return (
-
- {t("workflow_run.props.status.succeeded")}
-
- );
- case WORKFLOW_RUN_STATUSES.FAILED:
- return (
-
- {t("workflow_run.props.status.failed")}
-
- );
- case WORKFLOW_RUN_STATUSES.CANCELED:
- return (
-
- {t("workflow_run.props.status.canceled")}
-
- );
- default:
- return <>>;
- }
-};
-
-export default WorkflowStatusTag;
diff --git a/ui/src/components/workflow/designer/Editor.tsx b/ui/src/components/workflow/designer/Designer.tsx
similarity index 86%
rename from ui/src/components/workflow/designer/Editor.tsx
rename to ui/src/components/workflow/designer/Designer.tsx
index 218ac8ca..bee18f0a 100644
--- a/ui/src/components/workflow/designer/Editor.tsx
+++ b/ui/src/components/workflow/designer/Designer.tsx
@@ -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;
validateAllNodes(): Promise;
}
-const Editor = forwardRef(({ className, style, children, initialData, readonly, onNodeClick }, ref) => {
+const Designer = forwardRef(({ className, style, children, initialData, readonly, onNodeClick }, ref) => {
const { token: themeToken } = theme.useToken();
const flowgramEditorRef = useRef(null);
@@ -80,16 +80,16 @@ const Editor = forwardRef(({ 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(({ className, style, chil
return (
- onNodeClick?.(flowgramEditorRef.current!, node) }}>
+ onNodeClick?.(flowgramEditorRef.current!, node) }}>
{children}
-
+
);
});
-export default Editor;
+export default Designer;
diff --git a/ui/src/components/workflow/designer/DesignerContext.ts b/ui/src/components/workflow/designer/DesignerContext.ts
new file mode 100644
index 00000000..4db5975e
--- /dev/null
+++ b/ui/src/components/workflow/designer/DesignerContext.ts
@@ -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({
+ 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;
+};
diff --git a/ui/src/components/workflow/designer/EditorContext.ts b/ui/src/components/workflow/designer/EditorContext.ts
deleted file mode 100644
index fe36fcd3..00000000
--- a/ui/src/components/workflow/designer/EditorContext.ts
+++ /dev/null
@@ -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({
- 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;
-};
diff --git a/ui/src/components/workflow/designer/NodeDrawer.tsx b/ui/src/components/workflow/designer/NodeDrawer.tsx
index 60664856..0c6e26df 100644
--- a/ui/src/components/workflow/designer/NodeDrawer.tsx
+++ b/ui/src/components/workflow/designer/NodeDrawer.tsx
@@ -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(props, {
valuePropName: "open",
defaultValuePropName: "defaultOpen",
diff --git a/ui/src/components/workflow/designer/NodeRender.tsx b/ui/src/components/workflow/designer/NodeRender.tsx
index a0c80376..e1553678 100644
--- a/ui/src/components/workflow/designer/NodeRender.tsx
+++ b/ui/src/components/workflow/designer/NodeRender.tsx
@@ -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().meta?.clickable) {
- onNodeClick?.(node);
+ designer.onNodeClick?.(node);
}
};
diff --git a/ui/src/components/workflow/designer/components/Adder.tsx b/ui/src/components/workflow/designer/components/Adder.tsx
deleted file mode 100644
index ecb840e8..00000000
--- a/ui/src/components/workflow/designer/components/Adder.tsx
+++ /dev/null
@@ -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: {Icon && },
- onClick: () => {
- const block = operation.addFromNode(from, registry.onAdd!(ctx, from));
-
- setTimeout(() => {
- playground.scrollToView({
- bounds: block.bounds,
- scrollToCenter: true,
- });
- }, 1);
- },
- };
- });
-
- return playground.config.readonlyOrDisabled ? null : (
-
-
- {hoverActivated ? (
- } shape="circle" size="small" type="primary" />
- ) : (
-
- )}
-
-
- );
-};
-
-export default Adder;
diff --git a/ui/src/components/workflow/designer/elements/Adder.tsx b/ui/src/components/workflow/designer/elements/Adder.tsx
new file mode 100644
index 00000000..ef2307ca
--- /dev/null
+++ b/ui/src/components/workflow/designer/elements/Adder.tsx
@@ -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: {NodeIcon && },
+ onClick: () => {
+ const block = operation.addFromNode(from, registry.onAdd!(ctx, from));
+
+ setTimeout(() => {
+ playground.scrollToView({
+ bounds: block.bounds,
+ scrollToCenter: true,
+ });
+ }, 1);
+ },
+ });
+ }
+
+ return acc;
+ },
+ [] as Required["items"]
+ );
+
+ return playground.config.readonlyOrDisabled ? null : (
+
+
+ {hoverActivated || menuOpen ? (
+ } shape="circle" size="small" type="primary" />
+ ) : (
+
+ )}
+
+
+ );
+};
+
+export default Adder;
diff --git a/ui/src/components/workflow/designer/components/BranchAdder.tsx b/ui/src/components/workflow/designer/elements/BranchAdder.tsx
similarity index 100%
rename from ui/src/components/workflow/designer/components/BranchAdder.tsx
rename to ui/src/components/workflow/designer/elements/BranchAdder.tsx
diff --git a/ui/src/components/workflow/designer/components/Collapse.tsx b/ui/src/components/workflow/designer/elements/Collapse.tsx
similarity index 100%
rename from ui/src/components/workflow/designer/components/Collapse.tsx
rename to ui/src/components/workflow/designer/elements/Collapse.tsx
diff --git a/ui/src/components/workflow/designer/components/DragHighlightAdder.tsx b/ui/src/components/workflow/designer/elements/DragHighlightAdder.tsx
similarity index 100%
rename from ui/src/components/workflow/designer/components/DragHighlightAdder.tsx
rename to ui/src/components/workflow/designer/elements/DragHighlightAdder.tsx
diff --git a/ui/src/components/workflow/designer/components/DragNode.tsx b/ui/src/components/workflow/designer/elements/DragNode.tsx
similarity index 100%
rename from ui/src/components/workflow/designer/components/DragNode.tsx
rename to ui/src/components/workflow/designer/elements/DragNode.tsx
diff --git a/ui/src/components/workflow/designer/components/DraggingAdder.tsx b/ui/src/components/workflow/designer/elements/DraggingAdder.tsx
similarity index 100%
rename from ui/src/components/workflow/designer/components/DraggingAdder.tsx
rename to ui/src/components/workflow/designer/elements/DraggingAdder.tsx
diff --git a/ui/src/components/workflow/designer/components/Null.tsx b/ui/src/components/workflow/designer/elements/Null.tsx
similarity index 100%
rename from ui/src/components/workflow/designer/components/Null.tsx
rename to ui/src/components/workflow/designer/elements/Null.tsx
diff --git a/ui/src/components/workflow/designer/components/TryCatchCollapse.tsx b/ui/src/components/workflow/designer/elements/TryCatchCollapse.tsx
similarity index 100%
rename from ui/src/components/workflow/designer/components/TryCatchCollapse.tsx
rename to ui/src/components/workflow/designer/elements/TryCatchCollapse.tsx
diff --git a/ui/src/components/workflow/designer/components/index.ts b/ui/src/components/workflow/designer/elements/index.ts
similarity index 96%
rename from ui/src/components/workflow/designer/components/index.ts
rename to ui/src/components/workflow/designer/elements/index.ts
index 90fb868a..f975f718 100644
--- a/ui/src/components/workflow/designer/components/index.ts
+++ b/ui/src/components/workflow/designer/elements/index.ts
@@ -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,
diff --git a/ui/src/components/workflow/designer/forms/BizApplyNodeConfigDrawer.tsx b/ui/src/components/workflow/designer/forms/BizApplyNodeConfigDrawer.tsx
index 08dede4f..6d736aa8 100644
--- a/ui/src/components/workflow/designer/forms/BizApplyNodeConfigDrawer.tsx
+++ b/ui/src/components/workflow/designer/forms/BizApplyNodeConfigDrawer.tsx
@@ -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}`);
}
diff --git a/ui/src/components/workflow/designer/forms/BizApplyNodeConfigForm.tsx b/ui/src/components/workflow/designer/forms/BizApplyNodeConfigForm.tsx
index 76fd62bf..57a22030 100644
--- a/ui/src/components/workflow/designer/forms/BizApplyNodeConfigForm.tsx
+++ b/ui/src/components/workflow/designer/forms/BizApplyNodeConfigForm.tsx
@@ -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)
-
+
@@ -72,7 +72,7 @@ const BizMonitorNodeConfigForm = ({ node, ...props }: BizMonitorNodeConfigFormPr
);
};
-const getAnchorItems = ({ i18n = getI18n() }: { i18n: ReturnType }): Required["items"] => {
+const getAnchorItems = ({ i18n = getI18n() }: { i18n?: ReturnType }): Required["items"] => {
const { t } = i18n;
return ["parameters"].map((key) => ({
@@ -86,7 +86,7 @@ const getInitialValues = (): Nullish>> => {
return defaultNodeConfigForMonitor();
};
-const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType }) => {
+const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType }) => {
const { t } = i18n;
return z.object({
diff --git a/ui/src/components/workflow/designer/forms/BizNotifyNodeConfigDrawer.tsx b/ui/src/components/workflow/designer/forms/BizNotifyNodeConfigDrawer.tsx
index faf51db5..a88c08a7 100644
--- a/ui/src/components/workflow/designer/forms/BizNotifyNodeConfigDrawer.tsx
+++ b/ui/src/components/workflow/designer/forms/BizNotifyNodeConfigDrawer.tsx
@@ -13,8 +13,7 @@ export interface BizNotifyNodeConfigDrawerProps {
onOpenChange?: (open: boolean) => void;
}
-const BizNotifyNodeConfigDrawer = (_: BizNotifyNodeConfigDrawerProps) => {
- const { node, ...props } = _;
+const BizNotifyNodeConfigDrawer = ({ node, ...props }: BizNotifyNodeConfigDrawerProps) => {
if (node.flowNodeType !== NodeType.BizNotify) {
console.warn(`[certimate] current workflow node type is not: ${NodeType.BizNotify}`);
}
diff --git a/ui/src/components/workflow/designer/forms/BizNotifyNodeConfigForm.tsx b/ui/src/components/workflow/designer/forms/BizNotifyNodeConfigForm.tsx
index 888b441f..b8e8be6b 100644
--- a/ui/src/components/workflow/designer/forms/BizNotifyNodeConfigForm.tsx
+++ b/ui/src/components/workflow/designer/forms/BizNotifyNodeConfigForm.tsx
@@ -134,34 +134,27 @@ const BizNotifyNodeConfigForm = ({ node, ...props }: BizNotifyNodeConfigFormProp
/>
-
-
-
+
+
+
+ {t("workflow_node.notify.form.provider_access.button")}
+
+
+ }
+ usage="notification"
+ afterSubmit={(record) => {
+ const provider = accessProvidersMap.get(record.provider);
+ if (provider?.usages?.includes(ACCESS_USAGES.NOTIFICATION)) {
+ formInst.setFieldValue("providerAccessId", record.id);
+ handleProviderAccessSelect(record.id);
+ }
+ }}
+ />
+
+
}): Required["items"] => {
+const getAnchorItems = ({ i18n = getI18n() }: { i18n?: ReturnType }): Required["items"] => {
const { t } = i18n;
return ["parameters", "strategy"].map((key) => ({
@@ -226,7 +219,7 @@ const getInitialValues = (): Nullish>> => {
};
};
-const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType }) => {
+const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType }) => {
const { t } = i18n;
return z.object({
diff --git a/ui/src/components/workflow/designer/forms/BizNotifyNodeConfigFormProviderDiscordBot.tsx b/ui/src/components/workflow/designer/forms/BizNotifyNodeConfigFormProviderDiscordBot.tsx
index 83bc2cae..3a395f38 100644
--- a/ui/src/components/workflow/designer/forms/BizNotifyNodeConfigFormProviderDiscordBot.tsx
+++ b/ui/src/components/workflow/designer/forms/BizNotifyNodeConfigFormProviderDiscordBot.tsx
@@ -21,8 +21,8 @@ const BizNotifyNodeConfigFormProviderDiscordBot = () => {
name={[parentNamePath, "channelId"]}
initialValue={initialValues.channelId}
label={t("workflow_node.notify.form.discordbot_channel_id.label")}
+ extra={t("workflow_node.notify.form.discordbot_channel_id.help")}
rules={[formRule]}
- tooltip={}
>
@@ -34,7 +34,7 @@ const getInitialValues = (): Nullish>> => {
return {};
};
-const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType }) => {
+const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType }) => {
const { t: _ } = i18n;
return z.object({
diff --git a/ui/src/components/workflow/designer/forms/BizNotifyNodeConfigFormProviderEmail.tsx b/ui/src/components/workflow/designer/forms/BizNotifyNodeConfigFormProviderEmail.tsx
index de878e59..65d5c848 100644
--- a/ui/src/components/workflow/designer/forms/BizNotifyNodeConfigFormProviderEmail.tsx
+++ b/ui/src/components/workflow/designer/forms/BizNotifyNodeConfigFormProviderEmail.tsx
@@ -23,8 +23,8 @@ const BizNotifyNodeConfigFormProviderEmail = () => {
name={[parentNamePath, "receiverAddress"]}
initialValue={initialValues.receiverAddress}
label={t("workflow_node.notify.form.email_receiver_address.label")}
+ extra={t("workflow_node.notify.form.email_receiver_address.help")}
rules={[formRule]}
- tooltip={}
>
@@ -36,7 +36,7 @@ const getInitialValues = (): Nullish>> => {
return {};
};
-const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType }) => {
+const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType }) => {
const { t } = i18n;
return z.object({
diff --git a/ui/src/components/workflow/designer/forms/BizNotifyNodeConfigFormProviderMattermost.tsx b/ui/src/components/workflow/designer/forms/BizNotifyNodeConfigFormProviderMattermost.tsx
index 53e8692d..89f44277 100644
--- a/ui/src/components/workflow/designer/forms/BizNotifyNodeConfigFormProviderMattermost.tsx
+++ b/ui/src/components/workflow/designer/forms/BizNotifyNodeConfigFormProviderMattermost.tsx
@@ -21,8 +21,8 @@ const BizNotifyNodeConfigFormProviderMattermost = () => {
name={[parentNamePath, "channelId"]}
initialValue={initialValues.channelId}
label={t("workflow_node.notify.form.mattermost_channel_id.label")}
+ extra={t("workflow_node.notify.form.mattermost_channel_id.help")}
rules={[formRule]}
- tooltip={}
>
@@ -34,7 +34,7 @@ const getInitialValues = (): Nullish>> => {
return {};
};
-const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType }) => {
+const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType }) => {
const { t: _ } = i18n;
return z.object({
diff --git a/ui/src/components/workflow/designer/forms/BizNotifyNodeConfigFormProviderSlackBot.tsx b/ui/src/components/workflow/designer/forms/BizNotifyNodeConfigFormProviderSlackBot.tsx
index 761b11c6..286d5c40 100644
--- a/ui/src/components/workflow/designer/forms/BizNotifyNodeConfigFormProviderSlackBot.tsx
+++ b/ui/src/components/workflow/designer/forms/BizNotifyNodeConfigFormProviderSlackBot.tsx
@@ -21,8 +21,8 @@ const BizNotifyNodeConfigFormProviderSlackBot = () => {
name={[parentNamePath, "channelId"]}
initialValue={initialValues.channelId}
label={t("workflow_node.notify.form.slackbot_channel_id.label")}
+ extra={t("workflow_node.notify.form.slackbot_channel_id.help")}
rules={[formRule]}
- tooltip={}
>
@@ -34,7 +34,7 @@ const getInitialValues = (): Nullish
>> => {
return {};
};
-const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType }) => {
+const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType }) => {
const { t: _ } = i18n;
return z.object({
diff --git a/ui/src/components/workflow/designer/forms/BizNotifyNodeConfigFormProviderTelegramBot.tsx b/ui/src/components/workflow/designer/forms/BizNotifyNodeConfigFormProviderTelegramBot.tsx
index 1d4a5996..c085e79e 100644
--- a/ui/src/components/workflow/designer/forms/BizNotifyNodeConfigFormProviderTelegramBot.tsx
+++ b/ui/src/components/workflow/designer/forms/BizNotifyNodeConfigFormProviderTelegramBot.tsx
@@ -21,8 +21,8 @@ const BizNotifyNodeConfigFormProviderTelegramBot = () => {
name={[parentNamePath, "chatId"]}
initialValue={initialValues.chatId}
label={t("workflow_node.notify.form.telegrambot_chat_id.label")}
+ extra={t("workflow_node.notify.form.telegrambot_chat_id.help")}
rules={[formRule]}
- tooltip={}
>
@@ -34,7 +34,7 @@ const getInitialValues = (): Nullish>> => {
return {};
};
-const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType }) => {
+const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType }) => {
const { t } = i18n;
return z.object({
diff --git a/ui/src/components/workflow/designer/forms/BizNotifyNodeConfigFormProviderWebhook.tsx b/ui/src/components/workflow/designer/forms/BizNotifyNodeConfigFormProviderWebhook.tsx
index cccf3cff..005e297b 100644
--- a/ui/src/components/workflow/designer/forms/BizNotifyNodeConfigFormProviderWebhook.tsx
+++ b/ui/src/components/workflow/designer/forms/BizNotifyNodeConfigFormProviderWebhook.tsx
@@ -20,10 +20,10 @@ const BizNotifyNodeConfigFormProviderWebhook = () => {
const initialValues = getInitialValues();
const handleWebhookDataBlur = () => {
- const value = formInst.getFieldValue("webhookData");
+ const value = formInst.getFieldValue([parentNamePath, "webhookData"]);
try {
const json = JSON.stringify(JSON.parse(value), null, 2);
- formInst.setFieldValue("webhookData", json);
+ formInst.setFieldValue([parentNamePath, "webhookData"], json);
} catch {
return;
}
@@ -35,8 +35,8 @@ const BizNotifyNodeConfigFormProviderWebhook = () => {
name={[parentNamePath, "webhookData"]}
initialValue={initialValues.webhookData}
label={t("workflow_node.notify.form.webhook_data.label")}
+ extra={t("workflow_node.notify.form.webhook_data.help")}
rules={[formRule]}
- tooltip={}
>
>> => {
return {};
};
-const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType }) => {
+const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType }) => {
const { t } = i18n;
return z.object({
diff --git a/ui/src/components/workflow/designer/forms/BizUploadNodeConfigDrawer.tsx b/ui/src/components/workflow/designer/forms/BizUploadNodeConfigDrawer.tsx
index daf14007..826024b8 100644
--- a/ui/src/components/workflow/designer/forms/BizUploadNodeConfigDrawer.tsx
+++ b/ui/src/components/workflow/designer/forms/BizUploadNodeConfigDrawer.tsx
@@ -13,8 +13,7 @@ export interface BizUploadNodeConfigDrawerProps {
onOpenChange?: (open: boolean) => void;
}
-const BizUploadNodeConfigDrawer = (_: BizUploadNodeConfigDrawerProps) => {
- const { node, ...props } = _;
+const BizUploadNodeConfigDrawer = ({ node, ...props }: BizUploadNodeConfigDrawerProps) => {
if (node.flowNodeType !== NodeType.BizUpload) {
console.warn(`[certimate] current workflow node type is not: ${NodeType.BizUpload}`);
}
diff --git a/ui/src/components/workflow/designer/forms/BizUploadNodeConfigForm.tsx b/ui/src/components/workflow/designer/forms/BizUploadNodeConfigForm.tsx
index 1d9bf152..05a38702 100644
--- a/ui/src/components/workflow/designer/forms/BizUploadNodeConfigForm.tsx
+++ b/ui/src/components/workflow/designer/forms/BizUploadNodeConfigForm.tsx
@@ -115,7 +115,7 @@ const BizUploadNodeConfigForm = ({ node, ...props }: BizUploadNodeConfigFormProp
);
};
-const getAnchorItems = ({ i18n = getI18n() }: { i18n: ReturnType }): Required["items"] => {
+const getAnchorItems = ({ i18n = getI18n() }: { i18n?: ReturnType }): Required["items"] => {
const { t } = i18n;
return ["parameters"].map((key) => ({
@@ -133,7 +133,7 @@ const getInitialValues = (): Nullish>> => {
};
};
-const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType }) => {
+const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType }) => {
const { t } = i18n;
return z.object({
diff --git a/ui/src/components/workflow/designer/forms/BranchBlockNodeConfigDrawer.tsx b/ui/src/components/workflow/designer/forms/BranchBlockNodeConfigDrawer.tsx
index f1b7d103..98530101 100644
--- a/ui/src/components/workflow/designer/forms/BranchBlockNodeConfigDrawer.tsx
+++ b/ui/src/components/workflow/designer/forms/BranchBlockNodeConfigDrawer.tsx
@@ -13,8 +13,7 @@ export interface BranchBlockNodeConfigDrawerProps {
onOpenChange?: (open: boolean) => void;
}
-const BranchBlockNodeConfigDrawer = (_: BranchBlockNodeConfigDrawerProps) => {
- const { node, ...props } = _;
+const BranchBlockNodeConfigDrawer = ({ node, ...props }: BranchBlockNodeConfigDrawerProps) => {
if (node.flowNodeType !== NodeType.BranchBlock) {
console.warn(`[certimate] current workflow node type is not: ${NodeType.BranchBlock}`);
}
diff --git a/ui/src/components/workflow/designer/forms/BranchBlockNodeConfigForm.tsx b/ui/src/components/workflow/designer/forms/BranchBlockNodeConfigForm.tsx
index b915388c..e9d7557f 100644
--- a/ui/src/components/workflow/designer/forms/BranchBlockNodeConfigForm.tsx
+++ b/ui/src/components/workflow/designer/forms/BranchBlockNodeConfigForm.tsx
@@ -64,7 +64,7 @@ const BranchBlockNodeConfigForm = ({ node, ...props }: BranchBlockNodeConfigForm
);
};
-const getAnchorItems = ({ i18n = getI18n() }: { i18n: ReturnType }): Required["items"] => {
+const getAnchorItems = ({ i18n = getI18n() }: { i18n?: ReturnType }): Required["items"] => {
const { t } = i18n;
return ["parameters"].map((key) => ({
@@ -78,7 +78,7 @@ const getInitialValues = (): Nullish>> => {
return defaultNodeConfigForCondition();
};
-const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType }) => {
+const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType }) => {
const { t: _ } = i18n;
return z.object({
diff --git a/ui/src/components/workflow/designer/forms/StartNodeConfigDrawer.tsx b/ui/src/components/workflow/designer/forms/StartNodeConfigDrawer.tsx
index 8b59e30a..0f02fe6c 100644
--- a/ui/src/components/workflow/designer/forms/StartNodeConfigDrawer.tsx
+++ b/ui/src/components/workflow/designer/forms/StartNodeConfigDrawer.tsx
@@ -13,8 +13,7 @@ export interface StartNodeConfigDrawerProps {
onOpenChange?: (open: boolean) => void;
}
-const StartNodeConfigDrawer = (_: StartNodeConfigDrawerProps) => {
- const { node, ...props } = _;
+const StartNodeConfigDrawer = ({ node, ...props }: StartNodeConfigDrawerProps) => {
if (node.flowNodeType !== NodeType.Start) {
console.warn(`[certimate] current workflow node type is not: ${NodeType.Start}`);
}
diff --git a/ui/src/components/workflow/designer/forms/StartNodeConfigForm.tsx b/ui/src/components/workflow/designer/forms/StartNodeConfigForm.tsx
index 163768b3..9c458ca8 100644
--- a/ui/src/components/workflow/designer/forms/StartNodeConfigForm.tsx
+++ b/ui/src/components/workflow/designer/forms/StartNodeConfigForm.tsx
@@ -112,7 +112,7 @@ const StartNodeConfigForm = ({ node, ...props }: StartNodeConfigFormProps) => {
);
};
-const getAnchorItems = ({ i18n = getI18n() }: { i18n: ReturnType }): Required["items"] => {
+const getAnchorItems = ({ i18n = getI18n() }: { i18n?: ReturnType }): Required["items"] => {
const { t } = i18n;
return ["parameters"].map((key) => ({
@@ -129,7 +129,7 @@ const getInitialValues = (): Nullish>> => {
};
};
-const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType }) => {
+const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType }) => {
const { t } = i18n;
return z
diff --git a/ui/src/components/workflow/designer/forms/_shared.tsx b/ui/src/components/workflow/designer/forms/_shared.tsx
index 85d90669..6ca67a85 100644
--- a/ui/src/components/workflow/designer/forms/_shared.tsx
+++ b/ui/src/components/workflow/designer/forms/_shared.tsx
@@ -21,9 +21,7 @@ export interface NodeConfigDrawerProps {
onOpenChange?: (open: boolean) => void;
}
-export const NodeConfigDrawer = (_: NodeConfigDrawerProps) => {
- const { children, anchor, footer = true, form: formInst, loading, node, ...props } = _;
-
+export const NodeConfigDrawer = ({ children, anchor, footer = true, form: formInst, loading, node, ...props }: NodeConfigDrawerProps) => {
const { t } = useTranslation();
const { modal, notification } = App.useApp();
diff --git a/ui/src/components/workflow/designer/nodes/BizApplyNodeRegistry.tsx b/ui/src/components/workflow/designer/nodes/BizApplyNodeRegistry.tsx
index de3b99d9..4c1e0b7b 100644
--- a/ui/src/components/workflow/designer/nodes/BizApplyNodeRegistry.tsx
+++ b/ui/src/components/workflow/designer/nodes/BizApplyNodeRegistry.tsx
@@ -7,10 +7,12 @@ import { nanoid } from "nanoid";
import { acmeDns01ProvidersMap } from "@/domain/provider";
import { BaseNode } from "./_shared";
-import { type NodeRegistry, NodeType } from "./typings";
+import { NodeKindType, type NodeRegistry, NodeType } from "./typings";
+import BizApplyNodeConfigForm from "../forms/BizApplyNodeConfigForm";
export const BizApplyNodeRegistry: NodeRegistry = {
type: NodeType.BizApply,
+ kindType: NodeKindType.Business,
meta: {
helpText: getI18n().t("workflow_node.apply.help"),
@@ -25,34 +27,11 @@ export const BizApplyNodeRegistry: NodeRegistry = {
formMeta: {
validate: {
- ["config.domains"]: ({ value }) => {
- if (!value) {
+ ["config"]: ({ value }) => {
+ const res = BizApplyNodeConfigForm.getSchema({}).safeParse(value);
+ if (!res.success) {
return {
- message: "required",
- level: FeedbackLevel.Error,
- };
- }
- },
- ["config.contactEmail"]: ({ value }) => {
- if (!value) {
- return {
- message: "required",
- level: FeedbackLevel.Error,
- };
- }
- },
- ["config.provider"]: ({ value }) => {
- if (!value) {
- return {
- message: "required",
- level: FeedbackLevel.Error,
- };
- }
- },
- ["config.providerAccessId"]: ({ value }) => {
- if (!value) {
- return {
- message: "required",
+ message: res.error.message,
level: FeedbackLevel.Error,
};
}
diff --git a/ui/src/components/workflow/designer/nodes/BizDeployNodeRegistry.tsx b/ui/src/components/workflow/designer/nodes/BizDeployNodeRegistry.tsx
index d48c6192..c89e721c 100644
--- a/ui/src/components/workflow/designer/nodes/BizDeployNodeRegistry.tsx
+++ b/ui/src/components/workflow/designer/nodes/BizDeployNodeRegistry.tsx
@@ -7,10 +7,12 @@ import { nanoid } from "nanoid";
import { deploymentProvidersMap } from "@/domain/provider";
import { BaseNode } from "./_shared";
-import { type NodeRegistry, NodeType } from "./typings";
+import { NodeKindType, type NodeRegistry, NodeType } from "./typings";
+import BizDeployNodeConfigForm from "../forms/BizDeployNodeConfigForm";
export const BizDeployNodeRegistry: NodeRegistry = {
type: NodeType.BizDeploy,
+ kindType: NodeKindType.Business,
meta: {
helpText: getI18n().t("workflow_node.deploy.help"),
@@ -25,18 +27,11 @@ export const BizDeployNodeRegistry: NodeRegistry = {
formMeta: {
validate: {
- ["config.provider"]: ({ value }) => {
- if (!value) {
+ ["config"]: ({ value }) => {
+ const res = BizDeployNodeConfigForm.getSchema({}).safeParse(value);
+ if (!res.success) {
return {
- message: "required",
- level: FeedbackLevel.Error,
- };
- }
- },
- ["config.providerAccessId"]: ({ value }) => {
- if (!value) {
- return {
- message: "required",
+ message: res.error.message,
level: FeedbackLevel.Error,
};
}
diff --git a/ui/src/components/workflow/designer/nodes/BizMonitorNodeRegistry.tsx b/ui/src/components/workflow/designer/nodes/BizMonitorNodeRegistry.tsx
index 80a6851b..eb0d518b 100644
--- a/ui/src/components/workflow/designer/nodes/BizMonitorNodeRegistry.tsx
+++ b/ui/src/components/workflow/designer/nodes/BizMonitorNodeRegistry.tsx
@@ -4,10 +4,12 @@ import { IconDeviceDesktopSearch } from "@tabler/icons-react";
import { nanoid } from "nanoid";
import { BaseNode } from "./_shared";
-import { type NodeRegistry, NodeType } from "./typings";
+import { NodeKindType, type NodeRegistry, NodeType } from "./typings";
+import BizMonitorNodeConfigForm from "../forms/BizMonitorNodeConfigForm";
export const BizMonitorNodeRegistry: NodeRegistry = {
type: NodeType.BizMonitor,
+ kindType: NodeKindType.Business,
meta: {
helpText: getI18n().t("workflow_node.monitor.help"),
@@ -22,10 +24,11 @@ export const BizMonitorNodeRegistry: NodeRegistry = {
formMeta: {
validate: {
- ["config.host"]: ({ value }) => {
- if (!value) {
+ ["config"]: ({ value }) => {
+ const res = BizMonitorNodeConfigForm.getSchema({}).safeParse(value);
+ if (!res.success) {
return {
- message: "required",
+ message: res.error.message,
level: FeedbackLevel.Error,
};
}
diff --git a/ui/src/components/workflow/designer/nodes/BizNotifyNodeRegistry.tsx b/ui/src/components/workflow/designer/nodes/BizNotifyNodeRegistry.tsx
index 4cd245d6..86763c50 100644
--- a/ui/src/components/workflow/designer/nodes/BizNotifyNodeRegistry.tsx
+++ b/ui/src/components/workflow/designer/nodes/BizNotifyNodeRegistry.tsx
@@ -7,10 +7,12 @@ import { nanoid } from "nanoid";
import { notificationProvidersMap } from "@/domain/provider";
import { BaseNode } from "./_shared";
-import { type NodeRegistry, NodeType } from "./typings";
+import { NodeKindType, type NodeRegistry, NodeType } from "./typings";
+import BizNotifyNodeConfigForm from "../forms/BizNotifyNodeConfigForm";
export const BizNotifyNodeRegistry: NodeRegistry = {
type: NodeType.BizNotify,
+ kindType: NodeKindType.Business,
meta: {
helpText: getI18n().t("workflow_node.notify.help"),
@@ -25,34 +27,11 @@ export const BizNotifyNodeRegistry: NodeRegistry = {
formMeta: {
validate: {
- ["config.subject"]: ({ value }) => {
- if (!value) {
+ ["config"]: ({ value }) => {
+ const res = BizNotifyNodeConfigForm.getSchema({}).safeParse(value);
+ if (!res.success) {
return {
- message: "required",
- level: FeedbackLevel.Error,
- };
- }
- },
- ["config.message"]: ({ value }) => {
- if (!value) {
- return {
- message: "required",
- level: FeedbackLevel.Error,
- };
- }
- },
- ["config.provider"]: ({ value }) => {
- if (!value) {
- return {
- message: "required",
- level: FeedbackLevel.Error,
- };
- }
- },
- ["config.providerAccessId"]: ({ value }) => {
- if (!value) {
- return {
- message: "required",
+ message: res.error.message,
level: FeedbackLevel.Error,
};
}
diff --git a/ui/src/components/workflow/designer/nodes/BizUploadNodeRegistry.tsx b/ui/src/components/workflow/designer/nodes/BizUploadNodeRegistry.tsx
index 2a7b7d1e..270b9ac9 100644
--- a/ui/src/components/workflow/designer/nodes/BizUploadNodeRegistry.tsx
+++ b/ui/src/components/workflow/designer/nodes/BizUploadNodeRegistry.tsx
@@ -4,10 +4,12 @@ import { IconCloudUpload } from "@tabler/icons-react";
import { nanoid } from "nanoid";
import { BaseNode } from "./_shared";
-import { type NodeRegistry, NodeType } from "./typings";
+import { NodeKindType, type NodeRegistry, NodeType } from "./typings";
+import BizUploadNodeConfigForm from "../forms/BizUploadNodeConfigForm";
export const BizUploadNodeRegistry: NodeRegistry = {
type: NodeType.BizUpload,
+ kindType: NodeKindType.Business,
meta: {
helpText: getI18n().t("workflow_node.upload.help"),
@@ -22,18 +24,11 @@ export const BizUploadNodeRegistry: NodeRegistry = {
formMeta: {
validate: {
- ["config.certificate"]: ({ value }) => {
- if (!value) {
+ ["config"]: ({ value }) => {
+ const res = BizUploadNodeConfigForm.getSchema({}).safeParse(value);
+ if (!res.success) {
return {
- message: "required",
- level: FeedbackLevel.Error,
- };
- }
- },
- ["config.privateKey"]: ({ value }) => {
- if (!value) {
- return {
- message: "required",
+ message: res.error.message,
level: FeedbackLevel.Error,
};
}
diff --git a/ui/src/components/workflow/designer/nodes/ConditionNode.tsx b/ui/src/components/workflow/designer/nodes/ConditionNode.tsx
index 1f306e3c..52f788a7 100644
--- a/ui/src/components/workflow/designer/nodes/ConditionNode.tsx
+++ b/ui/src/components/workflow/designer/nodes/ConditionNode.tsx
@@ -1,5 +1,5 @@
import { getI18n } from "react-i18next";
-import { Field, FlowNodeBaseType, FlowNodeSplitType } from "@flowgram.ai/fixed-layout-editor";
+import { FeedbackLevel, Field, FlowNodeBaseType, FlowNodeSplitType } from "@flowgram.ai/fixed-layout-editor";
import { IconFilter, IconFilterFilled, IconSitemap } from "@tabler/icons-react";
import { Typography } from "antd";
import { nanoid } from "nanoid";
@@ -7,10 +7,12 @@ import { nanoid } from "nanoid";
import { type Expr, ExprType } from "@/domain/workflow";
import { BaseNode, BranchNode } from "./_shared";
-import { type NodeRegistry, NodeType } from "./typings";
+import { NodeKindType, type NodeRegistry, NodeType } from "./typings";
+import BranchBlockNodeConfigForm from "../forms/BranchBlockNodeConfigForm";
export const ConditionNodeRegistry: NodeRegistry = {
type: NodeType.Condition,
+ kindType: NodeKindType.Logic,
extend: FlowNodeSplitType.DYNAMIC_SPLIT,
@@ -66,6 +68,7 @@ export const ConditionNodeRegistry: NodeRegistry = {
export const BranchBlockNodeRegistry: NodeRegistry = {
type: NodeType.BranchBlock,
+ kindType: NodeKindType.Logic,
extend: FlowNodeBaseType.BLOCK,
@@ -83,6 +86,18 @@ export const BranchBlockNodeRegistry: NodeRegistry = {
},
formMeta: {
+ validate: {
+ ["config"]: ({ value }) => {
+ const res = BranchBlockNodeConfigForm.getSchema({}).safeParse(value);
+ if (!res.success) {
+ return {
+ message: res.error.message,
+ level: FeedbackLevel.Error,
+ };
+ }
+ },
+ },
+
render: () => {
const { t } = getI18n();
diff --git a/ui/src/components/workflow/designer/nodes/EndNode.tsx b/ui/src/components/workflow/designer/nodes/EndNode.tsx
index 649e5650..f851f8b6 100644
--- a/ui/src/components/workflow/designer/nodes/EndNode.tsx
+++ b/ui/src/components/workflow/designer/nodes/EndNode.tsx
@@ -4,10 +4,11 @@ import { IconLogout } from "@tabler/icons-react";
import { nanoid } from "nanoid";
import { BaseNode } from "./_shared";
-import { type NodeRegistry, NodeType } from "./typings";
+import { NodeKindType, type NodeRegistry, NodeType } from "./typings";
export const EndNodeRegistry: NodeRegistry = {
type: NodeType.End,
+ kindType: NodeKindType.Common,
meta: {
helpText: getI18n().t("workflow_node.end.help"),
diff --git a/ui/src/components/workflow/designer/nodes/StartNode.tsx b/ui/src/components/workflow/designer/nodes/StartNode.tsx
index 6dbb4a98..64b60faf 100644
--- a/ui/src/components/workflow/designer/nodes/StartNode.tsx
+++ b/ui/src/components/workflow/designer/nodes/StartNode.tsx
@@ -5,10 +5,12 @@ import { IconRocket } from "@tabler/icons-react";
import { WORKFLOW_TRIGGERS } from "@/domain/workflow";
import { BaseNode } from "./_shared";
-import { type NodeRegistry, NodeType } from "./typings";
+import { NodeKindType, type NodeRegistry, NodeType } from "./typings";
+import StartNodeConfigForm from "../forms/StartNodeConfigForm";
export const StartNodeRegistry: NodeRegistry = {
type: NodeType.Start,
+ kindType: NodeKindType.Common,
meta: {
helpText: getI18n().t("workflow_node.start.help"),
@@ -31,18 +33,11 @@ export const StartNodeRegistry: NodeRegistry = {
formMeta: {
validate: {
- ["config.trigger"]: ({ value }) => {
- if (!value) {
+ ["config"]: ({ value }) => {
+ const res = StartNodeConfigForm.getSchema({}).safeParse(value);
+ if (!res.success) {
return {
- message: "required",
- level: FeedbackLevel.Error,
- };
- }
- },
- ["config.triggerCron"]: ({ value, formValues }) => {
- if (!value && formValues.config.trigger === WORKFLOW_TRIGGERS.SCHEDULED) {
- return {
- message: "required",
+ message: res.error.message,
level: FeedbackLevel.Error,
};
}
diff --git a/ui/src/components/workflow/designer/nodes/TryCatchNode.tsx b/ui/src/components/workflow/designer/nodes/TryCatchNode.tsx
index 0dca9bdf..7149bf3b 100644
--- a/ui/src/components/workflow/designer/nodes/TryCatchNode.tsx
+++ b/ui/src/components/workflow/designer/nodes/TryCatchNode.tsx
@@ -4,10 +4,11 @@ import { IconArrowsSplit, IconCircleX } from "@tabler/icons-react";
import { nanoid } from "nanoid";
import { BaseNode, BranchNode } from "./_shared";
-import { type NodeRegistry, NodeType } from "./typings";
+import { NodeKindType, type NodeRegistry, NodeType } from "./typings";
export const TryCatchNodeRegistry: NodeRegistry = {
type: NodeType.TryCatch,
+ kindType: NodeKindType.Logic,
meta: {
helpText: getI18n().t("workflow_node.try_catch.help"),
@@ -65,6 +66,7 @@ export const TryCatchNodeRegistry: NodeRegistry = {
export const CatchBlockNodeRegistry: NodeRegistry = {
type: NodeType.CatchBlock,
+ kindType: NodeKindType.Logic,
meta: {
labelText: getI18n().t("workflow_node.catch_block.label"),
diff --git a/ui/src/components/workflow/designer/nodes/_shared.tsx b/ui/src/components/workflow/designer/nodes/_shared.tsx
index 2c92c2e8..ac195cb9 100644
--- a/ui/src/components/workflow/designer/nodes/_shared.tsx
+++ b/ui/src/components/workflow/designer/nodes/_shared.tsx
@@ -228,13 +228,26 @@ const InternalNodeMenuButton = ({
onClick: handleClickRemove,
},
],
+ onClick: (e) => {
+ e.domEvent.stopPropagation();
+ },
}}
overlayStyle={{
zIndex: 10 /* 确保要比 Minimap 组件层级要高,防止被遮挡而点击不到 */,
}}
trigger={["click"]}
>
- } type="text" {...props} />
+ }
+ type="text"
+ {...props}
+ onClick={(e) => {
+ e.stopPropagation();
+ props.onClick?.(e);
+ }}
+ />
);
};
@@ -305,7 +318,14 @@ export const BaseNode = ({ className, style, children, description }: BaseNodePr
}
placement="rightTop"
>
-
+
{
+ if (inputVisible) {
+ e.stopPropagation();
+ }
+ }}
+ >
{children != null ? (
children
@@ -398,7 +418,14 @@ export const BranchNode = ({ className, style, children, description }: BranchNo
}
placement="right"
>
-
+
{
+ if (inputVisible) {
+ e.stopPropagation();
+ }
+ }}
+ >
{children != null ? (
children
diff --git a/ui/src/components/workflow/designer/nodes/index.ts b/ui/src/components/workflow/designer/nodes/index.ts
index 236de4ea..8826046f 100644
--- a/ui/src/components/workflow/designer/nodes/index.ts
+++ b/ui/src/components/workflow/designer/nodes/index.ts
@@ -8,9 +8,10 @@ import { EndNodeRegistry } from "./EndNode";
import { StartNodeRegistry } from "./StartNode";
import { CatchBlockNodeRegistry, TryCatchNodeRegistry } from "./TryCatchNode";
-export const getFlowNodeRegistries = () => {
+export const getAllNodeRegistries = () => {
return [
StartNodeRegistry,
+ EndNodeRegistry,
BizApplyNodeRegistry,
BizUploadNodeRegistry,
BizMonitorNodeRegistry,
@@ -20,7 +21,6 @@ export const getFlowNodeRegistries = () => {
BranchBlockNodeRegistry,
TryCatchNodeRegistry,
CatchBlockNodeRegistry,
- EndNodeRegistry,
];
};
diff --git a/ui/src/components/workflow/designer/nodes/typings.ts b/ui/src/components/workflow/designer/nodes/typings.ts
index 1c7d3bf3..9ba37713 100644
--- a/ui/src/components/workflow/designer/nodes/typings.ts
+++ b/ui/src/components/workflow/designer/nodes/typings.ts
@@ -24,6 +24,12 @@ export enum NodeType {
BizNotify = "bizNotify",
}
+export enum NodeKindType {
+ Common = "common",
+ Business = "business",
+ Logic = "logic",
+}
+
export interface NodeJSON extends FlowNodeJSON {
data: {
name?: string;
@@ -47,6 +53,7 @@ export interface NodeMeta extends FlowNodeMeta {
}
export interface NodeRegistry extends FlowNodeRegistry {
+ kindType?: NodeKindType;
formMeta?: Omit, "render"> & {
render: (props: FormRenderProps) => React.ReactElement;
};
diff --git a/ui/src/components/workflow/node/ApplyNodeConfigForm.tsx b/ui/src/components/workflow/node/ApplyNodeConfigForm.tsx
index 874d69c2..6108bfc4 100644
--- a/ui/src/components/workflow/node/ApplyNodeConfigForm.tsx
+++ b/ui/src/components/workflow/node/ApplyNodeConfigForm.tsx
@@ -1,10 +1,9 @@
import { forwardRef, memo, useEffect, useImperativeHandle, useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
import { Link } from "react-router";
-import { QuestionCircleOutlined as IconQuestionCircleOutlined } from "@ant-design/icons";
import { IconChevronRight, IconCircleMinus, IconPlus } from "@tabler/icons-react";
import { useControllableValue } from "ahooks";
-import { AutoComplete, Button, Divider, Flex, Form, type FormInstance, Input, InputNumber, Select, Switch, Tooltip, Typography } from "antd";
+import { AutoComplete, Button, Divider, Flex, Form, type FormInstance, Input, InputNumber, Select, Switch, Typography } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
@@ -269,7 +268,7 @@ const ApplyNodeConfigForm = forwardRef}
+ tooltip={}
>
{t("workflow_node.apply.form.provider_access.label")}
-
-
-
-
-
void }) => {
const { emails, fetchEmails, removeEmail } = useContactEmailsStore();
useEffect(() => {
- fetchEmails();
+ fetchEmails(false);
}, []);
const [value, setValue] = useControllableValue(props, {
diff --git a/ui/src/components/workflow/node/DeployNodeConfigForm.tsx b/ui/src/components/workflow/node/DeployNodeConfigForm.tsx
index d27f1ec1..f43b1e9d 100644
--- a/ui/src/components/workflow/node/DeployNodeConfigForm.tsx
+++ b/ui/src/components/workflow/node/DeployNodeConfigForm.tsx
@@ -1,8 +1,7 @@
import { forwardRef, memo, useEffect, useImperativeHandle, useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
-import { QuestionCircleOutlined as IconQuestionCircleOutlined } from "@ant-design/icons";
import { IconPlus } from "@tabler/icons-react";
-import { Button, Divider, Flex, Form, type FormInstance, Select, Switch, Tooltip, Typography, theme } from "antd";
+import { Button, Divider, Flex, Form, type FormInstance, Select, Switch, Typography, theme } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
@@ -481,11 +480,6 @@ const DeployNodeConfigForm = forwardRef
{t("workflow_node.deploy.form.provider_access.label")}
-
-
-
-
-
}
+ tooltip={}
>