import { ref, readonly } from "vue"; type Action = "copy" | "download" | "cart"; interface Stats { copies: number; downloads: number; carts: number; total: number; } // Global cache for fetched stats const statsCache = ref>({}); const isFetching = ref(false); // Declare GTM dataLayer declare global { interface Window { dataLayer: any[]; } } // Push event to GTM dataLayer function pushToGTM(event: string, data: Record) { if (typeof window !== "undefined" && window.dataLayer) { window.dataLayer.push({ event, ...data, }); } } // Track event to server (fire-and-forget) async function trackToServer(template: string, action: Action) { try { await fetch("/api/track", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ template, action }), }); } catch { // Silent fail - tracking is non-critical } } export function useUsageStats() { // Initialize stats for template if not in cache function ensureStats(name: string) { if (!statsCache.value[name]) { statsCache.value[name] = { copies: 0, downloads: 0, carts: 0, total: 0 }; } } // Track copy event function trackCopy(name: string) { trackToServer(name, "copy"); pushToGTM("template_copy", { template_name: name, action: "copy", }); ensureStats(name); statsCache.value[name].copies++; statsCache.value[name].total++; } // Track download event function trackDownload(name: string) { trackToServer(name, "download"); pushToGTM("template_download", { template_name: name, action: "download", }); ensureStats(name); statsCache.value[name].downloads++; statsCache.value[name].total++; } // Track cart add event function trackCart(name: string) { trackToServer(name, "cart"); pushToGTM("template_add_to_cart", { template_name: name, action: "add_to_cart", }); ensureStats(name); statsCache.value[name].carts++; statsCache.value[name].total++; } // Get stats for a template function getStats(name: string): Stats { return ( statsCache.value[name] || { copies: 0, downloads: 0, carts: 0, total: 0 } ); } // Fetch stats for multiple templates async function fetchStats(templates: string[]) { if (templates.length === 0 || isFetching.value) return; isFetching.value = true; try { const res = await fetch(`/api/stats?templates=${templates.join(",")}`); if (res.ok) { const data = await res.json(); for (const [name, stats] of Object.entries(data)) { statsCache.value[name] = stats as Stats; } } } catch { // Silent fail - stats are non-critical } finally { isFetching.value = false; } } // Fetch all stats (for leaderboard) async function fetchAllStats() { if (isFetching.value) return; isFetching.value = true; try { const res = await fetch("/api/stats"); if (res.ok) { const data = await res.json(); statsCache.value = data; } } catch { // Silent fail - stats are non-critical } finally { isFetching.value = false; } } // Format count for display (1.2k, 5.4k, etc.) function formatCount(count: number): string { if (count >= 1000) { return `${(count / 1000).toFixed(1)}k`; } return String(count); } return { stats: readonly(statsCache), trackCopy, trackDownload, trackCart, getStats, fetchStats, fetchAllStats, formatCount, }; }