| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273 | 
							- import {
 -   memo,
 -   useCallback,
 -   useMemo,
 -   useState,
 - } from 'react'
 - import {
 -   RiCloseLine,
 -   RiHistoryLine,
 - } from '@remixicon/react'
 - import { useTranslation } from 'react-i18next'
 - import { useShallow } from 'zustand/react/shallow'
 - import { useStoreApi } from 'reactflow'
 - import {
 -   useNodesReadOnly,
 -   useWorkflowHistory,
 - } from '../hooks'
 - import TipPopup from '../operator/tip-popup'
 - import type { WorkflowHistoryState } from '../workflow-history-store'
 - import cn from '@/utils/classnames'
 - import {
 -   PortalToFollowElem,
 -   PortalToFollowElemContent,
 -   PortalToFollowElemTrigger,
 - } from '@/app/components/base/portal-to-follow-elem'
 - import { useStore as useAppStore } from '@/app/components/app/store'
 - 
 - type ChangeHistoryEntry = {
 -   label: string
 -   index: number
 -   state: Partial<WorkflowHistoryState>
 - }
 - 
 - type ChangeHistoryList = {
 -   pastStates: ChangeHistoryEntry[]
 -   futureStates: ChangeHistoryEntry[]
 -   statesCount: number
 - }
 - 
 - const ViewWorkflowHistory = () => {
 -   const { t } = useTranslation()
 -   const [open, setOpen] = useState(false)
 - 
 -   const { nodesReadOnly } = useNodesReadOnly()
 -   const { setCurrentLogItem, setShowMessageLogModal } = useAppStore(useShallow(state => ({
 -     appDetail: state.appDetail,
 -     setCurrentLogItem: state.setCurrentLogItem,
 -     setShowMessageLogModal: state.setShowMessageLogModal,
 -   })))
 -   const reactflowStore = useStoreApi()
 -   const { store, getHistoryLabel } = useWorkflowHistory()
 - 
 -   const { pastStates, futureStates, undo, redo, clear } = store.temporal.getState()
 -   const [currentHistoryStateIndex, setCurrentHistoryStateIndex] = useState<number>(0)
 - 
 -   const handleClearHistory = useCallback(() => {
 -     clear()
 -     setCurrentHistoryStateIndex(0)
 -   }, [clear])
 - 
 -   const handleSetState = useCallback(({ index }: ChangeHistoryEntry) => {
 -     const { setEdges, setNodes } = reactflowStore.getState()
 -     const diff = currentHistoryStateIndex + index
 -     if (diff === 0)
 -       return
 - 
 -     if (diff < 0)
 -       undo(diff * -1)
 -     else
 -       redo(diff)
 - 
 -     const { edges, nodes } = store.getState()
 -     if (edges.length === 0 && nodes.length === 0)
 -       return
 - 
 -     setEdges(edges)
 -     setNodes(nodes)
 -   }, [currentHistoryStateIndex, reactflowStore, redo, store, undo])
 - 
 -   const calculateStepLabel = useCallback((index: number) => {
 -     if (!index)
 -       return
 - 
 -     const count = index < 0 ? index * -1 : index
 -     return `${index > 0 ? t('workflow.changeHistory.stepForward', { count }) : t('workflow.changeHistory.stepBackward', { count })}`
 -   }
 -   , [t])
 - 
 -   const calculateChangeList: ChangeHistoryList = useMemo(() => {
 -     const filterList = (list: any, startIndex = 0, reverse = false) => list.map((state: Partial<WorkflowHistoryState>, index: number) => {
 -       return {
 -         label: state.workflowHistoryEvent && getHistoryLabel(state.workflowHistoryEvent),
 -         index: reverse ? list.length - 1 - index - startIndex : index - startIndex,
 -         state,
 -       }
 -     }).filter(Boolean)
 - 
 -     const historyData = {
 -       pastStates: filterList(pastStates, pastStates.length).reverse(),
 -       futureStates: filterList([...futureStates, (!pastStates.length && !futureStates.length) ? undefined : store.getState()].filter(Boolean), 0, true),
 -       statesCount: 0,
 -     }
 - 
 -     historyData.statesCount = pastStates.length + futureStates.length
 - 
 -     return {
 -       ...historyData,
 -       statesCount: pastStates.length + futureStates.length,
 -     }
 -   }, [futureStates, getHistoryLabel, pastStates, store])
 - 
 -   return (
 -     (
 -       <PortalToFollowElem
 -         placement='bottom-end'
 -         offset={{
 -           mainAxis: 4,
 -           crossAxis: 131,
 -         }}
 -         open={open}
 -         onOpenChange={setOpen}
 -       >
 -         <PortalToFollowElemTrigger onClick={() => !nodesReadOnly && setOpen(v => !v)}>
 -           <TipPopup
 -             title={t('workflow.changeHistory.title')}
 -           >
 -             <div
 -               className={`
 -                 flex items-center justify-center w-8 h-8 rounded-md hover:bg-black/5 cursor-pointer
 -                 ${open && 'bg-primary-50'} ${nodesReadOnly && 'bg-primary-50 opacity-50 !cursor-not-allowed'}
 -               `}
 -               onClick={() => {
 -                 if (nodesReadOnly)
 -                   return
 -                 setCurrentLogItem()
 -                 setShowMessageLogModal(false)
 -               }}
 -             >
 -               <RiHistoryLine className={`w-4 h-4 hover:bg-black/5 hover:text-gray-700 ${open ? 'text-primary-600' : 'text-gray-500'}`} />
 -             </div>
 -           </TipPopup>
 -         </PortalToFollowElemTrigger>
 -         <PortalToFollowElemContent className='z-[12]'>
 -           <div
 -             className='flex flex-col ml-2 min-w-[240px] max-w-[360px] bg-white border-[0.5px] border-gray-200 shadow-xl rounded-xl overflow-y-auto'
 -           >
 -             <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.changeHistory.title')}</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>
 -             {
 -               (
 -                 <div
 -                   className='p-2 overflow-y-auto'
 -                   style={{
 -                     maxHeight: 'calc(1 / 2 * 100vh)',
 -                   }}
 -                 >
 -                   {
 -                     !calculateChangeList.statesCount && (
 -                       <div className='py-12'>
 -                         <RiHistoryLine className='mx-auto mb-2 w-8 h-8 text-gray-300' />
 -                         <div className='text-center text-[13px] text-gray-400'>
 -                           {t('workflow.changeHistory.placeholder')}
 -                         </div>
 -                       </div>
 -                     )
 -                   }
 -                   <div className='flex flex-col'>
 -                     {
 -                       calculateChangeList.futureStates.map((item: ChangeHistoryEntry) => (
 -                         <div
 -                           key={item?.index}
 -                           className={cn(
 -                             'flex mb-0.5 px-2 py-[7px] rounded-lg hover:bg-primary-50 cursor-pointer',
 -                             item?.index === currentHistoryStateIndex && 'bg-primary-50',
 -                           )}
 -                           onClick={() => {
 -                             handleSetState(item)
 -                             setOpen(false)
 -                           }}
 -                         >
 -                           <div>
 -                             <div
 -                               className={cn(
 -                                 'flex items-center text-[13px] font-medium leading-[18px]',
 -                                 item?.index === currentHistoryStateIndex && 'text-primary-600',
 -                               )}
 -                             >
 -                               {item?.label || t('workflow.changeHistory.sessionStart')} ({calculateStepLabel(item?.index)}{item?.index === currentHistoryStateIndex && t('workflow.changeHistory.currentState')})
 -                             </div>
 -                           </div>
 -                         </div>
 -                       ))
 -                     }
 -                     {
 -                       calculateChangeList.pastStates.map((item: ChangeHistoryEntry) => (
 -                         <div
 -                           key={item?.index}
 -                           className={cn(
 -                             'flex mb-0.5 px-2 py-[7px] rounded-lg hover:bg-primary-50 cursor-pointer',
 -                             item?.index === calculateChangeList.statesCount - 1 && 'bg-primary-50',
 -                           )}
 -                           onClick={() => {
 -                             handleSetState(item)
 -                             setOpen(false)
 -                           }}
 -                         >
 -                           <div>
 -                             <div
 -                               className={cn(
 -                                 'flex items-center text-[13px] font-medium leading-[18px]',
 -                                 item?.index === calculateChangeList.statesCount - 1 && 'text-primary-600',
 -                               )}
 -                             >
 -                               {item?.label || t('workflow.changeHistory.sessionStart')} ({calculateStepLabel(item?.index)})
 -                             </div>
 -                           </div>
 -                         </div>
 -                       ))
 -                     }
 -                   </div>
 -                 </div>
 -               )
 -             }
 -             {
 -               !!calculateChangeList.statesCount && (
 -                 <>
 -                   <div className="h-[1px] bg-gray-100" />
 -                   <div
 -                     className={cn(
 -                       'flex my-0.5 px-2 py-[7px] rounded-lg cursor-pointer',
 -                       'hover:bg-red-50 hover:text-red-600',
 -                     )}
 -                     onClick={() => {
 -                       handleClearHistory()
 -                       setOpen(false)
 -                     }}
 -                   >
 -                     <div>
 -                       <div
 -                         className={cn(
 -                           'flex items-center text-[13px] font-medium leading-[18px]',
 -                         )}
 -                       >
 -                         {t('workflow.changeHistory.clearHistory')}
 -                       </div>
 -                     </div>
 -                   </div>
 -                 </>
 -               )
 -             }
 -             <div className="px-3 w-[240px] py-2 text-xs text-gray-500" >
 -               <div className="flex items-center mb-1 h-[22px] font-medium uppercase">{t('workflow.changeHistory.hint')}</div>
 -               <div className="mb-1 text-gray-700 leading-[18px]">{t('workflow.changeHistory.hintText')}</div>
 -             </div>
 -           </div>
 -         </PortalToFollowElemContent>
 -       </PortalToFollowElem>
 -     )
 -   )
 - }
 - 
 - export default memo(ViewWorkflowHistory)
 
 
  |