client/server: route guards, caching, overflow, loaders, sessions polish#104
Merged
Conversation
- server/.env.example: uncomment every required key so cp gives a working scaffold; add Vercel hint to TRUST_PROXY_HOPS; document CONFIRM_PROD as a migration-only flag. - client/.env.example: clarify VITE_* are build-time inlined (not runtime secrets); add per-env values for VITE_BUDGETTER_API_HOST (local / docker / Vercel); drop the unused VITE_BUDGETTER_ADMIN_PASSWORD that no src file references.
SidebarInset is a flex item with default min-width: auto, so wide children (tables) pushed it past w-full and produced page-level horizontal scroll. Adding min-w-0 lets it shrink to its allocated column, restoring the inner table's own overflow-x-auto. The sidebar internally used @/hooks/use-mobile (< 768) while UserLayout used @/shared/hooks/useMediaQuery (<= 768), letting the two disagree at exactly 768px. Collapsed the duplicate hook into a re-export and aligned the canonical query to 767.98px.
The /user subtree previously had no route-level guard. UserLayout
mounted for any path, /me fired with no token, and MainLayout's
post-paint useEffect redirected only after the dashboard skeleton had
already rendered. The same effect also bounced authed users away
from /login, /reset-password and the email-link landings.
Replace the side-effect guard with two declarative route wrappers:
RequireAuth around /user, RedirectIfAuthed around login/signup/
forgot-password. Strip the broken auth effects out of MainLayout.
Also drop the duplicate <Route path="reports" element={<Dashboard/>}>
which was dead code.
QueryClient was constructed with no options, so every query inherited TanStack v5's defaults: staleTime 0, refetchOnWindowFocus true, retry 3. Result: /me fired on every component mount, all caches refetched on tab focus, and a single 401 cycled three times before any guard kicked in. Switch to staleTime 5m + gcTime 30m + refetchOnWindowFocus false, and short-circuit retry on 401/403 so stale-token states fail fast. Also remove useMeVerified: /auth/me/verified was an extra round-trip that returned a flag already present on the /me payload as user.isVerified. NotificationsPopover now reads that directly, with its auto-open effect properly depending on isVerified instead of running once with the value still undefined.
The Skeleton primitive now uses a moving gradient sweep instead of opacity pulse. Every existing consumer (sessions list, sidebar menu skeleton) inherits the new look for free. Page-level loaders (DashboardLoader, TopHeaderLoader) and chart loaders (DonutChartLoader, LineChartLoader) were rebuilt around Skeleton with shapes that mirror the real layout, so the swap when data arrives is seamless rather than a hard transition from gray blocks to charts.
Two small follow-ups bundled because the pre-commit hook re-staged both: - Landing-page Navbar items use hash hrefs (#features_section etc.) that produced an instant browser jump. Add scroll-behavior: smooth to <html> so the same anchor clicks animate. Respects the OS reduced-motion preference automatically. - Route guards previously only checked cookie existence, so a stale or revoked JWT passed RedirectIfAuthed/RequireAuth and the user got stuck on the dashboard loader once /users/me 401d. Add a response interceptor that on a 401 from any non-/auth/* endpoint clears the cookie and hard-reloads to /login. Auth endpoints are exempt so a wrong-credentials 401 stays in the LoginForm's toast.
The sessions popover surfaced every row in the ActiveSession collection up to the 30-day TTL cutoff. With 15-min access tokens and no refresh-token flow, every login left a new row behind, so users routinely saw 4-5 entries with long-dead JWTs and no signal about which were still meaningful. Server: listSessions filters to lastUsedAt >= now - 7d. Older rows stay in the database for the audit trail (and Mongo's TTL still deletes them at 30d), they just don't surface in the popover. Client: SessionCard leads with "Last active 2 hours ago" via formatRelative; the signed-in timestamp and IP collapse into a secondary line. At-a-glance recency without scanning timestamps.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary :
Bundled post-deploy follow-ups on
fix/v2-followups:RequireAuth/RedirectIfAuthedguards (no more flash-then-redirect); 401 interceptor clears stale cookies and forces re-login.QueryClientgetsstaleTime 5m+refetchOnWindowFocus: false; dropped the redundant/auth/me/verifiedround-trip.min-w-0onSidebarInsetkills horizontal page-scroll at <xl viewports; single source of truth foruseIsMobile.Skeleton+ content-shaped page loaders (dashboard, top-header, charts).