feat(workflow-log): enhance workflow logs UI with sorting and status filters (#24978)

This commit is contained in:
lyzno1
2025-09-02 16:43:11 +08:00
committed by GitHub
parent 883a6caf96
commit 1fce1a61d4
4 changed files with 52 additions and 18 deletions

View File

@@ -27,7 +27,9 @@ class WorkflowAppLogApi(Resource):
"""
parser = reqparse.RequestParser()
parser.add_argument("keyword", type=str, location="args")
parser.add_argument("status", type=str, choices=["succeeded", "failed", "stopped"], location="args")
parser.add_argument(
"status", type=str, choices=["succeeded", "failed", "stopped", "partial-succeeded"], location="args"
)
parser.add_argument(
"created_at__before", type=str, location="args", help="Filter logs created before this timestamp"
)

View File

@@ -33,18 +33,6 @@ const Filter: FC<IFilterProps> = ({ queryParams, setQueryParams }: IFilterProps)
const { t } = useTranslation()
return (
<div className='mb-2 flex flex-row flex-wrap gap-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' },
]}
/>
<Chip
className='min-w-[150px]'
panelClassName='w-[270px]'
@@ -56,6 +44,19 @@ const Filter: FC<IFilterProps> = ({ queryParams, setQueryParams }: IFilterProps)
onClear={() => setQueryParams({ ...queryParams, period: '9' })}
items={Object.entries(TIME_PERIOD_MAPPING).map(([k, v]) => ({ value: k, name: t(`appLog.filter.period.${v.name}`) }))}
/>
<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: 'partial-succeeded', name: 'Partial Success' },
{ value: 'failed', name: 'Failure' },
{ value: 'stopped', name: 'Stop' },
]}
/>
<Input
wrapperClassName='w-[200px]'
showLeftIcon

View File

@@ -1,7 +1,8 @@
'use client'
import type { FC } from 'react'
import React, { useState } from 'react'
import React, { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { ArrowDownIcon } from '@heroicons/react/24/outline'
import DetailPanel from './detail'
import type { WorkflowAppLogDetail, WorkflowLogsResponse } from '@/models/log'
import type { App } from '@/types/app'
@@ -29,6 +30,26 @@ const WorkflowAppLogList: FC<ILogs> = ({ logs, appDetail, onRefresh }) => {
const [showDrawer, setShowDrawer] = useState<boolean>(false)
const [currentLog, setCurrentLog] = useState<WorkflowAppLogDetail | undefined>()
const [sortOrder, setSortOrder] = useState<'asc' | 'desc'>('desc')
const [localLogs, setLocalLogs] = useState<WorkflowAppLogDetail[]>(logs?.data || [])
useEffect(() => {
if (!logs?.data) {
setLocalLogs([])
return
}
const sortedLogs = [...logs.data].sort((a, b) => {
const result = a.created_at - b.created_at
return sortOrder === 'asc' ? result : -result
})
setLocalLogs(sortedLogs)
}, [logs?.data, sortOrder])
const handleSort = () => {
setSortOrder(sortOrder === 'asc' ? 'desc' : 'asc')
}
const isWorkflow = appDetail?.mode === 'workflow'
@@ -45,7 +66,7 @@ const WorkflowAppLogList: FC<ILogs> = ({ logs, appDetail, onRefresh }) => {
return (
<div className='system-xs-semibold-uppercase inline-flex items-center gap-1'>
<Indicator color={'red'} />
<span className='text-util-colors-red-red-600'>Fail</span>
<span className='text-util-colors-red-red-600'>Failure</span>
</div>
)
}
@@ -90,7 +111,17 @@ const WorkflowAppLogList: FC<ILogs> = ({ logs, appDetail, onRefresh }) => {
<thead className='system-xs-medium-uppercase text-text-tertiary'>
<tr>
<td className='w-5 whitespace-nowrap rounded-l-lg bg-background-section-burn pl-2 pr-1'></td>
<td className='whitespace-nowrap bg-background-section-burn py-1.5 pl-3'>{t('appLog.table.header.startTime')}</td>
<td className='whitespace-nowrap bg-background-section-burn py-1.5 pl-3'>
<div className='flex cursor-pointer items-center hover:text-text-secondary' onClick={handleSort}>
{t('appLog.table.header.startTime')}
<ArrowDownIcon
className={cn('ml-0.5 h-3 w-3 stroke-current stroke-2 transition-all',
'text-text-tertiary',
sortOrder === 'asc' ? 'rotate-180' : '',
)}
/>
</div>
</td>
<td className='whitespace-nowrap bg-background-section-burn py-1.5 pl-3'>{t('appLog.table.header.status')}</td>
<td className='whitespace-nowrap bg-background-section-burn py-1.5 pl-3'>{t('appLog.table.header.runtime')}</td>
<td className='whitespace-nowrap bg-background-section-burn py-1.5 pl-3'>{t('appLog.table.header.tokens')}</td>
@@ -99,7 +130,7 @@ const WorkflowAppLogList: FC<ILogs> = ({ logs, appDetail, onRefresh }) => {
</tr>
</thead>
<tbody className="system-sm-regular text-text-secondary">
{logs.data.map((log: WorkflowAppLogDetail) => {
{localLogs.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}

View File

@@ -18,7 +18,7 @@ const translation = {
status: 'STATUS',
runtime: 'RUN TIME',
tokens: 'TOKENS',
user: 'End User or Account',
user: 'END USER OR ACCOUNT',
version: 'VERSION',
triggered_from: 'TRIGGER BY',
},