Commit Graph

2 Commits

Author SHA1 Message Date
Arnas Donauskas
47104042fd
feat(join): /join/[token] page + signup/login invite redirects
PR 7 of the multi-user accounts series. Closes the invite loop —
PR 6 lets admins generate links, this PR is what the recipient
sees when they click one.

What lands
- `src/app/join/layout.tsx` — minimal centered dark shell. Sits
  outside both `(auth)` and `(dashboard)` route groups because
  the page is hybrid: it renders for anonymous AND signed-in
  visitors. Reusing either neighbouring layout would funnel one
  audience through the wrong middleware redirect.
- `src/app/join/[token]/page.tsx` — client component. Calls
  /api/invitations/[token]/peek + supabase.auth.getUser() in
  parallel on mount and routes through four UI states:

    peek          | auth         | render
    --------------|--------------|----------------------------------
    loading       | —            | spinner
    ok:false (any)| —            | friendly error + signup/signin CTAs
    ok:true       | signed out   | "Sign up & join" + "Sign in" links
    ok:true       | signed in    | "Accept invitation" button → redeem

  Redeem is intentionally manual — auto-accepting on mount would
  race with the signup → email-verification → return flow and
  hide what's being accepted from the visitor.

  AuthContext doesn't reach this route group (AuthProvider lives
  inside (dashboard)), so we call Supabase directly here the same
  way /login and /signup already do.

  After a successful accept, the page does a full reload to
  /dashboard (not router.push) so AuthProvider re-fetches the
  profile with the new account_id and account_role.

- `src/app/(auth)/signup/page.tsx`:
  - Reads `?invite=<token>` via useSearchParams.
  - Passes emailRedirectTo: `${origin}/join/<token>` to
    supabase.auth.signUp so the verification email returns the
    user to the redeem step.
  - Re-skins the card header for invite signups ("Create account
    & join", different icon).
  - Preserves the token through the "Sign in" link and the
    "Back to sign in" button on the success screen.
  - Default export now wraps the form in `<Suspense>` — Next 16
    refuses to prerender pages calling useSearchParams without
    it.

- `src/app/(auth)/login/page.tsx`:
  - Reads `?invite=<token>`.
  - On successful sign-in, routes to /join/<token> instead of
    /dashboard.
  - Re-skins the card header for invite logins.
  - Preserves the token through the "Create account" link.
  - Default export wrapped in `<Suspense>` for the same prerender
    reason.

- `src/middleware.ts`:
  - When an authenticated user hits /login?invite=<t> or
    /signup?invite=<t>, redirect to /join/<t> instead of the
    default /dashboard. Without this, a forwarded invite link
    landing on an already-signed-in browser would silently drop
    on /dashboard and the visitor would never see the accept
    prompt.
  - Both pre-existing redirect cases (no invite present, OR
    /forgot-password with an invite — which doesn't make sense)
    fall through to /dashboard as before. The search string is
    cleared on both paths so we don't leak the invite token
    into the dashboard URL bar.

Verification
- `npm run lint`      → 0 errors (same 19 pre-existing warnings)
- `npm run typecheck` → clean
- `npm test`          → 366/366 pass
- `npm run build`     → succeeds:
                          ƒ /join/[token]   — dynamic (peek + auth fetch)
                          ○ /login          — static, hydrates the form
                          ○ /signup         — static, hydrates the form

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-31 18:30:32 +03:00
Arnas Donauskas
658d070811
Initial scaffold: WhatsApp CRM with Next.js + Supabase
Complete WhatsApp CRM application built with Next.js 14 (App Router),
Supabase (PostgreSQL + Auth + Realtime), and shadcn/ui.

Features:
- Auth: Login, signup, forgot password with Supabase Auth
- Dashboard: Analytics overview with stats, recent activity
- Inbox: Real-time WhatsApp conversations with message threading
- Contacts: CRUD with tags, custom fields, notes, CSV import
- Broadcasts: 4-step wizard with audience selection, template sending
- Pipelines: Kanban board with deal management
- Settings: WhatsApp API config, templates, tags
- WhatsApp API: Webhook (verify + receive), send, broadcast, media proxy
- Database: 16 tables with RLS policies, indexes, realtime subscriptions
- Dark theme throughout with emerald accents

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 18:17:31 +03:00