feat: DeploymentProgress step indicator component
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
74
ui/src/components/DeploymentProgress.module.css
Normal file
74
ui/src/components/DeploymentProgress.module.css
Normal file
@@ -0,0 +1,74 @@
|
||||
.container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0;
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
.step {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
.dot {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 50%;
|
||||
border: 2px solid var(--border-subtle);
|
||||
background: transparent;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.dotCompleted {
|
||||
background: var(--success);
|
||||
border-color: var(--success);
|
||||
}
|
||||
|
||||
.dotActive {
|
||||
background: var(--accent, #6c7aff);
|
||||
border-color: var(--accent, #6c7aff);
|
||||
animation: pulse 1.5s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.dotFailed {
|
||||
background: var(--error);
|
||||
border-color: var(--error);
|
||||
}
|
||||
|
||||
.line {
|
||||
width: 32px;
|
||||
height: 2px;
|
||||
background: var(--border-subtle);
|
||||
}
|
||||
|
||||
.lineCompleted {
|
||||
background: var(--success);
|
||||
}
|
||||
|
||||
.label {
|
||||
font-size: 10px;
|
||||
color: var(--text-muted);
|
||||
text-align: center;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.labelActive {
|
||||
color: var(--accent, #6c7aff);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.labelFailed {
|
||||
color: var(--error);
|
||||
}
|
||||
|
||||
.stepColumn {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0%, 100% { opacity: 1; }
|
||||
50% { opacity: 0.5; }
|
||||
}
|
||||
46
ui/src/components/DeploymentProgress.tsx
Normal file
46
ui/src/components/DeploymentProgress.tsx
Normal file
@@ -0,0 +1,46 @@
|
||||
import styles from './DeploymentProgress.module.css';
|
||||
|
||||
const STAGES = [
|
||||
{ key: 'PRE_FLIGHT', label: 'Pre-flight' },
|
||||
{ key: 'PULL_IMAGE', label: 'Pull' },
|
||||
{ key: 'CREATE_NETWORK', label: 'Network' },
|
||||
{ key: 'START_REPLICAS', label: 'Start' },
|
||||
{ key: 'HEALTH_CHECK', label: 'Health' },
|
||||
{ key: 'SWAP_TRAFFIC', label: 'Swap' },
|
||||
{ key: 'COMPLETE', label: 'Done' },
|
||||
];
|
||||
|
||||
interface DeploymentProgressProps {
|
||||
currentStage: string | null;
|
||||
failed?: boolean;
|
||||
}
|
||||
|
||||
export function DeploymentProgress({ currentStage, failed }: DeploymentProgressProps) {
|
||||
if (!currentStage) return null;
|
||||
|
||||
const currentIndex = STAGES.findIndex((s) => s.key === currentStage);
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
{STAGES.map((stage, i) => {
|
||||
const isCompleted = i < currentIndex;
|
||||
const isActive = i === currentIndex && !failed;
|
||||
const isFailed = i === currentIndex && failed;
|
||||
|
||||
return (
|
||||
<div key={stage.key} className={styles.step}>
|
||||
{i > 0 && (
|
||||
<div className={`${styles.line} ${isCompleted || isActive || isFailed ? styles.lineCompleted : ''}`} />
|
||||
)}
|
||||
<div className={styles.stepColumn}>
|
||||
<div className={`${styles.dot} ${isCompleted ? styles.dotCompleted : ''} ${isActive ? styles.dotActive : ''} ${isFailed ? styles.dotFailed : ''}`} />
|
||||
<span className={`${styles.label} ${isActive ? styles.labelActive : ''} ${isFailed ? styles.labelFailed : ''}`}>
|
||||
{stage.label}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user