forked from cardosofelipe/fast-next-template
feat(frontend): wire useProjects hook to SDK and enhance MSW handlers
- Regenerate API SDK with 77 endpoints (up from 61) - Update useProjects hook to use SDK's listProjects function - Add comprehensive project mock data for demo mode - Add project CRUD handlers to MSW overrides - Map API response to frontend ProjectListItem format - Fix test files with required slug and autonomyLevel properties 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
@@ -4,12 +4,14 @@
|
||||
* Provides data for the projects list page with filtering,
|
||||
* sorting, and pagination.
|
||||
*
|
||||
* Uses mock data until backend endpoints are available.
|
||||
* Uses SDK to call API (intercepted by MSW in demo mode).
|
||||
*
|
||||
* @see Issue #54
|
||||
*/
|
||||
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { listProjects as listProjectsApi } from '@/lib/api/generated';
|
||||
import type { ProjectStatus as ApiProjectStatus, ProjectResponse } from '@/lib/api/generated';
|
||||
import type { ProjectStatus } from '@/components/projects/types';
|
||||
|
||||
// ============================================================================
|
||||
@@ -19,6 +21,7 @@ import type { ProjectStatus } from '@/components/projects/types';
|
||||
export interface ProjectListItem {
|
||||
id: string;
|
||||
name: string;
|
||||
slug: string;
|
||||
description?: string;
|
||||
status: ProjectStatus;
|
||||
complexity: 'low' | 'medium' | 'high';
|
||||
@@ -33,6 +36,7 @@ export interface ProjectListItem {
|
||||
name: string;
|
||||
};
|
||||
tags?: string[];
|
||||
autonomyLevel: string;
|
||||
}
|
||||
|
||||
export interface ProjectsListParams {
|
||||
@@ -56,134 +60,64 @@ export interface ProjectsListResponse {
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Mock Data
|
||||
// Helpers
|
||||
// ============================================================================
|
||||
|
||||
const mockProjects: ProjectListItem[] = [
|
||||
{
|
||||
id: 'proj-001',
|
||||
name: 'E-Commerce Platform Redesign',
|
||||
description:
|
||||
'Complete redesign of the e-commerce platform with modern UI/UX and improved checkout flow',
|
||||
status: 'active',
|
||||
complexity: 'high',
|
||||
progress: 67,
|
||||
openIssues: 12,
|
||||
activeAgents: 4,
|
||||
currentSprint: 'Sprint 3',
|
||||
lastActivity: '2 minutes ago',
|
||||
createdAt: '2025-11-15T10:00:00Z',
|
||||
owner: { id: 'user-001', name: 'Felipe Cardoso' },
|
||||
tags: ['e-commerce', 'frontend', 'ux'],
|
||||
},
|
||||
{
|
||||
id: 'proj-002',
|
||||
name: 'Mobile Banking App',
|
||||
description:
|
||||
'Native mobile app for banking services with biometric authentication and real-time notifications',
|
||||
status: 'active',
|
||||
complexity: 'high',
|
||||
progress: 45,
|
||||
openIssues: 8,
|
||||
activeAgents: 5,
|
||||
currentSprint: 'Sprint 2',
|
||||
lastActivity: '15 minutes ago',
|
||||
createdAt: '2025-11-20T09:00:00Z',
|
||||
owner: { id: 'user-001', name: 'Felipe Cardoso' },
|
||||
tags: ['mobile', 'fintech', 'security'],
|
||||
},
|
||||
{
|
||||
id: 'proj-003',
|
||||
name: 'Internal HR Portal',
|
||||
description:
|
||||
'Employee self-service portal for HR operations including leave requests and performance reviews',
|
||||
status: 'paused',
|
||||
complexity: 'medium',
|
||||
progress: 23,
|
||||
openIssues: 5,
|
||||
activeAgents: 0,
|
||||
currentSprint: 'Sprint 1',
|
||||
lastActivity: '2 days ago',
|
||||
createdAt: '2025-10-01T08:00:00Z',
|
||||
owner: { id: 'user-002', name: 'Maria Santos' },
|
||||
tags: ['internal', 'hr', 'portal'],
|
||||
},
|
||||
{
|
||||
id: 'proj-004',
|
||||
name: 'API Gateway Modernization',
|
||||
description:
|
||||
'Migrate legacy API gateway to cloud-native architecture with improved rate limiting and caching',
|
||||
status: 'active',
|
||||
complexity: 'high',
|
||||
progress: 82,
|
||||
openIssues: 3,
|
||||
activeAgents: 2,
|
||||
currentSprint: 'Sprint 4',
|
||||
lastActivity: '1 hour ago',
|
||||
createdAt: '2025-12-01T11:00:00Z',
|
||||
owner: { id: 'user-001', name: 'Felipe Cardoso' },
|
||||
tags: ['api', 'cloud', 'infrastructure'],
|
||||
},
|
||||
{
|
||||
id: 'proj-005',
|
||||
name: 'Customer Analytics Dashboard',
|
||||
description:
|
||||
'Real-time analytics dashboard for customer behavior insights with ML-powered predictions',
|
||||
status: 'completed',
|
||||
complexity: 'medium',
|
||||
progress: 100,
|
||||
openIssues: 0,
|
||||
activeAgents: 0,
|
||||
lastActivity: '2 weeks ago',
|
||||
createdAt: '2025-09-01T10:00:00Z',
|
||||
owner: { id: 'user-003', name: 'Alex Johnson' },
|
||||
tags: ['analytics', 'ml', 'dashboard'],
|
||||
},
|
||||
{
|
||||
id: 'proj-006',
|
||||
name: 'DevOps Pipeline Automation',
|
||||
description: 'Automate CI/CD pipelines with AI-assisted deployments and rollback capabilities',
|
||||
status: 'active',
|
||||
complexity: 'medium',
|
||||
progress: 35,
|
||||
openIssues: 6,
|
||||
activeAgents: 3,
|
||||
currentSprint: 'Sprint 1',
|
||||
lastActivity: '30 minutes ago',
|
||||
createdAt: '2025-12-10T14:00:00Z',
|
||||
owner: { id: 'user-001', name: 'Felipe Cardoso' },
|
||||
tags: ['devops', 'automation', 'ci-cd'],
|
||||
},
|
||||
{
|
||||
id: 'proj-007',
|
||||
name: 'Inventory Management System',
|
||||
description: 'Warehouse inventory tracking with barcode scanning and automated reordering',
|
||||
status: 'archived',
|
||||
complexity: 'low',
|
||||
progress: 100,
|
||||
openIssues: 0,
|
||||
activeAgents: 0,
|
||||
lastActivity: '1 month ago',
|
||||
createdAt: '2025-06-15T08:00:00Z',
|
||||
owner: { id: 'user-002', name: 'Maria Santos' },
|
||||
tags: ['inventory', 'warehouse', 'logistics'],
|
||||
},
|
||||
{
|
||||
id: 'proj-008',
|
||||
name: 'Customer Support Chatbot',
|
||||
description: 'AI-powered chatbot for 24/7 customer support with sentiment analysis',
|
||||
status: 'active',
|
||||
complexity: 'medium',
|
||||
progress: 58,
|
||||
openIssues: 4,
|
||||
activeAgents: 2,
|
||||
currentSprint: 'Sprint 2',
|
||||
lastActivity: '45 minutes ago',
|
||||
createdAt: '2025-12-05T09:00:00Z',
|
||||
owner: { id: 'user-003', name: 'Alex Johnson' },
|
||||
tags: ['ai', 'chatbot', 'support'],
|
||||
},
|
||||
];
|
||||
/**
|
||||
* Maps API ProjectResponse to our ProjectListItem format
|
||||
*/
|
||||
function mapProjectResponse(project: ProjectResponse & Record<string, unknown>): ProjectListItem {
|
||||
// Map complexity from mock data format to UI format
|
||||
const rawComplexity = project.complexity as string;
|
||||
let complexity: 'low' | 'medium' | 'high' = 'medium';
|
||||
if (rawComplexity === 'script' || rawComplexity === 'simple' || rawComplexity === 'low') {
|
||||
complexity = 'low';
|
||||
} else if (rawComplexity === 'complex' || rawComplexity === 'high') {
|
||||
complexity = 'high';
|
||||
}
|
||||
|
||||
return {
|
||||
id: project.id,
|
||||
name: project.name,
|
||||
slug: project.slug || project.name.toLowerCase().replace(/\s+/g, '-'),
|
||||
description: project.description || undefined,
|
||||
status: project.status as ProjectStatus,
|
||||
complexity,
|
||||
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: (project.lastActivity as string) || formatRelativeTime(project.updated_at),
|
||||
createdAt: project.created_at,
|
||||
owner: {
|
||||
id: project.owner_id || 'unknown',
|
||||
name: (project.ownerName as string) || 'Unknown',
|
||||
},
|
||||
tags: (project.tags as string[]) || [],
|
||||
autonomyLevel: project.autonomy_level || 'milestone',
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
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`;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Hook
|
||||
@@ -206,35 +140,33 @@ export function useProjects(params: ProjectsListParams = {}) {
|
||||
return useQuery<ProjectsListResponse>({
|
||||
queryKey: ['projects', { search, status, complexity, sortBy, sortOrder, page, limit }],
|
||||
queryFn: async () => {
|
||||
// Simulate network delay
|
||||
await new Promise((resolve) => setTimeout(resolve, 400));
|
||||
// Call API via SDK (MSW intercepts in demo mode)
|
||||
const response = await listProjectsApi({
|
||||
query: {
|
||||
status: status !== 'all' ? (status as ApiProjectStatus) : undefined,
|
||||
search: search || undefined,
|
||||
page,
|
||||
limit,
|
||||
},
|
||||
});
|
||||
|
||||
// Filter projects
|
||||
let filtered = [...mockProjects];
|
||||
|
||||
// Search filter
|
||||
if (search) {
|
||||
const searchLower = search.toLowerCase();
|
||||
filtered = filtered.filter(
|
||||
(p) =>
|
||||
p.name.toLowerCase().includes(searchLower) ||
|
||||
p.description?.toLowerCase().includes(searchLower) ||
|
||||
p.tags?.some((t) => t.toLowerCase().includes(searchLower))
|
||||
);
|
||||
if (response.error) {
|
||||
throw new Error('Failed to fetch projects');
|
||||
}
|
||||
|
||||
// Status filter
|
||||
if (status !== 'all') {
|
||||
filtered = filtered.filter((p) => p.status === status);
|
||||
}
|
||||
// Get raw response data
|
||||
const apiData = response.data;
|
||||
let projects = apiData.data.map((p) =>
|
||||
mapProjectResponse(p as ProjectResponse & Record<string, unknown>)
|
||||
);
|
||||
|
||||
// Complexity filter
|
||||
// Client-side complexity filter (not supported by API)
|
||||
if (complexity !== 'all') {
|
||||
filtered = filtered.filter((p) => p.complexity === complexity);
|
||||
projects = projects.filter((p) => p.complexity === complexity);
|
||||
}
|
||||
|
||||
// Sort
|
||||
filtered.sort((a, b) => {
|
||||
// Client-side sorting
|
||||
projects.sort((a, b) => {
|
||||
let comparison = 0;
|
||||
switch (sortBy) {
|
||||
case 'name':
|
||||
@@ -254,15 +186,12 @@ export function useProjects(params: ProjectsListParams = {}) {
|
||||
return sortOrder === 'asc' ? comparison : -comparison;
|
||||
});
|
||||
|
||||
// Pagination
|
||||
const total = filtered.length;
|
||||
// Calculate pagination
|
||||
const total = apiData.pagination.total;
|
||||
const totalPages = Math.ceil(total / limit);
|
||||
const start = (page - 1) * limit;
|
||||
const end = start + limit;
|
||||
const paginatedData = filtered.slice(start, end);
|
||||
|
||||
return {
|
||||
data: paginatedData,
|
||||
data: projects,
|
||||
pagination: {
|
||||
page,
|
||||
limit,
|
||||
|
||||
266
frontend/src/mocks/data/projects.ts
Normal file
266
frontend/src/mocks/data/projects.ts
Normal file
@@ -0,0 +1,266 @@
|
||||
/**
|
||||
* Mock Project Data for Demo Mode
|
||||
*
|
||||
* Sample projects used by MSW handlers in demo mode.
|
||||
*/
|
||||
|
||||
import type { ProjectResponse, ProjectStatus } from '@/lib/api/generated';
|
||||
|
||||
export interface ProjectListItem extends ProjectResponse {
|
||||
// Extended UI fields (computed/stored separately in real app)
|
||||
complexity?: 'script' | 'simple' | 'medium' | 'complex';
|
||||
progress?: number;
|
||||
openIssues?: number;
|
||||
activeAgents?: number;
|
||||
lastActivity?: string;
|
||||
tags?: string[];
|
||||
ownerName?: string;
|
||||
}
|
||||
|
||||
export const sampleProjects: ProjectListItem[] = [
|
||||
{
|
||||
id: 'proj-001',
|
||||
name: 'E-Commerce Platform Redesign',
|
||||
slug: 'ecommerce-redesign',
|
||||
description: 'Complete redesign of the e-commerce platform with modern UI/UX',
|
||||
autonomy_level: 'milestone',
|
||||
status: 'active',
|
||||
settings: {},
|
||||
owner_id: 'user-001',
|
||||
created_at: '2025-11-15T10:00:00Z',
|
||||
updated_at: new Date(Date.now() - 2 * 60 * 1000).toISOString(),
|
||||
agent_count: 5,
|
||||
issue_count: 70,
|
||||
active_sprint_name: 'Sprint 3',
|
||||
// Extended fields
|
||||
complexity: 'complex',
|
||||
progress: 67,
|
||||
openIssues: 12,
|
||||
activeAgents: 4,
|
||||
lastActivity: '2 minutes ago',
|
||||
tags: ['e-commerce', 'frontend', 'ux'],
|
||||
ownerName: 'Felipe Cardoso',
|
||||
},
|
||||
{
|
||||
id: 'proj-002',
|
||||
name: 'Mobile Banking App',
|
||||
slug: 'mobile-banking',
|
||||
description: 'Native mobile app for banking services with biometric authentication',
|
||||
autonomy_level: 'full_control',
|
||||
status: 'active',
|
||||
settings: {},
|
||||
owner_id: 'user-001',
|
||||
created_at: '2025-11-20T09:00:00Z',
|
||||
updated_at: new Date(Date.now() - 15 * 60 * 1000).toISOString(),
|
||||
agent_count: 5,
|
||||
issue_count: 45,
|
||||
active_sprint_name: 'Sprint 2',
|
||||
complexity: 'complex',
|
||||
progress: 45,
|
||||
openIssues: 8,
|
||||
activeAgents: 5,
|
||||
lastActivity: '15 minutes ago',
|
||||
tags: ['mobile', 'fintech', 'security'],
|
||||
ownerName: 'Felipe Cardoso',
|
||||
},
|
||||
{
|
||||
id: 'proj-003',
|
||||
name: 'Internal HR Portal',
|
||||
slug: 'hr-portal',
|
||||
description: 'Employee self-service portal for HR operations',
|
||||
autonomy_level: 'milestone',
|
||||
status: 'paused',
|
||||
settings: {},
|
||||
owner_id: 'user-002',
|
||||
created_at: '2025-10-01T08:00:00Z',
|
||||
updated_at: new Date(Date.now() - 2 * 24 * 60 * 60 * 1000).toISOString(),
|
||||
agent_count: 3,
|
||||
issue_count: 25,
|
||||
active_sprint_name: 'Sprint 1',
|
||||
complexity: 'medium',
|
||||
progress: 23,
|
||||
openIssues: 5,
|
||||
activeAgents: 0,
|
||||
lastActivity: '2 days ago',
|
||||
tags: ['internal', 'hr', 'portal'],
|
||||
ownerName: 'Maria Santos',
|
||||
},
|
||||
{
|
||||
id: 'proj-004',
|
||||
name: 'API Gateway Modernization',
|
||||
slug: 'api-gateway',
|
||||
description: 'Migrate legacy API gateway to cloud-native architecture',
|
||||
autonomy_level: 'autonomous',
|
||||
status: 'active',
|
||||
settings: {},
|
||||
owner_id: 'user-001',
|
||||
created_at: '2025-12-01T11:00:00Z',
|
||||
updated_at: new Date(Date.now() - 60 * 60 * 1000).toISOString(),
|
||||
agent_count: 3,
|
||||
issue_count: 40,
|
||||
active_sprint_name: 'Sprint 4',
|
||||
complexity: 'complex',
|
||||
progress: 82,
|
||||
openIssues: 3,
|
||||
activeAgents: 2,
|
||||
lastActivity: '1 hour ago',
|
||||
tags: ['api', 'cloud', 'infrastructure'],
|
||||
ownerName: 'Felipe Cardoso',
|
||||
},
|
||||
{
|
||||
id: 'proj-005',
|
||||
name: 'Customer Analytics Dashboard',
|
||||
slug: 'analytics-dashboard',
|
||||
description: 'Real-time analytics dashboard for customer behavior insights',
|
||||
autonomy_level: 'milestone',
|
||||
status: 'completed',
|
||||
settings: {},
|
||||
owner_id: 'user-003',
|
||||
created_at: '2025-09-01T10:00:00Z',
|
||||
updated_at: new Date(Date.now() - 14 * 24 * 60 * 60 * 1000).toISOString(),
|
||||
agent_count: 0,
|
||||
issue_count: 50,
|
||||
active_sprint_name: null,
|
||||
complexity: 'medium',
|
||||
progress: 100,
|
||||
openIssues: 0,
|
||||
activeAgents: 0,
|
||||
lastActivity: '2 weeks ago',
|
||||
tags: ['analytics', 'ml', 'dashboard'],
|
||||
ownerName: 'Alex Johnson',
|
||||
},
|
||||
{
|
||||
id: 'proj-006',
|
||||
name: 'DevOps Pipeline Automation',
|
||||
slug: 'devops-automation',
|
||||
description: 'Automate CI/CD pipelines with AI-assisted deployments',
|
||||
autonomy_level: 'milestone',
|
||||
status: 'active',
|
||||
settings: {},
|
||||
owner_id: 'user-001',
|
||||
created_at: '2025-12-10T14:00:00Z',
|
||||
updated_at: new Date(Date.now() - 30 * 60 * 1000).toISOString(),
|
||||
agent_count: 4,
|
||||
issue_count: 30,
|
||||
active_sprint_name: 'Sprint 1',
|
||||
complexity: 'medium',
|
||||
progress: 35,
|
||||
openIssues: 6,
|
||||
activeAgents: 3,
|
||||
lastActivity: '30 minutes ago',
|
||||
tags: ['devops', 'automation', 'ci-cd'],
|
||||
ownerName: 'Felipe Cardoso',
|
||||
},
|
||||
{
|
||||
id: 'proj-007',
|
||||
name: 'Inventory Management System',
|
||||
slug: 'inventory-system',
|
||||
description: 'Warehouse inventory tracking with barcode scanning',
|
||||
autonomy_level: 'full_control',
|
||||
status: 'archived',
|
||||
settings: {},
|
||||
owner_id: 'user-002',
|
||||
created_at: '2025-06-15T08:00:00Z',
|
||||
updated_at: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString(),
|
||||
agent_count: 0,
|
||||
issue_count: 80,
|
||||
active_sprint_name: null,
|
||||
complexity: 'simple',
|
||||
progress: 100,
|
||||
openIssues: 0,
|
||||
activeAgents: 0,
|
||||
lastActivity: '1 month ago',
|
||||
tags: ['inventory', 'warehouse', 'logistics'],
|
||||
ownerName: 'Maria Santos',
|
||||
},
|
||||
{
|
||||
id: 'proj-008',
|
||||
name: 'Customer Support Chatbot',
|
||||
slug: 'support-chatbot',
|
||||
description: 'AI-powered chatbot for 24/7 customer support',
|
||||
autonomy_level: 'autonomous',
|
||||
status: 'active',
|
||||
settings: {},
|
||||
owner_id: 'user-003',
|
||||
created_at: '2025-12-05T09:00:00Z',
|
||||
updated_at: new Date(Date.now() - 45 * 60 * 1000).toISOString(),
|
||||
agent_count: 3,
|
||||
issue_count: 35,
|
||||
active_sprint_name: 'Sprint 2',
|
||||
complexity: 'medium',
|
||||
progress: 58,
|
||||
openIssues: 4,
|
||||
activeAgents: 2,
|
||||
lastActivity: '45 minutes ago',
|
||||
tags: ['ai', 'chatbot', 'support'],
|
||||
ownerName: 'Alex Johnson',
|
||||
},
|
||||
];
|
||||
|
||||
// In-memory store for demo mode (allows create/update/delete)
|
||||
let projectsStore = [...sampleProjects];
|
||||
|
||||
export function getProjects(): ProjectListItem[] {
|
||||
return projectsStore;
|
||||
}
|
||||
|
||||
export function getProjectById(id: string): ProjectListItem | undefined {
|
||||
return projectsStore.find((p) => p.id === id);
|
||||
}
|
||||
|
||||
export function getProjectBySlug(slug: string): ProjectListItem | undefined {
|
||||
return projectsStore.find((p) => p.slug === slug);
|
||||
}
|
||||
|
||||
export function createProject(data: Partial<ProjectListItem>): ProjectListItem {
|
||||
const newProject: ProjectListItem = {
|
||||
id: `proj-${Date.now()}`,
|
||||
name: data.name || 'New Project',
|
||||
slug: data.slug || data.name?.toLowerCase().replace(/\s+/g, '-') || `project-${Date.now()}`,
|
||||
description: data.description || null,
|
||||
autonomy_level: data.autonomy_level || 'milestone',
|
||||
status: 'active',
|
||||
settings: data.settings || {},
|
||||
owner_id: data.owner_id || 'user-001',
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString(),
|
||||
agent_count: 0,
|
||||
issue_count: 0,
|
||||
active_sprint_name: null,
|
||||
complexity: data.complexity || 'medium',
|
||||
progress: 0,
|
||||
openIssues: 0,
|
||||
activeAgents: 0,
|
||||
lastActivity: 'Just now',
|
||||
tags: data.tags || [],
|
||||
ownerName: 'Demo User',
|
||||
};
|
||||
projectsStore.unshift(newProject);
|
||||
return newProject;
|
||||
}
|
||||
|
||||
export function updateProject(
|
||||
id: string,
|
||||
data: Partial<ProjectListItem>
|
||||
): ProjectListItem | undefined {
|
||||
const index = projectsStore.findIndex((p) => p.id === id);
|
||||
if (index === -1) return undefined;
|
||||
|
||||
projectsStore[index] = {
|
||||
...projectsStore[index],
|
||||
...data,
|
||||
updated_at: new Date().toISOString(),
|
||||
};
|
||||
return projectsStore[index];
|
||||
}
|
||||
|
||||
export function deleteProject(id: string): boolean {
|
||||
const index = projectsStore.findIndex((p) => p.id === id);
|
||||
if (index === -1) return false;
|
||||
projectsStore.splice(index, 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
export function resetProjects(): void {
|
||||
projectsStore = [...sampleProjects];
|
||||
}
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
* For custom handler behavior, use src/mocks/handlers/overrides.ts
|
||||
*
|
||||
* Generated: 2025-12-30T02:14:59.598Z
|
||||
* Generated: 2026-01-03T01:13:34.961Z
|
||||
*/
|
||||
|
||||
import { http, HttpResponse, delay } from 'msw';
|
||||
@@ -603,4 +603,544 @@ export const generatedHandlers = [
|
||||
message: 'Operation successful'
|
||||
});
|
||||
}),
|
||||
|
||||
/**
|
||||
* Create Project
|
||||
*/
|
||||
http.post(`${API_BASE_URL}/api/v1/projects`, async ({ request, params }) => {
|
||||
await delay(NETWORK_DELAY);
|
||||
|
||||
return HttpResponse.json({
|
||||
success: true,
|
||||
message: 'Operation successful'
|
||||
});
|
||||
}),
|
||||
|
||||
/**
|
||||
* List Projects
|
||||
*/
|
||||
http.get(`${API_BASE_URL}/api/v1/projects`, async ({ request, params }) => {
|
||||
await delay(NETWORK_DELAY);
|
||||
|
||||
return HttpResponse.json({
|
||||
success: true,
|
||||
message: 'Operation successful'
|
||||
});
|
||||
}),
|
||||
|
||||
/**
|
||||
* Get Project
|
||||
*/
|
||||
http.get(`${API_BASE_URL}/api/v1/projects/:project_id`, async ({ request, params }) => {
|
||||
await delay(NETWORK_DELAY);
|
||||
|
||||
return HttpResponse.json({
|
||||
success: true,
|
||||
message: 'Operation successful'
|
||||
});
|
||||
}),
|
||||
|
||||
/**
|
||||
* Update Project
|
||||
*/
|
||||
http.patch(`${API_BASE_URL}/api/v1/projects/:project_id`, async ({ request, params }) => {
|
||||
await delay(NETWORK_DELAY);
|
||||
|
||||
return HttpResponse.json({
|
||||
success: true,
|
||||
message: 'Operation successful'
|
||||
});
|
||||
}),
|
||||
|
||||
/**
|
||||
* Archive Project
|
||||
*/
|
||||
http.delete(`${API_BASE_URL}/api/v1/projects/:project_id`, async ({ request, params }) => {
|
||||
await delay(NETWORK_DELAY);
|
||||
|
||||
return HttpResponse.json({
|
||||
success: true,
|
||||
message: 'Operation successful'
|
||||
});
|
||||
}),
|
||||
|
||||
/**
|
||||
* Get Project by Slug
|
||||
*/
|
||||
http.get(`${API_BASE_URL}/api/v1/projects/slug/:slug`, async ({ request, params }) => {
|
||||
await delay(NETWORK_DELAY);
|
||||
|
||||
return HttpResponse.json({
|
||||
success: true,
|
||||
message: 'Operation successful'
|
||||
});
|
||||
}),
|
||||
|
||||
/**
|
||||
* Pause Project
|
||||
*/
|
||||
http.post(`${API_BASE_URL}/api/v1/projects/:project_id/pause`, async ({ request, params }) => {
|
||||
await delay(NETWORK_DELAY);
|
||||
|
||||
return HttpResponse.json({
|
||||
success: true,
|
||||
message: 'Operation successful'
|
||||
});
|
||||
}),
|
||||
|
||||
/**
|
||||
* Resume Project
|
||||
*/
|
||||
http.post(`${API_BASE_URL}/api/v1/projects/:project_id/resume`, async ({ request, params }) => {
|
||||
await delay(NETWORK_DELAY);
|
||||
|
||||
return HttpResponse.json({
|
||||
success: true,
|
||||
message: 'Operation successful'
|
||||
});
|
||||
}),
|
||||
|
||||
/**
|
||||
* Create Agent Type
|
||||
*/
|
||||
http.post(`${API_BASE_URL}/api/v1/agent-types`, async ({ request, params }) => {
|
||||
await delay(NETWORK_DELAY);
|
||||
|
||||
return HttpResponse.json({
|
||||
success: true,
|
||||
message: 'Operation successful'
|
||||
});
|
||||
}),
|
||||
|
||||
/**
|
||||
* List Agent Types
|
||||
*/
|
||||
http.get(`${API_BASE_URL}/api/v1/agent-types`, async ({ request, params }) => {
|
||||
await delay(NETWORK_DELAY);
|
||||
|
||||
return HttpResponse.json({
|
||||
success: true,
|
||||
message: 'Operation successful'
|
||||
});
|
||||
}),
|
||||
|
||||
/**
|
||||
* Update Agent Type
|
||||
*/
|
||||
http.patch(`${API_BASE_URL}/api/v1/agent-types/:agent_type_id`, async ({ request, params }) => {
|
||||
await delay(NETWORK_DELAY);
|
||||
|
||||
return HttpResponse.json({
|
||||
success: true,
|
||||
message: 'Operation successful'
|
||||
});
|
||||
}),
|
||||
|
||||
/**
|
||||
* Deactivate Agent Type
|
||||
*/
|
||||
http.delete(`${API_BASE_URL}/api/v1/agent-types/:agent_type_id`, async ({ request, params }) => {
|
||||
await delay(NETWORK_DELAY);
|
||||
|
||||
return HttpResponse.json({
|
||||
success: true,
|
||||
message: 'Operation successful'
|
||||
});
|
||||
}),
|
||||
|
||||
/**
|
||||
* Get Agent Type
|
||||
*/
|
||||
http.get(`${API_BASE_URL}/api/v1/agent-types/:agent_type_id`, async ({ request, params }) => {
|
||||
await delay(NETWORK_DELAY);
|
||||
|
||||
return HttpResponse.json({
|
||||
success: true,
|
||||
message: 'Operation successful'
|
||||
});
|
||||
}),
|
||||
|
||||
/**
|
||||
* Get Agent Type by Slug
|
||||
*/
|
||||
http.get(`${API_BASE_URL}/api/v1/agent-types/slug/:slug`, async ({ request, params }) => {
|
||||
await delay(NETWORK_DELAY);
|
||||
|
||||
return HttpResponse.json({
|
||||
success: true,
|
||||
message: 'Operation successful'
|
||||
});
|
||||
}),
|
||||
|
||||
/**
|
||||
* Create Issue
|
||||
*/
|
||||
http.post(`${API_BASE_URL}/api/v1/projects/:project_id/issues`, async ({ request, params }) => {
|
||||
await delay(NETWORK_DELAY);
|
||||
|
||||
return HttpResponse.json({
|
||||
success: true,
|
||||
message: 'Operation successful'
|
||||
});
|
||||
}),
|
||||
|
||||
/**
|
||||
* List Issues
|
||||
*/
|
||||
http.get(`${API_BASE_URL}/api/v1/projects/:project_id/issues`, async ({ request, params }) => {
|
||||
await delay(NETWORK_DELAY);
|
||||
|
||||
return HttpResponse.json({
|
||||
success: true,
|
||||
message: 'Operation successful'
|
||||
});
|
||||
}),
|
||||
|
||||
/**
|
||||
* Get Issue Statistics
|
||||
*/
|
||||
http.get(`${API_BASE_URL}/api/v1/projects/:project_id/issues/stats`, async ({ request, params }) => {
|
||||
await delay(NETWORK_DELAY);
|
||||
|
||||
return HttpResponse.json({
|
||||
success: true,
|
||||
message: 'Operation successful'
|
||||
});
|
||||
}),
|
||||
|
||||
/**
|
||||
* Get Issue
|
||||
*/
|
||||
http.get(`${API_BASE_URL}/api/v1/projects/:project_id/issues/:issue_id`, async ({ request, params }) => {
|
||||
await delay(NETWORK_DELAY);
|
||||
|
||||
return HttpResponse.json({
|
||||
success: true,
|
||||
message: 'Operation successful'
|
||||
});
|
||||
}),
|
||||
|
||||
/**
|
||||
* Update Issue
|
||||
*/
|
||||
http.patch(`${API_BASE_URL}/api/v1/projects/:project_id/issues/:issue_id`, async ({ request, params }) => {
|
||||
await delay(NETWORK_DELAY);
|
||||
|
||||
return HttpResponse.json({
|
||||
success: true,
|
||||
message: 'Operation successful'
|
||||
});
|
||||
}),
|
||||
|
||||
/**
|
||||
* Delete Issue
|
||||
*/
|
||||
http.delete(`${API_BASE_URL}/api/v1/projects/:project_id/issues/:issue_id`, async ({ request, params }) => {
|
||||
await delay(NETWORK_DELAY);
|
||||
|
||||
return HttpResponse.json({
|
||||
success: true,
|
||||
message: 'Operation successful'
|
||||
});
|
||||
}),
|
||||
|
||||
/**
|
||||
* Assign Issue
|
||||
*/
|
||||
http.post(`${API_BASE_URL}/api/v1/projects/:project_id/issues/:issue_id/assign`, async ({ request, params }) => {
|
||||
await delay(NETWORK_DELAY);
|
||||
|
||||
return HttpResponse.json({
|
||||
success: true,
|
||||
message: 'Operation successful'
|
||||
});
|
||||
}),
|
||||
|
||||
/**
|
||||
* Unassign Issue
|
||||
*/
|
||||
http.delete(`${API_BASE_URL}/api/v1/projects/:project_id/issues/:issue_id/assignment`, async ({ request, params }) => {
|
||||
await delay(NETWORK_DELAY);
|
||||
|
||||
return HttpResponse.json({
|
||||
success: true,
|
||||
message: 'Operation successful'
|
||||
});
|
||||
}),
|
||||
|
||||
/**
|
||||
* Trigger Issue Sync
|
||||
*/
|
||||
http.post(`${API_BASE_URL}/api/v1/projects/:project_id/issues/:issue_id/sync`, async ({ request, params }) => {
|
||||
await delay(NETWORK_DELAY);
|
||||
|
||||
return HttpResponse.json({
|
||||
success: true,
|
||||
message: 'Operation successful'
|
||||
});
|
||||
}),
|
||||
|
||||
/**
|
||||
* Spawn Agent Instance
|
||||
*/
|
||||
http.post(`${API_BASE_URL}/api/v1/projects/:project_id/agents`, async ({ request, params }) => {
|
||||
await delay(NETWORK_DELAY);
|
||||
|
||||
return HttpResponse.json({
|
||||
success: true,
|
||||
message: 'Operation successful'
|
||||
});
|
||||
}),
|
||||
|
||||
/**
|
||||
* List Project Agents
|
||||
*/
|
||||
http.get(`${API_BASE_URL}/api/v1/projects/:project_id/agents`, async ({ request, params }) => {
|
||||
await delay(NETWORK_DELAY);
|
||||
|
||||
return HttpResponse.json({
|
||||
success: true,
|
||||
message: 'Operation successful'
|
||||
});
|
||||
}),
|
||||
|
||||
/**
|
||||
* Get Project Agent Metrics
|
||||
*/
|
||||
http.get(`${API_BASE_URL}/api/v1/projects/:project_id/agents/metrics`, async ({ request, params }) => {
|
||||
await delay(NETWORK_DELAY);
|
||||
|
||||
return HttpResponse.json({
|
||||
success: true,
|
||||
message: 'Operation successful'
|
||||
});
|
||||
}),
|
||||
|
||||
/**
|
||||
* Get Agent Details
|
||||
*/
|
||||
http.get(`${API_BASE_URL}/api/v1/projects/:project_id/agents/:agent_id`, async ({ request, params }) => {
|
||||
await delay(NETWORK_DELAY);
|
||||
|
||||
return HttpResponse.json({
|
||||
success: true,
|
||||
message: 'Operation successful'
|
||||
});
|
||||
}),
|
||||
|
||||
/**
|
||||
* Update Agent
|
||||
*/
|
||||
http.patch(`${API_BASE_URL}/api/v1/projects/:project_id/agents/:agent_id`, async ({ request, params }) => {
|
||||
await delay(NETWORK_DELAY);
|
||||
|
||||
return HttpResponse.json({
|
||||
success: true,
|
||||
message: 'Operation successful'
|
||||
});
|
||||
}),
|
||||
|
||||
/**
|
||||
* Terminate Agent
|
||||
*/
|
||||
http.delete(`${API_BASE_URL}/api/v1/projects/:project_id/agents/:agent_id`, async ({ request, params }) => {
|
||||
await delay(NETWORK_DELAY);
|
||||
|
||||
return HttpResponse.json({
|
||||
success: true,
|
||||
message: 'Operation successful'
|
||||
});
|
||||
}),
|
||||
|
||||
/**
|
||||
* Pause Agent
|
||||
*/
|
||||
http.post(`${API_BASE_URL}/api/v1/projects/:project_id/agents/:agent_id/pause`, async ({ request, params }) => {
|
||||
await delay(NETWORK_DELAY);
|
||||
|
||||
return HttpResponse.json({
|
||||
success: true,
|
||||
message: 'Operation successful'
|
||||
});
|
||||
}),
|
||||
|
||||
/**
|
||||
* Resume Agent
|
||||
*/
|
||||
http.post(`${API_BASE_URL}/api/v1/projects/:project_id/agents/:agent_id/resume`, async ({ request, params }) => {
|
||||
await delay(NETWORK_DELAY);
|
||||
|
||||
return HttpResponse.json({
|
||||
success: true,
|
||||
message: 'Operation successful'
|
||||
});
|
||||
}),
|
||||
|
||||
/**
|
||||
* Get Agent Metrics
|
||||
*/
|
||||
http.get(`${API_BASE_URL}/api/v1/projects/:project_id/agents/:agent_id/metrics`, async ({ request, params }) => {
|
||||
await delay(NETWORK_DELAY);
|
||||
|
||||
return HttpResponse.json({
|
||||
success: true,
|
||||
message: 'Operation successful'
|
||||
});
|
||||
}),
|
||||
|
||||
/**
|
||||
* Create Sprint
|
||||
*/
|
||||
http.post(`${API_BASE_URL}/api/v1/projects/:project_id/sprints`, async ({ request, params }) => {
|
||||
await delay(NETWORK_DELAY);
|
||||
|
||||
return HttpResponse.json({
|
||||
success: true,
|
||||
message: 'Operation successful'
|
||||
});
|
||||
}),
|
||||
|
||||
/**
|
||||
* List Sprints
|
||||
*/
|
||||
http.get(`${API_BASE_URL}/api/v1/projects/:project_id/sprints`, async ({ request, params }) => {
|
||||
await delay(NETWORK_DELAY);
|
||||
|
||||
return HttpResponse.json({
|
||||
success: true,
|
||||
message: 'Operation successful'
|
||||
});
|
||||
}),
|
||||
|
||||
/**
|
||||
* Get Active Sprint
|
||||
*/
|
||||
http.get(`${API_BASE_URL}/api/v1/projects/:project_id/sprints/active`, async ({ request, params }) => {
|
||||
await delay(NETWORK_DELAY);
|
||||
|
||||
return HttpResponse.json({
|
||||
success: true,
|
||||
message: 'Operation successful'
|
||||
});
|
||||
}),
|
||||
|
||||
/**
|
||||
* Get Project Velocity
|
||||
*/
|
||||
http.get(`${API_BASE_URL}/api/v1/projects/:project_id/sprints/velocity`, async ({ request, params }) => {
|
||||
await delay(NETWORK_DELAY);
|
||||
|
||||
return HttpResponse.json({
|
||||
success: true,
|
||||
message: 'Operation successful'
|
||||
});
|
||||
}),
|
||||
|
||||
/**
|
||||
* Get Sprint Details
|
||||
*/
|
||||
http.get(`${API_BASE_URL}/api/v1/projects/:project_id/sprints/:sprint_id`, async ({ request, params }) => {
|
||||
await delay(NETWORK_DELAY);
|
||||
|
||||
return HttpResponse.json({
|
||||
success: true,
|
||||
message: 'Operation successful'
|
||||
});
|
||||
}),
|
||||
|
||||
/**
|
||||
* Update Sprint
|
||||
*/
|
||||
http.patch(`${API_BASE_URL}/api/v1/projects/:project_id/sprints/:sprint_id`, async ({ request, params }) => {
|
||||
await delay(NETWORK_DELAY);
|
||||
|
||||
return HttpResponse.json({
|
||||
success: true,
|
||||
message: 'Operation successful'
|
||||
});
|
||||
}),
|
||||
|
||||
/**
|
||||
* Delete Sprint
|
||||
*/
|
||||
http.delete(`${API_BASE_URL}/api/v1/projects/:project_id/sprints/:sprint_id`, async ({ request, params }) => {
|
||||
await delay(NETWORK_DELAY);
|
||||
|
||||
return HttpResponse.json({
|
||||
success: true,
|
||||
message: 'Operation successful'
|
||||
});
|
||||
}),
|
||||
|
||||
/**
|
||||
* Start Sprint
|
||||
*/
|
||||
http.post(`${API_BASE_URL}/api/v1/projects/:project_id/sprints/:sprint_id/start`, async ({ request, params }) => {
|
||||
await delay(NETWORK_DELAY);
|
||||
|
||||
return HttpResponse.json({
|
||||
success: true,
|
||||
message: 'Operation successful'
|
||||
});
|
||||
}),
|
||||
|
||||
/**
|
||||
* Complete Sprint
|
||||
*/
|
||||
http.post(`${API_BASE_URL}/api/v1/projects/:project_id/sprints/:sprint_id/complete`, async ({ request, params }) => {
|
||||
await delay(NETWORK_DELAY);
|
||||
|
||||
return HttpResponse.json({
|
||||
success: true,
|
||||
message: 'Operation successful'
|
||||
});
|
||||
}),
|
||||
|
||||
/**
|
||||
* Cancel Sprint
|
||||
*/
|
||||
http.post(`${API_BASE_URL}/api/v1/projects/:project_id/sprints/:sprint_id/cancel`, async ({ request, params }) => {
|
||||
await delay(NETWORK_DELAY);
|
||||
|
||||
return HttpResponse.json({
|
||||
success: true,
|
||||
message: 'Operation successful'
|
||||
});
|
||||
}),
|
||||
|
||||
/**
|
||||
* Get Sprint Issues
|
||||
*/
|
||||
http.get(`${API_BASE_URL}/api/v1/projects/:project_id/sprints/:sprint_id/issues`, async ({ request, params }) => {
|
||||
await delay(NETWORK_DELAY);
|
||||
|
||||
return HttpResponse.json({
|
||||
success: true,
|
||||
message: 'Operation successful'
|
||||
});
|
||||
}),
|
||||
|
||||
/**
|
||||
* Add Issue to Sprint
|
||||
*/
|
||||
http.post(`${API_BASE_URL}/api/v1/projects/:project_id/sprints/:sprint_id/issues`, async ({ request, params }) => {
|
||||
await delay(NETWORK_DELAY);
|
||||
|
||||
return HttpResponse.json({
|
||||
success: true,
|
||||
message: 'Operation successful'
|
||||
});
|
||||
}),
|
||||
|
||||
/**
|
||||
* Remove Issue from Sprint
|
||||
*/
|
||||
http.delete(`${API_BASE_URL}/api/v1/projects/:project_id/sprints/:sprint_id/issues`, async ({ request, params }) => {
|
||||
await delay(NETWORK_DELAY);
|
||||
|
||||
return HttpResponse.json({
|
||||
success: true,
|
||||
message: 'Operation successful'
|
||||
});
|
||||
}),
|
||||
];
|
||||
|
||||
@@ -14,6 +14,14 @@
|
||||
import { http, HttpResponse, delay } from 'msw';
|
||||
import { generateMockToken } from '../utils/tokens';
|
||||
import { validateCredentials, setCurrentUser, currentUser } from '../data/users';
|
||||
import {
|
||||
getProjects,
|
||||
getProjectById,
|
||||
getProjectBySlug,
|
||||
createProject,
|
||||
updateProject,
|
||||
deleteProject,
|
||||
} from '../data/projects';
|
||||
import config from '@/config/app.config';
|
||||
|
||||
const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL || 'http://localhost:8000';
|
||||
@@ -182,4 +190,164 @@ export const overrideHandlers = [
|
||||
accounts: [],
|
||||
});
|
||||
}),
|
||||
|
||||
// ============================================
|
||||
// PROJECT HANDLERS
|
||||
// ============================================
|
||||
|
||||
/**
|
||||
* List Projects
|
||||
* Returns paginated list of projects with filtering support
|
||||
*/
|
||||
http.get(`${API_BASE_URL}/api/v1/projects`, async ({ request }) => {
|
||||
await delay(NETWORK_DELAY);
|
||||
|
||||
const url = new URL(request.url);
|
||||
const status = url.searchParams.get('status');
|
||||
const search = url.searchParams.get('search');
|
||||
const skip = parseInt(url.searchParams.get('skip') || '0');
|
||||
const limit = parseInt(url.searchParams.get('limit') || '20');
|
||||
|
||||
let projects = getProjects();
|
||||
|
||||
// Filter by status
|
||||
if (status && status !== 'all') {
|
||||
projects = projects.filter((p) => p.status === status);
|
||||
}
|
||||
|
||||
// Filter by search term
|
||||
if (search) {
|
||||
const searchLower = search.toLowerCase();
|
||||
projects = projects.filter(
|
||||
(p) =>
|
||||
p.name.toLowerCase().includes(searchLower) ||
|
||||
p.description?.toLowerCase().includes(searchLower)
|
||||
);
|
||||
}
|
||||
|
||||
const total = projects.length;
|
||||
const paginatedProjects = projects.slice(skip, skip + limit);
|
||||
const totalPages = Math.ceil(total / limit);
|
||||
|
||||
return HttpResponse.json({
|
||||
data: paginatedProjects,
|
||||
pagination: {
|
||||
total,
|
||||
page: Math.floor(skip / limit) + 1,
|
||||
limit,
|
||||
total_pages: totalPages,
|
||||
has_next: skip + limit < total,
|
||||
has_prev: skip > 0,
|
||||
},
|
||||
});
|
||||
}),
|
||||
|
||||
/**
|
||||
* Create Project
|
||||
*/
|
||||
http.post(`${API_BASE_URL}/api/v1/projects`, async ({ request }) => {
|
||||
await delay(NETWORK_DELAY);
|
||||
|
||||
const body = (await request.json()) as any;
|
||||
const newProject = createProject(body);
|
||||
|
||||
return HttpResponse.json(newProject, { status: 201 });
|
||||
}),
|
||||
|
||||
/**
|
||||
* Get Project by ID
|
||||
*/
|
||||
http.get(`${API_BASE_URL}/api/v1/projects/:projectId`, async ({ params }) => {
|
||||
await delay(NETWORK_DELAY);
|
||||
|
||||
const { projectId } = params;
|
||||
const project = getProjectById(projectId as string);
|
||||
|
||||
if (!project) {
|
||||
return HttpResponse.json({ detail: 'Project not found' }, { status: 404 });
|
||||
}
|
||||
|
||||
return HttpResponse.json(project);
|
||||
}),
|
||||
|
||||
/**
|
||||
* Get Project by Slug
|
||||
*/
|
||||
http.get(`${API_BASE_URL}/api/v1/projects/slug/:slug`, async ({ params }) => {
|
||||
await delay(NETWORK_DELAY);
|
||||
|
||||
const { slug } = params;
|
||||
const project = getProjectBySlug(slug as string);
|
||||
|
||||
if (!project) {
|
||||
return HttpResponse.json({ detail: 'Project not found' }, { status: 404 });
|
||||
}
|
||||
|
||||
return HttpResponse.json(project);
|
||||
}),
|
||||
|
||||
/**
|
||||
* Update Project
|
||||
*/
|
||||
http.patch(`${API_BASE_URL}/api/v1/projects/:projectId`, async ({ params, request }) => {
|
||||
await delay(NETWORK_DELAY);
|
||||
|
||||
const { projectId } = params;
|
||||
const body = (await request.json()) as any;
|
||||
const updated = updateProject(projectId as string, body);
|
||||
|
||||
if (!updated) {
|
||||
return HttpResponse.json({ detail: 'Project not found' }, { status: 404 });
|
||||
}
|
||||
|
||||
return HttpResponse.json(updated);
|
||||
}),
|
||||
|
||||
/**
|
||||
* Archive (Delete) Project
|
||||
*/
|
||||
http.delete(`${API_BASE_URL}/api/v1/projects/:projectId`, async ({ params }) => {
|
||||
await delay(NETWORK_DELAY);
|
||||
|
||||
const { projectId } = params;
|
||||
const deleted = deleteProject(projectId as string);
|
||||
|
||||
if (!deleted) {
|
||||
return HttpResponse.json({ detail: 'Project not found' }, { status: 404 });
|
||||
}
|
||||
|
||||
return HttpResponse.json({ message: 'Project archived successfully' });
|
||||
}),
|
||||
|
||||
/**
|
||||
* Pause Project
|
||||
*/
|
||||
http.post(`${API_BASE_URL}/api/v1/projects/:projectId/pause`, async ({ params }) => {
|
||||
await delay(NETWORK_DELAY);
|
||||
|
||||
const { projectId } = params;
|
||||
const updated = updateProject(projectId as string, { status: 'paused' as any });
|
||||
|
||||
if (!updated) {
|
||||
return HttpResponse.json({ detail: 'Project not found' }, { status: 404 });
|
||||
}
|
||||
|
||||
return HttpResponse.json(updated);
|
||||
}),
|
||||
|
||||
/**
|
||||
* Resume Project
|
||||
*/
|
||||
http.post(`${API_BASE_URL}/api/v1/projects/:projectId/resume`, async ({ params }) => {
|
||||
await delay(NETWORK_DELAY);
|
||||
|
||||
const { projectId } = params;
|
||||
const updated = updateProject(projectId as string, { status: 'active' as any });
|
||||
|
||||
if (!updated) {
|
||||
return HttpResponse.json({ detail: 'Project not found' }, { status: 404 });
|
||||
}
|
||||
|
||||
return HttpResponse.json(updated);
|
||||
}),
|
||||
];
|
||||
|
||||
@@ -10,6 +10,7 @@ describe('ProjectCard', () => {
|
||||
const mockProject: ProjectListItem = {
|
||||
id: 'proj-1',
|
||||
name: 'Test Project',
|
||||
slug: 'test-project',
|
||||
description: 'This is a test project description',
|
||||
status: 'active',
|
||||
complexity: 'medium',
|
||||
@@ -21,6 +22,7 @@ describe('ProjectCard', () => {
|
||||
createdAt: '2025-01-01T00:00:00Z',
|
||||
owner: { id: 'user-1', name: 'Test User' },
|
||||
tags: ['frontend', 'react', 'typescript'],
|
||||
autonomyLevel: 'milestone',
|
||||
};
|
||||
|
||||
it('renders project name', () => {
|
||||
|
||||
@@ -18,6 +18,7 @@ describe('ProjectsGrid', () => {
|
||||
{
|
||||
id: 'proj-1',
|
||||
name: 'Project One',
|
||||
slug: 'project-one',
|
||||
description: 'First project',
|
||||
status: 'active',
|
||||
complexity: 'medium',
|
||||
@@ -27,10 +28,12 @@ describe('ProjectsGrid', () => {
|
||||
lastActivity: '5 min ago',
|
||||
createdAt: '2025-01-01T00:00:00Z',
|
||||
owner: { id: 'user-1', name: 'User One' },
|
||||
autonomyLevel: 'milestone',
|
||||
},
|
||||
{
|
||||
id: 'proj-2',
|
||||
name: 'Project Two',
|
||||
slug: 'project-two',
|
||||
description: 'Second project',
|
||||
status: 'paused',
|
||||
complexity: 'high',
|
||||
@@ -40,6 +43,7 @@ describe('ProjectsGrid', () => {
|
||||
lastActivity: '1 day ago',
|
||||
createdAt: '2025-01-02T00:00:00Z',
|
||||
owner: { id: 'user-2', name: 'User Two' },
|
||||
autonomyLevel: 'full_control',
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user