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)
|
- 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
|
- 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
|
- 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.
|
- 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
|
- 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)
|
- 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
|
<MailboxView
|
||||||
projectId={currentProject?.id}
|
projectId={currentProject?.id}
|
||||||
addToast={addToast}
|
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) => {
|
onOpenTask={(taskId) => {
|
||||||
void fetchTaskDetail(taskId, currentProject?.id)
|
void fetchTaskDetail(taskId, currentProject?.id)
|
||||||
.then((task) => openDetailTask(task as TaskDetail))
|
.then((task) => popOutTaskDetail(task))
|
||||||
.catch(() => addToast?.("Failed to open task", "error"));
|
.catch(() => addToast?.("Failed to open task", "error"));
|
||||||
}}
|
}}
|
||||||
onUnreadCountChange={setMailboxUnreadCount}
|
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