feat(frontend): implement main dashboard page (#48)

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>
This commit is contained in:
2025-12-30 23:46:50 +01:00
parent e85788f79f
commit 5b1e2852ea
67 changed files with 8879 additions and 0 deletions

View File

@@ -0,0 +1,97 @@
/**
* Status Badge Components
*
* Reusable badge components for displaying project and autonomy status.
*/
'use client';
import { CircleDot } from 'lucide-react';
import { Badge } from '@/components/ui/badge';
import { cn } from '@/lib/utils';
import type { ProjectStatus, AutonomyLevel } from './types';
// ============================================================================
// Project Status Badge
// ============================================================================
const projectStatusConfig: Record<ProjectStatus, { label: string; className: string }> = {
draft: {
label: 'Draft',
className: 'bg-gray-100 text-gray-800 dark:bg-gray-800 dark:text-gray-200',
},
in_progress: {
label: 'In Progress',
className: 'bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200',
},
paused: {
label: 'Paused',
className: 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200',
},
completed: {
label: 'Completed',
className: 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200',
},
blocked: {
label: 'Blocked',
className: 'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200',
},
archived: {
label: 'Archived',
className: 'bg-gray-100 text-gray-600 dark:bg-gray-800 dark:text-gray-400',
},
};
interface ProjectStatusBadgeProps {
status: ProjectStatus;
className?: string;
}
export function ProjectStatusBadge({ status, className }: ProjectStatusBadgeProps) {
const config = projectStatusConfig[status] || projectStatusConfig.draft;
return (
<Badge variant="outline" className={cn(config.className, className)}>
{config.label}
</Badge>
);
}
// ============================================================================
// Autonomy Level Badge
// ============================================================================
const autonomyLevelConfig: Record<AutonomyLevel, { label: string; description: string }> = {
full_control: {
label: 'Full Control',
description: 'Approve every action',
},
milestone: {
label: 'Milestone',
description: 'Approve at sprint boundaries',
},
autonomous: {
label: 'Autonomous',
description: 'Only major decisions',
},
};
interface AutonomyBadgeProps {
level: AutonomyLevel;
showDescription?: boolean;
className?: string;
}
export function AutonomyBadge({ level, showDescription = false, className }: AutonomyBadgeProps) {
const config = autonomyLevelConfig[level] || autonomyLevelConfig.milestone;
return (
<Badge variant="secondary" className={cn('gap-1', className)} title={config.description}>
<CircleDot className="h-3 w-3" aria-hidden="true" />
{config.label}
{showDescription && (
<span className="text-muted-foreground"> - {config.description}</span>
)}
</Badge>
);
}