fix: Multiple Command Center improvements

LISTINGS PAGE:
- Added missing Sparkles import

PORTFOLIO PAGE:
- Changed dropdown menu to open downward (top-full mt-1)
  instead of upward for better visibility

SEO PAGE:
- Added cleanDomain() helper to sanitize input
- Removes whitespace, protocol, www, and trailing slashes
- Fixes 'hushen. app' -> 'hushen.app' input issue

PRICING PAGE:
- Removed accent highlight from 'TLDs Tracked' StatCard

All StatCards now have consistent styling without
green accent highlights.
This commit is contained in:
yves.gugger
2025-12-10 17:02:01 +01:00
parent 4a0b230e1f
commit 8d5bd95147
4 changed files with 21 additions and 8 deletions

View File

@ -22,6 +22,7 @@ import {
X, X,
Tag, Tag,
Store, Store,
Sparkles,
} from 'lucide-react' } from 'lucide-react'
import Link from 'next/link' import Link from 'next/link'
import clsx from 'clsx' import clsx from 'clsx'

View File

@ -446,8 +446,8 @@ export default function PortfolioPage() {
className="fixed inset-0 z-40" className="fixed inset-0 z-40"
onClick={() => setOpenMenuId(null)} onClick={() => setOpenMenuId(null)}
/> />
{/* Menu - opens upward */} {/* Menu - opens downward */}
<div className="absolute right-0 bottom-full mb-1 z-50 w-48 py-1 bg-background-secondary border border-border/50 rounded-xl shadow-xl"> <div className="absolute right-0 top-full mt-1 z-50 w-48 py-1 bg-background-secondary border border-border/50 rounded-xl shadow-xl">
<button <button
onClick={() => { handleHealthCheck(domain.domain); setOpenMenuId(null) }} onClick={() => { handleHealthCheck(domain.domain); setOpenMenuId(null) }}
className="w-full flex items-center gap-3 px-4 py-2.5 text-sm text-foreground-muted hover:text-foreground hover:bg-foreground/5 transition-colors" className="w-full flex items-center gap-3 px-4 py-2.5 text-sm text-foreground-muted hover:text-foreground hover:bg-foreground/5 transition-colors"

View File

@ -315,7 +315,7 @@ export default function TLDPricingPage() {
<PageContainer> <PageContainer>
{/* Stats Overview */} {/* Stats Overview */}
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4"> <div className="grid grid-cols-2 lg:grid-cols-4 gap-4">
<StatCard title="TLDs Tracked" value={total > 0 ? total.toLocaleString() : '—'} subtitle="updated daily" icon={Globe} accent /> <StatCard title="TLDs Tracked" value={total > 0 ? total.toLocaleString() : '—'} subtitle="updated daily" icon={Globe} />
<StatCard title="Lowest Price" value={total > 0 ? `$${stats.lowestPrice.toFixed(2)}` : '—'} icon={DollarSign} /> <StatCard title="Lowest Price" value={total > 0 ? `$${stats.lowestPrice.toFixed(2)}` : '—'} icon={DollarSign} />
<StatCard title="Hottest TLD" value={total > 0 ? `.${stats.hottestTld}` : '—'} subtitle="rising prices" icon={TrendingUp} /> <StatCard title="Hottest TLD" value={total > 0 ? `.${stats.hottestTld}` : '—'} subtitle="rising prices" icon={TrendingUp} />
<StatCard title="Renewal Traps" value={stats.trapCount.toString()} subtitle="high renewal ratio" icon={AlertTriangle} /> <StatCard title="Renewal Traps" value={stats.trapCount.toString()} subtitle="high renewal ratio" icon={AlertTriangle} />

View File

@ -83,18 +83,29 @@ export default function SEOPage() {
localStorage.setItem('seo-recent-searches', JSON.stringify(updated)) localStorage.setItem('seo-recent-searches', JSON.stringify(updated))
} }
const cleanDomain = (d: string): string => {
// Remove whitespace, protocol, www, and trailing slashes
return d.trim()
.toLowerCase()
.replace(/\s+/g, '')
.replace(/^https?:\/\//, '')
.replace(/^www\./, '')
.replace(/\/.*$/, '')
}
const handleSearch = async (e: React.FormEvent) => { const handleSearch = async (e: React.FormEvent) => {
e.preventDefault() e.preventDefault()
if (!domain.trim()) return const cleanedDomain = cleanDomain(domain)
if (!cleanedDomain) return
setLoading(true) setLoading(true)
setError(null) setError(null)
setSeoData(null) setSeoData(null)
try { try {
const data = await api.request<SEOData>(`/seo/${encodeURIComponent(domain.trim())}`) const data = await api.request<SEOData>(`/seo/${encodeURIComponent(cleanedDomain)}`)
setSeoData(data) setSeoData(data)
saveRecentSearch(domain.trim().toLowerCase()) saveRecentSearch(cleanedDomain)
} catch (err: any) { } catch (err: any) {
setError(err.message || 'Failed to analyze domain') setError(err.message || 'Failed to analyze domain')
} finally { } finally {
@ -103,13 +114,14 @@ export default function SEOPage() {
} }
const handleQuickSearch = async (searchDomain: string) => { const handleQuickSearch = async (searchDomain: string) => {
setDomain(searchDomain) const cleanedDomain = cleanDomain(searchDomain)
setDomain(cleanedDomain)
setLoading(true) setLoading(true)
setError(null) setError(null)
setSeoData(null) setSeoData(null)
try { try {
const data = await api.request<SEOData>(`/seo/${encodeURIComponent(searchDomain)}`) const data = await api.request<SEOData>(`/seo/${encodeURIComponent(cleanedDomain)}`)
setSeoData(data) setSeoData(data)
} catch (err: any) { } catch (err: any) {
setError(err.message || 'Failed to analyze domain') setError(err.message || 'Failed to analyze domain')