-
Notifications
You must be signed in to change notification settings - Fork 13
update ui #53
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Merge pull request #44 from MigoXLab/main
fix number of requests and optimization
update translation
common APIs
Summary of ChangesHello @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
Using Gemini Code AssistThe 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
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 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
|
There was a problem hiding this 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.
| 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> | ||
| ); | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| style={{ | ||
| minHeight: '1.8em', | ||
| lineHeight: '1.8', | ||
| whiteSpace: 'pre-wrap', | ||
| wordBreak: 'break-all', | ||
| padding: '2px 0', | ||
| }} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| <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> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
No description provided.