yves.gugger 772dd4a5af
Some checks failed
CI / Frontend Lint & Type Check (push) Has been cancelled
CI / Frontend Build (push) Has been cancelled
CI / Backend Lint (push) Has been cancelled
CI / Backend Tests (push) Has been cancelled
CI / Docker Build (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
Deploy / Build & Push Images (push) Has been cancelled
Deploy / Deploy to Server (push) Has been cancelled
Deploy / Notify (push) Has been cancelled
🚀 Implement tier-based scan intervals & improve Settings page
Backend (Scheduler):
- Add check_domains_by_frequency() for tier-based scanning
- Scout: Daily checks (at configured hour)
- Trader: Hourly checks (every :00)
- Tycoon: 10-minute real-time checks
- Smart tier filtering to avoid duplicate checks

Frontend (Pricing):
- All feature text now white (text-foreground)

Frontend (Settings/Billing):
- Show current plan with visual stats (domains, interval, portfolio)
- Display check frequency in human-readable format
- Full plan comparison table
- Green checkmarks for active features
- Upgrade CTA for free users
2025-12-09 17:25:44 +01:00

744 lines
36 KiB
TypeScript

'use client'
import { useEffect, useState } from 'react'
import { useRouter } from 'next/navigation'
import { Header } from '@/components/Header'
import { Footer } from '@/components/Footer'
import { useStore } from '@/lib/store'
import { api, PriceAlert } from '@/lib/api'
import {
User,
Bell,
CreditCard,
Shield,
ChevronRight,
Loader2,
Check,
AlertCircle,
Trash2,
ExternalLink,
Crown,
Zap,
Key,
TrendingUp,
} from 'lucide-react'
import Link from 'next/link'
import clsx from 'clsx'
type SettingsTab = 'profile' | 'notifications' | 'billing' | 'security'
export default function SettingsPage() {
const router = useRouter()
const { user, isAuthenticated, isLoading, checkAuth, subscription } = useStore()
const [activeTab, setActiveTab] = useState<SettingsTab>('profile')
const [saving, setSaving] = useState(false)
const [success, setSuccess] = useState<string | null>(null)
const [error, setError] = useState<string | null>(null)
// Profile form
const [profileForm, setProfileForm] = useState({
name: '',
email: '',
})
// Notification preferences (local state - would be persisted via API in production)
const [notificationPrefs, setNotificationPrefs] = useState({
domain_availability: true,
price_alerts: true,
weekly_digest: false,
})
const [savingNotifications, setSavingNotifications] = useState(false)
// Price alerts
const [priceAlerts, setPriceAlerts] = useState<PriceAlert[]>([])
const [loadingAlerts, setLoadingAlerts] = useState(false)
const [deletingAlertId, setDeletingAlertId] = useState<number | null>(null)
useEffect(() => {
checkAuth()
}, [checkAuth])
useEffect(() => {
if (!isLoading && !isAuthenticated) {
router.push('/login')
}
}, [isLoading, isAuthenticated, router])
useEffect(() => {
if (user) {
setProfileForm({
name: user.name || '',
email: user.email || '',
})
}
}, [user])
useEffect(() => {
if (isAuthenticated && activeTab === 'notifications') {
loadPriceAlerts()
}
}, [isAuthenticated, activeTab])
const loadPriceAlerts = async () => {
setLoadingAlerts(true)
try {
const alerts = await api.getPriceAlerts()
setPriceAlerts(alerts)
} catch (err) {
console.error('Failed to load alerts:', err)
} finally {
setLoadingAlerts(false)
}
}
const handleSaveProfile = async (e: React.FormEvent) => {
e.preventDefault()
setSaving(true)
setError(null)
setSuccess(null)
try {
await api.updateMe({ name: profileForm.name || undefined })
// Update store with new user info
const { checkAuth } = useStore.getState()
await checkAuth()
setSuccess('Profile updated successfully')
} catch (err) {
setError(err instanceof Error ? err.message : 'Failed to update profile')
} finally {
setSaving(false)
}
}
const handleSaveNotifications = async () => {
setSavingNotifications(true)
setError(null)
setSuccess(null)
try {
// Store in localStorage for now (would be API in production)
localStorage.setItem('notification_prefs', JSON.stringify(notificationPrefs))
setSuccess('Notification preferences saved')
} catch (err) {
setError(err instanceof Error ? err.message : 'Failed to save preferences')
} finally {
setSavingNotifications(false)
}
}
// Load notification preferences from localStorage
useEffect(() => {
const saved = localStorage.getItem('notification_prefs')
if (saved) {
try {
setNotificationPrefs(JSON.parse(saved))
} catch {}
}
}, [])
const handleDeletePriceAlert = async (tld: string, alertId: number) => {
setDeletingAlertId(alertId)
try {
await api.deletePriceAlert(tld)
setPriceAlerts(prev => prev.filter(a => a.id !== alertId))
} catch (err) {
setError(err instanceof Error ? err.message : 'Failed to delete alert')
} finally {
setDeletingAlertId(null)
}
}
const handleOpenBillingPortal = async () => {
try {
const { portal_url } = await api.createPortalSession()
window.location.href = portal_url
} catch (err) {
setError(err instanceof Error ? err.message : 'Failed to open billing portal')
}
}
if (isLoading) {
return (
<div className="min-h-screen flex items-center justify-center">
<div className="w-5 h-5 border-2 border-accent border-t-transparent rounded-full animate-spin" />
</div>
)
}
if (!isAuthenticated || !user) {
return null
}
const tierName = subscription?.tier_name || subscription?.tier || 'Scout'
const isProOrHigher = ['Trader', 'Tycoon', 'Professional', 'Enterprise'].includes(tierName)
const tabs = [
{ id: 'profile' as const, label: 'Profile', icon: User },
{ id: 'notifications' as const, label: 'Notifications', icon: Bell },
{ id: 'billing' as const, label: 'Billing', icon: CreditCard },
{ id: 'security' as const, label: 'Security', icon: Shield },
]
return (
<div className="min-h-screen bg-background relative overflow-hidden">
{/* Background Effects - matching landing page */}
<div className="fixed inset-0 pointer-events-none">
<div className="absolute top-[-20%] left-1/2 -translate-x-1/2 w-[1200px] h-[800px] bg-accent/[0.03] rounded-full blur-[120px]" />
<div className="absolute bottom-[-10%] right-[-10%] w-[600px] h-[600px] bg-accent/[0.02] rounded-full blur-[100px]" />
<div
className="absolute inset-0 opacity-[0.015]"
style={{
backgroundImage: `linear-gradient(rgba(255,255,255,.1) 1px, transparent 1px), linear-gradient(90deg, rgba(255,255,255,.1) 1px, transparent 1px)`,
backgroundSize: '64px 64px',
}}
/>
</div>
<Header />
<main className="relative flex-1 pt-32 sm:pt-40 pb-20 sm:pb-28 px-4 sm:px-6 lg:px-8">
<div className="max-w-7xl mx-auto">
{/* Header */}
<div className="mb-12 sm:mb-16 animate-fade-in">
<span className="text-sm font-semibold text-accent uppercase tracking-wider">Settings</span>
<h1 className="mt-4 font-display text-[2rem] sm:text-[2.75rem] md:text-[3.5rem] leading-[1.1] tracking-[-0.03em] text-foreground">
Your account.
</h1>
<p className="mt-3 text-lg text-foreground-muted">
Your rules. Configure everything in one place.
</p>
</div>
{/* Messages */}
{error && (
<div className="mb-6 p-4 bg-danger/5 border border-danger/20 rounded-2xl flex items-center gap-3">
<AlertCircle className="w-5 h-5 text-danger shrink-0" />
<p className="text-body-sm text-danger flex-1">{error}</p>
<button onClick={() => setError(null)} className="text-danger hover:text-danger/80">
<Trash2 className="w-4 h-4" />
</button>
</div>
)}
{success && (
<div className="mb-6 p-4 bg-accent/5 border border-accent/20 rounded-2xl flex items-center gap-3">
<Check className="w-5 h-5 text-accent shrink-0" />
<p className="text-body-sm text-accent flex-1">{success}</p>
<button onClick={() => setSuccess(null)} className="text-accent hover:text-accent/80">
<Trash2 className="w-4 h-4" />
</button>
</div>
)}
<div className="flex flex-col lg:flex-row gap-8 animate-slide-up">
{/* Sidebar - Horizontal scroll on mobile, vertical on desktop */}
<div className="lg:w-72 shrink-0">
{/* Mobile: Horizontal scroll tabs */}
<nav className="lg:hidden flex gap-2 overflow-x-auto pb-2 -mx-4 px-4 scrollbar-hide">
{tabs.map((tab) => (
<button
key={tab.id}
onClick={() => setActiveTab(tab.id)}
className={clsx(
"flex items-center gap-2.5 px-5 py-3 text-sm font-medium rounded-xl whitespace-nowrap transition-all duration-300",
activeTab === tab.id
? "bg-accent text-background shadow-lg shadow-accent/20"
: "bg-background-secondary/50 text-foreground-muted hover:text-foreground border border-border hover:border-accent/30"
)}
>
<tab.icon className="w-4 h-4" />
{tab.label}
</button>
))}
</nav>
{/* Desktop: Vertical tabs */}
<nav className="hidden lg:block p-2 bg-background-secondary/50 border border-border rounded-2xl">
{tabs.map((tab) => (
<button
key={tab.id}
onClick={() => setActiveTab(tab.id)}
className={clsx(
"w-full flex items-center gap-3 px-5 py-3.5 text-sm font-medium rounded-xl transition-all duration-300",
activeTab === tab.id
? "bg-accent text-background shadow-lg shadow-accent/20"
: "text-foreground-muted hover:text-foreground hover:bg-foreground/5"
)}
>
<tab.icon className="w-4 h-4" />
{tab.label}
</button>
))}
</nav>
{/* Plan info - hidden on mobile, shown in content area instead */}
<div className="hidden lg:block mt-5 p-6 bg-accent/5 border border-accent/20 rounded-2xl">
<div className="flex items-center gap-2 mb-3">
{isProOrHigher ? <Crown className="w-5 h-5 text-accent" /> : <Zap className="w-5 h-5 text-accent" />}
<span className="text-sm font-semibold text-foreground">{tierName} Plan</span>
</div>
<p className="text-xs text-foreground-muted mb-4">
{subscription?.domains_used || 0} / {subscription?.domain_limit || 5} domains tracked
</p>
{!isProOrHigher && (
<Link
href="/pricing"
className="flex items-center justify-center gap-2 w-full py-2.5 bg-accent text-background text-ui-sm font-medium rounded-xl hover:bg-accent-hover transition-all"
>
Upgrade
<ChevronRight className="w-3.5 h-3.5" />
</Link>
)}
</div>
</div>
{/* Content */}
<div className="flex-1 min-w-0">
{/* Profile Tab */}
{activeTab === 'profile' && (
<div className="p-6 sm:p-8 bg-background-secondary/30 border border-border rounded-2xl">
<h2 className="text-body-lg font-medium text-foreground mb-6">Profile Information</h2>
<form onSubmit={handleSaveProfile} className="space-y-5">
<div>
<label className="block text-ui-sm text-foreground-muted mb-2">Name</label>
<input
type="text"
value={profileForm.name}
onChange={(e) => setProfileForm({ ...profileForm, name: e.target.value })}
placeholder="Your name"
className="w-full px-4 py-3.5 bg-background border border-border rounded-xl text-body text-foreground
placeholder:text-foreground-subtle focus:outline-none focus:ring-1 focus:ring-accent/50 focus:border-accent/50 transition-all"
/>
</div>
<div>
<label className="block text-ui-sm text-foreground-muted mb-2">Email</label>
<input
type="email"
value={profileForm.email}
disabled
className="w-full px-4 py-3.5 bg-background-tertiary border border-border rounded-xl text-body text-foreground-muted cursor-not-allowed"
/>
<p className="text-ui-xs text-foreground-subtle mt-1.5">Email cannot be changed</p>
</div>
<button
type="submit"
disabled={saving}
className="px-6 py-3.5 bg-foreground text-background text-ui font-medium rounded-xl
hover:bg-foreground/90 disabled:opacity-50 transition-all flex items-center gap-2 shadow-lg shadow-foreground/10"
>
{saving ? <Loader2 className="w-4 h-4 animate-spin" /> : <Check className="w-4 h-4" />}
Save Changes
</button>
</form>
</div>
)}
{/* Notifications Tab */}
{activeTab === 'notifications' && (
<div className="space-y-6">
<div className="p-6 sm:p-8 bg-background-secondary/30 border border-border rounded-2xl">
<h2 className="text-body-lg font-medium text-foreground mb-5">Email Preferences</h2>
<div className="space-y-3">
<label className="flex items-center justify-between p-4 bg-background border border-border rounded-xl cursor-pointer hover:border-foreground/20 transition-colors">
<div>
<p className="text-body-sm font-medium text-foreground">Domain Availability</p>
<p className="text-body-xs text-foreground-muted">Get notified when watched domains become available</p>
</div>
<input
type="checkbox"
checked={notificationPrefs.domain_availability}
onChange={(e) => setNotificationPrefs({ ...notificationPrefs, domain_availability: e.target.checked })}
className="w-5 h-5 accent-accent cursor-pointer"
/>
</label>
<label className="flex items-center justify-between p-4 bg-background border border-border rounded-xl cursor-pointer hover:border-foreground/20 transition-colors">
<div>
<p className="text-body-sm font-medium text-foreground">Price Alerts</p>
<p className="text-body-xs text-foreground-muted">Get notified when TLD prices change</p>
</div>
<input
type="checkbox"
checked={notificationPrefs.price_alerts}
onChange={(e) => setNotificationPrefs({ ...notificationPrefs, price_alerts: e.target.checked })}
className="w-5 h-5 accent-accent cursor-pointer"
/>
</label>
<label className="flex items-center justify-between p-4 bg-background border border-border rounded-xl cursor-pointer hover:border-foreground/20 transition-colors">
<div>
<p className="text-body-sm font-medium text-foreground">Weekly Digest</p>
<p className="text-body-xs text-foreground-muted">Receive a weekly summary of your portfolio</p>
</div>
<input
type="checkbox"
checked={notificationPrefs.weekly_digest}
onChange={(e) => setNotificationPrefs({ ...notificationPrefs, weekly_digest: e.target.checked })}
className="w-5 h-5 accent-accent cursor-pointer"
/>
</label>
</div>
<button
onClick={handleSaveNotifications}
disabled={savingNotifications}
className="mt-5 px-6 py-3 bg-foreground text-background text-ui font-medium rounded-xl
hover:bg-foreground/90 disabled:opacity-50 transition-all flex items-center gap-2"
>
{savingNotifications ? <Loader2 className="w-4 h-4 animate-spin" /> : <Check className="w-4 h-4" />}
Save Preferences
</button>
</div>
{/* Active Price Alerts */}
<div className="p-6 sm:p-8 bg-background-secondary/30 border border-border rounded-2xl">
<h2 className="text-body-lg font-medium text-foreground mb-5">Active Price Alerts</h2>
{loadingAlerts ? (
<div className="py-10 flex items-center justify-center">
<Loader2 className="w-6 h-6 animate-spin text-accent" />
</div>
) : priceAlerts.length === 0 ? (
<div className="py-12 text-center border border-dashed border-border/50 rounded-xl bg-background/30">
<Bell className="w-10 h-10 text-foreground-subtle mx-auto mb-4" />
<p className="text-body text-foreground-muted mb-3">No price alerts set</p>
<Link
href="/tld-pricing"
className="text-accent hover:text-accent-hover text-body-sm font-medium"
>
Browse TLD prices
</Link>
</div>
) : (
<div className="space-y-2">
{priceAlerts.map((alert) => (
<div
key={alert.id}
className="flex items-center justify-between p-4 bg-background border border-border rounded-xl hover:border-foreground/20 transition-colors"
>
<div className="flex items-center gap-3">
<div className="relative">
<div className={clsx(
"w-2.5 h-2.5 rounded-full",
alert.is_active ? "bg-accent" : "bg-foreground-subtle"
)} />
{alert.is_active && (
<span className="absolute inset-0 rounded-full bg-accent animate-ping opacity-40" />
)}
</div>
<div>
<Link
href={`/tld-pricing/${alert.tld}`}
className="text-body-sm font-mono font-medium text-foreground hover:text-accent transition-colors"
>
.{alert.tld}
</Link>
<p className="text-body-xs text-foreground-muted">
Alert on {alert.threshold_percent}% change
{alert.target_price && ` or below $${alert.target_price}`}
</p>
</div>
</div>
<button
onClick={() => handleDeletePriceAlert(alert.tld, alert.id)}
disabled={deletingAlertId === alert.id}
className="p-2 text-foreground-subtle hover:text-danger hover:bg-danger/10 rounded-lg transition-all"
>
{deletingAlertId === alert.id ? (
<Loader2 className="w-4 h-4 animate-spin" />
) : (
<Trash2 className="w-4 h-4" />
)}
</button>
</div>
))}
</div>
)}
</div>
</div>
)}
{/* Billing Tab */}
{activeTab === 'billing' && (
<div className="space-y-6">
{/* Current Plan */}
<div className="p-6 sm:p-8 bg-background-secondary/30 border border-border rounded-2xl">
<h2 className="text-body-lg font-medium text-foreground mb-6">Your Current Plan</h2>
<div className="p-5 bg-accent/5 border border-accent/20 rounded-xl mb-6">
<div className="flex items-center justify-between mb-4">
<div className="flex items-center gap-3">
{tierName === 'Tycoon' ? (
<Crown className="w-6 h-6 text-accent" />
) : tierName === 'Trader' ? (
<TrendingUp className="w-6 h-6 text-accent" />
) : (
<Zap className="w-6 h-6 text-accent" />
)}
<div>
<p className="text-xl font-semibold text-foreground">{tierName}</p>
<p className="text-body-sm text-foreground-muted">
{tierName === 'Scout' ? 'Free forever' : tierName === 'Trader' ? '$19/month' : '$49/month'}
</p>
</div>
</div>
<span className={clsx(
"px-3 py-1.5 text-ui-xs font-medium rounded-full",
isProOrHigher ? "bg-accent/10 text-accent" : "bg-foreground/5 text-foreground-muted"
)}>
{isProOrHigher ? 'Active' : 'Free'}
</span>
</div>
{/* Plan Stats */}
<div className="grid grid-cols-3 gap-4 p-4 bg-background/50 rounded-xl mb-4">
<div className="text-center">
<p className="text-2xl font-semibold text-foreground">{subscription?.domain_limit || 5}</p>
<p className="text-xs text-foreground-muted">Domains</p>
</div>
<div className="text-center border-x border-border/50">
<p className="text-2xl font-semibold text-foreground">
{subscription?.check_frequency === 'realtime' ? '10m' :
subscription?.check_frequency === 'hourly' ? '1h' : '24h'}
</p>
<p className="text-xs text-foreground-muted">Check Interval</p>
</div>
<div className="text-center">
<p className="text-2xl font-semibold text-foreground">
{subscription?.portfolio_limit === -1 ? '∞' : subscription?.portfolio_limit || 0}
</p>
<p className="text-xs text-foreground-muted">Portfolio</p>
</div>
</div>
{isProOrHigher ? (
<button
onClick={handleOpenBillingPortal}
className="w-full py-3 bg-background text-foreground text-ui font-medium rounded-xl border border-border
hover:border-foreground/20 transition-all flex items-center justify-center gap-2"
>
<ExternalLink className="w-4 h-4" />
Manage Subscription
</button>
) : (
<Link
href="/pricing"
className="w-full py-3 bg-accent text-background text-ui font-medium rounded-xl
hover:bg-accent-hover transition-all flex items-center justify-center gap-2 shadow-lg shadow-accent/20"
>
<Zap className="w-4 h-4" />
Upgrade Plan
</Link>
)}
</div>
{/* Plan Features */}
<h3 className="text-body-sm font-medium text-foreground mb-3">Your Plan Includes</h3>
<ul className="grid grid-cols-2 gap-2">
<li className="flex items-center gap-2 text-body-sm">
<Check className="w-4 h-4 text-accent" />
<span className="text-foreground">{subscription?.domain_limit || 5} Watchlist Domains</span>
</li>
<li className="flex items-center gap-2 text-body-sm">
<Check className="w-4 h-4 text-accent" />
<span className="text-foreground">
{subscription?.check_frequency === 'realtime' ? '10-minute' :
subscription?.check_frequency === 'hourly' ? 'Hourly' : 'Daily'} Scans
</span>
</li>
<li className="flex items-center gap-2 text-body-sm">
<Check className="w-4 h-4 text-accent" />
<span className="text-foreground">Email Alerts</span>
</li>
<li className="flex items-center gap-2 text-body-sm">
<Check className="w-4 h-4 text-accent" />
<span className="text-foreground">TLD Price Data</span>
</li>
{subscription?.features?.domain_valuation && (
<li className="flex items-center gap-2 text-body-sm">
<Check className="w-4 h-4 text-accent" />
<span className="text-foreground">Domain Valuation</span>
</li>
)}
{(subscription?.portfolio_limit ?? 0) !== 0 && (
<li className="flex items-center gap-2 text-body-sm">
<Check className="w-4 h-4 text-accent" />
<span className="text-foreground">
{subscription?.portfolio_limit === -1 ? 'Unlimited' : subscription?.portfolio_limit} Portfolio
</span>
</li>
)}
{subscription?.features?.expiration_tracking && (
<li className="flex items-center gap-2 text-body-sm">
<Check className="w-4 h-4 text-accent" />
<span className="text-foreground">Expiry Tracking</span>
</li>
)}
{(subscription?.history_days ?? 0) !== 0 && (
<li className="flex items-center gap-2 text-body-sm">
<Check className="w-4 h-4 text-accent" />
<span className="text-foreground">
{subscription?.history_days === -1 ? 'Full' : `${subscription?.history_days}-day`} History
</span>
</li>
)}
</ul>
</div>
{/* Compare All Plans */}
<div className="p-6 sm:p-8 bg-background-secondary/30 border border-border rounded-2xl">
<h2 className="text-body-lg font-medium text-foreground mb-6">Compare All Plans</h2>
<div className="overflow-x-auto -mx-2">
<table className="w-full min-w-[500px]">
<thead>
<tr className="border-b border-border">
<th className="text-left py-3 px-3 text-body-sm font-medium text-foreground-muted">Feature</th>
<th className={clsx(
"text-center py-3 px-3 text-body-sm font-medium",
tierName === 'Scout' ? "text-accent" : "text-foreground-muted"
)}>Scout</th>
<th className={clsx(
"text-center py-3 px-3 text-body-sm font-medium",
tierName === 'Trader' ? "text-accent" : "text-foreground-muted"
)}>Trader</th>
<th className={clsx(
"text-center py-3 px-3 text-body-sm font-medium",
tierName === 'Tycoon' ? "text-accent" : "text-foreground-muted"
)}>Tycoon</th>
</tr>
</thead>
<tbody>
<tr className="border-b border-border/50">
<td className="py-3 px-3 text-body-sm text-foreground">Price</td>
<td className="py-3 px-3 text-center text-body-sm text-foreground">Free</td>
<td className="py-3 px-3 text-center text-body-sm text-foreground">$19/mo</td>
<td className="py-3 px-3 text-center text-body-sm text-foreground">$49/mo</td>
</tr>
<tr className="border-b border-border/50">
<td className="py-3 px-3 text-body-sm text-foreground">Watchlist Domains</td>
<td className="py-3 px-3 text-center text-body-sm text-foreground">5</td>
<td className="py-3 px-3 text-center text-body-sm text-foreground">50</td>
<td className="py-3 px-3 text-center text-body-sm text-foreground">500</td>
</tr>
<tr className="border-b border-border/50">
<td className="py-3 px-3 text-body-sm text-foreground">Scan Frequency</td>
<td className="py-3 px-3 text-center text-body-sm text-foreground">Daily</td>
<td className="py-3 px-3 text-center text-body-sm text-foreground">Hourly</td>
<td className="py-3 px-3 text-center text-body-sm text-accent font-medium">10 min</td>
</tr>
<tr className="border-b border-border/50">
<td className="py-3 px-3 text-body-sm text-foreground">Portfolio</td>
<td className="py-3 px-3 text-center text-body-sm text-foreground-muted"></td>
<td className="py-3 px-3 text-center text-body-sm text-foreground">25</td>
<td className="py-3 px-3 text-center text-body-sm text-foreground">Unlimited</td>
</tr>
<tr className="border-b border-border/50">
<td className="py-3 px-3 text-body-sm text-foreground">Domain Valuation</td>
<td className="py-3 px-3 text-center text-body-sm text-foreground-muted"></td>
<td className="py-3 px-3 text-center"><Check className="w-5 h-5 text-accent mx-auto" /></td>
<td className="py-3 px-3 text-center"><Check className="w-5 h-5 text-accent mx-auto" /></td>
</tr>
<tr className="border-b border-border/50">
<td className="py-3 px-3 text-body-sm text-foreground">Price History</td>
<td className="py-3 px-3 text-center text-body-sm text-foreground-muted"></td>
<td className="py-3 px-3 text-center text-body-sm text-foreground">90 days</td>
<td className="py-3 px-3 text-center text-body-sm text-foreground">Unlimited</td>
</tr>
<tr>
<td className="py-3 px-3 text-body-sm text-foreground">Expiry Tracking</td>
<td className="py-3 px-3 text-center text-body-sm text-foreground-muted"></td>
<td className="py-3 px-3 text-center"><Check className="w-5 h-5 text-accent mx-auto" /></td>
<td className="py-3 px-3 text-center"><Check className="w-5 h-5 text-accent mx-auto" /></td>
</tr>
</tbody>
</table>
</div>
{!isProOrHigher && (
<div className="mt-6 text-center">
<Link
href="/pricing"
className="inline-flex items-center gap-2 px-6 py-3 bg-accent text-background text-ui font-medium rounded-xl
hover:bg-accent-hover transition-all shadow-lg shadow-accent/20"
>
<Zap className="w-4 h-4" />
Upgrade Now
</Link>
</div>
)}
</div>
</div>
)}
{/* Security Tab */}
{activeTab === 'security' && (
<div className="space-y-6">
<div className="p-6 sm:p-8 bg-background-secondary/30 border border-border rounded-2xl">
<h2 className="text-body-lg font-medium text-foreground mb-4">Password</h2>
<p className="text-body-sm text-foreground-muted mb-5">
Change your password or reset it if you've forgotten it.
</p>
<Link
href="/forgot-password"
className="inline-flex items-center gap-2 px-5 py-3 bg-background border border-border text-foreground text-ui font-medium rounded-xl
hover:border-foreground/20 transition-all"
>
<Key className="w-4 h-4" />
Change Password
</Link>
</div>
<div className="p-6 sm:p-8 bg-background-secondary/30 border border-border rounded-2xl">
<h2 className="text-body-lg font-medium text-foreground mb-5">Account Security</h2>
<div className="space-y-3">
<div className="flex items-center justify-between p-4 bg-background border border-border rounded-xl">
<div>
<p className="text-body-sm font-medium text-foreground">Email Verified</p>
<p className="text-body-xs text-foreground-muted">Your email address has been verified</p>
</div>
<div className="w-8 h-8 bg-accent/10 rounded-lg flex items-center justify-center">
<Check className="w-4 h-4 text-accent" />
</div>
</div>
<div className="flex items-center justify-between p-4 bg-background border border-border rounded-xl">
<div>
<p className="text-body-sm font-medium text-foreground">Two-Factor Authentication</p>
<p className="text-body-xs text-foreground-muted">Coming soon</p>
</div>
<span className="text-ui-xs px-2.5 py-1 bg-foreground/5 text-foreground-muted rounded-full">Soon</span>
</div>
</div>
</div>
<div className="p-6 sm:p-8 bg-danger/5 border border-danger/20 rounded-2xl">
<h2 className="text-body-lg font-medium text-danger mb-2">Danger Zone</h2>
<p className="text-body-sm text-foreground-muted mb-5">
Permanently delete your account and all associated data.
</p>
<button
className="px-5 py-3 bg-danger text-white text-ui font-medium rounded-xl hover:bg-danger/90 transition-all"
>
Delete Account
</button>
</div>
</div>
)}
</div>
</div>
</div>
</main>
<Footer />
</div>
)
}