- 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
116 lines
4.0 KiB
TypeScript
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 });
|
|
}
|
|
}
|