iteration-result-panel.tsx 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  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. RiArrowRightSLine,
  7. RiCloseLine,
  8. RiErrorWarningLine,
  9. } from '@remixicon/react'
  10. import { ArrowNarrowLeft } from '../../base/icons/src/vender/line/arrows'
  11. import TracingPanel from './tracing-panel'
  12. import { Iteration } from '@/app/components/base/icons/src/vender/workflow'
  13. import cn from '@/utils/classnames'
  14. import type { NodeTracing } from '@/types/workflow'
  15. const i18nPrefix = 'workflow.singleRun'
  16. type Props = {
  17. list: NodeTracing[][]
  18. onHide: () => void
  19. onBack: () => void
  20. noWrap?: boolean
  21. }
  22. const IterationResultPanel: FC<Props> = ({
  23. list,
  24. onHide,
  25. onBack,
  26. noWrap,
  27. }) => {
  28. const { t } = useTranslation()
  29. const [expandedIterations, setExpandedIterations] = useState<Record<number, boolean>>({})
  30. const toggleIteration = useCallback((index: number) => {
  31. setExpandedIterations(prev => ({
  32. ...prev,
  33. [index]: !prev[index],
  34. }))
  35. }, [])
  36. const main = (
  37. <>
  38. <div className={cn(!noWrap && 'shrink-0 ', 'px-4 pt-3')}>
  39. <div className='shrink-0 flex justify-between items-center h-8'>
  40. <div className='system-xl-semibold text-text-primary truncate'>
  41. {t(`${i18nPrefix}.testRunIteration`)}
  42. </div>
  43. <div className='ml-2 shrink-0 p-1 cursor-pointer' onClick={onHide}>
  44. <RiCloseLine className='w-4 h-4 text-text-tertiary' />
  45. </div>
  46. </div>
  47. <div className='flex items-center py-2 space-x-1 text-text-accent-secondary cursor-pointer' onClick={onBack}>
  48. <ArrowNarrowLeft className='w-4 h-4' />
  49. <div className='system-sm-medium'>{t(`${i18nPrefix}.back`)}</div>
  50. </div>
  51. </div>
  52. {/* List */}
  53. <div className={cn(!noWrap ? 'flex-grow overflow-auto' : 'max-h-full', 'p-2 bg-components-panel-bg')}>
  54. {list.map((iteration, index) => (
  55. <div key={index} className={cn('mb-1 overflow-hidden rounded-xl bg-background-section-burn border-none')}>
  56. <div
  57. className={cn(
  58. 'flex items-center justify-between w-full px-3 cursor-pointer',
  59. expandedIterations[index] ? 'pt-3 pb-2' : 'py-3',
  60. 'rounded-xl text-left',
  61. )}
  62. onClick={() => toggleIteration(index)}
  63. >
  64. <div className={cn('flex items-center gap-2 flex-grow')}>
  65. <div className='flex items-center justify-center w-4 h-4 rounded-[5px] border-divider-subtle bg-util-colors-cyan-cyan-500 flex-shrink-0'>
  66. <Iteration className='w-3 h-3 text-text-primary-on-surface' />
  67. </div>
  68. <span className='system-sm-semibold-uppercase text-text-primary flex-grow'>
  69. {t(`${i18nPrefix}.iteration`)} {index + 1}
  70. </span>
  71. {
  72. iteration.some(item => item.status === 'failed')
  73. ? (
  74. <RiErrorWarningLine className='w-4 h-4 text-text-destructive' />
  75. )
  76. : (< RiArrowRightSLine className={
  77. cn(
  78. 'w-4 h-4 text-text-tertiary transition-transform duration-200 flex-shrink-0',
  79. expandedIterations[index] && 'transform rotate-90',
  80. )} />
  81. )
  82. }
  83. </div>
  84. </div>
  85. {expandedIterations[index] && <div
  86. className="flex-grow h-px bg-divider-subtle"
  87. ></div>}
  88. <div className={cn(
  89. 'overflow-hidden transition-all duration-200',
  90. expandedIterations[index] ? 'max-h-[1000px] opacity-100' : 'max-h-0 opacity-0',
  91. )}>
  92. <TracingPanel
  93. list={iteration}
  94. className='bg-background-section-burn'
  95. />
  96. </div>
  97. </div>
  98. ))}
  99. </div>
  100. </>
  101. )
  102. const handleNotBubble = useCallback((e: React.MouseEvent) => {
  103. // if not do this, it will trigger the message log modal disappear(useClickAway)
  104. e.stopPropagation()
  105. e.nativeEvent.stopImmediatePropagation()
  106. }, [])
  107. if (noWrap)
  108. return main
  109. return (
  110. <div
  111. className='absolute inset-0 z-10 rounded-2xl pt-10'
  112. style={{
  113. backgroundColor: 'rgba(16, 24, 40, 0.20)',
  114. }}
  115. onClick={handleNotBubble}
  116. >
  117. <div className='h-full rounded-2xl bg-components-panel-bg flex flex-col'>
  118. {main}
  119. </div>
  120. </div >
  121. )
  122. }
  123. export default React.memo(IterationResultPanel)