mission-control/app/api/tasks/route.ts
OpenClaw Bot c1c01bd21e feat: merge Gantt Board into Mission Control
- Add Projects page with Sprint Board and Backlog views
- Copy SprintBoard and BacklogView components to components/gantt/
- Copy useTaskStore for project/task/sprint management
- Add API routes for task persistence with SQLite
- Add UI components: dialog, select, table, textarea
- Add avatar and attachment utilities
- Update sidebar with Projects navigation link
- Remove static export config to support API routes
- Add dist to .gitignore
2026-02-20 18:49:52 -06:00

116 lines
4.0 KiB
TypeScript

import { NextResponse } from "next/server";
import { getData, saveData, type DataStore, type Task } from "@/lib/server/taskDb";
import { getAuthenticatedUser } from "@/lib/server/auth";
export const runtime = "nodejs";
// GET - fetch all tasks, projects, and sprints
export async function GET() {
try {
const user = await getAuthenticatedUser();
if (!user) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
const data = getData();
return NextResponse.json(data);
} catch (error) {
console.error(">>> API GET: database error:", error);
return NextResponse.json({ error: "Failed to fetch data" }, { status: 500 });
}
}
// POST - create or update tasks, projects, or sprints
export async function POST(request: Request) {
try {
const user = await getAuthenticatedUser();
if (!user) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
const body = await request.json();
const { task, tasks, projects, sprints } = body as {
task?: Task;
tasks?: Task[];
projects?: DataStore["projects"];
sprints?: DataStore["sprints"];
};
const data = getData();
if (projects) data.projects = projects;
if (sprints) data.sprints = sprints;
if (task) {
const existingIndex = data.tasks.findIndex((t) => t.id === task.id);
if (existingIndex >= 0) {
const existingTask = data.tasks[existingIndex];
data.tasks[existingIndex] = {
...existingTask,
...task,
updatedAt: new Date().toISOString(),
updatedById: user.id,
updatedByName: user.name,
updatedByAvatarUrl: user.avatarUrl,
};
} else {
data.tasks.push({
...task,
id: task.id || Date.now().toString(),
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
createdById: task.createdById || user.id,
createdByName: task.createdByName || user.name,
createdByAvatarUrl: task.createdByAvatarUrl || user.avatarUrl,
updatedById: user.id,
updatedByName: user.name,
updatedByAvatarUrl: user.avatarUrl,
assigneeId: task.assigneeId || user.id,
assigneeName: task.assigneeName || user.name,
assigneeEmail: task.assigneeEmail || user.email,
});
}
}
if (tasks && Array.isArray(tasks)) {
data.tasks = tasks.map((entry) => ({
...entry,
createdById: entry.createdById || user.id,
createdByName: entry.createdByName || user.name,
createdByAvatarUrl: entry.createdByAvatarUrl || (entry.createdById === user.id ? user.avatarUrl : undefined),
updatedById: entry.updatedById || user.id,
updatedByName: entry.updatedByName || user.name,
updatedByAvatarUrl: entry.updatedByAvatarUrl || (entry.updatedById === user.id ? user.avatarUrl : undefined),
assigneeId: entry.assigneeId || undefined,
assigneeName: entry.assigneeName || undefined,
assigneeEmail: entry.assigneeEmail || undefined,
assigneeAvatarUrl: undefined,
}));
}
const saved = saveData(data);
return NextResponse.json({ success: true, data: saved });
} catch (error) {
console.error(">>> API POST: database error:", error);
return NextResponse.json({ error: "Failed to save" }, { status: 500 });
}
}
// DELETE - remove a task
export async function DELETE(request: Request) {
try {
const user = await getAuthenticatedUser();
if (!user) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
const { id } = (await request.json()) as { id: string };
const data = getData();
data.tasks = data.tasks.filter((t) => t.id !== id);
saveData(data);
return NextResponse.json({ success: true });
} catch (error) {
console.error(">>> API DELETE: database error:", error);
return NextResponse.json({ error: "Failed to delete" }, { status: 500 });
}
}