A purchase in a currency with no configured exchange rate makes
src/domain/fx.ts throw MissingRateError (correct behavior — never silently
convert at a wrong rate). Nothing caught it: 5 of 6 routes call
monthView/yearView/summaryView and threw during render, and with no error
boundary anywhere in src/, the throw unmounted the entire React tree
including the nav bar. Since the catch-all route redirects to the current
month on first load, one un-rated foreign-currency purchase white-screened
the app immediately on landing, with the only way out (Settings, where the
rate is added) unreachable because the nav was gone too.
Adds RouteErrorBoundary (a class component, as required for
getDerivedStateFromError/componentDidCatch) that renders the thrown
message plus a link to Settings and a "Try again" reset, and
RouteErrorBoundaryReset, a wrapper that keys the boundary on the current
route so it clears on navigation. Wraps only <Routes> in App.tsx, not
<nav>, so navigation stays usable when a route throws.
Also guards the happy-dom per-file GlobalRegistrator.register() call in
registerSw.test.ts and the new ErrorBoundary.test.tsx with
isRegistered, since Bun can run both files in one process.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>