mirror of
				https://github.com/langgenius/dify.git
				synced 2025-11-03 20:33:00 +00:00 
			
		
		
		
	Co-authored-by: NFish <douxc512@gmail.com> Co-authored-by: zxhlyh <jasonapring2015@outlook.com> Co-authored-by: twwu <twwu@dify.ai> Co-authored-by: jZonG <jzongcode@gmail.com>
		
			
				
	
	
		
			173 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			173 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
'use client'
 | 
						|
import type { FC } from 'react'
 | 
						|
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
 | 
						|
import { useContext } from 'use-context-selector'
 | 
						|
import { useTranslation } from 'react-i18next'
 | 
						|
import OutputPanel from './output-panel'
 | 
						|
import ResultPanel from './result-panel'
 | 
						|
import TracingPanel from './tracing-panel'
 | 
						|
import cn from '@/utils/classnames'
 | 
						|
import { ToastContext } from '@/app/components/base/toast'
 | 
						|
import Loading from '@/app/components/base/loading'
 | 
						|
import { fetchRunDetail, fetchTracingList } from '@/service/log'
 | 
						|
import type { NodeTracing } from '@/types/workflow'
 | 
						|
import type { WorkflowRunDetailResponse } from '@/models/log'
 | 
						|
import { useStore as useAppStore } from '@/app/components/app/store'
 | 
						|
export type RunProps = {
 | 
						|
  hideResult?: boolean
 | 
						|
  activeTab?: 'RESULT' | 'DETAIL' | 'TRACING'
 | 
						|
  runID: string
 | 
						|
  getResultCallback?: (result: WorkflowRunDetailResponse) => void
 | 
						|
}
 | 
						|
 | 
						|
