Files
davila7__claude-code-templates/docs/js/event-tracker.js
daniel cf7e008c59 feat: Add monitoring points P1-P5 (installation outcomes, website events, API health)
- Create 3 Neon tables: installation_outcomes, website_events, api_health_logs
- Add API endpoints: track-installation-outcome, track-website-events, health-check
- Track CLI installation success/failure with timing and error classification
- Add website event tracker (search, cart, component views) with batched sendBeacon
- Add health-check cron every 15min with Discord alerts on failure
- Integrate tracking in all 6 installIndividual* functions with batchId support

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-10 22:08:34 -05:00

109 lines
3.2 KiB
JavaScript

// Event Tracker for Claude Code Templates Website
// Batches and sends website analytics events (search, cart, component views)
class EventTracker {
constructor() {
this.queue = [];
this.flushInterval = 30000; // 30 seconds
this.maxQueueSize = 20;
this.endpoint = '/api/track-website-events';
this.sessionId = this.getOrCreateSessionId();
this.visitorId = this.getOrCreateVisitorId();
this.screenWidth = window.screen?.width || null;
this.referrer = document.referrer || null;
this.timer = null;
this.startAutoFlush();
this.setupBeforeUnload();
}
getOrCreateSessionId() {
let id = sessionStorage.getItem('cct_session_id');
if (!id) {
id = this.generateId();
sessionStorage.setItem('cct_session_id', id);
}
return id;
}
getOrCreateVisitorId() {
let id = localStorage.getItem('cct_visitor_id');
if (!id) {
id = this.generateId();
localStorage.setItem('cct_visitor_id', id);
}
return id;
}
generateId() {
return Math.random().toString(36).substring(2, 15) +
Math.random().toString(36).substring(2, 15);
}
track(eventType, eventData) {
this.queue.push({
event_type: eventType,
event_data: eventData || {},
page_path: window.location.pathname,
timestamp: new Date().toISOString()
});
if (this.queue.length >= this.maxQueueSize) {
this.flush();
}
}
flush() {
if (this.queue.length === 0) return;
const events = this.queue.splice(0);
const payload = JSON.stringify({
events: events,
session_id: this.sessionId,
visitor_id: this.visitorId,
screen_width: this.screenWidth,
referrer: this.referrer
});
// Use sendBeacon for reliability (survives page unloads)
if (navigator.sendBeacon) {
const blob = new Blob([payload], { type: 'application/json' });
const sent = navigator.sendBeacon(this.endpoint, blob);
if (!sent) {
// Fallback to fetch if sendBeacon fails
this.sendViaFetch(payload);
}
} else {
this.sendViaFetch(payload);
}
}
sendViaFetch(payload) {
fetch(this.endpoint, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: payload,
keepalive: true
}).catch(() => {
// Silent failure - analytics should never break user experience
});
}
startAutoFlush() {
this.timer = setInterval(() => this.flush(), this.flushInterval);
}
setupBeforeUnload() {
window.addEventListener('beforeunload', () => this.flush());
// Also flush on visibility change (mobile tab switching)
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'hidden') {
this.flush();
}
});
}
}
// Initialize global instance
window.eventTracker = new EventTracker();