Files
router-lists-ui/frontend/src/components/ProgressBar.jsx
T

196 lines
5.6 KiB
React

import { IconCheck, IconX } from '@tabler/icons-react'
/**
* ProgressBar - компонент прогресс бара с estimated time
* @param {number} progress - прогресс от 0 до 100
* @param {string} status - статус операции
* @param {number} estimatedTime - оставшееся время в секундах
* @param {string} variant - цвет: 'primary', 'success', 'danger', 'warning'
*/
function ProgressBar({
progress = 0,
status = '',
estimatedTime = null,
variant = 'primary',
showPercentage = true,
striped = false,
animated = false,
size = 'default' // 'sm', 'default', 'lg'
}) {
const sizeClass = size === 'sm' ? 'progress-sm' : size === 'lg' ? 'progress-lg' : ''
const stripedClass = striped ? 'progress-bar-striped' : ''
const animatedClass = animated ? 'progress-bar-animated' : ''
const formatTime = (seconds) => {
if (seconds < 60) {
return `${Math.round(seconds)} сек`
} else if (seconds < 3600) {
const minutes = Math.floor(seconds / 60)
const secs = Math.round(seconds % 60)
return `${minutes} мин ${secs} сек`
} else {
const hours = Math.floor(seconds / 3600)
const minutes = Math.floor((seconds % 3600) / 60)
return `${hours} ч ${minutes} мин`
}
}
return (
<div className="progress-container">
<div className="d-flex justify-content-between align-items-center mb-2">
{status && (
<div className="text-muted small">{status}</div>
)}
<div className="d-flex align-items-center gap-2">
{estimatedTime !== null && estimatedTime > 0 && (
<span className="text-muted small">
Осталось ~{formatTime(estimatedTime)}
</span>
)}
{showPercentage && (
<span className="text-muted small fw-bold">
{Math.round(progress)}%
</span>
)}
</div>
</div>
<div className={`progress ${sizeClass}`}>
<div
className={`progress-bar bg-${variant} ${stripedClass} ${animatedClass}`}
role="progressbar"
style={{ width: `${progress}%` }}
aria-valuenow={progress}
aria-valuemin="0"
aria-valuemax="100"
>
{size === 'lg' && showPercentage && (
<span className="progress-bar-label">{Math.round(progress)}%</span>
)}
</div>
</div>
<style jsx>{`
.progress-container {
width: 100%;
}
.progress-sm {
height: 0.5rem;
}
.progress-lg {
height: 2rem;
font-size: 1rem;
}
.progress-bar-label {
line-height: 2rem;
color: white;
}
`}</style>
</div>
)
}
/**
* MultiStepProgress - компонент для отображения многошагового прогресса
*/
function MultiStepProgress({ steps, currentStep, variant = 'primary' }) {
const progress = ((currentStep) / steps.length) * 100
return (
<div className="multi-step-progress">
{/* Индикаторы шагов */}
<div className="steps-indicator mb-3">
{steps.map((step, index) => {
const isPast = index < currentStep
const isCurrent = index === currentStep
const status = isPast ? 'completed' : isCurrent ? 'current' : 'pending'
return (
<div key={index} className={`step-item step-${status}`}>
<div className={`step-circle bg-${isPast ? 'success' : isCurrent ? variant : 'secondary'}`}>
{isPast ? (
<IconCheck size={16} className="text-white" />
) : (
<span className="text-white">{index + 1}</span>
)}
</div>
<div className="step-label text-muted small mt-1">{step}</div>
{index < steps.length - 1 && (
<div className={`step-line ${isPast ? 'completed' : ''}`}></div>
)}
</div>
)
})}
</div>
{/* Прогресс бар */}
<ProgressBar
progress={progress}
variant={variant}
showPercentage={false}
animated
/>
<style jsx>{`
.steps-indicator {
display: flex;
justify-content: space-between;
align-items: flex-start;
position: relative;
}
.step-item {
display: flex;
flex-direction: column;
align-items: center;
flex: 1;
position: relative;
}
.step-circle {
width: 2rem;
height: 2rem;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-weight: 600;
z-index: 2;
position: relative;
}
.step-line {
position: absolute;
top: 1rem;
left: 50%;
right: -50%;
height: 2px;
background: var(--tblr-border-color, #e9ecef);
z-index: 1;
}
.step-line.completed {
background: var(--tblr-success, #2fb344);
}
.step-label {
text-align: center;
max-width: 100px;
}
.step-current .step-label {
font-weight: 600;
color: var(--tblr-body-color) !important;
}
`}</style>
</div>
)
}
export { ProgressBar, MultiStepProgress }
export default ProgressBar