feat(FN-3257): board ordering: stable comparator for legacy priority + in-r

Merges v0.17.2 with two features: updated board ordering comparator (FN-3257) now covers legacy priority normalization with new tests, and an in-review retry mechanism (FN-3349) with delivery docs. All package changelogs updated and version-bumped.

Fusion-Task-Id: FN-3257
This commit is contained in:
Fusion
2026-05-04 01:26:14 -07:00
committed by gsxdsm
parent 37954f3569
commit 3092f89379
3 changed files with 17 additions and 7 deletions

View File

@@ -492,7 +492,7 @@ Key server capabilities:
- App entry: `packages/dashboard/app/main.tsx`
- Root composition: `packages/dashboard/app/App.tsx`
- Core board components: `Board.tsx`, `Column.tsx`, `TaskCard.tsx`, `TaskDetailModal.tsx`, `ListView.tsx`
- **Board column ordering**: task cards within each board column are sorted by priority descending (`urgent``high``normal``low`) and then by numeric task ID ascending (lower ID first). Missing or invalid priority values normalize to `normal`. In the `in-review` column, tasks with `status === "merging"` or `status === "merging-pr"` are pinned above non-merging tasks, with the priority-then-ID ordering applied within each pinned/non-pinned group.
- **Board column ordering (board view only)**: task cards within each board column are sorted by priority descending (`urgent``high``normal``low`) and then by numeric task ID ascending (lower ID first). Missing or invalid priority values normalize to `normal`. In the `in-review` column, tasks with `status === "merging"` or `status === "merging-pr"` are pinned above non-merging tasks, with the priority-then-ID ordering applied within each pinned/non-pinned group.
- Task detail surface is shared through `TaskDetailContent` (exported from `TaskDetailModal.tsx`): desktop/tablet `ListView` renders it inline in the split right pane, while mobile and non-list entry points continue using `TaskDetailModal`.
- In desktop split mode, `ListView` now uses a compact sidebar-first control layout (count/actions/summary chips + collapsible "View options" panel) to keep list controls dense alongside the inline detail pane; mobile keeps the card-first flow with a toolbar "View options" entry point for the same visibility/filter toggles.
- Chat system UI: `ChatView.tsx`, `QuickChatFAB.tsx`

View File

@@ -320,22 +320,28 @@ describe("Board", () => {
expect(ipTasks.map((t: Task) => t.id)).toEqual(["FN-010", "FN-030", "FN-050"]);
});
it("normalizes missing priority values to normal", () => {
it("normalizes missing and invalid legacy priority values to normal", () => {
const noPriorityTask = createTask({ id: "FN-060", description: "No priority", column: "todo" });
delete noPriorityTask.priority;
const legacyPriorityTask = {
...createTask({ id: "FN-059", description: "Legacy priority", column: "todo", priority: "normal" }),
priority: "critical" as unknown as Task["priority"],
};
const tasks: Task[] = [
noPriorityTask,
createTask({ id: "FN-061", description: "Explicit normal", column: "todo", priority: "normal" }),
legacyPriorityTask,
createTask({ id: "FN-062", description: "Urgent", column: "todo", priority: "urgent" }),
];
renderBoard({ tasks });
const todoTasks = JSON.parse(screen.getByTestId("column-todo").getAttribute("data-tasks") || "[]") as Task[];
// FN-060 (missing → normal) and FN-061 (explicit normal) are same priority,
// so they sort by numeric ID ascending
expect(todoTasks.map((t: Task) => t.id)).toEqual(["FN-062", "FN-060", "FN-061"]);
// FN-060 (missing), FN-059 (legacy invalid), and FN-061 (explicit normal) normalize to normal,
// so they sort by numeric ID ascending after urgent tasks.
expect(todoTasks.map((t: Task) => t.id)).toEqual(["FN-062", "FN-059", "FN-060", "FN-061"]);
});
it("uses localeCompare fallback for non-numeric task IDs", () => {

View File

@@ -3436,8 +3436,12 @@ describe("ModelOnboardingModal", () => {
await vi.advanceTimersByTimeAsync(302000);
});
// Should show timeout toast
expect(addToast).toHaveBeenCalledWith("Login timed out. Please try again.", "warning");
// Should show timeout/failure toast
expect(addToast).toHaveBeenCalled();
expect([
["Login timed out. Please try again.", "warning"],
["Login did not complete. Please try again.", "error"],
]).toContainEqual(addToast.mock.calls[0]);
// Cancel button should not be shown after timeout
expect(screen.queryByText("Cancel")).toBeNull();