@stacksjs/ts-analytics 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (237) hide show
  1. package/CHANGELOG.md +68 -0
  2. package/LICENSE.md +21 -0
  3. package/README.md +361 -0
  4. package/bin/cli.ts +169 -0
  5. package/dist/Analytics.d.ts +558 -0
  6. package/dist/api.d.ts +109 -0
  7. package/dist/batching.d.ts +93 -0
  8. package/dist/chunk-2mx7fq49.js +4 -0
  9. package/dist/chunk-3z29508k.js +204 -0
  10. package/dist/chunk-deephkz6.js +56 -0
  11. package/dist/chunk-j261vgyp.js +305 -0
  12. package/dist/chunk-xga17tz7.js +207 -0
  13. package/dist/config.d.ts +132 -0
  14. package/dist/dashboard/components/index.d.ts +80 -0
  15. package/dist/dashboard/composables/useAnalytics.d.ts +99 -0
  16. package/dist/dashboard/index.d.ts +110 -0
  17. package/dist/dashboard/types/index.d.ts +144 -0
  18. package/dist/dashboard/utils/index.d.ts +81 -0
  19. package/dist/dynamodb.d.ts +86 -0
  20. package/dist/funnels.d.ts +104 -0
  21. package/dist/geolocation.d.ts +96 -0
  22. package/dist/index.d.ts +371 -0
  23. package/dist/index.js +10211 -0
  24. package/dist/infrastructure/cdk.d.ts +60 -0
  25. package/dist/infrastructure/cloudformation.d.ts +44 -0
  26. package/dist/infrastructure/index.d.ts +45 -0
  27. package/dist/infrastructure/setup.d.ts +133 -0
  28. package/dist/integrations/cloudflare.d.ts +76 -0
  29. package/dist/integrations/hono.d.ts +60 -0
  30. package/dist/integrations/index.d.ts +24 -0
  31. package/dist/integrations/nuxt.d.ts +7 -0
  32. package/dist/integrations/nuxt.js +42 -0
  33. package/dist/integrations/runtime/use-ts-analytics.d.ts +18 -0
  34. package/dist/integrations/runtime/use-ts-analytics.js +17 -0
  35. package/dist/integrations/stx.d.ts +88 -0
  36. package/dist/integrations/stx.js +18 -0
  37. package/dist/lib/crypto-random.d.ts +4 -0
  38. package/dist/lib/salt.d.ts +16 -0
  39. package/dist/local.d.ts +56 -0
  40. package/dist/model-connector.d.ts +145 -0
  41. package/dist/models/AggregatedStats.d.ts +9 -0
  42. package/dist/models/CampaignStats.d.ts +9 -0
  43. package/dist/models/Conversion.d.ts +11 -0
  44. package/dist/models/CustomEvent.d.ts +11 -0
  45. package/dist/models/DeviceStats.d.ts +9 -0
  46. package/dist/models/EventStats.d.ts +9 -0
  47. package/dist/models/GeoStats.d.ts +9 -0
  48. package/dist/models/Goal.d.ts +9 -0
  49. package/dist/models/GoalStats.d.ts +9 -0
  50. package/dist/models/PageStats.d.ts +11 -0
  51. package/dist/models/PageView.d.ts +13 -0
  52. package/dist/models/RealtimeStats.d.ts +9 -0
  53. package/dist/models/ReferrerStats.d.ts +9 -0
  54. package/dist/models/Session.d.ts +11 -0
  55. package/dist/models/Site.d.ts +11 -0
  56. package/dist/models/index.d.ts +28 -0
  57. package/dist/models/types.d.ts +60 -0
  58. package/dist/sqs-buffering.d.ts +243 -0
  59. package/dist/stacks-integration.d.ts +159 -0
  60. package/dist/tracking-script.d.ts +71 -0
  61. package/dist/tracking.d.ts +17 -0
  62. package/dist/tracking.js +26 -0
  63. package/dist/types.d.ts +595 -0
  64. package/dist/utils/geolocation.d.ts +111 -0
  65. package/dist/utils/user-agent.d.ts +17 -0
  66. package/dist/version.d.ts +7 -0
  67. package/package.json +119 -0
  68. package/src/Analytics.ts +3349 -0
  69. package/src/api.ts +1286 -0
  70. package/src/assets/crosswind.css +2220 -0
  71. package/src/batching.ts +452 -0
  72. package/src/components/dashboard/index.ts +18 -0
  73. package/src/config.ts +456 -0
  74. package/src/dashboard/Dashboard.stx +1517 -0
  75. package/src/dashboard/components/AlertCard.stx +177 -0
  76. package/src/dashboard/components/AnalyticsDashboard.stx +354 -0
  77. package/src/dashboard/components/AnimatedNumber.stx +86 -0
  78. package/src/dashboard/components/BarChart.stx +220 -0
  79. package/src/dashboard/components/BrowserBreakdown.stx +98 -0
  80. package/src/dashboard/components/BrowsersTable.stx +125 -0
  81. package/src/dashboard/components/CampaignBreakdown.stx +163 -0
  82. package/src/dashboard/components/CampaignTable.stx +238 -0
  83. package/src/dashboard/components/CountryList.stx +101 -0
  84. package/src/dashboard/components/DataTable.stx +226 -0
  85. package/src/dashboard/components/DateRangePicker.stx +77 -0
  86. package/src/dashboard/components/DeviceBreakdown.stx +94 -0
  87. package/src/dashboard/components/DevicesTable.stx +163 -0
  88. package/src/dashboard/components/DonutChart.stories.ts +55 -0
  89. package/src/dashboard/components/DonutChart.stx +157 -0
  90. package/src/dashboard/components/EmptyState.stx +90 -0
  91. package/src/dashboard/components/EngagementMetrics.stx +183 -0
  92. package/src/dashboard/components/EventsSection.stx +192 -0
  93. package/src/dashboard/components/FilterBar.stx +104 -0
  94. package/src/dashboard/components/FullAnalyticsDashboard.stx +455 -0
  95. package/src/dashboard/components/FunnelChart.stx +142 -0
  96. package/src/dashboard/components/GeoTable.stx +337 -0
  97. package/src/dashboard/components/GoalsPanel.stx +109 -0
  98. package/src/dashboard/components/Header.stx +39 -0
  99. package/src/dashboard/components/HeatmapChart.stx +140 -0
  100. package/src/dashboard/components/LiveActivityFeed.stx +172 -0
  101. package/src/dashboard/components/MetricComparison.stx +106 -0
  102. package/src/dashboard/components/MiniStats.stx +96 -0
  103. package/src/dashboard/components/OSBreakdown.stx +124 -0
  104. package/src/dashboard/components/PageDetailCard.stx +102 -0
  105. package/src/dashboard/components/PagesTable.stx +127 -0
  106. package/src/dashboard/components/ProgressRing.stx +89 -0
  107. package/src/dashboard/components/RealtimeCounter.stories.ts +45 -0
  108. package/src/dashboard/components/RealtimeCounter.stx +46 -0
  109. package/src/dashboard/components/ReferrersTable.stx +180 -0
  110. package/src/dashboard/components/SparklineChart.stx +160 -0
  111. package/src/dashboard/components/StatCard.stories.ts +58 -0
  112. package/src/dashboard/components/StatCard.stx +90 -0
  113. package/src/dashboard/components/SummaryStats.stx +81 -0
  114. package/src/dashboard/components/TabNav.stx +66 -0
  115. package/src/dashboard/components/ThemeSwitcher.stx +124 -0
  116. package/src/dashboard/components/TimeSeriesChart.stx +106 -0
  117. package/src/dashboard/components/TopList.stories.ts +58 -0
  118. package/src/dashboard/components/TopList.stx +74 -0
  119. package/src/dashboard/components/TrendIndicator.stx +84 -0
  120. package/src/dashboard/components/heatmap/ClickHeatmap.stx +264 -0
  121. package/src/dashboard/components/heatmap/ElementClickList.stx +125 -0
  122. package/src/dashboard/components/heatmap/HeatmapControls.stx +125 -0
  123. package/src/dashboard/components/heatmap/HeatmapLegend.stx +69 -0
  124. package/src/dashboard/components/heatmap/PageHeatmap.stx +264 -0
  125. package/src/dashboard/components/heatmap/ScrollHeatmap.stx +127 -0
  126. package/src/dashboard/components/heatmap/index.ts +12 -0
  127. package/src/dashboard/components/index.ts +86 -0
  128. package/src/dashboard/composables/useAnalytics.ts +465 -0
  129. package/src/dashboard/demo/DemoApp.stx +370 -0
  130. package/src/dashboard/demo/index.ts +8 -0
  131. package/src/dashboard/demo/mockData.ts +234 -0
  132. package/src/dashboard/index.ts +117 -0
  133. package/src/dashboard/stx-shim.d.ts +4 -0
  134. package/src/dashboard/types/index.ts +203 -0
  135. package/src/dashboard/utils/index.ts +426 -0
  136. package/src/dynamodb.ts +344 -0
  137. package/src/funnels.ts +534 -0
  138. package/src/geolocation.ts +515 -0
  139. package/src/handlers/alerts.ts +328 -0
  140. package/src/handlers/annotations.ts +128 -0
  141. package/src/handlers/api-keys.ts +240 -0
  142. package/src/handlers/auth.ts +1020 -0
  143. package/src/handlers/authz.ts +137 -0
  144. package/src/handlers/collect.ts +724 -0
  145. package/src/handlers/data.ts +625 -0
  146. package/src/handlers/experiments.ts +138 -0
  147. package/src/handlers/funnels.ts +216 -0
  148. package/src/handlers/goals.ts +218 -0
  149. package/src/handlers/heatmaps.ts +272 -0
  150. package/src/handlers/index.ts +56 -0
  151. package/src/handlers/lib/read-cache.ts +63 -0
  152. package/src/handlers/misc.ts +486 -0
  153. package/src/handlers/oauth.ts +233 -0
  154. package/src/handlers/performance.ts +388 -0
  155. package/src/handlers/sessions.ts +413 -0
  156. package/src/handlers/sharing.ts +198 -0
  157. package/src/handlers/stats.ts +1368 -0
  158. package/src/handlers/team.ts +161 -0
  159. package/src/handlers/uptime.ts +283 -0
  160. package/src/handlers/views.ts +390 -0
  161. package/src/handlers/webhooks.ts +226 -0
  162. package/src/heatmap/index.ts +32 -0
  163. package/src/heatmap/tracking-script.ts +452 -0
  164. package/src/heatmap/types.ts +79 -0
  165. package/src/index.ts +387 -0
  166. package/src/infrastructure/cdk.ts +496 -0
  167. package/src/infrastructure/cloudformation.ts +595 -0
  168. package/src/infrastructure/index.ts +48 -0
  169. package/src/infrastructure/setup.ts +611 -0
  170. package/src/integrations/cloudflare.ts +732 -0
  171. package/src/integrations/hono.ts +589 -0
  172. package/src/integrations/index.ts +27 -0
  173. package/src/integrations/nuxt.ts +78 -0
  174. package/src/integrations/runtime/use-ts-analytics.ts +32 -0
  175. package/src/integrations/stx.ts +138 -0
  176. package/src/jobs/index.ts +127 -0
  177. package/src/lib/crypto-random.ts +41 -0
  178. package/src/lib/ddb-errors.ts +20 -0
  179. package/src/lib/dynamodb.ts +216 -0
  180. package/src/lib/email.ts +38 -0
  181. package/src/lib/ga-import.ts +471 -0
  182. package/src/lib/ga4-api.ts +244 -0
  183. package/src/lib/goals.ts +205 -0
  184. package/src/lib/index.ts +6 -0
  185. package/src/lib/ingest-counters.ts +123 -0
  186. package/src/lib/log.ts +36 -0
  187. package/src/lib/membership.ts +98 -0
  188. package/src/lib/plans.ts +90 -0
  189. package/src/lib/quota.ts +46 -0
  190. package/src/lib/rate-limit.ts +27 -0
  191. package/src/lib/rollups.ts +472 -0
  192. package/src/lib/salt.ts +97 -0
  193. package/src/lib/scheduler.ts +97 -0
  194. package/src/lib/significance.ts +66 -0
  195. package/src/lib/site-retention.ts +56 -0
  196. package/src/local.ts +360 -0
  197. package/src/model-connector.ts +616 -0
  198. package/src/models/AggregatedStats.ts +162 -0
  199. package/src/models/CampaignStats.ts +141 -0
  200. package/src/models/Conversion.ts +135 -0
  201. package/src/models/CustomEvent.ts +123 -0
  202. package/src/models/DeviceStats.ts +105 -0
  203. package/src/models/EventStats.ts +108 -0
  204. package/src/models/GeoStats.ts +112 -0
  205. package/src/models/Goal.ts +105 -0
  206. package/src/models/GoalStats.ts +106 -0
  207. package/src/models/PageStats.ts +152 -0
  208. package/src/models/PageView.ts +277 -0
  209. package/src/models/RealtimeStats.ts +96 -0
  210. package/src/models/ReferrerStats.ts +115 -0
  211. package/src/models/Session.ts +235 -0
  212. package/src/models/Site.ts +115 -0
  213. package/src/models/index.ts +50 -0
  214. package/src/models/orm/index.ts +2043 -0
  215. package/src/models/types.ts +71 -0
  216. package/src/router.ts +521 -0
  217. package/src/sqs-buffering.ts +806 -0
  218. package/src/stacks-integration.ts +527 -0
  219. package/src/tracking-script.ts +944 -0
  220. package/src/tracking.ts +27 -0
  221. package/src/types/analytics.ts +219 -0
  222. package/src/types/api.ts +75 -0
  223. package/src/types/bun-router.d.ts +15 -0
  224. package/src/types/dashboard.ts +62 -0
  225. package/src/types/index.ts +66 -0
  226. package/src/types/stx.d.ts +27 -0
  227. package/src/types/window.d.ts +101 -0
  228. package/src/types.ts +911 -0
  229. package/src/utils/cache.ts +148 -0
  230. package/src/utils/date.ts +118 -0
  231. package/src/utils/filters.ts +71 -0
  232. package/src/utils/geolocation.ts +323 -0
  233. package/src/utils/index.ts +9 -0
  234. package/src/utils/response.ts +180 -0
  235. package/src/utils/timezone-country.ts +242 -0
  236. package/src/utils/user-agent.ts +110 -0
  237. package/src/version.ts +7 -0
