feat(frontend): Implement Phase 1 - ProjectManagement API Client & Hooks

Add complete API integration for ProjectManagement module:
- Epics, Stories, Tasks API clients
- React Query hooks for all entities
- Updated type definitions to match backend API
- API test page for connection verification

Changes:
- Update lib/api/config.ts: Add all ProjectManagement endpoints
- Update types/project.ts: Match backend API models (Epic, Story, Task)
- Create lib/api/pm.ts: API clients for Epics, Stories, Tasks
- Create lib/hooks/use-epics.ts: React Query hooks for Epic CRUD
- Create lib/hooks/use-stories.ts: React Query hooks for Story CRUD
- Create lib/hooks/use-tasks.ts: React Query hooks for Task CRUD
- Create app/(dashboard)/api-test/page.tsx: API connection test page

Features:
- Full CRUD operations for Epics, Stories, Tasks
- Status change and assignment operations
- Optimistic updates for better UX
- Error handling with toast notifications
- Query invalidation for cache consistency

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Yaojia Wang
2025-11-04 20:58:59 +01:00
parent de697d436b
commit e52c8300de
7 changed files with 901 additions and 39 deletions

View File

@@ -1,4 +1,4 @@
export const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:5000';
export const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:5167';
export const API_ENDPOINTS = {
// Auth
@@ -17,7 +17,25 @@ export const API_ENDPOINTS = {
ASSIGN_ROLE: (tenantId: string, userId: string) =>
`/api/tenants/${tenantId}/users/${userId}/role`,
// Projects (to be implemented)
PROJECTS: '/api/projects',
PROJECT: (id: string) => `/api/projects/${id}`,
// Projects
PROJECTS: '/api/v1/projects',
PROJECT: (id: string) => `/api/v1/projects/${id}`,
// Epics
EPICS: '/api/v1/epics',
EPIC: (id: string) => `/api/v1/epics/${id}`,
EPIC_STATUS: (id: string) => `/api/v1/epics/${id}/status`,
EPIC_ASSIGN: (id: string) => `/api/v1/epics/${id}/assign`,
// Stories
STORIES: '/api/v1/stories',
STORY: (id: string) => `/api/v1/stories/${id}`,
STORY_STATUS: (id: string) => `/api/v1/stories/${id}/status`,
STORY_ASSIGN: (id: string) => `/api/v1/stories/${id}/assign`,
// Tasks
TASKS: '/api/v1/tasks',
TASK: (id: string) => `/api/v1/tasks/${id}`,
TASK_STATUS: (id: string) => `/api/v1/tasks/${id}/status`,
TASK_ASSIGN: (id: string) => `/api/v1/tasks/${id}/assign`,
};

109
lib/api/pm.ts Normal file
View File

@@ -0,0 +1,109 @@
import { api } from './client';
import type {
Epic,
CreateEpicDto,
UpdateEpicDto,
Story,
CreateStoryDto,
UpdateStoryDto,
Task,
CreateTaskDto,
UpdateTaskDto,
WorkItemStatus,
} from '@/types/project';
// ==================== Epics API ====================
export const epicsApi = {
list: async (projectId?: string): Promise<Epic[]> => {
const params = projectId ? { projectId } : undefined;
return api.get('/api/v1/epics', { params });
},
get: async (id: string): Promise<Epic> => {
return api.get(`/api/v1/epics/${id}`);
},
create: async (data: CreateEpicDto): Promise<Epic> => {
return api.post('/api/v1/epics', data);
},
update: async (id: string, data: UpdateEpicDto): Promise<Epic> => {
return api.put(`/api/v1/epics/${id}`, data);
},
delete: async (id: string): Promise<void> => {
return api.delete(`/api/v1/epics/${id}`);
},
changeStatus: async (id: string, status: WorkItemStatus): Promise<Epic> => {
return api.put(`/api/v1/epics/${id}/status`, { status });
},
assign: async (id: string, assigneeId: string): Promise<Epic> => {
return api.put(`/api/v1/epics/${id}/assign`, { assigneeId });
},
};
// ==================== Stories API ====================
export const storiesApi = {
list: async (epicId?: string): Promise<Story[]> => {
const params = epicId ? { epicId } : undefined;
return api.get('/api/v1/stories', { params });
},
get: async (id: string): Promise<Story> => {
return api.get(`/api/v1/stories/${id}`);
},
create: async (data: CreateStoryDto): Promise<Story> => {
return api.post('/api/v1/stories', data);
},
update: async (id: string, data: UpdateStoryDto): Promise<Story> => {
return api.put(`/api/v1/stories/${id}`, data);
},
delete: async (id: string): Promise<void> => {
return api.delete(`/api/v1/stories/${id}`);
},
changeStatus: async (id: string, status: WorkItemStatus): Promise<Story> => {
return api.put(`/api/v1/stories/${id}/status`, { status });
},
assign: async (id: string, assigneeId: string): Promise<Story> => {
return api.put(`/api/v1/stories/${id}/assign`, { assigneeId });
},
};
// ==================== Tasks API ====================
export const tasksApi = {
list: async (storyId?: string): Promise<Task[]> => {
const params = storyId ? { storyId } : undefined;
return api.get('/api/v1/tasks', { params });
},
get: async (id: string): Promise<Task> => {
return api.get(`/api/v1/tasks/${id}`);
},
create: async (data: CreateTaskDto): Promise<Task> => {
return api.post('/api/v1/tasks', data);
},
update: async (id: string, data: UpdateTaskDto): Promise<Task> => {
return api.put(`/api/v1/tasks/${id}`, data);
},
delete: async (id: string): Promise<void> => {
return api.delete(`/api/v1/tasks/${id}`);
},
changeStatus: async (id: string, status: WorkItemStatus): Promise<Task> => {
return api.put(`/api/v1/tasks/${id}/status`, { status });
},
assign: async (id: string, assigneeId: string): Promise<Task> => {
return api.put(`/api/v1/tasks/${id}/assign`, { assigneeId });
},
};