Open a task in its own modal from its title, with status moves, and edit its brief in the code editor

The modal shows who has the task and where, who added it and when it
moved, its files and notes (each note naming its bot). The task form's
fields all show their labels.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
2026-10-28 05:49:42 +01:00
co-authored by Claude Opus 5.5
parent 74da091dd4
commit c547e556ec
13 changed files with 462 additions and 240 deletions
+12 -6
View File
@@ -1,6 +1,5 @@
import BunSQLite from "@moduletrace/bun-sqlite";
import type { BUN_SQLITE_COBBLE_TASK_NOTES } from "@/db/types/db";
import type { TaskDetail } from "@/src/types";
import type { TaskDetail, TaskNote } from "@/src/types";
import AppError from "@/src/utils/app-error";
import botFaceSql from "@/src/utils/bot-face-sql";
import taskScopesSql from "@/src/utils/task-access/task-scopes-sql";
@@ -16,7 +15,8 @@ type Params = {
};
/**
* One task with its notes (oldest first) and files, only if the user
* One task with its notes (oldest first, each with the bot that wrote it),
* its files and who added it, only if the user
* reaches the bot or workspace it belongs to (`taskScopesSql`, the same
* condition the lists use).
*/
@@ -26,10 +26,14 @@ export default async function grabTask({ user_id, task_id }: Params): Promise<Ta
const row_res = await BunSQLite.sql<TaskDetail>({
sql: `SELECT t.*, b.name AS bot_name, b.kind AS bot_kind, b.hue AS bot_hue, b.color AS bot_color, b.stone AS bot_stone,
b.image_media_id AS bot_image_media_id, ${face.sql}, w.name AS workspace_name
b.image_media_id AS bot_image_media_id, ${face.sql}, w.name AS workspace_name,
cb.name AS created_by_bot_name,
NULLIF(TRIM(COALESCE(cu.first_name, '') || ' ' || COALESCE(cu.last_name, '')), '') AS created_by_user_name
FROM tasks t
LEFT JOIN bots b ON b.id = t.bot_id
LEFT JOIN workspaces w ON w.id = t.workspace_id
LEFT JOIN bots cb ON cb.id = t.created_by_bot_id
LEFT JOIN users cu ON cu.id = t.created_by_user_id
WHERE t.id = ? AND ${access.sql}
LIMIT 1`,
values: [...face.values, Number(task_id), ...access.values],
@@ -42,8 +46,10 @@ export default async function grabTask({ user_id, task_id }: Params): Promise<Ta
}
const [notes_res, files] = await Promise.all([
BunSQLite.sql<BUN_SQLITE_COBBLE_TASK_NOTES>({
sql: `SELECT * FROM task_notes WHERE task_id = ? ORDER BY id ASC`,
BunSQLite.sql<TaskNote>({
sql: `SELECT n.*, nb.name AS bot_name FROM task_notes n
LEFT JOIN bots nb ON nb.id = n.bot_id
WHERE n.task_id = ? ORDER BY n.id ASC`,
values: [Number(task_id)],
}),
grabTaskMedia({ task_id }),