Cleaner design: smaller titles, no caps, better search UX
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
This commit is contained in:
@ -59,7 +59,7 @@ function LiveTicker({ items }: { items: { label: string; value: string; highligh
|
|||||||
<div className="flex animate-[ticker_30s_linear_infinite] py-2.5" style={{ width: 'max-content' }}>
|
<div className="flex animate-[ticker_30s_linear_infinite] py-2.5" style={{ width: 'max-content' }}>
|
||||||
{[...items, ...items, ...items].map((item, i) => (
|
{[...items, ...items, ...items].map((item, i) => (
|
||||||
<div key={i} className="flex items-center gap-3 px-6 border-r border-white/[0.08]">
|
<div key={i} className="flex items-center gap-3 px-6 border-r border-white/[0.08]">
|
||||||
<span className="text-[9px] font-mono uppercase tracking-widest text-white/30">{item.label}</span>
|
<span className="text-[10px] font-mono tracking-wide text-white/30">{item.label}</span>
|
||||||
<span className={clsx(
|
<span className={clsx(
|
||||||
"text-xs font-mono font-medium",
|
"text-xs font-mono font-medium",
|
||||||
item.highlight ? "text-accent" : "text-white/70"
|
item.highlight ? "text-accent" : "text-white/70"
|
||||||
@ -181,72 +181,65 @@ export default function RadarPage() {
|
|||||||
<div className="grid lg:grid-cols-2 gap-10 lg:gap-16 items-center">
|
<div className="grid lg:grid-cols-2 gap-10 lg:gap-16 items-center">
|
||||||
|
|
||||||
{/* Left: Typography */}
|
{/* Left: Typography */}
|
||||||
<div className="space-y-6">
|
<div className="space-y-5">
|
||||||
<div className="inline-flex items-center gap-3">
|
<div className="inline-flex items-center gap-3">
|
||||||
<div className="w-1.5 h-1.5 bg-accent rounded-full animate-pulse shadow-[0_0_10px_rgba(16,185,129,0.8)]" />
|
<div className="w-1.5 h-1.5 bg-accent rounded-full animate-pulse shadow-[0_0_10px_rgba(16,185,129,0.8)]" />
|
||||||
<span className="text-[10px] font-mono uppercase tracking-[0.2em] text-accent">
|
<span className="text-[10px] font-mono tracking-[0.15em] text-accent">
|
||||||
Intelligence Hub
|
Intelligence Hub
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h1 className="font-display text-[2.5rem] sm:text-[3rem] lg:text-[3.5rem] leading-[0.95] tracking-[-0.03em]">
|
<h1 className="font-display text-[2rem] sm:text-[2.5rem] lg:text-[2.75rem] leading-[1] tracking-[-0.02em]">
|
||||||
<span className="block text-white">Global Recon.</span>
|
<span className="block text-white">Domain Radar</span>
|
||||||
<span className="block text-white/30">Zero Blind Spots.</span>
|
<span className="block text-white/30">Find your next acquisition.</span>
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<p className="text-sm lg:text-base text-white/50 max-w-md font-light leading-relaxed">
|
<p className="text-sm text-white/50 max-w-md font-light leading-relaxed">
|
||||||
Real-time monitoring across {marketStats.totalAuctions.toLocaleString()}+ auctions.
|
Real-time monitoring across {marketStats.totalAuctions.toLocaleString()}+ auctions.
|
||||||
<span className="text-white/70"> Your targets. Your intel.</span>
|
<span className="text-white/70"> Your targets. Your intel.</span>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
{/* Stats Row */}
|
{/* Stats Row */}
|
||||||
<div className="flex gap-8 lg:gap-10 pt-6 border-t border-white/[0.08]">
|
<div className="flex gap-8 lg:gap-10 pt-5 border-t border-white/[0.08]">
|
||||||
<div>
|
<div>
|
||||||
<div className="text-2xl lg:text-3xl font-display text-white">{totalDomains}</div>
|
<div className="text-xl lg:text-2xl font-display text-white">{totalDomains}</div>
|
||||||
<div className="text-[9px] uppercase tracking-widest text-white/30 font-mono mt-1">Tracking</div>
|
<div className="text-[9px] tracking-wide text-white/30 font-mono mt-1">Tracking</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div className="text-2xl lg:text-3xl font-display text-accent">{availableDomains.length}</div>
|
<div className="text-xl lg:text-2xl font-display text-accent">{availableDomains.length}</div>
|
||||||
<div className="text-[9px] uppercase tracking-widest text-white/30 font-mono mt-1">Available</div>
|
<div className="text-[9px] tracking-wide text-white/30 font-mono mt-1">Available</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div className="text-2xl lg:text-3xl font-display text-white">{marketStats.endingSoon}</div>
|
<div className="text-xl lg:text-2xl font-display text-white">{marketStats.endingSoon}</div>
|
||||||
<div className="text-[9px] uppercase tracking-widest text-white/30 font-mono mt-1">Ending Soon</div>
|
<div className="text-[9px] tracking-wide text-white/30 font-mono mt-1">Ending Soon</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Right: Search Terminal */}
|
{/* Right: Search Terminal */}
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<div className="absolute -inset-2 bg-gradient-to-tr from-accent/10 via-transparent to-accent/5 blur-2xl opacity-40" />
|
<div className="absolute -inset-4 bg-gradient-to-tr from-accent/10 via-transparent to-accent/5 blur-3xl opacity-30" />
|
||||||
|
|
||||||
<div className="relative bg-[#0A0A0A] border border-white/15 p-1.5">
|
<div className="relative bg-[#0A0A0A] border border-white/10 overflow-hidden">
|
||||||
{/* Tech Corners */}
|
{/* Header Bar */}
|
||||||
<div className="absolute -top-px -left-px w-4 h-4 border-t border-l border-accent/60" />
|
<div className="flex items-center justify-between px-5 py-3 border-b border-white/[0.06] bg-black/40">
|
||||||
<div className="absolute -top-px -right-px w-4 h-4 border-t border-r border-accent/60" />
|
<span className="text-[10px] font-mono text-white/40 flex items-center gap-2">
|
||||||
<div className="absolute -bottom-px -left-px w-4 h-4 border-b border-l border-accent/60" />
|
<Crosshair className="w-3 h-3 text-accent" />
|
||||||
<div className="absolute -bottom-px -right-px w-4 h-4 border-b border-r border-accent/60" />
|
Domain Search
|
||||||
|
|
||||||
<div className="bg-[#050505] p-6 lg:p-8 relative">
|
|
||||||
{/* Header */}
|
|
||||||
<div className="flex items-center justify-between mb-6">
|
|
||||||
<span className="text-[9px] font-mono uppercase tracking-[0.15em] text-accent flex items-center gap-2">
|
|
||||||
<Crosshair className="w-3 h-3" />
|
|
||||||
Target Acquisition
|
|
||||||
</span>
|
</span>
|
||||||
<div className="flex gap-1">
|
<div className="flex gap-1.5">
|
||||||
<div className="w-1 h-1 bg-accent/50" />
|
<div className="w-2 h-2 rounded-full bg-white/10" />
|
||||||
<div className="w-1 h-1 bg-accent/30" />
|
<div className="w-2 h-2 rounded-full bg-white/10" />
|
||||||
<div className="w-1 h-1 bg-accent/10" />
|
<div className="w-2 h-2 rounded-full bg-accent/50" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="p-5 lg:p-6">
|
||||||
{/* Input */}
|
{/* Input */}
|
||||||
<div className={clsx(
|
<div className={clsx(
|
||||||
"relative border transition-all duration-300",
|
"relative border-2 transition-all duration-300 bg-black/30",
|
||||||
searchFocused ? "border-accent/50 shadow-[0_0_20px_-5px_rgba(16,185,129,0.2)]" : "border-white/10"
|
searchFocused ? "border-accent/60 shadow-[0_0_30px_-10px_rgba(16,185,129,0.3)]" : "border-white/10"
|
||||||
)}>
|
)}>
|
||||||
<div className="absolute left-3 top-1/2 -translate-y-1/2 text-accent font-mono text-sm">{'>'}</div>
|
|
||||||
<input
|
<input
|
||||||
ref={searchInputRef}
|
ref={searchInputRef}
|
||||||
type="text"
|
type="text"
|
||||||
@ -254,76 +247,92 @@ export default function RadarPage() {
|
|||||||
onChange={(e) => setSearchQuery(e.target.value)}
|
onChange={(e) => setSearchQuery(e.target.value)}
|
||||||
onFocus={() => setSearchFocused(true)}
|
onFocus={() => setSearchFocused(true)}
|
||||||
onBlur={() => setSearchFocused(false)}
|
onBlur={() => setSearchFocused(false)}
|
||||||
placeholder="ENTER_TARGET..."
|
placeholder="example.com"
|
||||||
className="w-full bg-black/50 px-8 py-4 text-lg lg:text-xl text-white placeholder:text-white/15 font-mono uppercase tracking-tight outline-none"
|
className="w-full bg-transparent px-4 py-4 text-lg text-white placeholder:text-white/20 outline-none"
|
||||||
/>
|
/>
|
||||||
{searchQuery && (
|
{searchQuery && (
|
||||||
<button
|
<button
|
||||||
onClick={() => { setSearchQuery(''); setSearchResult(null) }}
|
onClick={() => { setSearchQuery(''); setSearchResult(null) }}
|
||||||
className="absolute right-3 top-1/2 -translate-y-1/2 text-white/20 hover:text-white"
|
className="absolute right-4 top-1/2 -translate-y-1/2 text-white/30 hover:text-white transition-colors"
|
||||||
>
|
>
|
||||||
<XCircle className="w-4 h-4" />
|
<XCircle className="w-5 h-5" />
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Results */}
|
{/* Results */}
|
||||||
{searchResult && (
|
{searchResult && (
|
||||||
<div className="mt-5 border-t border-white/[0.08] pt-5">
|
<div className="mt-5">
|
||||||
{searchResult.loading ? (
|
{searchResult.loading ? (
|
||||||
<div className="flex items-center gap-3 text-accent">
|
<div className="flex items-center justify-center gap-3 py-6 text-white/40">
|
||||||
<Loader2 className="w-3 h-3 animate-spin" />
|
<Loader2 className="w-4 h-4 animate-spin" />
|
||||||
<span className="text-xs font-mono uppercase tracking-widest">Scanning...</span>
|
<span className="text-sm">Checking availability...</span>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="space-y-4">
|
<div className={clsx(
|
||||||
<div className="flex items-center justify-between">
|
"p-5 border-2 transition-all",
|
||||||
<div className="flex items-center gap-2">
|
searchResult.is_available
|
||||||
|
? "bg-accent/5 border-accent/40"
|
||||||
|
: "bg-white/[0.02] border-white/10"
|
||||||
|
)}>
|
||||||
|
{/* Status Header */}
|
||||||
|
<div className="flex items-center justify-between mb-4">
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
{searchResult.is_available ? (
|
{searchResult.is_available ? (
|
||||||
<div className="w-2 h-2 bg-accent shadow-[0_0_8px_rgba(16,185,129,0.8)]" />
|
<CheckCircle2 className="w-5 h-5 text-accent" />
|
||||||
) : (
|
) : (
|
||||||
<div className="w-2 h-2 bg-white/20" />
|
<XCircle className="w-5 h-5 text-white/30" />
|
||||||
)}
|
)}
|
||||||
<span className="text-base font-mono text-white">{searchResult.domain}</span>
|
<span className="text-lg font-medium text-white">{searchResult.domain}</span>
|
||||||
</div>
|
</div>
|
||||||
<span className={clsx(
|
<span className={clsx(
|
||||||
"text-[9px] font-mono uppercase tracking-widest px-2 py-0.5 border",
|
"text-xs font-medium px-3 py-1",
|
||||||
searchResult.is_available
|
searchResult.is_available
|
||||||
? "text-accent border-accent/30 bg-accent/5"
|
? "text-accent bg-accent/10"
|
||||||
: "text-white/30 border-white/10"
|
: "text-white/40 bg-white/5"
|
||||||
)}>
|
)}>
|
||||||
{searchResult.is_available ? 'AVAILABLE' : 'TAKEN'}
|
{searchResult.is_available ? 'Available' : 'Taken'}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Available Actions */}
|
||||||
{searchResult.is_available && (
|
{searchResult.is_available && (
|
||||||
<div className="flex gap-2 pt-2">
|
<div className="flex gap-3">
|
||||||
<button
|
<button
|
||||||
onClick={handleAddToWatchlist}
|
onClick={handleAddToWatchlist}
|
||||||
disabled={addingToWatchlist}
|
disabled={addingToWatchlist}
|
||||||
className="flex-1 py-2.5 border border-white/15 text-white/70 font-mono text-[10px] uppercase tracking-widest hover:bg-white/5 transition-colors"
|
className="flex-1 py-3 border border-white/20 text-white/80 text-sm font-medium hover:bg-white/5 transition-colors flex items-center justify-center gap-2"
|
||||||
>
|
>
|
||||||
{addingToWatchlist ? 'TRACKING...' : '+ TRACK'}
|
{addingToWatchlist ? <Loader2 className="w-4 h-4 animate-spin" /> : <Eye className="w-4 h-4" />}
|
||||||
|
Add to Watchlist
|
||||||
</button>
|
</button>
|
||||||
<a
|
<a
|
||||||
href={`https://www.namecheap.com/domains/registration/results/?domain=${searchResult.domain}`}
|
href={`https://www.namecheap.com/domains/registration/results/?domain=${searchResult.domain}`}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
className="flex-1 py-2.5 bg-accent text-black font-mono text-[10px] font-bold uppercase tracking-widest hover:bg-white transition-colors flex items-center justify-center gap-1.5"
|
className="flex-1 py-3 bg-accent text-black text-sm font-bold hover:bg-white transition-colors flex items-center justify-center gap-2"
|
||||||
>
|
>
|
||||||
GET <ArrowRight className="w-3 h-3" />
|
Register Now <ArrowRight className="w-4 h-4" />
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Taken Info */}
|
||||||
|
{!searchResult.is_available && searchResult.registrar && (
|
||||||
|
<p className="text-xs text-white/30 mt-2">
|
||||||
|
Registered with {searchResult.registrar}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Footer */}
|
{/* Hint */}
|
||||||
<div className="mt-6 pt-4 border-t border-white/[0.05] flex justify-between items-center text-[9px] text-white/15 font-mono">
|
{!searchResult && (
|
||||||
<span>SECURE</span>
|
<p className="text-xs text-white/20 mt-4 text-center">
|
||||||
<span>V2.1</span>
|
Enter a domain name to check availability
|
||||||
</div>
|
</p>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -341,13 +350,13 @@ export default function RadarPage() {
|
|||||||
|
|
||||||
{/* Hot Auctions - 2 cols */}
|
{/* Hot Auctions - 2 cols */}
|
||||||
<div className="lg:col-span-2 bg-[#020202] p-6 lg:p-8">
|
<div className="lg:col-span-2 bg-[#020202] p-6 lg:p-8">
|
||||||
<div className="flex items-center justify-between mb-6">
|
<div className="flex items-center justify-between mb-5">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Gavel className="w-4 h-4 text-accent" />
|
<Gavel className="w-4 h-4 text-accent" />
|
||||||
<span className="text-xs font-bold text-white uppercase tracking-wider">Live Auctions</span>
|
<span className="text-sm font-semibold text-white">Live Auctions</span>
|
||||||
</div>
|
</div>
|
||||||
<Link href="/terminal/market" className="text-[9px] font-mono uppercase text-white/30 hover:text-white transition-colors">
|
<Link href="/terminal/market" className="text-xs text-white/40 hover:text-white transition-colors">
|
||||||
View All →
|
View all →
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -356,35 +365,35 @@ export default function RadarPage() {
|
|||||||
<Loader2 className="w-5 h-5 text-accent animate-spin" />
|
<Loader2 className="w-5 h-5 text-accent animate-spin" />
|
||||||
</div>
|
</div>
|
||||||
) : hotAuctions.length > 0 ? (
|
) : hotAuctions.length > 0 ? (
|
||||||
<div className="space-y-px">
|
<div className="space-y-1">
|
||||||
{hotAuctions.map((auction, i) => (
|
{hotAuctions.map((auction, i) => (
|
||||||
<a
|
<a
|
||||||
key={i}
|
key={i}
|
||||||
href={auction.affiliate_url || '#'}
|
href={auction.affiliate_url || '#'}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
className="flex items-center justify-between p-3 bg-white/[0.02] hover:bg-white/[0.04] transition-colors group"
|
className="flex items-center justify-between p-3 bg-white/[0.02] hover:bg-white/[0.05] transition-colors group"
|
||||||
>
|
>
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<span className="text-[9px] font-mono text-white/20 w-5">{auction.platform.substring(0, 2).toUpperCase()}</span>
|
<span className="text-[10px] font-mono text-white/25 w-6">{auction.platform.substring(0, 3)}</span>
|
||||||
<div>
|
<div>
|
||||||
<div className="font-mono text-sm text-white group-hover:text-accent transition-colors">{auction.domain}</div>
|
<div className="text-sm text-white group-hover:text-accent transition-colors">{auction.domain}</div>
|
||||||
<div className="text-[9px] text-white/25 font-mono uppercase">{auction.time_remaining}</div>
|
<div className="text-[10px] text-white/30">{auction.time_remaining}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="font-mono text-sm text-accent">${auction.current_bid.toLocaleString()}</div>
|
<div className="font-mono text-sm text-accent font-medium">${auction.current_bid.toLocaleString()}</div>
|
||||||
</a>
|
</a>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="text-center py-8 text-white/15 font-mono text-xs uppercase">No active auctions</div>
|
<div className="text-center py-8 text-white/20 text-sm">No active auctions</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Quick Links */}
|
{/* Quick Links */}
|
||||||
<div className="bg-[#020202] p-6 lg:p-8">
|
<div className="bg-[#020202] p-6 lg:p-8">
|
||||||
<div className="flex items-center gap-2 mb-6">
|
<div className="flex items-center gap-2 mb-5">
|
||||||
<Zap className="w-4 h-4 text-white/50" />
|
<Zap className="w-4 h-4 text-white/50" />
|
||||||
<span className="text-xs font-bold text-white uppercase tracking-wider">Quick Access</span>
|
<span className="text-sm font-semibold text-white">Quick Access</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
@ -408,8 +417,8 @@ export default function RadarPage() {
|
|||||||
{/* Status */}
|
{/* Status */}
|
||||||
<div className="mt-6 pt-4 border-t border-white/[0.05]">
|
<div className="mt-6 pt-4 border-t border-white/[0.05]">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<div className="w-1 h-1 bg-accent rounded-full animate-pulse" />
|
<div className="w-1.5 h-1.5 bg-accent rounded-full animate-pulse" />
|
||||||
<span className="text-[9px] font-mono text-white/30 uppercase">System Online</span>
|
<span className="text-[10px] font-mono text-white/30">System online</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -61,11 +61,11 @@ function getTimeAgo(date: string | null): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const healthConfig: Record<HealthStatus, { label: string; color: string; bg: string }> = {
|
const healthConfig: Record<HealthStatus, { label: string; color: string; bg: string }> = {
|
||||||
healthy: { label: 'ONLINE', color: 'text-accent', bg: 'bg-accent/10 border-accent/20' },
|
healthy: { label: 'Healthy', color: 'text-accent', bg: 'bg-accent/10 border-accent/20' },
|
||||||
weakening: { label: 'WEAK', color: 'text-amber-400', bg: 'bg-amber-500/10 border-amber-500/20' },
|
weakening: { label: 'Weak', color: 'text-amber-400', bg: 'bg-amber-500/10 border-amber-500/20' },
|
||||||
parked: { label: 'PARKED', color: 'text-blue-400', bg: 'bg-blue-500/10 border-blue-500/20' },
|
parked: { label: 'Parked', color: 'text-blue-400', bg: 'bg-blue-500/10 border-blue-500/20' },
|
||||||
critical: { label: 'CRIT', color: 'text-rose-400', bg: 'bg-rose-500/10 border-rose-500/20' },
|
critical: { label: 'Critical', color: 'text-rose-400', bg: 'bg-rose-500/10 border-rose-500/20' },
|
||||||
unknown: { label: '???', color: 'text-white/40', bg: 'bg-white/5 border-white/10' },
|
unknown: { label: 'Unknown', color: 'text-white/40', bg: 'bg-white/5 border-white/10' },
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
@ -199,10 +199,10 @@ export default function WatchlistPage() {
|
|||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<div className="inline-flex items-center gap-2">
|
<div className="inline-flex items-center gap-2">
|
||||||
<Target className="w-4 h-4 text-accent" />
|
<Target className="w-4 h-4 text-accent" />
|
||||||
<span className="text-[9px] font-mono uppercase tracking-[0.2em] text-accent">Surveillance</span>
|
<span className="text-[10px] font-mono tracking-wide text-accent">Domain Surveillance</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h1 className="font-display text-[2.5rem] lg:text-[3rem] leading-[0.95] tracking-[-0.03em]">
|
<h1 className="font-display text-[2rem] lg:text-[2.5rem] leading-[1] tracking-[-0.02em]">
|
||||||
<span className="text-white">Watchlist</span>
|
<span className="text-white">Watchlist</span>
|
||||||
<span className="text-white/30 ml-3">{stats.total}</span>
|
<span className="text-white/30 ml-3">{stats.total}</span>
|
||||||
</h1>
|
</h1>
|
||||||
@ -211,12 +211,12 @@ export default function WatchlistPage() {
|
|||||||
{/* Right: Stats */}
|
{/* Right: Stats */}
|
||||||
<div className="flex gap-6 lg:gap-8">
|
<div className="flex gap-6 lg:gap-8">
|
||||||
<div className="text-right">
|
<div className="text-right">
|
||||||
<div className="text-2xl font-display text-accent">{stats.available}</div>
|
<div className="text-xl font-display text-accent">{stats.available}</div>
|
||||||
<div className="text-[8px] uppercase tracking-widest text-white/30 font-mono">Available</div>
|
<div className="text-[9px] tracking-wide text-white/30 font-mono">Available</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-right">
|
<div className="text-right">
|
||||||
<div className="text-2xl font-display text-amber-400">{stats.expiring}</div>
|
<div className="text-xl font-display text-amber-400">{stats.expiring}</div>
|
||||||
<div className="text-[8px] uppercase tracking-widest text-white/30 font-mono">Expiring</div>
|
<div className="text-[9px] tracking-wide text-white/30 font-mono">Expiring</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -228,20 +228,20 @@ export default function WatchlistPage() {
|
|||||||
<section className="pb-6">
|
<section className="pb-6">
|
||||||
<form onSubmit={handleAdd} className="relative max-w-xl">
|
<form onSubmit={handleAdd} className="relative max-w-xl">
|
||||||
<div className="flex items-center bg-[#050505] border border-white/10 focus-within:border-accent/40 transition-colors">
|
<div className="flex items-center bg-[#050505] border border-white/10 focus-within:border-accent/40 transition-colors">
|
||||||
<div className="pl-4 text-accent font-mono">{'>'}</div>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={newDomain}
|
value={newDomain}
|
||||||
onChange={(e) => setNewDomain(e.target.value)}
|
onChange={(e) => setNewDomain(e.target.value)}
|
||||||
placeholder="ADD_TARGET..."
|
placeholder="Add domain to watch..."
|
||||||
className="flex-1 bg-transparent px-3 py-3 text-sm text-white placeholder:text-white/20 font-mono uppercase outline-none"
|
className="flex-1 bg-transparent px-4 py-3 text-sm text-white placeholder:text-white/25 outline-none"
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={adding || !newDomain.trim()}
|
disabled={adding || !newDomain.trim()}
|
||||||
className="h-full px-5 py-3 bg-accent text-black font-mono text-[10px] font-bold uppercase tracking-wider hover:bg-white transition-colors disabled:opacity-30"
|
className="h-full px-5 py-3 bg-accent text-black text-sm font-semibold hover:bg-white transition-colors disabled:opacity-30 flex items-center gap-2"
|
||||||
>
|
>
|
||||||
{adding ? <Loader2 className="w-3.5 h-3.5 animate-spin" /> : <Plus className="w-3.5 h-3.5" />}
|
{adding ? <Loader2 className="w-4 h-4 animate-spin" /> : <Plus className="w-4 h-4" />}
|
||||||
|
<span className="hidden sm:inline">Add</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
@ -261,10 +261,10 @@ export default function WatchlistPage() {
|
|||||||
key={item.value}
|
key={item.value}
|
||||||
onClick={() => setFilter(item.value as typeof filter)}
|
onClick={() => setFilter(item.value as typeof filter)}
|
||||||
className={clsx(
|
className={clsx(
|
||||||
"px-4 py-2 text-[10px] font-mono uppercase tracking-wider transition-colors",
|
"px-4 py-2 text-xs font-medium transition-colors",
|
||||||
filter === item.value
|
filter === item.value
|
||||||
? "bg-white/10 text-white"
|
? "bg-white/10 text-white"
|
||||||
: "text-white/30 hover:text-white/50"
|
: "text-white/40 hover:text-white/60"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{item.label} ({item.count})
|
{item.label} ({item.count})
|
||||||
@ -279,15 +279,16 @@ export default function WatchlistPage() {
|
|||||||
<section className="py-6">
|
<section className="py-6">
|
||||||
{!filteredDomains.length ? (
|
{!filteredDomains.length ? (
|
||||||
<div className="text-center py-16">
|
<div className="text-center py-16">
|
||||||
<div className="w-12 h-12 mx-auto border border-white/10 flex items-center justify-center mb-4">
|
<div className="w-14 h-14 mx-auto bg-white/[0.02] border border-white/10 rounded-lg flex items-center justify-center mb-4">
|
||||||
<Crosshair className="w-5 h-5 text-white/20" />
|
<Eye className="w-6 h-6 text-white/20" />
|
||||||
</div>
|
</div>
|
||||||
<p className="text-white/30 font-mono text-sm uppercase">No targets</p>
|
<p className="text-white/40 text-sm">No domains in your watchlist</p>
|
||||||
|
<p className="text-white/25 text-xs mt-1">Add a domain above to start monitoring</p>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="space-y-px">
|
<div className="space-y-px">
|
||||||
{/* Table Header */}
|
{/* Table Header */}
|
||||||
<div className="hidden lg:grid grid-cols-[1fr_100px_100px_100px_80px_100px] gap-4 px-4 py-2 text-[9px] font-mono uppercase tracking-widest text-white/30 border-b border-white/[0.05]">
|
<div className="hidden lg:grid grid-cols-[1fr_100px_100px_100px_80px_100px] gap-4 px-4 py-2 text-xs text-white/40 border-b border-white/[0.06]">
|
||||||
<div>Domain</div>
|
<div>Domain</div>
|
||||||
<div>Status</div>
|
<div>Status</div>
|
||||||
<div>Health</div>
|
<div>Health</div>
|
||||||
@ -313,27 +314,27 @@ export default function WatchlistPage() {
|
|||||||
<div className="flex items-center justify-between mb-3">
|
<div className="flex items-center justify-between mb-3">
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<div className={clsx(
|
<div className={clsx(
|
||||||
"w-2 h-2",
|
"w-2 h-2 rounded-full",
|
||||||
domain.is_available ? "bg-accent shadow-[0_0_8px_rgba(16,185,129,0.8)]" : "bg-white/15"
|
domain.is_available ? "bg-accent shadow-[0_0_8px_rgba(16,185,129,0.8)]" : "bg-white/20"
|
||||||
)} />
|
)} />
|
||||||
<span className="font-mono text-sm text-white">{domain.name}</span>
|
<span className="text-sm text-white font-medium">{domain.name}</span>
|
||||||
</div>
|
</div>
|
||||||
<span className={clsx(
|
<span className={clsx(
|
||||||
"text-[9px] font-mono uppercase px-2 py-0.5 border",
|
"text-xs px-2 py-0.5",
|
||||||
domain.is_available ? "text-accent border-accent/30" : "text-white/30 border-white/10"
|
domain.is_available ? "text-accent bg-accent/10" : "text-white/40 bg-white/5"
|
||||||
)}>
|
)}>
|
||||||
{domain.is_available ? 'OPEN' : 'TAKEN'}
|
{domain.is_available ? 'Available' : 'Taken'}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center justify-between text-[10px] font-mono text-white/30">
|
<div className="flex items-center justify-between text-xs text-white/40">
|
||||||
<span>{formatExpiryDate(domain.expiration_date)}</span>
|
<span>{formatExpiryDate(domain.expiration_date)}</span>
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<button onClick={() => handleRefresh(domain.id)} className="p-1.5 hover:bg-white/5">
|
<button onClick={() => handleRefresh(domain.id)} className="p-1.5 hover:bg-white/5 rounded">
|
||||||
<RefreshCw className={clsx("w-3.5 h-3.5", refreshingId === domain.id && "animate-spin")} />
|
<RefreshCw className={clsx("w-4 h-4", refreshingId === domain.id && "animate-spin")} />
|
||||||
</button>
|
</button>
|
||||||
<button onClick={() => handleDelete(domain.id, domain.name)} className="p-1.5 hover:bg-rose-500/10 hover:text-rose-400">
|
<button onClick={() => handleDelete(domain.id, domain.name)} className="p-1.5 hover:bg-rose-500/10 hover:text-rose-400 rounded">
|
||||||
<Trash2 className="w-3.5 h-3.5" />
|
<Trash2 className="w-4 h-4" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -344,44 +345,44 @@ export default function WatchlistPage() {
|
|||||||
{/* Domain */}
|
{/* Domain */}
|
||||||
<div className="flex items-center gap-3 min-w-0">
|
<div className="flex items-center gap-3 min-w-0">
|
||||||
<div className={clsx(
|
<div className={clsx(
|
||||||
"w-1.5 h-1.5 shrink-0",
|
"w-2 h-2 rounded-full shrink-0",
|
||||||
domain.is_available ? "bg-accent shadow-[0_0_6px_rgba(16,185,129,0.8)]" : "bg-white/15"
|
domain.is_available ? "bg-accent shadow-[0_0_6px_rgba(16,185,129,0.8)]" : "bg-white/20"
|
||||||
)} />
|
)} />
|
||||||
<span className="font-mono text-sm text-white truncate">{domain.name}</span>
|
<span className="text-sm text-white font-medium truncate">{domain.name}</span>
|
||||||
<a href={`https://${domain.name}`} target="_blank" className="opacity-0 group-hover:opacity-50 hover:!opacity-100 transition-opacity">
|
<a href={`https://${domain.name}`} target="_blank" className="opacity-0 group-hover:opacity-50 hover:!opacity-100 transition-opacity">
|
||||||
<ExternalLink className="w-3 h-3" />
|
<ExternalLink className="w-3.5 h-3.5" />
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Status */}
|
{/* Status */}
|
||||||
<div>
|
<div>
|
||||||
<span className={clsx(
|
<span className={clsx(
|
||||||
"text-[9px] font-mono uppercase px-2 py-0.5 border",
|
"text-xs px-2 py-0.5",
|
||||||
domain.is_available ? "text-accent border-accent/30 bg-accent/5" : "text-white/30 border-white/10"
|
domain.is_available ? "text-accent bg-accent/10" : "text-white/40 bg-white/5"
|
||||||
)}>
|
)}>
|
||||||
{domain.is_available ? 'AVAIL' : 'TAKEN'}
|
{domain.is_available ? 'Available' : 'Taken'}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Health */}
|
{/* Health */}
|
||||||
<button
|
<button
|
||||||
onClick={() => { setSelectedDomain(domain.id); handleHealthCheck(domain.id) }}
|
onClick={() => { setSelectedDomain(domain.id); handleHealthCheck(domain.id) }}
|
||||||
className="flex items-center gap-1.5 group/health"
|
className="flex items-center gap-1.5 hover:opacity-80 transition-opacity"
|
||||||
>
|
>
|
||||||
{loadingHealth[domain.id] ? (
|
{loadingHealth[domain.id] ? (
|
||||||
<Loader2 className="w-3 h-3 animate-spin text-white/30" />
|
<Loader2 className="w-3.5 h-3.5 animate-spin text-white/30" />
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<Activity className={clsx("w-3 h-3", config.color)} />
|
<Activity className={clsx("w-3.5 h-3.5", config.color)} />
|
||||||
<span className={clsx("text-[9px] font-mono uppercase", config.color)}>{config.label}</span>
|
<span className={clsx("text-xs", config.color)}>{config.label}</span>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{/* Expires */}
|
{/* Expires */}
|
||||||
<div className="text-[10px] font-mono text-white/40">
|
<div className="text-xs text-white/50">
|
||||||
{days !== null && days <= 30 && days > 0 ? (
|
{days !== null && days <= 30 && days > 0 ? (
|
||||||
<span className="text-amber-400">{days}d</span>
|
<span className="text-amber-400 font-medium">{days} days</span>
|
||||||
) : (
|
) : (
|
||||||
formatExpiryDate(domain.expiration_date)
|
formatExpiryDate(domain.expiration_date)
|
||||||
)}
|
)}
|
||||||
@ -451,42 +452,42 @@ export default function WatchlistPage() {
|
|||||||
<div className="flex items-center justify-between mb-6">
|
<div className="flex items-center justify-between mb-6">
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<Activity className="w-4 h-4 text-accent" />
|
<Activity className="w-4 h-4 text-accent" />
|
||||||
<span className="text-[10px] font-mono uppercase tracking-widest text-accent">Health Intel</span>
|
<span className="text-sm font-medium text-white">Health Report</span>
|
||||||
</div>
|
</div>
|
||||||
<button onClick={() => setSelectedDomain(null)} className="text-white/30 hover:text-white">
|
<button onClick={() => setSelectedDomain(null)} className="text-white/30 hover:text-white p-1">
|
||||||
<X className="w-4 h-4" />
|
<X className="w-5 h-5" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Domain */}
|
{/* Domain */}
|
||||||
<div className="mb-6">
|
<div className="mb-6">
|
||||||
<h3 className="font-mono text-lg text-white">{selectedDomainData.name}</h3>
|
<h3 className="text-lg font-medium text-white">{selectedDomainData.name}</h3>
|
||||||
<div className="flex items-center gap-2 mt-2">
|
<div className="flex items-center gap-2 mt-2">
|
||||||
<div className={clsx(
|
<span className={clsx(
|
||||||
"text-[9px] font-mono uppercase px-2 py-0.5 border",
|
"text-xs px-2.5 py-1",
|
||||||
healthConfig[selectedHealth?.status || 'unknown'].bg,
|
healthConfig[selectedHealth?.status || 'unknown'].bg,
|
||||||
healthConfig[selectedHealth?.status || 'unknown'].color
|
healthConfig[selectedHealth?.status || 'unknown'].color
|
||||||
)}>
|
)}>
|
||||||
{healthConfig[selectedHealth?.status || 'unknown'].label}
|
{healthConfig[selectedHealth?.status || 'unknown'].label}
|
||||||
</div>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Checks */}
|
{/* Checks */}
|
||||||
{selectedHealth && (
|
{selectedHealth && (
|
||||||
<div className="space-y-2">
|
<div className="space-y-1">
|
||||||
{[
|
{[
|
||||||
{ label: 'DNS', value: selectedHealth.dns?.has_a },
|
{ label: 'DNS Resolution', value: selectedHealth.dns?.has_a },
|
||||||
{ label: 'HTTP', value: selectedHealth.http?.is_reachable },
|
{ label: 'HTTP Reachable', value: selectedHealth.http?.is_reachable },
|
||||||
{ label: 'SSL', value: selectedHealth.ssl?.has_certificate },
|
{ label: 'SSL Certificate', value: selectedHealth.ssl?.has_certificate },
|
||||||
{ label: 'Parked', value: !selectedHealth.dns?.is_parked && !selectedHealth.http?.is_parked },
|
{ label: 'Not Parked', value: !selectedHealth.dns?.is_parked && !selectedHealth.http?.is_parked },
|
||||||
].map((check) => (
|
].map((check) => (
|
||||||
<div key={check.label} className="flex items-center justify-between py-2 border-b border-white/[0.05]">
|
<div key={check.label} className="flex items-center justify-between py-2.5 border-b border-white/[0.05]">
|
||||||
<span className="text-xs font-mono text-white/50 uppercase">{check.label}</span>
|
<span className="text-sm text-white/60">{check.label}</span>
|
||||||
{check.value ? (
|
{check.value ? (
|
||||||
<CheckCircle2 className="w-4 h-4 text-accent" />
|
<CheckCircle2 className="w-5 h-5 text-accent" />
|
||||||
) : (
|
) : (
|
||||||
<XCircle className="w-4 h-4 text-rose-400" />
|
<XCircle className="w-5 h-5 text-rose-400" />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
@ -497,14 +498,14 @@ export default function WatchlistPage() {
|
|||||||
<button
|
<button
|
||||||
onClick={() => handleHealthCheck(selectedDomainData.id)}
|
onClick={() => handleHealthCheck(selectedDomainData.id)}
|
||||||
disabled={loadingHealth[selectedDomainData.id]}
|
disabled={loadingHealth[selectedDomainData.id]}
|
||||||
className="w-full mt-6 py-3 border border-white/10 text-white/50 font-mono text-[10px] uppercase tracking-wider hover:bg-white/5 hover:text-white transition-all flex items-center justify-center gap-2"
|
className="w-full mt-6 py-3 bg-white/5 border border-white/10 text-white/70 text-sm font-medium hover:bg-white/10 hover:text-white transition-all flex items-center justify-center gap-2"
|
||||||
>
|
>
|
||||||
{loadingHealth[selectedDomainData.id] ? (
|
{loadingHealth[selectedDomainData.id] ? (
|
||||||
<Loader2 className="w-3.5 h-3.5 animate-spin" />
|
<Loader2 className="w-4 h-4 animate-spin" />
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<RefreshCw className="w-3.5 h-3.5" />
|
<RefreshCw className="w-4 h-4" />
|
||||||
Refresh
|
Refresh Health Check
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
Reference in New Issue
Block a user