1) Favicon
Next.js was serving its default triangle icon because src/app/
favicon.ico was the stock placeholder. Replaced with a dynamic
icon route (src/app/icon.tsx) that renders the sidebar mark —
emerald-500 rounded square with a white MessageSquare glyph.
Next.js auto-injects <link rel="icon"> pointing to the generated
PNG.
2) Template language default
Users hit Meta error #132001 "Template name does not exist in the
translation" because the template manager defaulted new templates'
language to "en" while Meta's WhatsApp templates are typically
approved under "en_US". Meta treats "en" and "en_US" as distinct
language codes — a template approved under one is rejected if sent
with the other.
Fixes:
- Default template form language to "en_US" (matches the DB
default on message_templates.language and the broadcast
sender's fallback).
- Swap the freeform <Input> for a datalist-backed input
autocompleting the ~17 most common Meta language codes.
- Inline help text: "Must match the exact language code the
template is approved under on Meta — e.g. en_US and en are
distinct."
For the user's existing welcome_message template (language="en"),
the fix is to edit it in Settings → Message Templates and change
Language to whatever Meta shows in the WhatsApp Manager
(typically en_US). One-time data fix.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>