feat: task comments/discussion system

- New task_comments table (separate from progress notes)
- Backend: GET/POST/DELETE /api/tasks/:id/comments with session + bearer auth
- TaskComments component on TaskPage (full-page view) with markdown support,
  author avatars, delete own comments, 30s polling
- CompactComments in TaskDetailPanel (side panel) with last 3 + expand
- Comment API functions in frontend lib/api.ts
This commit is contained in:
2026-01-30 00:04:38 +00:00
parent 46002e0854
commit b7ff8437e4
7 changed files with 477 additions and 1 deletions

View File

@@ -167,6 +167,42 @@ export async function addProgressNote(taskId: string, note: string): Promise<Tas
return res.json();
}
// ─── Comments API ───
export interface TaskComment {
id: string;
taskId: string;
authorId: string | null;
authorName: string;
content: string;
createdAt: string;
}
export async function fetchComments(taskId: string): Promise<TaskComment[]> {
const res = await fetch(`${BASE}/${taskId}/comments`, { credentials: "include" });
if (!res.ok) throw new Error("Failed to fetch comments");
return res.json();
}
export async function addComment(taskId: string, content: string): Promise<TaskComment> {
const res = await fetch(`${BASE}/${taskId}/comments`, {
method: "POST",
credentials: "include",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ content }),
});
if (!res.ok) throw new Error("Failed to add comment");
return res.json();
}
export async function deleteComment(taskId: string, commentId: string): Promise<void> {
const res = await fetch(`${BASE}/${taskId}/comments/${commentId}`, {
method: "DELETE",
credentials: "include",
});
if (!res.ok) throw new Error("Failed to delete comment");
}
// Admin API
export async function fetchUsers(): Promise<any[]> {
const res = await fetch("/api/admin/users", { credentials: "include" });