- 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
151 lines
4.6 KiB
TypeScript
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 });
|
|
}
|
|
}
|