Fixes the cascading warnings:
[useAuth] getSession() timed out after 3s, clearing loading state
[useAuth] getSession() timed out after 3s, clearing loading state
[useAuth] getSession() timed out after 3s, clearing loading state
The warning appeared 3x per page load because 3 components each called
useAuth() independently (DashboardLayout, Sidebar, Header), each firing
their own supabase.auth.getSession() call. The Supabase client has
internal mutex locks around session reads, so concurrent calls from
multiple components queue up and timeout.
Fix: introduce an AuthProvider React Context that calls getSession()
ONCE for the whole subtree. useAuth() now reads from context instead
of making its own call.
Also fixes the Settings page stuck on loading: WhatsAppConfig,
TemplateManager, and TagManager were making their own getSession()
calls in fetch functions. They now read the user from useAuth()
context — no redundant session calls.
Changes:
- src/hooks/use-auth.ts -> use-auth.tsx (JSX for the Provider)
Added AuthProvider component and converted useAuth() to a
context consumer. Renamed because the file now exports JSX.
- src/app/(dashboard)/layout.tsx — wraps children in <AuthProvider>
with a DashboardShell inner component that consumes the context.
- src/components/settings/whatsapp-config.tsx — uses useAuth() for
user instead of calling getSession() itself.
- src/components/settings/template-manager.tsx — same.
- src/components/settings/tag-manager.tsx — same.
- Profile fetch is now fire-and-forget (doesn't block setLoading),
so the UI renders as soon as the session is known.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Major performance improvement. Every client component that needed the
current user was calling supabase.auth.getUser(), which makes a
network round-trip to Supabase to validate the JWT. On a page that
fetches data conditional on the user, the flow was:
Page mount
-> getUser() [network ~150-400ms]
-> query data [network ~150-400ms]
-> render
That's 2 serial round-trips before the page renders.
supabase.auth.getSession() reads the session from localStorage with
NO network call. Using it in client code cuts per-page load by one
full round-trip (~150-400ms depending on region latency).
Replaced in 10 client files:
- src/app/(dashboard)/inbox/page.tsx
- src/app/(dashboard)/pipelines/page.tsx
- src/components/contacts/contact-detail-view.tsx
- src/components/contacts/contact-form.tsx
- src/components/contacts/import-modal.tsx
- src/components/inbox/contact-sidebar.tsx
- src/components/pipelines/deal-form.tsx
- src/components/pipelines/pipeline-settings.tsx
- src/components/settings/tag-manager.tsx
- src/components/settings/template-manager.tsx
- src/components/settings/whatsapp-config.tsx
NOT changed (correctly continue using getUser):
- src/middleware.ts (server-side, needs JWT validation)
- src/app/api/whatsapp/*/route.ts (server-side, security-critical)
- src/hooks/use-auth.ts (PR #4 handles this file)
Security note: getSession() trusts the client-stored session. For
API routes and middleware that make authorization decisions, we still
use getUser() which verifies with Supabase. Client components only
use the session's user ID to filter RLS-protected queries — the RLS
on the database is the actual authorization boundary, so a forged
user ID in the browser wouldn't bypass server-side policies.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Next.js route groups like (dashboard) don't appear in URLs, so the
sidebar, header, and dashboard page were linking to /dashboard/inbox,
/dashboard/contacts, etc. which don't exist and returned 404.
Fixed paths in:
- src/components/layout/sidebar.tsx (nav hrefs)
- src/components/layout/header.tsx (page title map)
- src/app/(dashboard)/dashboard/page.tsx (router.push calls)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>