FN-7377: hide failed banner in expanded planner chat
Hide failed-task alert chrome while the task planner chat is maximized. - Gate the failed-task banner on Planner Chat expansion state so the expanded conversation gets the full detail surface.\n- Preserve failed-banner visibility for normal detail, collapsed Planner Chat, and expanded Activity views.\n- Add dashboard regression coverage and a patch changeset for the published CLI package.\n\nFiles changed:\n .changeset/fn-7377-planner-chat-failed-banner.md | 7 ++\n .../dashboard/app/components/TaskDetailModal.tsx | 7 +-\n .../components/__tests__/TaskDetailModal.test.tsx | 101 +++++++++++++++++++++\n 3 files changed, 114 insertions(+), 1 deletion(-) Fusion-Task-Id: FN-7377 Fusion-Task-Lineage: 5cd8c846-a27d-4040-8124-ecf547300c78 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
This commit is contained in:
7
.changeset/fn-7377-planner-chat-failed-banner.md
Normal file
7
.changeset/fn-7377-planner-chat-failed-banner.md
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
"@runfusion/fusion": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
summary: Hide failed-task banners while task planner chat is maximized.
|
||||||
|
category: fix
|
||||||
|
dev: TaskDetailModal no longer mounts failed-banner chrome during expanded planner Chat; Activity and collapsed detail still show failures.
|
||||||
@@ -2728,6 +2728,11 @@ export function TaskDetailContent({
|
|||||||
Maximized Activity applies to Live, Feed, and Raw Logs, not only the legacy `current` chat segment. Reserve the detail surface for header context and Activity content, and do not mount branch-group chrome in this mode so expand/promote controls are not hidden-but-focusable.
|
Maximized Activity applies to Live, Feed, and Raw Logs, not only the legacy `current` chat segment. Reserve the detail surface for header context and Activity content, and do not mount branch-group chrome in this mode so expand/promote controls are not hidden-but-focusable.
|
||||||
*/
|
*/
|
||||||
const shouldShowBranchGroupCard = Boolean(task.branchContext?.groupId && !isActivityExpanded);
|
const shouldShowBranchGroupCard = Boolean(task.branchContext?.groupId && !isActivityExpanded);
|
||||||
|
/*
|
||||||
|
FNXC:TaskDetailPlannerChat 2026-07-01-00:00:
|
||||||
|
Maximized Planner Chat reserves vertical room for task identity and the planner conversation, so failed-task chrome is not mounted in that state. Normal detail, Activity expansion, and collapsed Planner Chat still surface task failures immediately.
|
||||||
|
*/
|
||||||
|
const shouldShowTaskFailureAlert = Boolean(task.status === "failed" && task.error && !isPlannerChatExpanded);
|
||||||
|
|
||||||
const taskActionMenuModel = useMemo(() => buildTaskActionMenuModel({
|
const taskActionMenuModel = useMemo(() => buildTaskActionMenuModel({
|
||||||
task,
|
task,
|
||||||
@@ -3463,7 +3468,7 @@ export function TaskDetailContent({
|
|||||||
{isWorkspaceTask(workingTask) && <WorkspaceWorktreesSummary task={workingTask} />}
|
{isWorkspaceTask(workingTask) && <WorkspaceWorktreesSummary task={workingTask} />}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{task.status === "failed" && task.error && (
|
{shouldShowTaskFailureAlert && (
|
||||||
<div className="detail-error-alert">
|
<div className="detail-error-alert">
|
||||||
<span className="detail-error-icon">⚠</span>
|
<span className="detail-error-icon">⚠</span>
|
||||||
<div className="detail-error-content">
|
<div className="detail-error-content">
|
||||||
|
|||||||
@@ -228,6 +228,107 @@ describe("TaskDetailModal planner Chat tab", () => {
|
|||||||
expect(detail).toHaveClass("task-detail-content--planner-chat-expanded");
|
expect(detail).toHaveClass("task-detail-content--planner-chat-expanded");
|
||||||
expect(detail).not.toHaveClass("task-detail-content--chat-expanded");
|
expect(detail).not.toHaveClass("task-detail-content--chat-expanded");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("hides failed-task banner only while planner Chat is expanded and restores it on collapse", async () => {
|
||||||
|
const user = userEvent.setup();
|
||||||
|
const { container } = render(
|
||||||
|
<TaskDetailModal
|
||||||
|
initialTab="planner-chat"
|
||||||
|
taskDetailChatFirst
|
||||||
|
task={makeTask({ column: "todo" as any, status: "failed", error: "Planner failed hard" })}
|
||||||
|
onClose={noop}
|
||||||
|
onMoveTask={noopMove}
|
||||||
|
onDeleteTask={noopDelete}
|
||||||
|
onMergeTask={noopMerge}
|
||||||
|
onOpenDetail={noopOpenDetail}
|
||||||
|
addToast={noop}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
const detail = container.querySelector(".task-detail-content");
|
||||||
|
|
||||||
|
expect(screen.getByText("Task Failed")).toBeInTheDocument();
|
||||||
|
expect(screen.getByText("Planner failed hard")).toBeInTheDocument();
|
||||||
|
expect(container.querySelector(".detail-error-alert")).toBeInTheDocument();
|
||||||
|
|
||||||
|
await user.click(screen.getByTestId("task-planner-chat-expand-toggle"));
|
||||||
|
|
||||||
|
expect(detail).toHaveClass("task-detail-content--planner-chat-expanded");
|
||||||
|
expect(screen.queryByText("Task Failed")).not.toBeInTheDocument();
|
||||||
|
expect(screen.queryByText("Planner failed hard")).not.toBeInTheDocument();
|
||||||
|
expect(container.querySelector(".detail-error-alert")).toBeNull();
|
||||||
|
|
||||||
|
await user.click(screen.getByTestId("task-planner-chat-expand-toggle"));
|
||||||
|
|
||||||
|
expect(detail).not.toHaveClass("task-detail-content--planner-chat-expanded");
|
||||||
|
expect(screen.getByText("Task Failed")).toBeInTheDocument();
|
||||||
|
expect(screen.getByText("Planner failed hard")).toBeInTheDocument();
|
||||||
|
expect(container.querySelector(".detail-error-alert")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("keeps failed-task banner visible while Activity is expanded", async () => {
|
||||||
|
const user = userEvent.setup();
|
||||||
|
const { container } = render(
|
||||||
|
<TaskDetailModal
|
||||||
|
initialTab="chat"
|
||||||
|
taskDetailChatFirst
|
||||||
|
task={makeTask({ column: "todo" as any, status: "failed", error: "Activity failure stays visible" })}
|
||||||
|
onClose={noop}
|
||||||
|
onMoveTask={noopMove}
|
||||||
|
onDeleteTask={noopDelete}
|
||||||
|
onMergeTask={noopMerge}
|
||||||
|
onOpenDetail={noopOpenDetail}
|
||||||
|
addToast={noop}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
const detail = container.querySelector(".task-detail-content");
|
||||||
|
|
||||||
|
expect(screen.getByText("Task Failed")).toBeInTheDocument();
|
||||||
|
expect(screen.getByText("Activity failure stays visible")).toBeInTheDocument();
|
||||||
|
|
||||||
|
await user.click(screen.getByTestId("task-chat-expand-toggle"));
|
||||||
|
|
||||||
|
expect(detail).toHaveClass("task-detail-content--chat-expanded");
|
||||||
|
expect(detail).not.toHaveClass("task-detail-content--planner-chat-expanded");
|
||||||
|
expect(screen.getByText("Task Failed")).toBeInTheDocument();
|
||||||
|
expect(screen.getByText("Activity failure stays visible")).toBeInTheDocument();
|
||||||
|
expect(container.querySelector(".detail-error-alert")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not render empty failed-task alert shells for non-failed or errorless failed tasks", () => {
|
||||||
|
const { container, rerender } = render(
|
||||||
|
<TaskDetailModal
|
||||||
|
initialTab="planner-chat"
|
||||||
|
taskDetailChatFirst
|
||||||
|
task={makeTask({ column: "todo" as any, status: "failed" })}
|
||||||
|
onClose={noop}
|
||||||
|
onMoveTask={noopMove}
|
||||||
|
onDeleteTask={noopDelete}
|
||||||
|
onMergeTask={noopMerge}
|
||||||
|
onOpenDetail={noopOpenDetail}
|
||||||
|
addToast={noop}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(screen.queryByText("Task Failed")).not.toBeInTheDocument();
|
||||||
|
expect(container.querySelector(".detail-error-alert")).toBeNull();
|
||||||
|
|
||||||
|
rerender(
|
||||||
|
<TaskDetailModal
|
||||||
|
initialTab="planner-chat"
|
||||||
|
taskDetailChatFirst
|
||||||
|
task={makeTask({ column: "todo" as any, status: "in-progress", error: "Ignored because task is not failed" })}
|
||||||
|
onClose={noop}
|
||||||
|
onMoveTask={noopMove}
|
||||||
|
onDeleteTask={noopDelete}
|
||||||
|
onMergeTask={noopMerge}
|
||||||
|
onOpenDetail={noopOpenDetail}
|
||||||
|
addToast={noop}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(screen.queryByText("Task Failed")).not.toBeInTheDocument();
|
||||||
|
expect(container.querySelector(".detail-error-alert")).toBeNull();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("TaskDetailModal summarize title action", () => {
|
describe("TaskDetailModal summarize title action", () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user