Feat/attachments (#9526)
Co-authored-by: Joel <iamjoel007@gmail.com> Co-authored-by: JzoNg <jzongcode@gmail.com>
This commit is contained in:
@@ -13,11 +13,11 @@ const DetailPanel: FC<ILogDetail> = ({ runID, onClose }) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<div className='grow relative flex flex-col py-3'>
|
||||
<div className='grow relative flex flex-col pt-3'>
|
||||
<span className='absolute right-3 top-4 p-1 cursor-pointer z-20' onClick={onClose}>
|
||||
<RiCloseLine className='w-4 h-4 text-gray-500' />
|
||||
<RiCloseLine className='w-4 h-4 text-text-tertiary' />
|
||||
</span>
|
||||
<h1 className='shrink-0 px-4 py-1 text-md font-semibold text-gray-900'>{t('appLog.runDetail.workflowTitle')}</h1>
|
||||
<h1 className='shrink-0 px-4 py-1 text-text-primary system-xl-semibold'>{t('appLog.runDetail.workflowTitle')}</h1>
|
||||
<Run runID={runID}/>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -2,11 +2,9 @@
|
||||
import type { FC } from 'react'
|
||||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import {
|
||||
MagnifyingGlassIcon,
|
||||
} from '@heroicons/react/24/solid'
|
||||
import type { QueryParam } from './index'
|
||||
import { SimpleSelect } from '@/app/components/base/select'
|
||||
import Chip from '@/app/components/base/chip'
|
||||
import Input from '@/app/components/base/input'
|
||||
|
||||
type IFilterProps = {
|
||||
queryParams: QueryParam
|
||||
@@ -16,40 +14,30 @@ type IFilterProps = {
|
||||
const Filter: FC<IFilterProps> = ({ queryParams, setQueryParams }: IFilterProps) => {
|
||||
const { t } = useTranslation()
|
||||
return (
|
||||
<div className='flex flex-row flex-wrap gap-y-2 gap-x-4 items-center mb-4 text-gray-900 text-base'>
|
||||
<div className="relative rounded-md">
|
||||
<SimpleSelect
|
||||
defaultValue={'all'}
|
||||
className='!min-w-[100px]'
|
||||
onSelect={
|
||||
(item) => {
|
||||
if (!item.value)
|
||||
return
|
||||
setQueryParams({ ...queryParams, status: item.value as string })
|
||||
}
|
||||
}
|
||||
items={[{ value: 'all', name: 'All' },
|
||||
{ value: 'succeeded', name: 'Success' },
|
||||
{ value: 'failed', name: 'Fail' },
|
||||
{ value: 'stopped', name: 'Stop' },
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
<div className="relative">
|
||||
<div className="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">
|
||||
<MagnifyingGlassIcon className="h-5 w-5 text-gray-400" aria-hidden="true" />
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
name="query"
|
||||
className="block w-[240px] bg-gray-100 shadow-sm rounded-md border-0 py-1.5 pl-10 text-gray-900 placeholder:text-gray-400 focus:ring-1 focus:ring-inset focus:ring-gray-200 focus-visible:outline-none sm:text-sm sm:leading-6"
|
||||
placeholder={t('common.operation.search')!}
|
||||
value={queryParams.keyword}
|
||||
onChange={(e) => {
|
||||
setQueryParams({ ...queryParams, keyword: e.target.value })
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className='flex flex-row flex-wrap gap-2 mb-2'>
|
||||
<Chip
|
||||
value={queryParams.status || 'all'}
|
||||
onSelect={(item) => {
|
||||
setQueryParams({ ...queryParams, status: item.value as string })
|
||||
}}
|
||||
onClear={() => setQueryParams({ ...queryParams, status: 'all' })}
|
||||
items={[{ value: 'all', name: 'All' },
|
||||
{ value: 'succeeded', name: 'Success' },
|
||||
{ value: 'failed', name: 'Fail' },
|
||||
{ value: 'stopped', name: 'Stop' },
|
||||
]}
|
||||
/>
|
||||
<Input
|
||||
wrapperClassName='w-[200px]'
|
||||
showLeftIcon
|
||||
showClearIcon
|
||||
value={queryParams.keyword}
|
||||
placeholder={t('common.operation.search')!}
|
||||
onChange={(e) => {
|
||||
setQueryParams({ ...queryParams, keyword: e.target.value })
|
||||
}}
|
||||
onClear={() => setQueryParams({ ...queryParams, keyword: '' })}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -36,12 +36,12 @@ const EmptyElement: FC<{ appUrl: string }> = ({ appUrl }) => {
|
||||
const pathSegments = pathname.split('/')
|
||||
pathSegments.pop()
|
||||
return <div className='flex items-center justify-center h-full'>
|
||||
<div className='bg-gray-50 w-[560px] h-fit box-border px-5 py-4 rounded-2xl'>
|
||||
<span className='text-gray-700 font-semibold'>{t('appLog.table.empty.element.title')}<ThreeDotsIcon className='inline relative -top-3 -left-1.5' /></span>
|
||||
<div className='mt-2 text-gray-500 text-sm font-normal'>
|
||||
<div className='bg-background-section-burn w-[560px] h-fit box-border px-5 py-4 rounded-2xl'>
|
||||
<span className='text-text-secondary system-md-semibold'>{t('appLog.table.empty.element.title')}<ThreeDotsIcon className='inline relative -top-3 -left-1.5' /></span>
|
||||
<div className='mt-2 text-text-tertiary system-sm-regular'>
|
||||
<Trans
|
||||
i18nKey="appLog.table.empty.element.content"
|
||||
components={{ shareLink: <Link href={`${pathSegments.join('/')}/overview`} className='text-primary-600' />, testLink: <Link href={appUrl} className='text-primary-600' target='_blank' rel='noopener noreferrer' /> }}
|
||||
components={{ shareLink: <Link href={`${pathSegments.join('/')}/overview`} className='text-util-colors-blue-blue-600' />, testLink: <Link href={appUrl} className='text-util-colors-blue-blue-600' target='_blank' rel='noopener noreferrer' /> }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -75,8 +75,8 @@ const Logs: FC<ILogsProps> = ({ appDetail }) => {
|
||||
|
||||
return (
|
||||
<div className='flex flex-col h-full'>
|
||||
<h1 className='text-md font-semibold text-gray-900'>{t('appLog.workflowTitle')}</h1>
|
||||
<p className='flex text-sm font-normal text-gray-500'>{t('appLog.workflowSubtitle')}</p>
|
||||
<h1 className='text-text-primary system-xl-semibold'>{t('appLog.workflowTitle')}</h1>
|
||||
<p className='text-text-tertiary system-sm-regular'>{t('appLog.workflowSubtitle')}</p>
|
||||
<div className='flex flex-col py-4 flex-1'>
|
||||
<Filter queryParams={queryParams} setQueryParams={setQueryParams} />
|
||||
{/* workflow log */}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import type { FC } from 'react'
|
||||
import React, { useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import s from './style.module.css'
|
||||
// import s from './style.module.css'
|
||||
import DetailPanel from './detail'
|
||||
import cn from '@/utils/classnames'
|
||||
import type { WorkflowAppLogDetail, WorkflowLogsResponse } from '@/models/log'
|
||||
@@ -34,33 +34,33 @@ const WorkflowAppLogList: FC<ILogs> = ({ logs, appDetail, onRefresh }) => {
|
||||
const statusTdRender = (status: string) => {
|
||||
if (status === 'succeeded') {
|
||||
return (
|
||||
<div className='inline-flex items-center gap-1'>
|
||||
<div className='inline-flex items-center gap-1 system-xs-semibold-uppercase'>
|
||||
<Indicator color={'green'} />
|
||||
<span>Success</span>
|
||||
<span className='text-util-colors-green-green-600'>Success</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
if (status === 'failed') {
|
||||
return (
|
||||
<div className='inline-flex items-center gap-1'>
|
||||
<div className='inline-flex items-center gap-1 system-xs-semibold-uppercase'>
|
||||
<Indicator color={'red'} />
|
||||
<span className='text-red-600'>Fail</span>
|
||||
<span className='text-util-colors-red-red-600'>Fail</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
if (status === 'stopped') {
|
||||
return (
|
||||
<div className='inline-flex items-center gap-1'>
|
||||
<div className='inline-flex items-center gap-1 system-xs-semibold-uppercase'>
|
||||
<Indicator color={'yellow'} />
|
||||
<span>Stop</span>
|
||||
<span className='text-util-colors-warning-warning-600'>Stop</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
if (status === 'running') {
|
||||
return (
|
||||
<div className='inline-flex items-center gap-1'>
|
||||
<div className='inline-flex items-center gap-1 system-xs-semibold-uppercase'>
|
||||
<Indicator color={'blue'} />
|
||||
<span className='text-primary-600'>Running</span>
|
||||
<span className='text-util-colors-blue-light-blue-light-600'>Running</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -77,43 +77,47 @@ const WorkflowAppLogList: FC<ILogs> = ({ logs, appDetail, onRefresh }) => {
|
||||
|
||||
return (
|
||||
<div className='overflow-x-auto'>
|
||||
<table className={`w-full min-w-[440px] border-collapse border-0 text-sm mt-3 ${s.logTable}`}>
|
||||
<thead className="h-8 !pl-3 py-2 leading-[18px] border-b border-gray-200 text-xs text-gray-500 font-medium">
|
||||
<table className={cn('mt-2 w-full min-w-[440px] border-collapse border-0')}>
|
||||
<thead className='system-xs-medium-uppercase text-text-tertiary'>
|
||||
<tr>
|
||||
<td className='w-[1.375rem] whitespace-nowrap'></td>
|
||||
<td className='whitespace-nowrap'>{t('appLog.table.header.startTime')}</td>
|
||||
<td className='whitespace-nowrap'>{t('appLog.table.header.status')}</td>
|
||||
<td className='whitespace-nowrap'>{t('appLog.table.header.runtime')}</td>
|
||||
<td className='whitespace-nowrap'>{t('appLog.table.header.tokens')}</td>
|
||||
<td className='whitespace-nowrap'>{t('appLog.table.header.user')}</td>
|
||||
{/* <td className='whitespace-nowrap'>{t('appLog.table.header.version')}</td> */}
|
||||
<td className='pl-2 pr-1 w-5 rounded-l-lg bg-background-section-burn whitespace-nowrap'></td>
|
||||
<td className='pl-3 py-1.5 bg-background-section-burn whitespace-nowrap'>{t('appLog.table.header.startTime')}</td>
|
||||
<td className='pl-3 py-1.5 bg-background-section-burn whitespace-nowrap'>{t('appLog.table.header.status')}</td>
|
||||
<td className='pl-3 py-1.5 bg-background-section-burn whitespace-nowrap'>{t('appLog.table.header.runtime')}</td>
|
||||
<td className='pl-3 py-1.5 bg-background-section-burn whitespace-nowrap'>{t('appLog.table.header.tokens')}</td>
|
||||
<td className='pl-3 py-1.5 rounded-r-lg bg-background-section-burn whitespace-nowrap'>{t('appLog.table.header.user')}</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="text-gray-700 text-[13px]">
|
||||
<tbody className="text-text-secondary system-sm-regular">
|
||||
{logs.data.map((log: WorkflowAppLogDetail) => {
|
||||
const endUser = log.created_by_end_user ? log.created_by_end_user.session_id : log.created_by_account ? log.created_by_account.name : defaultValue
|
||||
return <tr
|
||||
key={log.id}
|
||||
className={`border-b border-gray-200 h-8 hover:bg-gray-50 cursor-pointer ${currentLog?.id !== log.id ? '' : 'bg-gray-50'}`}
|
||||
className={cn('border-b border-divider-subtle hover:bg-background-default-hover cursor-pointer', currentLog?.id !== log.id ? '' : 'bg-background-default-hover')}
|
||||
onClick={() => {
|
||||
setCurrentLog(log)
|
||||
setShowDrawer(true)
|
||||
}}>
|
||||
<td className='text-center align-middle'>{!log.read_at && <span className='inline-block bg-[#3F83F8] h-1.5 w-1.5 rounded'></span>}</td>
|
||||
<td className='w-[160px]'>{formatTime(log.created_at, t('appLog.dateTimeFormat') as string)}</td>
|
||||
<td>{statusTdRender(log.workflow_run.status)}</td>
|
||||
<td>
|
||||
<td className='h-4'>
|
||||
{!log.read_at && (
|
||||
<div className='p-3 pr-0.5 flex items-center'>
|
||||
<span className='inline-block bg-util-colors-blue-blue-500 h-1.5 w-1.5 rounded'></span>
|
||||
</div>
|
||||
)}
|
||||
</td>
|
||||
<td className='p-3 pr-2 w-[160px]'>{formatTime(log.created_at, t('appLog.dateTimeFormat') as string)}</td>
|
||||
<td className='p-3 pr-2'>{statusTdRender(log.workflow_run.status)}</td>
|
||||
<td className='p-3 pr-2'>
|
||||
<div className={cn(
|
||||
log.workflow_run.elapsed_time === 0 && 'text-gray-400',
|
||||
log.workflow_run.elapsed_time === 0 && 'text-text-quaternary',
|
||||
)}>{`${log.workflow_run.elapsed_time.toFixed(3)}s`}</div>
|
||||
</td>
|
||||
<td>{log.workflow_run.total_tokens}</td>
|
||||
<td>
|
||||
<div className={cn(endUser === defaultValue ? 'text-gray-400' : 'text-gray-700', 'text-sm overflow-hidden text-ellipsis whitespace-nowrap')}>
|
||||
<td className='p-3 pr-2'>{log.workflow_run.total_tokens}</td>
|
||||
<td className='p-3 pr-2'>
|
||||
<div className={cn(endUser === defaultValue ? 'text-text-quaternary' : 'text-text-secondary', 'overflow-hidden text-ellipsis whitespace-nowrap')}>
|
||||
{endUser}
|
||||
</div>
|
||||
</td>
|
||||
{/* <td>VERSION</td> */}
|
||||
</tr>
|
||||
})}
|
||||
</tbody>
|
||||
@@ -123,7 +127,7 @@ const WorkflowAppLogList: FC<ILogs> = ({ logs, appDetail, onRefresh }) => {
|
||||
onClose={onCloseDrawer}
|
||||
mask={isMobile}
|
||||
footer={null}
|
||||
panelClassname='mt-16 mx-2 sm:mr-2 mb-3 !p-0 !max-w-[600px] rounded-xl border border-gray-200'
|
||||
panelClassname='mt-16 mx-2 sm:mr-2 mb-3 !p-0 !max-w-[600px] rounded-xl border border-components-panel-border'
|
||||
>
|
||||
<DetailPanel onClose={onCloseDrawer} runID={currentLog?.workflow_run.id || ''} />
|
||||
</Drawer>
|
||||
|
||||
@@ -1,9 +1,3 @@
|
||||
.logTable td {
|
||||
padding: 7px 8px;
|
||||
box-sizing: border-box;
|
||||
max-width: 200px;
|
||||
}
|
||||
|
||||
.pagination li {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user