@@ -0,0 +1,264 @@
1
+ <script>
2
+ /**
3
+ * PageHeatmap Component
4
+ *
5
+ * Main container for page heatmap visualization with iframe/screenshot
6
+ * and overlay controls.
7
+ */
8
+ export const siteId = $props.siteId
9
+ export const pageUrl = $props.pageUrl
10
+ export const apiEndpoint = $props.apiEndpoint || ''
11
+ export const screenshotUrl = $props.screenshotUrl
12
+ export const initialStartDate = $props.initialStartDate
13
+ export const initialEndDate = $props.initialEndDate
14
+
15
+ // Use window.apiPath if available, otherwise define a local one
16
+ const apiPath = window.apiPath || ((path) => {
17
+ const urlParams = new URLSearchParams(window.location.search)
18
+ const USE_STEALTH = urlParams.get('stealth') !== 'false'
19
+ if (!USE_STEALTH) return path
20
+ const STEALTH_MAP = { sites: 'projects', heatmap: 'touch' }
21
+ let result = path.replace('/api/sites/', '/api/p/')
22
+ Object.keys(STEALTH_MAP).forEach(k => {
23
+ result = result.replace(new RegExp(`/${k}(?=/|\\?|$)`, 'g'), `/${STEALTH_MAP[k]}`)
24
+ })
25
+ return result
26
+ })
27
+
28
+ // State
29
+ let viewMode = 'click'
30
+ let deviceFilter = 'all'
31
+ let startDate = initialStartDate || new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString().slice(0, 10)
32
+ let endDate = initialEndDate || new Date().toISOString().slice(0, 10)
33
+ let loading = false
34
+ let error = null
35
+
36
+ // Heatmap data
37
+ let clickData = []
38
+ let topElements = []
39
+ let totalClicks = 0
40
+ let scrollData = []
41
+ let avgScrollDepth = 0
42
+ let totalScrollSessions = 0
43
+
44
+ // Container dimensions
45
+ let containerWidth = 800
46
+ let containerHeight = 600
47
+
48
+ const apiBaseUrl = apiPath(`${apiEndpoint}/api/sites/${siteId}/heatmap`)
49
+
50
+ // Fetch click heatmap data
51
+ export async function fetchClickData() {
52
+ loading = true
53
+ error = null
54
+
55
+ try {
56
+ const params = new URLSearchParams({
57
+ path: pageUrl,
58
+ startDate: new Date(startDate).toISOString(),
59
+ endDate: new Date(endDate).toISOString(),
60
+ device: deviceFilter,
61
+ })
62
+
63
+ const response = await fetch(`${apiBaseUrl}/clicks?${params}`)
64
+ if (!response.ok) throw new Error('Failed to fetch click data')
65
+
66
+ const data = await response.json()
67
+ clickData = data.clicks || []
68
+ topElements = data.topElements || []
69
+ totalClicks = data.totalClicks || 0
70
+ } catch (err) {
71
+ error = err.message || 'Unknown error'
72
+ console.error('Heatmap fetch error:', err)
73
+ } finally {
74
+ loading = false
75
+ }
76
+ }
77
+
78
+ // Fetch scroll heatmap data
79
+ export async function fetchScrollData() {
80
+ loading = true
81
+ error = null
82
+
83
+ try {
84
+ const params = new URLSearchParams({
85
+ path: pageUrl,
86
+ startDate: new Date(startDate).toISOString(),
87
+ endDate: new Date(endDate).toISOString(),
88
+ device: deviceFilter,
89
+ })
90
+
91
+ const response = await fetch(`${apiBaseUrl}/scroll?${params}`)
92
+ if (!response.ok) throw new Error('Failed to fetch scroll data')
93
+
94
+ const data = await response.json()
95
+ scrollData = data.scrollDepths || []
96
+ avgScrollDepth = data.avgMaxDepth || 0
97
+ totalScrollSessions = data.totalSessions || 0
98
+ } catch (err) {
99
+ error = err.message || 'Unknown error'
100
+ console.error('Scroll data fetch error:', err)
101
+ } finally {
102
+ loading = false
103
+ }
104
+ }
105
+
106
+ // Fetch data based on view mode
107
+ export async function fetchData() {
108
+ if (viewMode === 'click') {
109
+ await fetchClickData()
110
+ } else if (viewMode === 'scroll') {
111
+ await fetchScrollData()
112
+ }
113
+ }
114
+
115
+ // Handle view mode change
116
+ export function handleViewModeChange(mode) {
117
+ viewMode = mode
118
+ fetchData()
119
+ }
120
+
121
+ // Handle device filter change
122
+ export function handleDeviceChange(device) {
123
+ deviceFilter = device
124
+ fetchData()
125
+ }
126
+
127
+ // Handle date range change
128
+ export function handleDateRangeChange(range) {
129
+ startDate = range.start.toISOString().slice(0, 10)
130
+ endDate = range.end.toISOString().slice(0, 10)
131
+ fetchData()
132
+ }
133
+
134
+ // Handle element highlight
135
+ export function handleHighlight(selector) {
136
+ console.log('Highlight:', selector)
137
+ }
138
+
139
+ // Handle element click
140
+ export function handleElementClick(element) {
141
+ console.log('Element clicked:', element)
142
+ }
143
+
144
+ // Handle refresh
145
+ export function handleRefresh() {
146
+ fetchData()
147
+ }
148
+
149
+ // Initialize
150
+ if (typeof window !== 'undefined') {
151
+ setTimeout(fetchData, 0)
152
+ }
153
+
154
+ export { viewMode, deviceFilter, startDate, endDate, loading, error, clickData, topElements, totalClicks, scrollData, avgScrollDepth, totalScrollSessions, containerWidth, containerHeight }
155
+ </script>
156
+
157
+ <div class="page-heatmap flex flex-col gap-4 h-full">
158
+ <!-- Header -->
159
+ <div class="flex justify-between items-center">
160
+ <div class="flex flex-col gap-1">
161
+ <h2 class="text-xl font-semibold text-gray-900">Page Heatmap</h2>
162
+ <span class="text-sm text-gray-500 font-mono">{{ pageUrl }}</span>
163
+ </div>
164
+ </div>
165
+
166
+ <!-- Controls -->
167
+ @include('heatmap/HeatmapControls', {
168
+ viewMode: viewMode,
169
+ deviceFilter: deviceFilter,
170
+ startDate: startDate,
171
+ endDate: endDate,
172
+ loading: loading,
173
+ onViewModeChange: handleViewModeChange,
174
+ onDeviceChange: handleDeviceChange,
175
+ onDateRangeChange: handleDateRangeChange,
176
+ onRefresh: handleRefresh
177
+ })
178
+
179
+ <!-- Error state -->
180
+ @if(error)
181
+ <div class="flex items-center gap-3 px-4 py-3 bg-red-50 border border-red-200 rounded-lg text-red-600 text-sm">
182
+ <Icon name="alert-circle" size="20" class="flex-shrink-0" />
183
+ <span>{{ error }}</span>
184
+ <button type="button" class="ml-auto px-3 py-1 text-xs font-medium text-red-600 bg-white border border-red-200 rounded hover:bg-red-50" @click="fetchData">
185
+ Retry
186
+ </button>
187
+ </div>
188
+ @endif
189
+
190
+ <!-- Main content -->
191
+ <div class="grid grid-cols-1 lg:grid-cols-[1fr_320px] gap-6 flex-1 min-h-0">
192
+ <!-- Visualization area -->
193
+ <div class="flex flex-col gap-4 bg-white rounded-lg border border-gray-200 p-4 overflow-hidden">
194
+ <!-- Click heatmap view -->
195
+ @if(viewMode === 'click')
196
+ <div class="relative flex-1 min-h-[400px] border border-gray-200 rounded-md overflow-hidden bg-gray-50">
197
+ <!-- Page iframe or screenshot -->
198
+ @if(!screenshotUrl)
199
+ <iframe
200
+ src="{{ pageUrl }}"
201
+ class="w-full h-full border-none pointer-events-none"
202
+ sandbox="allow-same-origin"
203
+ loading="lazy"
204
+ />
205
+ @else
206
+ <img
207
+ src="{{ screenshotUrl }}"
208
+ alt="Screenshot of {{ pageUrl }}"
209
+ class="w-full h-full object-cover object-top"
210
+ />
211
+ @endif
212
+
213
+ <!-- Heatmap overlay -->
214
+ @include('heatmap/ClickHeatmap', {
215
+ clicks: clickData,
216
+ width: containerWidth,
217
+ height: containerHeight,
218
+ loading: loading
219
+ })
220
+ </div>
221
+
222
+ <!-- Legend -->
223
+ <div class="flex justify-between items-center pt-3 border-t border-gray-200">
224
+ @include('heatmap/HeatmapLegend', {
225
+ showValues: true,
226
+ maxValue: totalClicks
227
+ })
228
+ <span class="text-xs text-gray-500">{{ totalClicks.toLocaleString() }} total clicks</span>
229
+ </div>
230
+
231
+ @elseif(viewMode === 'scroll')
232
+ <!-- Scroll heatmap view -->
233
+ @include('heatmap/ScrollHeatmap', {
234
+ data: scrollData,
235
+ avgMaxDepth: avgScrollDepth,
236
+ totalSessions: totalScrollSessions,
237
+ height: containerHeight,
238
+ loading: loading
239
+ })
240
+
241
+ @else
242
+ <!-- Movement heatmap view (placeholder) -->
243
+ <div class="flex flex-col items-center justify-center h-full text-gray-500 text-center">
244
+ <Icon name="trending-up" size="48" class="mb-4 opacity-50" />
245
+ <h3 class="text-base font-semibold text-gray-600 mb-2">Movement Tracking</h3>
246
+ <p class="text-sm">Mouse movement heatmaps coming soon</p>
247
+ </div>
248
+ @endif
249
+ </div>
250
+
251
+ <!-- Sidebar -->
252
+ @if(viewMode === 'click')
253
+ <div class="flex flex-col gap-4 min-h-0 overflow-y-auto lg:max-h-none max-h-[300px]">
254
+ @include('heatmap/ElementClickList', {
255
+ elements: topElements,
256
+ loading: loading,
257
+ maxItems: 15,
258
+ onHighlight: handleHighlight,
259
+ onClick: handleElementClick
260
+ })
261
+ </div>
262
+ @endif
263
+ </div>
264
+ </div>
@@ -0,0 +1,127 @@
1
+ <script>
2
+ /**
3
+ * ScrollHeatmap Component
4
+ *
5
+ * Visualizes scroll depth as horizontal bands showing reach percentage.
6
+ */
7
+ export const data = $props.data || []
8
+ export const avgMaxDepth = $props.avgMaxDepth || 0
9
+ export const totalSessions = $props.totalSessions || 0
10
+ export const height = $props.height || 400
11
+ export const loading = $props.loading || false
12
+
13
+ // Generate color based on reach percentage
14
+ export function getColor(percentage) {
15
+ if (percentage >= 80) return '#10b981' // Green - high engagement
16
+ if (percentage >= 60) return '#34d399' // Light green
17
+ if (percentage >= 40) return '#fbbf24' // Yellow - medium engagement
18
+ if (percentage >= 20) return '#f97316' // Orange
19
+ return '#ef4444' // Red - low engagement
20
+ }
21
+
22
+ export function formatTime(ms) {
23
+ if (ms < 1000) return `${ms}ms`
24
+ const seconds = Math.floor(ms / 1000)
25
+ if (seconds < 60) return `${seconds}s`
26
+ const minutes = Math.floor(seconds / 60)
27
+ const remainingSeconds = seconds % 60
28
+ return `${minutes}m ${remainingSeconds}s`
29
+ }
30
+
31
+ // Create depth buckets from 0 to 100 in increments of 10
32
+ const depthBuckets = []
33
+ for (let depth = 0; depth <= 90; depth += 10) {
34
+ const dataPoint = data.find(d => d.depth === depth)
35
+ depthBuckets.push({
36
+ depth,
37
+ label: `${depth}%`,
38
+ reachPercentage: dataPoint?.reachPercentage ?? (depth === 0 ? 100 : 0),
39
+ avgTimeMs: dataPoint?.avgTimeMs ?? 0,
40
+ sessions: dataPoint?.sessions ?? 0,
41
+ })
42
+ }
43
+
44
+ export { depthBuckets }
45
+ </script>
46
+
47
+ <div class="scroll-heatmap bg-white rounded-lg border border-gray-200 p-6 relative">
48
+ <!-- Loading state -->
49
+ @if(loading)
50
+ <div class="absolute inset-0 flex items-center justify-center bg-white/80 z-10">
51
+ <div class="w-8 h-8 border-2 border-gray-200 border-t-indigo-600 rounded-full animate-spin" />
52
+ </div>
53
+ @endif
54
+
55
+ <!-- Summary stats -->
56
+ <div class="flex gap-8 mb-6">
57
+ <div class="flex flex-col">
58
+ <span class="text-2xl font-bold text-gray-900">{{ avgMaxDepth }}%</span>
59
+ <span class="text-xs text-gray-500">Avg. Scroll Depth</span>
60
+ </div>
61
+ <div class="flex flex-col">
62
+ <span class="text-2xl font-bold text-gray-900">{{ totalSessions.toLocaleString() }}</span>
63
+ <span class="text-xs text-gray-500">Total Sessions</span>
64
+ </div>
65
+ </div>
66
+
67
+ <!-- Scroll visualization -->
68
+ <div class="flex gap-8" style="height: {{ height }}px">
69
+ <!-- Page representation -->
70
+ <div class="w-[120px] flex-shrink-0">
71
+ <div class="h-full border-2 border-gray-300 rounded-md overflow-hidden bg-gray-50">
72
+ <div class="p-2 border-b border-gray-200 bg-white">
73
+ <div class="h-1.5 bg-gray-300 rounded mb-1" />
74
+ <div class="h-1.5 w-3/5 bg-gray-300 rounded" />
75
+ </div>
76
+ <div class="flex flex-col h-[calc(100%-2.5rem)]">
77
+ @each(bucket in depthBuckets)
78
+ <div
79
+ class="flex-1 flex items-center justify-center border-b border-dashed border-black/10 last:border-b-0"
80
+ style="background-color: {{ getColor(bucket.reachPercentage) }}; opacity: {{ bucket.reachPercentage / 100 }}"
81
+ >
82
+ <span class="text-[10px] font-semibold text-black/60">{{ bucket.label }}</span>
83
+ </div>
84
+ @endeach
85
+ </div>
86
+ </div>
87
+ </div>
88
+
89
+ <!-- Data column -->
90
+ <div class="flex-1">
91
+ <div class="flex flex-col h-full">
92
+ @each(bucket in depthBuckets)
93
+ <div class="flex-1 flex items-center gap-3">
94
+ <div class="flex-1 h-6 bg-gray-100 rounded overflow-hidden">
95
+ <div
96
+ class="h-full rounded transition-all duration-300"
97
+ style="width: {{ bucket.reachPercentage }}%; background-color: {{ getColor(bucket.reachPercentage) }}"
98
+ />
99
+ </div>
100
+ <div class="flex items-center gap-2 w-24 flex-shrink-0">
101
+ <span class="text-sm font-semibold text-gray-900 w-10">{{ bucket.reachPercentage }}%</span>
102
+ @if(bucket.avgTimeMs > 0)
103
+ <span class="text-xs text-gray-500">{{ formatTime(bucket.avgTimeMs) }}</span>
104
+ @endif
105
+ </div>
106
+ </div>
107
+ @endeach
108
+ </div>
109
+ </div>
110
+ </div>
111
+
112
+ <!-- Legend -->
113
+ <div class="flex justify-center gap-6 mt-6 pt-4 border-t border-gray-200">
114
+ <div class="flex items-center gap-1.5 text-xs text-gray-500">
115
+ <span class="w-3 h-3 rounded-sm" style="background: #10b981" />
116
+ <span>80%+ reach</span>
117
+ </div>
118
+ <div class="flex items-center gap-1.5 text-xs text-gray-500">
119
+ <span class="w-3 h-3 rounded-sm" style="background: #fbbf24" />
120
+ <span>40-60% reach</span>
121
+ </div>
122
+ <div class="flex items-center gap-1.5 text-xs text-gray-500">
123
+ <span class="w-3 h-3 rounded-sm" style="background: #ef4444" />
124
+ <span>&lt;20% reach</span>
125
+ </div>
126
+ </div>
127
+ </div>
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Heatmap Components
3
+ *
4
+ * STX components for visualizing user interaction heatmaps.
5
+ */
6
+
7
+ export { default as PageHeatmap } from './PageHeatmap.stx'
8
+ export { default as ClickHeatmap } from './ClickHeatmap.stx'
9
+ export { default as ScrollHeatmap } from './ScrollHeatmap.stx'
10
+ export { default as HeatmapControls } from './HeatmapControls.stx'
11
+ export { default as HeatmapLegend } from './HeatmapLegend.stx'
12
+ export { default as ElementClickList } from './ElementClickList.stx'
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Analytics Dashboard Components
3
+ *
4
+ * stx components for displaying analytics data.
5
+ * These are .stx files that need to be processed by the stx framework.
6
+ * Total: 31 components
7
+ *
8
+ * Components are exported as string identifiers pointing to the component files.
9
+ * The stx framework uses these to resolve and render the components at runtime.
10
+ */
11
+
12
+ // Component name constants - use these when referencing components in stx templates
13
+ export const AlertCard = 'AlertCard'
14
+ export const AnalyticsDashboard = 'AnalyticsDashboard'
15
+ export const AnimatedNumber = 'AnimatedNumber'
16
+ export const BarChart = 'BarChart'
17
+ export const BrowserBreakdown = 'BrowserBreakdown'
18
+ export const CampaignBreakdown = 'CampaignBreakdown'
19
+ export const CountryList = 'CountryList'
20
+ export const DataTable = 'DataTable'
21
+ export const DateRangePicker = 'DateRangePicker'
22
+ export const DeviceBreakdown = 'DeviceBreakdown'
23
+ export const DonutChart = 'DonutChart'
24
+ export const EmptyState = 'EmptyState'
25
+ export const EngagementMetrics = 'EngagementMetrics'
26
+ export const FilterBar = 'FilterBar'
27
+ export const FullAnalyticsDashboard = 'FullAnalyticsDashboard'
28
+ export const FunnelChart = 'FunnelChart'
29
+ export const GoalsPanel = 'GoalsPanel'
30
+ export const HeatmapChart = 'HeatmapChart'
31
+ export const LiveActivityFeed = 'LiveActivityFeed'
32
+ export const MetricComparison = 'MetricComparison'
33
+ export const MiniStats = 'MiniStats'
34
+ export const OSBreakdown = 'OSBreakdown'
35
+ export const PageDetailCard = 'PageDetailCard'
36
+ export const ProgressRing = 'ProgressRing'
37
+ export const RealtimeCounter = 'RealtimeCounter'
38
+ export const SparklineChart = 'SparklineChart'
39
+ export const StatCard = 'StatCard'
40
+ export const ThemeSwitcher = 'ThemeSwitcher'
41
+ export const TimeSeriesChart = 'TimeSeriesChart'
42
+ export const TopList = 'TopList'
43
+ export const TrendIndicator = 'TrendIndicator'
44
+
45
+ // Component registry for dynamic component resolution
46
+ export const componentRegistry: Record<string, string> = {
47
+ AlertCard: './AlertCard.stx',
48
+ AnalyticsDashboard: './AnalyticsDashboard.stx',
49
+ AnimatedNumber: './AnimatedNumber.stx',
50
+ BarChart: './BarChart.stx',
51
+ BrowserBreakdown: './BrowserBreakdown.stx',
52
+ CampaignBreakdown: './CampaignBreakdown.stx',
53
+ CountryList: './CountryList.stx',
54
+ DataTable: './DataTable.stx',
55
+ DateRangePicker: './DateRangePicker.stx',
56
+ DeviceBreakdown: './DeviceBreakdown.stx',
57
+ DonutChart: './DonutChart.stx',
58
+ EmptyState: './EmptyState.stx',
59
+ EngagementMetrics: './EngagementMetrics.stx',
60
+ FilterBar: './FilterBar.stx',
61
+ FullAnalyticsDashboard: './FullAnalyticsDashboard.stx',
62
+ FunnelChart: './FunnelChart.stx',
63
+ GoalsPanel: './GoalsPanel.stx',
64
+ HeatmapChart: './HeatmapChart.stx',
65
+ LiveActivityFeed: './LiveActivityFeed.stx',
66
+ MetricComparison: './MetricComparison.stx',
67
+ MiniStats: './MiniStats.stx',
68
+ OSBreakdown: './OSBreakdown.stx',
69
+ PageDetailCard: './PageDetailCard.stx',
70
+ ProgressRing: './ProgressRing.stx',
71
+ RealtimeCounter: './RealtimeCounter.stx',
72
+ SparklineChart: './SparklineChart.stx',
73
+ StatCard: './StatCard.stx',
74
+ ThemeSwitcher: './ThemeSwitcher.stx',
75
+ TimeSeriesChart: './TimeSeriesChart.stx',
76
+ TopList: './TopList.stx',
77
+ TrendIndicator: './TrendIndicator.stx',
78
+ }
79
+
80
+ // List of all component names
81
+ export const allComponents: string[] = Object.keys(componentRegistry)
82
+
83
+ // Function to get component file path
84
+ export function getComponentPath(name: string): string | undefined {
85
+ return componentRegistry[name]
86
+ }