diff --git a/.changeset/fn-6956-immediate-delete-close.md b/.changeset/fn-6956-immediate-delete-close.md new file mode 100644 index 0000000000..eb9af5a114 --- /dev/null +++ b/.changeset/fn-6956-immediate-delete-close.md @@ -0,0 +1,5 @@ +--- +"@runfusion/fusion": patch +--- + +Close task detail dialogs and embedded task-detail hosts immediately after delete confirmations complete, while delete requests continue reporting success or error toasts asynchronously. diff --git a/docs/dashboard-guide.md b/docs/dashboard-guide.md index 0ce4b15586..5260be1c59 100644 --- a/docs/dashboard-guide.md +++ b/docs/dashboard-guide.md @@ -927,6 +927,7 @@ Inspect task definition, logs, review feedback, comments, artifacts, workflow ou - These two metadata controls share matched sizing/alignment in read mode (including mobile wrapping) so they behave like a single polished control group. - Task metadata keeps priority, execution mode, provenance, optional PR context, and compact `Created` / `Updated` timestamps in one wrapping row across desktop and mobile widths; recent timestamps render as relative time (`just now`, `Xm`, `Xh`, `Xd`) and older values switch to short month/day dates. - The **Actions** menu exposes **Pause** / **Unpause** for eligible non-terminal tasks, including tasks assigned to agents. If a task was paused by an agent, the **Paused by agent** note is informational; users can still unpause it manually from the same menu. +- After delete confirmations are complete, Task Detail closes immediately while the delete request finishes in the background; success and error outcomes still appear as toasts. - Eligible existing tasks (triage, todo, in-progress, in-review) expose a **GitHub tracking** section directly in Task Detail, even when tracking is currently disabled. - The GitHub tracking section now defaults to a compact summary row; use the disclosure arrow to expand linked-issue details plus tracking edit controls. - Backstop reconciliation runs every 15 minutes to close tracked GitHub issues for soft-deleted and archived tasks even after restart; the sweep is paginated so large archive backlogs are eventually drained. diff --git a/packages/dashboard/app/components/TaskDetailModal.tsx b/packages/dashboard/app/components/TaskDetailModal.tsx index 4b6c5b929e..9ce9f76d86 100644 --- a/packages/dashboard/app/components/TaskDetailModal.tsx +++ b/packages/dashboard/app/components/TaskDetailModal.tsx @@ -1888,6 +1888,18 @@ export function TaskDetailContent({ const handleDelete = useCallback(async () => { let allowResurrection = false; + let deleteCloseRequested = false; + const closeBeforeDeleteRequest = () => { + if (deleteCloseRequested) { + return; + } + /* + FNXC:TaskDetailDelete 2026-06-23-10:55: + Task detail hosts must close optimistically after the operator completes every required delete prompt and before the server delete request settles. Keep async success/error toasts attached to the delete promise so conflict handling and failure reporting continue after the modal, embedded panel, or floating host is gone. + */ + requestClose(); + deleteCloseRequested = true; + }; if (task.column !== "archived" && onArchiveTask) { const deleteChoice = await confirmWithChoice({ @@ -1975,12 +1987,12 @@ export function TaskDetailContent({ } try { + closeBeforeDeleteRequest(); if (githubIssueAction) { await onDeleteTask(task.id, { githubIssueAction, allowResurrection }); } else { await onDeleteTask(task.id, { allowResurrection }); } - requestClose(); const issueSuffix = trackedIssue?.owner && trackedIssue.repo && trackedIssue.number && githubIssueAction ? ` ${t("taskDetail.delete.issueSuffix", "and {{action}} issue {{ref}}", { action: githubIssueAction === "close" ? t("taskDetail.delete.actionClosed", "closed") : githubIssueAction === "delete" ? t("taskDetail.delete.actionDeleted", "deleted") : t("taskDetail.delete.actionLeft", "left"), ref: `${trackedIssue.owner}/${trackedIssue.repo}#${trackedIssue.number}` })}` : ""; @@ -2001,13 +2013,13 @@ export function TaskDetailContent({ } try { + closeBeforeDeleteRequest(); await onDeleteTask(task.id, { removeDependencyReferences: true, removeLineageReferences: true, githubIssueAction, allowResurrection, }); - requestClose(); addToast(t("taskDetail.delete.deletedAfterRemovingDeps", "Deleted {{id}} after removing dependency references", { id: task.id }), "info"); } catch (retryErr) { const lineageConflict = extractLineageDeleteConflict(retryErr); @@ -2028,13 +2040,13 @@ export function TaskDetailContent({ } try { + closeBeforeDeleteRequest(); await onDeleteTask(task.id, { removeDependencyReferences: true, removeLineageReferences: true, githubIssueAction, allowResurrection, }); - requestClose(); addToast(t("taskDetail.delete.deletedAfterUnlinkLineage", "Deleted {{id}} after unlinking lineage references", { id: task.id }), "info"); } catch (lineageRetryErr) { addToast(getErrorMessage(lineageRetryErr), "error"); @@ -2061,13 +2073,13 @@ export function TaskDetailContent({ } try { + closeBeforeDeleteRequest(); await onDeleteTask(task.id, { removeDependencyReferences: true, removeLineageReferences: true, githubIssueAction, allowResurrection, }); - requestClose(); addToast(t("taskDetail.delete.deletedAfterUnlinkLineage", "Deleted {{id}} after unlinking lineage references", { id: task.id }), "info"); } catch (retryErr) { addToast(getErrorMessage(retryErr), "error"); diff --git a/packages/dashboard/app/components/__tests__/TaskDetailModal.test.tsx b/packages/dashboard/app/components/__tests__/TaskDetailModal.test.tsx index c9423f0982..f9163f11fa 100644 --- a/packages/dashboard/app/components/__tests__/TaskDetailModal.test.tsx +++ b/packages/dashboard/app/components/__tests__/TaskDetailModal.test.tsx @@ -4,7 +4,7 @@ FN-6532 made Chat the default TaskDetailModal tab. Tests that assert Definition- */ import { describe, it, expect, vi } from "vitest"; import { render, screen, waitFor } from "@testing-library/react"; -import type { ComponentProps } from "react"; +import React, { type ComponentProps } from "react"; import userEvent from "@testing-library/user-event"; import { makeTask, @@ -14,9 +14,11 @@ import { noopMove, noopOpenDetail, setupTaskDetailModalHooks, + mockConfirm, + mockConfirmWithCheckbox, mockConfirmWithChoice, } from "./TaskDetailModal.test-helpers"; -import { TaskDetailModal } from "../TaskDetailModal"; +import { TaskDetailContent, TaskDetailModal } from "../TaskDetailModal"; vi.mock("../BranchGroupCard", () => ({ BranchGroupCard: ({ groupId }: { groupId: string }) =>