Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

loop-result-panel.tsx 5.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. 'use client'
  2. import type { FC } from 'react'
  3. import React, { useCallback, useState } from 'react'
  4. import { useTranslation } from 'react-i18next'
  5. import {
  6. RiArrowLeftLine,
  7. RiArrowRightSLine,
  8. RiErrorWarningLine,
  9. RiLoader2Line,
  10. } from '@remixicon/react'
  11. import { NodeRunningStatus } from '@/app/components/workflow/types'
  12. import TracingPanel from '@/app/components/workflow/run/tracing-panel'
  13. import { Loop } from '@/app/components/base/icons/src/vender/workflow'
  14. import cn from '@/utils/classnames'
  15. import type { LoopDurationMap, LoopVariableMap, NodeTracing } from '@/types/workflow'
  16. import CodeEditor from '@/app/components/workflow/nodes/_base/components/editor/code-editor'
  17. import { CodeLanguage } from '@/app/components/workflow/nodes/code/types'
  18. const i18nPrefix = 'workflow.singleRun'
  19. type Props = {
  20. list: NodeTracing[][]
  21. onBack: () => void
  22. loopDurationMap?: LoopDurationMap
  23. loopVariableMap?: LoopVariableMap
  24. }
  25. const LoopResultPanel: FC<Props> = ({
  26. list,
  27. onBack,
  28. loopDurationMap,
  29. loopVariableMap,
  30. }) => {
  31. const { t } = useTranslation()
  32. const [expandedLoops, setExpandedLoops] = useState<Record<number, boolean>>({})
  33. const toggleLoop = useCallback((index: number) => {
  34. setExpandedLoops(prev => ({
  35. ...prev,
  36. [index]: !prev[index],
  37. }))
  38. }, [])
  39. const countLoopDuration = (loop: NodeTracing[], loopDurationMap: LoopDurationMap): string => {
  40. const loopRunIndex = loop[0]?.execution_metadata?.loop_index as number
  41. const loopRunId = loop[0]?.execution_metadata?.parallel_mode_run_id
  42. const loopItem = loopDurationMap[loopRunId || loopRunIndex]
  43. const duration = loopItem
  44. return `${(duration && duration > 0.01) ? duration.toFixed(2) : 0.01}s`
  45. }
  46. const loopStatusShow = (index: number, loop: NodeTracing[], loopDurationMap?: LoopDurationMap) => {
  47. const hasFailed = loop.some(item => item.status === NodeRunningStatus.Failed)
  48. const isRunning = loop.some(item => item.status === NodeRunningStatus.Running)
  49. const hasDurationMap = loopDurationMap && Object.keys(loopDurationMap).length !== 0
  50. if (hasFailed)
  51. return <RiErrorWarningLine className='h-4 w-4 text-text-destructive' />
  52. if (isRunning)
  53. return <RiLoader2Line className='h-3.5 w-3.5 animate-spin text-primary-600' />
  54. return (
  55. <>
  56. {hasDurationMap && (
  57. <div className='system-xs-regular text-text-tertiary'>
  58. {countLoopDuration(loop, loopDurationMap)}
  59. </div>
  60. )}
  61. <RiArrowRightSLine
  62. className={cn(
  63. 'h-4 w-4 shrink-0 text-text-tertiary transition-transform duration-200',
  64. expandedLoops[index] && 'rotate-90',
  65. )}
  66. />
  67. </>
  68. )
  69. }
  70. return (
  71. <div className='bg-components-panel-bg'>
  72. <div
  73. className='flex h-8 cursor-pointer items-center border-b-[0.5px] border-b-divider-regular px-4 text-text-accent-secondary'
  74. onClick={(e) => {
  75. e.stopPropagation()
  76. e.nativeEvent.stopImmediatePropagation()
  77. onBack()
  78. }}
  79. >
  80. <RiArrowLeftLine className='mr-1 h-4 w-4' />
  81. <div className='system-sm-medium'>{t(`${i18nPrefix}.back`)}</div>
  82. </div>
  83. {/* List */}
  84. <div className='bg-components-panel-bg p-2'>
  85. {list.map((loop, index) => (
  86. <div key={index} className={cn('mb-1 overflow-hidden rounded-xl border-none bg-background-section-burn')}>
  87. <div
  88. className={cn(
  89. 'flex w-full cursor-pointer items-center justify-between px-3',
  90. expandedLoops[index] ? 'pb-2 pt-3' : 'py-3',
  91. 'rounded-xl text-left',
  92. )}
  93. onClick={() => toggleLoop(index)}
  94. >
  95. <div className={cn('flex grow items-center gap-2')}>
  96. <div className='flex h-4 w-4 shrink-0 items-center justify-center rounded-[5px] border-divider-subtle bg-util-colors-cyan-cyan-500'>
  97. <Loop className='h-3 w-3 text-text-primary-on-surface' />
  98. </div>
  99. <span className='system-sm-semibold-uppercase grow text-text-primary'>
  100. {t(`${i18nPrefix}.loop`)} {index + 1}
  101. </span>
  102. {loopStatusShow(index, loop, loopDurationMap)}
  103. </div>
  104. </div>
  105. {expandedLoops[index] && <div
  106. className="h-px grow bg-divider-subtle"
  107. ></div>}
  108. <div className={cn(
  109. 'transition-all duration-200',
  110. expandedLoops[index]
  111. ? 'opacity-100'
  112. : 'max-h-0 opacity-0 overflow-hidden',
  113. )}>
  114. {
  115. loopVariableMap?.[index] && (
  116. <div className='p-2 pb-0'>
  117. <CodeEditor
  118. readOnly
  119. title={<div>{t('workflow.nodes.loop.loopVariables').toLocaleUpperCase()}</div>}
  120. language={CodeLanguage.json}
  121. height={112}
  122. value={loopVariableMap[index]}
  123. isJSONStringifyBeauty
  124. />
  125. </div>
  126. )
  127. }
  128. <TracingPanel
  129. list={loop}
  130. className='bg-background-section-burn'
  131. />
  132. </div>
  133. </div>
  134. ))}
  135. </div>
  136. </div>
  137. )
  138. }
  139. export default React.memo(LoopResultPanel)