mirror of
https://github.com/rennf93/roboco.git
synced 2026-08-03 07:23:24 +02:00
fix(panel): align task actions to server contract + collect required audit notes
The panel's human action buttons had drifted from the server request
schemas: wrong field names (qa_notes/reason vs notes), missing bodies
(cancel/complete/submit-pm-review), and a bare-string docs-complete body —
so cancel/pass-qa/fail-qa/escalate-to-ceo 4xx'd and decisions recorded no
audit note. (Agents were unaffected — they go through the gateway.)
- tasks.ts: pass-qa/fail-qa -> {notes}; escalate-to-ceo -> {notes:reason};
cancel -> {reason}; complete -> {justification}; docs-complete -> {notes};
submit-pm-review -> {notes}.
- New reusable RequiredNotesDialog (generalizes CeoApproveDialog). Every
decision action now collects a substantive note before POSTing: cancel
(>=10), pass-qa/fail-qa/docs-complete/submit-pm-review/complete (>=20),
matching the server gates. Wired in the task detail page, the actions
dropdown, and the kanban board.
Verified: pnpm tsc --noEmit and eslint both clean.
This commit is contained in:
+11
-11
@@ -242,7 +242,7 @@ export const tasksApi = {
|
||||
mockTasks[idx] = { ...mockTasks[idx], status: TaskStatus.AWAITING_DOCUMENTATION, qa_verified: true, qa_notes: qaNotes ?? null, updated_at: now };
|
||||
return mockTasks[idx];
|
||||
}
|
||||
const { data } = await api.post<Task>("/tasks/" + taskId + "/pass-qa", { qa_notes: qaNotes });
|
||||
const { data } = await api.post<Task>("/tasks/" + taskId + "/pass-qa", { notes: qaNotes });
|
||||
return data;
|
||||
},
|
||||
|
||||
@@ -254,11 +254,11 @@ export const tasksApi = {
|
||||
mockTasks[idx] = { ...mockTasks[idx], status: TaskStatus.NEEDS_REVISION, qa_verified: false, qa_notes: qaNotes ?? null, updated_at: now };
|
||||
return mockTasks[idx];
|
||||
}
|
||||
const { data } = await api.post<Task>("/tasks/" + taskId + "/fail-qa", { qa_notes: qaNotes });
|
||||
const { data } = await api.post<Task>("/tasks/" + taskId + "/fail-qa", { notes: qaNotes });
|
||||
return data;
|
||||
},
|
||||
|
||||
complete: async (taskId: string): Promise<Task> => {
|
||||
complete: async (taskId: string, justification: string): Promise<Task> => {
|
||||
if (isMockMode()) {
|
||||
const idx = mockTasks.findIndex((t) => t.id === taskId);
|
||||
if (idx === -1) throw new Error("Task not found");
|
||||
@@ -266,11 +266,11 @@ export const tasksApi = {
|
||||
mockTasks[idx] = { ...mockTasks[idx], status: TaskStatus.COMPLETED, completed_at: now, updated_at: now };
|
||||
return mockTasks[idx];
|
||||
}
|
||||
const { data } = await api.post<Task>("/tasks/" + taskId + "/complete");
|
||||
const { data } = await api.post<Task>("/tasks/" + taskId + "/complete", { justification });
|
||||
return data;
|
||||
},
|
||||
|
||||
cancel: async (taskId: string): Promise<Task> => {
|
||||
cancel: async (taskId: string, reason: string): Promise<Task> => {
|
||||
if (isMockMode()) {
|
||||
const idx = mockTasks.findIndex((t) => t.id === taskId);
|
||||
if (idx === -1) throw new Error("Task not found");
|
||||
@@ -278,7 +278,7 @@ export const tasksApi = {
|
||||
mockTasks[idx] = { ...mockTasks[idx], status: TaskStatus.CANCELLED, updated_at: now };
|
||||
return mockTasks[idx];
|
||||
}
|
||||
const { data } = await api.post<Task>("/tasks/" + taskId + "/cancel");
|
||||
const { data } = await api.post<Task>("/tasks/" + taskId + "/cancel", { reason });
|
||||
return data;
|
||||
},
|
||||
|
||||
@@ -310,7 +310,7 @@ export const tasksApi = {
|
||||
},
|
||||
|
||||
// Mark documentation as complete (Documenter only)
|
||||
docsComplete: async (taskId: string, docNotes?: string): Promise<Task> => {
|
||||
docsComplete: async (taskId: string, docNotes: string): Promise<Task> => {
|
||||
if (isMockMode()) {
|
||||
const idx = mockTasks.findIndex((t) => t.id === taskId);
|
||||
if (idx === -1) throw new Error("Task not found");
|
||||
@@ -318,7 +318,7 @@ export const tasksApi = {
|
||||
mockTasks[idx] = { ...mockTasks[idx], status: TaskStatus.AWAITING_PM_REVIEW, updated_at: now };
|
||||
return mockTasks[idx];
|
||||
}
|
||||
const { data } = await api.post<Task>("/tasks/" + taskId + "/docs-complete", docNotes ?? null);
|
||||
const { data } = await api.post<Task>("/tasks/" + taskId + "/docs-complete", { notes: docNotes });
|
||||
return data;
|
||||
},
|
||||
|
||||
@@ -517,7 +517,7 @@ export const tasksApi = {
|
||||
return data;
|
||||
},
|
||||
|
||||
submitPmReview: async (taskId: string): Promise<Task> => {
|
||||
submitPmReview: async (taskId: string, notes: string): Promise<Task> => {
|
||||
if (isMockMode()) {
|
||||
const idx = mockTasks.findIndex((t) => t.id === taskId);
|
||||
if (idx === -1) throw new Error("Task not found");
|
||||
@@ -529,7 +529,7 @@ export const tasksApi = {
|
||||
};
|
||||
return mockTasks[idx];
|
||||
}
|
||||
const { data } = await api.post<Task>("/tasks/" + taskId + "/submit-pm-review");
|
||||
const { data } = await api.post<Task>("/tasks/" + taskId + "/submit-pm-review", { notes });
|
||||
return data;
|
||||
},
|
||||
|
||||
@@ -626,7 +626,7 @@ export const tasksApi = {
|
||||
message: "Task escalated to CEO (mock)",
|
||||
};
|
||||
}
|
||||
const { data } = await api.post<EscalateResponse>("/tasks/" + taskId + "/escalate-to-ceo", { reason });
|
||||
const { data } = await api.post<EscalateResponse>("/tasks/" + taskId + "/escalate-to-ceo", { notes: reason });
|
||||
return data;
|
||||
},
|
||||
|
||||
|
||||
Reference in New Issue
Block a user