list.tsx 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. 'use client'
  2. import type { FC } from 'react'
  3. import React, { useState } from 'react'
  4. import { useTranslation } from 'react-i18next'
  5. import DetailPanel from './detail'
  6. import type { WorkflowAppLogDetail, WorkflowLogsResponse } from '@/models/log'
  7. import type { App } from '@/types/app'
  8. import Loading from '@/app/components/base/loading'
  9. import Drawer from '@/app/components/base/drawer'
  10. import Indicator from '@/app/components/header/indicator'
  11. import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
  12. import useTimestamp from '@/hooks/use-timestamp'
  13. import cn from '@/utils/classnames'
  14. type ILogs = {
  15. logs?: WorkflowLogsResponse
  16. appDetail?: App
  17. onRefresh: () => void
  18. }
  19. const defaultValue = 'N/A'
  20. const WorkflowAppLogList: FC<ILogs> = ({ logs, appDetail, onRefresh }) => {
  21. const { t } = useTranslation()
  22. const { formatTime } = useTimestamp()
  23. const media = useBreakpoints()
  24. const isMobile = media === MediaType.mobile
  25. const [showDrawer, setShowDrawer] = useState<boolean>(false)
  26. const [currentLog, setCurrentLog] = useState<WorkflowAppLogDetail | undefined>()
  27. const statusTdRender = (status: string) => {
  28. if (status === 'succeeded') {
  29. return (
  30. <div className='inline-flex items-center gap-1 system-xs-semibold-uppercase'>
  31. <Indicator color={'green'} />
  32. <span className='text-util-colors-green-green-600'>Success</span>
  33. </div>
  34. )
  35. }
  36. if (status === 'failed') {
  37. return (
  38. <div className='inline-flex items-center gap-1 system-xs-semibold-uppercase'>
  39. <Indicator color={'red'} />
  40. <span className='text-util-colors-red-red-600'>Fail</span>
  41. </div>
  42. )
  43. }
  44. if (status === 'stopped') {
  45. return (
  46. <div className='inline-flex items-center gap-1 system-xs-semibold-uppercase'>
  47. <Indicator color={'yellow'} />
  48. <span className='text-util-colors-warning-warning-600'>Stop</span>
  49. </div>
  50. )
  51. }
  52. if (status === 'running') {
  53. return (
  54. <div className='inline-flex items-center gap-1 system-xs-semibold-uppercase'>
  55. <Indicator color={'blue'} />
  56. <span className='text-util-colors-blue-light-blue-light-600'>Running</span>
  57. </div>
  58. )
  59. }
  60. }
  61. const onCloseDrawer = () => {
  62. onRefresh()
  63. setShowDrawer(false)
  64. setCurrentLog(undefined)
  65. }
  66. if (!logs || !appDetail)
  67. return <Loading />
  68. return (
  69. <div className='overflow-x-auto'>
  70. <table className={cn('mt-2 w-full min-w-[440px] border-collapse border-0')}>
  71. <thead className='system-xs-medium-uppercase text-text-tertiary'>
  72. <tr>
  73. <td className='pl-2 pr-1 w-5 rounded-l-lg bg-background-section-burn whitespace-nowrap'></td>
  74. <td className='pl-3 py-1.5 bg-background-section-burn whitespace-nowrap'>{t('appLog.table.header.startTime')}</td>
  75. <td className='pl-3 py-1.5 bg-background-section-burn whitespace-nowrap'>{t('appLog.table.header.status')}</td>
  76. <td className='pl-3 py-1.5 bg-background-section-burn whitespace-nowrap'>{t('appLog.table.header.runtime')}</td>
  77. <td className='pl-3 py-1.5 bg-background-section-burn whitespace-nowrap'>{t('appLog.table.header.tokens')}</td>
  78. <td className='pl-3 py-1.5 rounded-r-lg bg-background-section-burn whitespace-nowrap'>{t('appLog.table.header.user')}</td>
  79. </tr>
  80. </thead>
  81. <tbody className="text-text-secondary system-sm-regular">
  82. {logs.data.map((log: WorkflowAppLogDetail) => {
  83. const endUser = log.created_by_end_user ? log.created_by_end_user.session_id : log.created_by_account ? log.created_by_account.name : defaultValue
  84. return <tr
  85. key={log.id}
  86. className={cn('border-b border-divider-subtle hover:bg-background-default-hover cursor-pointer', currentLog?.id !== log.id ? '' : 'bg-background-default-hover')}
  87. onClick={() => {
  88. setCurrentLog(log)
  89. setShowDrawer(true)
  90. }}>
  91. <td className='h-4'>
  92. {!log.read_at && (
  93. <div className='p-3 pr-0.5 flex items-center'>
  94. <span className='inline-block bg-util-colors-blue-blue-500 h-1.5 w-1.5 rounded'></span>
  95. </div>
  96. )}
  97. </td>
  98. <td className='p-3 pr-2 w-[160px]'>{formatTime(log.created_at, t('appLog.dateTimeFormat') as string)}</td>
  99. <td className='p-3 pr-2'>{statusTdRender(log.workflow_run.status)}</td>
  100. <td className='p-3 pr-2'>
  101. <div className={cn(
  102. log.workflow_run.elapsed_time === 0 && 'text-text-quaternary',
  103. )}>{`${log.workflow_run.elapsed_time.toFixed(3)}s`}</div>
  104. </td>
  105. <td className='p-3 pr-2'>{log.workflow_run.total_tokens}</td>
  106. <td className='p-3 pr-2'>
  107. <div className={cn(endUser === defaultValue ? 'text-text-quaternary' : 'text-text-secondary', 'overflow-hidden text-ellipsis whitespace-nowrap')}>
  108. {endUser}
  109. </div>
  110. </td>
  111. </tr>
  112. })}
  113. </tbody>
  114. </table>
  115. <Drawer
  116. isOpen={showDrawer}
  117. onClose={onCloseDrawer}
  118. mask={isMobile}
  119. footer={null}
  120. panelClassname='mt-16 mx-2 sm:mr-2 mb-3 !p-0 !max-w-[600px] rounded-xl border border-components-panel-border'
  121. >
  122. <DetailPanel onClose={onCloseDrawer} runID={currentLog?.workflow_run.id || ''} />
  123. </Drawer>
  124. </div>
  125. )
  126. }
  127. export default WorkflowAppLogList