diff --git a/app/Http/Controllers/Api/DaemonController.php b/app/Http/Controllers/Api/DaemonController.php index c5e7aa7..6ed0022 100644 --- a/app/Http/Controllers/Api/DaemonController.php +++ b/app/Http/Controllers/Api/DaemonController.php @@ -15,6 +15,7 @@ use App\Models\Approval; use App\Models\Server; use App\Models\Task; +use App\Models\TaskWorkProduct; use App\Models\UsageEvent; use App\Services\AuditService; use App\Services\TaskCheckoutService; @@ -170,6 +171,21 @@ public function reportResult(ReportResultRequest $request, string $token, Task $ ]); } + // Create work product records + if (! empty($validated['work_products'])) { + foreach ($validated['work_products'] as $wp) { + TaskWorkProduct::query()->create([ + 'task_id' => $task->id, + 'agent_id' => $task->agent_id, + 'type' => $wp['type'] ?? 'file', + 'title' => $wp['title'], + 'file_path' => $wp['file_path'] ?? null, + 'url' => $wp['url'] ?? null, + 'summary' => $wp['summary'] ?? null, + ]); + } + } + // Process delegations — create sub-tasks for named direct reports if (! empty($validated['delegations']) && $task->agent?->delegation_enabled) { foreach ($validated['delegations'] as $delegation) { @@ -335,6 +351,33 @@ public function reportUsage(Request $request): JsonResponse return response()->json(['status' => 'recorded']); } + /** + * POST /api/daemon/{token}/tasks/{task}/notes + */ + public function postNote(Request $request, string $token, Task $task): JsonResponse + { + /** @var Server $server */ + $server = $request->get('daemon_server'); + + abort_unless( + $task->agent && $task->agent->server_id === $server->id, + 403, + 'Task agent is not on this server.', + ); + + $validated = $request->validate([ + 'body' => ['required', 'string', 'max:5000'], + ]); + + $note = $task->notes()->create([ + 'author_type' => 'agent', + 'author_id' => $task->agent_id, + 'body' => $validated['body'], + ]); + + return response()->json(['status' => 'ok', 'note' => $note]); + } + /** * POST /api/daemon/{token}/heartbeat */ diff --git a/app/Http/Controllers/GovernanceTaskController.php b/app/Http/Controllers/GovernanceTaskController.php index 571f120..5a20025 100644 --- a/app/Http/Controllers/GovernanceTaskController.php +++ b/app/Http/Controllers/GovernanceTaskController.php @@ -96,6 +96,7 @@ public function show(Request $request, Task $task): Response 'parentTask', 'subTasks.agent', 'usageEvents', + 'notes', ]); $auditEntries = $task->team->auditLogs() diff --git a/app/Http/Controllers/TaskNoteController.php b/app/Http/Controllers/TaskNoteController.php new file mode 100644 index 0000000..9f1b02d --- /dev/null +++ b/app/Http/Controllers/TaskNoteController.php @@ -0,0 +1,45 @@ +team_id === $request->user()->current_team_id, 403); + + $validated = $request->validate([ + 'body' => ['required', 'string', 'max:5000'], + ]); + + $task->notes()->create([ + 'author_type' => 'user', + 'author_id' => $request->user()->id, + 'body' => $validated['body'], + ]); + + if ($task->status === 'done') { + $oldStatus = $task->status; + + $task->update([ + 'status' => 'todo', + 'checked_out_by_run' => null, + 'checkout_expires_at' => null, + ]); + + event(new TaskStatusChangedEvent($task->load('agent'), $oldStatus, 'todo')); + } + + return redirect()->back(); + } +} diff --git a/database/factories/TaskNoteFactory.php b/database/factories/TaskNoteFactory.php new file mode 100644 index 0000000..7545ac4 --- /dev/null +++ b/database/factories/TaskNoteFactory.php @@ -0,0 +1,33 @@ + + */ +class TaskNoteFactory extends Factory +{ + /** + * @return array + */ + public function definition(): array + { + return [ + 'task_id' => Task::factory(), + 'author_type' => 'user', + 'author_id' => fake()->uuid(), + 'body' => fake()->paragraph(), + ]; + } + + public function byAgent(): static + { + return $this->state(fn (array $attributes) => [ + 'author_type' => 'agent', + ]); + } +} diff --git a/packages/provisiond/bundle/provisiond.mjs b/packages/provisiond/bundle/provisiond.mjs index 480287f..2dcd018 100755 --- a/packages/provisiond/bundle/provisiond.mjs +++ b/packages/provisiond/bundle/provisiond.mjs @@ -288,6 +288,7 @@ async function executeTask(task, config, api) { logger.info(`Skipping task ${taskLabel} \u2014 checkout failed (likely already checked out)`); return; } + await api.postNote(task.id, "Starting task..."); try { const prompt = buildPrompt(task); const port = task.agent.harness_type === "hermes" ? task.agent.api_server_port : OPENCLAW_DEFAULT_PORT; @@ -419,6 +420,15 @@ var ProvisionApiClient = class { }); } } + async postNote(taskId, body) { + const res = await this.request("POST", `/tasks/${taskId}/notes`, { body }); + if (!res.ok) { + logger.error(`Failed to post note for task ${taskId}`, { + status: res.status, + statusText: res.statusText + }); + } + } async sendHeartbeat(activeRuns2) { const res = await this.request("POST", "/heartbeat", { timestamp: (/* @__PURE__ */ new Date()).toISOString(), diff --git a/packages/provisiond/dist/executor.d.ts.map b/packages/provisiond/dist/executor.d.ts.map index 6fcbd63..c83ce2e 100644 --- a/packages/provisiond/dist/executor.d.ts.map +++ b/packages/provisiond/dist/executor.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"executor.d.ts","sourceRoot":"","sources":["../src/executor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAMH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAE7D,OAAO,KAAK,EAAE,MAAM,EAAE,aAAa,EAAc,MAAM,YAAY,CAAC;AAIpE,wBAAsB,WAAW,CAC/B,IAAI,EAAE,aAAa,EACnB,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,kBAAkB,GACtB,OAAO,CAAC,IAAI,CAAC,CAiGf"} \ No newline at end of file +{"version":3,"file":"executor.d.ts","sourceRoot":"","sources":["../src/executor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAMH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAE7D,OAAO,KAAK,EAAE,MAAM,EAAE,aAAa,EAAc,MAAM,YAAY,CAAC;AAIpE,wBAAsB,WAAW,CAC/B,IAAI,EAAE,aAAa,EACnB,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,kBAAkB,GACtB,OAAO,CAAC,IAAI,CAAC,CAoGf"} \ No newline at end of file diff --git a/packages/provisiond/dist/executor.js b/packages/provisiond/dist/executor.js index 452bdc5..acadda7 100644 --- a/packages/provisiond/dist/executor.js +++ b/packages/provisiond/dist/executor.js @@ -26,6 +26,8 @@ export async function executeTask(task, config, api) { logger.info(`Skipping task ${taskLabel} — checkout failed (likely already checked out)`); return; } + // Post a note so the thread reflects that work has begun + await api.postNote(task.id, 'Starting task...'); try { // Step 2: Build prompt const prompt = buildPrompt(task); diff --git a/packages/provisiond/dist/executor.js.map b/packages/provisiond/dist/executor.js.map index 32c2581..f059f99 100644 --- a/packages/provisiond/dist/executor.js.map +++ b/packages/provisiond/dist/executor.js.map @@ -1 +1 @@ -{"version":3,"file":"executor.js","sourceRoot":"","sources":["../src/executor.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"executor.js","sourceRoot":"","sources":["../src/executor.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/packages/provisiond/dist/provision-api.d.ts b/packages/provisiond/dist/provision-api.d.ts index 6644c31..fe710ec 100644 --- a/packages/provisiond/dist/provision-api.d.ts +++ b/packages/provisiond/dist/provision-api.d.ts @@ -18,6 +18,7 @@ export declare class ProvisionApiClient { releaseTask(taskId: string, runId: string, reason?: string): Promise; getResolvedApprovals(): Promise; reportUsage(event: UsageEvent): Promise; + postNote(taskId: string, body: string): Promise; sendHeartbeat(activeRuns: string[]): Promise; private request; } diff --git a/packages/provisiond/dist/provision-api.d.ts.map b/packages/provisiond/dist/provision-api.d.ts.map index cb39883..aa9a3f6 100644 --- a/packages/provisiond/dist/provision-api.d.ts.map +++ b/packages/provisiond/dist/provision-api.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"provision-api.d.ts","sourceRoot":"","sources":["../src/provision-api.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,UAAU,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAElG,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;gBAEnB,MAAM,EAAE,MAAM;IAKpB,YAAY,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC;IAMxC,YAAY,CAChB,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,IAAI,CAAC,EAAE,aAAa,CAAA;KAAE,CAAC;IAsB3C,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAS/D,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAa1E,oBAAoB,IAAI,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAMnD,WAAW,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAU7C,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;YAa1C,OAAO;CAoBtB"} \ No newline at end of file +{"version":3,"file":"provision-api.d.ts","sourceRoot":"","sources":["../src/provision-api.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,UAAU,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAElG,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;gBAEnB,MAAM,EAAE,MAAM;IAKpB,YAAY,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC;IAMxC,YAAY,CAChB,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,IAAI,CAAC,EAAE,aAAa,CAAA;KAAE,CAAC;IAsB3C,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAS/D,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAa1E,oBAAoB,IAAI,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAMnD,WAAW,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAU7C,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAUrD,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;YAa1C,OAAO;CAoBtB"} \ No newline at end of file diff --git a/packages/provisiond/dist/provision-api.js b/packages/provisiond/dist/provision-api.js index 88164a9..8b3219a 100644 --- a/packages/provisiond/dist/provision-api.js +++ b/packages/provisiond/dist/provision-api.js @@ -67,6 +67,15 @@ export class ProvisionApiClient { }); } } + async postNote(taskId, body) { + const res = await this.request('POST', `/tasks/${taskId}/notes`, { body }); + if (!res.ok) { + logger.error(`Failed to post note for task ${taskId}`, { + status: res.status, + statusText: res.statusText, + }); + } + } async sendHeartbeat(activeRuns) { const res = await this.request('POST', '/heartbeat', { timestamp: new Date().toISOString(), diff --git a/packages/provisiond/dist/provision-api.js.map b/packages/provisiond/dist/provision-api.js.map index a6fdf41..b26500c 100644 --- a/packages/provisiond/dist/provision-api.js.map +++ b/packages/provisiond/dist/provision-api.js.map @@ -1 +1 @@ -{"version":3,"file":"provision-api.js","sourceRoot":"","sources":["../src/provision-api.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"provision-api.js","sourceRoot":"","sources":["../src/provision-api.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAGrC,MAAM,OAAO,kBAAkB;IACZ,OAAO,CAAS;IAChB,KAAK,CAAS;IAE/B,YAAY,MAAc;QACxB,IAAI,CAAC,OAAO,GAAG,GAAG,MAAM,CAAC,MAAM,eAAe,MAAM,CAAC,WAAW,EAAE,CAAC;QACnE,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC,WAAW,CAAC;IAClC,CAAC;IAED,KAAK,CAAC,YAAY;QAChB,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,aAAa,CAAC,CAAC;QACrD,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAA+B,CAAC;QAC9D,OAAO,IAAI,CAAC,KAAK,CAAC;IACpB,CAAC;IAED,KAAK,CAAC,YAAY,CAChB,MAAc,EACd,KAAa;QAEb,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,UAAU,MAAM,WAAW,EAAE;YAClE,aAAa,EAAE,KAAK;SACrB,CAAC,CAAC;QAEH,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YACvB,MAAM,CAAC,KAAK,CAAC,QAAQ,MAAM,sBAAsB,CAAC,CAAC;YACnD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC;QACvB,CAAC;QAED,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,MAAM,CAAC,KAAK,CAAC,4BAA4B,MAAM,EAAE,EAAE;gBACjD,MAAM,EAAE,GAAG,CAAC,MAAM;gBAClB,UAAU,EAAE,GAAG,CAAC,UAAU;aAC3B,CAAC,CAAC;YACH,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC;QACvB,CAAC;QAED,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAA4B,CAAC;QAC3D,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC;IACvC,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,MAAc,EAAE,MAAkB;QACnD,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,UAAU,MAAM,SAAS,EAAE,MAAM,CAAC,CAAC;QAC1E,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,MAAM,IAAI,KAAK,CACb,oCAAoC,MAAM,KAAK,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,UAAU,EAAE,CAC9E,CAAC;QACJ,CAAC;IACH,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,MAAc,EAAE,KAAa,EAAE,MAAe;QAC9D,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,UAAU,MAAM,UAAU,EAAE;YACjE,aAAa,EAAE,KAAK;YACpB,MAAM;SACP,CAAC,CAAC;QACH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,MAAM,CAAC,KAAK,CAAC,0BAA0B,MAAM,EAAE,EAAE;gBAC/C,MAAM,EAAE,GAAG,CAAC,MAAM;gBAClB,UAAU,EAAE,GAAG,CAAC,UAAU;aAC3B,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,KAAK,CAAC,oBAAoB;QACxB,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,qBAAqB,CAAC,CAAC;QAC7D,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAsC,CAAC;QACrE,OAAO,IAAI,CAAC,SAAS,CAAC;IACxB,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,KAAiB;QACjC,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,eAAe,EAAE,KAAK,CAAC,CAAC;QAC/D,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,MAAM,CAAC,KAAK,CAAC,8BAA8B,EAAE;gBAC3C,MAAM,EAAE,GAAG,CAAC,MAAM;gBAClB,UAAU,EAAE,GAAG,CAAC,UAAU;aAC3B,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,MAAc,EAAE,IAAY;QACzC,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,UAAU,MAAM,QAAQ,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;QAC3E,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,MAAM,CAAC,KAAK,CAAC,gCAAgC,MAAM,EAAE,EAAE;gBACrD,MAAM,EAAE,GAAG,CAAC,MAAM;gBAClB,UAAU,EAAE,GAAG,CAAC,UAAU;aAC3B,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,UAAoB;QACtC,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,YAAY,EAAE;YACnD,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACnC,WAAW,EAAE,UAAU;SACxB,CAAC,CAAC;QACH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,MAAM,CAAC,IAAI,CAAC,kBAAkB,EAAE;gBAC9B,MAAM,EAAE,GAAG,CAAC,MAAM;gBAClB,UAAU,EAAE,GAAG,CAAC,UAAU;aAC3B,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,OAAO,CACnB,MAAc,EACd,IAAY,EACZ,IAAc;QAEd,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,OAAO,GAAG,IAAI,EAAE,CAAC;QACrC,MAAM,OAAO,GAA2B;YACtC,MAAM,EAAE,kBAAkB;YAC1B,cAAc,EAAE,kBAAkB;SACnC,CAAC;QAEF,MAAM,IAAI,GAAgB,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;QAC9C,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACvB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QACnC,CAAC;QAED,MAAM,CAAC,KAAK,CAAC,GAAG,MAAM,IAAI,IAAI,EAAE,CAAC,CAAC;QAElC,OAAO,KAAK,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IAC1B,CAAC;CACF"} \ No newline at end of file diff --git a/packages/provisiond/src/executor.ts b/packages/provisiond/src/executor.ts index 9fa87e4..8d86d6c 100644 --- a/packages/provisiond/src/executor.ts +++ b/packages/provisiond/src/executor.ts @@ -38,6 +38,9 @@ export async function executeTask( return; } + // Post a note so the thread reflects that work has begun + await api.postNote(task.id, 'Starting task...'); + try { // Step 2: Build prompt const prompt = buildPrompt(task); @@ -88,6 +91,12 @@ export async function executeTask( title: a.title, description: a.description, })), + work_products: parsed.workProducts.map((wp) => ({ + title: wp.title, + file_path: wp.filePath, + type: 'file', + summary: wp.summary, + })), }; await api.reportResult(task.id, result); diff --git a/packages/provisiond/src/provision-api.ts b/packages/provisiond/src/provision-api.ts index fc78d45..fac411e 100644 --- a/packages/provisiond/src/provision-api.ts +++ b/packages/provisiond/src/provision-api.ts @@ -86,6 +86,16 @@ export class ProvisionApiClient { } } + async postNote(taskId: string, body: string): Promise { + const res = await this.request('POST', `/tasks/${taskId}/notes`, { body }); + if (!res.ok) { + logger.error(`Failed to post note for task ${taskId}`, { + status: res.status, + statusText: res.statusText, + }); + } + } + async sendHeartbeat(activeRuns: string[]): Promise { const res = await this.request('POST', '/heartbeat', { timestamp: new Date().toISOString(), diff --git a/resources/js/components/task-comment-thread.tsx b/resources/js/components/task-comment-thread.tsx new file mode 100644 index 0000000..9b4ccf1 --- /dev/null +++ b/resources/js/components/task-comment-thread.tsx @@ -0,0 +1,108 @@ +import { router } from '@inertiajs/react'; +import { MessageSquare } from 'lucide-react'; +import { FormEvent, useState } from 'react'; +import { Badge } from '@/components/ui/badge'; +import { Button } from '@/components/ui/button'; +import { Textarea } from '@/components/ui/textarea'; +import type { TaskNote } from '@/types'; + +function formatDate(d: string): string { + return new Date(d).toLocaleString(undefined, { + month: 'short', + day: 'numeric', + hour: '2-digit', + minute: '2-digit', + }); +} + +export default function TaskCommentThread({ + notes, + taskId, + taskStatus, +}: { + notes: TaskNote[]; + taskId: string; + taskStatus: string; +}) { + const [body, setBody] = useState(''); + const [processing, setProcessing] = useState(false); + + function handleSubmit(e: FormEvent) { + e.preventDefault(); + if (!body.trim() || processing) { + return; + } + + setProcessing(true); + router.post( + `/company/tasks/${taskId}/notes`, + { body }, + { + preserveScroll: true, + onSuccess: () => setBody(''), + onFinish: () => setProcessing(false), + }, + ); + } + + return ( +
+

+ + Comments ({notes.length}) +

+ + {notes.length > 0 && ( +
+ {notes.map((note) => ( +
+
+ + {note.author_type === 'user' + ? 'User' + : 'Agent'} + + + {formatDate(note.created_at)} + +
+

+ {note.body} +

+
+ ))} +
+ )} + +
+