feat: Enhanced domain status icons with color coding + UI polish
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
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
STATUS ICONS: - CircleCheck (emerald) = Available - ready to hunt - Radio (cyan, pulsing) = Monitoring - actively watching - CircleAlert (amber, pulsing) = Expiring soon - urgent - CircleX (gray) = Registered - taken VISUAL IMPROVEMENTS: - Status icons with colored backgrounds and ring effects - Pulsing animation for monitoring and expiring domains - Stats cards with vibrant color accents (emerald, cyan, amber, violet) - Icon badges in stat cards for better visual hierarchy - Gradient quick-links section - Increased max-width consistency (max-w-5xl) - More padding (pt-28/32, pb-20/24) - Rounded-2xl for modern card look - Hover effects with color transitions COLORS: - Emerald for positive/available - Cyan for monitoring/active - Amber for warnings/expiring - Violet for investments - Rose for negative/loss
This commit is contained in:
@ -36,6 +36,10 @@ import {
|
||||
Globe,
|
||||
ArrowUpRight,
|
||||
ArrowDownRight,
|
||||
Radio,
|
||||
CircleCheck,
|
||||
CircleX,
|
||||
CircleAlert,
|
||||
} from 'lucide-react'
|
||||
import clsx from 'clsx'
|
||||
import Link from 'next/link'
|
||||
@ -370,12 +374,59 @@ export default function DashboardPage() {
|
||||
const isProOrHigher = tierName === 'Professional' || tierName === 'Enterprise' || tierName === 'Trader' || tierName === 'Tycoon'
|
||||
const isEnterprise = tierName === 'Enterprise' || tierName === 'Tycoon'
|
||||
|
||||
// Get domain status with icon
|
||||
const getDomainStatus = (domain: any) => {
|
||||
const exp = formatExpirationDate(domain.expiration_date)
|
||||
|
||||
if (domain.is_available) {
|
||||
return {
|
||||
icon: CircleCheck,
|
||||
label: 'Available',
|
||||
color: 'text-emerald-400',
|
||||
bg: 'bg-emerald-400/10',
|
||||
ring: 'ring-emerald-400/30',
|
||||
pulse: false
|
||||
}
|
||||
}
|
||||
|
||||
if (exp?.urgent) {
|
||||
return {
|
||||
icon: CircleAlert,
|
||||
label: 'Expiring',
|
||||
color: 'text-amber-400',
|
||||
bg: 'bg-amber-400/10',
|
||||
ring: 'ring-amber-400/30',
|
||||
pulse: true
|
||||
}
|
||||
}
|
||||
|
||||
if (domain.notify_on_available) {
|
||||
return {
|
||||
icon: Radio,
|
||||
label: 'Monitoring',
|
||||
color: 'text-cyan-400',
|
||||
bg: 'bg-cyan-400/10',
|
||||
ring: 'ring-cyan-400/30',
|
||||
pulse: true
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
icon: CircleX,
|
||||
label: 'Registered',
|
||||
color: 'text-foreground-subtle',
|
||||
bg: 'bg-foreground/5',
|
||||
ring: 'ring-foreground/10',
|
||||
pulse: false
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background flex flex-col">
|
||||
<Header />
|
||||
|
||||
<main className="flex-1 pt-24 pb-16 px-4 sm:px-6">
|
||||
<div className="max-w-6xl mx-auto">
|
||||
<main className="flex-1 pt-28 sm:pt-32 pb-20 sm:pb-24 px-4 sm:px-6">
|
||||
<div className="max-w-5xl mx-auto">
|
||||
{/* Header */}
|
||||
<div className="flex flex-col sm:flex-row sm:items-end sm:justify-between gap-4 mb-8">
|
||||
<div>
|
||||
@ -409,28 +460,42 @@ export default function DashboardPage() {
|
||||
</div>
|
||||
|
||||
{/* Tabs */}
|
||||
<div className="flex items-center gap-1 p-1 bg-background-secondary border border-border rounded-xl w-fit mb-8">
|
||||
<div className="flex items-center gap-1 p-1.5 bg-background-secondary/70 backdrop-blur-sm border border-border rounded-2xl w-fit mb-10">
|
||||
<button
|
||||
onClick={() => setActiveTab('watchlist')}
|
||||
className={clsx(
|
||||
"flex items-center gap-2 px-4 py-2 text-ui-sm font-medium rounded-lg transition-all",
|
||||
activeTab === 'watchlist' ? "bg-foreground text-background" : "text-foreground-muted hover:text-foreground"
|
||||
"flex items-center gap-2.5 px-5 py-2.5 text-ui font-medium rounded-xl transition-all duration-200",
|
||||
activeTab === 'watchlist'
|
||||
? "bg-foreground text-background shadow-lg"
|
||||
: "text-foreground-muted hover:text-foreground hover:bg-foreground/5"
|
||||
)}
|
||||
>
|
||||
<Eye className="w-4 h-4" />
|
||||
Watchlist
|
||||
{domains.length > 0 && <span className="text-ui-xs px-1.5 py-0.5 bg-background/20 rounded">{domains.length}</span>}
|
||||
{domains.length > 0 && (
|
||||
<span className={clsx(
|
||||
"text-ui-xs px-2 py-0.5 rounded-md",
|
||||
activeTab === 'watchlist' ? "bg-background/20" : "bg-foreground/10"
|
||||
)}>{domains.length}</span>
|
||||
)}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setActiveTab('portfolio')}
|
||||
className={clsx(
|
||||
"flex items-center gap-2 px-4 py-2 text-ui-sm font-medium rounded-lg transition-all",
|
||||
activeTab === 'portfolio' ? "bg-foreground text-background" : "text-foreground-muted hover:text-foreground"
|
||||
"flex items-center gap-2.5 px-5 py-2.5 text-ui font-medium rounded-xl transition-all duration-200",
|
||||
activeTab === 'portfolio'
|
||||
? "bg-foreground text-background shadow-lg"
|
||||
: "text-foreground-muted hover:text-foreground hover:bg-foreground/5"
|
||||
)}
|
||||
>
|
||||
<Briefcase className="w-4 h-4" />
|
||||
Portfolio
|
||||
{portfolio.length > 0 && <span className="text-ui-xs px-1.5 py-0.5 bg-background/20 rounded">{portfolio.length}</span>}
|
||||
{portfolio.length > 0 && (
|
||||
<span className={clsx(
|
||||
"text-ui-xs px-2 py-0.5 rounded-md",
|
||||
activeTab === 'portfolio' ? "bg-background/20" : "bg-foreground/10"
|
||||
)}>{portfolio.length}</span>
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@ -447,21 +512,49 @@ export default function DashboardPage() {
|
||||
<div className="space-y-6">
|
||||
{/* Stats Row */}
|
||||
<div className="grid grid-cols-2 sm:grid-cols-4 gap-4">
|
||||
<div className="p-4 bg-background-secondary border border-border rounded-xl">
|
||||
<p className="text-ui-xs text-foreground-muted uppercase tracking-wider mb-1">Tracked</p>
|
||||
<p className="text-2xl font-display text-foreground">{domains.length}</p>
|
||||
<div className="p-5 bg-background-secondary/50 border border-border rounded-2xl hover:border-foreground/20 transition-colors">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<div className="w-6 h-6 rounded-md bg-foreground/10 flex items-center justify-center">
|
||||
<Eye className="w-3.5 h-3.5 text-foreground-muted" />
|
||||
</div>
|
||||
<p className="text-ui-xs text-foreground-muted uppercase tracking-wider">Tracked</p>
|
||||
</div>
|
||||
<p className="text-3xl font-display text-foreground">{domains.length}</p>
|
||||
</div>
|
||||
<div className="p-4 bg-accent/5 border border-accent/20 rounded-xl">
|
||||
<p className="text-ui-xs text-accent/70 uppercase tracking-wider mb-1">Available</p>
|
||||
<p className="text-2xl font-display text-accent">{availableCount}</p>
|
||||
<div className="p-5 bg-emerald-500/5 border border-emerald-500/20 rounded-2xl hover:border-emerald-500/40 transition-colors">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<div className="w-6 h-6 rounded-md bg-emerald-500/20 flex items-center justify-center">
|
||||
<CircleCheck className="w-3.5 h-3.5 text-emerald-400" />
|
||||
</div>
|
||||
<p className="text-ui-xs text-emerald-400/80 uppercase tracking-wider">Available</p>
|
||||
</div>
|
||||
<p className="text-3xl font-display text-emerald-400">{availableCount}</p>
|
||||
</div>
|
||||
<div className="p-4 bg-background-secondary border border-border rounded-xl">
|
||||
<p className="text-ui-xs text-foreground-muted uppercase tracking-wider mb-1">Registered</p>
|
||||
<p className="text-2xl font-display text-foreground">{domains.length - availableCount}</p>
|
||||
<div className="p-5 bg-cyan-500/5 border border-cyan-500/20 rounded-2xl hover:border-cyan-500/40 transition-colors">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<div className="w-6 h-6 rounded-md bg-cyan-500/20 flex items-center justify-center">
|
||||
<Radio className="w-3.5 h-3.5 text-cyan-400" />
|
||||
</div>
|
||||
<p className="text-ui-xs text-cyan-400/80 uppercase tracking-wider">Monitoring</p>
|
||||
</div>
|
||||
<p className="text-3xl font-display text-cyan-400">{domains.filter(d => d.notify_on_available).length}</p>
|
||||
</div>
|
||||
<div className={clsx("p-4 border rounded-xl", expiringCount > 0 ? "bg-warning/5 border-warning/20" : "bg-background-secondary border-border")}>
|
||||
<p className={clsx("text-ui-xs uppercase tracking-wider mb-1", expiringCount > 0 ? "text-warning/70" : "text-foreground-muted")}>Expiring</p>
|
||||
<p className={clsx("text-2xl font-display", expiringCount > 0 ? "text-warning" : "text-foreground")}>{expiringCount}</p>
|
||||
<div className={clsx(
|
||||
"p-5 border rounded-2xl transition-colors",
|
||||
expiringCount > 0
|
||||
? "bg-amber-500/5 border-amber-500/20 hover:border-amber-500/40"
|
||||
: "bg-background-secondary/50 border-border hover:border-foreground/20"
|
||||
)}>
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<div className={clsx(
|
||||
"w-6 h-6 rounded-md flex items-center justify-center",
|
||||
expiringCount > 0 ? "bg-amber-500/20" : "bg-foreground/10"
|
||||
)}>
|
||||
<CircleAlert className={clsx("w-3.5 h-3.5", expiringCount > 0 ? "text-amber-400" : "text-foreground-muted")} />
|
||||
</div>
|
||||
<p className={clsx("text-ui-xs uppercase tracking-wider", expiringCount > 0 ? "text-amber-400/80" : "text-foreground-muted")}>Expiring</p>
|
||||
</div>
|
||||
<p className={clsx("text-3xl font-display", expiringCount > 0 ? "text-amber-400" : "text-foreground")}>{expiringCount}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -486,73 +579,102 @@ export default function DashboardPage() {
|
||||
|
||||
{/* Domain Table */}
|
||||
{domains.length === 0 ? (
|
||||
<div className="text-center py-16 border border-dashed border-border rounded-xl">
|
||||
<Eye className="w-10 h-10 text-foreground-subtle mx-auto mb-4" />
|
||||
<p className="text-body text-foreground-muted">No domains yet</p>
|
||||
<p className="text-body-sm text-foreground-subtle">Add your first domain above</p>
|
||||
<div className="text-center py-20 border border-dashed border-border/50 rounded-2xl bg-background-secondary/20">
|
||||
<div className="w-14 h-14 bg-foreground/5 rounded-2xl flex items-center justify-center mx-auto mb-5">
|
||||
<Eye className="w-7 h-7 text-foreground-subtle" />
|
||||
</div>
|
||||
<p className="text-body-lg text-foreground-muted mb-1">No domains tracked yet</p>
|
||||
<p className="text-body-sm text-foreground-subtle">Add your first domain above to start monitoring</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="border border-border rounded-xl overflow-hidden">
|
||||
<div className="border border-border rounded-2xl overflow-hidden bg-background-secondary/30">
|
||||
<table className="w-full">
|
||||
<thead className="bg-background-secondary">
|
||||
<thead className="bg-background-secondary/80 backdrop-blur-sm">
|
||||
<tr className="border-b border-border">
|
||||
<th className="text-left text-ui-xs font-medium text-foreground-muted uppercase tracking-wider px-4 py-3">Domain</th>
|
||||
<th className="text-left text-ui-xs font-medium text-foreground-muted uppercase tracking-wider px-4 py-3 hidden sm:table-cell">Status</th>
|
||||
<th className="text-left text-ui-xs font-medium text-foreground-muted uppercase tracking-wider px-4 py-3 hidden lg:table-cell">Expiration</th>
|
||||
<th className="text-left text-ui-xs font-medium text-foreground-muted uppercase tracking-wider px-4 py-3 hidden md:table-cell">Last Check</th>
|
||||
<th className="text-right text-ui-xs font-medium text-foreground-muted uppercase tracking-wider px-4 py-3">Actions</th>
|
||||
<th className="text-left text-ui-xs font-medium text-foreground-muted uppercase tracking-wider px-5 py-4">Domain</th>
|
||||
<th className="text-left text-ui-xs font-medium text-foreground-muted uppercase tracking-wider px-5 py-4 hidden sm:table-cell">Status</th>
|
||||
<th className="text-left text-ui-xs font-medium text-foreground-muted uppercase tracking-wider px-5 py-4 hidden lg:table-cell">Expiration</th>
|
||||
<th className="text-left text-ui-xs font-medium text-foreground-muted uppercase tracking-wider px-5 py-4 hidden md:table-cell">Last Check</th>
|
||||
<th className="text-right text-ui-xs font-medium text-foreground-muted uppercase tracking-wider px-5 py-4">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-border">
|
||||
<tbody className="divide-y divide-border/50">
|
||||
{domains.map((domain) => {
|
||||
const exp = formatExpirationDate(domain.expiration_date)
|
||||
const status = getDomainStatus(domain)
|
||||
const StatusIcon = status.icon
|
||||
return (
|
||||
<tr key={domain.id} className="group hover:bg-background-secondary/50 transition-colors">
|
||||
<td className="px-4 py-3">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className={clsx("w-2 h-2 rounded-full", domain.is_available ? "bg-accent" : "bg-foreground-subtle")} />
|
||||
<span className="font-mono text-body-sm text-foreground">{domain.name}</span>
|
||||
<tr key={domain.id} className="group hover:bg-background-secondary/60 transition-all duration-200">
|
||||
<td className="px-5 py-4">
|
||||
<div className="flex items-center gap-3.5">
|
||||
<div className={clsx(
|
||||
"relative w-8 h-8 rounded-lg flex items-center justify-center ring-1",
|
||||
status.bg, status.ring
|
||||
)}>
|
||||
<StatusIcon className={clsx("w-4 h-4", status.color)} />
|
||||
{status.pulse && (
|
||||
<span className={clsx(
|
||||
"absolute inset-0 rounded-lg animate-ping opacity-30",
|
||||
status.bg
|
||||
)} />
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<span className="font-mono text-body-sm font-medium text-foreground">{domain.name}</span>
|
||||
<p className={clsx("text-ui-xs mt-0.5 sm:hidden", status.color)}>{status.label}</p>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-4 py-3 hidden sm:table-cell">
|
||||
<span className={clsx("text-ui-xs px-2 py-1 rounded-full", domain.is_available ? "text-accent bg-accent/10" : "text-foreground-muted bg-foreground/5")}>
|
||||
{domain.is_available ? 'Available' : 'Registered'}
|
||||
<td className="px-5 py-4 hidden sm:table-cell">
|
||||
<span className={clsx(
|
||||
"inline-flex items-center gap-1.5 text-ui-xs px-2.5 py-1 rounded-full font-medium",
|
||||
status.bg, status.color
|
||||
)}>
|
||||
{status.label}
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-4 py-3 hidden lg:table-cell">
|
||||
<td className="px-5 py-4 hidden lg:table-cell">
|
||||
{exp ? (
|
||||
<span className={clsx("text-body-sm flex items-center gap-1", exp.urgent ? "text-warning" : "text-foreground-subtle")}>
|
||||
<span className={clsx(
|
||||
"text-body-sm flex items-center gap-1.5",
|
||||
exp.urgent ? "text-amber-400 font-medium" : "text-foreground-subtle"
|
||||
)}>
|
||||
<Calendar className="w-3.5 h-3.5" />{exp.text}
|
||||
</span>
|
||||
) : <span className="text-foreground-subtle">—</span>}
|
||||
) : <span className="text-foreground-subtle/50">—</span>}
|
||||
</td>
|
||||
<td className="px-4 py-3 hidden md:table-cell">
|
||||
<span className="text-body-sm text-foreground-subtle flex items-center gap-1">
|
||||
<td className="px-5 py-4 hidden md:table-cell">
|
||||
<span className="text-body-sm text-foreground-subtle flex items-center gap-1.5">
|
||||
<Clock className="w-3.5 h-3.5" />{formatDate(domain.last_checked)}
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-4 py-3">
|
||||
<div className="flex items-center justify-end gap-1 opacity-60 group-hover:opacity-100 transition-opacity">
|
||||
<td className="px-5 py-4">
|
||||
<div className="flex items-center justify-end gap-0.5 opacity-50 group-hover:opacity-100 transition-opacity">
|
||||
{isProOrHigher && (
|
||||
<button onClick={() => loadDomainHistory(domain.id)} className="p-2 text-foreground-subtle hover:text-foreground hover:bg-foreground/5 rounded-lg transition-all" title="History">
|
||||
<button onClick={() => loadDomainHistory(domain.id)} className="p-2 text-foreground-subtle hover:text-foreground hover:bg-foreground/10 rounded-lg transition-all" title="History">
|
||||
<History className="w-4 h-4" />
|
||||
</button>
|
||||
)}
|
||||
<button onClick={() => handleGetValuation(domain.name)} className="p-2 text-foreground-subtle hover:text-foreground hover:bg-foreground/5 rounded-lg transition-all" title="Valuation">
|
||||
<button onClick={() => handleGetValuation(domain.name)} className="p-2 text-foreground-subtle hover:text-foreground hover:bg-foreground/10 rounded-lg transition-all" title="Valuation">
|
||||
<Sparkles className="w-4 h-4" />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handleToggleNotify(domain.id, domain.notify_on_available)}
|
||||
disabled={togglingNotifyId === domain.id}
|
||||
className={clsx("p-2 rounded-lg transition-all", domain.notify_on_available ? "text-accent hover:bg-accent/10" : "text-foreground-subtle hover:text-foreground hover:bg-foreground/5")}
|
||||
title={domain.notify_on_available ? "Alerts on" : "Alerts off"}
|
||||
className={clsx(
|
||||
"p-2 rounded-lg transition-all",
|
||||
domain.notify_on_available
|
||||
? "text-cyan-400 hover:bg-cyan-400/10"
|
||||
: "text-foreground-subtle hover:text-foreground hover:bg-foreground/10"
|
||||
)}
|
||||
title={domain.notify_on_available ? "Monitoring active" : "Enable monitoring"}
|
||||
>
|
||||
{togglingNotifyId === domain.id ? <Loader2 className="w-4 h-4 animate-spin" /> : domain.notify_on_available ? <Bell className="w-4 h-4" /> : <BellOff className="w-4 h-4" />}
|
||||
</button>
|
||||
<button onClick={() => handleRefresh(domain.id)} disabled={refreshingId === domain.id} className="p-2 text-foreground-subtle hover:text-foreground hover:bg-foreground/5 rounded-lg transition-all" title="Refresh">
|
||||
<button onClick={() => handleRefresh(domain.id)} disabled={refreshingId === domain.id} className="p-2 text-foreground-subtle hover:text-foreground hover:bg-foreground/10 rounded-lg transition-all" title="Refresh">
|
||||
<RefreshCw className={clsx("w-4 h-4", refreshingId === domain.id && "animate-spin")} />
|
||||
</button>
|
||||
<button onClick={() => handleDelete(domain.id)} className="p-2 text-foreground-subtle hover:text-danger hover:bg-danger/10 rounded-lg transition-all" title="Remove">
|
||||
<button onClick={() => handleDelete(domain.id)} className="p-2 text-foreground-subtle hover:text-rose-400 hover:bg-rose-400/10 rounded-lg transition-all" title="Remove">
|
||||
<Trash2 className="w-4 h-4" />
|
||||
</button>
|
||||
</div>
|
||||
@ -573,42 +695,82 @@ export default function DashboardPage() {
|
||||
{/* Portfolio Stats */}
|
||||
{portfolioSummary && (
|
||||
<div className="grid grid-cols-2 sm:grid-cols-4 gap-4">
|
||||
<div className="p-4 bg-background-secondary border border-border rounded-xl">
|
||||
<p className="text-ui-xs text-foreground-muted uppercase tracking-wider mb-1">Total Value</p>
|
||||
<p className="text-2xl font-display text-foreground">{formatCurrency(portfolioSummary.total_value)}</p>
|
||||
<div className="p-5 bg-background-secondary/50 border border-border rounded-2xl hover:border-foreground/20 transition-colors">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<div className="w-6 h-6 rounded-md bg-foreground/10 flex items-center justify-center">
|
||||
<DollarSign className="w-3.5 h-3.5 text-foreground-muted" />
|
||||
</div>
|
||||
<p className="text-ui-xs text-foreground-muted uppercase tracking-wider">Total Value</p>
|
||||
</div>
|
||||
<p className="text-2xl sm:text-3xl font-display text-foreground">{formatCurrency(portfolioSummary.total_value)}</p>
|
||||
</div>
|
||||
<div className="p-4 bg-background-secondary border border-border rounded-xl">
|
||||
<p className="text-ui-xs text-foreground-muted uppercase tracking-wider mb-1">Invested</p>
|
||||
<p className="text-2xl font-display text-foreground">{formatCurrency(portfolioSummary.total_invested)}</p>
|
||||
<div className="p-5 bg-violet-500/5 border border-violet-500/20 rounded-2xl hover:border-violet-500/40 transition-colors">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<div className="w-6 h-6 rounded-md bg-violet-500/20 flex items-center justify-center">
|
||||
<Tag className="w-3.5 h-3.5 text-violet-400" />
|
||||
</div>
|
||||
<p className="text-ui-xs text-violet-400/80 uppercase tracking-wider">Invested</p>
|
||||
</div>
|
||||
<p className="text-2xl sm:text-3xl font-display text-violet-400">{formatCurrency(portfolioSummary.total_invested)}</p>
|
||||
</div>
|
||||
<div className={clsx("p-4 border rounded-xl", portfolioSummary.unrealized_profit >= 0 ? "bg-accent/5 border-accent/20" : "bg-danger/5 border-danger/20")}>
|
||||
<p className={clsx("text-ui-xs uppercase tracking-wider mb-1", portfolioSummary.unrealized_profit >= 0 ? "text-accent/70" : "text-danger/70")}>P/L</p>
|
||||
<p className={clsx("text-2xl font-display flex items-center gap-1", portfolioSummary.unrealized_profit >= 0 ? "text-accent" : "text-danger")}>
|
||||
{portfolioSummary.unrealized_profit >= 0 ? <ArrowUpRight className="w-5 h-5" /> : <ArrowDownRight className="w-5 h-5" />}
|
||||
{formatCurrency(Math.abs(portfolioSummary.unrealized_profit))}
|
||||
<div className={clsx(
|
||||
"p-5 border rounded-2xl transition-colors",
|
||||
portfolioSummary.unrealized_profit >= 0
|
||||
? "bg-emerald-500/5 border-emerald-500/20 hover:border-emerald-500/40"
|
||||
: "bg-rose-500/5 border-rose-500/20 hover:border-rose-500/40"
|
||||
)}>
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<div className={clsx(
|
||||
"w-6 h-6 rounded-md flex items-center justify-center",
|
||||
portfolioSummary.unrealized_profit >= 0 ? "bg-emerald-500/20" : "bg-rose-500/20"
|
||||
)}>
|
||||
{portfolioSummary.unrealized_profit >= 0
|
||||
? <ArrowUpRight className="w-3.5 h-3.5 text-emerald-400" />
|
||||
: <ArrowDownRight className="w-3.5 h-3.5 text-rose-400" />
|
||||
}
|
||||
</div>
|
||||
<p className={clsx("text-ui-xs uppercase tracking-wider", portfolioSummary.unrealized_profit >= 0 ? "text-emerald-400/80" : "text-rose-400/80")}>P/L</p>
|
||||
</div>
|
||||
<p className={clsx("text-2xl sm:text-3xl font-display", portfolioSummary.unrealized_profit >= 0 ? "text-emerald-400" : "text-rose-400")}>
|
||||
{portfolioSummary.unrealized_profit >= 0 ? '+' : ''}{formatCurrency(portfolioSummary.unrealized_profit)}
|
||||
</p>
|
||||
</div>
|
||||
<div className={clsx("p-4 border rounded-xl", portfolioSummary.overall_roi >= 0 ? "bg-accent/5 border-accent/20" : "bg-danger/5 border-danger/20")}>
|
||||
<p className={clsx("text-ui-xs uppercase tracking-wider mb-1", portfolioSummary.overall_roi >= 0 ? "text-accent/70" : "text-danger/70")}>ROI</p>
|
||||
<p className={clsx("text-2xl font-display", portfolioSummary.overall_roi >= 0 ? "text-accent" : "text-danger")}>
|
||||
<div className={clsx(
|
||||
"p-5 border rounded-2xl transition-colors",
|
||||
portfolioSummary.overall_roi >= 0
|
||||
? "bg-emerald-500/5 border-emerald-500/20 hover:border-emerald-500/40"
|
||||
: "bg-rose-500/5 border-rose-500/20 hover:border-rose-500/40"
|
||||
)}>
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<div className={clsx(
|
||||
"w-6 h-6 rounded-md flex items-center justify-center",
|
||||
portfolioSummary.overall_roi >= 0 ? "bg-emerald-500/20" : "bg-rose-500/20"
|
||||
)}>
|
||||
<BarChart3 className={clsx("w-3.5 h-3.5", portfolioSummary.overall_roi >= 0 ? "text-emerald-400" : "text-rose-400")} />
|
||||
</div>
|
||||
<p className={clsx("text-ui-xs uppercase tracking-wider", portfolioSummary.overall_roi >= 0 ? "text-emerald-400/80" : "text-rose-400/80")}>ROI</p>
|
||||
</div>
|
||||
<p className={clsx("text-2xl sm:text-3xl font-display", portfolioSummary.overall_roi >= 0 ? "text-emerald-400" : "text-rose-400")}>
|
||||
{portfolioSummary.overall_roi >= 0 ? '+' : ''}{portfolioSummary.overall_roi.toFixed(1)}%
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<button onClick={() => setShowAddPortfolioModal(true)} className="flex items-center gap-2 px-5 py-3 bg-foreground text-background text-ui-sm font-medium rounded-xl hover:bg-foreground/90 transition-all">
|
||||
<button onClick={() => setShowAddPortfolioModal(true)} className="flex items-center gap-2.5 px-6 py-3.5 bg-foreground text-background text-ui font-medium rounded-xl hover:bg-foreground/90 transition-all shadow-lg shadow-foreground/10">
|
||||
<Plus className="w-4 h-4" />
|
||||
Add Domain
|
||||
Add Domain to Portfolio
|
||||
</button>
|
||||
|
||||
{loadingPortfolio ? (
|
||||
<div className="flex justify-center py-16"><Loader2 className="w-6 h-6 animate-spin text-accent" /></div>
|
||||
<div className="flex justify-center py-20"><Loader2 className="w-7 h-7 animate-spin text-accent" /></div>
|
||||
) : portfolio.length === 0 ? (
|
||||
<div className="text-center py-16 border border-dashed border-border rounded-xl">
|
||||
<Briefcase className="w-10 h-10 text-foreground-subtle mx-auto mb-4" />
|
||||
<p className="text-body text-foreground-muted">Portfolio is empty</p>
|
||||
<p className="text-body-sm text-foreground-subtle">Add domains you own to track their value</p>
|
||||
<div className="text-center py-20 border border-dashed border-border/50 rounded-2xl bg-background-secondary/20">
|
||||
<div className="w-14 h-14 bg-foreground/5 rounded-2xl flex items-center justify-center mx-auto mb-5">
|
||||
<Briefcase className="w-7 h-7 text-foreground-subtle" />
|
||||
</div>
|
||||
<p className="text-body-lg text-foreground-muted mb-1">Your portfolio is empty</p>
|
||||
<p className="text-body-sm text-foreground-subtle">Add domains you own to track their value and ROI</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-3">
|
||||
@ -655,18 +817,19 @@ export default function DashboardPage() {
|
||||
)}
|
||||
|
||||
{/* Quick Links */}
|
||||
<div className="mt-10 p-5 bg-background-secondary/50 border border-border rounded-xl flex flex-col sm:flex-row items-start sm:items-center justify-between gap-4">
|
||||
<div className="mt-12 p-6 bg-gradient-to-r from-accent/5 via-cyan-500/5 to-violet-500/5 border border-accent/20 rounded-2xl flex flex-col sm:flex-row items-start sm:items-center justify-between gap-4">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="w-10 h-10 bg-accent/10 rounded-lg flex items-center justify-center">
|
||||
<TrendingUp className="w-5 h-5 text-accent" />
|
||||
<div className="w-12 h-12 bg-accent/10 rounded-xl flex items-center justify-center ring-1 ring-accent/20">
|
||||
<TrendingUp className="w-6 h-6 text-accent" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-body-sm font-medium text-foreground">TLD Price Intelligence</p>
|
||||
<p className="text-body-xs text-foreground-muted">Track 886+ domain extensions</p>
|
||||
<p className="text-body font-medium text-foreground">TLD Price Intelligence</p>
|
||||
<p className="text-body-sm text-foreground-muted">Track 886+ domain extensions in real-time</p>
|
||||
</div>
|
||||
</div>
|
||||
<Link href="/tld-pricing" className="text-ui-sm text-accent hover:text-accent-hover transition-colors flex items-center gap-1">
|
||||
Explore <ChevronRight className="w-4 h-4" />
|
||||
<Link href="/tld-pricing" className="text-ui font-medium text-accent hover:text-accent-hover transition-colors flex items-center gap-1.5 group">
|
||||
Explore Prices
|
||||
<ChevronRight className="w-4 h-4 group-hover:translate-x-0.5 transition-transform" />
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user