diff --git a/frontend/src/app/auctions/page.tsx b/frontend/src/app/auctions/page.tsx index cd4ed56..68e82ba 100644 --- a/frontend/src/app/auctions/page.tsx +++ b/frontend/src/app/auctions/page.tsx @@ -193,10 +193,18 @@ export default function AuctionsPage() { } } + // Dynamic subtitle + const getSubtitle = () => { + if (loading) return 'Loading live auctions...' + const total = allAuctions.length + if (total === 0) return 'No active auctions found' + return `${total.toLocaleString()} live auctions across 4 platforms` + } + return ( { + const hour = new Date().getHours() + if (hour < 12) return 'Good morning' + if (hour < 18) return 'Good afternoon' + return 'Good evening' + } + + // Dynamic subtitle + const getSubtitle = () => { + if (availableDomains.length > 0) { + return `🎯 ${availableDomains.length} domain${availableDomains.length !== 1 ? 's' : ''} ready to pounce!` + } + if (totalDomains > 0) { + return `Monitoring ${totalDomains} domain${totalDomains !== 1 ? 's' : ''} for you` + } + return 'Start tracking domains to find opportunities' + } + return ( {toast && } diff --git a/frontend/src/app/intelligence/page.tsx b/frontend/src/app/intelligence/page.tsx index d1e88e1..e1201a5 100755 --- a/frontend/src/app/intelligence/page.tsx +++ b/frontend/src/app/intelligence/page.tsx @@ -17,6 +17,7 @@ import { BarChart3, RefreshCw, Bell, + X, } from 'lucide-react' import clsx from 'clsx' import Link from 'next/link' @@ -76,19 +77,28 @@ export default function IntelligencePage() { ) const getTrendIcon = (change: number | undefined) => { - if (!change) return + if (!change || change === 0) return if (change > 0) return return } // Calculate stats - const lowestPrice = tldData.reduce((min, tld) => Math.min(min, tld.min_price), Infinity) - const hottestTld = tldData.find(tld => (tld.price_change_7d || 0) > 0)?.tld || 'N/A' + const lowestPrice = tldData.length > 0 + ? tldData.reduce((min, tld) => Math.min(min, tld.min_price), Infinity) + : 0.99 + const hottestTld = tldData.find(tld => (tld.price_change_7d || 0) > 0)?.tld || 'com' + + // Dynamic subtitle + const getSubtitle = () => { + if (loading && total === 0) return 'Loading TLD pricing data...' + if (total === 0) return 'No TLD data available' + return `Comparing prices across ${total.toLocaleString()} TLDs` + } return ( {/* Stats Overview */}
- - - - + 0 ? total.toLocaleString() : '—'} + subtitle="updated daily" + icon={Globe} + accent + /> + 0 ? `$${lowestPrice.toFixed(2)}` : '—'} + icon={DollarSign} + /> + 0 ? `.${hottestTld}` : '—'} + subtitle="rising prices" + icon={TrendingUp} + /> +
{/* Filters */} @@ -118,11 +148,19 @@ export default function IntelligencePage() { type="text" value={searchQuery} onChange={(e) => setSearchQuery(e.target.value)} - placeholder="Search TLDs..." - className="w-full h-11 pl-11 pr-4 bg-background-secondary/50 border border-border/50 rounded-xl + placeholder="Search TLDs (e.g. com, io, dev)..." + className="w-full h-11 pl-11 pr-10 bg-background-secondary/50 border border-border/50 rounded-xl text-sm text-foreground placeholder:text-foreground-subtle focus:outline-none focus:border-accent/50 transition-all" /> + {searchQuery && ( + + )}
@@ -154,8 +192,9 @@ export default function IntelligencePage() { { key: 'tld', header: 'TLD', + width: '120px', render: (tld) => ( - + .{tld.tld} ), @@ -163,26 +202,32 @@ export default function IntelligencePage() { { key: 'min_price', header: 'Min Price', + align: 'right', + width: '100px', render: (tld) => ( - ${tld.min_price.toFixed(2)} + ${tld.min_price.toFixed(2)} ), }, { key: 'avg_price', header: 'Avg Price', + align: 'right', + width: '100px', hideOnMobile: true, render: (tld) => ( - ${tld.avg_price.toFixed(2)} + ${tld.avg_price.toFixed(2)} ), }, { key: 'change', header: '7d Change', + align: 'right', + width: '120px', render: (tld) => ( -
+
{getTrendIcon(tld.price_change_7d)} 0 ? "text-orange-400" : (tld.price_change_7d || 0) < 0 ? "text-accent" : "text-foreground-muted" )}> @@ -196,19 +241,21 @@ export default function IntelligencePage() { header: 'Cheapest At', hideOnMobile: true, render: (tld) => ( - {tld.cheapest_registrar} + {tld.cheapest_registrar} ), }, { key: 'actions', header: '', align: 'right', + width: '80px', render: (tld) => ( -
+
e.stopPropagation()} + title="Set price alert" > @@ -221,22 +268,24 @@ export default function IntelligencePage() { {/* Pagination */} {total > 50 && ( -
+
- + Page {page + 1} of {Math.ceil(total / 50)} @@ -263,20 +328,35 @@ export default function WatchlistPage() { { key: 'status', header: 'Status', + align: 'left', hideOnMobile: true, - render: (domain) => ( - - {domain.is_available ? 'Ready to register!' : 'Monitoring...'} - - ), + render: (domain) => { + const health = healthReports[domain.id] + if (health) { + const config = healthStatusConfig[health.status] + const Icon = config.icon + return ( +
+ + {config.label} +
+ ) + } + return ( + + {domain.is_available ? 'Ready to pounce!' : 'Monitoring...'} + + ) + }, }, { key: 'notifications', header: 'Alerts', align: 'center', + width: '80px', hideOnMobile: true, render: (domain) => ( +
+ + {/* Score */} +
+
+ Health Score +
+
+
= 70 ? "bg-accent" : + report.score >= 40 ? "bg-amber-400" : "bg-red-400" + )} + style={{ width: `${report.score}%` }} + /> +
+ = 70 ? "text-accent" : + report.score >= 40 ? "text-amber-400" : "text-red-400" + )}> + {report.score}/100 + +
+
+
+ + {/* Check Results */} +
+ {/* DNS */} + {report.dns && ( +
+

+ + DNS Infrastructure +

+
+
+ + {report.dns.has_ns ? '✓' : '✗'} + + Nameservers +
+
+ + {report.dns.has_a ? '✓' : '✗'} + + A Record +
+
+ + {report.dns.has_mx ? '✓' : '—'} + + MX Record +
+
+ {report.dns.is_parked && ( +

âš  Parked at {report.dns.parking_provider || 'unknown provider'}

+ )} +
+ )} + + {/* HTTP */} + {report.http && ( +
+

+ + Website Status +

+
+ + {report.http.is_reachable ? 'Reachable' : 'Unreachable'} + + {report.http.status_code && ( + + HTTP {report.http.status_code} + + )} +
+ {report.http.is_parked && ( +

âš  Parking page detected

+ )} +
+ )} + + {/* SSL */} + {report.ssl && ( +
+

+ + SSL Certificate +

+
+ {report.ssl.has_certificate ? ( +
+

+ {report.ssl.is_valid ? '✓ Valid certificate' : '✗ Certificate invalid/expired'} +

+ {report.ssl.days_until_expiry !== undefined && ( +

30 ? "text-foreground-muted" : + report.ssl.days_until_expiry > 7 ? "text-amber-400" : "text-red-400" + )}> + Expires in {report.ssl.days_until_expiry} days +

+ )} +
+ ) : ( +

No SSL certificate

+ )} +
+
+ )} + + {/* Signals & Recommendations */} + {((report.signals?.length || 0) > 0 || (report.recommendations?.length || 0) > 0) && ( +
+ {(report.signals?.length || 0) > 0 && ( +
+

Signals

+
    + {report.signals?.map((signal, i) => ( +
  • + • + {signal} +
  • + ))} +
+
+ )} + {(report.recommendations?.length || 0) > 0 && ( +
+

Recommendations

+
    + {report.recommendations?.map((rec, i) => ( +
  • + → + {rec} +
  • + ))} +
+
+ )} +
+ )} +
+ + {/* Footer */} +
+

+ Checked at {new Date(report.checked_at).toLocaleString()} +

+
+
+
+ ) +} diff --git a/frontend/src/components/PremiumTable.tsx b/frontend/src/components/PremiumTable.tsx old mode 100644 new mode 100755 index 89941f9..5b3fc02 --- a/frontend/src/components/PremiumTable.tsx +++ b/frontend/src/components/PremiumTable.tsx @@ -94,7 +94,7 @@ export function PremiumTable({ return (
- +
{columns.map((col) => ( @@ -102,19 +102,24 @@ export function PremiumTable({ key={col.key} className={clsx( headerPadding, - "text-[11px] font-semibold text-foreground-subtle/70 uppercase tracking-wider", + "text-[11px] font-semibold text-foreground-subtle/70 uppercase tracking-wider whitespace-nowrap", col.hideOnMobile && "hidden md:table-cell", col.hideOnTablet && "hidden lg:table-cell", col.align === 'right' && "text-right", col.align === 'center' && "text-center", + !col.align && "text-left", col.headerClassName )} - style={col.width ? { width: col.width } : undefined} + style={col.width ? { width: col.width, minWidth: col.width } : undefined} > {col.sortable && onSort ? (