Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 | 2x 13x 13x 13x 13x 13x 13x 13x 13x 13x 6x 13x | /**
* WorkflowProgressBar Component
*
* Story 2.5.3 - Task 10b (GREEN Phase)
* AC: 5 (Workflow Progress Display)
* Wireframe: docs/wireframes/story-1.16-workflow-visualization.md
*
* Displays workflow progress with:
* - Progress bar showing completion percentage (0-100%)
* - Current step indicator (Step X/8: Step Name)
* - Clickable progress bar navigation to workflow visualization
* - Warning indicators for blockers (⚠️)
* - [View Workflow Details] button
* - Color-coded progress (warning <30%, primary 30-70%, success >70%)
* - Full accessibility (keyboard navigation, ARIA labels)
*/
import React from 'react';
import {
Box,
LinearProgress,
Typography,
Tooltip,
Stack,
Chip,
useTheme,
useMediaQuery,
} from '@mui/material';
import { Warning as WarningIcon, Error as ErrorIcon } from '@mui/icons-material';
import { useTranslation } from 'react-i18next';
import type { WorkflowState } from '@/types/event.types';
import { WORKFLOW_STATE_ORDER, getProgressColor } from '@/utils/workflow/workflowState';
interface WorkflowProgressBarProps {
workflow: WorkflowState;
compact?: boolean;
/** Optional: Explicit workflow state (overrides derivation from currentStep) */
workflowState?: string;
}
export const WorkflowProgressBar: React.FC<WorkflowProgressBarProps> = ({
workflow,
compact = false,
}) => {
const { t } = useTranslation('events');
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down('sm'));
// Handle undefined/null workflow
const safeWorkflow: WorkflowState = workflow || {
currentStep: 1,
totalSteps: WORKFLOW_STATE_ORDER.length,
completionPercentage: 0,
steps: [],
blockers: [],
};
const { blockers } = safeWorkflow;
const completionPercentage = Math.round(safeWorkflow.completionPercentage * 10) / 10;
// Check for critical blockers
const hasCriticalBlockers = blockers.some((b) => b.severity === 'critical');
// Get progress bar color
const progressColor = getProgressColor(completionPercentage);
// Build blocker tooltip content
const blockerTooltipContent = (
<Stack spacing={1}>
{blockers.map((blocker, index) => (
<Box key={index}>
<Typography variant="caption" fontWeight="bold">
{blocker.severity === 'critical' ? '🔴' : '⚠️'} Step {blocker.stepNumber}
</Typography>
<Typography variant="caption" display="block">
{blocker.message}
</Typography>
</Box>
))}
</Stack>
);
return (
<Box
sx={{
width: '100%',
display: 'flex',
flexDirection: compact || isMobile ? 'column' : 'row',
alignItems: compact || isMobile ? 'stretch' : 'center',
gap: 2,
}}
className={isMobile ? 'mobile stacked' : ''}
>
{/* Progress Bar Section */}
<Box sx={{ flex: 1 }}>
{/* Blocker Warning Indicator */}
{blockers.length > 0 && (
<Box mb={0.5}>
<Tooltip title={blockerTooltipContent} arrow>
<Chip
icon={hasCriticalBlockers ? <ErrorIcon /> : <WarningIcon />}
label={
<>
{hasCriticalBlockers && '🔴 '}⚠️{' '}
{t('workflow.blockers', { count: blockers.length })}
</>
}
size="small"
color={hasCriticalBlockers ? 'error' : 'warning'}
variant="outlined"
/>
</Tooltip>
</Box>
)}
{/* Progress Bar with Percentage */}
<Stack direction="row" alignItems="center" spacing={1}>
<Box
role="progressbar"
aria-valuenow={completionPercentage}
aria-valuemin={0}
aria-valuemax={100}
aria-label={t('workflow.progressBarLabel', {
percentage: completionPercentage,
})}
sx={{ flex: 1, borderRadius: 1, overflow: 'hidden' }}
>
<LinearProgress
variant="determinate"
value={completionPercentage}
color={progressColor}
sx={{
height: 8,
borderRadius: 1,
}}
/>
</Box>
<Typography variant="body2" fontWeight="bold" sx={{ minWidth: '40px' }}>
{completionPercentage}%
</Typography>
</Stack>
</Box>
</Box>
);
};
export default WorkflowProgressBar;
|