feat(HAI-057): add task dependency editing to dashboard UI

- Add dependencies field support to store updateTask method
- Wire dependencies through PATCH route and client API
- Add dependency editing UI (add/remove) to TaskDetailModal
- Pass tasks prop from App to TaskDetailModal for dependency picker
- Add tests for dependency CRUD in modal and API layer
This commit is contained in:
Dustin Byrne
2026-03-26 00:15:29 -04:00
parent e41d3936d6
commit efda541713
9 changed files with 318 additions and 11 deletions

View File

@@ -199,6 +199,55 @@ describe("POST /tasks/:id/retry", () => {
});
});
describe("PATCH /tasks/:id", () => {
let store: TaskStore;
beforeEach(() => {
store = createMockStore();
});
function buildApp() {
const app = express();
app.use(express.json());
app.use("/api", createApiRoutes(store));
return app;
}
it("forwards dependencies to store.updateTask", async () => {
const updatedTask = { ...FAKE_TASK_DETAIL, dependencies: ["HAI-002"] };
(store.updateTask as ReturnType<typeof vi.fn>).mockResolvedValue(updatedTask);
const res = await REQUEST(buildApp(), "PATCH", "/api/tasks/HAI-001", JSON.stringify({ dependencies: ["HAI-002"] }), {
"Content-Type": "application/json",
});
expect(res.status).toBe(200);
expect(store.updateTask).toHaveBeenCalledWith("HAI-001", {
title: undefined,
description: undefined,
prompt: undefined,
dependencies: ["HAI-002"],
});
expect(res.body.dependencies).toEqual(["HAI-002"]);
});
it("forwards title and description without dependencies", async () => {
(store.updateTask as ReturnType<typeof vi.fn>).mockResolvedValue({ ...FAKE_TASK_DETAIL, title: "New" });
const res = await REQUEST(buildApp(), "PATCH", "/api/tasks/HAI-001", JSON.stringify({ title: "New" }), {
"Content-Type": "application/json",
});
expect(res.status).toBe(200);
expect(store.updateTask).toHaveBeenCalledWith("HAI-001", {
title: "New",
description: undefined,
prompt: undefined,
dependencies: undefined,
});
});
});
describe("Attachment routes", () => {
const FAKE_ATTACHMENT: TaskAttachment = {
filename: "1234-screenshot.png",

View File

@@ -194,11 +194,12 @@ export function createApiRoutes(store: TaskStore, options?: ServerOptions): Rout
// Update task
router.patch("/tasks/:id", async (req, res) => {
try {
const { title, description, prompt } = req.body;
const { title, description, prompt, dependencies } = req.body;
const task = await store.updateTask(req.params.id, {
title,
description,
prompt,
dependencies,
});
res.json(task);
} catch (err: any) {