1
+ ---
2
+ // Client-side Hall Analytics Component
3
+ // This works with static hosting like AWS Amplify
4
+ const HALL_API_KEY = import .meta .env .HALL_API_KEY ;
5
+ ---
6
+
7
+ <script define:vars ={ { apiKey: HALL_API_KEY || ' ' }} >
8
+ // Hall Analytics Client-Side Tracking
9
+ (function() {
10
+ 'use strict';
11
+
12
+ const HALL_API_KEY = apiKey;
13
+
14
+ if (!HALL_API_KEY) {
15
+ return;
16
+ }
17
+
18
+ // Track page view when component loads
19
+ function trackPageView() {
20
+ const path = window.location.pathname + window.location.search;
21
+ const method = 'GET';
22
+ const timestamp = Date.now();
23
+
24
+ // Get IP from headers (will be client IP)
25
+ const requestHeaders = {
26
+ 'User-Agent': navigator.userAgent,
27
+ 'Host': window.location.host,
28
+ 'Referer': document.referrer,
29
+ 'Accept-Language': navigator.language,
30
+ 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
31
+ ...(navigator.userAgentData ? {
32
+ 'Sec-Ch-Ua-Mobile': navigator.userAgentData.mobile ? '?1' : '?0',
33
+ 'Sec-Ch-Ua-Platform': `"${navigator.userAgentData.platform}"`
34
+ } : {})
35
+ };
36
+
37
+ const analyticsData = {
38
+ request_path: path,
39
+ request_method: method,
40
+ request_ip: '127.0.0.1', // Client-side can't get real IP
41
+ request_headers: requestHeaders,
42
+ request_timestamp: timestamp
43
+ };
44
+
45
+ // Send to Hall Analytics
46
+ fetch('https://analytics.usehall.com/visit', {
47
+ method: 'POST',
48
+ headers: {
49
+ 'Content-Type': 'application/json',
50
+ 'Authorization': `Bearer ${HALL_API_KEY}`,
51
+ },
52
+ body: JSON.stringify(analyticsData),
53
+ })
54
+ .then(response => {
55
+ if (!response.ok) {
56
+ console.error('[Hall Analytics] Failed to track:', response.status);
57
+ }
58
+ })
59
+ .catch(error => {
60
+ console.error('[Hall Analytics] Network error:', error);
61
+ });
62
+ }
63
+
64
+ // Track initial page load
65
+ if (document.readyState === 'loading') {
66
+ document.addEventListener('DOMContentLoaded', trackPageView);
67
+ } else {
68
+ trackPageView();
69
+ }
70
+
71
+ // Store observers for potential cleanup
72
+ const observers = [];
73
+
74
+ // Also track when body becomes available (fallback)
75
+ if (!document.body) {
76
+ const bodyObserver = new MutationObserver(() => {
77
+ if (document.body) {
78
+ bodyObserver.disconnect();
79
+ trackPageView();
80
+ }
81
+ });
82
+ observers.push(bodyObserver);
83
+ bodyObserver.observe(document.documentElement, {
84
+ childList: true,
85
+ subtree: true
86
+ });
87
+ }
88
+
89
+ // Track navigation (for SPA-like behavior) - only if document.body exists
90
+ if (document.body) {
91
+ let currentPath = window.location.pathname;
92
+ const observer = new MutationObserver(() => {
93
+ if (window.location.pathname !== currentPath) {
94
+ currentPath = window.location.pathname;
95
+ setTimeout(trackPageView, 100); // Small delay to ensure new page is loaded
96
+ }
97
+ });
98
+ observers.push(observer);
99
+
100
+ observer.observe(document.body, {
101
+ childList: true,
102
+ subtree: true
103
+ });
104
+ }
105
+
106
+ // Cleanup on page unload
107
+ window.addEventListener('beforeunload', () => {
108
+ observers.forEach(observer => observer.disconnect());
109
+ });
110
+
111
+ })();
112
+ </script >
0 commit comments