Enhance AppUpdateProgress component with detailed download and installation stages; introduce modal for error handling and improve progress status management.
All checks were successful
farmcontrol/farmcontrol-ui/pipeline/head This commit looks good
All checks were successful
farmcontrol/farmcontrol-ui/pipeline/head This commit looks good
This commit is contained in:
parent
71fe6e3462
commit
8a0bc22124
@ -1,6 +1,12 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import { Alert, Flex, Progress, Typography } from 'antd'
|
||||
import { LoadingOutlined } from '@ant-design/icons'
|
||||
import { useState } from 'react'
|
||||
import { Button, Flex, Modal, Progress, Typography, theme } from 'antd'
|
||||
|
||||
import CloudIcon from '../../../Icons/CloudIcon'
|
||||
import HostIcon from '../../../Icons/HostIcon'
|
||||
|
||||
import CheckCircleIcon from '../../../Icons/CheckCircleIcon'
|
||||
import XMarkCircleIcon from '../../../Icons/XMarkCircleIcon'
|
||||
|
||||
const { Text } = Typography
|
||||
|
||||
@ -21,27 +27,137 @@ const formatBytes = (bytes) => {
|
||||
}`
|
||||
}
|
||||
|
||||
const getProgressStatus = (phase) => {
|
||||
if (phase === 'error') return 'exception'
|
||||
if (phase === 'downloaded' || phase === 'installing') return 'success'
|
||||
const STAGE_CONFIG = {
|
||||
download: {
|
||||
icon: CloudIcon,
|
||||
labels: {
|
||||
pending: 'Download',
|
||||
active: 'Downloading',
|
||||
complete: 'Downloaded',
|
||||
error: 'Download failed'
|
||||
}
|
||||
},
|
||||
install: {
|
||||
icon: HostIcon,
|
||||
labels: {
|
||||
pending: 'Install',
|
||||
active: 'Installing',
|
||||
complete: 'Installed',
|
||||
error: 'Install failed'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const getStageColor = (status, token) => {
|
||||
if (status === 'complete') return token.colorSuccess
|
||||
if (status === 'active') return token.colorPrimary
|
||||
if (status === 'error') return token.colorError
|
||||
return token.colorTextQuaternary
|
||||
}
|
||||
|
||||
const getDownloadStageStatus = (phase, isError) => {
|
||||
if (isError && ['preparing', 'downloading'].includes(phase)) return 'error'
|
||||
if (['downloaded', 'installing'].includes(phase)) return 'complete'
|
||||
if (['preparing', 'downloading'].includes(phase)) return 'active'
|
||||
return 'pending'
|
||||
}
|
||||
|
||||
const getInstallStageStatus = (phase, isError, message) => {
|
||||
if (isError && ['downloaded', 'installing'].includes(phase)) return 'error'
|
||||
if (
|
||||
phase === 'installing' &&
|
||||
String(message || '')
|
||||
.toLowerCase()
|
||||
.includes('complete')
|
||||
) {
|
||||
return 'complete'
|
||||
}
|
||||
if (phase === 'installing') return 'active'
|
||||
return 'pending'
|
||||
}
|
||||
|
||||
const getProgressStatus = (stageStatus) => {
|
||||
if (stageStatus === 'error') return 'exception'
|
||||
if (stageStatus === 'complete') return 'success'
|
||||
return 'active'
|
||||
}
|
||||
|
||||
const AppUpdateProgress = ({ progress, update }) => {
|
||||
const UpdateStage = ({ stage, status, percent, detail }) => {
|
||||
const { token } = theme.useToken()
|
||||
const config = STAGE_CONFIG[stage]
|
||||
const StageIcon = config.icon
|
||||
const color = getStageColor(status, token)
|
||||
const showProgress = status === 'active'
|
||||
const resolvedPercent =
|
||||
typeof percent === 'number' ? Math.min(percent, 100) : undefined
|
||||
|
||||
const StatusIcon =
|
||||
status === 'complete'
|
||||
? CheckCircleIcon
|
||||
: status === 'error'
|
||||
? XMarkCircleIcon
|
||||
: StageIcon
|
||||
|
||||
return (
|
||||
<Flex vertical gap={4}>
|
||||
<Flex align='center' gap='middle'>
|
||||
<StatusIcon style={{ fontSize: 20, color, flexShrink: 0 }} />
|
||||
<Flex align='center' gap='small' style={{ flex: 1, minWidth: 0 }}>
|
||||
<Text style={{ flexShrink: 0, minWidth: 96 }}>
|
||||
{config.labels[status]}
|
||||
</Text>
|
||||
{showProgress && (
|
||||
<Progress
|
||||
percent={resolvedPercent}
|
||||
status={getProgressStatus(status)}
|
||||
showInfo={typeof resolvedPercent === 'number'}
|
||||
style={{ flex: 1, margin: 0 }}
|
||||
/>
|
||||
)}
|
||||
</Flex>
|
||||
</Flex>
|
||||
{detail && (
|
||||
<Text type='secondary' style={{ marginLeft: 36 }}>
|
||||
{detail}
|
||||
</Text>
|
||||
)}
|
||||
</Flex>
|
||||
)
|
||||
}
|
||||
|
||||
UpdateStage.propTypes = {
|
||||
stage: PropTypes.oneOf(['download', 'install']).isRequired,
|
||||
status: PropTypes.oneOf(['pending', 'active', 'complete', 'error'])
|
||||
.isRequired,
|
||||
percent: PropTypes.number,
|
||||
detail: PropTypes.string
|
||||
}
|
||||
|
||||
const AppUpdateProgress = ({ progress, update, onClose }) => {
|
||||
const phase = progress?.phase || 'preparing'
|
||||
const percent =
|
||||
typeof progress?.percent === 'number' ? Math.min(progress.percent, 100) : 0
|
||||
typeof progress?.percent === 'number'
|
||||
? Math.min(progress.percent, 100)
|
||||
: null
|
||||
const downloaded = formatBytes(progress?.downloadedBytes)
|
||||
const total = formatBytes(progress?.totalBytes)
|
||||
const artifactName =
|
||||
progress?.artifact?.fileName ||
|
||||
progress?.artifact?.relativePath ||
|
||||
'Selected installer'
|
||||
const message = progress?.message || 'Preparing update'
|
||||
const isInstalling = phase === 'installing'
|
||||
const isError = phase === 'error'
|
||||
const showProgress =
|
||||
!isError && (!isInstalling || typeof progress?.percent === 'number')
|
||||
|
||||
const [errorModalOpen, setErrorModalOpen] = useState(true)
|
||||
|
||||
const downloadStatus = getDownloadStageStatus(phase, isError)
|
||||
const installStatus = getInstallStageStatus(phase, isError, message)
|
||||
|
||||
const downloadPercent =
|
||||
downloadStatus === 'active' ? (phase === 'preparing' ? 0 : percent) : null
|
||||
|
||||
const installPercent = installStatus === 'active' ? percent : null
|
||||
|
||||
const downloadDetail =
|
||||
downloadStatus === 'active' && downloaded && total
|
||||
? `${downloaded} of ${total}`
|
||||
: null
|
||||
|
||||
return (
|
||||
<Flex vertical gap='middle'>
|
||||
@ -52,34 +168,44 @@ const AppUpdateProgress = ({ progress, update }) => {
|
||||
{update?.branch ? `${update.branch}` : 'unknown'}...
|
||||
</Text>
|
||||
|
||||
{isError ? (
|
||||
<Alert type='error' showIcon message={message} />
|
||||
) : (
|
||||
<Alert
|
||||
type={isInstalling ? 'success' : 'info'}
|
||||
showIcon
|
||||
icon={isInstalling ? undefined : <LoadingOutlined />}
|
||||
message={
|
||||
isInstalling
|
||||
? message
|
||||
: `Downloading ${artifactName}`
|
||||
}
|
||||
<Flex vertical gap='middle'>
|
||||
<UpdateStage
|
||||
stage='download'
|
||||
status={downloadStatus}
|
||||
percent={downloadPercent}
|
||||
detail={downloadDetail}
|
||||
/>
|
||||
)}
|
||||
|
||||
{showProgress && (
|
||||
<Progress
|
||||
percent={percent}
|
||||
status={getProgressStatus(phase)}
|
||||
showInfo={phase !== 'preparing'}
|
||||
<UpdateStage
|
||||
stage='install'
|
||||
status={installStatus}
|
||||
percent={installPercent}
|
||||
/>
|
||||
)}
|
||||
</Flex>
|
||||
|
||||
{downloaded && total && (
|
||||
<Text type='secondary'>
|
||||
{downloaded} of {total} downloaded
|
||||
</Text>
|
||||
)}
|
||||
<Modal
|
||||
title='Update Failed'
|
||||
open={isError && errorModalOpen == true}
|
||||
centered
|
||||
closable={false}
|
||||
maskClosable={false}
|
||||
onCancel={() => {
|
||||
setErrorModalOpen(false)
|
||||
onClose()
|
||||
}}
|
||||
footer={[
|
||||
<Button
|
||||
key='close'
|
||||
onClick={() => {
|
||||
setErrorModalOpen(false)
|
||||
onClose()
|
||||
}}
|
||||
>
|
||||
Close
|
||||
</Button>
|
||||
]}
|
||||
>
|
||||
<Text>{message}</Text>
|
||||
</Modal>
|
||||
</Flex>
|
||||
)
|
||||
}
|
||||
@ -93,7 +219,8 @@ AppUpdateProgress.propTypes = {
|
||||
message: PropTypes.string,
|
||||
artifact: PropTypes.object
|
||||
}),
|
||||
update: PropTypes.object
|
||||
update: PropTypes.object,
|
||||
onClose: PropTypes.func
|
||||
}
|
||||
|
||||
export default AppUpdateProgress
|
||||
|
||||
@ -269,6 +269,7 @@ export const AppUpdateProvider = ({ children }) => {
|
||||
<AppUpdateProgress
|
||||
progress={updateProgress}
|
||||
update={installingUpdate}
|
||||
onClose={closeUpdateModal}
|
||||
/>
|
||||
) : (
|
||||
<NewAppUpdate
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user