Skip to content

Conversation

@LuckyYC
Copy link
Collaborator

@LuckyYC LuckyYC commented Dec 23, 2025

No description provided.

@gemini-code-assist
Copy link

Summary of Changes

Hello @LuckyYC, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request delivers a substantial refresh to the application's frontend, focusing on a sleek, modern, and intuitive user experience. It introduces a new global design language, streamlines navigation by moving from a sidebar to a top-header menu, and refines the presentation of data across various dashboards and log viewers. The changes aim to improve visual consistency and user interaction throughout the platform.

Highlights

  • Frontend UI/UX Overhaul: The user interface has undergone a significant redesign, adopting a modern, minimalist, and tech-inspired aesthetic across the application. This includes new global styling for components like buttons, tables, tabs, and inputs.
  • Header Navigation & Sidebar Removal: The application now features a new top-level navigation menu integrated into the header, replacing the previous sidebar component. The header also includes a visually enhanced logo with gradient and glow effects.
  • Internationalization (i18n) Updates: Key labels and descriptions have been updated for clarity and consistency in both English and Chinese locales. For instance, 'LLM Load Test' is now 'LLM API' and 'Common API Load Test' is now 'Business API'.
  • Enhanced Log Viewers: The system logs and task logs pages have received styling improvements for better readability, including refined log line formatting and adjusted container heights for both normal and fullscreen modes.
  • Improved Report Generation: The report download functionality for common and LLM test results has been enhanced to capture multiple sections of the report more effectively, including task information and overview metrics, with added horizontal padding for better presentation.
  • Component Refactoring for Consistency: Many pages, including test results, job listings, system monitor, and configuration, have been refactored to remove Ant Design 'Card' components and instead utilize new custom layout classes like 'unified-section' and 'info-grid' for a consistent look and feel.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a significant and impressive UI overhaul, creating a more modern, consistent, and professional look and feel across the application. The shift from a sidebar to a top-bar navigation is a major structural improvement. Pages have been refactored to use a new, cleaner, section-based layout, and a comprehensive design system has been introduced via new CSS, which greatly enhances maintainability. The improved report download functionality is also a great addition. My review includes a few suggestions to further improve maintainability by reducing code duplication and centralizing styles.

