From 017b4ce1f9552fe3ba086018ffa9391ed9762d5f Mon Sep 17 00:00:00 2001 From: Yves Gugger Date: Sat, 13 Dec 2025 16:43:32 +0100 Subject: [PATCH] feat: Ultimate plan switcher in settings --- frontend/src/app/terminal/settings/page.tsx | 395 ++++++++++++++++---- 1 file changed, 323 insertions(+), 72 deletions(-) diff --git a/frontend/src/app/terminal/settings/page.tsx b/frontend/src/app/terminal/settings/page.tsx index b8c1c40..58f45ea 100644 --- a/frontend/src/app/terminal/settings/page.tsx +++ b/frontend/src/app/terminal/settings/page.tsx @@ -22,12 +22,93 @@ import { TrendingUp, X, Settings, + ArrowUp, + ArrowDown, + Sparkles, + Clock, + Target, + BarChart3, + MessageSquare, + Database, } from 'lucide-react' import Link from 'next/link' import clsx from 'clsx' type SettingsTab = 'profile' | 'notifications' | 'billing' | 'security' +// Plan definitions +const PLANS = [ + { + id: 'scout', + name: 'Scout', + icon: Zap, + price: 0, + period: 'forever', + description: 'Perfect for getting started', + color: 'white', + features: [ + { icon: Target, text: '5 Watchlist Domains' }, + { icon: Clock, text: 'Daily Checks' }, + { icon: Bell, text: '2 Sniper Alerts' }, + { icon: BarChart3, text: 'Basic Market Data' }, + ], + limits: { + domains: 5, + alerts: 2, + listings: 0, + portfolio: 0, + } + }, + { + id: 'trader', + name: 'Trader', + icon: TrendingUp, + price: 9, + period: '/month', + description: 'For serious domain investors', + color: 'accent', + badge: 'Popular', + features: [ + { icon: Target, text: '50 Watchlist Domains' }, + { icon: Clock, text: 'Hourly Checks' }, + { icon: Bell, text: '10 Sniper Alerts' }, + { icon: Sparkles, text: 'Domain Valuation' }, + { icon: Database, text: '25 Portfolio Domains' }, + { icon: MessageSquare, text: '5 Marketplace Listings' }, + ], + limits: { + domains: 50, + alerts: 10, + listings: 5, + portfolio: 25, + } + }, + { + id: 'tycoon', + name: 'Tycoon', + icon: Crown, + price: 29, + period: '/month', + description: 'Maximum power & control', + color: 'amber', + badge: 'Full Power', + features: [ + { icon: Target, text: '500 Watchlist Domains' }, + { icon: Clock, text: '10-Minute Checks' }, + { icon: Bell, text: '50 Sniper Alerts' }, + { icon: Sparkles, text: 'Full SEO Metrics' }, + { icon: Database, text: 'Unlimited Portfolio' }, + { icon: MessageSquare, text: '50 Featured Listings' }, + ], + limits: { + domains: 500, + alerts: 50, + listings: 50, + portfolio: -1, + } + }, +] + export default function SettingsPage() { const router = useRouter() const { user, isAuthenticated, isLoading, checkAuth, subscription } = useStore() @@ -52,6 +133,7 @@ export default function SettingsPage() { const [priceAlerts, setPriceAlerts] = useState([]) const [loadingAlerts, setLoadingAlerts] = useState(false) const [deletingAlertId, setDeletingAlertId] = useState(null) + const [changingPlan, setChangingPlan] = useState(null) useEffect(() => { checkAuth() @@ -153,6 +235,52 @@ export default function SettingsPage() { } } + const handlePlanChange = async (planId: string) => { + setChangingPlan(planId) + setError(null) + + try { + if (planId === 'scout') { + // Downgrade to free - cancel subscription + await api.cancelSubscription() + setSuccess('Downgraded to Scout. Your premium features will remain active until the end of your billing period.') + await checkAuth() + } else { + // Upgrade to paid plan + const successUrl = `${window.location.origin}/terminal/settings?upgraded=${planId}` + const cancelUrl = `${window.location.origin}/terminal/settings?cancelled=true` + const { checkout_url } = await api.createCheckoutSession(planId, successUrl, cancelUrl) + window.location.href = checkout_url + } + } catch (err) { + setError(err instanceof Error ? err.message : 'Failed to change plan') + } finally { + setChangingPlan(null) + } + } + + // Handle URL params for upgrade success/cancel + useEffect(() => { + const params = new URLSearchParams(window.location.search) + const upgraded = params.get('upgraded') + const cancelled = params.get('cancelled') + + if (upgraded) { + setSuccess(`🎉 Welcome to ${upgraded.charAt(0).toUpperCase() + upgraded.slice(1)}! Your account has been upgraded.`) + setActiveTab('billing') + // Clean URL + window.history.replaceState({}, '', '/terminal/settings') + // Refresh subscription data + checkAuth() + } + + if (cancelled) { + setError('Checkout was cancelled. No changes were made.') + setActiveTab('billing') + window.history.replaceState({}, '', '/terminal/settings') + } + }, []) + if (isLoading) { return (
@@ -429,85 +557,208 @@ export default function SettingsPage() { {/* Billing Tab */} {activeTab === 'billing' && (
+ {/* Plan Switcher */}
-

Current Plan

- -
-
-
- {tierName === 'Tycoon' ? : - tierName === 'Trader' ? : - } -
-

{tierName}

-

- {tierName === 'Scout' ? 'Free forever' : tierName === 'Trader' ? '$9/month' : '$29/month'} -

-
-
- - {isProOrHigher ? 'Active' : 'Free'} - -
- - {/* Plan Stats */} -
-
-

{subscription?.domain_limit || 5}

-

Domains

-
-
-

- {subscription?.check_frequency === 'realtime' ? '10m' : - subscription?.check_frequency === 'hourly' ? '1h' : '24h'} -

-

Interval

-
-
-

- {subscription?.portfolio_limit === -1 ? '∞' : subscription?.portfolio_limit || 0} -

-

Portfolio

-
-
- - {isProOrHigher ? ( +
+

Choose Your Plan

+ {isProOrHigher && ( - ) : ( - - - Upgrade Plan - )}
- - {/* Plan Features */} -

Includes

-
    - {[ - `${subscription?.domain_limit || 5} Watchlist Domains`, - `${subscription?.check_frequency === 'realtime' ? '10-minute' : subscription?.check_frequency === 'hourly' ? 'Hourly' : 'Daily'} Scans`, - 'Email Alerts', - 'TLD Price Data', - ].map((feature) => ( -
  • - - {feature} -
  • - ))} -
+ + {/* Plan Cards */} +
+ {PLANS.map((plan) => { + const isCurrent = tierName.toLowerCase() === plan.id + const isUpgrade = + (tierName === 'Scout' && (plan.id === 'trader' || plan.id === 'tycoon')) || + (tierName === 'Trader' && plan.id === 'tycoon') + const isDowngrade = + (tierName === 'Tycoon' && (plan.id === 'trader' || plan.id === 'scout')) || + (tierName === 'Trader' && plan.id === 'scout') + + return ( +
+ {/* Badge */} + {plan.badge && ( +
+ {plan.badge} +
+ )} + + {/* Current Badge */} + {isCurrent && ( +
+ Current +
+ )} + + {/* Header */} +
+
+ +
+
+

{plan.name}

+

{plan.description}

+
+
+ + {/* Price */} +
+ {plan.price === 0 ? ( + Free + ) : ( + <> + ${plan.price} + {plan.period} + + )} +
+ + {/* Features */} +
    + {plan.features.map((feature, idx) => ( +
  • + + {feature.text} +
  • + ))} +
+ + {/* Action Button */} + {isCurrent ? ( +
+ + Active Plan +
+ ) : isUpgrade ? ( + + ) : isDowngrade ? ( + + ) : null} +
+ ) + })} +
+
+ + {/* Usage Stats */} +
+

Current Usage

+ +
+
+
+ Watchlist + +
+

+ {subscription?.domains_used || 0} + /{subscription?.domain_limit || 5} +

+
+
+
+
+ +
+
+ Check Speed + +
+

+ {subscription?.check_frequency === 'realtime' ? '10m' : + subscription?.check_frequency === 'hourly' ? '1h' : '24h'} +

+

Interval

+
+ +
+
+ Portfolio + +
+

+ {subscription?.portfolio_limit === -1 ? '∞' : subscription?.portfolio_limit || 0} +

+

Slots

+
+ +
+
+ Status + +
+

+ {subscription?.status || 'active'} +

+

Subscription

+
+
+
+ + {/* Help Text */} +
+

+ Need help? Contact support +

)}