Persist workflow choices across dashboard board surfaces so operators return to their selected lane reliably. - Add project-scoped durable storage helpers for board workflow selection with stale-id validation and cleanup. - Share the workflow selection hook across Board, List, Header, and Graph surfaces while preserving cached payloads on transient refresh failures. - Update List quick-create/planning handoffs, cross-surface tests, and dashboard documentation for durable workflow selection behavior. - Add patch changesets for the published Fusion package. Files changed: .changeset/fn-7234-durable-board-workflow-selection.md | 7 ++ .changeset/workflow-selection-board-list.md | 7 ++ docs/dashboard-guide.md | 5 +- .../workflow-selection-cross-surface.test.tsx | 66 ++++++++-- packages/dashboard/app/components/ListView.tsx | 111 ++++------------- .../app/components/__tests__/Board.test.tsx | 45 +++++++ .../__tests__/GraphWorkflowSwitcherSlot.test.tsx | 57 +++++++++ .../__tests__/HeaderWorkflowSwitcherSlot.test.tsx | 42 ++++++- .../app/components/__tests__/ListView.test.tsx | 122 +++++++++++++++++++ .../app/hooks/__tests__/useBoardWorkflows.test.ts | 29 +++++ packages/dashboard/app/hooks/useBoardWorkflows.ts | 64 ++++++++- .../utils/__tests__/boardWorkflowSelection.test.ts | 135 +++++++++++++++++++++ .../app/utils/__tests__/projectStorage.test.ts | 3 +- .../dashboard/app/utils/boardWorkflowSelection.ts | 49 ++++++++ packages/dashboard/app/utils/projectStorage.ts | 1 + 15 files changed, 633 insertions(+), 110 deletions(-) Fusion-Task-Id: FN-7234 Fusion-Task-Lineage: 6ebc000c-19c3-4a8e-9303-4e18a59902ed Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
50 lines
1.9 KiB
TypeScript
50 lines
1.9 KiB
TypeScript
import { getScopedItem, removeScopedItem, setScopedItem } from "./projectStorage";
|
|
|
|
export const BOARD_WORKFLOW_SELECTION_STORAGE_KEY = "kb-dashboard-board-workflow-selection";
|
|
|
|
function isValidWorkflowSelection(value: unknown): value is string {
|
|
if (typeof value !== "string") return false;
|
|
|
|
const trimmed = value.trim();
|
|
if (trimmed.length === 0) return false;
|
|
if (trimmed.startsWith("{") || trimmed.startsWith("[")) return false;
|
|
if (/\p{C}/u.test(trimmed)) return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* FNXC:BoardWorkflowSelection 2026-06-29-12:00:
|
|
* Persist only the last selected workflow id in project-scoped localStorage so Board, Header, Graph, and List selectors can restore the operator's workflow after board remounts, task state changes, respecification returns, and browser/server restarts. Storage is best-effort because private-mode, SSR, missing APIs, and quota failures must never block board rendering.
|
|
*/
|
|
export function readBoardWorkflowSelection(projectId?: string): string | null {
|
|
try {
|
|
const stored = getScopedItem(BOARD_WORKFLOW_SELECTION_STORAGE_KEY, projectId);
|
|
return isValidWorkflowSelection(stored) ? stored.trim() : null;
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
export function writeBoardWorkflowSelection(projectId: string | undefined, workflowId: string): void {
|
|
try {
|
|
const trimmed = workflowId.trim();
|
|
if (!isValidWorkflowSelection(trimmed)) {
|
|
removeScopedItem(BOARD_WORKFLOW_SELECTION_STORAGE_KEY, projectId);
|
|
return;
|
|
}
|
|
|
|
setScopedItem(BOARD_WORKFLOW_SELECTION_STORAGE_KEY, trimmed, projectId);
|
|
} catch {
|
|
// Best-effort preference persistence; board rendering must continue on storage failures.
|
|
}
|
|
}
|
|
|
|
export function removeBoardWorkflowSelection(projectId?: string): void {
|
|
try {
|
|
removeScopedItem(BOARD_WORKFLOW_SELECTION_STORAGE_KEY, projectId);
|
|
} catch {
|
|
// Best-effort preference cleanup; storage failures are non-fatal.
|
|
}
|
|
}
|