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

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:
2025-12-18 14:14:37 +01:00
parent c41f870040
commit 202e615e2b
3 changed files with 55 additions and 22 deletions

View File

@ -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",

View File

@ -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

View File

@ -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>