109 lines
3.2 KiB
JavaScript
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();
|