fix: Make Public TLD Pricing table 1:1 identical to Command Center
CHANGES: 1. Sparkline: Now uses exact same SVG polyline implementation - Upward trend: orange polyline - Downward trend: accent polyline - Neutral: horizontal line 2. Risk Badge: Now shows dot + text label (like Command Center) - 'Stable', 'Renewal trap', etc. visible on desktop - Width changed from 80px → 130px 3. Actions column: Width changed from 40px → 80px 4. Pagination: Simplified to match Command Center styling - Removed icons from buttons - Uses tabular-nums for page counter Both tables now render identically with same: - Column widths - Sparkline SVGs - Risk badges with text - Button styling
This commit is contained in:
@ -8,8 +8,6 @@ import { useStore } from '@/lib/store'
|
||||
import { api } from '@/lib/api'
|
||||
import {
|
||||
TrendingUp,
|
||||
TrendingDown,
|
||||
Minus,
|
||||
ChevronRight,
|
||||
ChevronLeft,
|
||||
Search,
|
||||
@ -55,31 +53,37 @@ interface PaginationData {
|
||||
has_more: boolean
|
||||
}
|
||||
|
||||
// Sparkline component
|
||||
// Sparkline component - matching Command Center exactly
|
||||
function Sparkline({ trend }: { trend: number }) {
|
||||
const isPositive = trend > 0
|
||||
const isNegative = trend < 0
|
||||
|
||||
// Generate simple sparkline points
|
||||
const points = Array.from({ length: 8 }, (_, i) => {
|
||||
const baseY = 50
|
||||
const variance = isPositive ? -trend * 3 : isNegative ? -trend * 3 : 5
|
||||
return `${i * 14},${baseY + (Math.random() * variance - variance / 2) * (i / 7)}`
|
||||
}).join(' ')
|
||||
const isNeutral = trend === 0
|
||||
|
||||
return (
|
||||
<div className="w-16 h-8 flex items-center">
|
||||
<svg viewBox="0 0 100 100" className="w-full h-full" preserveAspectRatio="none">
|
||||
<polyline
|
||||
points={points}
|
||||
fill="none"
|
||||
strokeWidth="3"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
className={clsx(
|
||||
isPositive ? "stroke-orange-400" : isNegative ? "stroke-accent" : "stroke-foreground-subtle"
|
||||
)}
|
||||
/>
|
||||
<div className="flex items-center gap-1">
|
||||
<svg width="40" height="16" viewBox="0 0 40 16" className="overflow-visible">
|
||||
{isNeutral ? (
|
||||
<line x1="0" y1="8" x2="40" y2="8" stroke="currentColor" className="text-foreground-muted" strokeWidth="1.5" />
|
||||
) : isPositive ? (
|
||||
<polyline
|
||||
points="0,14 10,12 20,10 30,6 40,2"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
className="text-orange-400"
|
||||
strokeWidth="1.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
) : (
|
||||
<polyline
|
||||
points="0,2 10,6 20,10 30,12 40,14"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
className="text-accent"
|
||||
strokeWidth="1.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
)}
|
||||
</svg>
|
||||
</div>
|
||||
)
|
||||
@ -90,7 +94,7 @@ export default function TldPricingPage() {
|
||||
const [tlds, setTlds] = useState<TldData[]>([])
|
||||
const [trending, setTrending] = useState<TrendingTld[]>([])
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [pagination, setPagination] = useState<PaginationData>({ total: 0, limit: 25, offset: 0, has_more: false })
|
||||
const [pagination, setPagination] = useState<PaginationData>({ total: 0, limit: 50, offset: 0, has_more: false })
|
||||
|
||||
// Search & Sort state
|
||||
const [searchQuery, setSearchQuery] = useState('')
|
||||
@ -150,6 +154,28 @@ export default function TldPricingPage() {
|
||||
}
|
||||
}
|
||||
|
||||
// Risk badge - matching Command Center exactly
|
||||
const getRiskBadge = (tld: TldData) => {
|
||||
const level = tld.risk_level || 'low'
|
||||
const reason = tld.risk_reason || 'Stable'
|
||||
return (
|
||||
<span className={clsx(
|
||||
"inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-xs font-medium",
|
||||
level === 'high' && "bg-red-500/10 text-red-400",
|
||||
level === 'medium' && "bg-amber-500/10 text-amber-400",
|
||||
level === 'low' && "bg-accent/10 text-accent"
|
||||
)}>
|
||||
<span className={clsx(
|
||||
"w-2.5 h-2.5 rounded-full",
|
||||
level === 'high' && "bg-red-400",
|
||||
level === 'medium' && "bg-amber-400",
|
||||
level === 'low' && "bg-accent"
|
||||
)} />
|
||||
<span className="hidden sm:inline ml-1">{reason}</span>
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
// Get renewal trap indicator
|
||||
const getRenewalTrap = (tld: TldData) => {
|
||||
if (!tld.min_renewal_price || !tld.min_registration_price) return null
|
||||
@ -348,7 +374,7 @@ export default function TldPricingPage() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* TLD Table using PremiumTable */}
|
||||
{/* TLD Table using PremiumTable - matching Command Center exactly */}
|
||||
<PremiumTable
|
||||
data={tlds}
|
||||
keyExtractor={(tld) => tld.tld}
|
||||
@ -387,7 +413,7 @@ export default function TldPricingPage() {
|
||||
render: (tld, idx) => {
|
||||
const showData = isAuthenticated || (page === 0 && idx === 0)
|
||||
if (!showData) {
|
||||
return <div className="w-16 h-8 bg-foreground/5 rounded blur-[3px]" />
|
||||
return <div className="w-10 h-4 bg-foreground/5 rounded blur-[3px]" />
|
||||
}
|
||||
return <Sparkline trend={tld.price_change_1y || 0} />
|
||||
},
|
||||
@ -473,30 +499,25 @@ export default function TldPricingPage() {
|
||||
key: 'risk',
|
||||
header: 'Risk',
|
||||
align: 'center',
|
||||
width: '80px',
|
||||
hideOnMobile: true,
|
||||
width: '130px',
|
||||
render: (tld, idx) => {
|
||||
const showData = isAuthenticated || (page === 0 && idx === 0)
|
||||
if (!showData) {
|
||||
return <span className="w-3 h-3 rounded-full bg-foreground-subtle blur-[3px]" />
|
||||
return (
|
||||
<span className="inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-xs font-medium bg-foreground/5 blur-[3px]">
|
||||
<span className="w-2.5 h-2.5 rounded-full bg-foreground-subtle" />
|
||||
<span className="hidden sm:inline ml-1">Hidden</span>
|
||||
</span>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<div className="flex justify-center" title={tld.risk_reason}>
|
||||
<span className={clsx(
|
||||
"w-3 h-3 rounded-full",
|
||||
tld.risk_level === 'high' && "bg-red-400",
|
||||
tld.risk_level === 'medium' && "bg-amber-400",
|
||||
tld.risk_level === 'low' && "bg-accent"
|
||||
)} />
|
||||
</div>
|
||||
)
|
||||
return getRiskBadge(tld)
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'action',
|
||||
key: 'actions',
|
||||
header: '',
|
||||
align: 'right',
|
||||
width: '40px',
|
||||
width: '80px',
|
||||
render: () => (
|
||||
<ChevronRight className="w-5 h-5 text-foreground-subtle group-hover:text-accent transition-colors" />
|
||||
),
|
||||
@ -506,29 +527,27 @@ export default function TldPricingPage() {
|
||||
|
||||
{/* Pagination */}
|
||||
{!loading && pagination.total > pagination.limit && (
|
||||
<div className="flex items-center justify-center gap-4 pt-6">
|
||||
<div className="flex items-center justify-center gap-4 pt-2">
|
||||
<button
|
||||
onClick={() => setPage(Math.max(0, page - 1))}
|
||||
disabled={page === 0}
|
||||
className="flex items-center gap-1 px-4 py-2 text-sm font-medium text-foreground-muted hover:text-foreground
|
||||
className="px-4 py-2 text-sm font-medium text-foreground-muted hover:text-foreground
|
||||
bg-foreground/5 hover:bg-foreground/10 rounded-lg
|
||||
disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
||||
>
|
||||
<ChevronLeft className="w-4 h-4" />
|
||||
Previous
|
||||
</button>
|
||||
<span className="text-sm text-foreground-muted">
|
||||
<span className="text-sm text-foreground-muted tabular-nums">
|
||||
Page {currentPage} of {totalPages}
|
||||
</span>
|
||||
<button
|
||||
onClick={() => setPage(page + 1)}
|
||||
disabled={!pagination.has_more}
|
||||
className="flex items-center gap-1 px-4 py-2 text-sm font-medium text-foreground-muted hover:text-foreground
|
||||
className="px-4 py-2 text-sm font-medium text-foreground-muted hover:text-foreground
|
||||
bg-foreground/5 hover:bg-foreground/10 rounded-lg
|
||||
disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
||||
>
|
||||
Next
|
||||
<ChevronRight className="w-4 h-4" />
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user