Scope workspace tasks to the bot that has them
A workspace's task now carries the bot that has it there (bot_id with workspace_id), or none while it's shared. A bot reaches its own list and, in its workspaces, its own tasks plus the shared ones; a workspace admin every task there. Bots create workspace tasks for themselves by default, take a shared one by starting it, and hand-overs record the new bot. Removing or deleting a bot makes its workspace tasks shared again. The conversation's Tasks modal shows that bot's tasks in the workspace. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
@@ -14,8 +14,8 @@ type Params = {
|
||||
};
|
||||
|
||||
/**
|
||||
* A conversation's tasks: a bot's own list, or in a workspace the
|
||||
* workspace's shared one. Loaded on open and refreshed after every change,
|
||||
* A conversation's tasks: a bot's own list, or in a workspace the ones it
|
||||
* has there plus the shared ones nobody has yet. Loaded on open and refreshed after every change,
|
||||
* never part of the page props.
|
||||
*/
|
||||
export default function useBotTasks({ bot_id, workspace_id, enabled = true }: Params) {
|
||||
@@ -29,8 +29,8 @@ export default function useBotTasks({ bot_id, workspace_id, enabled = true }: Pa
|
||||
|
||||
const query = new URLSearchParams();
|
||||
|
||||
query.set("bot_id", String(bot_id));
|
||||
if (workspace_id) query.set("workspace_id", String(workspace_id));
|
||||
else query.set("bot_id", String(bot_id));
|
||||
|
||||
const res = await fetchApi<{}, APIResponseObject<TaskWithNames>>(
|
||||
`/api/admin/tasks/list-tasks?${query.toString()}`,
|
||||
|
||||
@@ -31,7 +31,7 @@ type Props = {
|
||||
|
||||
/**
|
||||
* The tasks this conversation works on: a bot's own list, or in a workspace
|
||||
* the workspace's shared one. Open it from the header menu; the list loads
|
||||
* the ones it has there plus the shared ones nobody has yet. Open it from the header menu; the list loads
|
||||
* then, so the page's props stay small. A crew manager adds, moves, edits
|
||||
* and deletes; a task opens to show its notes and files.
|
||||
*/
|
||||
@@ -50,7 +50,7 @@ export default function BotTasksModal({ bot, workspace, menu, on_open }: Props)
|
||||
});
|
||||
|
||||
const name = bot.name || "This bot";
|
||||
const title = workspace?.id ? `${workspace.name || "Workspace"} tasks` : `${name}'s tasks`;
|
||||
const title = workspace?.id ? `${name}'s tasks in ${workspace.name || "this workspace"}` : `${name}'s tasks`;
|
||||
|
||||
useEffect(() => {
|
||||
if (open) on_open?.();
|
||||
@@ -169,7 +169,7 @@ export default function BotTasksModal({ bot, workspace, menu, on_open }: Props)
|
||||
{!open_task ? (
|
||||
<Span className="text-[13px] leading-[18px] text-ink-secondary">
|
||||
{workspace?.id
|
||||
? `The shared to-do list of ${workspace.name || "this workspace"}. Every bot here sees it, and they keep it up to date as they work.`
|
||||
? `What ${name} has in ${workspace.name || "this workspace"}, and the shared tasks there nobody has taken up yet. The workspace shows every bot's.`
|
||||
: `What ${name} is working through, and what it's waiting on.`}
|
||||
</Span>
|
||||
) : null}
|
||||
@@ -184,9 +184,9 @@ export default function BotTasksModal({ bot, workspace, menu, on_open }: Props)
|
||||
set_status(msg);
|
||||
|
||||
/**
|
||||
* Handed to another bot, it's on that bot's list now.
|
||||
* Handed to another bot, it's that bot's now.
|
||||
*/
|
||||
if (!workspace?.id && saved.bot_id != bot.id) {
|
||||
if (saved.bot_id && saved.bot_id != bot.id) {
|
||||
setTasks((prev) => prev.filter((item) => item.id != saved.id));
|
||||
set_open_task(null);
|
||||
return;
|
||||
@@ -203,7 +203,7 @@ export default function BotTasksModal({ bot, workspace, menu, on_open }: Props)
|
||||
/>
|
||||
) : adding ? (
|
||||
<TaskForm
|
||||
bot_id={workspace?.id ? null : Number(bot.id)}
|
||||
bot_id={Number(bot.id)}
|
||||
workspace_id={workspace?.id ? Number(workspace.id) : null}
|
||||
onCancel={() => set_adding(false)}
|
||||
onSaved={(saved, msg) => {
|
||||
|
||||
@@ -25,8 +25,8 @@ type Props = {
|
||||
};
|
||||
|
||||
/**
|
||||
* A workspace's shared to-do list: every bot working there sees these, and
|
||||
* the coordinator sees them all. Add and change one by hand here; the bots
|
||||
* Every task in a workspace, whichever bot has it (or shared, nobody's
|
||||
* yet). Each bot there sees its own and the shared ones. Add and change one by hand here; the bots
|
||||
* keep it up to date themselves as they work.
|
||||
*/
|
||||
export default function WorkspaceTasksGroup({ workspace_id, tasks }: Props) {
|
||||
@@ -82,7 +82,7 @@ export default function WorkspaceTasksGroup({ workspace_id, tasks }: Props) {
|
||||
<SettingsGroup
|
||||
id="tasks"
|
||||
title="Tasks"
|
||||
description="The workspace's shared to-do list. Every bot here sees it, and they keep it up to date as they work."
|
||||
description="Every task in this workspace, whichever bot has it. A bot sees its own here and the shared ones nobody has taken up yet."
|
||||
>
|
||||
<Stack className="w-full gap-2! items-stretch">
|
||||
{open_task ? (
|
||||
@@ -153,7 +153,7 @@ export default function WorkspaceTasksGroup({ workspace_id, tasks }: Props) {
|
||||
open={adding}
|
||||
setOpen={setAdding}
|
||||
title="Add a task"
|
||||
description="It goes on the workspace's shared list, where every bot here can pick it up."
|
||||
description="It's added shared: whichever bot here takes it up has it. Hand it over to give it to one."
|
||||
>
|
||||
<TaskForm
|
||||
workspace_id={workspace_id}
|
||||
|
||||
@@ -5,8 +5,9 @@ import safeErrorMessage from "@/src/utils/safe-error-message";
|
||||
import type { APIResponseObject, BunextAPIRouteHandler } from "@moduletrace/bunext/types";
|
||||
|
||||
/**
|
||||
* The user's tasks: a bot's own list (`?bot_id=`), a workspace's shared one
|
||||
* (`?workspace_id=`), or every task they reach. `?status=` narrows to one
|
||||
* The user's tasks: a bot's own list (`?bot_id=`), a workspace's
|
||||
* (`?workspace_id=`), one bot's there with the unassigned ones
|
||||
* (`?workspace_id=&bot_id=`), or every task they reach. `?status=` narrows to one
|
||||
* status, `?open=1` to the open ones.
|
||||
*/
|
||||
export const handler: BunextAPIRouteHandler<APIResponseObject<TaskWithNames>> = async ({ req }) => {
|
||||
@@ -32,7 +33,9 @@ export const handler: BunextAPIRouteHandler<APIResponseObject<TaskWithNames>> =
|
||||
payload: await grabTasks({
|
||||
user_id: user.id,
|
||||
only_bot_id: bot_id,
|
||||
with_unassigned: Boolean(bot_id && workspace_id),
|
||||
only_workspace_id: workspace_id,
|
||||
own_list: Boolean(bot_id && !workspace_id),
|
||||
statuses: status ? [status] : undefined,
|
||||
open_only: open,
|
||||
}),
|
||||
|
||||
@@ -11,8 +11,8 @@ import safeErrorMessage from "@/src/utils/safe-error-message";
|
||||
import type { APIResponseObject, BunextAPIRouteHandler } from "@moduletrace/bunext/types";
|
||||
|
||||
/**
|
||||
* Adds a task to a bot's list or a workspace's (`bot_id` or
|
||||
* `workspace_id`), or changes one (`task_id`). Returns the task with its
|
||||
* Adds a task to a bot's list (`bot_id`), a workspace (`workspace_id`,
|
||||
* shared) or a bot in a workspace (both), or changes one (`task_id`). Returns the task with its
|
||||
* notes and files.
|
||||
*/
|
||||
export const handler: BunextAPIRouteHandler<APIResponseObject<TaskDetail>> = async ({ req, body }) => {
|
||||
|
||||
Reference in New Issue
Block a user