From 94ca0edb6876d049846e7f867eb250aa72fd3413 Mon Sep 17 00:00:00 2001 From: StyleZhang Date: Wed, 20 Mar 2024 20:27:33 +0800 Subject: [PATCH] run history --- web/app/components/workflow/header/index.tsx | 13 +++- .../workflow/header/run-and-history.tsx | 61 ++++++++++++++++--- .../workflow/hooks/use-nodes-sync-draft.ts | 3 +- .../workflow/hooks/use-workflow-run.ts | 28 ++++++--- .../components/workflow/hooks/use-workflow.ts | 24 +++++++- web/app/components/workflow/panel/record.tsx | 22 ++++++- web/app/components/workflow/run/index.tsx | 7 ++- 7 files changed, 129 insertions(+), 29 deletions(-) diff --git a/web/app/components/workflow/header/index.tsx b/web/app/components/workflow/header/index.tsx index ebcb5add19..75964db1ab 100644 --- a/web/app/components/workflow/header/index.tsx +++ b/web/app/components/workflow/header/index.tsx @@ -30,6 +30,7 @@ const Header: FC = () => { const appSidebarExpand = useAppStore(s => s.appSidebarExpand) const { nodesReadOnly, + getNodesReadOnly, } = useNodesReadOnly() const isRestoring = useStore(s => s.isRestoring) const { @@ -39,8 +40,15 @@ const Header: FC = () => { const { handleSyncWorkflowDraft } = useNodesSyncDraft() const handleShowFeatures = useCallback(() => { - workflowStore.setState({ showFeaturesPanel: true }) - }, [workflowStore]) + const { + isRestoring, + setShowFeaturesPanel, + } = workflowStore.getState() + if (getNodesReadOnly() && !isRestoring) + return + + setShowFeaturesPanel(true) + }, [workflowStore, getNodesReadOnly]) const handleGoBackToEdit = useCallback(() => { handleRunSetting(true) @@ -102,6 +110,7 @@ const Header: FC = () => { className={` mr-2 px-3 py-0 h-8 bg-white text-[13px] font-medium text-gray-700 border-[0.5px] border-gray-200 shadow-xs + ${nodesReadOnly && !isRestoring && 'opacity-50 !cursor-not-allowed'} `} onClick={handleShowFeatures} > diff --git a/web/app/components/workflow/header/run-and-history.tsx b/web/app/components/workflow/header/run-and-history.tsx index af27a8184a..8cc44cc07e 100644 --- a/web/app/components/workflow/header/run-and-history.tsx +++ b/web/app/components/workflow/header/run-and-history.tsx @@ -1,6 +1,7 @@ import type { FC } from 'react' -import { memo } from 'react' +import { memo, useCallback } from 'react' import { useTranslation } from 'react-i18next' +import { useStoreApi } from 'reactflow' import { useStore, useWorkflowStore, @@ -11,7 +12,10 @@ import { useNodesSyncDraft, useWorkflowRun, } from '../hooks' -import { WorkflowRunningStatus } from '../types' +import { + BlockEnum, + WorkflowRunningStatus, +} from '../types' import { Play, StopCircle, @@ -20,20 +24,59 @@ import { ClockPlay } from '@/app/components/base/icons/src/vender/line/time' import TooltipPlus from '@/app/components/base/tooltip-plus' import { Loading02 } from '@/app/components/base/icons/src/vender/line/general' import { useStore as useAppStore } from '@/app/components/app/store' +import { useFeaturesStore } from '@/app/components/base/features/hooks' const RunMode = memo(() => { const { t } = useTranslation() + const store = useStoreApi() const workflowStore = useWorkflowStore() - const { handleStopRun } = useWorkflowRun() - const { handleSyncWorkflowDraft } = useNodesSyncDraft() + const featuresStore = useFeaturesStore() + const { + handleStopRun, + handleRunSetting, + handleRun, + } = useWorkflowRun() + const { + doSyncWorkflowDraft, + handleSyncWorkflowDraft, + } = useNodesSyncDraft() const workflowRunningData = useStore(s => s.workflowRunningData) const showInputsPanel = useStore(s => s.showInputsPanel) const isRunning = workflowRunningData?.result.status === WorkflowRunningStatus.Running - const handleClick = () => { - workflowStore.setState({ showInputsPanel: true }) - handleSyncWorkflowDraft(true) - } + const handleClick = useCallback(async () => { + const { + setShowInputsPanel, + workflowRunningData, + } = workflowStore.getState() + + if (workflowRunningData?.result.status === WorkflowRunningStatus.Running) + return + + const { getNodes } = store.getState() + const nodes = getNodes() + const startNode = nodes.find(node => node.data.type === BlockEnum.Start) + const startVariables = startNode?.data.variables || [] + const fileSettings = featuresStore!.getState().features.file + + if (!startVariables.length && !fileSettings.image.enabled) { + await doSyncWorkflowDraft() + handleRunSetting() + handleRun({ inputs: {}, files: [] }) + } + else { + setShowInputsPanel(true) + handleSyncWorkflowDraft(true) + } + }, [ + workflowStore, + handleSyncWorkflowDraft, + handleRunSetting, + handleRun, + doSyncWorkflowDraft, + store, + featuresStore, + ]) return ( <> @@ -44,7 +87,7 @@ const RunMode = memo(() => { ${showInputsPanel && 'bg-primary-50'} ${isRunning && 'bg-primary-50 !cursor-not-allowed'} `} - onClick={() => !isRunning && handleClick()} + onClick={handleClick} > { isRunning diff --git a/web/app/components/workflow/hooks/use-nodes-sync-draft.ts b/web/app/components/workflow/hooks/use-nodes-sync-draft.ts index 54ba18cd5d..b98fb83ed1 100644 --- a/web/app/components/workflow/hooks/use-nodes-sync-draft.ts +++ b/web/app/components/workflow/hooks/use-nodes-sync-draft.ts @@ -22,7 +22,7 @@ export const useNodesSyncDraft = () => { const { getNodesReadOnly } = useNodesReadOnly() const debouncedSyncWorkflowDraft = useStore(s => s.debouncedSyncWorkflowDraft) - const doSyncWorkflowDraft = useCallback(() => { + const doSyncWorkflowDraft = useCallback(async () => { const { getNodes, edges, @@ -90,6 +90,7 @@ export const useNodesSyncDraft = () => { }, [debouncedSyncWorkflowDraft, doSyncWorkflowDraft, getNodesReadOnly]) return { + doSyncWorkflowDraft, handleSyncWorkflowDraft, } } diff --git a/web/app/components/workflow/hooks/use-workflow-run.ts b/web/app/components/workflow/hooks/use-workflow-run.ts index 5640c0d540..30100a8d8c 100644 --- a/web/app/components/workflow/hooks/use-workflow-run.ts +++ b/web/app/components/workflow/hooks/use-workflow-run.ts @@ -36,19 +36,22 @@ export const useWorkflowRun = () => { const handleBackupDraft = useCallback(() => { const { getNodes, - getEdges, - getViewport, - } = reactflow + edges, + } = store.getState() + const { getViewport } = reactflow const { + backupDraft, setBackupDraft, } = workflowStore.getState() - setBackupDraft({ - nodes: getNodes(), - edges: getEdges(), - viewport: getViewport(), - }) - }, [reactflow, workflowStore]) + if (!backupDraft) { + setBackupDraft({ + nodes: getNodes(), + edges, + viewport: getViewport(), + }) + } + }, [reactflow, workflowStore, store]) const handleLoadBackupDraft = useCallback(() => { const { @@ -56,7 +59,10 @@ export const useWorkflowRun = () => { setEdges, } = store.getState() const { setViewport } = reactflow - const { backupDraft } = workflowStore.getState() + const { + backupDraft, + setBackupDraft, + } = workflowStore.getState() if (backupDraft) { const { @@ -67,6 +73,8 @@ export const useWorkflowRun = () => { setNodes(nodes) setEdges(edges) setViewport(viewport) + + setBackupDraft(undefined) } }, [store, reactflow, workflowStore]) diff --git a/web/app/components/workflow/hooks/use-workflow.ts b/web/app/components/workflow/hooks/use-workflow.ts index e1caf0de18..83bca57393 100644 --- a/web/app/components/workflow/hooks/use-workflow.ts +++ b/web/app/components/workflow/hooks/use-workflow.ts @@ -13,13 +13,21 @@ import { useReactFlow, useStoreApi, } from 'reactflow' -import type { Connection } from 'reactflow' +import type { + Connection, + Viewport, +} from 'reactflow' import type { ToolsMap } from '../block-selector/types' import { generateNewNode, getLayoutByDagre, + initialEdges, + initialNodes, } from '../utils' -import type { Node } from '../types' +import type { + Edge, + Node, +} from '../types' import { BlockEnum, WorkflowRunningStatus, @@ -270,6 +278,17 @@ export const useWorkflow = () => { } }, [store]) + const renderTreeFromRecord = useCallback((nodes: Node[], edges: Edge[], viewport?: Viewport) => { + const { setNodes } = store.getState() + const { setViewport, setEdges } = reactflow + + setNodes(initialNodes(nodes, edges)) + setEdges(initialEdges(edges, nodes)) + + if (viewport) + setViewport(viewport) + }, [store, reactflow]) + return { handleLayout, getTreeLeafNodes, @@ -278,6 +297,7 @@ export const useWorkflow = () => { isValidConnection, formatTimeFromNow, getValidTreeNodes, + renderTreeFromRecord, } } diff --git a/web/app/components/workflow/panel/record.tsx b/web/app/components/workflow/panel/record.tsx index 83c7fea14b..efe691df5f 100644 --- a/web/app/components/workflow/panel/record.tsx +++ b/web/app/components/workflow/panel/record.tsx @@ -1,13 +1,24 @@ -import { memo } from 'react' -import { useIsChatMode } from '../hooks' +import { memo, useCallback } from 'react' +import { + useIsChatMode, + useWorkflow, +} from '../hooks' import Run from '../run' import { useStore } from '../store' import ChatRecord from './chat-record' +import type { WorkflowRunDetailResponse } from '@/models/log' const Record = () => { const isChatMode = useIsChatMode() + const { renderTreeFromRecord } = useWorkflow() const historyWorkflowData = useStore(s => s.historyWorkflowData) + const getResultCallback = useCallback((res: WorkflowRunDetailResponse) => { + const { graph } = res + + renderTreeFromRecord(graph.nodes, graph.edges, graph.viewport) + }, [renderTreeFromRecord]) + return (
{ { isChatMode ? - : + : ( + + ) }
) diff --git a/web/app/components/workflow/run/index.tsx b/web/app/components/workflow/run/index.tsx index 180d5f880a..5f76cdd94b 100644 --- a/web/app/components/workflow/run/index.tsx +++ b/web/app/components/workflow/run/index.tsx @@ -16,9 +16,10 @@ import { useStore as useAppStore } from '@/app/components/app/store' export type RunProps = { activeTab?: 'RESULT' | 'TRACING' runID: string + getResultCallback?: (result: WorkflowRunDetailResponse) => void } -const RunPanel: FC = ({ activeTab = 'RESULT', runID }) => { +const RunPanel: FC = ({ activeTab = 'RESULT', runID, getResultCallback }) => { const { t } = useTranslation() const { notify } = useContext(ToastContext) const [currentTab, setCurrentTab] = useState(activeTab) @@ -42,6 +43,8 @@ const RunPanel: FC = ({ activeTab = 'RESULT', runID }) => { runID, }) setRunDetail(res) + if (getResultCallback) + getResultCallback(res) } catch (err) { notify({ @@ -49,7 +52,7 @@ const RunPanel: FC = ({ activeTab = 'RESULT', runID }) => { message: `${err}`, }) } - }, [notify]) + }, [notify, getResultCallback]) const getTracingList = useCallback(async (appID: string, runID: string) => { try {