FN-7935: route mailbox artifact View task to the popped-out task-detail window
Mailbox artifact "View task" now opens the producing task in the same shared, movable/resizable floating task-detail window used elsewhere in the dashboard, instead of the docked task-detail modal. - MainContent's MailboxView onOpenTask handler now calls popOutTaskDetail(task) after fetchTaskDetail resolves, instead of openDetailTask(task), matching DocumentsView's artifact-task open path - add regression test verifying mailbox artifact "View task" clicks resolve the task and route to popOutTaskDetail (not openDetailTask) - update docs/dashboard-guide.md to describe the shared movable/resizable task-detail window behavior - add changeset (patch) documenting the fix for @runfusion/fusion Files changed: .changeset/fn-7935-mailbox-artifact-view-task-popout.md | 7 ++ docs/dashboard-guide.md | 2 +- packages/dashboard/app/components/dashboard/MainContent.tsx | 8 ++- .../MainContent.mailbox-view-task.test.tsx | 83 ++++++++++++++++++++++ 4 files changed, 97 insertions(+), 3 deletions(-) Fusion-Task-Id: FN-7935 Fusion-Task-Lineage: 51374962-aa36-4390-a6b5-b519e7fc2bf2 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
This commit is contained in:
7
.changeset/fn-7935-mailbox-artifact-view-task-popout.md
Normal file
7
.changeset/fn-7935-mailbox-artifact-view-task-popout.md
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
summary: Mailbox artifact "View task" now opens the same movable, resizable task window used elsewhere.
|
||||
category: fix
|
||||
dev: MainContent mailbox onOpenTask routes fetchTaskDetail -> popOutTaskDetail (floating-window--task-detail) instead of the docked openDetailTask modal, matching DocumentsView's artifact-task path.
|
||||
@@ -632,7 +632,7 @@ Mailbox view shows inbox/outbox communication threads and unread state.
|
||||
- Inbox renders one row per message (no sender-based collapsing)
|
||||
- clicking a message in the Mail tab opens the task detail pane with full message content and conversation context
|
||||
- reply rows in the mailbox modal can expand inline to show the replied-to message context for easier thread reading
|
||||
- when an agent or dashboard chat session registers an artifact with `fn_artifact_register`, Fusion sends a best-effort `system` → user inbox message announcing the new artifact (for example, `New image artifact registered: <title>`) with metadata for `artifactId`, `artifactType`, `title`, optional `mimeType`, `authorId`, and optional `taskId`; notification delivery is informational and never blocks or rolls back the artifact registration. Artifact notifications are actionable in message detail views: image artifacts show an inline preview plus **Open artifact**, while video/audio/document/other artifacts show an **Open artifact** link to the managed media URL. When `taskId` metadata is present, the same artifact block also shows **View task** so users can open the producing task detail directly from the mailbox.
|
||||
- when an agent or dashboard chat session registers an artifact with `fn_artifact_register`, Fusion sends a best-effort `system` → user inbox message announcing the new artifact (for example, `New image artifact registered: <title>`) with metadata for `artifactId`, `artifactType`, `title`, optional `mimeType`, `authorId`, and optional `taskId`; notification delivery is informational and never blocks or rolls back the artifact registration. Artifact notifications are actionable in message detail views: image artifacts show an inline preview plus **Open artifact**, while video/audio/document/other artifacts show an **Open artifact** link to the managed media URL. When `taskId` metadata is present, the same artifact block also shows **View task** so users can open the producing task detail directly from the mailbox in the shared movable/resizable task-detail window.
|
||||
- on first engine startup under Fusion `0.59.x`, each project receives one best-effort `system` inbox notice about the upcoming embedded-Postgres storage migration with the Discord help link; `metadata.kind = "postgres-migration-notice"` prevents duplicates across restarts.
|
||||
- mailbox now includes an **Approvals** tab with pending and history filters (`approved` / `denied` / `completed`), approval detail context, and inline approve/deny actions for pending requests
|
||||
- for approvals gated by an agent's permission policy (permanent agents and task-worker heartbeats), the Approvals detail pane renders the gated action's real payload — tool name, shell command line or structured arguments, and working directory when present — instead of only a generic "Agent gated action for `<tool>`" summary; a stateless heartbeat retrying the same gated command reuses the existing pending approval instead of creating a duplicate (FN-7609)
|
||||
|
||||
@@ -358,10 +358,14 @@ export function MainContent({
|
||||
<MailboxView
|
||||
projectId={currentProject?.id}
|
||||
addToast={addToast}
|
||||
/* FNXC:ArtifactRegistry 2026-07-12-00:00: Artifact-registration mail notifications open their producing task through the shared task-detail fetch path so the mailbox does not invent a separate deep-link scheme. */
|
||||
/*
|
||||
FNXC:ArtifactRegistry 2026-07-12-00:00: Artifact-registration mail notifications open their producing task through the shared task-detail fetch path so the mailbox does not invent a separate deep-link scheme.
|
||||
|
||||
FNXC:ArtifactRegistry 2026-07-13-00:00: Mailbox artifact "View task" opens the producing task in the shared movable/resizable popped-out task-detail FloatingWindow (`popOutTaskDetail`), matching DocumentsView's artifact-task path instead of the docked `openDetailTask` modal, so the modal has full resize/move parity.
|
||||
*/
|
||||
onOpenTask={(taskId) => {
|
||||
void fetchTaskDetail(taskId, currentProject?.id)
|
||||
.then((task) => openDetailTask(task as TaskDetail))
|
||||
.then((task) => popOutTaskDetail(task))
|
||||
.catch(() => addToast?.("Failed to open task", "error"));
|
||||
}}
|
||||
onUnreadCountChange={setMailboxUnreadCount}
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
import { render, screen, waitFor } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import type { TaskDetail } from "@fusion/core";
|
||||
import { MainContent } from "../MainContent";
|
||||
import type { MainContentProps } from "../types";
|
||||
|
||||
const { fetchTaskDetailMock } = vi.hoisted(() => ({
|
||||
fetchTaskDetailMock: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("../../../api", () => ({
|
||||
fetchTaskDetail: fetchTaskDetailMock,
|
||||
}));
|
||||
|
||||
vi.mock("../../MailboxView", () => ({
|
||||
MailboxView: ({ onOpenTask }: { onOpenTask?: (taskId: string) => void }) => (
|
||||
<button type="button" onClick={() => onOpenTask?.("FN-7935")}>Open mailbox artifact task</button>
|
||||
),
|
||||
}));
|
||||
|
||||
function mainContentProps(overrides: Partial<MainContentProps> = {}): MainContentProps {
|
||||
return {
|
||||
showBackendConnectionErrorPage: false,
|
||||
projectsError: null,
|
||||
t: ((key: string, fallback?: string) => fallback ?? key) as MainContentProps["t"],
|
||||
retryingProjects: false,
|
||||
handleRetryProjects: vi.fn(),
|
||||
shellApi: null,
|
||||
taskView: "mailbox",
|
||||
modalManager: {} as MainContentProps["modalManager"],
|
||||
handleChangeTaskView: vi.fn(),
|
||||
refreshAppSettings: vi.fn(async () => undefined),
|
||||
addToast: vi.fn(),
|
||||
currentProject: { id: "project-1", name: "Project 1" } as MainContentProps["currentProject"],
|
||||
viewMode: "project",
|
||||
tasks: [],
|
||||
workflowSteps: [],
|
||||
openDetailTask: vi.fn(),
|
||||
popOutTaskDetail: vi.fn(),
|
||||
setMailboxUnreadCount: vi.fn(),
|
||||
settingsLoaded: true,
|
||||
skillsEnabled: true,
|
||||
insightsEnabled: true,
|
||||
researchEnabled: true,
|
||||
evalsEnabled: true,
|
||||
memoryEnabled: true,
|
||||
goalsEnabled: true,
|
||||
todosEnabled: true,
|
||||
nodesEnabled: true,
|
||||
capacityRiskBannerEnabled: false,
|
||||
capacityRiskDismissed: false,
|
||||
capacityRiskSignal: { level: "low", reasons: [] } as unknown as MainContentProps["capacityRiskSignal"],
|
||||
...overrides,
|
||||
} as unknown as MainContentProps;
|
||||
}
|
||||
|
||||
describe("MainContent mailbox artifact View task routing", () => {
|
||||
it("opens mailbox artifact tasks in the shared popped-out task-detail window", async () => {
|
||||
const fetchedTask = {
|
||||
id: "FN-7935",
|
||||
title: "Mailbox artifact task",
|
||||
description: "Task opened from a mailbox artifact message",
|
||||
column: "todo",
|
||||
status: "todo",
|
||||
dependencies: [],
|
||||
createdAt: new Date(0).toISOString(),
|
||||
updatedAt: new Date(0).toISOString(),
|
||||
steps: [],
|
||||
} as unknown as TaskDetail;
|
||||
const openDetailTask = vi.fn();
|
||||
const popOutTaskDetail = vi.fn();
|
||||
|
||||
fetchTaskDetailMock.mockResolvedValueOnce(fetchedTask);
|
||||
|
||||
render(<MainContent {...mainContentProps({ openDetailTask, popOutTaskDetail })} />);
|
||||
|
||||
screen.getByText("Open mailbox artifact task").click();
|
||||
|
||||
await waitFor(() => expect(popOutTaskDetail).toHaveBeenCalledWith(fetchedTask));
|
||||
expect(fetchTaskDetailMock).toHaveBeenCalledWith("FN-7935", "project-1");
|
||||
expect(openDetailTask).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user