| import type { FC } from 'react' | |
| import { | |
| memo, | |
| useCallback, | |
| useMemo, | |
| } from 'react' | |
| import { RiApps2AddLine } from '@remixicon/react' | |
| import { useNodes } from 'reactflow' | |
| import { useTranslation } from 'react-i18next' | |
| import { useContext } from 'use-context-selector' | |
| import { | |
| useStore, | |
| useWorkflowStore, | |
| } from '../store' | |
| import { | |
| BlockEnum, | |
| InputVarType, | |
| } from '../types' | |
| import type { StartNodeType } from '../nodes/start/types' | |
| import { | |
| useChecklistBeforePublish, | |
| useIsChatMode, | |
| useNodesReadOnly, | |
| useNodesSyncDraft, | |
| useWorkflowMode, | |
| useWorkflowRun, | |
| } from '../hooks' | |
| import AppPublisher from '../../app/app-publisher' | |
| import { ToastContext } from '../../base/toast' | |
| 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 ChatVariableButton from './chat-variable-button' | |
| import EnvButton from './env-button' | |
| import Button from '@/app/components/base/button' | |
| 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' | |
| import { useFeatures } from '@/app/components/base/features/hooks' | |
| const Header: FC = () => { | |
| const { t } = useTranslation() | |
| const workflowStore = useWorkflowStore() | |
| const appDetail = useAppStore(s => s.appDetail) | |
| const appSidebarExpand = useAppStore(s => s.appSidebarExpand) | |
| const appID = appDetail?.id | |
| const isChatMode = useIsChatMode() | |
| const { nodesReadOnly, getNodesReadOnly } = useNodesReadOnly() | |
| const publishedAt = useStore(s => s.publishedAt) | |
| const draftUpdatedAt = useStore(s => s.draftUpdatedAt) | |
| const toolPublished = useStore(s => s.toolPublished) | |
| const nodes = useNodes<StartNodeType>() | |
| const startNode = nodes.find(node => node.data.type === BlockEnum.Start) | |
| const startVariables = startNode?.data.variables | |
| const fileSettings = useFeatures(s => s.features.file) | |
| const variables = useMemo(() => { | |
| const data = startVariables || [] | |
| if (fileSettings?.image?.enabled) { | |
| return [ | |
| ...data, | |
| { | |
| type: InputVarType.files, | |
| variable: '__image', | |
| required: false, | |
| label: 'files', | |
| }, | |
| ] | |
| } | |
| return data | |
| }, [fileSettings?.image?.enabled, startVariables]) | |
| const { | |
| handleLoadBackupDraft, | |
| handleBackupDraft, | |
| handleRestoreFromPublishedWorkflow, | |
| } = useWorkflowRun() | |
| const { handleCheckBeforePublish } = useChecklistBeforePublish() | |
| const { handleSyncWorkflowDraft } = useNodesSyncDraft() | |
| const { notify } = useContext(ToastContext) | |
| const { | |
| normal, | |
| restoring, | |
| viewHistory, | |
| } = useWorkflowMode() | |
| const handleShowFeatures = useCallback(() => { | |
| const { | |
| showFeaturesPanel, | |
| isRestoring, | |
| setShowFeaturesPanel, | |
| } = workflowStore.getState() | |
| if (getNodesReadOnly() && !isRestoring) | |
| return | |
| setShowFeaturesPanel(!showFeaturesPanel) | |
| }, [workflowStore, getNodesReadOnly]) | |
| const handleCancelRestore = useCallback(() => { | |
| handleLoadBackupDraft() | |
| workflowStore.setState({ isRestoring: false }) | |
| }, [workflowStore, handleLoadBackupDraft]) | |
| const handleRestore = useCallback(() => { | |
| workflowStore.setState({ isRestoring: false }) | |
| workflowStore.setState({ backupDraft: undefined }) | |
| handleSyncWorkflowDraft(true) | |
| }, [handleSyncWorkflowDraft, workflowStore]) | |
| const onPublish = useCallback(async () => { | |
| if (handleCheckBeforePublish()) { | |
| const res = await publishWorkflow(`/apps/${appID}/workflows/publish`) | |
| if (res) { | |
| notify({ type: 'success', message: t('common.api.actionSuccess') }) | |
| workflowStore.getState().setPublishedAt(res.created_at) | |
| } | |
| } | |
| else { | |
| throw new Error('Checklist failed') | |
| } | |
| }, [appID, handleCheckBeforePublish, notify, t, workflowStore]) | |
| const onStartRestoring = useCallback(() => { | |
| workflowStore.setState({ isRestoring: true }) | |
| handleBackupDraft() | |
| handleRestoreFromPublishedWorkflow() | |
| }, [handleBackupDraft, handleRestoreFromPublishedWorkflow, workflowStore]) | |
| const onPublisherToggle = useCallback((state: boolean) => { | |
| if (state) | |
| handleSyncWorkflowDraft(true) | |
| }, [handleSyncWorkflowDraft]) | |
| const handleGoBackToEdit = useCallback(() => { | |
| handleLoadBackupDraft() | |
| workflowStore.setState({ historyWorkflowData: undefined }) | |
| }, [workflowStore, handleLoadBackupDraft]) | |
| const handleToolConfigureUpdate = useCallback(() => { | |
| workflowStore.setState({ toolPublished: true }) | |
| }, [workflowStore]) | |
| return ( | |
| <div | |
| className='absolute top-0 left-0 z-10 flex items-center justify-between w-full px-3 h-14' | |
| style={{ | |
| background: 'linear-gradient(180deg, #F9FAFB 0%, rgba(249, 250, 251, 0.00) 100%)', | |
| }} | |
| > | |
| <div> | |
| { | |
| appSidebarExpand === 'collapse' && ( | |
| <div className='text-xs font-medium text-gray-700'>{appDetail?.name}</div> | |
| ) | |
| } | |
| { | |
| normal && <EditingTitle /> | |
| } | |
| { | |
| viewHistory && <RunningTitle /> | |
| } | |
| { | |
| restoring && <RestoringTitle /> | |
| } | |
| </div> | |
| { | |
| normal && ( | |
| <div className='flex items-center gap-2'> | |
| {/* <GlobalVariableButton disabled={nodesReadOnly} /> */} | |
| {isChatMode && <ChatVariableButton disabled={nodesReadOnly} />} | |
| <EnvButton disabled={nodesReadOnly} /> | |
| <div className='w-[1px] h-3.5 bg-gray-200'></div> | |
| <RunAndHistory /> | |
| <Button className='text-components-button-secondary-text' onClick={handleShowFeatures}> | |
| <RiApps2AddLine className='w-4 h-4 mr-1 text-components-button-secondary-text' /> | |
| {t('workflow.common.features')} | |
| </Button> | |
| <AppPublisher | |
| {...{ | |
| publishedAt, | |
| draftUpdatedAt, | |
| disabled: nodesReadOnly, | |
| toolPublished, | |
| inputs: variables, | |
| onRefreshData: handleToolConfigureUpdate, | |
| onPublish, | |
| onRestore: onStartRestoring, | |
| onToggle: onPublisherToggle, | |
| crossAxisOffset: 4, | |
| }} | |
| /> | |
| </div> | |
| ) | |
| } | |
| { | |
| viewHistory && ( | |
| <div className='flex items-center'> | |
| <ViewHistory withText /> | |
| <div className='mx-2 w-[1px] h-3.5 bg-gray-200'></div> | |
| <Button | |
| variant='primary' | |
| className='mr-2' | |
| onClick={handleGoBackToEdit} | |
| > | |
| <ArrowNarrowLeft className='w-4 h-4 mr-1' /> | |
| {t('workflow.common.goBackToEdit')} | |
| </Button> | |
| </div> | |
| ) | |
| } | |
| { | |
| restoring && ( | |
| <div className='flex items-center'> | |
| <Button className='text-components-button-secondary-text' onClick={handleShowFeatures}> | |
| <RiApps2AddLine className='w-4 h-4 mr-1 text-components-button-secondary-text' /> | |
| {t('workflow.common.features')} | |
| </Button> | |
| <div className='mx-2 w-[1px] h-3.5 bg-gray-200'></div> | |
| <Button | |
| className='mr-2' | |
| onClick={handleCancelRestore} | |
| > | |
| {t('common.operation.cancel')} | |
| </Button> | |
| <Button | |
| onClick={handleRestore} | |
| variant='primary' | |
| > | |
| {t('workflow.common.restore')} | |
| </Button> | |
| </div> | |
| ) | |
| } | |
| </div> | |
| ) | |
| } | |
| export default memo(Header) | |