|
import { |
|
memo, |
|
useState, |
|
} from 'react' |
|
import useSWR from 'swr' |
|
import { useTranslation } from 'react-i18next' |
|
import { useShallow } from 'zustand/react/shallow' |
|
import { |
|
RiCheckboxCircleLine, |
|
RiCloseLine, |
|
RiErrorWarningLine, |
|
} from '@remixicon/react' |
|
import { |
|
useIsChatMode, |
|
useNodesInteractions, |
|
useWorkflow, |
|
useWorkflowInteractions, |
|
useWorkflowRun, |
|
} from '../hooks' |
|
import { ControlMode, WorkflowRunningStatus } from '../types' |
|
import cn from '@/utils/classnames' |
|
import { |
|
PortalToFollowElem, |
|
PortalToFollowElemContent, |
|
PortalToFollowElemTrigger, |
|
} from '@/app/components/base/portal-to-follow-elem' |
|
import Tooltip from '@/app/components/base/tooltip' |
|
import { useStore as useAppStore } from '@/app/components/app/store' |
|
import { |
|
ClockPlay, |
|
ClockPlaySlim, |
|
} from '@/app/components/base/icons/src/vender/line/time' |
|
import { AlertTriangle } from '@/app/components/base/icons/src/vender/line/alertsAndFeedback' |
|
import { |
|
fetchChatRunHistory, |
|
fetchWorkflowRunHistory, |
|
} from '@/service/workflow' |
|
import Loading from '@/app/components/base/loading' |
|
import { |
|
useStore, |
|
useWorkflowStore, |
|
} from '@/app/components/workflow/store' |
|
|
|
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 setControlMode = useStore(s => s.setControlMode) |
|
const { appDetail, setCurrentLogItem, setShowMessageLogModal } = useAppStore(useShallow(state => ({ |
|
appDetail: state.appDetail, |
|
setCurrentLogItem: state.setCurrentLogItem, |
|
setShowMessageLogModal: state.setShowMessageLogModal, |
|
}))) |
|
const historyWorkflowData = useStore(s => s.historyWorkflowData) |
|
const { handleBackupDraft } = useWorkflowRun() |
|
const { data: runList, isLoading: runListLoading } = useSWR((appDetail && !isChatMode && open) ? `/apps/${appDetail.id}/workflow-runs` : null, fetchWorkflowRunHistory) |
|
const { data: chatList, isLoading: chatListLoading } = useSWR((appDetail && isChatMode && open) ? `/apps/${appDetail.id}/advanced-chat/workflow-runs` : null, fetchChatRunHistory) |
|
|
|
const data = isChatMode ? chatList : runList |
|
const isLoading = isChatMode ? chatListLoading : runListLoading |
|
|
|
return ( |
|
( |
|
<PortalToFollowElem |
|
placement={withText ? 'bottom-start' : 'bottom-end'} |
|
offset={{ |
|
mainAxis: 4, |
|
crossAxis: withText ? -8 : 10, |
|
}} |
|
open={open} |
|
onOpenChange={setOpen} |
|
> |
|
<PortalToFollowElemTrigger onClick={() => setOpen(v => !v)}> |
|
{ |
|
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 && ( |
|
<Tooltip |
|
popupContent={t('workflow.common.viewRunHistory')} |
|
> |
|
<div |
|
className={cn('group flex items-center justify-center w-7 h-7 rounded-md hover:bg-state-accent-hover cursor-pointer', open && 'bg-state-accent-hover')} |
|
onClick={() => { |
|
setCurrentLogItem() |
|
setShowMessageLogModal(false) |
|
}} |
|
> |
|
<ClockPlay className={cn('w-4 h-4 group-hover:text-components-button-secondary-accent-text', open ? 'text-components-button-secondary-accent-text' : 'text-components-button-ghost-text')} /> |
|
</div> |
|
</Tooltip> |
|
) |
|
} |
|
</PortalToFollowElemTrigger> |
|
<PortalToFollowElemContent className='z-[12]'> |
|
<div |
|
className='flex flex-col ml-2 w-[240px] bg-white border-[0.5px] border-gray-200 shadow-xl rounded-xl overflow-y-auto' |
|
style={{ |
|
maxHeight: 'calc(2 / 3 * 100vh)', |
|
}} |
|
> |
|
<div className='sticky top-0 bg-white flex items-center justify-between px-4 pt-3 text-base font-semibold text-gray-900'> |
|
<div className='grow'>{t('workflow.common.runHistory')}</div> |
|
<div |
|
className='shrink-0 flex items-center justify-center w-6 h-6 cursor-pointer' |
|
onClick={() => { |
|
setCurrentLogItem() |
|
setShowMessageLogModal(false) |
|
setOpen(false) |
|
}} |
|
> |
|
<RiCloseLine className='w-4 h-4 text-gray-500' /> |
|
</div> |
|
</div> |
|
{ |
|
isLoading && ( |
|
<div className='flex items-center justify-center h-10'> |
|
<Loading /> |
|
</div> |
|
) |
|
} |
|
{ |
|
!isLoading && ( |
|
<div className='p-2'> |
|
{ |
|
!data?.data.length && ( |
|
<div className='py-12'> |
|
<ClockPlaySlim className='mx-auto mb-2 w-8 h-8 text-gray-300' /> |
|
<div className='text-center text-[13px] text-gray-400'> |
|
{t('workflow.common.notRunning')} |
|
</div> |
|
</div> |
|
) |
|
} |
|
{ |
|
data?.data.map(item => ( |
|
<div |
|
key={item.id} |
|
className={cn( |
|
'flex mb-0.5 px-2 py-[7px] rounded-lg hover:bg-primary-50 cursor-pointer', |
|
item.id === historyWorkflowData?.id && 'bg-primary-50', |
|
)} |
|
onClick={() => { |
|
workflowStore.setState({ |
|
historyWorkflowData: item, |
|
showInputsPanel: false, |
|
showEnvPanel: false, |
|
}) |
|
handleBackupDraft() |
|
setOpen(false) |
|
handleNodesCancelSelected() |
|
handleCancelDebugAndPreviewPanel() |
|
setControlMode(ControlMode.Hand) |
|
}} |
|
> |
|
{ |
|
!isChatMode && item.status === WorkflowRunningStatus.Stopped && ( |
|
<AlertTriangle className='mt-0.5 mr-1.5 w-3.5 h-3.5 text-[#F79009]' /> |
|
) |
|
} |
|
{ |
|
!isChatMode && item.status === WorkflowRunningStatus.Failed && ( |
|
<RiErrorWarningLine className='mt-0.5 mr-1.5 w-3.5 h-3.5 text-[#F04438]' /> |
|
) |
|
} |
|
{ |
|
!isChatMode && item.status === WorkflowRunningStatus.Succeeded && ( |
|
<RiCheckboxCircleLine className='mt-0.5 mr-1.5 w-3.5 h-3.5 text-[#12B76A]' /> |
|
) |
|
} |
|
<div> |
|
<div |
|
className={cn( |
|
'flex items-center text-[13px] font-medium leading-[18px]', |
|
item.id === historyWorkflowData?.id && 'text-primary-600', |
|
)} |
|
> |
|
{`Test ${isChatMode ? 'Chat' : 'Run'}#${item.sequence_number}`} |
|
</div> |
|
<div className='flex items-center text-xs text-gray-500 leading-[18px]'> |
|
{item.created_by_account.name} · {formatTimeFromNow((item.finished_at || item.created_at) * 1000)} |
|
</div> |
|
</div> |
|
</div> |
|
)) |
|
} |
|
</div> |
|
) |
|
} |
|
</div> |
|
</PortalToFollowElemContent> |
|
</PortalToFollowElem> |
|
) |
|
) |
|
} |
|
|
|
export default memo(ViewHistory) |
|
|