import { mountLogo } from "./ds/logo.js"; const app = document.querySelector("#app"), npub = new URLSearchParams(location.search).get("npub")?.trim() || ""; const esc = (value) => String(value ?? "").replace( /[&<>"']/g, (char) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[ char ], ); const compact = (value) => new Intl.NumberFormat(undefined, { notation: "compact", maximumFractionDigits: 1, }).format(Number(value || 0)); const sats = (value) => `${Number(value || 0).toLocaleString()} sats`, euro = (value) => new Intl.NumberFormat(undefined, { style: "currency", currency: "EUR", }).format(Number(value || 0) / 100), pct = (value) => (value == null ? "—" : `${(value * 100).toFixed(1)}%`); function chart(rows) { const w = 900, h = 250, p = 18, max = Math.max( 1, ...rows.flatMap((row) => [row.pageViews, row.interactions]), ); const points = (key) => rows .map( (row, i) => `${p + (i / Math.max(1, rows.length - 1)) * (w - p * 2)},${h - p - (Number(row[key] || 0) / max) * (h - p * 2)}`, ) .join(" "); return `
${esc(rows[0]?.date || "")}${esc(rows.at(-1)?.date || "")}
`; } function render(data) { const m = data.merchant, a = data.analytics || {}; document.title = `${m.name} · Stats`; app.innerHTML = `
Just WorksStatsPublic preview · Nostr login follows

Merchant intelligence · ${data.periodDays} days

${esc(m.name)}

${esc(m.npub)}

Back to Business ↙
${[ ["Routed views", compact(a.pageViews)], ["Interactions", compact(a.interactions)], ["Engagement", pct(a.engagementRate)], ["Revenue", euro(a.revenueCents)], ["Settled sats", sats(a.paymentSats)], ["Paid", a.paidPayments || 0], ] .map( ([label, value]) => `
${label}${value}
`, ) .join( "", )}

Audience & intent

What moves people.

ViewsInteractions
${chart(a.daily || [])}

Business outcomes

From attention to action.

${[ ["Orders", a.orders || 0], ["Bookings", a.bookings || 0], ["Payment attempts", a.paymentAttempts || 0], ["Payment conversion", pct(a.paymentConversionRate)], ["Public tips", sats(m.publicTipSats)], ] .map( ([label, value]) => `
${label}${value}
`, ) .join( "", )}

Top actions

What customers do.

${a.actions?.length ? a.actions.map((item) => `
${esc(item.action.replaceAll("_", " "))}${item.count}
`).join("") : '

No tracked interactions yet. Collection starts with the analytics release.

'}

Modules

Product footprint.

${m.modules?.map((item) => `
${esc(item.id)}${item.enabled ? "On" : "Off"} · ${esc(item.status)}
`).join("") || '

No Business modules configured.

'}
`; document .querySelectorAll("[data-logo]") .forEach((node) => mountLogo(node, { icon: true })); } function fail(message) { app.innerHTML = `
∩

Stats unavailable

${esc(message)}

Back to Business
`; } if (!npub) fail("Add a merchant npub to open its stats."); else fetch( `https://justworks.cash/api/merchant-stats?npub=${encodeURIComponent(npub)}`, { cache: "no-store" }, ) .then(async (response) => { const data = await response.json(); if (!response.ok) throw new Error(data.error || "Stats unavailable"); render(data); }) .catch((error) => fail(error.message));