mirror of
http://112.124.100.131/huang.ze/ebiz-dify-ai.git
synced 2025-12-15 13:56:53 +08:00
feat: workflow remove preview mode (#3941)
This commit is contained in:
@@ -7,6 +7,7 @@ import {
|
||||
useEdges,
|
||||
useNodes,
|
||||
} from 'reactflow'
|
||||
import cn from 'classnames'
|
||||
import BlockIcon from '../block-icon'
|
||||
import {
|
||||
useChecklist,
|
||||
@@ -28,7 +29,12 @@ import {
|
||||
} from '@/app/components/base/icons/src/vender/line/general'
|
||||
import { AlertTriangle } from '@/app/components/base/icons/src/vender/line/alertsAndFeedback'
|
||||
|
||||
const WorkflowChecklist = () => {
|
||||
type WorkflowChecklistProps = {
|
||||
disabled: boolean
|
||||
}
|
||||
const WorkflowChecklist = ({
|
||||
disabled,
|
||||
}: WorkflowChecklistProps) => {
|
||||
const { t } = useTranslation()
|
||||
const [open, setOpen] = useState(false)
|
||||
const nodes = useNodes<CommonNodeType>()
|
||||
@@ -46,8 +52,13 @@ const WorkflowChecklist = () => {
|
||||
open={open}
|
||||
onOpenChange={setOpen}
|
||||
>
|
||||
<PortalToFollowElemTrigger onClick={() => setOpen(v => !v)}>
|
||||
<div className='relative flex items-center justify-center p-0.5 w-8 h-8 rounded-lg border-[0.5px] border-gray-200 bg-white shadow-xs'>
|
||||
<PortalToFollowElemTrigger onClick={() => !disabled && setOpen(v => !v)}>
|
||||
<div
|
||||
className={cn(
|
||||
'relative flex items-center justify-center p-0.5 w-8 h-8 rounded-lg border-[0.5px] border-gray-200 bg-white shadow-xs',
|
||||
disabled && 'opacity-50 cursor-not-allowed',
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className={`
|
||||
group flex items-center justify-center w-full h-full rounded-md cursor-pointer
|
||||
|
||||
@@ -2,7 +2,6 @@ import { memo } from 'react'
|
||||
import dayjs from 'dayjs'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useWorkflow } from '../hooks'
|
||||
import { Edit03 } from '@/app/components/base/icons/src/vender/solid/general'
|
||||
import { useStore } from '@/app/components/workflow/store'
|
||||
|
||||
const EditingTitle = () => {
|
||||
@@ -13,12 +12,9 @@ const EditingTitle = () => {
|
||||
|
||||
return (
|
||||
<div className='flex items-center h-[18px] text-xs text-gray-500'>
|
||||
<Edit03 className='mr-1 w-3 h-3 text-gray-400' />
|
||||
{t('workflow.common.editing')}
|
||||
{
|
||||
!!draftUpdatedAt && (
|
||||
<>
|
||||
<span className='flex items-center mx-1'>·</span>
|
||||
{t('workflow.common.autoSaved')} {dayjs(draftUpdatedAt).format('HH:mm:ss')}
|
||||
</>
|
||||
)
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
useChecklistBeforePublish,
|
||||
useNodesReadOnly,
|
||||
useNodesSyncDraft,
|
||||
useWorkflowMode,
|
||||
useWorkflowRun,
|
||||
} from '../hooks'
|
||||
import AppPublisher from '../../app/app-publisher'
|
||||
@@ -21,12 +22,13 @@ import RunAndHistory from './run-and-history'
|
||||
import EditingTitle from './editing-title'
|
||||
import RunningTitle from './running-title'
|
||||
import RestoringTitle from './restoring-title'
|
||||
import ViewHistory from './view-history'
|
||||
import Checklist from './checklist'
|
||||
import { Grid01 } from '@/app/components/base/icons/src/vender/line/layout'
|
||||
import Button from '@/app/components/base/button'
|
||||
import { ArrowNarrowLeft } from '@/app/components/base/icons/src/vender/line/arrows'
|
||||
import { useStore as useAppStore } from '@/app/components/app/store'
|
||||
import { publishWorkflow } from '@/service/workflow'
|
||||
import { ArrowNarrowLeft } from '@/app/components/base/icons/src/vender/line/arrows'
|
||||
|
||||
const Header: FC = () => {
|
||||
const { t } = useTranslation()
|
||||
@@ -38,18 +40,21 @@ const Header: FC = () => {
|
||||
nodesReadOnly,
|
||||
getNodesReadOnly,
|
||||
} = useNodesReadOnly()
|
||||
const isRestoring = useStore(s => s.isRestoring)
|
||||
const publishedAt = useStore(s => s.publishedAt)
|
||||
const draftUpdatedAt = useStore(s => s.draftUpdatedAt)
|
||||
const {
|
||||
handleLoadBackupDraft,
|
||||
handleRunSetting,
|
||||
handleBackupDraft,
|
||||
handleRestoreFromPublishedWorkflow,
|
||||
} = useWorkflowRun()
|
||||
const { handleCheckBeforePublish } = useChecklistBeforePublish()
|
||||
const { handleSyncWorkflowDraft } = useNodesSyncDraft()
|
||||
const { notify } = useContext(ToastContext)
|
||||
const {
|
||||
normal,
|
||||
restoring,
|
||||
viewHistory,
|
||||
} = useWorkflowMode()
|
||||
|
||||
const handleShowFeatures = useCallback(() => {
|
||||
const {
|
||||
@@ -62,10 +67,6 @@ const Header: FC = () => {
|
||||
setShowFeaturesPanel(true)
|
||||
}, [workflowStore, getNodesReadOnly])
|
||||
|
||||
const handleGoBackToEdit = useCallback(() => {
|
||||
handleRunSetting(true)
|
||||
}, [handleRunSetting])
|
||||
|
||||
const handleCancelRestore = useCallback(() => {
|
||||
handleLoadBackupDraft()
|
||||
workflowStore.setState({ isRestoring: false })
|
||||
@@ -102,6 +103,11 @@ const Header: FC = () => {
|
||||
handleSyncWorkflowDraft(true)
|
||||
}, [handleSyncWorkflowDraft])
|
||||
|
||||
const handleGoBackToEdit = useCallback(() => {
|
||||
handleLoadBackupDraft()
|
||||
workflowStore.setState({ historyWorkflowData: undefined })
|
||||
}, [workflowStore, handleLoadBackupDraft])
|
||||
|
||||
return (
|
||||
<div
|
||||
className='absolute top-0 left-0 z-10 flex items-center justify-between w-full px-3 h-14'
|
||||
@@ -116,39 +122,25 @@ const Header: FC = () => {
|
||||
)
|
||||
}
|
||||
{
|
||||
!nodesReadOnly && !isRestoring && <EditingTitle />
|
||||
normal && <EditingTitle />
|
||||
}
|
||||
{
|
||||
nodesReadOnly && !isRestoring && <RunningTitle />
|
||||
viewHistory && <RunningTitle />
|
||||
}
|
||||
{
|
||||
isRestoring && <RestoringTitle />
|
||||
restoring && <RestoringTitle />
|
||||
}
|
||||
</div>
|
||||
{
|
||||
!isRestoring && (
|
||||
normal && (
|
||||
<div className='flex items-center'>
|
||||
{
|
||||
nodesReadOnly && (
|
||||
<Button
|
||||
className={`
|
||||
mr-2 px-3 py-0 h-8 bg-white text-[13px] font-medium text-primary-600
|
||||
border-[0.5px] border-gray-200 shadow-xs
|
||||
`}
|
||||
onClick={handleGoBackToEdit}
|
||||
>
|
||||
<ArrowNarrowLeft className='w-4 h-4 mr-1' />
|
||||
{t('workflow.common.goBackToEdit')}
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
<RunAndHistory />
|
||||
<div className='mx-2 w-[1px] h-3.5 bg-gray-200'></div>
|
||||
<Button
|
||||
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'}
|
||||
${nodesReadOnly && 'opacity-50 !cursor-not-allowed'}
|
||||
`}
|
||||
onClick={handleShowFeatures}
|
||||
>
|
||||
@@ -166,19 +158,32 @@ const Header: FC = () => {
|
||||
crossAxisOffset: 53,
|
||||
}}
|
||||
/>
|
||||
{
|
||||
!nodesReadOnly && (
|
||||
<>
|
||||
<div className='mx-2 w-[1px] h-3.5 bg-gray-200'></div>
|
||||
<Checklist />
|
||||
</>
|
||||
)
|
||||
}
|
||||
<div className='mx-2 w-[1px] h-3.5 bg-gray-200'></div>
|
||||
<Checklist disabled={nodesReadOnly} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
{
|
||||
isRestoring && (
|
||||
viewHistory && (
|
||||
<div className='flex items-center'>
|
||||
<ViewHistory withText />
|
||||
<div className='mx-2 w-[1px] h-3.5 bg-gray-200'></div>
|
||||
<Button
|
||||
type='primary'
|
||||
className={`
|
||||
mr-2 px-3 py-0 h-8 text-[13px] font-medium
|
||||
border-[0.5px] border-gray-200 shadow-xs
|
||||
`}
|
||||
onClick={handleGoBackToEdit}
|
||||
>
|
||||
<ArrowNarrowLeft className='w-4 h-4 mr-1' />
|
||||
{t('workflow.common.goBackToEdit')}
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
{
|
||||
restoring && (
|
||||
<div className='flex items-center'>
|
||||
<Button
|
||||
className={`
|
||||
|
||||
@@ -2,14 +2,15 @@ import type { FC } from 'react'
|
||||
import { memo, useCallback } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useStoreApi } from 'reactflow'
|
||||
import cn from 'classnames'
|
||||
import {
|
||||
useStore,
|
||||
useWorkflowStore,
|
||||
} from '../store'
|
||||
import {
|
||||
useIsChatMode,
|
||||
useNodesReadOnly,
|
||||
useNodesSyncDraft,
|
||||
useWorkflowInteractions,
|
||||
useWorkflowRun,
|
||||
} from '../hooks'
|
||||
import {
|
||||
@@ -23,6 +24,7 @@ import {
|
||||
} from '@/app/components/base/icons/src/vender/line/mediaAndDevices'
|
||||
import { Loading02 } from '@/app/components/base/icons/src/vender/line/general'
|
||||
import { useFeaturesStore } from '@/app/components/base/features/hooks'
|
||||
import { MessagePlay } from '@/app/components/base/icons/src/vender/line/communication'
|
||||
|
||||
const RunMode = memo(() => {
|
||||
const { t } = useTranslation()
|
||||
@@ -31,15 +33,12 @@ const RunMode = memo(() => {
|
||||
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 = useCallback(async () => {
|
||||
@@ -55,23 +54,23 @@ const RunMode = memo(() => {
|
||||
const startNode = nodes.find(node => node.data.type === BlockEnum.Start)
|
||||
const startVariables = startNode?.data.variables || []
|
||||
const fileSettings = featuresStore!.getState().features.file
|
||||
const {
|
||||
setShowDebugAndPreviewPanel,
|
||||
setShowInputsPanel,
|
||||
} = workflowStore.getState()
|
||||
|
||||
if (!startVariables.length && !fileSettings?.image?.enabled) {
|
||||
await doSyncWorkflowDraft()
|
||||
handleRunSetting()
|
||||
handleRun({ inputs: {}, files: [] })
|
||||
setShowDebugAndPreviewPanel(true)
|
||||
setShowInputsPanel(false)
|
||||
}
|
||||
else {
|
||||
workflowStore.setState({
|
||||
historyWorkflowData: undefined,
|
||||
showInputsPanel: true,
|
||||
})
|
||||
handleSyncWorkflowDraft(true)
|
||||
setShowDebugAndPreviewPanel(true)
|
||||
setShowInputsPanel(true)
|
||||
}
|
||||
}, [
|
||||
workflowStore,
|
||||
handleSyncWorkflowDraft,
|
||||
handleRunSetting,
|
||||
handleRun,
|
||||
doSyncWorkflowDraft,
|
||||
store,
|
||||
@@ -81,12 +80,11 @@ const RunMode = memo(() => {
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className={`
|
||||
flex items-center px-1.5 h-7 rounded-md text-[13px] font-medium text-primary-600
|
||||
hover:bg-primary-50 cursor-pointer
|
||||
${showInputsPanel && 'bg-primary-50'}
|
||||
${isRunning && 'bg-primary-50 !cursor-not-allowed'}
|
||||
`}
|
||||
className={cn(
|
||||
'flex items-center px-1.5 h-7 rounded-md text-[13px] font-medium text-primary-600',
|
||||
'hover:bg-primary-50 cursor-pointer',
|
||||
isRunning && 'bg-primary-50 !cursor-not-allowed',
|
||||
)}
|
||||
onClick={handleClick}
|
||||
>
|
||||
{
|
||||
@@ -122,38 +120,34 @@ RunMode.displayName = 'RunMode'
|
||||
|
||||
const PreviewMode = memo(() => {
|
||||
const { t } = useTranslation()
|
||||
const { handleRunSetting } = useWorkflowRun()
|
||||
const { handleSyncWorkflowDraft } = useNodesSyncDraft()
|
||||
const { nodesReadOnly } = useNodesReadOnly()
|
||||
const workflowStore = useWorkflowStore()
|
||||
const { handleCancelDebugAndPreviewPanel } = useWorkflowInteractions()
|
||||
|
||||
const handleClick = () => {
|
||||
handleSyncWorkflowDraft(true)
|
||||
handleRunSetting()
|
||||
const {
|
||||
showDebugAndPreviewPanel,
|
||||
setShowDebugAndPreviewPanel,
|
||||
setHistoryWorkflowData,
|
||||
} = workflowStore.getState()
|
||||
|
||||
if (showDebugAndPreviewPanel)
|
||||
handleCancelDebugAndPreviewPanel()
|
||||
else
|
||||
setShowDebugAndPreviewPanel(true)
|
||||
|
||||
setHistoryWorkflowData(undefined)
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`
|
||||
flex items-center px-1.5 h-7 rounded-md text-[13px] font-medium text-primary-600
|
||||
hover:bg-primary-50 cursor-pointer
|
||||
${nodesReadOnly && 'bg-primary-50 opacity-50 !cursor-not-allowed'}
|
||||
`}
|
||||
onClick={() => !nodesReadOnly && handleClick()}
|
||||
className={cn(
|
||||
'flex items-center px-1.5 h-7 rounded-md text-[13px] font-medium text-primary-600',
|
||||
'hover:bg-primary-50 cursor-pointer',
|
||||
)}
|
||||
onClick={() => handleClick()}
|
||||
>
|
||||
{
|
||||
nodesReadOnly
|
||||
? (
|
||||
<>
|
||||
{t('workflow.common.inPreview')}
|
||||
</>
|
||||
)
|
||||
: (
|
||||
<>
|
||||
<Play className='mr-1 w-4 h-4' />
|
||||
{t('workflow.common.preview')}
|
||||
</>
|
||||
)
|
||||
}
|
||||
<MessagePlay className='mr-1 w-4 h-4' />
|
||||
{t('workflow.common.debugAndPreview')}
|
||||
</div>
|
||||
)
|
||||
})
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
import { memo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useStore as useAppStore } from '@/app/components/app/store'
|
||||
import { Play } from '@/app/components/base/icons/src/vender/solid/mediaAndDevices'
|
||||
import { useIsChatMode } from '../hooks'
|
||||
import { useStore } from '../store'
|
||||
import { ClockPlay } from '@/app/components/base/icons/src/vender/line/time'
|
||||
|
||||
const RunningTitle = () => {
|
||||
const { t } = useTranslation()
|
||||
const appDetail = useAppStore(state => state.appDetail)
|
||||
const isChatMode = useIsChatMode()
|
||||
const historyWorkflowData = useStore(s => s.historyWorkflowData)
|
||||
|
||||
return (
|
||||
<div className='flex items-center h-[18px] text-xs text-primary-600'>
|
||||
<Play className='mr-1 w-3 h-3' />
|
||||
{
|
||||
appDetail?.mode === 'advanced-chat'
|
||||
? t('workflow.common.inPreviewMode')
|
||||
: t('workflow.common.inRunMode')
|
||||
}
|
||||
<div className='flex items-center h-[18px] text-xs text-gray-500'>
|
||||
<ClockPlay className='mr-1 w-3 h-3 text-gray-500' />
|
||||
<span>{isChatMode ? `Test Chat#${historyWorkflowData?.sequence_number}` : `Test Run#${historyWorkflowData?.sequence_number}`}</span>
|
||||
<span className='mx-1'>·</span>
|
||||
<span className='text-gray-500'>Test Run#2</span>
|
||||
<span className='ml-1 uppercase flex items-center px-1 h-[18px] rounded-[5px] border border-indigo-300 bg-white/[0.48] text-[10px] font-semibold text-indigo-600'>
|
||||
{t('workflow.common.viewOnly')}
|
||||
</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -8,7 +8,9 @@ import { useTranslation } from 'react-i18next'
|
||||
import { useShallow } from 'zustand/react/shallow'
|
||||
import {
|
||||
useIsChatMode,
|
||||
useNodesInteractions,
|
||||
useWorkflow,
|
||||
useWorkflowInteractions,
|
||||
useWorkflowRun,
|
||||
} from '../hooks'
|
||||
import { WorkflowRunningStatus } from '../types'
|
||||
@@ -35,11 +37,22 @@ import {
|
||||
useWorkflowStore,
|
||||
} from '@/app/components/workflow/store'
|
||||
|
||||
const ViewHistory = () => {
|
||||
type ViewHistoryProps = {
|
||||
withText?: boolean
|
||||
}
|
||||
const ViewHistory = ({
|
||||
withText,
|
||||
}: ViewHistoryProps) => {
|
||||
const { t } = useTranslation()
|
||||
const isChatMode = useIsChatMode()
|
||||
const [open, setOpen] = useState(false)
|
||||
const { formatTimeFromNow } = useWorkflow()
|
||||
const {
|
||||
handleNodesCancelSelected,
|
||||
} = useNodesInteractions()
|
||||
const {
|
||||
handleCancelDebugAndPreviewPanel,
|
||||
} = useWorkflowInteractions()
|
||||
const workflowStore = useWorkflowStore()
|
||||
const { appDetail, setCurrentLogItem, setShowMessageLogModal } = useAppStore(useShallow(state => ({
|
||||
appDetail: state.appDetail,
|
||||
@@ -57,31 +70,49 @@ const ViewHistory = () => {
|
||||
return (
|
||||
(
|
||||
<PortalToFollowElem
|
||||
placement='bottom-end'
|
||||
placement={withText ? 'bottom-start' : 'bottom-end'}
|
||||
offset={{
|
||||
mainAxis: 4,
|
||||
crossAxis: 131,
|
||||
crossAxis: withText ? -8 : 10,
|
||||
}}
|
||||
open={open}
|
||||
onOpenChange={setOpen}
|
||||
>
|
||||
<PortalToFollowElemTrigger onClick={() => setOpen(v => !v)}>
|
||||
<TooltipPlus
|
||||
popupContent={t('workflow.common.viewRunHistory')}
|
||||
>
|
||||
<div
|
||||
className={`
|
||||
flex items-center justify-center w-7 h-7 rounded-md hover:bg-black/5 cursor-pointer
|
||||
${open && 'bg-primary-50'}
|
||||
`}
|
||||
onClick={() => {
|
||||
setCurrentLogItem()
|
||||
setShowMessageLogModal(false)
|
||||
}}
|
||||
>
|
||||
<ClockPlay className={`w-4 h-4 ${open ? 'text-primary-600' : 'text-gray-500'}`} />
|
||||
</div>
|
||||
</TooltipPlus>
|
||||
{
|
||||
withText && (
|
||||
<div className={cn(
|
||||
'flex items-center px-3 h-8 rounded-lg border-[0.5px] border-gray-200 bg-white shadow-xs',
|
||||
'text-[13px] font-medium text-primary-600 cursor-pointer',
|
||||
open && '!bg-primary-50',
|
||||
)}>
|
||||
<ClockPlay
|
||||
className={'mr-1 w-4 h-4'}
|
||||
/>
|
||||
{t('workflow.common.showRunHistory')}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
{
|
||||
!withText && (
|
||||
<TooltipPlus
|
||||
popupContent={t('workflow.common.viewRunHistory')}
|
||||
>
|
||||
<div
|
||||
className={`
|
||||
flex items-center justify-center w-7 h-7 rounded-md hover:bg-black/5 cursor-pointer
|
||||
${open && 'bg-primary-50'}
|
||||
`}
|
||||
onClick={() => {
|
||||
setCurrentLogItem()
|
||||
setShowMessageLogModal(false)
|
||||
}}
|
||||
>
|
||||
<ClockPlay className={`w-4 h-4 ${open ? 'text-primary-600' : 'text-gray-500'}`} />
|
||||
</div>
|
||||
</TooltipPlus>
|
||||
)
|
||||
}
|
||||
</PortalToFollowElemTrigger>
|
||||
<PortalToFollowElemContent className='z-[12]'>
|
||||
<div
|
||||
@@ -138,6 +169,8 @@ const ViewHistory = () => {
|
||||
})
|
||||
handleBackupDraft()
|
||||
setOpen(false)
|
||||
handleNodesCancelSelected()
|
||||
handleCancelDebugAndPreviewPanel()
|
||||
}}
|
||||
>
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user