feat(agent-api): POST /api/agent/cards/:cardId/subtasks
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
0e74dbdf14
commit
ded7ffbb1f
1 changed files with 68 additions and 0 deletions
68
src/app/api/agent/cards/[cardId]/subtasks/route.ts
Normal file
68
src/app/api/agent/cards/[cardId]/subtasks/route.ts
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
import { getAdminPb } from "@/lib/pb/admin";
|
||||
import { requireAgentToken } from "@/lib/agent/auth";
|
||||
import { positionBetween } from "@/lib/ordering/fractional";
|
||||
import { jsonError, jsonOk } from "@/lib/http/json";
|
||||
import type { CardRecord, SubtaskRecord } from "@/lib/pb/types";
|
||||
|
||||
/**
|
||||
* POST /api/agent/cards/:cardId/subtasks — turn a plan into a checklist.
|
||||
*
|
||||
* Appends after the card's existing subtasks, in the order given, using the
|
||||
* same fractional-position spacing every other subtask-creation path in this
|
||||
* app uses (see addSubtask in src/app/actions/subtasks.ts).
|
||||
*/
|
||||
export async function POST(
|
||||
request: Request,
|
||||
context: { params: Promise<{ cardId: string }> }
|
||||
): Promise<Response> {
|
||||
const denied = requireAgentToken(request);
|
||||
if (denied) return denied;
|
||||
|
||||
const { cardId } = await context.params;
|
||||
|
||||
let json: unknown;
|
||||
try {
|
||||
json = await request.json();
|
||||
} catch {
|
||||
return jsonError("Invalid request body", 400);
|
||||
}
|
||||
|
||||
const titles = (json as { titles?: unknown } | null)?.titles;
|
||||
if (!Array.isArray(titles)) {
|
||||
return jsonError("titles must be an array of strings", 400);
|
||||
}
|
||||
|
||||
const cleaned = titles
|
||||
.filter((t): t is string => typeof t === "string")
|
||||
.map((t) => t.trim().slice(0, 200))
|
||||
.filter((t) => t.length > 0);
|
||||
|
||||
if (cleaned.length === 0) {
|
||||
return jsonError("At least one non-empty title is required", 400);
|
||||
}
|
||||
|
||||
const pb = await getAdminPb();
|
||||
const card = await pb.collection("cards").getOne<CardRecord>(cardId).catch(() => null);
|
||||
if (!card || card.archived) return jsonError("Card not found", 404);
|
||||
|
||||
const last = await pb
|
||||
.collection("subtasks")
|
||||
.getFirstListItem<SubtaskRecord>(`card = "${cardId}"`, { sort: "-position" })
|
||||
.catch(() => null);
|
||||
|
||||
let position: number | null = last ? last.position : null;
|
||||
const created: string[] = [];
|
||||
|
||||
for (const title of cleaned) {
|
||||
position = positionBetween(position, null);
|
||||
const subtask = await pb.collection("subtasks").create({
|
||||
card: cardId,
|
||||
title,
|
||||
done: false,
|
||||
position,
|
||||
});
|
||||
created.push(subtask.id);
|
||||
}
|
||||
|
||||
return jsonOk({ created }, 201);
|
||||
}
|
||||
Loading…
Reference in a new issue