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:
97
frontend/src/components/projects/StatusBadge.tsx
Normal file
97
frontend/src/components/projects/StatusBadge.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user