Files
smoothschedule/frontend/src/components/ui/StepIndicator.tsx
poduck 8c52d6a275 refactor: Extract reusable UI components and add TDD documentation
- Add comprehensive TDD documentation to CLAUDE.md with coverage requirements and examples
- Extract reusable UI components to frontend/src/components/ui/ (Modal, FormInput, Button, Alert, etc.)
- Add shared constants (schedulePresets) and utility hooks (useCrudMutation, useFormValidation)
- Update frontend/CLAUDE.md with component documentation and usage examples
- Refactor CreateTaskModal to use shared components and constants
- Fix test assertions to be more robust and accurate across all test files

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-10 15:27:27 -05:00

119 lines
3.5 KiB
TypeScript

import React from 'react';
import { Check } from 'lucide-react';
interface Step {
id: string | number;
label: string;
description?: string;
}
interface StepIndicatorProps {
steps: Step[];
currentStep: number;
/** Color for completed/active steps */
color?: 'blue' | 'brand' | 'green' | 'purple';
/** Show connector lines between steps */
showConnectors?: boolean;
/** Additional class name */
className?: string;
}
const colorClasses = {
blue: {
active: 'bg-blue-600 text-white',
completed: 'bg-blue-600 text-white',
pending: 'bg-gray-200 dark:bg-gray-700 text-gray-500',
textActive: 'text-blue-600 dark:text-blue-400',
textPending: 'text-gray-400',
connector: 'bg-blue-600',
connectorPending: 'bg-gray-200 dark:bg-gray-700',
},
brand: {
active: 'bg-brand-600 text-white',
completed: 'bg-brand-600 text-white',
pending: 'bg-gray-200 dark:bg-gray-700 text-gray-500',
textActive: 'text-brand-600 dark:text-brand-400',
textPending: 'text-gray-400',
connector: 'bg-brand-600',
connectorPending: 'bg-gray-200 dark:bg-gray-700',
},
green: {
active: 'bg-green-600 text-white',
completed: 'bg-green-600 text-white',
pending: 'bg-gray-200 dark:bg-gray-700 text-gray-500',
textActive: 'text-green-600 dark:text-green-400',
textPending: 'text-gray-400',
connector: 'bg-green-600',
connectorPending: 'bg-gray-200 dark:bg-gray-700',
},
purple: {
active: 'bg-purple-600 text-white',
completed: 'bg-purple-600 text-white',
pending: 'bg-gray-200 dark:bg-gray-700 text-gray-500',
textActive: 'text-purple-600 dark:text-purple-400',
textPending: 'text-gray-400',
connector: 'bg-purple-600',
connectorPending: 'bg-gray-200 dark:bg-gray-700',
},
};
export const StepIndicator: React.FC<StepIndicatorProps> = ({
steps,
currentStep,
color = 'blue',
showConnectors = true,
className = '',
}) => {
const colors = colorClasses[color];
return (
<div className={`flex items-center justify-center ${className}`}>
{steps.map((step, index) => {
const stepNumber = index + 1;
const isCompleted = stepNumber < currentStep;
const isActive = stepNumber === currentStep;
const isPending = stepNumber > currentStep;
return (
<React.Fragment key={step.id}>
<div className="flex items-center gap-2">
{/* Step circle */}
<div
className={`w-8 h-8 rounded-full flex items-center justify-center font-medium text-sm transition-colors ${
isCompleted
? colors.completed
: isActive
? colors.active
: colors.pending
}`}
>
{isCompleted ? <Check size={16} /> : stepNumber}
</div>
{/* Step label */}
<span
className={`font-medium text-sm ${
isActive || isCompleted ? colors.textActive : colors.textPending
}`}
>
{step.label}
</span>
</div>
{/* Connector */}
{showConnectors && index < steps.length - 1 && (
<div
className={`w-16 h-0.5 mx-4 ${
stepNumber < currentStep ? colors.connector : colors.connectorPending
}`}
/>
)}
</React.Fragment>
);
})}
</div>
);
};
export default StepIndicator;