Improve Portfolio UX and redesign AnalyzePanel
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

- Move Portfolio tabs directly under subtitle (above filters line)
- Add Health Detail Overlay when clicking health score (like Watchlist)
- Redesign AnalyzePanel: wider (600-680px), larger text, better contrast
- Improved section headers with colored backgrounds
- Larger status indicators and score display
- Better spacing and readability throughout
This commit is contained in:
2025-12-16 15:02:47 +01:00
parent 7d68266745
commit 82619f5506
2 changed files with 307 additions and 142 deletions

View File

@ -30,6 +30,9 @@ import {
Calendar, Calendar,
Edit3, Edit3,
CheckCircle, CheckCircle,
CheckCircle2,
XCircle,
AlertTriangle,
Copy, Copy,
Check, Check,
Navigation, Navigation,
@ -687,6 +690,9 @@ export default function PortfolioPage() {
const [navDrawerOpen, setNavDrawerOpen] = useState(false) const [navDrawerOpen, setNavDrawerOpen] = useState(false)
const [expandedRow, setExpandedRow] = useState<number | null>(null) const [expandedRow, setExpandedRow] = useState<number | null>(null)
// Health detail overlay
const [selectedHealthDomain, setSelectedHealthDomain] = useState<PortfolioDomain | null>(null)
const tier = subscription?.tier || 'scout' const tier = subscription?.tier || 'scout'
const tierName = subscription?.tier_name || tier const tierName = subscription?.tier_name || tier
const TierIcon = tierName === 'Tycoon' ? Crown : tierName === 'Trader' ? TrendingUp : Zap const TierIcon = tierName === 'Tycoon' ? Crown : tierName === 'Trader' ? TrendingUp : Zap
@ -949,9 +955,12 @@ export default function PortfolioPage() {
return ( return (
<button <button
onClick={() => handleHealthCheck(domain.domain)} onClick={() => {
setSelectedHealthDomain(domain)
if (!health) handleHealthCheck(domain.domain)
}}
disabled={isChecking} disabled={isChecking}
title={health ? `${cfg.label} • Score ${health.score}/100` : 'Run health check'} title={health ? `${cfg.label} • Score ${health.score}/100 • Click for details` : 'Run health check'}
className={clsx( className={clsx(
"flex items-center gap-1 px-2 py-1 text-[10px] font-mono uppercase border transition-colors", "flex items-center gap-1 px-2 py-1 text-[10px] font-mono uppercase border transition-colors",
cfg.color, cfg.bg, cfg.border, cfg.color, cfg.bg, cfg.border,
@ -1109,51 +1118,23 @@ export default function PortfolioPage() {
{/* DESKTOP HEADER */} {/* DESKTOP HEADER */}
<section className="hidden lg:block px-10 pt-10 pb-6"> <section className="hidden lg:block px-10 pt-10 pb-6">
<div className="flex items-end justify-between gap-8"> <div className="flex items-start justify-between gap-8">
<div className="space-y-3"> <div className="space-y-4">
<div className="flex items-center gap-2"> <div>
<div className="flex items-center gap-2 mb-2">
<div className="w-1.5 h-1.5 bg-accent animate-pulse" /> <div className="w-1.5 h-1.5 bg-accent animate-pulse" />
<span className="text-[10px] font-mono tracking-[0.2em] text-accent uppercase">Portfolio Manager</span> <span className="text-[10px] font-mono tracking-[0.2em] text-accent uppercase">Portfolio Manager</span>
</div> </div>
<h1 className="font-display text-[2.5rem] leading-[1] tracking-[-0.02em] text-white"> <h1 className="font-display text-[2.5rem] leading-[1] tracking-[-0.02em] text-white">
My Portfolio My Portfolio
</h1> </h1>
<p className="text-sm text-white/40 font-mono max-w-lg"> <p className="text-sm text-white/40 font-mono max-w-lg mt-2">
Track your domain investments. Add purchase details, monitor values, verify ownership, and list for sale. Track your domain investments. Add purchase details, monitor values, verify ownership, and list for sale.
</p> </p>
</div> </div>
<div className="flex items-center gap-8"> {/* TABS - Directly under subtitle */}
<div className="text-right" title="Total invested"> <div className="flex gap-2 pt-2">
<div className="text-2xl font-bold text-white font-mono">{formatCurrency(summary?.total_invested || 0)}</div>
<div className="text-[9px] font-mono text-white/30 uppercase">Invested</div>
</div>
<div className="text-right" title="Current market value">
<div className="text-2xl font-bold text-accent font-mono">{formatCurrency(summary?.total_value || 0)}</div>
<div className="text-[9px] font-mono text-white/30 uppercase">Value</div>
</div>
<div className="text-right" title="Return on investment">
<div className={clsx("text-2xl font-bold font-mono", (summary?.overall_roi || 0) >= 0 ? "text-accent" : "text-rose-400")}>
{formatROI(summary?.overall_roi || 0)}
</div>
<div className="text-[9px] font-mono text-white/30 uppercase">ROI</div>
</div>
<div className="pl-6 border-l border-white/10">
<button
onClick={() => setShowAddModal(true)}
className="flex items-center gap-2 px-5 py-3 bg-accent text-black text-xs font-bold uppercase tracking-wider hover:bg-white transition-colors"
>
<Plus className="w-4 h-4" />
Add Domain
</button>
</div>
</div>
</div>
</section>
{/* TABS - Matching Hunt page style */}
<section className="px-4 lg:px-10 py-4 border-y border-white/[0.08] bg-white/[0.01]">
<div className="flex gap-2 mb-4">
<button <button
onClick={() => setActiveTab('assets')} onClick={() => setActiveTab('assets')}
className={clsx( className={clsx(
@ -1184,8 +1165,38 @@ export default function PortfolioPage() {
)} )}
</button> </button>
</div> </div>
</div>
{/* Asset Filters - only show when assets tab active */} <div className="flex items-center gap-8">
<div className="text-right" title="Total invested">
<div className="text-2xl font-bold text-white font-mono">{formatCurrency(summary?.total_invested || 0)}</div>
<div className="text-[9px] font-mono text-white/30 uppercase">Invested</div>
</div>
<div className="text-right" title="Current market value">
<div className="text-2xl font-bold text-accent font-mono">{formatCurrency(summary?.total_value || 0)}</div>
<div className="text-[9px] font-mono text-white/30 uppercase">Value</div>
</div>
<div className="text-right" title="Return on investment">
<div className={clsx("text-2xl font-bold font-mono", (summary?.overall_roi || 0) >= 0 ? "text-accent" : "text-rose-400")}>
{formatROI(summary?.overall_roi || 0)}
</div>
<div className="text-[9px] font-mono text-white/30 uppercase">ROI</div>
</div>
<div className="pl-6 border-l border-white/10">
<button
onClick={() => setShowAddModal(true)}
className="flex items-center gap-2 px-5 py-3 bg-accent text-black text-xs font-bold uppercase tracking-wider hover:bg-white transition-colors"
>
<Plus className="w-4 h-4" />
Add Domain
</button>
</div>
</div>
</div>
</section>
{/* FILTERS BAR - Only for assets tab */}
<section className="hidden lg:block px-10 py-3 border-y border-white/[0.08] bg-white/[0.01]">
{activeTab === 'assets' && ( {activeTab === 'assets' && (
<div className="flex items-center gap-2 overflow-x-auto"> <div className="flex items-center gap-2 overflow-x-auto">
{[ {[
@ -1209,6 +1220,11 @@ export default function PortfolioPage() {
))} ))}
</div> </div>
)} )}
{activeTab === 'financials' && (
<div className="text-[10px] font-mono text-white/40 uppercase tracking-wider">
Financial Overview & Renewal Costs
</div>
)}
</section> </section>
{/* TAB CONTENT */} {/* TAB CONTENT */}
@ -1744,6 +1760,149 @@ export default function PortfolioPage() {
/> />
)} )}
{/* HEALTH DETAIL OVERLAY */}
{selectedHealthDomain && (
<div
className="fixed inset-0 z-[100] bg-black/80 flex items-center justify-center p-4"
onClick={() => setSelectedHealthDomain(null)}
>
<div
className="w-full max-w-md bg-[#0A0A0A] border border-white/[0.08]"
onClick={e => e.stopPropagation()}
>
{/* Header */}
<div className="px-5 py-4 border-b border-white/[0.08] flex items-center justify-between">
<div>
<div className="text-[10px] font-mono text-white/40 uppercase tracking-wider mb-1">Health Check</div>
<h3 className="text-lg font-bold text-white font-mono">{selectedHealthDomain.domain}</h3>
</div>
<button
onClick={() => setSelectedHealthDomain(null)}
className="p-2 text-white/40 hover:text-white transition-colors"
>
<X className="w-5 h-5" />
</button>
</div>
{/* Body */}
<div className="p-5">
{(() => {
const key = selectedHealthDomain.domain.toLowerCase()
const health = healthByDomain[key]
const isLoading = checkingHealth.has(key)
const cfg = healthConfig[(health?.status as HealthStatus) || 'unknown']
return (
<>
{/* Score Badge */}
<div className="flex items-center gap-3 mb-5">
<div className={clsx(
"px-3 py-1.5 text-sm font-mono font-bold uppercase border",
cfg.bg, cfg.color, cfg.border
)}>
{cfg.label}
</div>
{health?.score !== undefined && (
<span className="text-sm font-mono text-white/60">Score: {health.score}/100</span>
)}
</div>
{/* Checks */}
{health ? (
<div className="space-y-px mb-5 border border-white/[0.08]">
{/* DNS */}
<div className="flex items-center justify-between px-4 py-3 bg-[#020202]">
<span className="text-sm font-mono text-white/70">DNS Resolution</span>
{health.dns?.has_a || health.dns?.has_ns ? (
<div className="flex items-center gap-2">
<CheckCircle2 className="w-5 h-5 text-accent" />
<span className="text-sm font-mono text-accent font-bold">OK</span>
</div>
) : (
<div className="flex items-center gap-2">
<XCircle className="w-5 h-5 text-rose-400" />
<span className="text-sm font-mono text-rose-400 font-bold">FAIL</span>
</div>
)}
</div>
{/* HTTP */}
<div className="flex items-center justify-between px-4 py-3 bg-[#020202]">
<span className="text-sm font-mono text-white/70">HTTP Access</span>
{health.http?.is_reachable ? (
<div className="flex items-center gap-2">
<CheckCircle2 className="w-5 h-5 text-accent" />
<span className="text-sm font-mono text-accent font-bold">OK</span>
</div>
) : (
<div className="flex items-center gap-2">
<XCircle className="w-5 h-5 text-rose-400" />
<span className="text-sm font-mono text-rose-400 font-bold">{health.http?.error?.substring(0, 15) || 'FAIL'}</span>
</div>
)}
</div>
{/* SSL */}
<div className="flex items-center justify-between px-4 py-3 bg-[#020202]">
<span className="text-sm font-mono text-white/70">SSL Certificate</span>
{health.ssl?.has_certificate ? (
<div className="flex items-center gap-2">
<CheckCircle2 className="w-5 h-5 text-accent" />
<span className="text-sm font-mono text-accent font-bold">VALID</span>
</div>
) : (
<div className="flex items-center gap-2">
<XCircle className="w-5 h-5 text-rose-400" />
<span className="text-sm font-mono text-rose-400 font-bold">MISSING</span>
</div>
)}
</div>
{/* Parked Status */}
<div className="flex items-center justify-between px-4 py-3 bg-[#020202]">
<span className="text-sm font-mono text-white/70">Not Parked</span>
{!health.dns?.is_parked && !health.http?.is_parked ? (
<div className="flex items-center gap-2">
<CheckCircle2 className="w-5 h-5 text-accent" />
<span className="text-sm font-mono text-accent font-bold">OK</span>
</div>
) : (
<div className="flex items-center gap-2">
<AlertTriangle className="w-5 h-5 text-amber-400" />
<span className="text-sm font-mono text-amber-400 font-bold">PARKED</span>
</div>
)}
</div>
</div>
) : (
<div className="py-10 text-center text-white/40 text-sm font-mono mb-5 border border-white/[0.08] bg-[#020202]">
{isLoading ? 'Running health check...' : 'Click below to run health check'}
</div>
)}
{/* Run Check Button */}
<button
onClick={() => handleHealthCheck(selectedHealthDomain.domain)}
disabled={isLoading}
className="w-full py-3.5 bg-accent text-black text-sm font-bold uppercase tracking-wider hover:bg-white transition-colors flex items-center justify-center gap-2 disabled:opacity-50"
>
{isLoading ? (
<Loader2 className="w-5 h-5 animate-spin" />
) : (
<>
<RefreshCw className="w-5 h-5" />
{health ? 'Refresh Check' : 'Run Check'}
</>
)}
</button>
</>
)
})()}
</div>
</div>
</div>
)}
{toast && <Toast message={toast.message} type={toast.type} onClose={hideToast} />} {toast && <Toast message={toast.message} type={toast.type} onClose={hideToast} />}
</div> </div>
) )

View File

@ -20,6 +20,8 @@ import {
Eye, Eye,
ChevronDown, ChevronDown,
ChevronUp, ChevronUp,
CheckCircle2,
XCircle,
} from 'lucide-react' } from 'lucide-react'
import { api } from '@/lib/api' import { api } from '@/lib/api'
import { useAnalyzePanelStore } from '@/lib/analyze-store' import { useAnalyzePanelStore } from '@/lib/analyze-store'
@ -32,13 +34,13 @@ import type { AnalyzeResponse, AnalyzeSection, AnalyzeItem } from '@/components/
function getStatusColor(status: string) { function getStatusColor(status: string) {
switch (status) { switch (status) {
case 'pass': case 'pass':
return { bg: 'bg-accent/10', text: 'text-accent', border: 'border-accent/30', icon: Check } return { bg: 'bg-accent/20', text: 'text-accent', border: 'border-accent/40', icon: CheckCircle2 }
case 'warn': case 'warn':
return { bg: 'bg-amber-400/10', text: 'text-amber-300', border: 'border-amber-400/30', icon: AlertTriangle } return { bg: 'bg-amber-400/20', text: 'text-amber-300', border: 'border-amber-400/40', icon: AlertTriangle }
case 'fail': case 'fail':
return { bg: 'bg-red-500/10', text: 'text-red-400', border: 'border-red-500/30', icon: X } return { bg: 'bg-red-500/20', text: 'text-red-400', border: 'border-red-500/40', icon: XCircle }
default: default:
return { bg: 'bg-white/5', text: 'text-white/40', border: 'border-white/10', icon: null } return { bg: 'bg-white/10', text: 'text-white/50', border: 'border-white/20', icon: null }
} }
} }
@ -60,15 +62,15 @@ function getSectionIcon(key: string) {
function getSectionColor(key: string) { function getSectionColor(key: string) {
switch (key) { switch (key) {
case 'authority': case 'authority':
return 'text-blue-400' return { text: 'text-blue-400', bg: 'bg-blue-500/10', border: 'border-blue-500/30' }
case 'market': case 'market':
return 'text-emerald-400' return { text: 'text-emerald-400', bg: 'bg-emerald-500/10', border: 'border-emerald-500/30' }
case 'risk': case 'risk':
return 'text-amber-400' return { text: 'text-amber-400', bg: 'bg-amber-500/10', border: 'border-amber-500/30' }
case 'value': case 'value':
return 'text-violet-400' return { text: 'text-violet-400', bg: 'bg-violet-500/10', border: 'border-violet-500/30' }
default: default:
return 'text-white/60' return { text: 'text-white/60', bg: 'bg-white/5', border: 'border-white/20' }
} }
} }
@ -203,28 +205,28 @@ export function AnalyzePanel() {
return ( return (
<div className="fixed inset-0 z-[200]"> <div className="fixed inset-0 z-[200]">
{/* Backdrop */} {/* Backdrop */}
<div className="absolute inset-0 bg-black/80 backdrop-blur-sm" onClick={close} /> <div className="absolute inset-0 bg-black/85 backdrop-blur-md" onClick={close} />
{/* Panel */} {/* Panel - WIDER & MORE READABLE */}
<div className="absolute right-0 top-0 bottom-0 w-full sm:w-[480px] bg-[#050505] border-l border-white/[0.06] flex flex-col overflow-hidden"> <div className="absolute right-0 top-0 bottom-0 w-full sm:w-[600px] lg:w-[680px] bg-[#0A0A0A] border-l border-white/10 flex flex-col overflow-hidden shadow-2xl">
{/* Header */} {/* Header */}
<div className="shrink-0 border-b border-white/[0.06]"> <div className="shrink-0 border-b border-white/10 bg-[#050505]">
{/* Top Bar */} {/* Top Bar */}
<div className="px-4 py-3 flex items-center justify-between"> <div className="px-6 py-5 flex items-center justify-between">
<div className="flex items-center gap-3"> <div className="flex items-center gap-4">
<div className="w-10 h-10 bg-accent/10 border border-accent/20 flex items-center justify-center"> <div className="w-12 h-12 bg-accent/15 border border-accent/30 flex items-center justify-center">
<Shield className="w-5 h-5 text-accent" /> <Shield className="w-6 h-6 text-accent" />
</div> </div>
<div> <div>
<div className="text-xs font-mono text-white/40 uppercase tracking-wider">Analyze</div> <div className="text-xs font-mono text-accent uppercase tracking-widest mb-1">Domain Analysis</div>
<div className="text-base font-bold text-white font-mono truncate max-w-[200px]"> <div className="text-xl font-bold text-white font-mono truncate max-w-[300px]">
{headerDomain} {headerDomain}
</div> </div>
</div> </div>
</div> </div>
<div className="flex items-center gap-1.5"> <div className="flex items-center gap-2">
<button <button
onClick={async () => { onClick={async () => {
const ok = await copyToClipboard(headerDomain) const ok = await copyToClipboard(headerDomain)
@ -232,48 +234,49 @@ export function AnalyzePanel() {
setTimeout(() => setCopied(false), 1500) setTimeout(() => setCopied(false), 1500)
}} }}
className={clsx( className={clsx(
"w-8 h-8 flex items-center justify-center border transition-all", "w-10 h-10 flex items-center justify-center border transition-all",
copied ? "border-accent bg-accent/10 text-accent" : "border-white/10 text-white/40 hover:text-white" copied ? "border-accent bg-accent/20 text-accent" : "border-white/20 text-white/50 hover:text-white hover:bg-white/10"
)} )}
> >
{copied ? <Check className="w-4 h-4" /> : <Copy className="w-4 h-4" />} {copied ? <Check className="w-5 h-5" /> : <Copy className="w-5 h-5" />}
</button> </button>
<a <a
href={`https://${encodeURIComponent(headerDomain)}`} href={`https://${encodeURIComponent(headerDomain)}`}
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
className="w-8 h-8 flex items-center justify-center border border-white/10 text-white/40 hover:text-white transition-colors" className="w-10 h-10 flex items-center justify-center border border-white/20 text-white/50 hover:text-white hover:bg-white/10 transition-colors"
> >
<ExternalLink className="w-4 h-4" /> <ExternalLink className="w-5 h-5" />
</a> </a>
<button <button
onClick={refresh} onClick={refresh}
disabled={loading} disabled={loading}
className="w-8 h-8 flex items-center justify-center border border-white/10 text-white/40 hover:text-white transition-colors disabled:opacity-50" className="w-10 h-10 flex items-center justify-center border border-white/20 text-white/50 hover:text-white hover:bg-white/10 transition-colors disabled:opacity-50"
> >
<RefreshCw className={clsx('w-4 h-4', loading && 'animate-spin')} /> <RefreshCw className={clsx('w-5 h-5', loading && 'animate-spin')} />
</button> </button>
<button <button
onClick={close} onClick={close}
className="w-8 h-8 flex items-center justify-center border border-white/10 text-white/40 hover:text-white transition-colors" className="w-10 h-10 flex items-center justify-center border border-white/20 text-white/50 hover:text-white hover:bg-white/10 transition-colors ml-2"
> >
<X className="w-4 h-4" /> <X className="w-5 h-5" />
</button> </button>
</div> </div>
</div> </div>
{/* Score Bar */} {/* Score Bar - LARGER */}
{overallScore && !loading && ( {overallScore && !loading && (
<div className="px-4 pb-3"> <div className="px-6 pb-5">
<div className="flex items-center gap-3"> <div className="flex items-center gap-4 p-4 bg-white/[0.03] border border-white/10">
<div className={clsx( <div className={clsx(
"text-2xl font-bold font-mono", "text-4xl font-bold font-mono",
overallScore.score >= 70 ? "text-accent" : overallScore.score >= 40 ? "text-amber-400" : "text-red-400" overallScore.score >= 70 ? "text-accent" : overallScore.score >= 40 ? "text-amber-400" : "text-red-400"
)}> )}>
{overallScore.score} {overallScore.score}
</div> </div>
<div className="flex-1"> <div className="flex-1">
<div className="h-1.5 bg-white/5 rounded-full overflow-hidden flex"> <div className="text-xs font-mono text-white/50 uppercase tracking-wider mb-2">Overall Score</div>
<div className="h-3 bg-white/10 overflow-hidden flex">
<div <div
className="h-full bg-accent transition-all" className="h-full bg-accent transition-all"
style={{ width: `${(overallScore.pass / overallScore.total) * 100}%` }} style={{ width: `${(overallScore.pass / overallScore.total) * 100}%` }}
@ -288,90 +291,93 @@ export function AnalyzePanel() {
/> />
</div> </div>
</div> </div>
<div className="flex items-center gap-2 text-[10px] font-mono"> <div className="flex flex-col gap-1 text-sm font-mono">
<span className="text-accent">{overallScore.pass} pass</span> <span className="text-accent flex items-center gap-2"><CheckCircle2 className="w-4 h-4" /> {overallScore.pass}</span>
<span className="text-amber-400">{overallScore.warn} warn</span> <span className="text-amber-400 flex items-center gap-2"><AlertTriangle className="w-4 h-4" /> {overallScore.warn}</span>
<span className="text-red-400">{overallScore.fail} fail</span> <span className="text-red-400 flex items-center gap-2"><XCircle className="w-4 h-4" /> {overallScore.fail}</span>
</div> </div>
</div> </div>
</div> </div>
)} )}
{/* Mode Toggle */} {/* Mode Toggle */}
<div className="px-4 pb-3 flex items-center gap-2"> <div className="px-6 pb-4 flex items-center gap-3">
<button <button
onClick={() => setFastMode(!fastMode)} onClick={() => setFastMode(!fastMode)}
className={clsx( className={clsx(
"flex items-center gap-1.5 px-3 py-1.5 text-[10px] font-bold uppercase tracking-wider border transition-all", "flex items-center gap-2 px-4 py-2 text-sm font-bold uppercase tracking-wider border transition-all",
fastMode fastMode
? "border-accent/30 bg-accent/10 text-accent" ? "border-accent/40 bg-accent/15 text-accent"
: "border-white/10 text-white/40 hover:text-white" : "border-white/20 text-white/50 hover:text-white hover:bg-white/10"
)} )}
> >
<Zap className="w-3 h-3" /> <Zap className="w-4 h-4" />
Fast Fast Mode
</button> </button>
{data?.cached && ( {data?.cached && (
<span className="text-[10px] font-mono text-white/30 px-2 py-1 border border-white/10"> <span className="text-sm font-mono text-white/40 px-3 py-2 border border-white/20 bg-white/5">
Cached Cached
</span> </span>
)} )}
</div> </div>
</div> </div>
{/* Body */} {/* Body - BETTER SPACING */}
<div className="flex-1 overflow-y-auto"> <div className="flex-1 overflow-y-auto">
{loading ? ( {loading ? (
<div className="flex items-center justify-center py-20"> <div className="flex items-center justify-center py-24">
<div className="text-center"> <div className="text-center">
<RefreshCw className="w-6 h-6 text-accent animate-spin mx-auto mb-3" /> <RefreshCw className="w-10 h-10 text-accent animate-spin mx-auto mb-4" />
<div className="text-sm font-mono text-white/40">Analyzing...</div> <div className="text-base font-mono text-white/50">Analyzing domain...</div>
</div> </div>
</div> </div>
) : error ? ( ) : error ? (
<div className="p-4"> <div className="p-6">
<div className="border border-red-500/20 bg-red-500/5 p-4"> <div className="border border-red-500/30 bg-red-500/10 p-6">
<div className="text-sm font-bold text-red-400 mb-1">Analysis Failed</div> <div className="text-lg font-bold text-red-400 mb-2">Analysis Failed</div>
<div className="text-xs font-mono text-white/40">{error}</div> <div className="text-sm font-mono text-white/60">{error}</div>
</div> </div>
</div> </div>
) : !data ? ( ) : !data ? (
<div className="flex items-center justify-center py-20"> <div className="flex items-center justify-center py-24">
<div className="text-sm font-mono text-white/30">No data</div> <div className="text-base font-mono text-white/40">No data available</div>
</div> </div>
) : ( ) : (
<div className="p-3 space-y-2"> <div className="p-6 space-y-4">
{visibleSections.map((section) => { {visibleSections.map((section) => {
const SectionIcon = getSectionIcon(section.key) const SectionIcon = getSectionIcon(section.key)
const sectionColor = getSectionColor(section.key) const sectionStyle = getSectionColor(section.key)
const isExpanded = expandedSections[section.key] !== false const isExpanded = expandedSections[section.key] !== false
return ( return (
<div key={section.key} className="border border-white/[0.06] bg-[#020202] overflow-hidden"> <div key={section.key} className={clsx("border overflow-hidden", sectionStyle.border, "bg-[#050505]")}>
{/* Section Header */} {/* Section Header - LARGER */}
<button <button
onClick={() => toggleSection(section.key)} onClick={() => toggleSection(section.key)}
className="w-full px-3 py-2.5 flex items-center justify-between hover:bg-white/[0.02] transition-colors" className={clsx(
"w-full px-5 py-4 flex items-center justify-between transition-colors",
sectionStyle.bg, "hover:brightness-110"
)}
> >
<div className="flex items-center gap-2"> <div className="flex items-center gap-3">
<SectionIcon className={clsx("w-4 h-4", sectionColor)} /> <SectionIcon className={clsx("w-5 h-5", sectionStyle.text)} />
<span className="text-xs font-bold uppercase tracking-wider text-white/80"> <span className={clsx("text-sm font-bold uppercase tracking-wider", sectionStyle.text)}>
{section.title} {section.title}
</span> </span>
<span className="text-[10px] font-mono text-white/30"> <span className="text-sm font-mono text-white/40 ml-2">
{section.items.length} {section.items.length} checks
</span> </span>
</div> </div>
{isExpanded ? ( {isExpanded ? (
<ChevronUp className="w-4 h-4 text-white/30" /> <ChevronUp className="w-5 h-5 text-white/40" />
) : ( ) : (
<ChevronDown className="w-4 h-4 text-white/30" /> <ChevronDown className="w-5 h-5 text-white/40" />
)} )}
</button> </button>
{/* Section Items */} {/* Section Items - BETTER CONTRAST */}
{isExpanded && ( {isExpanded && (
<div className="border-t border-white/[0.04]"> <div className="border-t border-white/10">
{section.items.map((item) => { {section.items.map((item) => {
const statusStyle = getStatusColor(item.status) const statusStyle = getStatusColor(item.status)
const StatusIcon = statusStyle.icon const StatusIcon = statusStyle.icon
@ -379,53 +385,53 @@ export function AnalyzePanel() {
return ( return (
<div <div
key={item.key} key={item.key}
className="px-3 py-2 border-b border-white/[0.04] last:border-0 hover:bg-white/[0.01] transition-colors" className="px-5 py-4 border-b border-white/[0.06] last:border-0 hover:bg-white/[0.03] transition-colors"
> >
<div className="flex items-start gap-3"> <div className="flex items-start gap-4">
{/* Status Indicator */} {/* Status Indicator - LARGER */}
<div className={clsx( <div className={clsx(
"w-6 h-6 flex items-center justify-center shrink-0 mt-0.5", "w-10 h-10 flex items-center justify-center shrink-0",
statusStyle.bg, statusStyle.border, "border" statusStyle.bg, statusStyle.border, "border"
)}> )}>
{StatusIcon && <StatusIcon className={clsx("w-3 h-3", statusStyle.text)} />} {StatusIcon && <StatusIcon className={clsx("w-5 h-5", statusStyle.text)} />}
</div> </div>
{/* Content */} {/* Content - BETTER READABILITY */}
<div className="flex-1 min-w-0"> <div className="flex-1 min-w-0">
<div className="flex items-center justify-between gap-2"> <div className="flex items-center justify-between gap-4 mb-2">
<span className="text-[11px] font-medium text-white/70"> <span className="text-base font-medium text-white">
{item.label} {item.label}
</span> </span>
<span className="text-[10px] font-mono text-white/30"> <span className="text-xs font-mono text-white/40 uppercase tracking-wider">
{item.source} {item.source}
</span> </span>
</div> </div>
{/* Value */} {/* Value - LARGER TEXT */}
<div className="mt-1"> <div>
{isMatrix(item) ? ( {isMatrix(item) ? (
<div className="grid grid-cols-3 gap-1"> <div className="grid grid-cols-4 gap-2">
{(item.value as any[]).slice(0, 12).map((row: any) => ( {(item.value as any[]).slice(0, 12).map((row: any) => (
<div <div
key={String(row.domain)} key={String(row.domain)}
className={clsx( className={clsx(
"px-2 py-1 text-[10px] font-mono flex items-center justify-between border", "px-3 py-2 text-sm font-mono flex items-center justify-between border",
row.status === 'available' row.status === 'available'
? "border-accent/20 bg-accent/5 text-accent" ? "border-accent/30 bg-accent/10 text-accent"
: "border-white/5 bg-white/[0.02] text-white/40" : "border-white/10 bg-white/[0.03] text-white/50"
)} )}
> >
<span className="truncate">{String(row.domain)}</span> <span className="truncate">{String(row.domain)}</span>
{row.status === 'available' && <Check className="w-2.5 h-2.5 shrink-0" />} {row.status === 'available' && <Check className="w-4 h-4 shrink-0 ml-2" />}
</div> </div>
))} ))}
</div> </div>
) : ( ) : (
<div className={clsx( <div className={clsx(
"text-xs font-mono", "text-base font-mono",
item.status === 'pass' ? "text-white/60" : item.status === 'pass' ? "text-white/80" :
item.status === 'warn' ? "text-amber-300/80" : item.status === 'warn' ? "text-amber-300" :
item.status === 'fail' ? "text-red-300/80" : "text-white/40" item.status === 'fail' ? "text-red-300" : "text-white/50"
)}> )}>
{formatValue(item.value)} {formatValue(item.value)}
</div> </div>
@ -434,11 +440,11 @@ export function AnalyzePanel() {
{/* Details Toggle */} {/* Details Toggle */}
{item.details && Object.keys(item.details).length > 0 && ( {item.details && Object.keys(item.details).length > 0 && (
<details className="mt-2"> <details className="mt-3">
<summary className="text-[10px] font-mono text-white/25 cursor-pointer hover:text-white/40 select-none"> <summary className="text-sm font-mono text-white/40 cursor-pointer hover:text-white/60 select-none">
View details View raw details
</summary> </summary>
<pre className="mt-1.5 text-[9px] font-mono text-white/30 bg-black/40 border border-white/5 p-2 overflow-x-auto rounded"> <pre className="mt-2 text-xs font-mono text-white/50 bg-black/50 border border-white/10 p-4 overflow-x-auto">
{JSON.stringify(item.details, null, 2)} {JSON.stringify(item.details, null, 2)}
</pre> </pre>
</details> </details>