fix: TLD Matrix shows own domain correctly as taken + Portfolio table responsive
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
1. TLD Matrix fix: - Pass original_is_taken flag to run_tld_matrix() - If main domain check shows taken, force same TLD to show as taken - Fixes incorrect "available" display for owned domains 2. Portfolio table: - Use minmax() for flexible column widths - Smaller gaps and padding for compact layout - Smaller action buttons (w-8 h-8) - Shorter column labels (Bought, Exp.)
This commit is contained in:
@ -10,7 +10,12 @@ class TldMatrixAnalyzer:
|
|||||||
ttl_seconds = 60 * 30 # 30m (availability can change)
|
ttl_seconds = 60 * 30 # 30m (availability can change)
|
||||||
|
|
||||||
async def analyze(self, ctx: AnalyzeContext) -> list[AnalyzerContribution]:
|
async def analyze(self, ctx: AnalyzeContext) -> list[AnalyzerContribution]:
|
||||||
rows = await run_tld_matrix(ctx.domain)
|
# If main domain check says it's taken, pass that info to TLD matrix
|
||||||
|
# This ensures the original TLD shows correctly as "taken" even if
|
||||||
|
# DNS-based checks fail (e.g., domain registered but no DNS records)
|
||||||
|
original_is_taken = ctx.check and not ctx.check.is_available
|
||||||
|
|
||||||
|
rows = await run_tld_matrix(ctx.domain, original_is_taken=original_is_taken)
|
||||||
item = AnalyzeItem(
|
item = AnalyzeItem(
|
||||||
key="tld_matrix",
|
key="tld_matrix",
|
||||||
label="TLD Matrix",
|
label="TLD Matrix",
|
||||||
|
|||||||
@ -48,11 +48,21 @@ async def _check_one(domain: str) -> TldMatrixRow:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def run_tld_matrix(domain: str, tlds: list[str] | None = None) -> list[TldMatrixRow]:
|
async def run_tld_matrix(domain: str, tlds: list[str] | None = None, original_is_taken: bool = False) -> list[TldMatrixRow]:
|
||||||
sld = (domain or "").split(".")[0].lower().strip()
|
"""
|
||||||
|
Check availability for the same SLD across multiple TLDs.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
domain: The full domain being analyzed (e.g., "akaya.ch")
|
||||||
|
tlds: List of TLDs to check (defaults to DEFAULT_TLDS)
|
||||||
|
original_is_taken: If True, force the original domain's TLD to show as taken
|
||||||
|
"""
|
||||||
|
parts = (domain or "").lower().strip().split(".")
|
||||||
|
sld = parts[0] if parts else ""
|
||||||
|
original_tld = parts[-1] if len(parts) > 1 else ""
|
||||||
tlds = [t.lower().lstrip(".") for t in (tlds or DEFAULT_TLDS)]
|
tlds = [t.lower().lstrip(".") for t in (tlds or DEFAULT_TLDS)]
|
||||||
|
|
||||||
# Avoid repeated checks and the original TLD duplication
|
# Avoid repeated checks
|
||||||
seen = set()
|
seen = set()
|
||||||
candidates: list[str] = []
|
candidates: list[str] = []
|
||||||
for t in tlds:
|
for t in tlds:
|
||||||
@ -62,5 +72,23 @@ async def run_tld_matrix(domain: str, tlds: list[str] | None = None) -> list[Tld
|
|||||||
seen.add(d)
|
seen.add(d)
|
||||||
|
|
||||||
rows = await asyncio.gather(*[_check_one(d) for d in candidates])
|
rows = await asyncio.gather(*[_check_one(d) for d in candidates])
|
||||||
return list(rows)
|
result = list(rows)
|
||||||
|
|
||||||
|
# If the original domain is known to be taken, ensure its TLD shows as taken
|
||||||
|
# This fixes cases where DNS-based quick checks incorrectly show "available"
|
||||||
|
# for domains that are registered but have no DNS records
|
||||||
|
if original_is_taken and original_tld:
|
||||||
|
result = [
|
||||||
|
TldMatrixRow(
|
||||||
|
tld=r.tld,
|
||||||
|
domain=r.domain,
|
||||||
|
is_available=False,
|
||||||
|
status="taken",
|
||||||
|
method=r.method,
|
||||||
|
error=r.error,
|
||||||
|
) if r.tld == original_tld else r
|
||||||
|
for r in result
|
||||||
|
]
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|||||||
@ -1346,31 +1346,31 @@ export default function PortfolioPage() {
|
|||||||
) : (
|
) : (
|
||||||
<div className="border border-white/[0.08] bg-[#020202] overflow-hidden">
|
<div className="border border-white/[0.08] bg-[#020202] overflow-hidden">
|
||||||
{/* Desktop Table Header */}
|
{/* Desktop Table Header */}
|
||||||
<div className="hidden lg:grid grid-cols-[1fr_140px_90px_90px_100px_90px_80px_60px_160px] gap-6 px-6 py-4 text-[10px] font-mono text-white/40 uppercase tracking-[0.15em] border-b border-white/[0.08] bg-white/[0.02]">
|
<div className="hidden lg:grid grid-cols-[minmax(150px,1.5fr)_minmax(100px,1fr)_80px_70px_90px_70px_70px_50px_auto] gap-4 px-5 py-3 text-[10px] font-mono text-white/40 uppercase tracking-[0.12em] border-b border-white/[0.08] bg-white/[0.02]">
|
||||||
<button onClick={() => handleSort('domain')} className="flex items-center gap-2 hover:text-white transition-colors text-left">
|
<button onClick={() => handleSort('domain')} className="flex items-center gap-2 hover:text-white transition-colors text-left">
|
||||||
<span className={clsx(sortField === 'domain' && "text-accent font-bold")}>Domain</span>
|
<span className={clsx(sortField === 'domain' && "text-accent font-bold")}>Domain</span>
|
||||||
{sortField === 'domain' && (sortDirection === 'asc' ? <ChevronUp className="w-3 h-3 text-accent" /> : <ChevronDown className="w-3 h-3 text-accent" />)}
|
{sortField === 'domain' && (sortDirection === 'asc' ? <ChevronUp className="w-3 h-3 text-accent" /> : <ChevronDown className="w-3 h-3 text-accent" />)}
|
||||||
</button>
|
</button>
|
||||||
<div>Registrar</div>
|
<div>Registrar</div>
|
||||||
<button onClick={() => handleSort('purchased')} className="flex items-center gap-2 justify-center hover:text-white transition-colors">
|
<button onClick={() => handleSort('purchased')} className="flex items-center gap-1 justify-center hover:text-white transition-colors">
|
||||||
<span className={clsx(sortField === 'purchased' && "text-accent font-bold")}>Purchased</span>
|
<span className={clsx(sortField === 'purchased' && "text-accent font-bold")}>Bought</span>
|
||||||
{sortField === 'purchased' && (sortDirection === 'asc' ? <ChevronUp className="w-3 h-3 text-accent" /> : <ChevronDown className="w-3 h-3 text-accent" />)}
|
{sortField === 'purchased' && (sortDirection === 'asc' ? <ChevronUp className="w-3 h-3 text-accent" /> : <ChevronDown className="w-3 h-3 text-accent" />)}
|
||||||
</button>
|
</button>
|
||||||
<button onClick={() => handleSort('renewal')} className="flex items-center gap-2 justify-center hover:text-white transition-colors">
|
<button onClick={() => handleSort('renewal')} className="flex items-center gap-1 justify-center hover:text-white transition-colors">
|
||||||
<span className={clsx(sortField === 'renewal' && "text-accent font-bold")}>Expires</span>
|
<span className={clsx(sortField === 'renewal' && "text-accent font-bold")}>Exp.</span>
|
||||||
{sortField === 'renewal' && (sortDirection === 'asc' ? <ChevronUp className="w-3 h-3 text-accent" /> : <ChevronDown className="w-3 h-3 text-accent" />)}
|
{sortField === 'renewal' && (sortDirection === 'asc' ? <ChevronUp className="w-3 h-3 text-accent" /> : <ChevronDown className="w-3 h-3 text-accent" />)}
|
||||||
</button>
|
</button>
|
||||||
<button onClick={() => handleSort('value')} className="flex items-center gap-2 justify-end hover:text-white transition-colors">
|
<button onClick={() => handleSort('value')} className="flex items-center gap-1 justify-end hover:text-white transition-colors">
|
||||||
<span className={clsx(sortField === 'value' && "text-accent font-bold")}>Value</span>
|
<span className={clsx(sortField === 'value' && "text-accent font-bold")}>Value</span>
|
||||||
{sortField === 'value' && (sortDirection === 'asc' ? <ChevronUp className="w-3 h-3 text-accent" /> : <ChevronDown className="w-3 h-3 text-accent" />)}
|
{sortField === 'value' && (sortDirection === 'asc' ? <ChevronUp className="w-3 h-3 text-accent" /> : <ChevronDown className="w-3 h-3 text-accent" />)}
|
||||||
</button>
|
</button>
|
||||||
<button onClick={() => handleSort('roi')} className="flex items-center gap-2 justify-end hover:text-white transition-colors">
|
<button onClick={() => handleSort('roi')} className="flex items-center gap-1 justify-end hover:text-white transition-colors">
|
||||||
<span className={clsx(sortField === 'roi' && "text-accent font-bold")}>ROI</span>
|
<span className={clsx(sortField === 'roi' && "text-accent font-bold")}>ROI</span>
|
||||||
{sortField === 'roi' && (sortDirection === 'asc' ? <ChevronUp className="w-3 h-3 text-accent" /> : <ChevronDown className="w-3 h-3 text-accent" />)}
|
{sortField === 'roi' && (sortDirection === 'asc' ? <ChevronUp className="w-3 h-3 text-accent" /> : <ChevronDown className="w-3 h-3 text-accent" />)}
|
||||||
</button>
|
</button>
|
||||||
<div className="text-center">Yield</div>
|
<div className="text-center">Yield</div>
|
||||||
<div className="text-center">Health</div>
|
<div className="text-center">Health</div>
|
||||||
<div className="text-right">Actions</div>
|
<div className="text-right pr-1">Actions</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Table Body */}
|
{/* Table Body */}
|
||||||
@ -1393,7 +1393,7 @@ export default function PortfolioPage() {
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{/* DESKTOP ROW */}
|
{/* DESKTOP ROW */}
|
||||||
<div className="hidden lg:grid grid-cols-[1fr_140px_90px_90px_100px_90px_80px_60px_160px] gap-6 px-6 py-4 items-center group-hover:bg-white/[0.02]">
|
<div className="hidden lg:grid grid-cols-[minmax(150px,1.5fr)_minmax(100px,1fr)_80px_70px_90px_70px_70px_50px_auto] gap-4 px-5 py-3 items-center group-hover:bg-white/[0.02]">
|
||||||
{/* Domain */}
|
{/* Domain */}
|
||||||
<div className="flex items-center gap-3 min-w-0">
|
<div className="flex items-center gap-3 min-w-0">
|
||||||
<button
|
<button
|
||||||
@ -1462,31 +1462,31 @@ export default function PortfolioPage() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Actions */}
|
{/* Actions */}
|
||||||
<div className="flex items-center justify-end gap-2 opacity-40 group-hover:opacity-100 transition-all">
|
<div className="flex items-center justify-end gap-1 opacity-40 group-hover:opacity-100 transition-all">
|
||||||
<button
|
<button
|
||||||
onClick={() => openAnalyze(domain.domain)}
|
onClick={() => openAnalyze(domain.domain)}
|
||||||
title="Analyze"
|
title="Analyze"
|
||||||
className="w-10 h-10 flex items-center justify-center text-white/40 hover:text-accent border border-white/10 hover:bg-accent/10 hover:border-accent/20 transition-all"
|
className="w-8 h-8 flex items-center justify-center text-white/40 hover:text-accent border border-white/10 hover:bg-accent/10 hover:border-accent/20 transition-all"
|
||||||
>
|
>
|
||||||
<Shield className="w-4 h-4" />
|
<Shield className="w-3.5 h-3.5" />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => setEditingDomain(domain)}
|
onClick={() => setEditingDomain(domain)}
|
||||||
title="Edit"
|
title="Edit"
|
||||||
className="w-10 h-10 flex items-center justify-center text-white/40 hover:text-white border border-white/10 hover:bg-white/5 transition-all"
|
className="w-8 h-8 flex items-center justify-center text-white/40 hover:text-white border border-white/10 hover:bg-white/5 transition-all"
|
||||||
>
|
>
|
||||||
<Edit3 className="w-4 h-4" />
|
<Edit3 className="w-3.5 h-3.5" />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => handleDelete(domain.id, domain.domain)}
|
onClick={() => handleDelete(domain.id, domain.domain)}
|
||||||
disabled={deletingId === domain.id}
|
disabled={deletingId === domain.id}
|
||||||
title="Remove"
|
title="Remove"
|
||||||
className="w-10 h-10 flex items-center justify-center text-white/40 hover:text-rose-400 border border-white/10 hover:border-rose-400/20 hover:bg-rose-500/10 transition-all"
|
className="w-8 h-8 flex items-center justify-center text-white/40 hover:text-rose-400 border border-white/10 hover:border-rose-400/20 hover:bg-rose-500/10 transition-all"
|
||||||
>
|
>
|
||||||
{deletingId === domain.id ? (
|
{deletingId === domain.id ? (
|
||||||
<Loader2 className="w-4 h-4 animate-spin" />
|
<Loader2 className="w-3.5 h-3.5 animate-spin" />
|
||||||
) : (
|
) : (
|
||||||
<Trash2 className="w-4 h-4" />
|
<Trash2 className="w-3.5 h-3.5" />
|
||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user