Skip to content

client/server: route guards, caching, overflow, loaders, sessions polish#104

Merged
lokeshwardewangan merged 7 commits into
masterfrom
fix/v2-followups
May 29, 2026
Merged

client/server: route guards, caching, overflow, loaders, sessions polish#104
lokeshwardewangan merged 7 commits into
masterfrom
fix/v2-followups

Conversation

@lokeshwardewangan

Copy link
Copy Markdown
Owner

Summary :

Bundled post-deploy follow-ups on fix/v2-followups:

  • Auth: declarative RequireAuth / RedirectIfAuthed guards (no more flash-then-redirect); 401 interceptor clears stale cookies and forces re-login.
  • Perf: QueryClient gets staleTime 5m + refetchOnWindowFocus: false; dropped the redundant /auth/me/verified round-trip.
  • Layout: min-w-0 on SidebarInset kills horizontal page-scroll at <xl viewports; single source of truth for useIsMobile.
  • Loaders: shimmer Skeleton + content-shaped page loaders (dashboard, top-header, charts).
  • Sessions: server filters list to last 7 days; card leads with "Last active 2h ago" instead of a wall of timestamps.
  • UX: landing-page nav hash links smooth-scroll.

- 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.
@vercel

vercel Bot commented May 29, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
api-budgetter Ready Ready Preview, Comment May 29, 2026 6:18pm
budgetter Ready Ready Preview, Comment May 29, 2026 6:18pm

@lokeshwardewangan lokeshwardewangan merged commit 7378e8f into master May 29, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant