import { useState, useCallback, useEffect } from "react";
import {
listTasks,
getTask,
createTask,
updateTask,
deleteTask,
dismissTask,
VersionConflictError,
type TaskSummary,
type TaskWithSubtasks,
type CreateTaskRequest,
type UpdateTaskRequest,
} from "../lib/api";
export interface ConflictState {
hasConflict: boolean;
expectedVersion: number;
actualVersion: number;
}
export function useTasks() {
const [tasks, setTasks] = useState<TaskSummary[]>([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const [conflict, setConflict] = useState<ConflictState | null>(null);
const fetchTasks = useCallback(async () => {
setLoading(true);
setError(null);
try {
const response = await listTasks();
setTasks(response.tasks);
} catch (e) {
setError(e instanceof Error ? e.message : "Failed to fetch tasks");
} finally {
setLoading(false);
}
}, []);
const fetchTask = useCallback(
async (id: string): Promise<TaskWithSubtasks | null> => {
setError(null);
try {
return await getTask(id);
} catch (e) {
setError(e instanceof Error ? e.message : "Failed to fetch task");
return null;
}
},
[]
);
const saveTask = useCallback(
async (data: CreateTaskRequest): Promise<TaskWithSubtasks | null> => {
setError(null);
try {
const task = await createTask(data);
await fetchTasks(); // Refresh list
// Return as TaskWithSubtasks
return { ...task, subtasks: [] };
} catch (e) {
setError(e instanceof Error ? e.message : "Failed to save task");
return null;
}
},
[fetchTasks]
);
const editTask = useCallback(
async (id: string, data: UpdateTaskRequest): Promise<TaskWithSubtasks | null> => {
setError(null);
setConflict(null);
try {
await updateTask(id, data);
await fetchTasks(); // Refresh list
// Re-fetch to get subtasks
return await getTask(id);
} catch (e) {
if (e instanceof VersionConflictError) {
setConflict({
hasConflict: true,
expectedVersion: e.expectedVersion,
actualVersion: e.actualVersion,
});
return null;
}
setError(e instanceof Error ? e.message : "Failed to update task");
return null;
}
},
[fetchTasks]
);
const clearConflict = useCallback(() => {
setConflict(null);
}, []);
const removeTask = useCallback(
async (id: string): Promise<boolean> => {
setError(null);
try {
await deleteTask(id);
await fetchTasks(); // Refresh list
return true;
} catch (e) {
setError(e instanceof Error ? e.message : "Failed to delete task");
return false;
}
},
[fetchTasks]
);
const hideTask = useCallback(
async (id: string): Promise<boolean> => {
setError(null);
try {
await dismissTask(id);
await fetchTasks(); // Refresh list
return true;
} catch (e) {
setError(e instanceof Error ? e.message : "Failed to dismiss task");
return false;
}
},
[fetchTasks]
);
// Initial fetch
useEffect(() => {
fetchTasks();
}, [fetchTasks]);
return {
tasks,
loading,
error,
conflict,
clearConflict,
fetchTasks,
fetchTask,
saveTask,
editTask,
removeTask,
hideTask,
};
}