Initial commit

This commit is contained in:
Yaojia Wang
2025-11-03 00:04:07 +01:00
parent 34b701de48
commit 097300e8ec
37 changed files with 3473 additions and 109 deletions

90
lib/api/client.ts Normal file
View File

@@ -0,0 +1,90 @@
const API_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:5000/api/v1';
export class ApiError extends Error {
constructor(
public status: number,
message: string,
public data?: any
) {
super(message);
this.name = 'ApiError';
}
}
async function handleResponse<T>(response: Response): Promise<T> {
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new ApiError(
response.status,
errorData.message || response.statusText,
errorData
);
}
if (response.status === 204) {
return {} as T;
}
return response.json();
}
export async function apiRequest<T>(
endpoint: string,
options: RequestInit = {}
): Promise<T> {
const url = `${API_URL}${endpoint}`;
const headers: Record<string, string> = {
'Content-Type': 'application/json',
};
// Add auth token if available
if (typeof window !== 'undefined') {
const token = localStorage.getItem('accessToken');
if (token) {
headers['Authorization'] = `Bearer ${token}`;
}
}
// Merge with options headers
if (options.headers) {
Object.assign(headers, options.headers);
}
const config: RequestInit = {
...options,
headers,
};
const response = await fetch(url, config);
return handleResponse<T>(response);
}
export const api = {
get: <T>(endpoint: string, options?: RequestInit) =>
apiRequest<T>(endpoint, { ...options, method: 'GET' }),
post: <T>(endpoint: string, data?: any, options?: RequestInit) =>
apiRequest<T>(endpoint, {
...options,
method: 'POST',
body: JSON.stringify(data),
}),
put: <T>(endpoint: string, data?: any, options?: RequestInit) =>
apiRequest<T>(endpoint, {
...options,
method: 'PUT',
body: JSON.stringify(data),
}),
patch: <T>(endpoint: string, data?: any, options?: RequestInit) =>
apiRequest<T>(endpoint, {
...options,
method: 'PATCH',
body: JSON.stringify(data),
}),
delete: <T>(endpoint: string, options?: RequestInit) =>
apiRequest<T>(endpoint, { ...options, method: 'DELETE' }),
};

29
lib/api/projects.ts Normal file
View File

@@ -0,0 +1,29 @@
import { api } from './client';
import type { Project, CreateProjectDto, UpdateProjectDto } from '@/types/project';
import type { KanbanBoard } from '@/types/kanban';
export const projectsApi = {
getAll: async (page = 1, pageSize = 20): Promise<Project[]> => {
return api.get(`/projects?page=${page}&pageSize=${pageSize}`);
},
getById: async (id: string): Promise<Project> => {
return api.get(`/projects/${id}`);
},
create: async (data: CreateProjectDto): Promise<Project> => {
return api.post('/projects', data);
},
update: async (id: string, data: UpdateProjectDto): Promise<Project> => {
return api.put(`/projects/${id}`, data);
},
delete: async (id: string): Promise<void> => {
return api.delete(`/projects/${id}`);
},
getKanban: async (id: string): Promise<KanbanBoard> => {
return api.get(`/projects/${id}/kanban`);
},
};