mirror of https://github.com/langgenius/dify.git
feat: enhance workflow error handling and internationalization (#24648)
This commit is contained in:
parent
5cbe6bf8f8
commit
c90dad566f
|
|
@ -9,6 +9,7 @@ import { BlockEnum } from '@/app/components/workflow/types'
|
|||
import type { ToolDefaultValue } from '@/app/components/workflow/block-selector/types'
|
||||
import Modal from '@/app/components/base/modal'
|
||||
import StartNodeSelectionPanel from './start-node-selection-panel'
|
||||
import { useDocLink } from '@/context/i18n'
|
||||
|
||||
type WorkflowOnboardingModalProps = {
|
||||
isShow: boolean
|
||||
|
|
@ -22,6 +23,7 @@ const WorkflowOnboardingModal: FC<WorkflowOnboardingModalProps> = ({
|
|||
onSelectStartNode,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const docLink = useDocLink()
|
||||
|
||||
const handleSelectUserInput = useCallback(() => {
|
||||
onSelectStartNode(BlockEnum.Start)
|
||||
|
|
@ -60,15 +62,15 @@ const WorkflowOnboardingModal: FC<WorkflowOnboardingModalProps> = ({
|
|||
</h3>
|
||||
<div className="body-xs-regular leading-4 text-text-tertiary">
|
||||
{t('workflow.onboarding.description')}{' '}
|
||||
<button
|
||||
type="button"
|
||||
<a
|
||||
href={docLink('guides/workflow/node/start')}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="hover:text-text-accent-hover cursor-pointer text-text-accent underline"
|
||||
onClick={() => {
|
||||
console.log('Navigate to start node documentation')
|
||||
}}
|
||||
>
|
||||
Learn more
|
||||
</button> about start node.
|
||||
{t('workflow.onboarding.learnMore')}
|
||||
</a>{' '}
|
||||
{t('workflow.onboarding.aboutStartNode')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import type { MouseEvent } from 'react'
|
||||
import { useCallback, useRef, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useToastContext } from '@/app/components/base/toast'
|
||||
import produce from 'immer'
|
||||
import type {
|
||||
NodeDragHandler,
|
||||
|
|
@ -67,6 +68,7 @@ import { getNodeUsedVars } from '../nodes/_base/components/variable/utils'
|
|||
|
||||
export const useNodesInteractions = () => {
|
||||
const { t } = useTranslation()
|
||||
const { notify } = useToastContext()
|
||||
const store = useStoreApi()
|
||||
const workflowStore = useWorkflowStore()
|
||||
const reactflow = useReactFlow()
|
||||
|
|
@ -91,6 +93,32 @@ export const useNodesInteractions = () => {
|
|||
|
||||
const { saveStateToHistory, undo, redo } = useWorkflowHistory()
|
||||
|
||||
// Unified error handler for start node missing scenarios
|
||||
const handleStartNodeMissingError = useCallback((error: Error, operationKey: string): boolean => {
|
||||
if (error.message === 'Start node not found') {
|
||||
const operation = t(`workflow.error.operations.${operationKey}`) || operationKey
|
||||
notify({
|
||||
type: 'error',
|
||||
message: t('workflow.error.startNodeRequired', { operation }) || `Please add a start node first before ${operation}`,
|
||||
})
|
||||
return true // Error handled
|
||||
}
|
||||
return false // Error not handled, should re-throw
|
||||
}, [notify, t])
|
||||
|
||||
// Safe wrapper for checkNestedParallelLimit with error handling
|
||||
const safeCheckParallelLimit = useCallback((nodes: Node[], edges: Edge[], parentNodeId?: string, operationKey = 'updatingWorkflow') => {
|
||||
try {
|
||||
return checkNestedParallelLimit(nodes, edges, parentNodeId)
|
||||
}
|
||||
catch (error: any) {
|
||||
if (handleStartNodeMissingError(error, operationKey))
|
||||
return false // Operation blocked but gracefully handled
|
||||
|
||||
throw error // Re-throw other errors
|
||||
}
|
||||
}, [checkNestedParallelLimit, handleStartNodeMissingError])
|
||||
|
||||
const handleNodeDragStart = useCallback<NodeDragHandler>((_, node) => {
|
||||
workflowStore.setState({ nodeAnimation: false })
|
||||
|
||||
|
|
@ -419,7 +447,7 @@ export const useNodesInteractions = () => {
|
|||
draft.push(newEdge)
|
||||
})
|
||||
|
||||
if (checkNestedParallelLimit(newNodes, newEdges, targetNode?.parentId)) {
|
||||
if (safeCheckParallelLimit(newNodes, newEdges, targetNode?.parentId, 'connectingNodes')) {
|
||||
setNodes(newNodes)
|
||||
setEdges(newEdges)
|
||||
|
||||
|
|
@ -434,7 +462,7 @@ export const useNodesInteractions = () => {
|
|||
setConnectingNodePayload(undefined)
|
||||
setEnteringNodePayload(undefined)
|
||||
}
|
||||
}, [getNodesReadOnly, store, workflowStore, handleSyncWorkflowDraft, saveStateToHistory, checkNestedParallelLimit])
|
||||
}, [getNodesReadOnly, store, workflowStore, handleSyncWorkflowDraft, saveStateToHistory, safeCheckParallelLimit])
|
||||
|
||||
const handleNodeConnectStart = useCallback<OnConnectStart>((_, { nodeId, handleType, handleId }) => {
|
||||
if (getNodesReadOnly())
|
||||
|
|
@ -824,7 +852,7 @@ export const useNodesInteractions = () => {
|
|||
draft.push(newEdge)
|
||||
})
|
||||
|
||||
if (checkNestedParallelLimit(newNodes, newEdges, prevNode.parentId)) {
|
||||
if (safeCheckParallelLimit(newNodes, newEdges, prevNode.parentId, 'addingNodes')) {
|
||||
setNodes(newNodes)
|
||||
setEdges(newEdges)
|
||||
}
|
||||
|
|
@ -944,7 +972,7 @@ export const useNodesInteractions = () => {
|
|||
draft.push(newEdge)
|
||||
})
|
||||
|
||||
if (checkNestedParallelLimit(newNodes, newEdges, nextNode.parentId)) {
|
||||
if (safeCheckParallelLimit(newNodes, newEdges, nextNode.parentId, 'modifyingWorkflow')) {
|
||||
setNodes(newNodes)
|
||||
setEdges(newEdges)
|
||||
}
|
||||
|
|
@ -953,7 +981,7 @@ export const useNodesInteractions = () => {
|
|||
}
|
||||
}
|
||||
else {
|
||||
if (checkNestedParallelLimit(newNodes, edges))
|
||||
if (safeCheckParallelLimit(newNodes, edges, undefined, 'updatingWorkflow'))
|
||||
setNodes(newNodes)
|
||||
|
||||
else
|
||||
|
|
@ -1102,7 +1130,7 @@ export const useNodesInteractions = () => {
|
|||
}
|
||||
handleSyncWorkflowDraft()
|
||||
saveStateToHistory(WorkflowHistoryEvent.NodeAdd)
|
||||
}, [getNodesReadOnly, store, t, handleSyncWorkflowDraft, saveStateToHistory, workflowStore, getAfterNodesInSameBranch, checkNestedParallelLimit])
|
||||
}, [getNodesReadOnly, store, t, handleSyncWorkflowDraft, saveStateToHistory, workflowStore, getAfterNodesInSameBranch, safeCheckParallelLimit])
|
||||
|
||||
const handleNodeChange = useCallback((
|
||||
currentNodeId: string,
|
||||
|
|
|
|||
|
|
@ -210,6 +210,16 @@ const translation = {
|
|||
invalidVariable: 'Invalid variable',
|
||||
noValidTool: '{{field}} no valid tool selected',
|
||||
toolParameterRequired: '{{field}}: parameter [{{param}}] is required',
|
||||
startNodeRequired: 'Please add a start node first before {{operation}}',
|
||||
},
|
||||
error: {
|
||||
startNodeRequired: 'Please add a start node first before {{operation}}',
|
||||
operations: {
|
||||
connectingNodes: 'connecting nodes',
|
||||
addingNodes: 'adding nodes',
|
||||
modifyingWorkflow: 'modifying workflow',
|
||||
updatingWorkflow: 'updating workflow',
|
||||
},
|
||||
},
|
||||
singleRun: {
|
||||
testRun: 'Test Run ',
|
||||
|
|
|
|||
|
|
@ -210,6 +210,16 @@ const translation = {
|
|||
invalidVariable: '無効な変数です',
|
||||
noValidTool: '{{field}} に利用可能なツールがありません',
|
||||
toolParameterRequired: '{{field}}: パラメータ [{{param}}] は必須です',
|
||||
startNodeRequired: '{{operation}}前に開始ノードを追加してください',
|
||||
},
|
||||
error: {
|
||||
startNodeRequired: '{{operation}}前に開始ノードを追加してください',
|
||||
operations: {
|
||||
connectingNodes: 'ノード接続',
|
||||
addingNodes: 'ノード追加',
|
||||
modifyingWorkflow: 'ワークフロー変更',
|
||||
updatingWorkflow: 'ワークフロー更新',
|
||||
},
|
||||
},
|
||||
singleRun: {
|
||||
testRun: 'テスト実行',
|
||||
|
|
|
|||
|
|
@ -210,6 +210,16 @@ const translation = {
|
|||
invalidVariable: '无效的变量',
|
||||
noValidTool: '{{field}} 无可用工具',
|
||||
toolParameterRequired: '{{field}}: 参数 [{{param}}] 不能为空',
|
||||
startNodeRequired: '请先添加开始节点,然后再{{operation}}',
|
||||
},
|
||||
error: {
|
||||
startNodeRequired: '请先添加开始节点,然后再{{operation}}',
|
||||
operations: {
|
||||
connectingNodes: '连接节点',
|
||||
addingNodes: '添加节点',
|
||||
modifyingWorkflow: '修改工作流',
|
||||
updatingWorkflow: '更新工作流',
|
||||
},
|
||||
},
|
||||
singleRun: {
|
||||
testRun: '测试运行 ',
|
||||
|
|
|
|||
Loading…
Reference in New Issue