mirror of
				https://github.com/langgenius/dify.git
				synced 2025-10-31 10:53:02 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			222 lines
		
	
	
		
			8.4 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			222 lines
		
	
	
		
			8.4 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| 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)
 | 
