You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

index.tsx 5.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. 'use client'
  2. import type { FC } from 'react'
  3. import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
  4. import { useContext } from 'use-context-selector'
  5. import { useTranslation } from 'react-i18next'
  6. import OutputPanel from './output-panel'
  7. import ResultPanel from './result-panel'
  8. import TracingPanel from './tracing-panel'
  9. import cn from '@/utils/classnames'
  10. import { ToastContext } from '@/app/components/base/toast'
  11. import Loading from '@/app/components/base/loading'
  12. import { fetchRunDetail, fetchTracingList } from '@/service/log'
  13. import type { NodeTracing } from '@/types/workflow'
  14. import type { WorkflowRunDetailResponse } from '@/models/log'
  15. export type RunProps = {
  16. hideResult?: boolean
  17. activeTab?: 'RESULT' | 'DETAIL' | 'TRACING'
  18. getResultCallback?: (result: WorkflowRunDetailResponse) => void
  19. runDetailUrl: string
  20. tracingListUrl: string
  21. }
  22. const RunPanel: FC<RunProps> = ({
  23. hideResult,
  24. activeTab = 'RESULT',
  25. getResultCallback,
  26. runDetailUrl,
  27. tracingListUrl,
  28. }) => {
  29. const { t } = useTranslation()
  30. const { notify } = useContext(ToastContext)
  31. const [currentTab, setCurrentTab] = useState<string>(activeTab)
  32. const [loading, setLoading] = useState<boolean>(true)
  33. const [runDetail, setRunDetail] = useState<WorkflowRunDetailResponse>()
  34. const [list, setList] = useState<NodeTracing[]>([])
  35. const executor = useMemo(() => {
  36. if (runDetail?.created_by_role === 'account')
  37. return runDetail.created_by_account?.name || ''
  38. if (runDetail?.created_by_role === 'end_user')
  39. return runDetail.created_by_end_user?.session_id || ''
  40. return 'N/A'
  41. }, [runDetail])
  42. const getResult = useCallback(async () => {
  43. try {
  44. const res = await fetchRunDetail(runDetailUrl)
  45. setRunDetail(res)
  46. if (getResultCallback)
  47. getResultCallback(res)
  48. }
  49. catch (err) {
  50. notify({
  51. type: 'error',
  52. message: `${err}`,
  53. })
  54. }
  55. }, [notify, getResultCallback, runDetailUrl])
  56. const getTracingList = useCallback(async () => {
  57. try {
  58. const { data: nodeList } = await fetchTracingList({
  59. url: tracingListUrl,
  60. })
  61. setList(nodeList)
  62. }
  63. catch (err) {
  64. notify({
  65. type: 'error',
  66. message: `${err}`,
  67. })
  68. }
  69. }, [notify, tracingListUrl])
  70. const getData = useCallback(async () => {
  71. setLoading(true)
  72. await getResult()
  73. await getTracingList()
  74. setLoading(false)
  75. }, [getResult, getTracingList])
  76. const switchTab = async (tab: string) => {
  77. setCurrentTab(tab)
  78. if (tab === 'RESULT')
  79. runDetailUrl && await getResult()
  80. tracingListUrl && await getTracingList()
  81. }
  82. useEffect(() => {
  83. // fetch data
  84. if (runDetailUrl && tracingListUrl)
  85. getData()
  86. }, [runDetailUrl, tracingListUrl])
  87. const [height, setHeight] = useState(0)
  88. const ref = useRef<HTMLDivElement>(null)
  89. const adjustResultHeight = () => {
  90. if (ref.current)
  91. setHeight(ref.current?.clientHeight - 16 - 16 - 2 - 1)
  92. }
  93. useEffect(() => {
  94. adjustResultHeight()
  95. }, [loading])
  96. return (
  97. <div className='relative flex grow flex-col'>
  98. {/* tab */}
  99. <div className='flex shrink-0 items-center border-b-[0.5px] border-divider-subtle px-4'>
  100. {!hideResult && (
  101. <div
  102. className={cn(
  103. 'system-sm-semibold-uppercase mr-6 cursor-pointer border-b-2 border-transparent py-3 text-text-tertiary',
  104. currentTab === 'RESULT' && '!border-util-colors-blue-brand-blue-brand-600 text-text-primary',
  105. )}
  106. onClick={() => switchTab('RESULT')}
  107. >{t('runLog.result')}</div>
  108. )}
  109. <div
  110. className={cn(
  111. 'system-sm-semibold-uppercase mr-6 cursor-pointer border-b-2 border-transparent py-3 text-text-tertiary',
  112. currentTab === 'DETAIL' && '!border-util-colors-blue-brand-blue-brand-600 text-text-primary',
  113. )}
  114. onClick={() => switchTab('DETAIL')}
  115. >{t('runLog.detail')}</div>
  116. <div
  117. className={cn(
  118. 'system-sm-semibold-uppercase mr-6 cursor-pointer border-b-2 border-transparent py-3 text-text-tertiary',
  119. currentTab === 'TRACING' && '!border-util-colors-blue-brand-blue-brand-600 text-text-primary',
  120. )}
  121. onClick={() => switchTab('TRACING')}
  122. >{t('runLog.tracing')}</div>
  123. </div>
  124. {/* panel detail */}
  125. <div ref={ref} className={cn('relative h-0 grow overflow-y-auto rounded-b-xl bg-components-panel-bg')}>
  126. {loading && (
  127. <div className='flex h-full items-center justify-center bg-components-panel-bg'>
  128. <Loading />
  129. </div>
  130. )}
  131. {!loading && currentTab === 'RESULT' && runDetail && (
  132. <OutputPanel
  133. outputs={runDetail.outputs}
  134. error={runDetail.error}
  135. height={height}
  136. />
  137. )}
  138. {!loading && currentTab === 'DETAIL' && runDetail && (
  139. <ResultPanel
  140. inputs={runDetail.inputs}
  141. outputs={runDetail.outputs}
  142. status={runDetail.status}
  143. error={runDetail.error}
  144. elapsed_time={runDetail.elapsed_time}
  145. total_tokens={runDetail.total_tokens}
  146. created_at={runDetail.created_at}
  147. created_by={executor}
  148. steps={runDetail.total_steps}
  149. exceptionCounts={runDetail.exceptions_count}
  150. />
  151. )}
  152. {!loading && currentTab === 'TRACING' && (
  153. <TracingPanel
  154. className='bg-background-section-burn'
  155. list={list}
  156. />
  157. )}
  158. </div>
  159. </div>
  160. )
  161. }
  162. export default RunPanel