fix: feat: basic analytics funnel tracking for launch readiness (#1101)
Add self-hosted Umami analytics to replace the third-party cloud.umami.is tracker. Creates @harb/analytics package with typed event helpers and instruments the conversion funnel: CTA clicks (landing), wallet connect, swap initiated, and stake created (web-app). - Add Umami Docker service sharing existing postgres (separate DB) - Add Caddy /analytics route to proxy Umami dashboard - Configure via VITE_UMAMI_URL and VITE_UMAMI_WEBSITE_ID env vars - Document setup and funnel events in docs/ENVIRONMENT.md Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
8d67e61c17
commit
ca2bc03567
17 changed files with 195 additions and 6 deletions
8
packages/analytics/package.json
Normal file
8
packages/analytics/package.json
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"name": "@harb/analytics",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts"
|
||||
}
|
||||
69
packages/analytics/src/index.ts
Normal file
69
packages/analytics/src/index.ts
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
/**
|
||||
* Lightweight analytics wrapper for self-hosted Umami.
|
||||
*
|
||||
* Call `initAnalytics()` once at app startup to inject the tracker
|
||||
* script. All other helpers are safe to call at any time — if the
|
||||
* tracker hasn't loaded (ad-blocker, missing config, dev without
|
||||
* Umami running) every call is a silent no-op.
|
||||
*/
|
||||
|
||||
interface UmamiTracker {
|
||||
track: (name: string, data?: Record<string, string | number | boolean>) => void;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
umami?: UmamiTracker;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inject the Umami tracker script.
|
||||
* @param scriptUrl — full URL to `script.js`, e.g. `https://analytics.kraiken.org/script.js`
|
||||
* @param websiteId — Umami website ID (UUID)
|
||||
*/
|
||||
export function initAnalytics(scriptUrl: string | undefined, websiteId: string | undefined): void {
|
||||
if (!scriptUrl || !websiteId) return;
|
||||
if (typeof document === 'undefined') return;
|
||||
if (document.querySelector(`script[data-website-id="${websiteId}"]`)) return;
|
||||
|
||||
const el = document.createElement('script');
|
||||
el.defer = true;
|
||||
el.src = scriptUrl;
|
||||
el.dataset.websiteId = websiteId;
|
||||
document.head.appendChild(el);
|
||||
}
|
||||
|
||||
function getTracker(): UmamiTracker | undefined {
|
||||
return typeof window !== 'undefined' ? window.umami : undefined;
|
||||
}
|
||||
|
||||
/** Fire a named event with optional properties. */
|
||||
export function trackEvent(
|
||||
name: string,
|
||||
data?: Record<string, string | number | boolean>,
|
||||
): void {
|
||||
getTracker()?.track(name, data);
|
||||
}
|
||||
|
||||
/* ── Funnel events ─────────────────────────────────────────────── */
|
||||
|
||||
/** Landing page CTA clicked (e.g. "Get $KRK"). */
|
||||
export function trackCtaClick(label: string): void {
|
||||
trackEvent('cta_click', { label });
|
||||
}
|
||||
|
||||
/** Wallet successfully connected. */
|
||||
export function trackWalletConnect(): void {
|
||||
trackEvent('wallet_connect');
|
||||
}
|
||||
|
||||
/** Swap (buy/sell) initiated — user submitted the transaction. */
|
||||
export function trackSwapInitiated(direction: 'buy' | 'sell'): void {
|
||||
trackEvent('swap_initiated', { direction });
|
||||
}
|
||||
|
||||
/** Stake position created (snatch completed). */
|
||||
export function trackStakeCreated(): void {
|
||||
trackEvent('stake_created');
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue