PROBLEM: Redirect parameters were getting lost during user flows
FIXES APPLIED:
1. Register Page:
- Default redirect: /command/dashboard (was /dashboard)
- Stores redirect in localStorage before email verification
- Preserves redirect when linking to login page
2. Login Page:
- Checks localStorage for stored redirect (from registration)
- Clears stored redirect after successful login
- Uses useState for dynamic redirect handling
3. OAuth Callback:
- Default redirect: /command/dashboard (was /dashboard)
- Backend OAuth endpoints also updated
4. Fixed all /dashboard → /command/dashboard links:
- pricing/page.tsx
- page.tsx (landing page)
- AdminLayout.tsx
- DomainChecker.tsx
- command/dashboard/page.tsx
- Header.tsx (simplified check)
5. Backend OAuth:
- Default redirect_path: /command/dashboard
NEW USER JOURNEY:
Pricing → Register → Email Verify → Login → Pricing → Stripe
↓
Welcome Page
↓
Dashboard
The redirect is preserved throughout:
- Query param ?redirect=/pricing passed through register/login
- Stored in localStorage during email verification gap
- Cleaned up after successful login
STRIPE FLOW CLARIFICATION:
- Stripe does NOT create users
- Users must register FIRST with email/password
- Then they can upgrade via Stripe checkout
- This is by design for security and flexibility
68 lines
2.0 KiB
TypeScript
68 lines
2.0 KiB
TypeScript
'use client'
|
|
|
|
import { useEffect, Suspense } from 'react'
|
|
import { useRouter, useSearchParams } from 'next/navigation'
|
|
import { useStore } from '@/lib/store'
|
|
import { Loader2, CheckCircle } from 'lucide-react'
|
|
|
|
function OAuthCallbackContent() {
|
|
const router = useRouter()
|
|
const searchParams = useSearchParams()
|
|
const { checkAuth } = useStore()
|
|
|
|
useEffect(() => {
|
|
const token = searchParams.get('token')
|
|
const redirect = searchParams.get('redirect') || '/command/dashboard'
|
|
const isNew = searchParams.get('new') === 'true'
|
|
const error = searchParams.get('error')
|
|
|
|
if (error) {
|
|
router.push(`/login?error=${error}`)
|
|
return
|
|
}
|
|
|
|
if (token) {
|
|
// Store the token (using 'token' key to match api.ts)
|
|
localStorage.setItem('token', token)
|
|
|
|
// Update auth state
|
|
checkAuth().then(() => {
|
|
// Redirect with welcome message for new users
|
|
if (isNew) {
|
|
router.push(`${redirect}?welcome=true`)
|
|
} else {
|
|
router.push(redirect)
|
|
}
|
|
})
|
|
} else {
|
|
router.push('/login?error=no_token')
|
|
}
|
|
}, [searchParams, router, checkAuth])
|
|
|
|
return (
|
|
<div className="min-h-screen bg-background flex items-center justify-center">
|
|
<div className="text-center">
|
|
<div className="relative mb-6">
|
|
<Loader2 className="w-12 h-12 text-accent animate-spin mx-auto" />
|
|
<div className="absolute inset-0 bg-accent/20 blur-xl rounded-full" />
|
|
</div>
|
|
<h2 className="text-xl font-display text-foreground mb-2">Signing you in...</h2>
|
|
<p className="text-sm text-foreground-muted">Please wait while we complete authentication</p>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default function OAuthCallbackPage() {
|
|
return (
|
|
<Suspense fallback={
|
|
<div className="min-h-screen flex items-center justify-center bg-background">
|
|
<Loader2 className="w-6 h-6 animate-spin text-accent" />
|
|
</div>
|
|
}>
|
|
<OAuthCallbackContent />
|
|
</Suspense>
|
|
)
|
|
}
|
|
|