All files / src/components/organizer/EventPage/cockpit LifecycleSpine.tsx

100% Statements 10/10
100% Branches 9/9
100% Functions 2/2
100% Lines 10/10

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                                                  1x       3x 3x 3x 3x   3x         3x 1x                                   2x             16x                    
/**
 * LifecycleSpine (Epic 14 Phase B — Story 14.B.1; FR8, UX-DR2).
 *
 * Horizontal 8-state stepper: completed = green check, current = highlighted
 * with "Step N of 8". Reuses the existing workflow helpers (this is NOT
 * `WorkflowProgressBar`, which is a %-bar — the spine is a discrete stepper).
 * The `compact` seam (a "Step N/8" bar) is wired for Phase G mobile.
 */
 
import React from 'react';
import { Box, Stepper, Step, StepLabel, Typography, LinearProgress } from '@mui/material';
import { useTranslation } from 'react-i18next';
import {
  WORKFLOW_STATE_ORDER,
  getWorkflowStepNumber,
  getWorkflowProgress,
  getWorkflowStateLabel,
} from '@/utils/workflow/workflowState';
 
interface LifecycleSpineProps {
  workflowState: string;
  /** Mobile (Phase G): collapse to a "Step N/8" bar instead of the full stepper. */
  compact?: boolean;
}
 
export const LifecycleSpine: React.FC<LifecycleSpineProps> = ({
  workflowState,
  compact = false,
}) => {
  const { t } = useTranslation('events');
  const total = WORKFLOW_STATE_ORDER.length;
  const stepNumber = getWorkflowStepNumber(workflowState); // 1..8, 0 if invalid
  const activeIndex = stepNumber > 0 ? stepNumber - 1 : 0;
 
  const stepOfLabel = t('eventPage.cockpit.spine.stepOf', 'Step {{current}} of {{total}}', {
    current: stepNumber || 1,
    total,
  });
 
  if (compact) {
    return (
      <Box
        data-testid="cockpit-lifecycle-spine"
        data-workflow-state={workflowState}
        aria-label={stepOfLabel}
      >
        <Typography variant="subtitle2" gutterBottom>
          {stepOfLabel}
        </Typography>
        <LinearProgress
          variant="determinate"
          value={getWorkflowProgress(workflowState)}
          sx={{ height: 8, borderRadius: 1 }}
        />
      </Box>
    );
  }
 
  return (
    <Box data-testid="cockpit-lifecycle-spine" data-workflow-state={workflowState}>
      <Typography variant="subtitle2" color="text.secondary" gutterBottom>
        {stepOfLabel}
      </Typography>
      <Stepper activeStep={activeIndex} alternativeLabel aria-label={stepOfLabel}>
        {WORKFLOW_STATE_ORDER.map((state, i) => (
          <Step key={state} completed={i < activeIndex}>
            <StepLabel aria-current={i === activeIndex ? 'step' : undefined}>
              {getWorkflowStateLabel(state, t)}
            </StepLabel>
          </Step>
        ))}
      </Stepper>
    </Box>
  );
};