ux: remove TLD pricing section and keep simple top domains view
This commit is contained in:
parent
4a32b9b0fd
commit
b5d632e887
144
index.html
144
index.html
@ -34,36 +34,27 @@
|
||||
<body>
|
||||
<div class="wrap">
|
||||
<h1>Domain Hunter Panel</h1>
|
||||
<p class="muted">Dynamicznie z DB: domeny z Mongo + ceny rejestratorów zapisane na serwerze.</p>
|
||||
<p class="muted"><b>Ważne:</b> ceny dotyczą rozszerzenia (.pl/.com/.ai), a nie konkretnej nazwy domeny.</p>
|
||||
<p class="muted">Dynamicznie z DB: domeny z Mongo + status nowości w kolejnych skanach.</p>
|
||||
|
||||
<div class="grid">
|
||||
<div class="card">
|
||||
<h2>Filtr domen</h2>
|
||||
<div class="controls">
|
||||
<label>Szukaj <input id="q" placeholder="np. lead, ai, crm" /></label>
|
||||
<label>TLD
|
||||
<select id="tld"><option value="">Wszystkie</option><option>pl</option><option>com</option><option>ai</option></select>
|
||||
</label>
|
||||
<label class="small"><input type="checkbox" id="onlyNew" /> Tylko nowe domeny</label>
|
||||
<label class="small"><input type="checkbox" id="autoRefresh" checked /> Auto-refresh 60s</label>
|
||||
<button id="reload">Odśwież</button>
|
||||
</div>
|
||||
<p class="muted" id="meta"></p>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2>Cennik rejestratorów (TLD)</h2>
|
||||
<p class="muted">To cennik orientacyjny dla rozszerzeń (.pl/.com/.ai). Edycja zapisuje się na serwerze (SQLite), bez localStorage.</p>
|
||||
<div id="pricing"></div>
|
||||
<div class="card">
|
||||
<h2>Filtr domen</h2>
|
||||
<div class="controls">
|
||||
<label>Szukaj <input id="q" placeholder="np. lead, ai, crm" /></label>
|
||||
<label>TLD
|
||||
<select id="tld"><option value="">Wszystkie</option><option>pl</option><option>com</option><option>ai</option></select>
|
||||
</label>
|
||||
<label class="small"><input type="checkbox" id="onlyNew" /> Tylko nowe domeny</label>
|
||||
<label class="small"><input type="checkbox" id="autoRefresh" checked /> Auto-refresh 60s</label>
|
||||
<button id="reload">Odśwież</button>
|
||||
</div>
|
||||
<p class="muted" id="meta"></p>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2>TOP domen + szacowany koszt TLD</h2>
|
||||
<h2>TOP domen</h2>
|
||||
<div class="table-wrap">
|
||||
<table>
|
||||
<thead><tr><th>Domena</th><th>TLD</th><th>Score</th><th>Szacowany koszt 2 lata</th><th>Rejestrator</th></tr></thead>
|
||||
<thead><tr><th>Domena</th><th>TLD</th><th>Score</th><th>Status</th></tr></thead>
|
||||
<tbody id="rows"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
@ -72,58 +63,12 @@
|
||||
|
||||
<script>
|
||||
let domains = [];
|
||||
let registrarRows = [];
|
||||
let registrars = [];
|
||||
|
||||
function money(v){ return (v===null || v===undefined || Number.isNaN(Number(v))) ? '—' : `${Number(v).toFixed(2)} zł`; }
|
||||
|
||||
function groupPrices(items){
|
||||
const map = new Map();
|
||||
for(const x of items){
|
||||
if(!map.has(x.registrar)) map.set(x.registrar, {name:x.registrar, url:x.url, pricing:{}});
|
||||
const r = map.get(x.registrar);
|
||||
r.pricing[x.tld] = {register:x.register_price, renew:x.renew_price};
|
||||
}
|
||||
return [...map.values()];
|
||||
}
|
||||
|
||||
function priceFor(reg, tld, k){
|
||||
const p = reg.pricing?.[tld]?.[k];
|
||||
return (p===null || p===undefined || p==='') ? null : Number(p);
|
||||
}
|
||||
|
||||
function best(regs, tld, k){
|
||||
let b = null;
|
||||
for(const r of regs){
|
||||
const v = priceFor(r,tld,k);
|
||||
if(v === null || Number.isNaN(v)) continue;
|
||||
if(!b || v < b.value) b = {name:r.name,url:r.url,value:v};
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
function best2y(regs, tld){
|
||||
let b = null;
|
||||
for(const r of regs){
|
||||
const a = priceFor(r,tld,'register');
|
||||
const c = priceFor(r,tld,'renew');
|
||||
if(a===null || c===null || Number.isNaN(a) || Number.isNaN(c)) continue;
|
||||
const v = a+c;
|
||||
if(!b || v < b.value) b = {name:r.name,url:r.url,value:v};
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
function domainCheckUrl(registrarName, fallbackUrl, domain){
|
||||
function domainCheckUrl(domain){
|
||||
const d = encodeURIComponent(domain);
|
||||
const n = (registrarName || '').toLowerCase();
|
||||
if(n.includes('cyber')) return `https://cyberfolks.pl/domeny-rejestracja/?slowa=${d}`;
|
||||
if(n.includes('home.pl')) return `https://home.pl/szukaj/?query=${d}`;
|
||||
if(n.includes('nazwa')) return `https://www.nazwa.pl/sprawdz-domene/?domain=${d}`;
|
||||
if(n.includes('ovh')) return `https://www.ovhcloud.com/pl/domains/?domain=${d}`;
|
||||
if(n.includes('aftermarket')) return `https://aftermarket.pl/szukaj/${d}`;
|
||||
if(n.includes('porkbun')) return `https://porkbun.com/checkout/search?q=${d}`;
|
||||
return fallbackUrl || `https://www.whois.com/whois/${d}`;
|
||||
return `https://www.whois.com/whois/${d}`;
|
||||
}
|
||||
|
||||
async function loadDomains(){
|
||||
@ -140,73 +85,22 @@ async function loadDomains(){
|
||||
document.getElementById('meta').textContent = `runId: ${d.meta?.runId||'—'} | prev: ${d.meta?.prevRunId||'—'} | scannedAt: ${d.meta?.scannedAt||'—'} | nowe: ${d.meta?.newCount||0} | widoczne: ${domains.length}`;
|
||||
}
|
||||
|
||||
async function loadPrices(){
|
||||
const u = new URL('./api.php', location.href); u.searchParams.set('action','prices');
|
||||
const d = await (await fetch(u)).json();
|
||||
registrarRows = d.items || [];
|
||||
registrars = groupPrices(registrarRows);
|
||||
}
|
||||
|
||||
async function savePrice(registrar,tld,registerPrice,renewPrice){
|
||||
const u = new URL('./api.php', location.href); u.searchParams.set('action','prices');
|
||||
const r = await fetch(u, {
|
||||
method:'POST',
|
||||
headers:{'Content-Type':'application/json'},
|
||||
body: JSON.stringify({registrar,tld,register_price:registerPrice,renew_price:renewPrice})
|
||||
});
|
||||
return r.ok;
|
||||
}
|
||||
|
||||
function renderPricing(){
|
||||
const wrap = document.getElementById('pricing');
|
||||
wrap.innerHTML = '';
|
||||
for(const r of registrars){
|
||||
const row = document.createElement('div');
|
||||
row.style.marginBottom='8px';
|
||||
row.innerHTML = `<b>${r.name}</b> <a href="${r.url}" target="_blank">link</a><br>` + ['pl','com','ai'].map(tld=>{
|
||||
const a = priceFor(r,tld,'register');
|
||||
const b = priceFor(r,tld,'renew');
|
||||
return `<span class='pill'>.${tld} reg <input data-r='${r.name}' data-t='${tld}' data-k='register' value='${a??''}' /></span>
|
||||
<span class='pill'>odn <input data-r='${r.name}' data-t='${tld}' data-k='renew' value='${b??''}' /></span>`;
|
||||
}).join('');
|
||||
wrap.appendChild(row);
|
||||
}
|
||||
|
||||
wrap.querySelectorAll('input').forEach(inp=>{
|
||||
inp.addEventListener('change', async (e)=>{
|
||||
const {r,t,k} = e.target.dataset;
|
||||
const reg = registrars.find(x=>x.name===r);
|
||||
const value = e.target.value.trim()==='' ? null : Number(e.target.value.replace(',','.'));
|
||||
if(!reg.pricing[t]) reg.pricing[t] = {};
|
||||
reg.pricing[t][k] = Number.isFinite(value) ? value : null;
|
||||
const ok = await savePrice(r, t, reg.pricing[t].register ?? null, reg.pricing[t].renew ?? null);
|
||||
e.target.classList.toggle('ok', ok);
|
||||
e.target.classList.toggle('bad', !ok);
|
||||
renderRows();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function renderRows(){
|
||||
const rows = document.getElementById('rows');
|
||||
rows.innerHTML='';
|
||||
for(const d of domains.slice(0,300)){
|
||||
const two = best2y(registrars,d.tld);
|
||||
const tr = document.createElement('tr');
|
||||
const checkUrl = domainCheckUrl(two?.name, two?.url, d.domain);
|
||||
const checkUrl = domainCheckUrl(d.domain);
|
||||
tr.innerHTML = `<td><a href='${checkUrl}' target='_blank'><b>${d.domain}</b></a> ${d.is_new ? `<span class='chip'>NEW</span>` : ''}</td>
|
||||
<td>.${d.tld}</td>
|
||||
<td>${d.score}</td>
|
||||
<td>${two?money(two.value):'—'}</td>
|
||||
<td>${two?`<a href='${two.url}' target='_blank'>${two.name}</a>`:'brak danych'}</td>`;
|
||||
<td>${d.status || 'available'}</td>`;
|
||||
rows.appendChild(tr);
|
||||
}
|
||||
}
|
||||
|
||||
async function refreshAll(){
|
||||
await loadDomains();
|
||||
await loadPrices();
|
||||
renderPricing();
|
||||
renderRows();
|
||||
}
|
||||
|
||||
@ -219,8 +113,6 @@ function setupAutoRefresh(){
|
||||
if(enabled){
|
||||
autoTimer = setInterval(async()=>{
|
||||
await loadDomains();
|
||||
await loadPrices();
|
||||
renderPricing();
|
||||
renderRows();
|
||||
}, 60000);
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user