From ce4f173d8fa598cfef8a9323c1a5303ada4bf27d Mon Sep 17 00:00:00 2001 From: gsxdsm Date: Tue, 7 Jul 2026 20:18:59 -0700 Subject: [PATCH] FN-7649: resolve settings landing view to board on project switch Fixes project-switch hydration so a persisted "settings" view resolves to the Board instead of re-opening Settings. - Extend resolveLandingTaskView() in useViewState.ts to treat "settings" the same as "command-center", resolving both to "board" for the auto-restored/hydrated landing view only - Add regression tests covering the settings->board landing resolution in useViewState.test.ts - Add changeset documenting the patch-level fix Files changed: .changeset/fn-7649-project-switch-board-landing.md | 7 ++ .../app/hooks/__tests__/useViewState.test.ts | 74 ++++++++++++++++++++++ packages/dashboard/app/hooks/useViewState.ts | 5 +- 3 files changed, 85 insertions(+), 1 deletion(-) Fusion-Task-Id: FN-7649 Fusion-Task-Lineage: 7179efd6-bb1b-4ea4-a062-479b9b1fffa3 Co-authored-by: Fusion (runfusion.ai) --- .../fn-7649-project-switch-board-landing.md | 7 ++ .../app/hooks/__tests__/useViewState.test.ts | 74 +++++++++++++++++++ packages/dashboard/app/hooks/useViewState.ts | 5 +- 3 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 .changeset/fn-7649-project-switch-board-landing.md diff --git a/.changeset/fn-7649-project-switch-board-landing.md b/.changeset/fn-7649-project-switch-board-landing.md new file mode 100644 index 0000000000..1460846f7a --- /dev/null +++ b/.changeset/fn-7649-project-switch-board-landing.md @@ -0,0 +1,7 @@ +--- +"@runfusion/fusion": patch +--- + +summary: Switching projects now lands on the Board instead of Settings when the last-visited view was Settings. +category: fix +dev: Extended resolveLandingTaskView in useViewState.ts to resolve "settings" (in addition to "command-center") to "board" for the auto-restored/hydrated landing view only; deep links (?view=settings) and explicit navigation still open Settings. diff --git a/packages/dashboard/app/hooks/__tests__/useViewState.test.ts b/packages/dashboard/app/hooks/__tests__/useViewState.test.ts index b544741f05..ce7ac25950 100644 --- a/packages/dashboard/app/hooks/__tests__/useViewState.test.ts +++ b/packages/dashboard/app/hooks/__tests__/useViewState.test.ts @@ -77,6 +77,55 @@ describe("useViewState", () => { }); }); + // FNXC:ViewState FN-7649: Persisted Settings must not be the auto-restored landing view either; it lands on the Board instead. + it("lands on board when the persisted taskView is settings", async () => { + localStorage.setItem("kb-dashboard-task-view", "settings"); + + const { result } = renderHook(() => useViewState(createOptions())); + + await waitFor(() => { + expect(result.current.taskView).toBe("board"); + }); + }); + + it("?view=settings deep link still opens Settings (not routed through the landing guard)", async () => { + const originalSearch = window.location.search; + window.history.replaceState({}, "", "?view=settings"); + + try { + const { result } = renderHook(() => useViewState(createOptions())); + + await waitFor(() => { + expect(result.current.taskView).toBe("settings"); + }); + } finally { + window.history.replaceState({}, "", originalSearch ? `?${originalSearch.replace(/^\?/, "")}` : "/"); + } + }); + + it("explicit setTaskView/handleChangeTaskView to settings still opens Settings", async () => { + const { result } = renderHook(() => useViewState(createOptions())); + + await waitFor(() => { + expect(result.current.taskView).toBe("board"); + }); + + await act(async () => { + result.current.setTaskView("settings"); + }); + + expect(result.current.taskView).toBe("settings"); + + await act(async () => { + result.current.handleChangeTaskView("board"); + }); + await act(async () => { + result.current.handleChangeTaskView("settings"); + }); + + expect(result.current.taskView).toBe("settings"); + }); + it("migrates legacy reliability taskView from localStorage to Command Center", async () => { localStorage.setItem("kb-dashboard-task-view", "reliability"); @@ -578,6 +627,31 @@ describe("useViewState", () => { }); }); + // FNXC:ViewState FN-7649: Switching projects (rerender with a new currentProject) must not land on Settings when the newly selected project's scoped persisted view is settings; it resolves to board. Mirrors the existing command-center project-switch coverage above. + it("lands on board when switching to a project whose persisted scoped taskView is settings", async () => { + const projectA: ProjectInfo = { ...PROJECT, id: "proj_a", name: "Project A" }; + const projectB: ProjectInfo = { ...PROJECT, id: "proj_b", name: "Project B" }; + + localStorage.setItem("kb:proj_a:kb-dashboard-task-view", "list"); + localStorage.setItem("kb:proj_b:kb-dashboard-task-view", "settings"); + + const { result, rerender } = renderHook( + ({ project }) => useViewState(createOptions({ currentProject: project })), + { initialProps: { project: projectA } }, + ); + + await waitFor(() => { + expect(result.current.taskView).toBe("list"); + }); + + // Switch to project B, whose persisted view is settings - must land on board, not settings. + rerender({ project: projectB }); + + await waitFor(() => { + expect(result.current.taskView).toBe("board"); + }); + }); + it("no cross-project bleed when switching projects", async () => { const projectA: ProjectInfo = { ...PROJECT, id: "proj_a", name: "Project A" }; const projectB: ProjectInfo = { ...PROJECT, id: "proj_b", name: "Project B" }; diff --git a/packages/dashboard/app/hooks/useViewState.ts b/packages/dashboard/app/hooks/useViewState.ts index 12e0cfd7b8..7bbe5bbce3 100644 --- a/packages/dashboard/app/hooks/useViewState.ts +++ b/packages/dashboard/app/hooks/useViewState.ts @@ -76,9 +76,12 @@ function normalizeTaskView(value: TaskView): TaskView { /* FNXC:ViewState 2026-06-22-15:30: Fusion must land on the Board on load, never the Command Center "Dashboard" view. A persisted/normalized `command-center` value resolves to `board` for the auto-restored landing view only (initializer + project-hydration effect). Deep links (`?view=command-center`) and explicit user navigation still reach the Command Center — this only governs the restored landing surface. + +FNXC:ViewState 2026-07-07-00:00: +FN-7649: an auto-restored/hydrated landing surface must never be Settings either. Once a project's per-project persisted `kb-dashboard-task-view` becomes `settings` (a common state after configuring a project through Settings), switching projects re-hydrates that scoped value and — without this guard — restores straight to Settings instead of the Board. `settings`, like `command-center`, now resolves to `board` for the auto-restored landing view only (initializer + project-hydration effect). Deep links (`?view=settings`) and explicit header/sidebar navigation to Settings still work because the `?view=` URL effect and `setTaskView`/`handleChangeTaskView` paths use `normalizeTaskView`, not this guard. */ function resolveLandingTaskView(value: TaskView): TaskView { - return value === "command-center" ? "board" : value; + return value === "command-center" || value === "settings" ? "board" : value; } function migrateLegacyRoadmapsView(value: string): TaskView {