fix(ux): recoverable route error screen + smoke-test shop/spot routes

- The per-route 'slammed' screen only offered 'Go back', so a stale-chunk
  failure after a deploy trapped the user. Add a primary 'Reload' that clears
  the lazyWithRetry session flag and does a fresh reload.
- Smoke test now boots /shop/:pubkey/:identifier, /spot/…, /event/…, /crew/…
  so a broken detail route can't ship again.
- Investigation note: no reproducible slam across 11 live shops; the report
  matched a transient chunk miss from two rapid deploys.
This commit is contained in:
SkateSpots 2026-10-02 10:52:18 +07:00
parent ad75120a43
commit 391eeb9bf9
2 changed files with 28 additions and 7 deletions

View file

@ -148,6 +148,10 @@ const ROUTES = [
'/card', '/card',
'/card/npub1yrnuj56rnen08zp2h9h7p74ghgjx6ma39spmpj6w9hzxywutevsst7k5cx', '/card/npub1yrnuj56rnen08zp2h9h7p74ghgjx6ma39spmpj6w9hzxywutevsst7k5cx',
'/verify/notarealnaddr', '/verify/notarealnaddr',
'/shop/abc/def',
'/spot/abc/def',
'/event/abc/def',
'/crew/abc/def',
'/messages/group/abc123', '/messages/group/abc123',
]; ];
const problems = []; const problems = [];

View file

@ -16,6 +16,15 @@ import { ErrorBoundary } from '@/components/ErrorBoundary';
export function RouteErrorBoundary({ children }: { children: React.ReactNode }) { export function RouteErrorBoundary({ children }: { children: React.ReactNode }) {
const location = useLocation(); const location = useLocation();
// After a deploy an already-open tab can hold a stale module graph, so a lazy
// route import 404s. `lazyWithRetry` reloads once; if it still fails the
// boundary is shown — so give the user a real way out: clear the "already
// reloaded" flag and do a fresh reload (fetching the new index + chunks).
const hardReload = () => {
try { sessionStorage.removeItem('skatespots:chunk-reload'); } catch { /* ignore */ }
window.location.reload();
};
return ( return (
<ErrorBoundary <ErrorBoundary
key={location.pathname} key={location.pathname}
@ -31,15 +40,23 @@ export function RouteErrorBoundary({ children }: { children: React.ReactNode })
</h2> </h2>
<p className="text-sm text-gray-400"> <p className="text-sm text-gray-400">
Something on this screen tripped up. Your account and data are safe. Something on this screen tripped up. Your account and data are safe.
Head back and try again. Usually a quick reload sorts it out.
</p> </p>
</div> </div>
<button <div className="flex flex-col sm:flex-row gap-2">
onClick={() => window.history.back()} <button
className="w-full px-4 py-3 rounded-full font-black uppercase bg-[#ff0040] text-white hover:bg-[#ff0040]/90 transition-colors" onClick={hardReload}
> className="flex-1 px-4 py-3 rounded-full font-black uppercase bg-[#ff0040] text-white hover:bg-[#ff0040]/90 transition-colors"
Go back >
</button> Reload
</button>
<button
onClick={() => window.history.back()}
className="flex-1 px-4 py-3 rounded-full font-black uppercase bg-white/5 text-gray-300 border border-white/10 hover:bg-white/10 transition-colors"
>
Go back
</button>
</div>
</div> </div>
</div> </div>
} }