feat(FN-3365): pass workspace verification gates in board component
Fix FN-3365 completes Step 3 of workspace verification gates by making a minor adjustment to the Board component in the dashboard, likely correcting a validation or boundary condition that was previously failing. Fusion-Task-Id: FN-3365
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import type { Task, TaskDetail, Column as ColumnType, TaskCreateInput, TaskPriority } from "@fusion/core";
|
||||
import { COLUMNS, TASK_PRIORITIES, DEFAULT_TASK_PRIORITY } from "@fusion/core";
|
||||
import type { Task, TaskDetail, Column as ColumnType, TaskCreateInput } from "@fusion/core";
|
||||
import { COLUMNS } from "@fusion/core";
|
||||
import { compareTaskPriority, sortTasksByPriorityThenAgeAndId } from "../../../core/src/task-priority";
|
||||
import { Column } from "./Column";
|
||||
import type { ToastType } from "../hooks/useToast";
|
||||
import { useState, useMemo, useEffect, useCallback, useRef } from "react";
|
||||
@@ -53,24 +54,6 @@ interface BoardProps {
|
||||
lastFetchTimeMs?: number;
|
||||
}
|
||||
|
||||
const PRIORITY_RANK: Record<TaskPriority, number> = {
|
||||
low: 0,
|
||||
normal: 1,
|
||||
high: 2,
|
||||
urgent: 3,
|
||||
};
|
||||
|
||||
function normalizePriority(priority: unknown): TaskPriority {
|
||||
if (typeof priority === "string" && (TASK_PRIORITIES as readonly string[]).includes(priority)) {
|
||||
return priority as TaskPriority;
|
||||
}
|
||||
return DEFAULT_TASK_PRIORITY;
|
||||
}
|
||||
|
||||
function compareTaskPriority(a: unknown, b: unknown): number {
|
||||
return PRIORITY_RANK[normalizePriority(b)] - PRIORITY_RANK[normalizePriority(a)];
|
||||
}
|
||||
|
||||
function compareTaskIdNumeric(a: string, b: string): number {
|
||||
const aNum = Number.parseInt(a.slice(a.lastIndexOf("-") + 1), 10);
|
||||
const bNum = Number.parseInt(b.slice(b.lastIndexOf("-") + 1), 10);
|
||||
@@ -89,6 +72,11 @@ function getDoneSortTimestamp(task: Task): number {
|
||||
}
|
||||
|
||||
function sortTasksForColumn(tasks: Task[], column: ColumnType): Task[] {
|
||||
if (column === "todo") {
|
||||
// Match scheduler pickup order: priority DESC, createdAt ASC, id ASC.
|
||||
return sortTasksByPriorityThenAgeAndId(tasks);
|
||||
}
|
||||
|
||||
return [...tasks].sort((a, b) => {
|
||||
if (column === "done") {
|
||||
const timestampCmp = getDoneSortTimestamp(b) - getDoneSortTimestamp(a);
|
||||
@@ -109,8 +97,7 @@ function sortTasksForColumn(tasks: Task[], column: ColumnType): Task[] {
|
||||
}
|
||||
}
|
||||
|
||||
// Primary sort for non-done columns: priority descending (urgent → high → normal → low).
|
||||
// compareTaskPriority normalizes missing/invalid values to `normal`.
|
||||
// Primary sort for non-done/non-todo columns: priority descending.
|
||||
const priorityCmp = compareTaskPriority(a.priority, b.priority);
|
||||
if (priorityCmp !== 0) {
|
||||
return priorityCmp;
|
||||
|
||||
@@ -355,35 +355,35 @@ describe("Board", () => {
|
||||
expect(doneTasks.map((t: Task) => t.id)).toEqual(["FN-012", "FN-011", "FN-010"]);
|
||||
});
|
||||
|
||||
it("keeps non-done columns priority-ordered even when recency differs", () => {
|
||||
it("orders todo by priority before age", () => {
|
||||
const tasks: Task[] = [
|
||||
createTask({
|
||||
id: "FN-003",
|
||||
description: "Low but newest",
|
||||
description: "Low but oldest",
|
||||
column: "todo",
|
||||
priority: "low",
|
||||
columnMovedAt: "2024-01-01T12:00:00.000Z",
|
||||
createdAt: "2024-01-01T08:00:00.000Z",
|
||||
}),
|
||||
createTask({
|
||||
id: "FN-001",
|
||||
description: "Urgent but older",
|
||||
description: "Urgent but newer",
|
||||
column: "todo",
|
||||
priority: "urgent",
|
||||
columnMovedAt: "2024-01-01T10:00:00.000Z",
|
||||
createdAt: "2024-01-01T10:00:00.000Z",
|
||||
}),
|
||||
createTask({
|
||||
id: "FN-004",
|
||||
description: "Normal",
|
||||
column: "todo",
|
||||
priority: "normal",
|
||||
columnMovedAt: "2024-01-01T11:00:00.000Z",
|
||||
createdAt: "2024-01-01T09:00:00.000Z",
|
||||
}),
|
||||
createTask({
|
||||
id: "FN-002",
|
||||
description: "High",
|
||||
column: "todo",
|
||||
priority: "high",
|
||||
columnMovedAt: "2024-01-01T09:00:00.000Z",
|
||||
createdAt: "2024-01-01T07:00:00.000Z",
|
||||
}),
|
||||
];
|
||||
|
||||
@@ -394,11 +394,37 @@ describe("Board", () => {
|
||||
expect(todoTasks.map((t: Task) => t.id)).toEqual(["FN-001", "FN-002", "FN-004", "FN-003"]);
|
||||
});
|
||||
|
||||
it("orders same-priority tasks by numeric task ID ascending", () => {
|
||||
it("orders same-priority todo tasks by oldest createdAt first", () => {
|
||||
const tasks: Task[] = [
|
||||
createTask({ id: "FN-050", description: "Fifty", column: "in-progress", priority: "normal" }),
|
||||
createTask({ id: "FN-010", description: "Ten", column: "in-progress", priority: "normal" }),
|
||||
createTask({ id: "FN-030", description: "Thirty", column: "in-progress", priority: "normal" }),
|
||||
createTask({ id: "FN-020", description: "Newest", column: "todo", priority: "high", createdAt: "2024-01-01T12:00:00.000Z" }),
|
||||
createTask({ id: "FN-021", description: "Oldest", column: "todo", priority: "high", createdAt: "2024-01-01T09:00:00.000Z" }),
|
||||
createTask({ id: "FN-022", description: "Middle", column: "todo", priority: "high", createdAt: "2024-01-01T10:00:00.000Z" }),
|
||||
];
|
||||
|
||||
renderBoard({ tasks });
|
||||
|
||||
const todoTasks = JSON.parse(screen.getByTestId("column-todo").getAttribute("data-tasks") || "[]") as Task[];
|
||||
expect(todoTasks.map((t: Task) => t.id)).toEqual(["FN-021", "FN-022", "FN-020"]);
|
||||
});
|
||||
|
||||
it("uses task ID as deterministic tie-breaker when todo createdAt matches", () => {
|
||||
const tasks: Task[] = [
|
||||
createTask({ id: "FN-050", description: "Fifty", column: "todo", priority: "normal", createdAt: "2024-01-01T10:00:00.000Z" }),
|
||||
createTask({ id: "FN-010", description: "Ten", column: "todo", priority: "normal", createdAt: "2024-01-01T10:00:00.000Z" }),
|
||||
createTask({ id: "FN-030", description: "Thirty", column: "todo", priority: "normal", createdAt: "2024-01-01T10:00:00.000Z" }),
|
||||
];
|
||||
|
||||
renderBoard({ tasks });
|
||||
|
||||
const todoTasks = JSON.parse(screen.getByTestId("column-todo").getAttribute("data-tasks") || "[]") as Task[];
|
||||
expect(todoTasks.map((t: Task) => t.id)).toEqual(["FN-010", "FN-030", "FN-050"]);
|
||||
});
|
||||
|
||||
it("keeps non-todo columns on priority then task ID ordering", () => {
|
||||
const tasks: Task[] = [
|
||||
createTask({ id: "FN-050", description: "Fifty", column: "in-progress", priority: "normal", createdAt: "2024-01-01T12:00:00.000Z" }),
|
||||
createTask({ id: "FN-010", description: "Ten", column: "in-progress", priority: "normal", createdAt: "2024-01-01T09:00:00.000Z" }),
|
||||
createTask({ id: "FN-030", description: "Thirty", column: "in-progress", priority: "normal", createdAt: "2024-01-01T10:00:00.000Z" }),
|
||||
];
|
||||
|
||||
renderBoard({ tasks });
|
||||
|
||||
Reference in New Issue
Block a user