rspace-online/modules/data/components/folk-analytics-view.ts

126 lines
5.1 KiB
TypeScript

/**
* folk-analytics-view — Privacy-first analytics dashboard overview.
*
* Shows tracked apps, stats, and a link to the full Umami dashboard.
*/
class FolkAnalyticsView extends HTMLElement {
private shadow: ShadowRoot;
private space = "demo";
private stats: any = null;
constructor() {
super();
this.shadow = this.attachShadow({ mode: "open" });
}
connectedCallback() {
this.space = this.getAttribute("space") || "demo";
this.loadStats();
}
private async loadStats() {
try {
const base = window.location.pathname.replace(/\/$/, "");
const resp = await fetch(`${base}/api/stats`);
if (resp.ok) {
this.stats = await resp.json();
}
} catch { /* ignore */ }
this.render();
}
private render() {
const stats = this.stats || { trackedApps: 17, cookiesSet: 0, scriptSize: "~2KB", selfHosted: true, apps: [], dashboardUrl: "https://analytics.rspace.online" };
this.shadow.innerHTML = `
<style>
:host { display: block; min-height: 60vh; font-family: system-ui, sans-serif; color: #e2e8f0; }
.container { max-width: 800px; margin: 0 auto; }
.hero { text-align: center; margin-bottom: 2rem; }
.hero h2 { font-size: 1.75rem; font-weight: 700; margin-bottom: 0.5rem; }
.hero p { color: #94a3b8; max-width: 480px; margin: 0 auto; }
.stats-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1rem; margin-bottom: 2rem; }
.stat { text-align: center; background: rgba(15,23,42,0.5); border: 1px solid #1e293b; border-radius: 12px; padding: 1.25rem; }
.stat-value { font-size: 1.75rem; font-weight: 700; color: #22d3ee; }
.stat-label { font-size: 0.75rem; color: #64748b; margin-top: 0.25rem; }
.pillars { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; margin-bottom: 2rem; }
.pillar { background: rgba(15,23,42,0.5); border: 1px solid #1e293b; border-radius: 12px; padding: 1.5rem; }
.pillar-icon { width: 40px; height: 40px; border-radius: 8px; display: flex; align-items: center; justify-content: center; font-size: 1rem; font-weight: 700; margin-bottom: 0.75rem; }
.pillar-icon.zk { background: rgba(34,211,238,0.1); color: #22d3ee; }
.pillar-icon.lf { background: rgba(129,140,248,0.1); color: #818cf8; }
.pillar-icon.sh { background: rgba(52,211,153,0.1); color: #34d399; }
.pillar h3 { font-size: 1rem; font-weight: 600; margin-bottom: 0.5rem; }
.pillar p { font-size: 0.85rem; color: #94a3b8; line-height: 1.5; }
.apps-section { margin-bottom: 2rem; }
.apps-title { text-align: center; font-size: 1.25rem; font-weight: 600; margin-bottom: 1rem; }
.apps-grid { display: flex; flex-wrap: wrap; gap: 0.5rem; justify-content: center; }
.app-chip { padding: 0.35rem 0.75rem; background: rgba(15,23,42,0.5); border: 1px solid #1e293b; border-radius: 20px; font-size: 0.8rem; color: #94a3b8; }
.cta { text-align: center; padding: 2rem; border-top: 1px solid #1e293b; }
.cta a { display: inline-block; padding: 0.75rem 2rem; background: #22d3ee; color: #0f172a; border-radius: 8px; font-weight: 600; text-decoration: none; }
.cta a:hover { opacity: 0.85; }
@media (max-width: 768px) {
.stats-grid { grid-template-columns: repeat(2, 1fr); }
.pillars { grid-template-columns: 1fr; }
}
</style>
<div class="container">
<div class="hero">
<h2>Privacy-First Analytics</h2>
<p>Zero-knowledge, cookieless, self-hosted analytics for the r* ecosystem. Know how your tools are used without compromising anyone's privacy.</p>
</div>
<div class="stats-grid">
<div class="stat">
<div class="stat-value">${stats.trackedApps}</div>
<div class="stat-label">Apps Tracked</div>
</div>
<div class="stat">
<div class="stat-value">${stats.cookiesSet}</div>
<div class="stat-label">Cookies Set</div>
</div>
<div class="stat">
<div class="stat-value">${stats.scriptSize}</div>
<div class="stat-label">Script Size</div>
</div>
<div class="stat">
<div class="stat-value">100%</div>
<div class="stat-label">Self-Hosted</div>
</div>
</div>
<div class="pillars">
<div class="pillar">
<div class="pillar-icon zk">ZK</div>
<h3>Zero-Knowledge Privacy</h3>
<p>No cookies. No fingerprinting. No personal data. Each page view is anonymous. GDPR compliant by architecture.</p>
</div>
<div class="pillar">
<div class="pillar-icon lf">LF</div>
<h3>Local-First Data</h3>
<p>Analytics data never leaves your infrastructure. No third-party servers, no cloud dependencies.</p>
</div>
<div class="pillar">
<div class="pillar-icon sh">SH</div>
<h3>Self-Hosted</h3>
<p>Full control over data retention, access, and lifecycle. Powered by Umami.</p>
</div>
</div>
<div class="apps-section">
<div class="apps-title">Tracking the r* Ecosystem</div>
<div class="apps-grid">
${(stats.apps || []).map((a: string) => `<span class="app-chip">${a}</span>`).join("")}
</div>
</div>
<div class="cta">
<a href="${stats.dashboardUrl}" target="_blank" rel="noopener">Open Dashboard</a>
</div>
</div>
`;
}
}
customElements.define("folk-analytics-view", FolkAnalyticsView);