fix: Seamless user journey for register/login/Stripe
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
This commit is contained in:
@ -203,7 +203,7 @@ async def google_callback(
|
||||
)
|
||||
|
||||
# Parse redirect from state
|
||||
redirect_path = "/dashboard"
|
||||
redirect_path = "/command/dashboard"
|
||||
if ":" in state:
|
||||
_, redirect_path = state.split(":", 1)
|
||||
|
||||
@ -312,7 +312,7 @@ async def github_callback(
|
||||
)
|
||||
|
||||
# Parse redirect from state
|
||||
redirect_path = "/dashboard"
|
||||
redirect_path = "/command/dashboard"
|
||||
if ":" in state:
|
||||
_, redirect_path = state.split(":", 1)
|
||||
|
||||
|
||||
@ -63,7 +63,7 @@ export default function DashboardPage() {
|
||||
useEffect(() => {
|
||||
if (searchParams.get('upgraded') === 'true') {
|
||||
showToast('Welcome to your upgraded plan! 🎉', 'success')
|
||||
window.history.replaceState({}, '', '/dashboard')
|
||||
window.history.replaceState({}, '', '/command/dashboard')
|
||||
}
|
||||
}, [searchParams])
|
||||
|
||||
|
||||
@ -54,8 +54,17 @@ function LoginForm() {
|
||||
const [oauthProviders, setOauthProviders] = useState({ google_enabled: false, github_enabled: false })
|
||||
const [verified, setVerified] = useState(false)
|
||||
|
||||
// Get redirect URL from query params
|
||||
const redirectTo = searchParams.get('redirect') || '/command/dashboard'
|
||||
// Get redirect URL from query params or localStorage (set during registration)
|
||||
const paramRedirect = searchParams.get('redirect')
|
||||
const [redirectTo, setRedirectTo] = useState(paramRedirect || '/command/dashboard')
|
||||
|
||||
// Check localStorage for redirect (set during registration before email verification)
|
||||
useEffect(() => {
|
||||
const storedRedirect = localStorage.getItem('pounce_redirect_after_login')
|
||||
if (storedRedirect && !paramRedirect) {
|
||||
setRedirectTo(storedRedirect)
|
||||
}
|
||||
}, [paramRedirect])
|
||||
|
||||
// Check for verified status
|
||||
useEffect(() => {
|
||||
@ -88,6 +97,9 @@ function LoginForm() {
|
||||
return
|
||||
}
|
||||
|
||||
// Clear stored redirect (was set during registration)
|
||||
localStorage.removeItem('pounce_redirect_after_login')
|
||||
|
||||
// Redirect to intended destination or dashboard
|
||||
router.push(redirectTo)
|
||||
} catch (err: unknown) {
|
||||
|
||||
@ -12,7 +12,7 @@ function OAuthCallbackContent() {
|
||||
|
||||
useEffect(() => {
|
||||
const token = searchParams.get('token')
|
||||
const redirect = searchParams.get('redirect') || '/dashboard'
|
||||
const redirect = searchParams.get('redirect') || '/command/dashboard'
|
||||
const isNew = searchParams.get('new') === 'true'
|
||||
const error = searchParams.get('error')
|
||||
|
||||
|
||||
@ -768,7 +768,7 @@ export default function HomePage() {
|
||||
<ArrowRight className="w-4 h-4" />
|
||||
</Link>
|
||||
<Link
|
||||
href={isAuthenticated ? "/dashboard" : "/register"}
|
||||
href={isAuthenticated ? "/command/dashboard" : "/register"}
|
||||
className="inline-flex items-center gap-2 px-8 py-4 text-foreground-muted hover:text-foreground transition-colors"
|
||||
>
|
||||
{isAuthenticated ? "Go to Dashboard" : "Start Free"}
|
||||
@ -789,7 +789,7 @@ export default function HomePage() {
|
||||
Track your first domain in under a minute. Free forever, no credit card.
|
||||
</p>
|
||||
<Link
|
||||
href={isAuthenticated ? "/dashboard" : "/register"}
|
||||
href={isAuthenticated ? "/command/dashboard" : "/register"}
|
||||
className="group inline-flex items-center gap-3 px-10 py-5 bg-accent text-background rounded-2xl
|
||||
text-lg font-semibold hover:bg-accent-hover transition-all duration-300
|
||||
shadow-[0_0_40px_rgba(16,185,129,0.2)] hover:shadow-[0_0_60px_rgba(16,185,129,0.3)]"
|
||||
|
||||
@ -395,7 +395,7 @@ export default function PricingPage() {
|
||||
Start with Scout. It's free forever. Upgrade when you need more.
|
||||
</p>
|
||||
<Link
|
||||
href={isAuthenticated ? "/dashboard" : "/register"}
|
||||
href={isAuthenticated ? "/command/dashboard" : "/register"}
|
||||
className="btn-primary inline-flex items-center gap-2 px-6 py-3"
|
||||
>
|
||||
{isAuthenticated ? "Command Center" : "Join the Hunt"}
|
||||
|
||||
@ -62,7 +62,7 @@ function RegisterForm() {
|
||||
const [registered, setRegistered] = useState(false)
|
||||
|
||||
// Get redirect URL from query params
|
||||
const redirectTo = searchParams.get('redirect') || '/dashboard'
|
||||
const redirectTo = searchParams.get('redirect') || '/command/dashboard'
|
||||
|
||||
// Load OAuth providers
|
||||
useEffect(() => {
|
||||
@ -76,6 +76,13 @@ function RegisterForm() {
|
||||
|
||||
try {
|
||||
await register(email, password)
|
||||
|
||||
// Store redirect URL for after email verification
|
||||
// This will be picked up by the login page after verification
|
||||
if (redirectTo !== '/command/dashboard') {
|
||||
localStorage.setItem('pounce_redirect_after_login', redirectTo)
|
||||
}
|
||||
|
||||
// Show verification message
|
||||
setRegistered(true)
|
||||
} catch (err) {
|
||||
@ -86,7 +93,7 @@ function RegisterForm() {
|
||||
}
|
||||
|
||||
// Generate login link with redirect preserved
|
||||
const loginLink = redirectTo !== '/dashboard'
|
||||
const loginLink = redirectTo !== '/command/dashboard'
|
||||
? `/login?redirect=${encodeURIComponent(redirectTo)}`
|
||||
: '/login'
|
||||
|
||||
|
||||
@ -286,7 +286,7 @@ function AdminSidebar({
|
||||
<div className="border-t border-border/30 py-4 px-3 space-y-2">
|
||||
{/* Back to User Dashboard */}
|
||||
<Link
|
||||
href="/dashboard"
|
||||
href="/command/dashboard"
|
||||
className={clsx(
|
||||
"flex items-center gap-3 px-3 py-3 rounded-xl transition-all duration-300",
|
||||
"text-accent hover:bg-accent/10 border border-transparent hover:border-accent/20"
|
||||
|
||||
@ -152,7 +152,7 @@ export function DomainChecker() {
|
||||
Grab it now or track it in your watchlist.
|
||||
</p>
|
||||
<Link
|
||||
href={isAuthenticated ? '/dashboard' : '/register'}
|
||||
href={isAuthenticated ? '/command/dashboard' : '/register'}
|
||||
className="shrink-0 flex items-center justify-center sm:justify-start gap-2 px-5 py-2.5
|
||||
bg-accent text-background text-ui font-medium rounded-lg
|
||||
hover:bg-accent-hover transition-all duration-300"
|
||||
@ -268,7 +268,7 @@ export function DomainChecker() {
|
||||
<span className="text-left">We'll alert you the moment it drops.</span>
|
||||
</div>
|
||||
<Link
|
||||
href={isAuthenticated ? '/dashboard' : '/register'}
|
||||
href={isAuthenticated ? '/command/dashboard' : '/register'}
|
||||
className="shrink-0 flex items-center justify-center sm:justify-start gap-2 px-4 py-2.5
|
||||
bg-background-tertiary text-foreground text-ui font-medium rounded-lg
|
||||
border border-border hover:border-border-hover transition-all duration-300"
|
||||
|
||||
@ -51,9 +51,7 @@ export function Header() {
|
||||
}
|
||||
|
||||
// Check if we're on a Command Center page (should use Sidebar instead)
|
||||
const isCommandCenterPage = ['/dashboard', '/watchlist', '/portfolio', '/market', '/intelligence', '/settings', '/admin'].some(
|
||||
path => pathname.startsWith(path)
|
||||
)
|
||||
const isCommandCenterPage = pathname.startsWith('/command') || pathname.startsWith('/admin')
|
||||
|
||||
// If logged in and on Command Center page, don't render this header
|
||||
if (isAuthenticated && isCommandCenterPage) {
|
||||
|
||||
Reference in New Issue
Block a user