feat(FN-5123): merge fusion/fn-5123
This commit is contained in:
@@ -1611,16 +1611,18 @@ export function ListView({
|
||||
<div className="list-empty-cell list-card-empty">No tasks</div>
|
||||
) : (
|
||||
columnTasks.map((task) => {
|
||||
const isFailed = task.status === "failed";
|
||||
const isPaused = task.paused === true;
|
||||
const isDoneColumn = task.column === "done";
|
||||
const visualStatus = isDoneColumn ? "done" : task.status;
|
||||
const isFailed = !isDoneColumn && task.status === "failed";
|
||||
const isPaused = !isDoneColumn && task.paused === true;
|
||||
const isStuckState = isTaskStuck(task, taskStuckTimeoutMs, lastFetchTimeMs);
|
||||
const isAgentActive =
|
||||
!globalPaused &&
|
||||
!isFailed &&
|
||||
!isPaused &&
|
||||
!isStuckState &&
|
||||
(task.column === "in-progress" || ACTIVE_STATUSES.has(task.status as string));
|
||||
const hasStatus = typeof task.status === "string" && task.status.trim().length > 0;
|
||||
(task.column === "in-progress" || ACTIVE_STATUSES.has(visualStatus as string));
|
||||
const hasStatus = typeof visualStatus === "string" && visualStatus.trim().length > 0;
|
||||
const hasDependencies = Boolean(task.dependencies && task.dependencies.length > 0);
|
||||
const taskProgress = getTaskProgress(task);
|
||||
const hasProgress = taskProgress.hasProgress;
|
||||
@@ -1668,7 +1670,7 @@ export function ListView({
|
||||
<span className="list-status-badge stuck">Stuck</span>
|
||||
) : hasStatus ? (
|
||||
<span className={`list-status-badge list-status-badge--${task.column}${isFailed ? " failed" : ""}${isAgentActive ? " pulsing" : ""}`}>
|
||||
{getTaskStatusLabel(task.status ?? "")}
|
||||
{getTaskStatusLabel(visualStatus ?? "")}
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
@@ -1801,15 +1803,17 @@ export function ListView({
|
||||
</tr>
|
||||
) : (
|
||||
columnTasks.map((task) => {
|
||||
const isFailed = task.status === "failed";
|
||||
const isPaused = task.paused === true;
|
||||
const isDoneColumn = task.column === "done";
|
||||
const visualStatus = isDoneColumn ? "done" : task.status;
|
||||
const isFailed = !isDoneColumn && task.status === "failed";
|
||||
const isPaused = !isDoneColumn && task.paused === true;
|
||||
const isStuckState = isTaskStuck(task, taskStuckTimeoutMs, lastFetchTimeMs);
|
||||
const isAgentActive =
|
||||
!globalPaused &&
|
||||
!isFailed &&
|
||||
!isPaused &&
|
||||
!isStuckState &&
|
||||
(task.column === "in-progress" || ACTIVE_STATUSES.has(task.status as string));
|
||||
(task.column === "in-progress" || ACTIVE_STATUSES.has(visualStatus as string));
|
||||
const isDragging = draggingTaskId === task.id;
|
||||
|
||||
return (
|
||||
@@ -1869,13 +1873,13 @@ export function ListView({
|
||||
<span className="list-status-badge stuck">
|
||||
Stuck
|
||||
</span>
|
||||
) : task.status ? (
|
||||
) : visualStatus ? (
|
||||
<span
|
||||
className={`list-status-badge list-status-badge--${task.column}${isFailed ? " failed" : ""}${
|
||||
isAgentActive ? " pulsing" : ""
|
||||
}`}
|
||||
>
|
||||
{getTaskStatusLabel(task.status ?? "")}
|
||||
{getTaskStatusLabel(visualStatus ?? "")}
|
||||
</span>
|
||||
) : (
|
||||
<span className="list-status-badge">-</span>
|
||||
|
||||
@@ -782,9 +782,11 @@ function TaskCardComponent({
|
||||
}
|
||||
}, [onOpenDetail, addToast]);
|
||||
|
||||
const isFailed = task.status === "failed";
|
||||
const isPaused = task.paused === true || task.userPaused === true;
|
||||
const pausedByAgent = Boolean(task.paused && task.pausedByAgentId);
|
||||
const isDoneColumn = task.column === "done";
|
||||
const visualStatus = isDoneColumn ? "done" : task.status;
|
||||
const isFailed = !isDoneColumn && task.status === "failed";
|
||||
const isPaused = !isDoneColumn && (task.paused === true || task.userPaused === true);
|
||||
const pausedByAgent = Boolean(!isDoneColumn && task.paused && task.pausedByAgentId);
|
||||
const normalizedPriority = normalizeTaskPriorityValue(task.priority);
|
||||
const showPriorityBadge = normalizedPriority !== DEFAULT_TASK_PRIORITY;
|
||||
const isStuck = isTaskStuck(task, taskStuckTimeoutMs, lastFetchTimeMs);
|
||||
@@ -803,7 +805,7 @@ function TaskCardComponent({
|
||||
const taskAgeStalenessCopy = getTaskAgeStalenessCopy(task.ageStaleness);
|
||||
const isAwaitingApproval = task.column === "triage" && task.status === "awaiting-approval";
|
||||
const isArchived = task.column === "archived";
|
||||
const isAgentActive = !globalPaused && !queued && !isFailed && !isPaused && !isStuck && !isAwaitingApproval && (task.column === "in-progress" || ACTIVE_STATUSES.has(task.status as string));
|
||||
const isAgentActive = !globalPaused && !queued && !isFailed && !isPaused && !isStuck && !isAwaitingApproval && (task.column === "in-progress" || ACTIVE_STATUSES.has(visualStatus as string));
|
||||
const isDraggable = !disableDrag && !queued && !isPaused && !isEditing && !isArchived; // Disable drag during edit/archived or host embedding
|
||||
|
||||
// Check if this card can be edited inline
|
||||
@@ -1552,11 +1554,11 @@ function TaskCardComponent({
|
||||
{pausedByAgent ? "paused by agent" : "paused"}
|
||||
</span>
|
||||
)}
|
||||
{!isPaused && task.status && task.status !== "queued" && (
|
||||
{!isPaused && visualStatus && visualStatus !== "queued" && (
|
||||
<span
|
||||
className={`card-status-badge card-status-badge--${task.column}${isAwaitingApproval ? " awaiting-approval" : ""}${ACTIVE_STATUSES.has(task.status) ? " pulsing" : ""}${isFailed ? " failed" : ""}${isStuck ? " stuck" : ""}`}
|
||||
className={`card-status-badge card-status-badge--${task.column}${isAwaitingApproval ? " awaiting-approval" : ""}${ACTIVE_STATUSES.has(visualStatus) ? " pulsing" : ""}${isFailed ? " failed" : ""}${isStuck ? " stuck" : ""}`}
|
||||
>
|
||||
{isStuck ? "Stuck" : isAwaitingApproval ? "Awaiting Approval" : getTaskStatusLabel(task.status)}
|
||||
{isStuck ? "Stuck" : isAwaitingApproval ? "Awaiting Approval" : getTaskStatusLabel(visualStatus)}
|
||||
</span>
|
||||
)}
|
||||
{hasInReviewStall && stallCopy && (
|
||||
|
||||
@@ -288,6 +288,30 @@ describe("ListView", () => {
|
||||
matchMediaSpy.mockRestore();
|
||||
});
|
||||
|
||||
it("keeps done status badge in table view when stale paused metadata exists", () => {
|
||||
const tasks = [
|
||||
createMockTask({ id: "FN-001", column: "done", status: "paused", paused: true, pausedByAgentId: "agent-1" }),
|
||||
];
|
||||
|
||||
renderListView({ tasks });
|
||||
expect(screen.queryByText("paused by agent")).toBeNull();
|
||||
expect(screen.queryByText("paused")).toBeNull();
|
||||
expect(screen.getByText("done")).toBeDefined();
|
||||
});
|
||||
|
||||
it("keeps done status badge in mobile card view when stale paused metadata exists", () => {
|
||||
const matchMediaSpy = mockMobileViewport();
|
||||
const tasks = [
|
||||
createMockTask({ id: "FN-001", column: "done", status: "paused", paused: true, pausedByAgentId: "agent-1" }),
|
||||
];
|
||||
|
||||
renderListView({ tasks });
|
||||
expect(screen.queryByText("paused by agent")).toBeNull();
|
||||
expect(screen.queryByText("paused")).toBeNull();
|
||||
expect(screen.getByText("done")).toBeDefined();
|
||||
matchMediaSpy.mockRestore();
|
||||
});
|
||||
|
||||
it("shows empty state when no tasks", () => {
|
||||
renderListView({ tasks: [] });
|
||||
expect(screen.getByText("No tasks yet")).toBeDefined();
|
||||
|
||||
@@ -969,6 +969,30 @@ describe("TaskCard", () => {
|
||||
expect(screen.queryByText("paused by agent")).toBeNull();
|
||||
});
|
||||
|
||||
it("keeps done status badge when stale paused metadata exists", () => {
|
||||
const { container } = render(
|
||||
<TaskCard
|
||||
task={makeTask({ column: "done", status: "paused", paused: true, userPaused: true, pausedByAgentId: "agent-1" })}
|
||||
onOpenDetail={noop}
|
||||
addToast={noop}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(screen.queryByText("paused by agent")).toBeNull();
|
||||
expect(screen.queryByText("paused")).toBeNull();
|
||||
expect(screen.getByText("done")).toBeDefined();
|
||||
expect(container.querySelector(".card")?.className).not.toContain("paused");
|
||||
});
|
||||
|
||||
it("keeps done status badge when done task status is paused", () => {
|
||||
render(
|
||||
<TaskCard task={makeTask({ column: "done", status: "paused", paused: false, userPaused: false })} onOpenDetail={noop} addToast={noop} />,
|
||||
);
|
||||
|
||||
expect(screen.queryByText("paused")).toBeNull();
|
||||
expect(screen.getByText("done")).toBeDefined();
|
||||
});
|
||||
|
||||
it("renders decision-only badge when noCommitsExpected is true", () => {
|
||||
render(<TaskCard task={makeTask({ noCommitsExpected: true })} onOpenDetail={noop} addToast={noop} />);
|
||||
expect(screen.getByText("decision-only")).toBeTruthy();
|
||||
|
||||
Reference in New Issue
Block a user