您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

view-history.tsx 8.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. import {
  2. memo,
  3. useState,
  4. } from 'react'
  5. import useSWR from 'swr'
  6. import { useTranslation } from 'react-i18next'
  7. import { useShallow } from 'zustand/react/shallow'
  8. import {
  9. RiCheckboxCircleLine,
  10. RiCloseLine,
  11. RiErrorWarningLine,
  12. } from '@remixicon/react'
  13. import {
  14. useFormatTimeFromNow,
  15. useIsChatMode,
  16. useNodesInteractions,
  17. useWorkflowInteractions,
  18. useWorkflowRun,
  19. } from '../hooks'
  20. import { ControlMode, WorkflowRunningStatus } from '../types'
  21. import { formatWorkflowRunIdentifier } from '../utils'
  22. import cn from '@/utils/classnames'
  23. import {
  24. PortalToFollowElem,
  25. PortalToFollowElemContent,
  26. PortalToFollowElemTrigger,
  27. } from '@/app/components/base/portal-to-follow-elem'
  28. import Tooltip from '@/app/components/base/tooltip'
  29. import { useStore as useAppStore } from '@/app/components/app/store'
  30. import {
  31. ClockPlay,
  32. ClockPlaySlim,
  33. } from '@/app/components/base/icons/src/vender/line/time'
  34. import { AlertTriangle } from '@/app/components/base/icons/src/vender/line/alertsAndFeedback'
  35. import {
  36. fetchChatRunHistory,
  37. fetchWorkflowRunHistory,
  38. } from '@/service/workflow'
  39. import Loading from '@/app/components/base/loading'
  40. import {
  41. useStore,
  42. useWorkflowStore,
  43. } from '@/app/components/workflow/store'
  44. type ViewHistoryProps = {
  45. withText?: boolean
  46. }
  47. const ViewHistory = ({
  48. withText,
  49. }: ViewHistoryProps) => {
  50. const { t } = useTranslation()
  51. const isChatMode = useIsChatMode()
  52. const [open, setOpen] = useState(false)
  53. const { formatTimeFromNow } = useFormatTimeFromNow()
  54. const {
  55. handleNodesCancelSelected,
  56. } = useNodesInteractions()
  57. const {
  58. handleCancelDebugAndPreviewPanel,
  59. } = useWorkflowInteractions()
  60. const workflowStore = useWorkflowStore()
  61. const setControlMode = useStore(s => s.setControlMode)
  62. const { appDetail, setCurrentLogItem, setShowMessageLogModal } = useAppStore(useShallow(state => ({
  63. appDetail: state.appDetail,
  64. setCurrentLogItem: state.setCurrentLogItem,
  65. setShowMessageLogModal: state.setShowMessageLogModal,
  66. })))
  67. const historyWorkflowData = useStore(s => s.historyWorkflowData)
  68. const { handleBackupDraft } = useWorkflowRun()
  69. const { data: runList, isLoading: runListLoading } = useSWR((appDetail && !isChatMode && open) ? `/apps/${appDetail.id}/workflow-runs` : null, fetchWorkflowRunHistory)
  70. const { data: chatList, isLoading: chatListLoading } = useSWR((appDetail && isChatMode && open) ? `/apps/${appDetail.id}/advanced-chat/workflow-runs` : null, fetchChatRunHistory)
  71. const data = isChatMode ? chatList : runList
  72. const isLoading = isChatMode ? chatListLoading : runListLoading
  73. return (
  74. (
  75. <PortalToFollowElem
  76. placement={withText ? 'bottom-start' : 'bottom-end'}
  77. offset={{
  78. mainAxis: 4,
  79. crossAxis: withText ? -8 : 10,
  80. }}
  81. open={open}
  82. onOpenChange={setOpen}
  83. >
  84. <PortalToFollowElemTrigger onClick={() => setOpen(v => !v)}>
  85. {
  86. withText && (
  87. <div className={cn(
  88. 'flex h-8 items-center rounded-lg border-[0.5px] border-components-button-secondary-border bg-components-button-secondary-bg px-3 shadow-xs',
  89. 'cursor-pointer text-[13px] font-medium text-components-button-secondary-text hover:bg-components-button-secondary-bg-hover',
  90. open && 'bg-components-button-secondary-bg-hover',
  91. )}>
  92. <ClockPlay
  93. className={'mr-1 h-4 w-4'}
  94. />
  95. {t('workflow.common.showRunHistory')}
  96. </div>
  97. )
  98. }
  99. {
  100. !withText && (
  101. <Tooltip
  102. popupContent={t('workflow.common.viewRunHistory')}
  103. >
  104. <div
  105. className={cn('group flex h-7 w-7 cursor-pointer items-center justify-center rounded-md hover:bg-state-accent-hover', open && 'bg-state-accent-hover')}
  106. onClick={() => {
  107. setCurrentLogItem()
  108. setShowMessageLogModal(false)
  109. }}
  110. >
  111. <ClockPlay className={cn('h-4 w-4 group-hover:text-components-button-secondary-accent-text', open ? 'text-components-button-secondary-accent-text' : 'text-components-button-ghost-text')} />
  112. </div>
  113. </Tooltip>
  114. )
  115. }
  116. </PortalToFollowElemTrigger>
  117. <PortalToFollowElemContent className='z-[12]'>
  118. <div
  119. className='ml-2 flex w-[240px] flex-col overflow-y-auto rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg shadow-xl'
  120. style={{
  121. maxHeight: 'calc(2 / 3 * 100vh)',
  122. }}
  123. >
  124. <div className='sticky top-0 flex items-center justify-between bg-components-panel-bg px-4 pt-3 text-base font-semibold text-text-primary'>
  125. <div className='grow'>{t('workflow.common.runHistory')}</div>
  126. <div
  127. className='flex h-6 w-6 shrink-0 cursor-pointer items-center justify-center'
  128. onClick={() => {
  129. setCurrentLogItem()
  130. setShowMessageLogModal(false)
  131. setOpen(false)
  132. }}
  133. >
  134. <RiCloseLine className='h-4 w-4 text-text-tertiary' />
  135. </div>
  136. </div>
  137. {
  138. isLoading && (
  139. <div className='flex h-10 items-center justify-center'>
  140. <Loading />
  141. </div>
  142. )
  143. }
  144. {
  145. !isLoading && (
  146. <div className='p-2'>
  147. {
  148. !data?.data.length && (
  149. <div className='py-12'>
  150. <ClockPlaySlim className='mx-auto mb-2 h-8 w-8 text-text-quaternary' />
  151. <div className='text-center text-[13px] text-text-quaternary'>
  152. {t('workflow.common.notRunning')}
  153. </div>
  154. </div>
  155. )
  156. }
  157. {
  158. data?.data.map(item => (
  159. <div
  160. key={item.id}
  161. className={cn(
  162. 'mb-0.5 flex cursor-pointer rounded-lg px-2 py-[7px] hover:bg-state-base-hover',
  163. item.id === historyWorkflowData?.id && 'bg-state-accent-hover hover:bg-state-accent-hover',
  164. )}
  165. onClick={() => {
  166. workflowStore.setState({
  167. historyWorkflowData: item,
  168. showInputsPanel: false,
  169. showEnvPanel: false,
  170. })
  171. handleBackupDraft()
  172. setOpen(false)
  173. handleNodesCancelSelected()
  174. handleCancelDebugAndPreviewPanel()
  175. setControlMode(ControlMode.Hand)
  176. }}
  177. >
  178. {
  179. !isChatMode && item.status === WorkflowRunningStatus.Stopped && (
  180. <AlertTriangle className='mr-1.5 mt-0.5 h-3.5 w-3.5 text-[#F79009]' />
  181. )
  182. }
  183. {
  184. !isChatMode && item.status === WorkflowRunningStatus.Failed && (
  185. <RiErrorWarningLine className='mr-1.5 mt-0.5 h-3.5 w-3.5 text-[#F04438]' />
  186. )
  187. }
  188. {
  189. !isChatMode && item.status === WorkflowRunningStatus.Succeeded && (
  190. <RiCheckboxCircleLine className='mr-1.5 mt-0.5 h-3.5 w-3.5 text-[#12B76A]' />
  191. )
  192. }
  193. <div>
  194. <div
  195. className={cn(
  196. 'flex items-center text-[13px] font-medium leading-[18px] text-text-primary',
  197. item.id === historyWorkflowData?.id && 'text-text-accent',
  198. )}
  199. >
  200. {`Test ${isChatMode ? 'Chat' : 'Run'}${formatWorkflowRunIdentifier(item.finished_at)}`}
  201. </div>
  202. <div className='flex items-center text-xs leading-[18px] text-text-tertiary'>
  203. {item.created_by_account?.name} · {formatTimeFromNow((item.finished_at || item.created_at) * 1000)}
  204. </div>
  205. </div>
  206. </div>
  207. ))
  208. }
  209. </div>
  210. )
  211. }
  212. </div>
  213. </PortalToFollowElemContent>
  214. </PortalToFollowElem>
  215. )
  216. )
  217. }
  218. export default memo(ViewHistory)