File: //proc/self/root/tmp/ezdns_index.js
const apiBase = '';
const scansTbody = document.getElementById('scans');
const scanBtn = document.getElementById('scan');
const domainInput = document.getElementById('domain');
const API_KEY = 'dev-key';
const SUPPORT_URL = 'https://support.panomity.com/tickets/new/';
const ORDER_URLS = {
quick_fix: 'https://support.panomity.com/?cmd=cart&action=add&id=867',
cleanup: 'https://support.panomity.com/?cmd=cart&action=add&id=868',
managed_monitoring: 'https://support.panomity.com/?cmd=cart&action=add&id=856',
pro_monthly: 'https://support.panomity.com/?cmd=cart&action=add&id=854',
pro_yearly: 'https://support.panomity.com/?cmd=cart&action=add&id=854',
business_monthly: 'https://support.panomity.com/?cmd=cart&action=add&id=855',
business_yearly: 'https://support.panomity.com/?cmd=cart&action=add&id=855',
agency_monthly: 'https://support.panomity.com/?cmd=cart&action=add&id=856',
agency_yearly: 'https://support.panomity.com/?cmd=cart&action=add&id=856'
};
function trackEvent(category, action, name) {
if (window._paq) window._paq.push(['trackEvent', category, action, name || '']);
}
function currentDomainValue() {
return (domainInput && domainInput.value ? domainInput.value.trim().toLowerCase() : '');
}
async function recordLead(intent, plan) {
try {
await fetch('/v1/leads', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
source: 'website',
intent,
plan,
domain: currentDomainValue(),
page: window.location.pathname
})
});
} catch (e) { /* lead tracking must never block sales */ }
}
function supportTicketUrl(intent, plan) {
const params = new URLSearchParams();
params.set('subject', `ezdns.support ${intent}: ${plan}`);
if (currentDomainValue()) params.set('domain', currentDomainValue());
params.set('source', 'ezdns.support');
return `${SUPPORT_URL}?${params.toString()}`;
}
function orderUrlFor(plan) {
return ORDER_URLS[plan] || '';
}
async function requestService(plan, intent = 'service_lead') {
trackEvent('Revenue', intent, plan);
await recordLead(intent, plan);
const orderUrl = orderUrlFor(plan);
toast(orderUrl ? 'Warenkorb wird geöffnet…' : 'Anfrage wird geöffnet…', 'info', { timeoutMs: 1500 });
setTimeout(() => {
window.location.href = orderUrl || supportTicketUrl(intent, plan);
}, 250);
}
async function fetchJSON(url, opts={}) {
const headers = Object.assign({ 'x-api-key': API_KEY }, opts.headers||{});
const res = await fetch(url, Object.assign({}, opts, { headers }));
if (!res.ok) throw new Error('HTTP '+res.status);
return await res.json();
}
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
function toast(message, variant = 'info', opts = {}) {
const hostId = 'toastHost';
let host = document.getElementById(hostId);
if (!host) {
host = document.createElement('div');
host.id = hostId;
host.className = 'fixed top-4 right-4 z-50 flex flex-col gap-2';
document.body.appendChild(host);
}
const t = document.createElement('div');
const cls = variant === 'success'
? 'bg-emerald-900 text-emerald-100 border border-emerald-700'
: variant === 'error'
? 'bg-red-900 text-red-100 border border-red-700'
: 'bg-slate-900 text-slate-100 border border-slate-700';
t.className = `px-3 py-2 rounded shadow max-w-sm ${cls}`;
const row = document.createElement('div');
row.className = 'flex items-center gap-3';
const msg = document.createElement('div');
msg.className = 'text-sm';
msg.textContent = message;
row.appendChild(msg);
if (opts.actionHref && opts.actionLabel) {
const a = document.createElement('a');
a.href = opts.actionHref;
a.className = 'text-sm text-blue-200 hover:underline whitespace-nowrap';
a.textContent = opts.actionLabel;
row.appendChild(a);
}
t.appendChild(row);
host.appendChild(t);
const timeoutMs = Number.isFinite(opts.timeoutMs) ? opts.timeoutMs : 3500;
if (timeoutMs > 0) {
setTimeout(() => {
t.remove();
if (host && host.childElementCount === 0) host.remove();
}, timeoutMs);
}
return t;
}
const scanPollers = new Map();
async function pollScanUntilDone(scanId, { maxMs = 120000, intervalMs = 2000 } = {}) {
const start = Date.now();
while (Date.now() - start < maxMs) {
await sleep(intervalMs);
const scan = await fetchJSON(`${apiBase}/v1/scan/${scanId}`);
if (scan && scan.status === 'done') return scan;
}
throw new Error('timeout');
}
function row(status) {
const cls = status === 'done' ? 'bg-emerald-900 text-emerald-200' : 'bg-amber-900 text-amber-200';
return `<span class="px-2 py-0.5 rounded ${cls}">${status}</span>`;
}
function setActiveTab(tab) {
const buttons = document.querySelectorAll('.tab-btn');
const panels = document.querySelectorAll('.tab-panel');
for (const b of buttons) {
const isActive = b.dataset.tab === tab;
b.className = isActive
? 'tab-btn px-3 py-1.5 rounded-lg bg-slate-800 text-slate-100'
: 'tab-btn px-3 py-1.5 rounded-lg border border-slate-700 text-slate-200';
}
for (const p of panels) {
p.classList.toggle('hidden', p.id !== `tab-${tab}`);
}
}
async function loadScans() {
const data = await fetchJSON(`${apiBase}/v1/scans?limit=5`);
scansTbody.innerHTML = '';
for (const s of data.items) {
const tr = document.createElement('tr');
const domain = s.domain || s.domain_id;
tr.innerHTML = `
<td class="py-2 text-blue-300">${domain}</td>
<td>${row(s.status)}</td>
<td><a href="/app/detail.html?id=${s.id}" class="text-sm text-blue-400 hover:underline">Open</a></td>
`;
scansTbody.appendChild(tr);
}
}
// kein separates Detail-Panel mehr
const listedCheckbox = document.getElementById('listed');
scanBtn.addEventListener('click', async () => {
const d = domainInput.value.trim();
if (!d) return;
try {
scanBtn.disabled = true; scanBtn.textContent = 'Starting…';
const created = await fetchJSON(`${apiBase}/v1/scan`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ domain: d, listed: !!listedCheckbox.checked }) });
const scanId = created && created.id;
toast('Scan läuft… wir sagen Bescheid, sobald er fertig ist.', 'info', { timeoutMs: 4000 });
setActiveTab('scans');
await loadScans();
if (scanId && !scanPollers.has(scanId)) {
const p = pollScanUntilDone(scanId)
.then(async () => {
toast('Scan fertig.', 'success', { actionLabel: 'Report öffnen', actionHref: `/app/detail.html?id=${scanId}`, timeoutMs: 9000 });
await loadScans();
})
.catch(() => {
toast('Scan läuft noch – bitte in „Letzte Scans“ prüfen.', 'info', { timeoutMs: 6000 });
})
.finally(() => {
scanPollers.delete(scanId);
});
scanPollers.set(scanId, p);
}
} catch (e) {
toast('Scan konnte nicht gestartet werden.', 'error', { timeoutMs: 5000 });
} finally {
scanBtn.disabled = false; scanBtn.textContent = 'Scan';
}
});
// Pricing Configuration
const pricing = {
monthly: {
pro: { price: 29, name: 'Pro Monthly' },
business: { price: 99, name: 'Business Monthly' },
agency: { price: 199, name: 'Managed Monthly' }
},
yearly: {
pro: { price: 279, name: 'Pro Yearly' },
business: { price: 999, name: 'Business Yearly' },
agency: { price: 1999, name: 'Managed Yearly' }
}
};
// Direkte Stripe Payment Links können später gesetzt werden; live läuft Checkout über HostBill.
const stripeCheckoutLinks = {
pro_monthly: '',
pro_yearly: '',
business_monthly: '',
business_yearly: '',
agency_monthly: '',
agency_yearly: ''
};
// Checkout Funktion
function checkout(plan) {
if (plan === 'free') {
trackEvent('Revenue', 'free_plan_clicked', 'free');
domainInput.focus();
setActiveTab('why');
return;
}
const isYearly = document.getElementById('yearlyBilling') && document.getElementById('yearlyBilling').checked;
const actualPlan = isYearly ? plan.replace('monthly', 'yearly') : plan;
const checkoutUrl = stripeCheckoutLinks[actualPlan];
if (checkoutUrl && !checkoutUrl.includes('EXAMPLE')) {
trackEvent('Revenue', 'checkout_started', actualPlan);
recordLead('checkout_started', actualPlan);
toast('Du wirst zu Stripe Checkout weitergeleitet...', 'info', { timeoutMs: 2000 });
setTimeout(() => {
window.location.href = checkoutUrl;
}, 1000);
} else {
toast('Warenkorb wird geöffnet…', 'info', { timeoutMs: 1500 });
requestService(actualPlan, 'plan_inquiry');
}
}
// Jährlich/Monatlich Umschalten
function toggleYearly() {
const isYearly = document.getElementById('yearlyBilling') && document.getElementById('yearlyBilling').checked;
const monthlyPriceEl = document.getElementById('monthlyPrice');
if (isYearly) {
// 20% Rabatt: 29 * 0.8 = 23.2 ≈ €23
// 99 * 0.8 = 79.2 ≈ €79
// 199 * 0.8 = 159.2 ≈ €159
const monthlyPrices = { pro: 23, business: 79, agency: 159 };
// Aktualisiere alle Preis-Elemente
const priceEls = document.querySelectorAll('[data-plan]');
priceEls.forEach(el => {
const plan = el.dataset.plan;
if (monthlyPrices[plan]) {
el.textContent = '€' + monthlyPrices[plan];
}
});
if (monthlyPriceEl) {
monthlyPriceEl.textContent = '€' + monthlyPrices.pro;
}
toast('Jahresrabatt aktiv: Du sparst 20%', 'success', { timeoutMs: 2000 });
} else {
// Normale Preise
const monthlyPrices = { pro: 29, business: 99, agency: 199 };
// Aktualisiere alle Preis-Elemente
const priceEls = document.querySelectorAll('[data-plan]');
priceEls.forEach(el => {
const plan = el.dataset.plan;
if (monthlyPrices[plan]) {
el.textContent = '€' + monthlyPrices[plan];
}
});
if (monthlyPriceEl) {
monthlyPriceEl.textContent = '€' + monthlyPrices.pro;
}
}
}
// Tabs + initial state
document.querySelectorAll('.tab-btn').forEach((b) => {
b.addEventListener('click', () => setActiveTab(b.dataset.tab));
});
setActiveTab('why');
const cta = document.getElementById('ctaFocus');
if (cta) {
cta.addEventListener('click', () => {
trackEvent('Acquisition', 'scan_cta_clicked', 'hero');
domainInput.focus();
setActiveTab('scans');
});
}
const serviceCta = document.getElementById('serviceCta');
if (serviceCta) {
serviceCta.addEventListener('click', () => requestService('quick_fix', 'service_lead'));
}
loadScans();
// Footer year
document.getElementById('year').textContent = new Date().getFullYear();
// Matomo config + tracking
(async () => {
try {
await import('/config.js');
const cfg = window.__APP_CONFIG__ || {};
if (cfg.MATOMO_URL && cfg.MATOMO_SITE_ID) {
var _paq = (window._paq = window._paq || []);
_paq.push(['trackPageView']);
_paq.push(['enableLinkTracking']);
(function() {
var u = cfg.MATOMO_URL.endsWith('/') ? cfg.MATOMO_URL : cfg.MATOMO_URL + '/';
_paq.push(['setTrackerUrl', u + 'matomo.php']);
_paq.push(['setSiteId', cfg.MATOMO_SITE_ID]);
var d = document, g = d.createElement('script'), s = d.getElementsByTagName('script')[0];
g.async = true; g.src = u + 'matomo.js'; s.parentNode.insertBefore(g, s);
})();
}
} catch (e) { /* ignore */ }
})();