gantt-board/src/app/api/sprints/route.ts
Max 1475a13b4d Add sorting to main Kanban board in page.tsx
- Sprint tasks in main Kanban view now sorted by updatedAt descending
- Fixes issue where Done column tasks weren't sorted properly
- Matches sorting in SprintBoard.tsx and BacklogView.tsx
2026-02-21 22:22:47 -06:00

151 lines
4.6 KiB
TypeScript

import { NextResponse } from "next/server";
import { getServiceSupabase } from "@/lib/supabase/client";
import { getAuthenticatedUser } from "@/lib/server/auth";
export const runtime = "nodejs";
// GET - fetch all sprints (optionally filtered by status)
export async function GET(request: Request) {
try {
const user = await getAuthenticatedUser();
if (!user) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
// Parse query params
const { searchParams } = new URL(request.url);
const status = searchParams.get("status");
const supabase = getServiceSupabase();
let query = supabase
.from("sprints")
.select("*")
.order("start_date", { ascending: true });
// Filter by status if provided
if (status && ["planning", "active", "completed"].includes(status)) {
query = query.eq("status", status);
}
const { data: sprints, error } = await query;
if (error) throw error;
return NextResponse.json({ sprints: sprints || [] });
} catch (error) {
console.error(">>> API GET /sprints error:", error);
return NextResponse.json({ error: "Failed to fetch sprints" }, { status: 500 });
}
}
// POST - create a new sprint
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 { name, goal, startDate, endDate, status, projectId } = body;
if (!name || typeof name !== "string") {
return NextResponse.json({ error: "Missing sprint name" }, { status: 400 });
}
const supabase = getServiceSupabase();
const now = new Date().toISOString();
const { data, error } = await supabase
.from("sprints")
.insert({
name,
goal: goal || null,
start_date: startDate || now,
end_date: endDate || now,
status: status || "planning",
project_id: projectId || null,
created_at: now,
})
.select()
.single();
if (error) throw error;
return NextResponse.json({ success: true, sprint: data });
} catch (error) {
console.error(">>> API POST /sprints error:", error);
return NextResponse.json({ error: "Failed to create sprint" }, { status: 500 });
}
}
// PATCH - update a sprint
export async function PATCH(request: Request) {
try {
const user = await getAuthenticatedUser();
if (!user) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
const body = await request.json();
const { id, ...updates } = body;
if (!id) {
return NextResponse.json({ error: "Missing sprint id" }, { status: 400 });
}
const supabase = getServiceSupabase();
const now = new Date().toISOString();
// Map camelCase to snake_case for database
const dbUpdates: Record<string, unknown> = {};
if (updates.name !== undefined) dbUpdates.name = updates.name;
if (updates.goal !== undefined) dbUpdates.goal = updates.goal;
if (updates.startDate !== undefined) dbUpdates.start_date = updates.startDate;
if (updates.endDate !== undefined) dbUpdates.end_date = updates.endDate;
if (updates.status !== undefined) dbUpdates.status = updates.status;
if (updates.projectId !== undefined) dbUpdates.project_id = updates.projectId;
dbUpdates.updated_at = now;
const { data, error } = await supabase
.from("sprints")
.update(dbUpdates)
.eq("id", id)
.select()
.single();
if (error) throw error;
return NextResponse.json({ success: true, sprint: data });
} catch (error) {
console.error(">>> API PATCH /sprints error:", error);
return NextResponse.json({ error: "Failed to update sprint" }, { status: 500 });
}
}
// DELETE - delete a sprint
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();
if (!id) {
return NextResponse.json({ error: "Missing sprint id" }, { status: 400 });
}
const supabase = getServiceSupabase();
const { error } = await supabase.from("sprints").delete().eq("id", id);
if (error) throw error;
return NextResponse.json({ success: true });
} catch (error) {
console.error(">>> API DELETE /sprints error:", error);
return NextResponse.json({ error: "Failed to delete sprint" }, { status: 500 });
}
}