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>
This commit is contained in:
118
frontend/src/components/ui/StepIndicator.tsx
Normal file
118
frontend/src/components/ui/StepIndicator.tsx
Normal file
@@ -0,0 +1,118 @@
|
||||
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;
|
||||
Reference in New Issue
Block a user