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:
@@ -492,7 +492,7 @@ Key server capabilities:
|
|||||||
- App entry: `packages/dashboard/app/main.tsx`
|
- App entry: `packages/dashboard/app/main.tsx`
|
||||||
- Root composition: `packages/dashboard/app/App.tsx`
|
- Root composition: `packages/dashboard/app/App.tsx`
|
||||||
- Core board components: `Board.tsx`, `Column.tsx`, `TaskCard.tsx`, `TaskDetailModal.tsx`, `ListView.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`.
|
- 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.
|
- 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`
|
- Chat system UI: `ChatView.tsx`, `QuickChatFAB.tsx`
|
||||||
|
|||||||
@@ -320,22 +320,28 @@ describe("Board", () => {
|
|||||||
expect(ipTasks.map((t: Task) => t.id)).toEqual(["FN-010", "FN-030", "FN-050"]);
|
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" });
|
const noPriorityTask = createTask({ id: "FN-060", description: "No priority", column: "todo" });
|
||||||
delete noPriorityTask.priority;
|
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[] = [
|
const tasks: Task[] = [
|
||||||
noPriorityTask,
|
noPriorityTask,
|
||||||
createTask({ id: "FN-061", description: "Explicit normal", column: "todo", priority: "normal" }),
|
createTask({ id: "FN-061", description: "Explicit normal", column: "todo", priority: "normal" }),
|
||||||
|
legacyPriorityTask,
|
||||||
createTask({ id: "FN-062", description: "Urgent", column: "todo", priority: "urgent" }),
|
createTask({ id: "FN-062", description: "Urgent", column: "todo", priority: "urgent" }),
|
||||||
];
|
];
|
||||||
|
|
||||||
renderBoard({ tasks });
|
renderBoard({ tasks });
|
||||||
|
|
||||||
const todoTasks = JSON.parse(screen.getByTestId("column-todo").getAttribute("data-tasks") || "[]") as Task[];
|
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,
|
// FN-060 (missing), FN-059 (legacy invalid), and FN-061 (explicit normal) normalize to normal,
|
||||||
// so they sort by numeric ID ascending
|
// so they sort by numeric ID ascending after urgent tasks.
|
||||||
expect(todoTasks.map((t: Task) => t.id)).toEqual(["FN-062", "FN-060", "FN-061"]);
|
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", () => {
|
it("uses localeCompare fallback for non-numeric task IDs", () => {
|
||||||
|
|||||||
@@ -3436,8 +3436,12 @@ describe("ModelOnboardingModal", () => {
|
|||||||
await vi.advanceTimersByTimeAsync(302000);
|
await vi.advanceTimersByTimeAsync(302000);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Should show timeout toast
|
// Should show timeout/failure toast
|
||||||
expect(addToast).toHaveBeenCalledWith("Login timed out. Please try again.", "warning");
|
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
|
// Cancel button should not be shown after timeout
|
||||||
expect(screen.queryByText("Cancel")).toBeNull();
|
expect(screen.queryByText("Cancel")).toBeNull();
|
||||||
|
|||||||
Reference in New Issue
Block a user