Comment on lines +582 to 954
if (fullscreen) {
return (
<div
style={{
height: fullscreen ? '100vh' : 'auto',
boxShadow: fullscreen ? 'none' : undefined,
}}
title={
<div className='flex justify-between align-center w-full'>
<PageHeader
title={`Task Logs - ${id}`}
level={4}
extra={
isStatusRefreshing && (
<Tooltip title='refreshing...'>
<span className='ml-8'>
<LoadingSpinner size='small' showText={false} />
</span>
</Tooltip>
)
}
/>
<Space>
<Select
value={tailLines}
onChange={value => setTailLines(value)}
className='w-140'
>
<Select.Option value={100}>
{t('pages.taskLog.last100Lines')}
</Select.Option>
<Select.Option value={500}>
{t('pages.taskLog.last500Lines')}
</Select.Option>
<Select.Option value={1000}>
{t('pages.taskLog.last1000Lines')}
</Select.Option>
<Select.Option value={0}>
{t('pages.taskLog.allLogs')}
</Select.Option>
</Select>
<Switch
checkedChildren={t('pages.taskLog.autoRefresh')}
unCheckedChildren={t('pages.taskLog.stopRefresh')}
checked={autoRefresh}
onChange={checked => {
setAutoRefresh(checked);
if (checked) {
setFetchError(null);
}
}}
disabled={isTaskInFinalState(task?.status)}
/>
<Button icon={<SyncOutlined />} onClick={handleManualRefresh}>
{t('pages.taskLog.refreshLogs')}
</Button>
<Search
placeholder={t('pages.taskLog.searchLogContent')}
allowClear
enterButton={<SearchOutlined />}
onSearch={handleSearch}
className='w-250'
/>
<Button
type='primary'
icon={<DownloadOutlined />}
onClick={handleDownload}
>
{t('pages.taskLog.downloadLogs')}
</Button>
<Button
icon={
fullscreen ? (
<FullscreenExitOutlined />
) : (
<FullscreenOutlined />
)
}
onClick={toggleFullscreen}
>
{fullscreen
? t('pages.taskLog.exitFullscreen')
: t('pages.taskLog.fullscreen')}
</Button>
</Space>
</div>
}
styles={{
body: {
padding: fullscreen ? '8px' : '24px',
height: fullscreen ? 'calc(100vh - 70px)' : 'auto',
},
padding: '0',
height: '100vh',
width: '100vw',
position: 'fixed',
top: 0,
left: 0,
zIndex: 1000,
backgroundColor: token.colorBgContainer,
}}
>
{/* Toolbar */}
<div
ref={logContainerRef}
className='custom-scrollbar'
style={{
backgroundColor: token.colorBgElevated,
padding: '16px',
borderRadius: '4px',
height: getLogContainerHeight(),
overflowY: 'auto',
fontFamily:
'"SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace',
fontSize: '14px',
lineHeight: '1.6',
border: `1px solid ${token.colorBorderSecondary}`,
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
padding: '16px 0',
marginBottom: '16px',
borderBottom: '1px solid rgba(0, 0, 0, 0.06)',
}}
>
{searchTerm && (
<Alert
message={t('pages.taskLog.searchResults', { searchTerm })}
type='info'
showIcon
closable
onClose={() => {
setSearchTerm('');
setFilteredLogs(logs);
<div style={{ display: 'flex', alignItems: 'center', gap: '12px' }}>
<Text type='secondary' style={{ fontSize: '14px' }}>
{t('pages.taskLog.taskId', '任务ID')}:{' '}
</Text>
<Text style={{ fontSize: '14px' }}>{id}</Text>
</div>
<Space wrap size='middle'>
<Select
value={tailLines}
onChange={value => setTailLines(value)}
className='w-140'
style={{ minWidth: '140px' }}
>
<Select.Option value={100}>
{t('pages.taskLog.last100Lines')}
</Select.Option>
<Select.Option value={500}>
{t('pages.taskLog.last500Lines')}
</Select.Option>
<Select.Option value={1000}>
{t('pages.taskLog.last1000Lines')}
</Select.Option>
<Select.Option value={0}>
{t('pages.taskLog.allLogs')}
</Select.Option>
</Select>
<Switch
checkedChildren={t('pages.taskLog.autoRefresh')}
unCheckedChildren={t('pages.taskLog.stopRefresh')}
checked={autoRefresh}
onChange={checked => {
setAutoRefresh(checked);
if (checked) {
setFetchError(null);
}
}}
className='mb-16'
disabled={isTaskInFinalState(task?.status)}
/>
)}

{fetchError && (
<Alert
message={t('pages.taskLog.autoRefreshError')}
description={
<div>
<p>{fetchError}</p>
<p>{t('pages.taskLog.autoRefreshPaused')}</p>
</div>
<Button icon={<SyncOutlined />} onClick={handleManualRefresh}>
{t('pages.taskLog.refreshLogs')}
</Button>
<Search
placeholder={t('pages.taskLog.searchLogContent')}
allowClear
enterButton={<SearchOutlined />}
onSearch={handleSearch}
className='w-250'
style={{ minWidth: '250px' }}
/>
<Button
type='primary'
icon={<DownloadOutlined />}
onClick={handleDownload}
>
{t('pages.taskLog.downloadLogs')}
</Button>
<Button
icon={
fullscreen ? <FullscreenExitOutlined /> : <FullscreenOutlined />
}
type='warning'
showIcon
icon={<WarningOutlined />}
closable
action={
<Button
size='small'
type='primary'
onClick={handleManualRefresh}
>
{t('pages.taskLog.refreshNow')}
</Button>
onClick={toggleFullscreen}
>
{fullscreen
? t('pages.taskLog.exitFullscreen')
: t('pages.taskLog.fullscreen')}
</Button>
</Space>
</div>

{/* Log Container */}
<div style={{ position: 'relative' }}>
<div
ref={logContainerRef}
className='custom-scrollbar'
style={{
backgroundColor: 'transparent',
padding: '20px',
borderRadius: '0',
height: getLogContainerHeight(),
overflowY: 'auto',
fontFamily:
'"SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace',
fontSize: '14px',
lineHeight: '1.8',
border: 'none',
boxShadow: 'none',
}}
>
{searchTerm && (
<Alert
message={t('pages.taskLog.searchResults', { searchTerm })}
type='info'
showIcon
closable
onClose={() => {
setSearchTerm('');
setFilteredLogs(logs);
}}
className='mb-16'
/>
)}

{fetchError && (
<Alert
message={t('pages.taskLog.autoRefreshError')}
description={
<div>
<p>{fetchError}</p>
<p>{t('pages.taskLog.autoRefreshPaused')}</p>
</div>
}
type='warning'
showIcon
icon={<WarningOutlined />}
closable
action={
<Button
size='small'
type='primary'
onClick={handleManualRefresh}
>
{t('pages.taskLog.refreshNow')}
</Button>
}
onClose={() => setFetchError(null)}
className='mb-16'
/>
)}

{filteredLogs.split('\n').map((line, index) => (
<React.Fragment key={index}>{formatLogLine(line)}</React.Fragment>
))}
</div>
{showScrollToBottom && (
<Button
type='text'
onClick={handleScrollToBottomClick}
style={{
position: 'absolute',
bottom: '24px',
right: '24px',
zIndex: 10,
borderRadius: '50%',
width: '40px',
height: '40px',
boxShadow: '0 2px 8px rgba(0, 0, 0, 0.15)',
}}
icon={
<DownOutlined
style={{ fontSize: '20px', color: token.colorPrimary }}
/>
}
onClose={() => setFetchError(null)}
className='mb-16'
/>
)}
</div>
</div>
);
}

{filteredLogs.split('\n').map((line, index) => (
<React.Fragment key={index}>{formatLogLine(line)}</React.Fragment>
))}
return (
<div className='page-container'>
<div className='page-header-wrapper mb-24'>
<PageHeader
title={t('pages.taskLog.title', '任务日志')}
icon={<MonitorOutlined />}
level={3}
extra={
isStatusRefreshing && (
<Tooltip title='refreshing...'>
<span className='ml-8'>
<LoadingSpinner size='small' showText={false} />
</span>
</Tooltip>
)
}
/>
</div>
<div className='jobs-content-wrapper'>
{/* Toolbar */}
<div
style={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
padding: '16px 0',
marginBottom: '16px',
borderBottom: '1px solid rgba(0, 0, 0, 0.06)',
}}
>
<div style={{ display: 'flex', alignItems: 'center', gap: '12px' }}>
<Text type='secondary' style={{ fontSize: '14px' }}>
{t('pages.taskLog.taskId', '任务ID')}:{' '}
</Text>
<Text style={{ fontSize: '14px' }}>{id}</Text>
</div>
<Space wrap size='middle'>
<Select
value={tailLines}
onChange={value => setTailLines(value)}
className='w-140'
style={{ minWidth: '140px' }}
>
<Select.Option value={100}>
{t('pages.taskLog.last100Lines')}
</Select.Option>
<Select.Option value={500}>
{t('pages.taskLog.last500Lines')}
</Select.Option>
<Select.Option value={1000}>
{t('pages.taskLog.last1000Lines')}
</Select.Option>
<Select.Option value={0}>
{t('pages.taskLog.allLogs')}
</Select.Option>
</Select>
<Switch
checkedChildren={t('pages.taskLog.autoRefresh')}
unCheckedChildren={t('pages.taskLog.stopRefresh')}
checked={autoRefresh}
onChange={checked => {
setAutoRefresh(checked);
if (checked) {
setFetchError(null);
}
}}
disabled={isTaskInFinalState(task?.status)}
/>
<Button icon={<SyncOutlined />} onClick={handleManualRefresh}>
{t('pages.taskLog.refreshLogs')}
</Button>
<Search
placeholder={t('pages.taskLog.searchLogContent')}
allowClear
enterButton={<SearchOutlined />}
onSearch={handleSearch}
className='w-250'
style={{ minWidth: '250px' }}
/>
<Button
type='primary'
icon={<DownloadOutlined />}
onClick={handleDownload}
>
{t('pages.taskLog.downloadLogs')}
</Button>
<Button
icon={
fullscreen ? <FullscreenExitOutlined /> : <FullscreenOutlined />
}
onClick={toggleFullscreen}
>
{fullscreen
? t('pages.taskLog.exitFullscreen')
: t('pages.taskLog.fullscreen')}
</Button>
</Space>
</div>
{showScrollToBottom && (
<Button
type='text'
onClick={handleScrollToBottomClick}

{/* Log Container */}
<div style={{ position: 'relative' }}>
<div
ref={logContainerRef}
className='custom-scrollbar'
style={{
position: 'absolute',
bottom: fullscreen ? '24px' : '40px',
right: '40px',
zIndex: 10,
backgroundColor: 'transparent',
padding: '20px',
borderRadius: '0',
height: getLogContainerHeight(),
overflowY: 'auto',
fontFamily:
'"SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace',
fontSize: '14px',
lineHeight: '1.8',
border: 'none',
boxShadow: 'none',
}}
icon={
<DownOutlined
style={{ fontSize: '24px', color: token.colorPrimary }}
>
{searchTerm && (
<Alert
message={t('pages.taskLog.searchResults', { searchTerm })}
type='info'
showIcon
closable
onClose={() => {
setSearchTerm('');
setFilteredLogs(logs);
}}
className='mb-16'
/>
}
/>
)}
</Card>
)}

{fetchError && (
<Alert
message={t('pages.taskLog.autoRefreshError')}
description={
<div>
<p>{fetchError}</p>
<p>{t('pages.taskLog.autoRefreshPaused')}</p>
</div>
}
type='warning'
showIcon
icon={<WarningOutlined />}
closable
action={
<Button
size='small'
type='primary'
onClick={handleManualRefresh}
>
{t('pages.taskLog.refreshNow')}
</Button>
}
onClose={() => setFetchError(null)}
className='mb-16'
/>
)}

{filteredLogs.split('\n').map((line, index) => (
<React.Fragment key={index}>{formatLogLine(line)}</React.Fragment>
))}
</div>
{showScrollToBottom && (
<Button
type='text'
onClick={handleScrollToBottomClick}
style={{
position: 'absolute',
bottom: fullscreen ? '24px' : '24px',
right: '24px',
zIndex: 10,
borderRadius: '50%',
width: '40px',
height: '40px',
boxShadow: '0 2px 8px rgba(0, 0, 0, 0.15)',
}}
icon={
<DownOutlined
style={{ fontSize: '20px', color: token.colorPrimary }}
/>
}
/>
)}
</div>
</div>
</div>
);
};

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

This component has a large amount of duplicated JSX for rendering the view in normal and fullscreen modes. The logic for the toolbar and the log container is almost identical in both the if (fullscreen) block and the final return statement. This makes the component difficult to maintain, as any change needs to be applied in two places.

I recommend refactoring the duplicated parts into smaller, reusable components or render functions. For example, you could create renderToolbar() and renderLogContainer() functions that accept props to handle the minor differences between the two modes.

Comment on lines +306 to +312
style={{
minHeight: '1.8em',
lineHeight: '1.8',
whiteSpace: 'pre-wrap',
wordBreak: 'break-all',
padding: '2px 0',
}}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

These inline styles are duplicated for different log line types. To improve maintainability and adhere to the Don't Repeat Yourself (DRY) principle, consider extracting these common styles into a dedicated CSS class in src/styles/index.css and applying that class to the div.

Comment on lines +818 to 826
<style>{`
.table-highlight-row {
background-color: rgba(0, 0, 0, 0.02) !important;
}
.ant-table-row:hover {
cursor: pointer;
background-color: rgba(0, 0, 0, 0.02) !important;
}
`}</style>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This inline <style> block should be removed. The styles for .table-highlight-row and .ant-table-row:hover should be defined in the global src/styles/index.css file along with the other new UI styles. This centralizes styling, improves maintainability, and avoids potential conflicts or the need for !important. The index.css file already contains similar styles that can be leveraged or adjusted.

@LuckyYC LuckyYC merged commit 4670c6b into dev Dec 24, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants