import type { User, Project, Task, TaskCreate, TaskUpdate, Label, Comment, Invite, Section } from '@/types'; const API_BASE = import.meta.env.PROD ? 'https://api.todo.donovankelly.xyz/api' : '/api'; const AUTH_BASE = import.meta.env.PROD ? 'https://api.todo.donovankelly.xyz' : ''; class ApiClient { private token: string | null = null; setToken(token: string | null) { this.token = token; } private async fetch( path: string, options: RequestInit = {} ): Promise { const headers: HeadersInit = { 'Content-Type': 'application/json', ...options.headers, }; if (this.token) { headers['Authorization'] = `Bearer ${this.token}`; } const response = await fetch(`${API_BASE}${path}`, { ...options, headers, credentials: 'include', }); if (!response.ok) { const error = await response.json().catch(() => ({ error: 'Unknown error' })); throw new Error(error.error || error.details || 'Request failed'); } return response.json(); } // Auth async login(email: string, password: string) { const response = await fetch(`${AUTH_BASE}/api/auth/sign-in/email`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email, password }), credentials: 'include', }); if (!response.ok) { const error = await response.json().catch(() => ({ message: 'Login failed' })); throw new Error(error.message || 'Login failed'); } return response.json(); } async logout() { await fetch(`${AUTH_BASE}/api/auth/sign-out`, { method: 'POST', credentials: 'include', }); } async getSession(): Promise<{ user: User } | null> { try { const response = await fetch(`${AUTH_BASE}/api/auth/get-session`, { credentials: 'include', }); if (!response.ok) return null; return response.json(); } catch { return null; } } // Invite validation (public) async validateInvite(token: string): Promise<{ email: string; name: string }> { const response = await fetch(`${AUTH_BASE}/auth/invite/${token}`); if (!response.ok) { const error = await response.json().catch(() => ({ error: 'Invalid invite' })); throw new Error(error.error); } return response.json(); } async acceptInvite(token: string, password: string): Promise { const response = await fetch(`${AUTH_BASE}/auth/invite/${token}/accept`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ password }), }); if (!response.ok) { const error = await response.json().catch(() => ({ error: 'Failed to accept invite' })); throw new Error(error.error); } } // Projects async getProjects(): Promise { return this.fetch('/projects'); } async getProject(id: string): Promise { return this.fetch(`/projects/${id}`); } async createProject(data: { name: string; color?: string; icon?: string }): Promise { return this.fetch('/projects', { method: 'POST', body: JSON.stringify(data), }); } async updateProject(id: string, data: Partial): Promise { return this.fetch(`/projects/${id}`, { method: 'PATCH', body: JSON.stringify(data), }); } async deleteProject(id: string): Promise { await this.fetch(`/projects/${id}`, { method: 'DELETE' }); } // Sections async createSection(projectId: string, data: { name: string; sortOrder?: number }): Promise
{ return this.fetch(`/projects/${projectId}/sections`, { method: 'POST', body: JSON.stringify(data), }); } async updateSection(projectId: string, sectionId: string, data: Partial
): Promise
{ return this.fetch(`/projects/${projectId}/sections/${sectionId}`, { method: 'PATCH', body: JSON.stringify(data), }); } async deleteSection(projectId: string, sectionId: string): Promise { await this.fetch(`/projects/${projectId}/sections/${sectionId}`, { method: 'DELETE' }); } // Tasks async getTasks(params?: { projectId?: string; sectionId?: string; completed?: boolean; priority?: string; today?: boolean; upcoming?: boolean; overdue?: boolean; labelId?: string; includeSubtasks?: boolean; }): Promise { const searchParams = new URLSearchParams(); if (params) { Object.entries(params).forEach(([key, value]) => { if (value !== undefined) { searchParams.set(key, String(value)); } }); } const query = searchParams.toString(); return this.fetch(`/tasks${query ? `?${query}` : ''}`); } async getTask(id: string): Promise { return this.fetch(`/tasks/${id}`); } async createTask(data: TaskCreate): Promise { return this.fetch('/tasks', { method: 'POST', body: JSON.stringify(data), }); } async updateTask(id: string, data: TaskUpdate): Promise { return this.fetch(`/tasks/${id}`, { method: 'PATCH', body: JSON.stringify(data), }); } async deleteTask(id: string): Promise { await this.fetch(`/tasks/${id}`, { method: 'DELETE' }); } // Labels async getLabels(): Promise { return this.fetch('/labels'); } async createLabel(data: { name: string; color?: string }): Promise