feat: add personal todos feature
- New todos table in DB schema (title, description, priority, category, due date, completion) - Full CRUD + toggle API routes at /api/todos - Categories support with filtering - Bulk import endpoint for migration - New TodosPage with inline editing, priority badges, due date display - Add Todos to sidebar navigation - Dark mode support throughout
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import type { Task, Project, ProjectWithTasks, VelocityStats, Recurrence } from "./types";
|
||||
import type { Task, Project, ProjectWithTasks, VelocityStats, Recurrence, Todo, TodoPriority } from "./types";
|
||||
|
||||
const BASE = "/api/tasks";
|
||||
|
||||
@@ -228,3 +228,75 @@ export async function deleteUser(userId: string): Promise<void> {
|
||||
});
|
||||
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<Todo[]> {
|
||||
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<string[]> {
|
||||
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<Todo> {
|
||||
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<Todo> {
|
||||
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<Todo> {
|
||||
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<void> {
|
||||
const res = await fetch(`${TODOS_BASE}/${id}`, {
|
||||
method: "DELETE",
|
||||
credentials: "include",
|
||||
});
|
||||
if (!res.ok) throw new Error("Failed to delete todo");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user