From 873712e0376c38365ce627f24c04410f3f908676 Mon Sep 17 00:00:00 2001 From: Max Date: Sat, 21 Feb 2026 17:50:42 -0600 Subject: [PATCH] Add individual get endpoints for projects and sprints MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New API endpoints: - GET /api/projects/[id] - Get single project - GET /api/sprints/[id] - Get single sprint New CLI commands: - project get - Get specific project - sprint get - Get specific sprint Project and Sprint CRUD now complete: ✅ list, get, create, update, delete --- scripts/gantt.sh | 28 +++++++++++++++++++++ src/app/api/projects/[id]/route.ts | 39 ++++++++++++++++++++++++++++++ src/app/api/sprints/[id]/route.ts | 39 ++++++++++++++++++++++++++++++ 3 files changed, 106 insertions(+) create mode 100644 src/app/api/projects/[id]/route.ts create mode 100644 src/app/api/sprints/[id]/route.ts diff --git a/scripts/gantt.sh b/scripts/gantt.sh index 7f4cf40..a06d13c 100755 --- a/scripts/gantt.sh +++ b/scripts/gantt.sh @@ -295,6 +295,18 @@ cmd_project_list() { api_call GET "/projects" | jq '.projects' } +cmd_project_get() { + local project_id="$1" + + if [ -z "$project_id" ]; then + log_error "Usage: project get " + exit 1 + fi + + log_info "Fetching project $project_id..." + api_call GET "/projects/$project_id" | jq '.project' +} + cmd_project_create() { local name="$1" local description="${2:-}" @@ -351,6 +363,18 @@ cmd_sprint_list() { api_call GET "/sprints" | jq '.sprints' } +cmd_sprint_get() { + local sprint_id="$1" + + if [ -z "$sprint_id" ]; then + log_error "Usage: sprint get " + exit 1 + fi + + log_info "Fetching sprint $sprint_id..." + api_call GET "/sprints/$sprint_id" | jq '.sprint' +} + cmd_sprint_create() { local name="$1" local project_id="$2" @@ -630,6 +654,7 @@ TASK COMMANDS: PROJECT COMMANDS: project list List all projects + project get Get specific project project create [desc] [color] Create new project project update @@ -639,6 +664,7 @@ PROJECT COMMANDS: SPRINT COMMANDS: sprint list List all sprints + sprint get Get specific sprint sprint create [start] [end] [goal] Create new sprint sprint update @@ -727,6 +753,7 @@ main() { shift || true case "$subcmd" in list|ls) cmd_project_list "$@" ;; + get|show) cmd_project_get "$@" ;; create|new|add) cmd_project_create "$@" ;; update|set|edit) cmd_project_update "$@" ;; delete|rm|remove) cmd_project_delete "$@" ;; @@ -738,6 +765,7 @@ main() { shift || true case "$subcmd" in list|ls) cmd_sprint_list "$@" ;; + get|show) cmd_sprint_get "$@" ;; create|new|add) cmd_sprint_create "$@" ;; update|set|edit) cmd_sprint_update "$@" ;; delete|rm|remove) cmd_sprint_delete "$@" ;; diff --git a/src/app/api/projects/[id]/route.ts b/src/app/api/projects/[id]/route.ts new file mode 100644 index 0000000..44ccc53 --- /dev/null +++ b/src/app/api/projects/[id]/route.ts @@ -0,0 +1,39 @@ +import { NextResponse } from "next/server"; +import { getServiceSupabase } from "@/lib/supabase/client"; +import { getAuthenticatedUser } from "@/lib/server/auth"; + +export const runtime = "nodejs"; + +// GET - fetch single project by ID +export async function GET( + request: Request, + { params }: { params: Promise<{ id: string }> } +) { + try { + const user = await getAuthenticatedUser(); + if (!user) { + return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); + } + + const { id } = await params; + + const supabase = getServiceSupabase(); + const { data, error } = await supabase + .from("projects") + .select("*") + .eq("id", id) + .single(); + + if (error) { + if (error.code === "PGRST116") { + return NextResponse.json({ error: "Project not found" }, { status: 404 }); + } + throw error; + } + + return NextResponse.json({ project: data }); + } catch (error) { + console.error(">>> API GET /projects/[id] error:", error); + return NextResponse.json({ error: "Failed to fetch project" }, { status: 500 }); + } +} diff --git a/src/app/api/sprints/[id]/route.ts b/src/app/api/sprints/[id]/route.ts new file mode 100644 index 0000000..0365950 --- /dev/null +++ b/src/app/api/sprints/[id]/route.ts @@ -0,0 +1,39 @@ +import { NextResponse } from "next/server"; +import { getServiceSupabase } from "@/lib/supabase/client"; +import { getAuthenticatedUser } from "@/lib/server/auth"; + +export const runtime = "nodejs"; + +// GET - fetch single sprint by ID +export async function GET( + request: Request, + { params }: { params: Promise<{ id: string }> } +) { + try { + const user = await getAuthenticatedUser(); + if (!user) { + return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); + } + + const { id } = await params; + + const supabase = getServiceSupabase(); + const { data, error } = await supabase + .from("sprints") + .select("*") + .eq("id", id) + .single(); + + if (error) { + if (error.code === "PGRST116") { + return NextResponse.json({ error: "Sprint not found" }, { status: 404 }); + } + throw error; + } + + return NextResponse.json({ sprint: data }); + } catch (error) { + console.error(">>> API GET /sprints/[id] error:", error); + return NextResponse.json({ error: "Failed to fetch sprint" }, { status: 500 }); + } +}