fix(frontend): warn before an in-app navigation discards an unsaved edit (#464) #537
Loading…
Reference in a new issue
No description provided.
Delete branch "fix/464-data-router"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #464. First engineering lane of v4.4.0 (#535 phase 0).
What changes
App.jsxmounts the route table oncreateBrowserRouter+RouterProvider(fromreact-router/dom, the build that suppliesflushSync). The JSX route tree is kept viacreateRoutesFromElementsand exported asroutesso tests mount it undercreateMemoryRouter. Every route, the legacy redirects, the singleAuthGuard+AppLayout, and the catch-all are unchanged.AuthProviderandCampaignProvidermove into a pathless root layout route becauseCampaignProvidercalls router hooks and a data router has no "between the router and the routes" any more;ThemeProviderstays outside as before.useUnsavedChangesPromptadds the in-app half on top of the existingbeforeunloadguard: a confirm before a pathname change while an edit is dirty (?tab=and the#recordinganchor never prompt). It registers the blocker through the router's publicgetBlocker/deleteBlockerand no-ops without a data router, so the ~30 page tests that mountMemoryRouterkeep working and the hook cannot crash a page rendered on its own. React Router'suseBlockerthrows outside a data router and there is no public probe, which is why the hook reads the router offUNSAFE_DataRouterContext; a future react-router bump that renames it fails the hook's tests, not users.SessionDetail's summary and transcript editors use the new hook. The Shelf tonight-notes and the Beat Planner are deliberately untouched (#536 for the former, which has a real flush bug the prompt would not fix).One thing beyond the brief, on purpose. A data router wraps the root match in its own error boundary whether or not you ask, and its default element renders "Unexpected Application Error!" over a raw stack trace, in the production build too. The migration alone would therefore have bypassed the existing
<ErrorBoundary>inmain.jsxand shown users a JS stack. The root route now carries anerrorElementthat renders the sameErrorFallbackthe class boundary uses (extracted, not duplicated), with a test that locks it in.Verification (Docker, node 22): 64 files, 809 tests pass; eslint 0 errors (one pre-existing warning in a file this PR does not touch); vite build ok. The blocking tests were seen to fail with the blocker registration removed (2 failed, 9 passed) and pass with it restored.
🤖 Generated with Claude Code