diff --git a/backend/data/demo_data.json b/backend/data/demo_data.json index 707de72..836501c 100644 --- a/backend/data/demo_data.json +++ b/backend/data/demo_data.json @@ -403,6 +403,45 @@ "settings": { "mcp_servers": ["gitea", "knowledge-base"] } + }, + { + "name": "API Gateway Modernization", + "slug": "api-gateway", + "description": "Migrate legacy REST API gateway to modern GraphQL-based architecture with improved caching and rate limiting.", + "owner_email": "__admin__", + "autonomy_level": "milestone", + "status": "active", + "complexity": "complex", + "client_mode": "technical", + "settings": { + "mcp_servers": ["gitea", "knowledge-base"] + } + }, + { + "name": "Customer Analytics Dashboard", + "slug": "analytics-dashboard", + "description": "Real-time analytics dashboard for customer behavior insights, cohort analysis, and predictive modeling.", + "owner_email": "__admin__", + "autonomy_level": "autonomous", + "status": "completed", + "complexity": "medium", + "client_mode": "auto", + "settings": { + "mcp_servers": ["gitea", "knowledge-base"] + } + }, + { + "name": "DevOps Pipeline Automation", + "slug": "devops-automation", + "description": "Automate CI/CD pipelines with AI-assisted deployments, rollback detection, and infrastructure as code.", + "owner_email": "__admin__", + "autonomy_level": "full_control", + "status": "active", + "complexity": "complex", + "client_mode": "technical", + "settings": { + "mcp_servers": ["gitea", "knowledge-base"] + } } ], "sprints": [ @@ -445,6 +484,56 @@ "end_date": "2026-01-20", "status": "active", "planned_points": 18 + }, + { + "project_slug": "api-gateway", + "name": "Sprint 1: GraphQL Schema", + "number": 1, + "goal": "Define GraphQL schema and implement core resolvers for existing REST endpoints.", + "start_date": "2025-12-23", + "end_date": "2026-01-06", + "status": "completed", + "planned_points": 21 + }, + { + "project_slug": "api-gateway", + "name": "Sprint 2: Caching Layer", + "number": 2, + "goal": "Implement Redis-based caching layer and query batching.", + "start_date": "2026-01-06", + "end_date": "2026-01-20", + "status": "active", + "planned_points": 26 + }, + { + "project_slug": "analytics-dashboard", + "name": "Sprint 1: Data Pipeline", + "number": 1, + "goal": "Set up data ingestion pipeline and real-time event processing.", + "start_date": "2025-11-15", + "end_date": "2025-11-29", + "status": "completed", + "planned_points": 18 + }, + { + "project_slug": "analytics-dashboard", + "name": "Sprint 2: Dashboard UI", + "number": 2, + "goal": "Build interactive dashboard with charts and filtering capabilities.", + "start_date": "2025-11-29", + "end_date": "2025-12-13", + "status": "completed", + "planned_points": 21 + }, + { + "project_slug": "devops-automation", + "name": "Sprint 1: Pipeline Templates", + "number": 1, + "goal": "Create reusable CI/CD pipeline templates for common deployment patterns.", + "start_date": "2026-01-06", + "end_date": "2026-01-20", + "status": "active", + "planned_points": 24 } ], "agent_instances": [ @@ -500,6 +589,40 @@ "name": "Atlas", "status": "working", "current_task": "Building employee dashboard API" + }, + { + "project_slug": "api-gateway", + "agent_type_slug": "solutions-architect", + "name": "Orion", + "status": "working", + "current_task": "Designing caching strategy for GraphQL queries" + }, + { + "project_slug": "api-gateway", + "agent_type_slug": "senior-engineer", + "name": "Cleo", + "status": "working", + "current_task": "Implementing Redis cache invalidation" + }, + { + "project_slug": "devops-automation", + "agent_type_slug": "devops-engineer", + "name": "Volt", + "status": "working", + "current_task": "Creating Terraform modules for AWS ECS" + }, + { + "project_slug": "devops-automation", + "agent_type_slug": "senior-engineer", + "name": "Sage", + "status": "idle" + }, + { + "project_slug": "devops-automation", + "agent_type_slug": "qa-engineer", + "name": "Echo", + "status": "waiting", + "current_task": "Waiting for pipeline templates to test" } ], "issues": [ @@ -638,6 +761,119 @@ "priority": "medium", "labels": ["backend", "infrastructure", "storage"], "story_points": 5 + }, + { + "project_slug": "api-gateway", + "sprint_number": 2, + "type": "story", + "title": "Implement Redis caching layer", + "body": "As an API consumer, I want responses to be cached for improved performance.\n\n## Requirements\n- Cache GraphQL query results\n- Configurable TTL per query type\n- Cache invalidation on mutations\n- Cache hit/miss metrics", + "status": "in_progress", + "priority": "critical", + "labels": ["backend", "performance", "redis"], + "story_points": 8, + "assigned_agent_name": "Cleo" + }, + { + "project_slug": "api-gateway", + "sprint_number": 2, + "type": "task", + "title": "Set up query batching and deduplication", + "body": "Implement DataLoader pattern for:\n- Batching multiple queries into single database calls\n- Deduplicating identical queries within request scope\n- N+1 query prevention", + "status": "open", + "priority": "high", + "labels": ["backend", "performance", "graphql"], + "story_points": 5 + }, + { + "project_slug": "api-gateway", + "sprint_number": 2, + "type": "task", + "title": "Implement rate limiting middleware", + "body": "Add rate limiting to prevent API abuse:\n- Per-user rate limits\n- Per-IP fallback for anonymous requests\n- Sliding window algorithm\n- Custom limits per operation type", + "status": "open", + "priority": "high", + "labels": ["backend", "security", "middleware"], + "story_points": 5, + "assigned_agent_name": "Orion" + }, + { + "project_slug": "api-gateway", + "sprint_number": 2, + "type": "bug", + "title": "Fix N+1 query in user resolver", + "body": "The user resolver is making separate database calls for each user's organization.\n\n## Steps to Reproduce\n1. Query users with organization field\n2. Check database logs\n3. Observe N+1 queries", + "status": "open", + "priority": "high", + "labels": ["bug", "performance", "graphql"], + "story_points": 3 + }, + { + "project_slug": "analytics-dashboard", + "sprint_number": 2, + "type": "story", + "title": "Build cohort analysis charts", + "body": "As a product manager, I want to analyze user cohorts over time.\n\n## Features\n- Weekly/monthly cohort grouping\n- Retention curve visualization\n- Cohort comparison view", + "status": "closed", + "priority": "high", + "labels": ["frontend", "charts", "analytics"], + "story_points": 8 + }, + { + "project_slug": "analytics-dashboard", + "sprint_number": 2, + "type": "task", + "title": "Implement real-time event streaming", + "body": "Set up WebSocket connection for live event updates:\n- Event type filtering\n- Buffering for high-volume periods\n- Reconnection handling", + "status": "closed", + "priority": "high", + "labels": ["backend", "websocket", "realtime"], + "story_points": 5 + }, + { + "project_slug": "devops-automation", + "sprint_number": 1, + "type": "epic", + "title": "CI/CD Pipeline Templates", + "body": "Create reusable pipeline templates for common deployment patterns.\n\n## Templates Needed\n- Node.js applications\n- Python applications\n- Docker-based deployments\n- Kubernetes deployments", + "status": "in_progress", + "priority": "critical", + "labels": ["infrastructure", "cicd", "templates"], + "story_points": null + }, + { + "project_slug": "devops-automation", + "sprint_number": 1, + "type": "story", + "title": "Create Terraform modules for AWS ECS", + "body": "As a DevOps engineer, I want Terraform modules for ECS deployments.\n\n## Modules\n- ECS cluster configuration\n- Service and task definitions\n- Load balancer integration\n- Auto-scaling policies", + "status": "in_progress", + "priority": "high", + "labels": ["terraform", "aws", "ecs"], + "story_points": 8, + "assigned_agent_name": "Volt" + }, + { + "project_slug": "devops-automation", + "sprint_number": 1, + "type": "task", + "title": "Set up Gitea Actions runners", + "body": "Configure self-hosted Gitea Actions runners:\n- Docker-in-Docker support\n- Caching for npm/pip\n- Secrets management\n- Resource limits", + "status": "open", + "priority": "high", + "labels": ["infrastructure", "gitea", "cicd"], + "story_points": 5 + }, + { + "project_slug": "devops-automation", + "sprint_number": 1, + "type": "task", + "title": "Implement rollback detection system", + "body": "AI-assisted rollback detection:\n- Monitor deployment health metrics\n- Automatic rollback triggers\n- Notification system\n- Post-rollback analysis", + "status": "open", + "priority": "medium", + "labels": ["ai", "monitoring", "automation"], + "story_points": 8 } ] } diff --git a/frontend/src/lib/api/hooks/useDashboard.ts b/frontend/src/lib/api/hooks/useDashboard.ts index 79a55b7..6d2266f 100644 --- a/frontend/src/lib/api/hooks/useDashboard.ts +++ b/frontend/src/lib/api/hooks/useDashboard.ts @@ -6,13 +6,15 @@ * - Recent projects * - Pending approvals * - * Uses mock data until backend endpoints are available. + * Fetches real data from the API. * * @see Issue #53 */ import { useQuery } from '@tanstack/react-query'; -import type { Project, ProjectStatus } from '@/components/projects/types'; +import { listProjects as listProjectsApi } from '@/lib/api/generated'; +import type { ProjectResponse } from '@/lib/api/generated'; +import type { AutonomyLevel, Project, ProjectStatus } from '@/components/projects/types'; // ============================================================================ // Types @@ -52,118 +54,70 @@ export interface DashboardData { } // ============================================================================ -// Mock Data +// Helpers // ============================================================================ -const mockStats: DashboardStats = { - activeProjects: 3, - runningAgents: 8, - openIssues: 24, - pendingApprovals: 2, -}; +/** + * Format a date string as relative time (e.g., "2 minutes ago") + */ +function formatRelativeTime(dateStr: string): string { + const date = new Date(dateStr); + const now = new Date(); + const diffMs = now.getTime() - date.getTime(); + const diffMins = Math.floor(diffMs / 60000); + const diffHours = Math.floor(diffMins / 60); + const diffDays = Math.floor(diffHours / 24); + const diffWeeks = Math.floor(diffDays / 7); + const diffMonths = Math.floor(diffDays / 30); -const mockProjects: DashboardProject[] = [ - { - id: 'proj-001', - name: 'E-Commerce Platform Redesign', - description: 'Complete redesign of the e-commerce platform with modern UI/UX', - status: 'active' as ProjectStatus, - autonomy_level: 'milestone', - created_at: '2025-11-15T10:00:00Z', - updated_at: '2025-12-30T14:30:00Z', - owner_id: 'user-001', - progress: 67, - openIssues: 12, - activeAgents: 4, - currentSprint: 'Sprint 3', - lastActivity: '2 minutes ago', - }, - { - id: 'proj-002', - name: 'Mobile Banking App', - description: 'Native mobile app for banking services with biometric authentication', - status: 'active' as ProjectStatus, - autonomy_level: 'autonomous', - created_at: '2025-11-20T09:00:00Z', - updated_at: '2025-12-30T12:00:00Z', - owner_id: 'user-001', - progress: 45, - openIssues: 8, - activeAgents: 5, - currentSprint: 'Sprint 2', - lastActivity: '15 minutes ago', - }, - { - id: 'proj-003', - name: 'Internal HR Portal', - description: 'Employee self-service portal for HR operations', - status: 'paused' as ProjectStatus, - autonomy_level: 'full_control', - created_at: '2025-10-01T08:00:00Z', - updated_at: '2025-12-28T16:00:00Z', - owner_id: 'user-001', - progress: 23, - openIssues: 5, - activeAgents: 0, - currentSprint: 'Sprint 1', - lastActivity: '2 days ago', - }, - { - id: 'proj-004', - name: 'API Gateway Modernization', - description: 'Migrate legacy API gateway to cloud-native architecture', - status: 'active' as ProjectStatus, - autonomy_level: 'milestone', - created_at: '2025-12-01T11:00:00Z', - updated_at: '2025-12-30T10:00:00Z', - owner_id: 'user-001', - progress: 82, - openIssues: 3, - activeAgents: 2, - currentSprint: 'Sprint 4', - lastActivity: '1 hour ago', - }, - { - id: 'proj-005', - name: 'Customer Analytics Dashboard', - description: 'Real-time analytics dashboard for customer behavior insights', - status: 'completed' as ProjectStatus, - autonomy_level: 'autonomous', - created_at: '2025-09-01T10:00:00Z', - updated_at: '2025-12-15T17:00:00Z', - owner_id: 'user-001', - progress: 100, - openIssues: 0, - activeAgents: 0, - lastActivity: '2 weeks ago', - }, - { - id: 'proj-006', - name: 'DevOps Pipeline Automation', - description: 'Automate CI/CD pipelines with AI-assisted deployments', - status: 'active' as ProjectStatus, - autonomy_level: 'milestone', - created_at: '2025-12-10T14:00:00Z', - updated_at: '2025-12-30T09:00:00Z', - owner_id: 'user-001', - progress: 35, - openIssues: 6, - activeAgents: 3, - currentSprint: 'Sprint 1', - lastActivity: '30 minutes ago', - }, -]; + if (diffMins < 1) return 'Just now'; + if (diffMins < 60) return `${diffMins} minute${diffMins > 1 ? 's' : ''} ago`; + if (diffHours < 24) return `${diffHours} hour${diffHours > 1 ? 's' : ''} ago`; + if (diffDays < 7) return `${diffDays} day${diffDays > 1 ? 's' : ''} ago`; + if (diffWeeks < 4) return `${diffWeeks} week${diffWeeks > 1 ? 's' : ''} ago`; + return `${diffMonths} month${diffMonths > 1 ? 's' : ''} ago`; +} + +/** + * Maps API ProjectResponse to DashboardProject format + */ +function mapToDashboardProject( + project: ProjectResponse & Record +): DashboardProject { + const updatedAt = project.updated_at || project.created_at || new Date().toISOString(); + const createdAt = project.created_at || new Date().toISOString(); + + return { + id: project.id, + name: project.name, + description: project.description || undefined, + status: project.status as ProjectStatus, + autonomy_level: (project.autonomy_level || 'milestone') as AutonomyLevel, + created_at: createdAt, + updated_at: updatedAt, + owner_id: project.owner_id || 'unknown', + progress: (project.progress as number) || 0, + openIssues: (project.openIssues as number) || project.issue_count || 0, + activeAgents: (project.activeAgents as number) || project.agent_count || 0, + currentSprint: project.active_sprint_name || undefined, + lastActivity: formatRelativeTime(updatedAt), + }; +} + +// ============================================================================ +// Mock Data (for pending approvals - no backend endpoint yet) +// ============================================================================ const mockApprovals: PendingApproval[] = [ { id: 'approval-001', type: 'sprint_boundary', - title: 'Sprint 3 Completion Review', - description: 'Review sprint deliverables and approve transition to Sprint 4', + title: 'Sprint 1 Completion Review', + description: 'Review sprint deliverables and approve transition to Sprint 2', projectId: 'proj-001', projectName: 'E-Commerce Platform Redesign', requestedBy: 'Product Owner Agent', - requestedAt: '2025-12-30T14:00:00Z', + requestedAt: new Date().toISOString(), priority: 'high', }, { @@ -171,10 +125,10 @@ const mockApprovals: PendingApproval[] = [ type: 'architecture_decision', title: 'Database Migration Strategy', description: 'Approve PostgreSQL to CockroachDB migration plan', - projectId: 'proj-004', - projectName: 'API Gateway Modernization', + projectId: 'proj-002', + projectName: 'Mobile Banking App', requestedBy: 'Architect Agent', - requestedAt: '2025-12-30T10:30:00Z', + requestedAt: new Date(Date.now() - 3600000).toISOString(), priority: 'medium', }, ]; @@ -192,17 +146,41 @@ export function useDashboard() { return useQuery({ queryKey: ['dashboard'], queryFn: async () => { - // Simulate network delay - await new Promise((resolve) => setTimeout(resolve, 500)); + // Fetch real projects from API + const response = await listProjectsApi({ + query: { + limit: 6, + }, + }); - // Return mock data - // TODO: Replace with actual API call when backend is ready - // const response = await apiClient.get('/api/v1/dashboard'); - // return response.data; + if (response.error) { + throw new Error('Failed to fetch dashboard data'); + } + + const projects = response.data.data.map((p) => + mapToDashboardProject(p as ProjectResponse & Record) + ); + + // Sort by updated_at (most recent first) + projects.sort( + (a, b) => + new Date(b.updated_at || b.created_at).getTime() - + new Date(a.updated_at || a.created_at).getTime() + ); + + // Calculate stats from real data + const activeProjects = projects.filter((p) => p.status === 'active').length; + const runningAgents = projects.reduce((sum, p) => sum + p.activeAgents, 0); + const openIssues = projects.reduce((sum, p) => sum + p.openIssues, 0); return { - stats: mockStats, - recentProjects: mockProjects, + stats: { + activeProjects, + runningAgents, + openIssues, + pendingApprovals: mockApprovals.length, + }, + recentProjects: projects, pendingApprovals: mockApprovals, }; }, @@ -218,8 +196,24 @@ export function useDashboardStats() { return useQuery({ queryKey: ['dashboard', 'stats'], queryFn: async () => { - await new Promise((resolve) => setTimeout(resolve, 300)); - return mockStats; + const response = await listProjectsApi({ + query: { limit: 100 }, + }); + + if (response.error) { + throw new Error('Failed to fetch stats'); + } + + const projects = response.data.data.map((p) => + mapToDashboardProject(p as ProjectResponse & Record) + ); + + return { + activeProjects: projects.filter((p) => p.status === 'active').length, + runningAgents: projects.reduce((sum, p) => sum + p.activeAgents, 0), + openIssues: projects.reduce((sum, p) => sum + p.openIssues, 0), + pendingApprovals: mockApprovals.length, + }; }, staleTime: 30000, refetchInterval: 60000, @@ -235,8 +229,26 @@ export function useRecentProjects(limit: number = 6) { return useQuery({ queryKey: ['dashboard', 'recentProjects', limit], queryFn: async () => { - await new Promise((resolve) => setTimeout(resolve, 400)); - return mockProjects.slice(0, limit); + const response = await listProjectsApi({ + query: { limit }, + }); + + if (response.error) { + throw new Error('Failed to fetch recent projects'); + } + + const projects = response.data.data.map((p) => + mapToDashboardProject(p as ProjectResponse & Record) + ); + + // Sort by updated_at (most recent first) + projects.sort( + (a, b) => + new Date(b.updated_at || b.created_at).getTime() - + new Date(a.updated_at || a.created_at).getTime() + ); + + return projects; }, staleTime: 30000, }); @@ -249,7 +261,7 @@ export function usePendingApprovals() { return useQuery({ queryKey: ['dashboard', 'pendingApprovals'], queryFn: async () => { - await new Promise((resolve) => setTimeout(resolve, 300)); + // TODO: Fetch from real API when endpoint exists return mockApprovals; }, staleTime: 30000,