const RunPanel: FC<RunProps> = ({ hideResult, activeTab = 'RESULT', runID, getResultCallback }) => {
 | 
						|
  const { t } = useTranslation()
 | 
						|
  const { notify } = useContext(ToastContext)
 | 
						|
  const [currentTab, setCurrentTab] = useState<string>(activeTab)
 | 
						|
  const appDetail = useAppStore(state => state.appDetail)
 | 
						|
  const [loading, setLoading] = useState<boolean>(true)
 | 
						|
  const [runDetail, setRunDetail] = useState<WorkflowRunDetailResponse>()
 | 
						|
  const [list, setList] = useState<NodeTracing[]>([])
 | 
						|
 | 
						|
  const executor = useMemo(() => {
 | 
						|
    if (runDetail?.created_by_role === 'account')
 | 
						|
      return runDetail.created_by_account?.name || ''
 | 
						|
    if (runDetail?.created_by_role === 'end_user')
 | 
						|
      return runDetail.created_by_end_user?.session_id || ''
 | 
						|
    return 'N/A'
 | 
						|
  }, [runDetail])
 | 
						|
 | 
						|
  const getResult = useCallback(async (appID: string, runID: string) => {
 | 
						|
    try {
 | 
						|
      const res = await fetchRunDetail({
 | 
						|
        appID,
 | 
						|
        runID,
 | 
						|
      })
 | 
						|
      setRunDetail(res)
 | 
						|
      if (getResultCallback)
 | 
						|
        getResultCallback(res)
 | 
						|
    }
 | 
						|
    catch (err) {
 | 
						|
      notify({
 | 
						|
        type: 'error',
 | 
						|
        message: `${err}`,
 | 
						|
      })
 | 
						|
    }
 | 
						|
  }, [notify, getResultCallback])
 | 
						|
 | 
						|
  const getTracingList = useCallback(async (appID: string, runID: string) => {
 | 
						|
    try {
 | 
						|
      const { data: nodeList } = await fetchTracingList({
 | 
						|
        url: `/apps/${appID}/workflow-runs/${runID}/node-executions`,
 | 
						|
      })
 | 
						|
      setList(nodeList)
 | 
						|
    }
 | 
						|
    catch (err) {
 | 
						|
      notify({
 | 
						|
        type: 'error',
 | 
						|
        message: `${err}`,
 | 
						|
      })
 | 
						|
    }
 | 
						|
  }, [notify])
 | 
						|
 | 
						|
  const getData = async (appID: string, runID: string) => {
 | 
						|
    setLoading(true)
 | 
						|
    await getResult(appID, runID)
 | 
						|
    await getTracingList(appID, runID)
 | 
						|
    setLoading(false)
 | 
						|
  }
 | 
						|
 | 
						|
  const switchTab = async (tab: string) => {
 | 
						|
    setCurrentTab(tab)
 | 
						|
    if (tab === 'RESULT')
 | 
						|
      appDetail?.id && await getResult(appDetail.id, runID)
 | 
						|
    appDetail?.id && await getTracingList(appDetail.id, runID)
 | 
						|
  }
 | 
						|
 | 
						|
  useEffect(() => {
 | 
						|
    // fetch data
 | 
						|
    if (appDetail && runID)
 | 
						|
      getData(appDetail.id, runID)
 | 
						|
  }, [appDetail, runID])
 | 
						|
 | 
						|
  const [height, setHeight] = useState(0)
 | 
						|
  const ref = useRef<HTMLDivElement>(null)
 | 
						|
 | 
						|
  const adjustResultHeight = () => {
 | 
						|
    if (ref.current)
 | 
						|
      setHeight(ref.current?.clientHeight - 16 - 16 - 2 - 1)
 | 
						|
  }
 | 
						|
 | 
						|
  useEffect(() => {
 | 
						|
    adjustResultHeight()
 | 
						|
  }, [loading])
 | 
						|
 | 
						|
  return (
 | 
						|
    <div className='relative flex grow flex-col'>
 | 
						|
      {/* tab */}
 | 
						|
      <div className='flex shrink-0 items-center border-b-[0.5px] border-divider-subtle px-4'>
 | 
						|
        {!hideResult && (
 | 
						|
          <div
 | 
						|
            className={cn(
 | 
						|
              'system-sm-semibold-uppercase mr-6 cursor-pointer border-b-2 border-transparent py-3 text-text-tertiary',
 | 
						|
              currentTab === 'RESULT' && '!border-util-colors-blue-brand-blue-brand-600 text-text-primary',
 | 
						|
            )}
 | 
						|
            onClick={() => switchTab('RESULT')}
 | 
						|
          >{t('runLog.result')}</div>
 | 
						|
        )}
 | 
						|
        <div
 | 
						|
          className={cn(
 | 
						|
            'system-sm-semibold-uppercase mr-6 cursor-pointer border-b-2 border-transparent py-3 text-text-tertiary',
 | 
						|
            currentTab === 'DETAIL' && '!border-util-colors-blue-brand-blue-brand-600 text-text-primary',
 | 
						|
          )}
 | 
						|
          onClick={() => switchTab('DETAIL')}
 | 
						|
        >{t('runLog.detail')}</div>
 | 
						|
        <div
 | 
						|
          className={cn(
 | 
						|
            'system-sm-semibold-uppercase mr-6 cursor-pointer border-b-2 border-transparent py-3 text-text-tertiary',
 | 
						|
            currentTab === 'TRACING' && '!border-util-colors-blue-brand-blue-brand-600 text-text-primary',
 | 
						|
          )}
 | 
						|
          onClick={() => switchTab('TRACING')}
 | 
						|
        >{t('runLog.tracing')}</div>
 | 
						|
      </div>
 | 
						|
      {/* panel detail */}
 | 
						|
      <div ref={ref} className={cn('relative h-0 grow overflow-y-auto rounded-b-2xl bg-components-panel-bg')}>
 | 
						|
        {loading && (
 | 
						|
          <div className='flex h-full items-center justify-center bg-components-panel-bg'>
 | 
						|
            <Loading />
 | 
						|
          </div>
 | 
						|
        )}
 | 
						|
        {!loading && currentTab === 'RESULT' && runDetail && (
 | 
						|
          <OutputPanel
 | 
						|
            outputs={runDetail.outputs}
 | 
						|
            error={runDetail.error}
 | 
						|
            height={height}
 | 
						|
          />
 | 
						|
        )}
 | 
						|
        {!loading && currentTab === 'DETAIL' && runDetail && (
 | 
						|
          <ResultPanel
 | 
						|
            inputs={runDetail.inputs}
 | 
						|
            outputs={runDetail.outputs}
 | 
						|
            status={runDetail.status}
 | 
						|
            error={runDetail.error}
 | 
						|
            elapsed_time={runDetail.elapsed_time}
 | 
						|
            total_tokens={runDetail.total_tokens}
 | 
						|
            created_at={runDetail.created_at}
 | 
						|
            created_by={executor}
 | 
						|
            steps={runDetail.total_steps}
 | 
						|
            exceptionCounts={runDetail.exceptions_count}
 | 
						|
          />
 | 
						|
        )}
 | 
						|
        {!loading && currentTab === 'TRACING' && (
 | 
						|
          <TracingPanel
 | 
						|
            className='bg-background-section-burn'
 | 
						|
            list={list}
 | 
						|
          />
 | 
						|
        )}
 | 
						|
      </div>
 | 
						|
    </div>
 | 
						|
  )
 | 
						|
}
 | 
						|
 | 
						|
export default RunPanel
 |