Update sprint endpoint to support CRUD and listing
cgen-cdfaf793dbd64d42b6476f847b95bf27
This commit is contained in:
parent
d821441005
commit
01f57239a8
1 changed files with 198 additions and 44 deletions
|
|
@ -1,55 +1,209 @@
|
|||
import { supabase } from "../_supabase";
|
||||
import type { VercelRequest, VercelResponse } from "@vercel/node";
|
||||
import { getAdminClient } from "../../_supabase";
|
||||
|
||||
export default async (req: Request) => {
|
||||
if (req.method !== "GET") {
|
||||
return new Response("Method not allowed", { status: 405 });
|
||||
const admin = getAdminClient();
|
||||
|
||||
export default async function handler(req: VercelRequest, res: VercelResponse) {
|
||||
const authHeader = req.headers.authorization;
|
||||
if (!authHeader) {
|
||||
return res.status(401).json({ error: "Unauthorized" });
|
||||
}
|
||||
|
||||
const token = authHeader.replace("Bearer ", "");
|
||||
const {
|
||||
data: { user },
|
||||
error: authError,
|
||||
} = await admin.auth.getUser(token);
|
||||
|
||||
if (authError || !user) {
|
||||
return res.status(401).json({ error: "Invalid token" });
|
||||
}
|
||||
|
||||
try {
|
||||
const token = req.headers.get("Authorization")?.replace("Bearer ", "");
|
||||
if (!token) {
|
||||
return new Response("Unauthorized", { status: 401 });
|
||||
}
|
||||
// GET: List sprints for user's projects or sprints user is a member of
|
||||
if (req.method === "GET") {
|
||||
const { projectId, status } = req.query;
|
||||
|
||||
const { data: userData } = await supabase.auth.getUser(token);
|
||||
if (!userData.user) {
|
||||
return new Response("Unauthorized", { status: 401 });
|
||||
}
|
||||
let query = admin
|
||||
.from("gameforge_sprints")
|
||||
.select(
|
||||
`
|
||||
id,
|
||||
project_id,
|
||||
sprint_number,
|
||||
title,
|
||||
description,
|
||||
phase,
|
||||
status,
|
||||
goal,
|
||||
start_date,
|
||||
end_date,
|
||||
planned_velocity,
|
||||
actual_velocity,
|
||||
created_by,
|
||||
created_at,
|
||||
updated_at,
|
||||
gameforge_projects(name),
|
||||
gameforge_sprint_members(user_id)
|
||||
`,
|
||||
);
|
||||
|
||||
const { data: sprint, error } = await supabase
|
||||
.from("gameforge_sprints")
|
||||
.select(
|
||||
`
|
||||
id,
|
||||
project_id,
|
||||
title,
|
||||
phase,
|
||||
status,
|
||||
start_date,
|
||||
end_date,
|
||||
deadline,
|
||||
gdd,
|
||||
scope
|
||||
`,
|
||||
)
|
||||
.eq("user_id", userData.user.id)
|
||||
.order("created_at", { ascending: false })
|
||||
.limit(1)
|
||||
.single();
|
||||
if (projectId) {
|
||||
query = query.eq("project_id", projectId);
|
||||
} else {
|
||||
// Get sprints for projects user is on
|
||||
query = query.in(
|
||||
"project_id",
|
||||
`
|
||||
select id from gameforge_projects
|
||||
where lead_id = '${user.id}'
|
||||
or id in (
|
||||
select distinct project_id from gameforge_team_members
|
||||
where user_id = '${user.id}'
|
||||
)
|
||||
`,
|
||||
);
|
||||
}
|
||||
|
||||
if (error && error.code !== "PGRST116") {
|
||||
console.error("Sprint fetch error:", error);
|
||||
return new Response(JSON.stringify({ error: error.message }), {
|
||||
status: 500,
|
||||
if (status) {
|
||||
query = query.eq("status", status);
|
||||
}
|
||||
|
||||
const { data: sprints, error } = await query.order("created_at", {
|
||||
ascending: false,
|
||||
});
|
||||
|
||||
if (error) {
|
||||
return res.status(500).json({ error: error.message });
|
||||
}
|
||||
|
||||
return res.status(200).json(sprints || []);
|
||||
}
|
||||
|
||||
return new Response(JSON.stringify(sprint || null), {
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
} catch (err: any) {
|
||||
return new Response(JSON.stringify({ error: err.message }), {
|
||||
status: 500,
|
||||
});
|
||||
// POST: Create a sprint
|
||||
if (req.method === "POST") {
|
||||
const {
|
||||
projectId,
|
||||
title,
|
||||
description,
|
||||
goal,
|
||||
startDate,
|
||||
endDate,
|
||||
plannedVelocity,
|
||||
} = req.body;
|
||||
|
||||
// Verify user is project lead
|
||||
const { data: project, error: projectError } = await admin
|
||||
.from("gameforge_projects")
|
||||
.select("id")
|
||||
.eq("id", projectId)
|
||||
.eq("lead_id", user.id)
|
||||
.single();
|
||||
|
||||
if (projectError || !project) {
|
||||
return res.status(403).json({ error: "Not project lead" });
|
||||
}
|
||||
|
||||
// Get next sprint number
|
||||
const { data: lastSprint, error: lastSprintError } = await admin
|
||||
.from("gameforge_sprints")
|
||||
.select("sprint_number")
|
||||
.eq("project_id", projectId)
|
||||
.order("sprint_number", { ascending: false })
|
||||
.limit(1)
|
||||
.single();
|
||||
|
||||
const nextSprintNumber = (lastSprint?.sprint_number || 0) + 1;
|
||||
|
||||
const { data: sprint, error: createError } = await admin
|
||||
.from("gameforge_sprints")
|
||||
.insert([
|
||||
{
|
||||
project_id: projectId,
|
||||
sprint_number: nextSprintNumber,
|
||||
title,
|
||||
description,
|
||||
goal,
|
||||
start_date: startDate,
|
||||
end_date: endDate,
|
||||
planned_velocity: plannedVelocity,
|
||||
created_by: user.id,
|
||||
phase: "planning",
|
||||
status: "pending",
|
||||
},
|
||||
])
|
||||
.select()
|
||||
.single();
|
||||
|
||||
if (createError) {
|
||||
return res.status(500).json({ error: createError.message });
|
||||
}
|
||||
|
||||
// Auto-add creator as sprint lead
|
||||
await admin.from("gameforge_sprint_members").insert([
|
||||
{
|
||||
sprint_id: sprint.id,
|
||||
user_id: user.id,
|
||||
role: "lead",
|
||||
},
|
||||
]);
|
||||
|
||||
return res.status(201).json(sprint);
|
||||
}
|
||||
|
||||
// PUT: Update a sprint
|
||||
if (req.method === "PUT") {
|
||||
const { sprintId } = req.query;
|
||||
const { title, description, goal, startDate, endDate, phase, status } =
|
||||
req.body;
|
||||
|
||||
// Verify user is project lead
|
||||
const { data: sprint, error: sprintError } = await admin
|
||||
.from("gameforge_sprints")
|
||||
.select("project_id")
|
||||
.eq("id", sprintId)
|
||||
.single();
|
||||
|
||||
if (sprintError || !sprint) {
|
||||
return res.status(404).json({ error: "Sprint not found" });
|
||||
}
|
||||
|
||||
const { data: project, error: projectError } = await admin
|
||||
.from("gameforge_projects")
|
||||
.select("id")
|
||||
.eq("id", sprint.project_id)
|
||||
.eq("lead_id", user.id)
|
||||
.single();
|
||||
|
||||
if (projectError || !project) {
|
||||
return res.status(403).json({ error: "Not project lead" });
|
||||
}
|
||||
|
||||
const { data: updated, error: updateError } = await admin
|
||||
.from("gameforge_sprints")
|
||||
.update({
|
||||
title,
|
||||
description,
|
||||
goal,
|
||||
start_date: startDate,
|
||||
end_date: endDate,
|
||||
phase,
|
||||
status,
|
||||
})
|
||||
.eq("id", sprintId)
|
||||
.select()
|
||||
.single();
|
||||
|
||||
if (updateError) {
|
||||
return res.status(500).json({ error: updateError.message });
|
||||
}
|
||||
|
||||
return res.status(200).json(updated);
|
||||
}
|
||||
|
||||
return res.status(405).json({ error: "Method not allowed" });
|
||||
} catch (error: any) {
|
||||
console.error("[GameForge Sprint]", error);
|
||||
return res.status(500).json({ error: error?.message || "Server error" });
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue