Hunt: make Search default tab, reorder tabs
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
- Search is now first/default tab - Tab order: Search → Drops → Auctions → Trends → Forge - AnalyzePanel: improved tooltips and explanations - Better Fast/Cached mode descriptions - Enhanced Health Score display with explanations
This commit is contained in:
@ -46,10 +46,10 @@ type HuntTab = 'auctions' | 'drops' | 'search' | 'trends' | 'forge'
|
|||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
const TABS: Array<{ key: HuntTab; label: string; shortLabel: string; icon: any; color: string }> = [
|
const TABS: Array<{ key: HuntTab; label: string; shortLabel: string; icon: any; color: string }> = [
|
||||||
{ key: 'auctions', label: 'Auctions', shortLabel: 'Auctions', icon: Gavel, color: 'accent' },
|
{ key: 'search', label: 'Search', shortLabel: 'Search', icon: Search, color: 'accent' },
|
||||||
{ key: 'drops', label: 'Drops', shortLabel: 'Drops', icon: Download, color: 'blue' },
|
{ key: 'drops', label: 'Drops', shortLabel: 'Drops', icon: Download, color: 'blue' },
|
||||||
{ key: 'search', label: 'Search', shortLabel: 'Search', icon: Search, color: 'white' },
|
{ key: 'auctions', label: 'Auctions', shortLabel: 'Auctions', icon: Gavel, color: 'orange' },
|
||||||
{ key: 'trends', label: 'Trends', shortLabel: 'Trends', icon: Flame, color: 'orange' },
|
{ key: 'trends', label: 'Trends', shortLabel: 'Trends', icon: Flame, color: 'rose' },
|
||||||
{ key: 'forge', label: 'Forge', shortLabel: 'Forge', icon: Wand2, color: 'purple' },
|
{ key: 'forge', label: 'Forge', shortLabel: 'Forge', icon: Wand2, color: 'purple' },
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -60,7 +60,7 @@ const TABS: Array<{ key: HuntTab; label: string; shortLabel: string; icon: any;
|
|||||||
export default function HuntPage() {
|
export default function HuntPage() {
|
||||||
const { user, subscription, logout, checkAuth } = useStore()
|
const { user, subscription, logout, checkAuth } = useStore()
|
||||||
const { toast, showToast, hideToast } = useToast()
|
const { toast, showToast, hideToast } = useToast()
|
||||||
const [tab, setTab] = useState<HuntTab>('auctions')
|
const [tab, setTab] = useState<HuntTab>('search')
|
||||||
|
|
||||||
// Mobile Menu State
|
// Mobile Menu State
|
||||||
const [menuOpen, setMenuOpen] = useState(false)
|
const [menuOpen, setMenuOpen] = useState(false)
|
||||||
@ -143,13 +143,15 @@ export default function HuntPage() {
|
|||||||
onClick={() => setTab(t.key)}
|
onClick={() => setTab(t.key)}
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'flex items-center gap-1.5 px-3 py-2 border transition-all shrink-0',
|
'flex items-center gap-1.5 px-3 py-2 border transition-all shrink-0',
|
||||||
isActive
|
isActive
|
||||||
? t.color === 'accent'
|
? t.color === 'accent'
|
||||||
? 'border-accent/40 bg-accent/10 text-accent'
|
? 'border-accent/40 bg-accent/10 text-accent'
|
||||||
: t.color === 'blue'
|
: t.color === 'blue'
|
||||||
? 'border-blue-500/40 bg-blue-500/10 text-blue-400'
|
? 'border-blue-500/40 bg-blue-500/10 text-blue-400'
|
||||||
: t.color === 'orange'
|
: t.color === 'orange'
|
||||||
? 'border-orange-500/40 bg-orange-500/10 text-orange-400'
|
? 'border-orange-500/40 bg-orange-500/10 text-orange-400'
|
||||||
|
: t.color === 'rose'
|
||||||
|
? 'border-rose-500/40 bg-rose-500/10 text-rose-400'
|
||||||
: t.color === 'purple'
|
: t.color === 'purple'
|
||||||
? 'border-purple-500/40 bg-purple-500/10 text-purple-400'
|
? 'border-purple-500/40 bg-purple-500/10 text-purple-400'
|
||||||
: 'border-white/40 bg-white/10 text-white'
|
: 'border-white/40 bg-white/10 text-white'
|
||||||
@ -192,6 +194,7 @@ export default function HuntPage() {
|
|||||||
blue: { active: 'border-blue-500 bg-blue-500/10 text-blue-400', inactive: 'border-white/[0.08] text-white/50 hover:text-white hover:bg-white/[0.02]' },
|
blue: { active: 'border-blue-500 bg-blue-500/10 text-blue-400', inactive: 'border-white/[0.08] text-white/50 hover:text-white hover:bg-white/[0.02]' },
|
||||||
white: { active: 'border-white/40 bg-white/10 text-white', inactive: 'border-white/[0.08] text-white/50 hover:text-white hover:bg-white/[0.02]' },
|
white: { active: 'border-white/40 bg-white/10 text-white', inactive: 'border-white/[0.08] text-white/50 hover:text-white hover:bg-white/[0.02]' },
|
||||||
orange: { active: 'border-orange-500 bg-orange-500/10 text-orange-400', inactive: 'border-white/[0.08] text-white/50 hover:text-white hover:bg-white/[0.02]' },
|
orange: { active: 'border-orange-500 bg-orange-500/10 text-orange-400', inactive: 'border-white/[0.08] text-white/50 hover:text-white hover:bg-white/[0.02]' },
|
||||||
|
rose: { active: 'border-rose-500 bg-rose-500/10 text-rose-400', inactive: 'border-white/[0.08] text-white/50 hover:text-white hover:bg-white/[0.02]' },
|
||||||
purple: { active: 'border-purple-500 bg-purple-500/10 text-purple-400', inactive: 'border-white/[0.08] text-white/50 hover:text-white hover:bg-white/[0.02]' },
|
purple: { active: 'border-purple-500 bg-purple-500/10 text-purple-400', inactive: 'border-white/[0.08] text-white/50 hover:text-white hover:bg-white/[0.02]' },
|
||||||
}
|
}
|
||||||
const classes = colorClasses[t.color] || colorClasses.white
|
const classes = colorClasses[t.color] || colorClasses.white
|
||||||
|
|||||||
@ -56,7 +56,8 @@ function getSectionConfig(key: string) {
|
|||||||
color: 'text-blue-400',
|
color: 'text-blue-400',
|
||||||
bg: 'bg-blue-500/10',
|
bg: 'bg-blue-500/10',
|
||||||
border: 'border-blue-500/20',
|
border: 'border-blue-500/20',
|
||||||
description: 'Age, backlinks, and trust signals'
|
description: 'Domain age, backlink profile, and trust signals',
|
||||||
|
tooltip: 'Authority measures how established and trusted the domain is. Older domains with quality backlinks rank better.'
|
||||||
}
|
}
|
||||||
case 'market':
|
case 'market':
|
||||||
return {
|
return {
|
||||||
@ -64,7 +65,8 @@ function getSectionConfig(key: string) {
|
|||||||
color: 'text-emerald-400',
|
color: 'text-emerald-400',
|
||||||
bg: 'bg-emerald-500/10',
|
bg: 'bg-emerald-500/10',
|
||||||
border: 'border-emerald-500/20',
|
border: 'border-emerald-500/20',
|
||||||
description: 'Search volume, CPC, and TLD availability'
|
description: 'Search demand, ad value, and TLD availability',
|
||||||
|
tooltip: 'Market data shows commercial potential. High search volume + CPC = strong buyer intent.'
|
||||||
}
|
}
|
||||||
case 'risk':
|
case 'risk':
|
||||||
return {
|
return {
|
||||||
@ -72,7 +74,8 @@ function getSectionConfig(key: string) {
|
|||||||
color: 'text-amber-400',
|
color: 'text-amber-400',
|
||||||
bg: 'bg-amber-500/10',
|
bg: 'bg-amber-500/10',
|
||||||
border: 'border-amber-500/20',
|
border: 'border-amber-500/20',
|
||||||
description: 'Trademark, blacklist, and archive checks'
|
description: 'Trademark conflicts, blacklists, and history',
|
||||||
|
tooltip: 'Risk checks help avoid legal issues and spam penalties. Always clear before buying.'
|
||||||
}
|
}
|
||||||
case 'value':
|
case 'value':
|
||||||
return {
|
return {
|
||||||
@ -80,7 +83,8 @@ function getSectionConfig(key: string) {
|
|||||||
color: 'text-violet-400',
|
color: 'text-violet-400',
|
||||||
bg: 'bg-violet-500/10',
|
bg: 'bg-violet-500/10',
|
||||||
border: 'border-violet-500/20',
|
border: 'border-violet-500/20',
|
||||||
description: 'Estimated value and comparable sales'
|
description: 'Estimated worth and recent comparable sales',
|
||||||
|
tooltip: 'Value estimation based on length, keywords, extension, and actual market sales.'
|
||||||
}
|
}
|
||||||
case 'vision':
|
case 'vision':
|
||||||
return {
|
return {
|
||||||
@ -88,7 +92,8 @@ function getSectionConfig(key: string) {
|
|||||||
color: 'text-accent',
|
color: 'text-accent',
|
||||||
bg: 'bg-accent/10',
|
bg: 'bg-accent/10',
|
||||||
border: 'border-accent/20',
|
border: 'border-accent/20',
|
||||||
description: 'AI-powered business concept and buyer analysis'
|
description: 'AI business concepts and ideal buyer profiles',
|
||||||
|
tooltip: 'AI-powered analysis suggesting business uses and potential buyers for this domain.'
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return {
|
return {
|
||||||
@ -96,7 +101,8 @@ function getSectionConfig(key: string) {
|
|||||||
color: 'text-white/50',
|
color: 'text-white/50',
|
||||||
bg: 'bg-white/5',
|
bg: 'bg-white/5',
|
||||||
border: 'border-white/10',
|
border: 'border-white/10',
|
||||||
description: ''
|
description: '',
|
||||||
|
tooltip: ''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -125,18 +131,40 @@ function isMatrix(item: AnalyzeItem) {
|
|||||||
|
|
||||||
function getItemTooltip(key: string): string {
|
function getItemTooltip(key: string): string {
|
||||||
const tooltips: Record<string, string> = {
|
const tooltips: Record<string, string> = {
|
||||||
domain_age: 'How long the domain has been registered. Older = more authority.',
|
// Authority
|
||||||
backlinks: 'Number of external websites linking to this domain.',
|
domain_age: 'Registration age of the domain. Older domains typically have more authority and trust in search engines. 5+ years is excellent.',
|
||||||
trust_flow: 'Quality score of backlinks (0-100). Higher = more trusted.',
|
age: 'Registration age of the domain. Older domains typically have more authority and trust in search engines. 5+ years is excellent.',
|
||||||
radio_test: 'How easy is the domain to spell when heard verbally.',
|
backlinks: 'Number of external websites linking to this domain. More backlinks = higher authority. Quality matters more than quantity.',
|
||||||
search_volume: 'Monthly Google searches for the main keyword.',
|
trust_flow: 'Majestic Trust Flow score (0-100). Measures the quality of backlinks. Higher = more trusted by search engines.',
|
||||||
cpc: 'Cost-per-click for ads on this keyword. Higher = more commercial value.',
|
citation_flow: 'Majestic Citation Flow score (0-100). Measures the quantity of backlinks regardless of quality.',
|
||||||
tld_matrix: 'Availability of this name across popular TLDs.',
|
radio_test: 'Pronounceability test. Can someone spell the domain correctly after hearing it once? Important for word-of-mouth.',
|
||||||
trademark: 'Potential trademark conflicts. Clear = safe to use.',
|
syllables: 'Number of syllables. Fewer is better - 2-3 syllables is ideal for brandability.',
|
||||||
blacklist: 'Whether the domain appears on spam/malware lists.',
|
|
||||||
archive: 'Wayback Machine history. Shows previous usage.',
|
// Market
|
||||||
estimated_value: 'AI-estimated market value based on comparables.',
|
search_volume: 'Monthly Google searches for the main keyword. Higher = more organic traffic potential.',
|
||||||
comps: 'Similar domains that have sold recently.',
|
cpc: 'Google Ads Cost-Per-Click. Higher CPC = more commercial intent. $5+ indicates strong buyer intent.',
|
||||||
|
tld_matrix: 'Availability across popular TLDs (.com, .net, .org etc). Green = available for registration.',
|
||||||
|
competition: 'SEO competition level. Lower = easier to rank. "Low" is ideal for new sites.',
|
||||||
|
|
||||||
|
// Risk
|
||||||
|
trademark: 'USPTO trademark database check. "Clear" means no conflicts found. Always verify before buying.',
|
||||||
|
blacklist: 'Spam and malware blacklist check. "Clean" means domain is not flagged by security services.',
|
||||||
|
archive: 'Wayback Machine first capture date. Shows domain history and previous content.',
|
||||||
|
spam_score: 'Moz Spam Score (0-100). Lower = cleaner history. Above 30% is concerning.',
|
||||||
|
|
||||||
|
// Value
|
||||||
|
estimated_value: 'AI-estimated market value based on comparable sales, length, keywords, and extension.',
|
||||||
|
comps: 'Recently sold domains with similar characteristics. Used to determine market value.',
|
||||||
|
price_range: 'Suggested listing price range based on market analysis.',
|
||||||
|
|
||||||
|
// DNS
|
||||||
|
dns_records: 'Active DNS records. Shows if domain is currently configured.',
|
||||||
|
nameservers: 'Current nameservers. Indicates where domain is hosted.',
|
||||||
|
mx_records: 'Mail exchange records. Shows if email is configured.',
|
||||||
|
|
||||||
|
// General
|
||||||
|
length: 'Character count. Shorter is generally more valuable. Under 8 characters is premium.',
|
||||||
|
extension: 'Top-level domain (.com, .io, etc). .com is most valuable, followed by ccTLDs and new gTLDs.',
|
||||||
}
|
}
|
||||||
return tooltips[key] || ''
|
return tooltips[key] || ''
|
||||||
}
|
}
|
||||||
@ -329,66 +357,95 @@ export function AnalyzePanel() {
|
|||||||
{/* Score Bar */}
|
{/* Score Bar */}
|
||||||
{overallScore && !loading && (
|
{overallScore && !loading && (
|
||||||
<div className="px-4 pb-4">
|
<div className="px-4 pb-4">
|
||||||
<div className="flex items-center gap-4 p-3 bg-white/[0.02] border border-white/[0.08]">
|
<div className="p-4 bg-white/[0.02] border border-white/[0.08]">
|
||||||
<div className={clsx(
|
<div className="flex items-center gap-4">
|
||||||
"text-3xl font-bold font-mono",
|
<div
|
||||||
|
className={clsx(
|
||||||
|
"w-16 h-16 flex items-center justify-center border-2",
|
||||||
|
overallScore.score >= 70 ? "border-accent bg-accent/10" :
|
||||||
|
overallScore.score >= 40 ? "border-amber-400 bg-amber-400/10" : "border-rose-500 bg-rose-500/10"
|
||||||
|
)}
|
||||||
|
title={`Health Score: ${overallScore.score}/100. ${overallScore.score >= 70 ? 'Excellent - safe to buy' : overallScore.score >= 40 ? 'Moderate - review warnings' : 'Poor - significant issues'}`}
|
||||||
|
>
|
||||||
|
<span className={clsx(
|
||||||
|
"text-2xl font-bold font-mono",
|
||||||
overallScore.score >= 70 ? "text-accent" : overallScore.score >= 40 ? "text-amber-400" : "text-rose-400"
|
overallScore.score >= 70 ? "text-accent" : overallScore.score >= 40 ? "text-amber-400" : "text-rose-400"
|
||||||
)}>
|
)}>
|
||||||
{overallScore.score}
|
{overallScore.score}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<div className="text-[10px] font-mono text-white/40 uppercase tracking-wider mb-1.5">Health Score</div>
|
<div className="flex items-center justify-between mb-2">
|
||||||
<div className="h-2 bg-white/[0.05] overflow-hidden flex">
|
<div className="text-xs font-bold text-white uppercase tracking-wider">Health Score</div>
|
||||||
|
<div className="text-[10px] font-mono text-white/40">
|
||||||
|
{overallScore.score >= 70 ? '✓ Good to buy' : overallScore.score >= 40 ? '⚠ Review issues' : '⛔ High risk'}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="h-2.5 bg-white/[0.05] overflow-hidden flex mb-2">
|
||||||
<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}%` }}
|
||||||
|
title={`${overallScore.pass} passed checks`}
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
className="h-full bg-amber-400 transition-all"
|
className="h-full bg-amber-400 transition-all"
|
||||||
style={{ width: `${(overallScore.warn / overallScore.total) * 100}%` }}
|
style={{ width: `${(overallScore.warn / overallScore.total) * 100}%` }}
|
||||||
|
title={`${overallScore.warn} warnings`}
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
className="h-full bg-rose-500 transition-all"
|
className="h-full bg-rose-500 transition-all"
|
||||||
style={{ width: `${(overallScore.fail / overallScore.total) * 100}%` }}
|
style={{ width: `${(overallScore.fail / overallScore.total) * 100}%` }}
|
||||||
|
title={`${overallScore.fail} failed checks`}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="flex items-center gap-4 text-xs font-mono">
|
||||||
|
<span className="text-accent flex items-center gap-1.5" title="Checks passed - no issues found">
|
||||||
|
<CheckCircle2 className="w-3.5 h-3.5" /> {overallScore.pass} passed
|
||||||
|
</span>
|
||||||
|
<span className="text-amber-400 flex items-center gap-1.5" title="Warnings - review before buying">
|
||||||
|
<AlertTriangle className="w-3.5 h-3.5" /> {overallScore.warn} warnings
|
||||||
|
</span>
|
||||||
|
<span className="text-rose-400 flex items-center gap-1.5" title="Failed checks - potential problems">
|
||||||
|
<XCircle className="w-3.5 h-3.5" /> {overallScore.fail} failed
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-3 text-xs font-mono shrink-0">
|
|
||||||
<span className="text-accent flex items-center gap-1" title="Passed checks">
|
|
||||||
<CheckCircle2 className="w-3.5 h-3.5" /> {overallScore.pass}
|
|
||||||
</span>
|
|
||||||
<span className="text-amber-400 flex items-center gap-1" title="Warnings">
|
|
||||||
<AlertTriangle className="w-3.5 h-3.5" /> {overallScore.warn}
|
|
||||||
</span>
|
|
||||||
<span className="text-rose-400 flex items-center gap-1" title="Failed checks">
|
|
||||||
<XCircle className="w-3.5 h-3.5" /> {overallScore.fail}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Controls */}
|
{/* Controls with Explanations */}
|
||||||
<div className="px-4 pb-3 flex items-center gap-2">
|
<div className="px-4 pb-3">
|
||||||
|
<div className="flex items-center gap-2 flex-wrap">
|
||||||
<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-1.5 px-3 py-1.5 text-[10px] font-bold uppercase tracking-wider border transition-all group relative",
|
||||||
fastMode
|
fastMode
|
||||||
? "border-accent/30 bg-accent/10 text-accent"
|
? "border-accent/30 bg-accent/10 text-accent"
|
||||||
: "border-white/[0.08] text-white/40 hover:text-white hover:bg-white/[0.05]"
|
: "border-white/[0.08] text-white/40 hover:text-white hover:bg-white/[0.05]"
|
||||||
)}
|
)}
|
||||||
title="Fast mode uses DNS-only checks for speed"
|
title="Fast Mode: DNS-only checks (~2 sec). Skips WHOIS/RDAP for speed. Fewer details but instant results."
|
||||||
>
|
>
|
||||||
<Zap className="w-3.5 h-3.5" />
|
<Zap className="w-3.5 h-3.5" />
|
||||||
Fast
|
Fast Mode
|
||||||
</button>
|
</button>
|
||||||
{data?.cached && (
|
{data?.cached && (
|
||||||
<span className="text-[10px] font-mono text-white/30 px-2 py-1.5 border border-white/[0.08] bg-white/[0.02] flex items-center gap-1.5">
|
<span
|
||||||
|
className="text-[10px] font-mono text-white/30 px-2 py-1.5 border border-white/[0.08] bg-white/[0.02] flex items-center gap-1.5 cursor-help"
|
||||||
|
title="Cached Result: Data from previous analysis (saves time). Click refresh ↻ for live data."
|
||||||
|
>
|
||||||
<Clock className="w-3 h-3" />
|
<Clock className="w-3 h-3" />
|
||||||
Cached
|
Cached Result
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
{fastMode && (
|
||||||
|
<span className="text-[10px] font-mono text-white/30 italic">
|
||||||
|
DNS-only, limited details
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -431,29 +488,37 @@ export function AnalyzePanel() {
|
|||||||
<button
|
<button
|
||||||
onClick={() => toggleSection(section.key)}
|
onClick={() => toggleSection(section.key)}
|
||||||
className={clsx(
|
className={clsx(
|
||||||
"w-full px-4 py-3 flex items-center justify-between transition-colors",
|
"w-full px-4 py-3 flex items-center justify-between transition-colors group",
|
||||||
config.bg, "hover:brightness-110"
|
config.bg, "hover:brightness-110"
|
||||||
)}
|
)}
|
||||||
|
title={(config as any).tooltip || ''}
|
||||||
>
|
>
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
|
<div className={clsx("w-8 h-8 flex items-center justify-center border", config.border)}>
|
||||||
<SectionIcon className={clsx("w-4 h-4", config.color)} />
|
<SectionIcon className={clsx("w-4 h-4", config.color)} />
|
||||||
|
</div>
|
||||||
<div className="text-left">
|
<div className="text-left">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
<span className={clsx("text-xs font-bold uppercase tracking-wider", config.color)}>
|
<span className={clsx("text-xs font-bold uppercase tracking-wider", config.color)}>
|
||||||
{section.title}
|
{section.title}
|
||||||
</span>
|
</span>
|
||||||
<div className="text-[10px] font-mono text-white/30 mt-0.5">
|
<span title={(config as any).tooltip || ''}>
|
||||||
|
<Info className="w-3 h-3 text-white/20 opacity-0 group-hover:opacity-100 transition-opacity" />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="text-[10px] font-mono text-white/40 mt-0.5">
|
||||||
{config.description}
|
{config.description}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
{section.key !== 'vision' && (
|
{section.key !== 'vision' && section.items.length > 0 && (
|
||||||
<span className="text-[10px] font-mono text-white/30">
|
<span className="text-[10px] font-mono text-white/40 px-2 py-0.5 bg-white/5 border border-white/[0.06]">
|
||||||
{section.items.length}
|
{section.items.length} checks
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
{section.key === 'vision' && (
|
{section.key === 'vision' && (
|
||||||
<span className="text-[10px] font-mono text-accent px-1.5 py-0.5 bg-accent/10">AI</span>
|
<span className="text-[10px] font-mono text-accent px-2 py-0.5 bg-accent/10 border border-accent/20">AI Powered</span>
|
||||||
)}
|
)}
|
||||||
<ChevronRight className={clsx(
|
<ChevronRight className={clsx(
|
||||||
"w-4 h-4 text-white/30 transition-transform",
|
"w-4 h-4 text-white/30 transition-transform",
|
||||||
@ -479,12 +544,13 @@ export function AnalyzePanel() {
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={item.key}
|
key={item.key}
|
||||||
className="px-4 py-3 hover:bg-white/[0.02] transition-colors group"
|
className="px-4 py-3.5 hover:bg-white/[0.02] transition-colors group"
|
||||||
|
title={tooltip || undefined}
|
||||||
>
|
>
|
||||||
<div className="flex items-start gap-3">
|
<div className="flex items-start gap-3">
|
||||||
{/* Status Indicator */}
|
{/* Status Indicator */}
|
||||||
<div className={clsx(
|
<div className={clsx(
|
||||||
"w-8 h-8 flex items-center justify-center shrink-0 border",
|
"w-9 h-9 flex items-center justify-center shrink-0 border",
|
||||||
statusStyle.bg, statusStyle.border
|
statusStyle.bg, statusStyle.border
|
||||||
)}>
|
)}>
|
||||||
{StatusIcon && <StatusIcon className={clsx("w-4 h-4", statusStyle.text)} />}
|
{StatusIcon && <StatusIcon className={clsx("w-4 h-4", statusStyle.text)} />}
|
||||||
@ -492,20 +558,25 @@ export function AnalyzePanel() {
|
|||||||
|
|
||||||
{/* Content */}
|
{/* Content */}
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<div className="flex items-center justify-between gap-2 mb-1">
|
<div className="flex items-center justify-between gap-2 mb-1.5">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<span className="text-sm font-medium text-white">
|
<span className="text-sm font-semibold text-white">
|
||||||
{item.label}
|
{item.label}
|
||||||
</span>
|
</span>
|
||||||
{tooltip && (
|
{tooltip && (
|
||||||
<span title={tooltip}>
|
<span
|
||||||
<Info className="w-3.5 h-3.5 text-white/20 opacity-0 group-hover:opacity-100 transition-opacity cursor-help" />
|
title={tooltip}
|
||||||
|
className="cursor-help"
|
||||||
|
>
|
||||||
|
<Info className="w-3.5 h-3.5 text-white/20 group-hover:text-white/40 transition-colors" />
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<span className="text-[9px] font-mono text-white/30 uppercase">
|
{item.source && (
|
||||||
|
<span className="text-[9px] font-mono text-white/30 uppercase px-1.5 py-0.5 bg-white/[0.03] border border-white/[0.06]">
|
||||||
{item.source}
|
{item.source}
|
||||||
</span>
|
</span>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Value */}
|
{/* Value */}
|
||||||
@ -521,7 +592,7 @@ export function AnalyzePanel() {
|
|||||||
? "border-accent/20 bg-accent/5 text-accent"
|
? "border-accent/20 bg-accent/5 text-accent"
|
||||||
: "border-white/[0.06] bg-white/[0.02] text-white/40"
|
: "border-white/[0.06] bg-white/[0.02] text-white/40"
|
||||||
)}
|
)}
|
||||||
title={row.status === 'available' ? 'Available' : 'Taken'}
|
title={row.status === 'available' ? '✓ Available for registration' : '✗ Already registered'}
|
||||||
>
|
>
|
||||||
<span className="truncate">.{String(row.domain).split('.').pop()}</span>
|
<span className="truncate">.{String(row.domain).split('.').pop()}</span>
|
||||||
{row.status === 'available' && <Check className="w-3 h-3 shrink-0 ml-1" />}
|
{row.status === 'available' && <Check className="w-3 h-3 shrink-0 ml-1" />}
|
||||||
@ -530,21 +601,30 @@ export function AnalyzePanel() {
|
|||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className={clsx(
|
<div className={clsx(
|
||||||
"text-sm font-mono",
|
"text-base font-mono font-medium",
|
||||||
item.status === 'pass' ? "text-white/70" :
|
item.status === 'pass' ? "text-accent" :
|
||||||
item.status === 'warn' ? "text-amber-300" :
|
item.status === 'warn' ? "text-amber-400" :
|
||||||
item.status === 'fail' ? "text-rose-300" : "text-white/40"
|
item.status === 'fail' ? "text-rose-400" : "text-white/60"
|
||||||
)}>
|
)}>
|
||||||
{formatValue(item.value)}
|
{formatValue(item.value)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Inline Explanation for important items */}
|
||||||
|
{tooltip && item.status !== 'pass' && (
|
||||||
|
<div className="mt-1.5 text-[10px] font-mono text-white/30 leading-relaxed">
|
||||||
|
{item.status === 'warn' && '⚠️ '}
|
||||||
|
{item.status === 'fail' && '⛔ '}
|
||||||
|
{tooltip.split('.')[0]}.
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* 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-2">
|
||||||
<summary className="text-[10px] font-mono text-white/30 cursor-pointer hover:text-white/50 select-none">
|
<summary className="text-[10px] font-mono text-white/30 cursor-pointer hover:text-white/50 select-none">
|
||||||
Raw data
|
Show raw data
|
||||||
</summary>
|
</summary>
|
||||||
<pre className="mt-2 text-[10px] font-mono text-white/40 bg-black/50 border border-white/[0.06] p-3 overflow-x-auto max-h-32">
|
<pre className="mt-2 text-[10px] font-mono text-white/40 bg-black/50 border border-white/[0.06] p-3 overflow-x-auto max-h-32">
|
||||||
{JSON.stringify(item.details, null, 2)}
|
{JSON.stringify(item.details, null, 2)}
|
||||||
|
|||||||
Reference in New Issue
Block a user