forked from cardosofelipe/fast-next-template
feat(dashboard): use real API data and add 3 more demo projects
Dashboard changes: - Update useDashboard hook to fetch real projects from API - Calculate stats (active projects, agents, issues) from real data - Keep pending approvals as mock (no backend endpoint yet) Demo data additions: - API Gateway Modernization project (active, complex) - Customer Analytics Dashboard project (completed) - DevOps Pipeline Automation project (active, complex) - Added sprints, agent instances, and issues for each new project Total demo data: 6 projects, 14 agents, 22 issues 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -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<string, unknown>
|
||||
): 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<DashboardData>({
|
||||
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<string, unknown>)
|
||||
);
|
||||
|
||||
// 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<DashboardStats>({
|
||||
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<string, unknown>)
|
||||
);
|
||||
|
||||
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<DashboardProject[]>({
|
||||
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<string, unknown>)
|
||||
);
|
||||
|
||||
// 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<PendingApproval[]>({
|
||||
queryKey: ['dashboard', 'pendingApprovals'],
|
||||
queryFn: async () => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 300));
|
||||
// TODO: Fetch from real API when endpoint exists
|
||||
return mockApprovals;
|
||||
},
|
||||
staleTime: 30000,
|
||||
|
||||
Reference in New Issue
Block a user