import type { Task, Project, ProjectWithTasks, VelocityStats, Recurrence, Todo, TodoPriority } from "./types"; const BASE = "/api/tasks"; export async function fetchTasks(): Promise { const res = await fetch(BASE, { credentials: "include" }); if (!res.ok) throw new Error(res.status === 401 ? "Unauthorized" : "Failed to fetch tasks"); return res.json(); } export async function updateTask( id: string, updates: Record, token?: string ): Promise { const headers: Record = { "Content-Type": "application/json" }; if (token) headers["Authorization"] = `Bearer ${token}`; const res = await fetch(`${BASE}/${id}`, { method: "PATCH", credentials: "include", headers, body: JSON.stringify(updates), }); if (!res.ok) throw new Error("Failed to update task"); return res.json(); } export async function reorderTasks(ids: string[], token?: string): Promise { const headers: Record = { "Content-Type": "application/json" }; if (token) headers["Authorization"] = `Bearer ${token}`; const res = await fetch(`${BASE}/reorder`, { method: "PATCH", credentials: "include", headers, body: JSON.stringify({ ids }), }); if (!res.ok) throw new Error("Failed to reorder tasks"); } export async function createTask( task: { title: string; description?: string; source?: string; priority?: string; status?: string; projectId?: string; dueDate?: string; estimatedHours?: number; recurrence?: Recurrence | null }, token?: string ): Promise { const headers: Record = { "Content-Type": "application/json" }; if (token) headers["Authorization"] = `Bearer ${token}`; const res = await fetch(BASE, { method: "POST", credentials: "include", headers, body: JSON.stringify(task), }); if (!res.ok) throw new Error("Failed to create task"); return res.json(); } export async function deleteTask(id: string, token?: string): Promise { const headers: Record = {}; if (token) headers["Authorization"] = `Bearer ${token}`; const res = await fetch(`${BASE}/${id}`, { method: "DELETE", credentials: "include", headers, }); if (!res.ok) throw new Error("Failed to delete task"); } // ─── Velocity Stats ─── export async function fetchVelocityStats(): Promise { const res = await fetch(`${BASE}/stats/velocity`, { credentials: "include" }); if (!res.ok) throw new Error("Failed to fetch velocity stats"); return res.json(); } // ─── Projects API ─── const PROJECTS_BASE = "/api/projects"; export async function fetchProjects(): Promise { const res = await fetch(PROJECTS_BASE, { credentials: "include" }); if (!res.ok) throw new Error(res.status === 401 ? "Unauthorized" : "Failed to fetch projects"); return res.json(); } export async function fetchProject(id: string): Promise { const res = await fetch(`${PROJECTS_BASE}/${id}`, { credentials: "include" }); if (!res.ok) throw new Error("Failed to fetch project"); return res.json(); } export async function createProject( project: { name: string; description?: string; context?: string; repos?: string[]; links?: { label: string; url: string }[] } ): Promise { const res = await fetch(PROJECTS_BASE, { method: "POST", credentials: "include", headers: { "Content-Type": "application/json" }, body: JSON.stringify(project), }); if (!res.ok) throw new Error("Failed to create project"); return res.json(); } export async function updateProject( id: string, updates: Record ): Promise { const res = await fetch(`${PROJECTS_BASE}/${id}`, { method: "PATCH", credentials: "include", headers: { "Content-Type": "application/json" }, body: JSON.stringify(updates), }); if (!res.ok) throw new Error("Failed to update project"); return res.json(); } export async function deleteProject(id: string): Promise { const res = await fetch(`${PROJECTS_BASE}/${id}`, { method: "DELETE", credentials: "include", }); if (!res.ok) throw new Error("Failed to delete project"); } // Subtasks export async function addSubtask(taskId: string, title: string): Promise { const res = await fetch(`${BASE}/${taskId}/subtasks`, { method: "POST", credentials: "include", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ title }), }); if (!res.ok) throw new Error("Failed to add subtask"); return res.json(); } export async function toggleSubtask(taskId: string, subtaskId: string, completed: boolean): Promise { const res = await fetch(`${BASE}/${taskId}/subtasks/${subtaskId}`, { method: "PATCH", credentials: "include", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ completed }), }); if (!res.ok) throw new Error("Failed to toggle subtask"); return res.json(); } export async function deleteSubtask(taskId: string, subtaskId: string): Promise { const res = await fetch(`${BASE}/${taskId}/subtasks/${subtaskId}`, { method: "DELETE", credentials: "include", }); if (!res.ok) throw new Error("Failed to delete subtask"); return res.json(); } // Progress Notes export async function addProgressNote(taskId: string, note: string): Promise { const res = await fetch(`${BASE}/${taskId}/notes`, { method: "POST", credentials: "include", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ note }), }); if (!res.ok) throw new Error("Failed to add progress note"); return res.json(); } // ─── Comments API ─── export interface TaskComment { id: string; taskId: string; authorId: string | null; authorName: string; content: string; createdAt: string; } export async function fetchComments(taskId: string): Promise { const res = await fetch(`${BASE}/${taskId}/comments`, { credentials: "include" }); if (!res.ok) throw new Error("Failed to fetch comments"); return res.json(); } export async function addComment(taskId: string, content: string): Promise { const res = await fetch(`${BASE}/${taskId}/comments`, { method: "POST", credentials: "include", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ content }), }); if (!res.ok) throw new Error("Failed to add comment"); return res.json(); } export async function deleteComment(taskId: string, commentId: string): Promise { const res = await fetch(`${BASE}/${taskId}/comments/${commentId}`, { method: "DELETE", credentials: "include", }); if (!res.ok) throw new Error("Failed to delete comment"); } // Admin API export async function fetchUsers(): Promise { const res = await fetch("/api/admin/users", { credentials: "include" }); if (!res.ok) throw new Error("Failed to fetch users"); return res.json(); } export async function updateUserRole(userId: string, role: string): Promise { const res = await fetch(`/api/admin/users/${userId}/role`, { method: "PATCH", credentials: "include", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ role }), }); if (!res.ok) throw new Error("Failed to update user role"); return res.json(); } export async function deleteUser(userId: string): Promise { const res = await fetch(`/api/admin/users/${userId}`, { method: "DELETE", credentials: "include", }); if (!res.ok) throw new Error("Failed to delete user"); } // ─── Todos API ─── const TODOS_BASE = "/api/todos"; export async function fetchTodos(params?: { completed?: string; category?: string }): Promise { const url = new URL(TODOS_BASE, window.location.origin); if (params?.completed) url.searchParams.set("completed", params.completed); if (params?.category) url.searchParams.set("category", params.category); const res = await fetch(url.toString(), { credentials: "include" }); if (!res.ok) throw new Error(res.status === 401 ? "Unauthorized" : "Failed to fetch todos"); return res.json(); } export async function fetchTodoCategories(): Promise { const res = await fetch(`${TODOS_BASE}/categories`, { credentials: "include" }); if (!res.ok) throw new Error("Failed to fetch categories"); return res.json(); } export async function createTodo(todo: { title: string; description?: string; priority?: TodoPriority; category?: string; dueDate?: string | null; }): Promise { const res = await fetch(TODOS_BASE, { method: "POST", credentials: "include", headers: { "Content-Type": "application/json" }, body: JSON.stringify(todo), }); if (!res.ok) throw new Error("Failed to create todo"); return res.json(); } export async function updateTodo(id: string, updates: Partial<{ title: string; description: string; priority: TodoPriority; category: string | null; dueDate: string | null; isCompleted: boolean; sortOrder: number; }>): Promise { const res = await fetch(`${TODOS_BASE}/${id}`, { method: "PATCH", credentials: "include", headers: { "Content-Type": "application/json" }, body: JSON.stringify(updates), }); if (!res.ok) throw new Error("Failed to update todo"); return res.json(); } export async function toggleTodo(id: string): Promise { const res = await fetch(`${TODOS_BASE}/${id}/toggle`, { method: "PATCH", credentials: "include", }); if (!res.ok) throw new Error("Failed to toggle todo"); return res.json(); } export async function deleteTodo(id: string): Promise { const res = await fetch(`${TODOS_BASE}/${id}`, { method: "DELETE", credentials: "include", }); if (!res.ok) throw new Error("Failed to delete todo"); }