// --------------------------------------------------------------------------- // Tasks API client (client-side) // --------------------------------------------------------------------------- import { apiFetch } from './client'; // --------------------------------------------------------------------------- // Request / response shapes // TODO: import from @/lib/types once the shared types package is finalised // --------------------------------------------------------------------------- export interface CreateTaskRequest { residence_id: number; title: string; description?: string; category_id?: number; priority_id?: number; frequency_id?: number; custom_interval_days?: number; in_progress?: boolean; assigned_to_id?: number; due_date?: string; estimated_cost?: number; contractor_id?: number; } export interface UpdateTaskRequest { title?: string; description?: string; category_id?: number; priority_id?: number; frequency_id?: number; custom_interval_days?: number; in_progress?: boolean; assigned_to_id?: number; due_date?: string; estimated_cost?: number; actual_cost?: number; contractor_id?: number; } export interface TaskResponse { id: number; residence_id: number; residence_name: string; title: string; description: string; category_id?: number; category?: LookupResponse; priority_id?: number; priority?: LookupResponse; frequency_id?: number; frequency?: LookupResponse; custom_interval_days?: number; in_progress: boolean; is_cancelled: boolean; is_archived: boolean; assigned_to_id?: number; assigned_to?: TaskUserResponse; due_date?: string; next_due_date?: string; estimated_cost?: number; actual_cost?: number; contractor_id?: number; contractor?: TaskContractorResponse; completion_count: number; last_completed_at?: string; created_by_id: number; created_at: string; updated_at: string; } export interface LookupResponse { id: number; name: string; icon?: string; color?: string; } export interface TaskUserResponse { id: number; username: string; first_name: string; last_name: string; } export interface TaskContractorResponse { id: number; name: string; company: string; } /** Kanban board response returned by ListTasks and GetTasksByResidence. */ export interface KanbanResponse { columns: KanbanColumn[]; total_summary: TaskTotalSummary; } export interface KanbanColumn { name: string; display_name: string; count: number; tasks: TaskResponse[]; } export interface TaskTotalSummary { total: number; overdue: number; due_soon: number; in_progress: number; completed: number; upcoming: number; } // --- Task Completions --- export interface CreateCompletionRequest { task_id: number; completed_at?: string; notes?: string; actual_cost?: number; rating?: number; image_urls?: string[]; } export interface UpdateCompletionRequest { notes?: string; actual_cost?: number; rating?: number; image_urls?: string[]; } export interface CompletionResponse { id: number; task_id: number; task_title: string; completed_at: string; completed_by_id: number; completed_by?: TaskUserResponse; notes: string; actual_cost?: number; rating?: number; images: CompletionImageResponse[]; created_at: string; } export interface CompletionImageResponse { id: number; image_url: string; caption: string; } export interface MessageResponse { message: string; } // --------------------------------------------------------------------------- // API functions // --------------------------------------------------------------------------- /** * List all tasks for the current user (kanban board). * @param days Number of days for "due soon" threshold (default 30) */ export function listTasks(days?: number): Promise { const params = days != null ? `?days=${days}` : ''; return apiFetch(`/tasks/${params}`); } /** Get tasks for a specific residence (kanban board). */ export function getTasksByResidence( residenceId: number, days?: number, ): Promise { const params = days != null ? `?days=${days}` : ''; return apiFetch( `/tasks/by-residence/${residenceId}/${params}`, ); } /** Get a single task by ID. */ export function getTask(id: number): Promise { return apiFetch(`/tasks/${id}/`); } /** Create a new task. */ export function createTask(data: CreateTaskRequest): Promise { return apiFetch('/tasks/', { method: 'POST', body: JSON.stringify(data), }); } /** Update an existing task. */ export function updateTask( id: number, data: UpdateTaskRequest, ): Promise { return apiFetch(`/tasks/${id}/`, { method: 'PUT', body: JSON.stringify(data), }); } /** Delete a task. */ export function deleteTask(id: number): Promise { return apiFetch(`/tasks/${id}/`, { method: 'DELETE', }); } /** Mark a task as in-progress. */ export function markInProgress(id: number): Promise { return apiFetch(`/tasks/${id}/mark-in-progress/`, { method: 'POST', }); } /** Cancel a task. */ export function cancelTask(id: number): Promise { return apiFetch(`/tasks/${id}/cancel/`, { method: 'POST', }); } /** Un-cancel a previously cancelled task. */ export function uncancelTask(id: number): Promise { return apiFetch(`/tasks/${id}/uncancel/`, { method: 'POST', }); } /** Archive a task. */ export function archiveTask(id: number): Promise { return apiFetch(`/tasks/${id}/archive/`, { method: 'POST', }); } /** Un-archive a previously archived task. */ export function unarchiveTask(id: number): Promise { return apiFetch(`/tasks/${id}/unarchive/`, { method: 'POST', }); } /** Quick-complete a task (lightweight, for widgets). Returns void (204). */ export function quickComplete(id: number): Promise { return apiFetch(`/tasks/${id}/quick-complete/`, { method: 'POST', }); } // --- Task Completions --- /** Get completions for a specific task. */ export function getTaskCompletions( taskId: number, ): Promise { return apiFetch(`/tasks/${taskId}/completions/`); } /** List all completions for the current user. */ export function listCompletions(): Promise { return apiFetch('/task-completions/'); } /** Get a single completion by ID. */ export function getCompletion(id: number): Promise { return apiFetch(`/task-completions/${id}/`); } /** Create a new task completion (JSON). */ export function createCompletion( data: CreateCompletionRequest, ): Promise { return apiFetch('/task-completions/', { method: 'POST', body: JSON.stringify(data), }); } /** * Create a task completion with image upload (multipart/form-data). * Use this when the user attaches photos to the completion. */ export function createCompletionWithImages( data: { task_id: number; notes?: string; actual_cost?: number; completed_at?: string; }, images: File[], ): Promise { const formData = new FormData(); formData.append('task_id', String(data.task_id)); if (data.notes) formData.append('notes', data.notes); if (data.actual_cost != null) formData.append('actual_cost', String(data.actual_cost)); if (data.completed_at) formData.append('completed_at', data.completed_at); for (const image of images) { formData.append('images', image); } return apiFetch('/task-completions/', { method: 'POST', body: formData, }); } /** Update an existing completion. */ export function updateCompletion( id: number, data: UpdateCompletionRequest, ): Promise { return apiFetch(`/task-completions/${id}/`, { method: 'PUT', body: JSON.stringify(data), }); } /** Delete a completion. */ export function deleteCompletion(id: number): Promise { return apiFetch(`/task-completions/${id}/`, { method: 'DELETE', }); }