Implement the main dashboard / projects list page for Syndarix as the landing page after login. The implementation includes: Dashboard Components: - QuickStats: Overview cards showing active projects, agents, issues, approvals - ProjectsSection: Grid/list view with filtering and sorting controls - ProjectCardGrid: Rich project cards for grid view - ProjectRowList: Compact rows for list view - ActivityFeed: Real-time activity sidebar with connection status - PerformanceCard: Performance metrics display - EmptyState: Call-to-action for new users - ProjectStatusBadge: Status indicator with icons - ComplexityIndicator: Visual complexity dots - ProgressBar: Accessible progress bar component Features: - Projects grid/list view with view mode toggle - Filter by status (all, active, paused, completed, archived) - Sort by recent, name, progress, or issues - Quick stats overview with counts - Real-time activity feed sidebar with live/reconnecting status - Performance metrics card - Create project button linking to wizard - Responsive layout for mobile/desktop - Loading skeleton states - Empty state for new users API Integration: - useProjects hook for fetching projects (mock data until backend ready) - useDashboardStats hook for statistics - TanStack Query for caching and data fetching Testing: - 37 unit tests covering all dashboard components - E2E test suite for dashboard functionality - Accessibility tests (keyboard nav, aria attributes, heading hierarchy) Technical: - TypeScript strict mode compliance - ESLint passing - WCAG AA accessibility compliance - Mobile-first responsive design - Dark mode support via semantic tokens - Follows design system guidelines 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
76 lines
1.8 KiB
TypeScript
76 lines
1.8 KiB
TypeScript
/**
|
|
* Progress Bar Component
|
|
*
|
|
* Simple progress bar with customizable appearance.
|
|
*/
|
|
|
|
'use client';
|
|
|
|
import { cn } from '@/lib/utils';
|
|
|
|
interface ProgressBarProps {
|
|
/** Progress value (0-100) */
|
|
value: number;
|
|
/** Color variant */
|
|
variant?: 'default' | 'success' | 'warning' | 'error';
|
|
/** Height size */
|
|
size?: 'sm' | 'md' | 'lg';
|
|
/** Show percentage label */
|
|
showLabel?: boolean;
|
|
/** Additional CSS classes for the container */
|
|
className?: string;
|
|
/** Aria label for accessibility */
|
|
'aria-label'?: string;
|
|
}
|
|
|
|
const variantClasses = {
|
|
default: 'bg-primary',
|
|
success: 'bg-green-500',
|
|
warning: 'bg-yellow-500',
|
|
error: 'bg-red-500',
|
|
};
|
|
|
|
const sizeClasses = {
|
|
sm: 'h-1',
|
|
md: 'h-2',
|
|
lg: 'h-3',
|
|
};
|
|
|
|
export function ProgressBar({
|
|
value,
|
|
variant = 'default',
|
|
size = 'md',
|
|
showLabel = false,
|
|
className,
|
|
'aria-label': ariaLabel,
|
|
}: ProgressBarProps) {
|
|
const clampedValue = Math.min(100, Math.max(0, value));
|
|
|
|
return (
|
|
<div className={cn('w-full', className)}>
|
|
{showLabel && (
|
|
<div className="mb-1 flex justify-between text-sm">
|
|
<span className="text-muted-foreground">Progress</span>
|
|
<span className="font-medium">{Math.round(clampedValue)}%</span>
|
|
</div>
|
|
)}
|
|
<div
|
|
className={cn('w-full rounded-full bg-muted', sizeClasses[size])}
|
|
role="progressbar"
|
|
aria-valuenow={clampedValue}
|
|
aria-valuemin={0}
|
|
aria-valuemax={100}
|
|
aria-label={ariaLabel || `Progress: ${Math.round(clampedValue)}%`}
|
|
>
|
|
<div
|
|
className={cn(
|
|
'h-full rounded-full transition-all duration-300 ease-in-out',
|
|
variantClasses[variant]
|
|
)}
|
|
style={{ width: `${clampedValue}%` }}
|
|
/>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|