modify test run panel

This commit is contained in:
JzoNg 2024-04-02 16:57:11 +08:00
parent 5adbcacc52
commit 36c3774fac
9 changed files with 127 additions and 71 deletions

View File

@ -44,7 +44,6 @@ const RunMode = memo(() => {
const handleClick = useCallback(async () => {
const {
setShowInputsPanel,
workflowRunningData,
} = workflowStore.getState()
@ -63,7 +62,10 @@ const RunMode = memo(() => {
handleRun({ inputs: {}, files: [] })
}
else {
setShowInputsPanel(true)
workflowStore.setState({
historyWorkflowData: undefined,
showInputsPanel: true,
})
handleSyncWorkflowDraft(true)
}
}, [

View File

@ -129,6 +129,7 @@ const ViewHistory = () => {
onClick={() => {
workflowStore.setState({
historyWorkflowData: item,
showInputsPanel: false,
})
handleBackupDraft()
setOpen(false)

View File

@ -86,6 +86,7 @@ export const useWorkflowRun = () => {
workflowStore.setState({
workflowRunningData: undefined,
historyWorkflowData: undefined,
showInputsPanel: false,
})
}
else {
@ -155,6 +156,17 @@ export const useWorkflowRun = () => {
let prevNodeId = ''
const {
workflowRunningData,
setWorkflowRunningData,
} = workflowStore.getState()
setWorkflowRunningData(produce(workflowRunningData!, (draft) => {
draft.result = {
...draft?.result,
status: WorkflowRunningStatus.Running,
}
}))
ssePost(
url,
{

View File

@ -10,7 +10,6 @@ import { useStore } from '../store'
import { useIsChatMode } from '../hooks'
import DebugAndPreview from './debug-and-preview'
import Record from './record'
import InputsPanel from './inputs-panel'
import WorkflowPreview from './workflow-preview'
import ChatRecord from './chat-record'
import { useStore as useAppStore } from '@/app/components/app/store'
@ -30,11 +29,12 @@ const Panel: FC = () => {
showWorkflowPreview,
} = useMemo(() => {
return {
showNodePanel: !!selectedNode && !workflowRunningData && !historyWorkflowData,
showNodePanel: !!selectedNode && !workflowRunningData && !historyWorkflowData && !showInputsPanel,
showDebugAndPreviewPanel: isChatMode && workflowRunningData && !historyWorkflowData,
showWorkflowPreview: !isChatMode && workflowRunningData && !historyWorkflowData,
showWorkflowPreview: !isChatMode && !historyWorkflowData && (workflowRunningData || showInputsPanel),
}
}, [
showInputsPanel,
selectedNode,
isChatMode,
workflowRunningData,
@ -71,11 +71,6 @@ const Panel: FC = () => {
<DebugAndPreview />
)
}
{
showInputsPanel && (
<InputsPanel />
)
}
{
showWorkflowPreview && (
<WorkflowPreview />

View File

@ -1,6 +1,5 @@
import {
memo,
useCallback,
useMemo,
} from 'react'
import { useTranslation } from 'react-i18next'
@ -9,6 +8,7 @@ import FormItem from '../nodes/_base/components/before-run-form/form-item'
import {
BlockEnum,
InputVarType,
WorkflowRunningStatus,
} from '../types'
import {
useStore,
@ -19,13 +19,18 @@ import type { StartNodeType } from '../nodes/start/types'
import Button from '@/app/components/base/button'
import { useFeatures } from '@/app/components/base/features/hooks'
const InputsPanel = () => {
type Props = {
onRun: () => void
}
const InputsPanel = ({ onRun }: Props) => {
const { t } = useTranslation()
const workflowStore = useWorkflowStore()
const fileSettings = useFeatures(s => s.features.file)
const nodes = useNodes<StartNodeType>()
const inputs = useStore(s => s.inputs)
const files = useStore(s => s.files)
const workflowRunningData = useStore(s => s.workflowRunningData)
const {
handleRun,
handleRunSetting,
@ -64,56 +69,42 @@ const InputsPanel = () => {
}
}
const handleCancel = useCallback(() => {
workflowStore.setState({ showInputsPanel: false })
}, [workflowStore])
const doRun = () => {
handleCancel()
onRun()
handleRunSetting()
handleRun({ inputs, files })
}
return (
<div className='absolute top-0 right-2 w-[420px] h-full z-[11] overflow-y-auto'>
<div className='pb-2 rounded-2xl border-[0.5px] border-gray-200 bg-white shadow-xl'>
<div className='flex items-center pt-3 px-4 h-[44px] text-base font-semibold text-gray-900'>
{t('workflow.singleRun.testRun')}
</div>
<div className='px-4 pb-2'>
{
variables.map(variable => (
<div
key={variable.variable}
className='mb-2 last-of-type:mb-0'
>
<FormItem
className='!block'
payload={variable}
value={inputs[variable.variable]}
onChange={v => handleValueChange(variable.variable, v)}
/>
</div>
))
}
</div>
<div className='flex items-center justify-between px-4 py-2'>
<Button
className='py-0 w-[190px] h-8 rounded-lg border-[0.5px] border-gray-200 shadow-xs text-[13px] font-medium text-gray-700'
onClick={handleCancel}
>
{t('common.operation.cancel')}
</Button>
<Button
type='primary'
className='py-0 w-[190px] h-8 rounded-lg text-[13px] font-medium'
onClick={doRun}
>
{t('workflow.singleRun.startRun')}
</Button>
</div>
<>
<div className='px-4 pb-2'>
{
variables.map(variable => (
<div
key={variable.variable}
className='mb-2 last-of-type:mb-0'
>
<FormItem
className='!block'
payload={variable}
value={inputs[variable.variable]}
onChange={v => handleValueChange(variable.variable, v)}
/>
</div>
))
}
</div>
</div>
<div className='flex items-center justify-between px-4 py-2'>
<Button
type='primary'
disabled={workflowRunningData?.result?.status === WorkflowRunningStatus.Running}
className='py-0 w-full h-8 rounded-lg text-[13px] font-medium'
onClick={doRun}
>
{t('workflow.singleRun.startRun')}
</Button>
</div>
</>
)
}

View File

@ -7,13 +7,20 @@ import { useTranslation } from 'react-i18next'
import OutputPanel from '../run/output-panel'
import ResultPanel from '../run/result-panel'
import TracingPanel from '../run/tracing-panel'
import { useStore } from '../store'
import { useStore, useWorkflowStore } from '../store'
import {
WorkflowRunningStatus,
} from '../types'
import InputsPanel from './inputs-panel'
import Loading from '@/app/components/base/loading'
import { XClose } from '@/app/components/base/icons/src/vender/line/general'
const WorkflowPreview = () => {
const { t } = useTranslation()
const [currentTab, setCurrentTab] = useState<string>('TRACING')
const workflowStore = useWorkflowStore()
const showInputsPanel = useStore(s => s.showInputsPanel)
const workflowRunningData = useStore(s => s.workflowRunningData)
const [currentTab, setCurrentTab] = useState<string>(showInputsPanel ? 'INPUT' : 'TRACING')
const switchTab = async (tab: string) => {
setCurrentTab(tab)
@ -24,44 +31,80 @@ const WorkflowPreview = () => {
flex flex-col w-[420px] h-full rounded-2xl border-[0.5px] border-gray-200 shadow-xl bg-white
`}>
<div className='flex items-center justify-between p-4 pb-1 text-base font-semibold text-gray-900'>
Test Run#{workflowRunningData?.result.sequence_number}
{`Test Run${!workflowRunningData?.result.sequence_number ? '' : `#${workflowRunningData?.result.sequence_number}`}`}
{showInputsPanel && workflowRunningData?.result?.status !== WorkflowRunningStatus.Running && (
<div className='p-1 cursor-pointer' onClick={() => {
workflowStore.setState({
showInputsPanel: false,
workflowRunningData: undefined,
})
}}>
<XClose className='w-4 h-4 text-gray-500' />
</div>
)}
</div>
<div className='grow relative flex flex-col'>
<div className='shrink-0 flex items-center px-4 border-b-[0.5px] border-[rgba(0,0,0,0.05)]'>
{showInputsPanel && (
<div
className={cn(
'mr-6 py-3 border-b-2 border-transparent text-[13px] font-semibold leading-[18px] text-gray-400 cursor-pointer',
currentTab === 'INPUT' && '!border-[rgb(21,94,239)] text-gray-700',
)}
onClick={() => switchTab('INPUT')}
>{t('runLog.input')}</div>
)}
<div
className={cn(
'mr-6 py-3 border-b-2 border-transparent text-[13px] font-semibold leading-[18px] text-gray-400 cursor-pointer',
currentTab === 'RESULT' && '!border-[rgb(21,94,239)] text-gray-700',
!workflowRunningData && 'opacity-30 !cursor-not-allowed',
)}
onClick={() => switchTab('RESULT')}
onClick={() => {
if (!workflowRunningData)
return
switchTab('RESULT')
}}
>{t('runLog.result')}</div>
<div
className={cn(
'mr-6 py-3 border-b-2 border-transparent text-[13px] font-semibold leading-[18px] text-gray-400 cursor-pointer',
currentTab === 'DETAIL' && '!border-[rgb(21,94,239)] text-gray-700',
!workflowRunningData && 'opacity-30 !cursor-not-allowed',
)}
onClick={() => switchTab('DETAIL')}
onClick={() => {
if (!workflowRunningData)
return
switchTab('DETAIL')
}}
>{t('runLog.detail')}</div>
<div
className={cn(
'mr-6 py-3 border-b-2 border-transparent text-[13px] font-semibold leading-[18px] text-gray-400 cursor-pointer',
currentTab === 'TRACING' && '!border-[rgb(21,94,239)] text-gray-700',
!workflowRunningData && 'opacity-30 !cursor-not-allowed',
)}
onClick={() => switchTab('TRACING')}
onClick={() => {
if (!workflowRunningData)
return
switchTab('TRACING')
}}
>{t('runLog.tracing')}</div>
</div>
<div className={cn('grow bg-white h-0 overflow-y-auto rounded-b-2xl', currentTab !== 'DETAIL' && '!bg-gray-50')}>
<div className={cn(
'grow bg-white h-0 overflow-y-auto rounded-b-2xl',
(currentTab === 'RESULT' || currentTab === 'TRACING') && '!bg-gray-50',
)}>
{currentTab === 'INPUT' && (
<InputsPanel onRun={() => switchTab('RESULT')} />
)}
{currentTab === 'RESULT' && (
<OutputPanel
isRunning={workflowRunningData?.result?.status === WorkflowRunningStatus.Running || !workflowRunningData?.result}
outputs={workflowRunningData?.result?.outputs}
error={workflowRunningData?.result?.error}
/>
)}
{currentTab === 'RESULT' && !workflowRunningData?.result && (
<div className='flex h-full items-center justify-center bg-white'>
<Loading />
</div>
)}
{currentTab === 'DETAIL' && (
<ResultPanel
inputs={workflowRunningData?.result?.inputs}

View File

@ -3,24 +3,34 @@ import type { FC } from 'react'
import CodeEditor from '@/app/components/workflow/nodes/_base/components/editor/code-editor'
import { CodeLanguage } from '@/app/components/workflow/nodes/code/types'
import { Markdown } from '@/app/components/base/markdown'
import LoadingAnim from '@/app/components/app/chat/loading-anim'
type OutputPanelProps = {
isRunning?: boolean
outputs?: any
error?: string
}
const OutputPanel: FC<OutputPanelProps> = ({
isRunning,
outputs,
error,
}) => {
return (
<div className='bg-gray-50 py-2'>
{error && (
<div className='px-3 py-[10px] rounded-lg !bg-[#fef3f2] border-[0.5px] border-[rbga(0,0,0,0.05)] shadow-xs'>
<div className='text-xs leading-[18px] text-[#d92d20]'>{error}</div>
{isRunning && (
<div className='pt-4 pl-[26px]'>
<LoadingAnim type='text' />
</div>
)}
{!outputs && (
{!isRunning && error && (
<div className='px-4'>
<div className='px-3 py-[10px] rounded-lg !bg-[#fef3f2] border-[0.5px] border-[rbga(0,0,0,0.05)] shadow-xs'>
<div className='text-xs leading-[18px] text-[#d92d20]'>{error}</div>
</div>
</div>
)}
{!isRunning && !outputs && (
<div className='px-4 py-2'>
<Markdown content='No Output' />
</div>

View File

@ -1,4 +1,5 @@
const translation = {
input: 'INPUT',
result: 'RESULT',
detail: 'DETAIL',
tracing: 'TRACING',

View File

@ -1,4 +1,5 @@
const translation = {
input: '输入',
result: '结果',
detail: '详情',
tracing: '追踪',