import { memo, useEffect, useMemo, useRef, useState } from "react"; import { useTranslation } from "react-i18next"; import { Avatar, Flex, Typography } from "antd"; import { produce } from "immer"; import { deploymentProvidersMap } from "@/domain/provider"; import { type WorkflowNodeConfigForDeploy, WorkflowNodeType } from "@/domain/workflow"; import { useZustandShallowSelector } from "@/hooks"; import { useWorkflowStore } from "@/stores/workflow"; import SharedNode, { type SharedNodeProps } from "./_SharedNode"; import DeployNodeConfigForm, { type DeployNodeConfigFormInstance } from "./DeployNodeConfigForm"; export type DeployNodeProps = SharedNodeProps; const DeployNode = ({ node, disabled }: DeployNodeProps) => { if (node.type !== WorkflowNodeType.Deploy) { console.warn(`[certimate] current workflow node type is not: ${WorkflowNodeType.Deploy}`); } const { t } = useTranslation(); const { updateNode } = useWorkflowStore(useZustandShallowSelector(["updateNode"])); const formRef = useRef<DeployNodeConfigFormInstance>(null); const [formPending, setFormPending] = useState(false); const [drawerOpen, setDrawerOpen] = useState(false); const [drawerFooterShow, setDrawerFooterShow] = useState(true); const getFormValues = () => formRef.current!.getFieldsValue() as WorkflowNodeConfigForDeploy; useEffect(() => { setDrawerFooterShow(!!(node.config as WorkflowNodeConfigForDeploy)?.provider); }, [node.config?.provider]); const wrappedEl = useMemo(() => { if (node.type !== WorkflowNodeType.Deploy) { console.warn(`[certimate] current workflow node type is not: ${WorkflowNodeType.Deploy}`); } if (!node.validated) { return <Typography.Link>{t("workflow_node.action.configure_node")}</Typography.Link>; } const config = (node.config as WorkflowNodeConfigForDeploy) ?? {}; const provider = deploymentProvidersMap.get(config.provider); return ( <Flex className="size-full overflow-hidden" align="center" gap={8}> <Avatar src={provider?.icon} size="small" /> <Typography.Text className="flex-1 truncate">{t(provider?.name ?? "")}</Typography.Text> </Flex> ); }, [node]); const handleDrawerConfirm = async () => { setFormPending(true); try { await formRef.current!.validateFields(); } catch (err) { setFormPending(false); throw err; } try { const newValues = getFormValues(); const newNode = produce(node, (draft) => { draft.config = { ...newValues, }; draft.validated = true; }); await updateNode(newNode); } finally { setFormPending(false); } }; const handleFormValuesChange = (values: Partial<WorkflowNodeConfigForDeploy>) => { setDrawerFooterShow(!!values.provider); }; return ( <> <SharedNode.Block node={node} disabled={disabled} onClick={() => setDrawerOpen(true)}> {wrappedEl} </SharedNode.Block> <SharedNode.ConfigDrawer node={node} footer={drawerFooterShow} open={drawerOpen} pending={formPending} onConfirm={handleDrawerConfirm} onOpenChange={(open) => { setDrawerFooterShow(!!(node.config as WorkflowNodeConfigForDeploy)?.provider); setDrawerOpen(open); }} getFormValues={() => formRef.current!.getFieldsValue()} > <DeployNodeConfigForm ref={formRef} disabled={disabled} initialValues={node.config} nodeId={node.id} onValuesChange={handleFormValuesChange} /> </SharedNode.ConfigDrawer> </> ); }; export default memo(DeployNode);