const API_BASE = typeof window !== "undefined" ? "" : (process.env.NEXT_PUBLIC_API_URL || "http://localhost:3000"); interface ApiOptions { method?: string; body?: unknown; token?: string; } async function apiFetch(path: string, opts: ApiOptions = {}): Promise { const headers: Record = { "Content-Type": "application/json", }; if (opts.token) { headers["Authorization"] = `Bearer ${opts.token}`; } const url = `${API_BASE}${path}`; const res = await fetch(url, { method: opts.method || "GET", headers, body: opts.body ? JSON.stringify(opts.body) : undefined, cache: "no-store", }); if (!res.ok) { const err = await res.json().catch(() => ({ message: res.statusText })); throw new Error(err.message || err.error || `HTTP ${res.status}`); } if (res.status === 204 || res.headers.get("content-length") === "0") { return {} as T; } return res.json(); } // Auth export function register(data: { email: string; name: string; phone?: string; password: string }) { return apiFetch<{ data: { token: string; user: User } }>("/api/v1/auth/register", { method: "POST", body: data, }); } export function login(data: { email: string; password?: string }) { return apiFetch<{ data: { token: string; user: User } }>("/api/v1/auth/login", { method: "POST", body: data, }); } export function getMe(token: string) { return apiFetch<{ user: User }>("/api/v1/auth/me", { token }); } // WebAuthn export function webauthnRegisterOptions(token: string, userId: string) { return apiFetch<{ data: Record }>("/api/v1/webauthn/register/options", { method: "POST", token, body: { user_id: userId }, }); } export function webauthnRegisterVerify(token: string, data: { user_id: string; credential_id: string; public_key: string; device_name: string }) { return apiFetch<{ data: Record; status: string }>("/api/v1/webauthn/register/verify", { method: "POST", token, body: data, }); } export function webauthnAuthOptions(email: string) { return apiFetch<{ data: Record }>("/api/v1/webauthn/auth/options", { method: "POST", body: { email }, }); } export function webauthnAuthVerify(credentialId: string) { return apiFetch<{ data: { token: string; user: User } }>("/api/v1/webauthn/auth/verify", { method: "POST", body: { credential_id: credentialId }, }); } export function webauthnGetDevices(token: string, userId: string) { return apiFetch<{ data: Array<{ id: string; device_name: string; created_at: string }> }>(`/api/v1/webauthn/devices/${userId}`, { token }); } export function webauthnDeleteDevice(token: string, deviceId: string) { return apiFetch<{ status: string }>(`/api/v1/webauthn/devices/${deviceId}`, { method: "DELETE", token }); } // Tasks export function getTasks(token: string, params?: Record) { const qs = params ? "?" + new URLSearchParams(params).toString() : ""; return apiFetch<{ data: Task[]; total: number }>(`/api/v1/tasks${qs}`, { token }); } export function getTask(token: string, id: string) { return apiFetch(`/api/v1/tasks/${id}`, { token }); } export function createTask(token: string, data: Partial) { return apiFetch("/api/v1/tasks", { method: "POST", body: data, token }); } export function updateTask(token: string, id: string, data: Partial) { return apiFetch(`/api/v1/tasks/${id}`, { method: "PUT", body: data, token }); } export function deleteTask(token: string, id: string) { return apiFetch(`/api/v1/tasks/${id}`, { method: "DELETE", token }); } // Groups export function getGroups(token: string) { return apiFetch<{ data: Group[] }>("/api/v1/groups", { token }); } export function createGroup(token: string, data: Partial) { return apiFetch("/api/v1/groups", { method: "POST", body: data, token }); } export function updateGroup(token: string, id: string, data: Partial) { return apiFetch(`/api/v1/groups/${id}`, { method: "PUT", body: data, token }); } export function deleteGroup(token: string, id: string) { return apiFetch(`/api/v1/groups/${id}`, { method: "DELETE", token }); } export function reorderGroups(token: string, ids: string[]) { return apiFetch("/api/v1/groups/reorder", { method: "PUT", body: { ids }, token }); } // Connectors export function getConnectors(token: string) { return apiFetch<{ data: Connector[] }>("/api/v1/connectors", { token }); } export function createConnector(token: string, data: Partial) { return apiFetch("/api/v1/connectors", { method: "POST", body: data, token }); } // Types export interface User { id: string; email: string; name: string; phone?: string; } export interface Task { id: string; user_id: string | null; group_id: string | null; title: string; description: string; status: "pending" | "in_progress" | "done" | "cancelled"; priority: "low" | "medium" | "high" | "urgent"; scheduled_at: string | null; due_at: string | null; completed_at: string | null; assigned_to: string[]; attachments: string[]; external_id: string | null; external_source: string | null; created_at: string; updated_at: string; group_name: string | null; group_color: string | null; group_icon: string | null; } export interface GroupTimeZone { days: number[]; from: string; to: string; } export interface GroupLocation { name: string; lat: number | null; lng: number | null; radius_m: number; } export interface Group { id: string; name: string; color: string; icon: string | null; sort_order: number; display_name?: string; time_zones: GroupTimeZone[]; locations: GroupLocation[]; } export interface Connector { id: string; name: string; type: string; config: Record; } // Goals export interface Goal { id: string; user_id: string | null; title: string; target_date: string | null; progress_pct: number; group_id: string | null; plan: Record | null; created_at: string; updated_at: string; group_name: string | null; group_color: string | null; group_icon: string | null; tasks?: Task[]; } export interface GoalPlanResult { plan: Record; tasks_created: number; } export interface GoalReport { report: string; stats: { done: number; total: number; pct: number }; } export function getGoals(token: string) { return apiFetch<{ data: Goal[] }>("/api/v1/goals", { token }); } export function getGoal(token: string, id: string) { return apiFetch<{ data: Goal }>(`/api/v1/goals/${id}`, { token }); } export function createGoal(token: string, data: Partial) { return apiFetch<{ data: Goal }>("/api/v1/goals", { method: "POST", body: data, token }); } export function updateGoal(token: string, id: string, data: Partial) { return apiFetch<{ data: Goal }>(`/api/v1/goals/${id}`, { method: "PUT", body: data, token }); } export function deleteGoal(token: string, id: string) { return apiFetch(`/api/v1/goals/${id}`, { method: "DELETE", token }); } export function generateGoalPlan(token: string, id: string) { return apiFetch<{ data: GoalPlanResult }>(`/api/v1/goals/${id}/plan`, { method: "POST", token }); } export function getGoalReport(token: string, id: string) { return apiFetch<{ data: GoalReport }>(`/api/v1/goals/${id}/report`, { token }); } // Projects export interface Project { id: string; owner_id: string | null; name: string; description: string; color: string; icon: string; members: string[]; task_count?: number; done_count?: number; created_at: string; updated_at: string; } export function getProjects(token: string, userId?: string) { const qs = userId ? "?user_id=" + userId : ""; return apiFetch<{ data: Project[] }>("/api/v1/projects" + qs, { token }); } export function getProject(token: string, id: string) { return apiFetch<{ data: Project }>("/api/v1/projects/" + id, { token }); } export function createProject(token: string, data: Partial) { return apiFetch<{ data: Project }>("/api/v1/projects", { method: "POST", body: data, token }); } export function updateProject(token: string, id: string, data: Partial) { return apiFetch<{ data: Project }>("/api/v1/projects/" + id, { method: "PUT", body: data, token }); } export function deleteProject(token: string, id: string) { return apiFetch("/api/v1/projects/" + id, { method: "DELETE", token }); } export function inviteToProject(token: string, id: string, userId: string) { return apiFetch<{ data: Project; status: string }>("/api/v1/projects/" + id + "/invite", { method: "POST", body: { user_id: userId }, token }); } // Subtasks export interface Subtask { id: string; parent_task_id: string; title: string; description: string; status: "pending" | "in_progress" | "done" | "completed"; assigned_to: string | null; assignee_name: string | null; order_index: number; completed_at: string | null; created_at: string; updated_at: string; } export interface CollabRequest { id: string; task_id: string; from_user_id: string | null; to_user_id: string; type: "assign" | "transfer" | "collaborate" | "claim"; status: "pending" | "accepted" | "rejected"; message: string; from_name: string | null; to_name: string | null; task_title?: string; created_at: string; responded_at: string | null; } export function getSubtasks(token: string, taskId: string) { return apiFetch<{ data: Subtask[] }>(`/api/v1/tasks/${taskId}/subtasks`, { token }); } export function createSubtask(token: string, taskId: string, data: { title: string; description?: string; assigned_to?: string }) { return apiFetch<{ data: Subtask }>(`/api/v1/tasks/${taskId}/subtasks`, { method: "POST", body: data, token }); } export function updateSubtask(token: string, taskId: string, subtaskId: string, data: Partial) { return apiFetch<{ data: Subtask }>(`/api/v1/tasks/${taskId}/subtasks/${subtaskId}`, { method: "PUT", body: data, token }); } export function deleteSubtask(token: string, taskId: string, subtaskId: string) { return apiFetch(`/api/v1/tasks/${taskId}/subtasks/${subtaskId}`, { method: "DELETE", token }); } export function getCollaborationHistory(token: string, taskId: string) { return apiFetch<{ data: CollabRequest[] }>(`/api/v1/tasks/${taskId}/collaboration`, { token }); } export function sendCollabRequest(token: string, taskId: string, data: { to_user_id?: string; from_user_id?: string; type: string; message?: string }) { return apiFetch<{ data?: CollabRequest; status: string }>(`/api/v1/tasks/${taskId}/collaborate`, { method: "POST", body: data, token }); } export function searchUsers(token: string, query: string) { return apiFetch<{ data: { id: string; name: string; email: string; avatar_url: string | null }[] }>(`/api/v1/auth/users/search?q=${encodeURIComponent(query)}`, { token }); } // Invitations export interface Invitation { id: string; token: string; task_id: string; inviter_id: string | null; invitee_email: string | null; invitee_name: string | null; message: string; status: string; expires_at: string; accepted_at: string | null; created_at: string; } export interface InvitationResponse { invitation: Invitation; link: string; share: { whatsapp: string; telegram: string; sms: string; copy: string; }; } export function createInvitation( token: string, data: { task_id: string; invitee_email?: string; invitee_name?: string; message?: string; inviter_id?: string } ) { return apiFetch<{ data: InvitationResponse }>("/api/v1/invitations", { method: "POST", body: data, token }); } export function getTaskInvitations(token: string, taskId: string) { return apiFetch<{ data: Invitation[] }>(`/api/v1/tasks/${taskId}/invitations`, { token }); } export function revokeInvitation(token: string, invitationId: string) { return apiFetch<{ data: Invitation }>(`/api/v1/invitations/${invitationId}`, { method: "DELETE", token }); }