@probat/react 0.1.5 → 0.2.0
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.
- package/README.md +88 -0
- package/dist/index.d.mts +60 -2
- package/dist/index.d.ts +60 -2
- package/dist/index.js +415 -7
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +414 -9
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/context/ProbatContext.tsx +57 -2
- package/src/hoc/withExperiment.tsx +35 -5
- package/src/index.ts +2 -0
- package/src/utils/api.ts +22 -0
- package/src/utils/heatmapTracker.ts +478 -0
package/dist/index.js
CHANGED
|
@@ -21,6 +21,336 @@ function detectEnvironment() {
|
|
|
21
21
|
return "prod";
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
// src/utils/heatmapTracker.ts
|
|
25
|
+
function getStoredExperimentInfo() {
|
|
26
|
+
if (typeof window === "undefined") return {};
|
|
27
|
+
try {
|
|
28
|
+
const proposalId = window.localStorage.getItem("probat_active_proposal_id") || void 0;
|
|
29
|
+
const variantLabel = window.localStorage.getItem("probat_active_variant_label") || void 0;
|
|
30
|
+
return { proposalId, variantLabel };
|
|
31
|
+
} catch {
|
|
32
|
+
return {};
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
var HeatmapTracker = class {
|
|
36
|
+
constructor(config) {
|
|
37
|
+
this.clicks = [];
|
|
38
|
+
this.movements = [];
|
|
39
|
+
this.batchTimer = null;
|
|
40
|
+
this.cursorBatchTimer = null;
|
|
41
|
+
this.lastCursorTime = 0;
|
|
42
|
+
this.isInitialized = false;
|
|
43
|
+
this.handleMouseMove = (event) => {
|
|
44
|
+
if (!this.config.enabled || !this.config.trackCursor) return;
|
|
45
|
+
if (typeof window === "undefined") return;
|
|
46
|
+
const stored = getStoredExperimentInfo();
|
|
47
|
+
const proposalId = this.config.proposalId || stored.proposalId;
|
|
48
|
+
const variantLabel = this.config.variantLabel || stored.variantLabel;
|
|
49
|
+
const now2 = Date.now();
|
|
50
|
+
const cursorThrottle = this.config.cursorThrottle ?? 100;
|
|
51
|
+
if (now2 - this.lastCursorTime < cursorThrottle) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
this.lastCursorTime = now2;
|
|
55
|
+
if (this.movements.length < 3) {
|
|
56
|
+
console.log("[PROBAT Heatmap] Cursor movement captured:", {
|
|
57
|
+
x: event.clientX,
|
|
58
|
+
y: event.clientY,
|
|
59
|
+
movementsInBatch: this.movements.length + 1
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
const pageUrl = window.location.href;
|
|
63
|
+
const siteUrl = window.location.origin;
|
|
64
|
+
const x = event.clientX;
|
|
65
|
+
const y = event.clientY;
|
|
66
|
+
const viewportWidth = window.innerWidth;
|
|
67
|
+
const viewportHeight = window.innerHeight;
|
|
68
|
+
const movementEvent = {
|
|
69
|
+
page_url: pageUrl,
|
|
70
|
+
site_url: siteUrl,
|
|
71
|
+
x_coordinate: x,
|
|
72
|
+
y_coordinate: y,
|
|
73
|
+
viewport_width: viewportWidth,
|
|
74
|
+
viewport_height: viewportHeight,
|
|
75
|
+
session_id: this.sessionId,
|
|
76
|
+
proposal_id: proposalId,
|
|
77
|
+
variant_label: variantLabel
|
|
78
|
+
};
|
|
79
|
+
this.movements.push(movementEvent);
|
|
80
|
+
const cursorBatchSize = this.config.cursorBatchSize ?? 50;
|
|
81
|
+
if (this.movements.length >= cursorBatchSize) {
|
|
82
|
+
this.sendCursorBatch();
|
|
83
|
+
} else {
|
|
84
|
+
this.scheduleCursorBatchSend();
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
this.handleClick = (event) => {
|
|
88
|
+
if (!this.config.enabled) return;
|
|
89
|
+
if (typeof window === "undefined") return;
|
|
90
|
+
const stored = getStoredExperimentInfo();
|
|
91
|
+
const proposalId = this.config.proposalId || stored.proposalId;
|
|
92
|
+
const variantLabel = this.config.variantLabel || stored.variantLabel;
|
|
93
|
+
const target = event.target;
|
|
94
|
+
if (!target) return;
|
|
95
|
+
if (this.shouldExcludeElement(target)) {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
const pageUrl = window.location.href;
|
|
99
|
+
const siteUrl = window.location.origin;
|
|
100
|
+
const x = event.clientX;
|
|
101
|
+
const y = event.clientY;
|
|
102
|
+
const viewportWidth = window.innerWidth;
|
|
103
|
+
const viewportHeight = window.innerHeight;
|
|
104
|
+
const elementInfo = this.extractElementInfo(target);
|
|
105
|
+
const clickEvent = {
|
|
106
|
+
page_url: pageUrl,
|
|
107
|
+
site_url: siteUrl,
|
|
108
|
+
x_coordinate: x,
|
|
109
|
+
y_coordinate: y,
|
|
110
|
+
viewport_width: viewportWidth,
|
|
111
|
+
viewport_height: viewportHeight,
|
|
112
|
+
element_tag: elementInfo.tag,
|
|
113
|
+
element_class: elementInfo.class,
|
|
114
|
+
element_id: elementInfo.id,
|
|
115
|
+
session_id: this.sessionId,
|
|
116
|
+
proposal_id: proposalId,
|
|
117
|
+
variant_label: variantLabel
|
|
118
|
+
};
|
|
119
|
+
this.clicks.push(clickEvent);
|
|
120
|
+
const batchSize = this.config.batchSize ?? 10;
|
|
121
|
+
if (this.clicks.length >= batchSize) {
|
|
122
|
+
this.sendBatch();
|
|
123
|
+
} else {
|
|
124
|
+
this.scheduleBatchSend();
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
this.sessionId = this.getOrCreateSessionId();
|
|
128
|
+
const stored = getStoredExperimentInfo();
|
|
129
|
+
this.config = {
|
|
130
|
+
apiBaseUrl: config.apiBaseUrl,
|
|
131
|
+
batchSize: config.batchSize || 10,
|
|
132
|
+
batchInterval: config.batchInterval || 5e3,
|
|
133
|
+
enabled: config.enabled !== false,
|
|
134
|
+
excludeSelectors: config.excludeSelectors || [
|
|
135
|
+
"[data-heatmap-exclude]",
|
|
136
|
+
'input[type="password"]',
|
|
137
|
+
'input[type="email"]',
|
|
138
|
+
"textarea"
|
|
139
|
+
],
|
|
140
|
+
trackCursor: config.trackCursor !== false,
|
|
141
|
+
// Enable cursor tracking by default
|
|
142
|
+
cursorThrottle: config.cursorThrottle || 100,
|
|
143
|
+
// Capture cursor position every 100ms
|
|
144
|
+
cursorBatchSize: config.cursorBatchSize || 50,
|
|
145
|
+
// Send every 50 movements
|
|
146
|
+
proposalId: config.proposalId || stored.proposalId,
|
|
147
|
+
variantLabel: config.variantLabel || stored.variantLabel
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
getOrCreateSessionId() {
|
|
151
|
+
if (typeof window === "undefined") return "";
|
|
152
|
+
const storageKey = "probat_heatmap_session_id";
|
|
153
|
+
let sessionId = localStorage.getItem(storageKey);
|
|
154
|
+
if (!sessionId) {
|
|
155
|
+
sessionId = `heatmap_${Date.now()}_${Math.random().toString(36).substring(2, 15)}`;
|
|
156
|
+
localStorage.setItem(storageKey, sessionId);
|
|
157
|
+
}
|
|
158
|
+
return sessionId;
|
|
159
|
+
}
|
|
160
|
+
shouldExcludeElement(element) {
|
|
161
|
+
if (!this.config.excludeSelectors) return false;
|
|
162
|
+
for (const selector of this.config.excludeSelectors) {
|
|
163
|
+
if (element.matches(selector) || element.closest(selector)) {
|
|
164
|
+
return true;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
extractElementInfo(element) {
|
|
170
|
+
return {
|
|
171
|
+
tag: element.tagName || null,
|
|
172
|
+
class: element.className && typeof element.className === "string" ? element.className : null,
|
|
173
|
+
id: element.id || null
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
scheduleCursorBatchSend() {
|
|
177
|
+
if (this.cursorBatchTimer) {
|
|
178
|
+
clearTimeout(this.cursorBatchTimer);
|
|
179
|
+
}
|
|
180
|
+
this.cursorBatchTimer = setTimeout(() => {
|
|
181
|
+
if (this.movements.length > 0) {
|
|
182
|
+
this.sendCursorBatch();
|
|
183
|
+
}
|
|
184
|
+
}, this.config.batchInterval ?? 5e3);
|
|
185
|
+
}
|
|
186
|
+
async sendCursorBatch() {
|
|
187
|
+
if (this.movements.length === 0) return;
|
|
188
|
+
const siteUrl = this.movements[0]?.site_url || window.location.origin;
|
|
189
|
+
const batch = {
|
|
190
|
+
movements: this.movements,
|
|
191
|
+
site_url: siteUrl,
|
|
192
|
+
proposal_id: this.config.proposalId,
|
|
193
|
+
variant_label: this.config.variantLabel
|
|
194
|
+
};
|
|
195
|
+
this.movements = [];
|
|
196
|
+
if (this.cursorBatchTimer) {
|
|
197
|
+
clearTimeout(this.cursorBatchTimer);
|
|
198
|
+
this.cursorBatchTimer = null;
|
|
199
|
+
}
|
|
200
|
+
try {
|
|
201
|
+
console.log("[PROBAT Heatmap] Sending cursor movements batch:", {
|
|
202
|
+
count: batch.movements.length,
|
|
203
|
+
site_url: batch.site_url
|
|
204
|
+
});
|
|
205
|
+
const response = await fetch(`${this.config.apiBaseUrl}/api/heatmap/cursor-movements`, {
|
|
206
|
+
method: "POST",
|
|
207
|
+
headers: {
|
|
208
|
+
"Content-Type": "application/json"
|
|
209
|
+
},
|
|
210
|
+
body: JSON.stringify(batch)
|
|
211
|
+
// Don't wait for response - fire and forget for performance
|
|
212
|
+
});
|
|
213
|
+
if (!response.ok) {
|
|
214
|
+
console.warn("[PROBAT Heatmap] Failed to send cursor movements:", response.status);
|
|
215
|
+
} else {
|
|
216
|
+
console.log("[PROBAT Heatmap] Successfully sent cursor movements batch");
|
|
217
|
+
}
|
|
218
|
+
} catch (error) {
|
|
219
|
+
console.warn("[PROBAT Heatmap] Error sending cursor movements:", error);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
scheduleBatchSend() {
|
|
223
|
+
if (this.batchTimer) {
|
|
224
|
+
clearTimeout(this.batchTimer);
|
|
225
|
+
}
|
|
226
|
+
this.batchTimer = setTimeout(() => {
|
|
227
|
+
if (this.clicks.length > 0) {
|
|
228
|
+
this.sendBatch();
|
|
229
|
+
}
|
|
230
|
+
}, this.config.batchInterval ?? 5e3);
|
|
231
|
+
}
|
|
232
|
+
async sendBatch() {
|
|
233
|
+
if (this.clicks.length === 0) return;
|
|
234
|
+
if (this.batchTimer) {
|
|
235
|
+
clearTimeout(this.batchTimer);
|
|
236
|
+
this.batchTimer = null;
|
|
237
|
+
}
|
|
238
|
+
const siteUrl = this.clicks[0]?.site_url || window.location.origin;
|
|
239
|
+
const batch = {
|
|
240
|
+
clicks: [...this.clicks],
|
|
241
|
+
site_url: siteUrl
|
|
242
|
+
};
|
|
243
|
+
this.clicks = [];
|
|
244
|
+
try {
|
|
245
|
+
const response = await fetch(`${this.config.apiBaseUrl}/api/heatmap/clicks`, {
|
|
246
|
+
method: "POST",
|
|
247
|
+
headers: {
|
|
248
|
+
"Content-Type": "application/json"
|
|
249
|
+
},
|
|
250
|
+
body: JSON.stringify(batch)
|
|
251
|
+
});
|
|
252
|
+
if (!response.ok) {
|
|
253
|
+
console.warn("[PROBAT Heatmap] Failed to send clicks:", response.status);
|
|
254
|
+
}
|
|
255
|
+
} catch (error) {
|
|
256
|
+
console.warn("[PROBAT Heatmap] Error sending clicks:", error);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
init() {
|
|
260
|
+
if (this.isInitialized) {
|
|
261
|
+
console.warn("[PROBAT Heatmap] Tracker already initialized");
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
if (typeof window === "undefined") {
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
document.addEventListener("click", this.handleClick, true);
|
|
268
|
+
if (this.config.trackCursor) {
|
|
269
|
+
document.addEventListener("mousemove", this.handleMouseMove, { passive: true });
|
|
270
|
+
console.log("[PROBAT Heatmap] Cursor tracking enabled");
|
|
271
|
+
} else {
|
|
272
|
+
console.log("[PROBAT Heatmap] Cursor tracking disabled");
|
|
273
|
+
}
|
|
274
|
+
this.isInitialized = true;
|
|
275
|
+
console.log("[PROBAT Heatmap] Tracker initialized", {
|
|
276
|
+
enabled: this.config.enabled,
|
|
277
|
+
trackCursor: this.config.trackCursor,
|
|
278
|
+
cursorThrottle: this.config.cursorThrottle,
|
|
279
|
+
cursorBatchSize: this.config.cursorBatchSize
|
|
280
|
+
});
|
|
281
|
+
window.addEventListener("beforeunload", () => {
|
|
282
|
+
if (this.clicks.length > 0) {
|
|
283
|
+
const siteUrl = this.clicks[0]?.site_url || window.location.origin;
|
|
284
|
+
const batch = {
|
|
285
|
+
clicks: this.clicks,
|
|
286
|
+
site_url: siteUrl
|
|
287
|
+
};
|
|
288
|
+
const blob = new Blob([JSON.stringify(batch)], {
|
|
289
|
+
type: "application/json"
|
|
290
|
+
});
|
|
291
|
+
navigator.sendBeacon(
|
|
292
|
+
`${this.config.apiBaseUrl}/api/heatmap/clicks`,
|
|
293
|
+
blob
|
|
294
|
+
);
|
|
295
|
+
}
|
|
296
|
+
if (this.movements.length > 0) {
|
|
297
|
+
const siteUrl = this.movements[0]?.site_url || window.location.origin;
|
|
298
|
+
const batch = {
|
|
299
|
+
movements: this.movements,
|
|
300
|
+
site_url: siteUrl
|
|
301
|
+
};
|
|
302
|
+
const blob = new Blob([JSON.stringify(batch)], {
|
|
303
|
+
type: "application/json"
|
|
304
|
+
});
|
|
305
|
+
navigator.sendBeacon(
|
|
306
|
+
`${this.config.apiBaseUrl}/api/heatmap/cursor-movements`,
|
|
307
|
+
blob
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
stop() {
|
|
313
|
+
if (!this.isInitialized) return;
|
|
314
|
+
if (this.clicks.length > 0) {
|
|
315
|
+
this.sendBatch();
|
|
316
|
+
}
|
|
317
|
+
if (this.movements.length > 0) {
|
|
318
|
+
this.sendCursorBatch();
|
|
319
|
+
}
|
|
320
|
+
document.removeEventListener("click", this.handleClick, true);
|
|
321
|
+
if (this.config.trackCursor) {
|
|
322
|
+
document.removeEventListener("mousemove", this.handleMouseMove);
|
|
323
|
+
}
|
|
324
|
+
if (this.batchTimer) {
|
|
325
|
+
clearTimeout(this.batchTimer);
|
|
326
|
+
this.batchTimer = null;
|
|
327
|
+
}
|
|
328
|
+
if (this.cursorBatchTimer) {
|
|
329
|
+
clearTimeout(this.cursorBatchTimer);
|
|
330
|
+
this.cursorBatchTimer = null;
|
|
331
|
+
}
|
|
332
|
+
this.isInitialized = false;
|
|
333
|
+
}
|
|
334
|
+
};
|
|
335
|
+
var trackerInstance = null;
|
|
336
|
+
function initHeatmapTracking(config) {
|
|
337
|
+
if (trackerInstance) {
|
|
338
|
+
trackerInstance.stop();
|
|
339
|
+
}
|
|
340
|
+
trackerInstance = new HeatmapTracker(config);
|
|
341
|
+
trackerInstance.init();
|
|
342
|
+
return trackerInstance;
|
|
343
|
+
}
|
|
344
|
+
function getHeatmapTracker() {
|
|
345
|
+
return trackerInstance;
|
|
346
|
+
}
|
|
347
|
+
function stopHeatmapTracking() {
|
|
348
|
+
if (trackerInstance) {
|
|
349
|
+
trackerInstance.stop();
|
|
350
|
+
trackerInstance = null;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
24
354
|
// src/context/ProbatContext.tsx
|
|
25
355
|
var ProbatContext = React4.createContext(null);
|
|
26
356
|
function ProbatProvider({
|
|
@@ -28,8 +358,12 @@ function ProbatProvider({
|
|
|
28
358
|
clientKey,
|
|
29
359
|
environment: explicitEnvironment,
|
|
30
360
|
repoFullName: explicitRepoFullName,
|
|
361
|
+
proposalId,
|
|
362
|
+
variantLabel,
|
|
31
363
|
children
|
|
32
364
|
}) {
|
|
365
|
+
const storedProposalId = typeof window !== "undefined" ? window.localStorage.getItem("probat_active_proposal_id") || void 0 : void 0;
|
|
366
|
+
const storedVariantLabel = typeof window !== "undefined" ? window.localStorage.getItem("probat_active_variant_label") || void 0 : void 0;
|
|
33
367
|
const contextValue = React4.useMemo(() => {
|
|
34
368
|
const resolvedApiBaseUrl = apiBaseUrl || typeof ({ url: (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.js', document.baseURI).href)) }) !== "undefined" && undefined?.VITE_PROBAT_API || typeof globalThis !== "undefined" && globalThis.process?.env?.NEXT_PUBLIC_PROBAT_API || typeof window !== "undefined" && window.__PROBAT_API || "https://gushi.onrender.com";
|
|
35
369
|
const environment = explicitEnvironment || detectEnvironment();
|
|
@@ -38,9 +372,38 @@ function ProbatProvider({
|
|
|
38
372
|
apiBaseUrl: resolvedApiBaseUrl,
|
|
39
373
|
environment,
|
|
40
374
|
clientKey,
|
|
41
|
-
repoFullName: resolvedRepoFullName
|
|
375
|
+
repoFullName: resolvedRepoFullName,
|
|
376
|
+
proposalId: proposalId || storedProposalId,
|
|
377
|
+
variantLabel: variantLabel || storedVariantLabel
|
|
42
378
|
};
|
|
43
|
-
}, [apiBaseUrl, clientKey, explicitEnvironment, explicitRepoFullName]);
|
|
379
|
+
}, [apiBaseUrl, clientKey, explicitEnvironment, explicitRepoFullName, proposalId, variantLabel, storedProposalId, storedVariantLabel]);
|
|
380
|
+
React4.useEffect(() => {
|
|
381
|
+
if (typeof window !== "undefined") {
|
|
382
|
+
initHeatmapTracking({
|
|
383
|
+
apiBaseUrl: contextValue.apiBaseUrl,
|
|
384
|
+
batchSize: 10,
|
|
385
|
+
batchInterval: 5e3,
|
|
386
|
+
enabled: true,
|
|
387
|
+
excludeSelectors: [
|
|
388
|
+
"[data-heatmap-exclude]",
|
|
389
|
+
'input[type="password"]',
|
|
390
|
+
'input[type="email"]',
|
|
391
|
+
"textarea"
|
|
392
|
+
],
|
|
393
|
+
// Explicitly enable cursor tracking with sensible defaults
|
|
394
|
+
trackCursor: true,
|
|
395
|
+
cursorThrottle: 100,
|
|
396
|
+
// capture every 100ms
|
|
397
|
+
cursorBatchSize: 50,
|
|
398
|
+
// send every 50 movements (or after batchInterval)
|
|
399
|
+
proposalId: contextValue.proposalId,
|
|
400
|
+
variantLabel: contextValue.variantLabel
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
return () => {
|
|
404
|
+
stopHeatmapTracking();
|
|
405
|
+
};
|
|
406
|
+
}, [contextValue.apiBaseUrl]);
|
|
44
407
|
return /* @__PURE__ */ React4__default.default.createElement(ProbatContext.Provider, { value: contextValue }, children);
|
|
45
408
|
}
|
|
46
409
|
function useProbatContext() {
|
|
@@ -74,6 +437,14 @@ async function fetchDecision(baseUrl, proposalId) {
|
|
|
74
437
|
const data = await res.json();
|
|
75
438
|
const experiment_id = (data.experiment_id || `exp_${proposalId}`).toString();
|
|
76
439
|
const label = data.label && data.label.trim() ? data.label : "control";
|
|
440
|
+
if (typeof window !== "undefined") {
|
|
441
|
+
try {
|
|
442
|
+
window.localStorage.setItem("probat_active_proposal_id", proposalId);
|
|
443
|
+
window.localStorage.setItem("probat_active_variant_label", label);
|
|
444
|
+
} catch (e) {
|
|
445
|
+
console.warn("[PROBAT] Failed to set proposal/variant in localStorage:", e);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
77
448
|
return { experiment_id, label };
|
|
78
449
|
} finally {
|
|
79
450
|
pendingFetches.delete(proposalId);
|
|
@@ -152,6 +523,13 @@ async function fetchComponentExperimentConfig(baseUrl, repoFullName, componentPa
|
|
|
152
523
|
throw new Error(`HTTP ${res.status}`);
|
|
153
524
|
}
|
|
154
525
|
const data = await res.json();
|
|
526
|
+
if (typeof window !== "undefined" && data?.proposal_id) {
|
|
527
|
+
try {
|
|
528
|
+
window.localStorage.setItem("probat_active_proposal_id", data.proposal_id);
|
|
529
|
+
} catch (e) {
|
|
530
|
+
console.warn("[PROBAT] Failed to set proposal_id in localStorage:", e);
|
|
531
|
+
}
|
|
532
|
+
}
|
|
155
533
|
return data;
|
|
156
534
|
} catch (e) {
|
|
157
535
|
console.warn(`[PROBAT] Failed to fetch component config: ${e}`);
|
|
@@ -786,23 +1164,50 @@ function withExperiment(Control, options) {
|
|
|
786
1164
|
let alive = true;
|
|
787
1165
|
const cached = readChoice(proposalId);
|
|
788
1166
|
if (cached) {
|
|
789
|
-
|
|
1167
|
+
const choiceData = {
|
|
790
1168
|
experiment_id: cached.experiment_id,
|
|
791
1169
|
label: cached.label
|
|
792
|
-
}
|
|
1170
|
+
};
|
|
1171
|
+
setChoice(choiceData);
|
|
1172
|
+
if (typeof window !== "undefined") {
|
|
1173
|
+
try {
|
|
1174
|
+
window.localStorage.setItem("probat_active_proposal_id", proposalId);
|
|
1175
|
+
window.localStorage.setItem("probat_active_variant_label", cached.label);
|
|
1176
|
+
} catch (e) {
|
|
1177
|
+
console.warn("[PROBAT] Failed to set proposal/variant in localStorage:", e);
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
793
1180
|
} else {
|
|
794
1181
|
(async () => {
|
|
795
1182
|
try {
|
|
796
1183
|
const { experiment_id, label: label2 } = await fetchDecision(apiBaseUrl, proposalId);
|
|
797
1184
|
if (!alive) return;
|
|
798
1185
|
writeChoice(proposalId, experiment_id, label2);
|
|
799
|
-
|
|
1186
|
+
const choiceData = { experiment_id, label: label2 };
|
|
1187
|
+
setChoice(choiceData);
|
|
1188
|
+
if (typeof window !== "undefined") {
|
|
1189
|
+
try {
|
|
1190
|
+
window.localStorage.setItem("probat_active_proposal_id", proposalId);
|
|
1191
|
+
window.localStorage.setItem("probat_active_variant_label", label2);
|
|
1192
|
+
} catch (e) {
|
|
1193
|
+
console.warn("[PROBAT] Failed to set proposal/variant in localStorage:", e);
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
800
1196
|
} catch (e) {
|
|
801
1197
|
if (!alive) return;
|
|
802
|
-
|
|
1198
|
+
const choiceData = {
|
|
803
1199
|
experiment_id: `exp_${proposalId}`,
|
|
804
1200
|
label: "control"
|
|
805
|
-
}
|
|
1201
|
+
};
|
|
1202
|
+
setChoice(choiceData);
|
|
1203
|
+
if (typeof window !== "undefined") {
|
|
1204
|
+
try {
|
|
1205
|
+
window.localStorage.setItem("probat_active_proposal_id", proposalId);
|
|
1206
|
+
window.localStorage.setItem("probat_active_variant_label", "control");
|
|
1207
|
+
} catch (err) {
|
|
1208
|
+
console.warn("[PROBAT] Failed to set proposal/variant in localStorage:", err);
|
|
1209
|
+
}
|
|
1210
|
+
}
|
|
806
1211
|
}
|
|
807
1212
|
})();
|
|
808
1213
|
}
|
|
@@ -865,10 +1270,13 @@ exports.ProbatProviderClient = ProbatProviderClient;
|
|
|
865
1270
|
exports.detectEnvironment = detectEnvironment;
|
|
866
1271
|
exports.extractClickMeta = extractClickMeta;
|
|
867
1272
|
exports.fetchDecision = fetchDecision;
|
|
1273
|
+
exports.getHeatmapTracker = getHeatmapTracker;
|
|
868
1274
|
exports.hasTrackedVisit = hasTrackedVisit;
|
|
1275
|
+
exports.initHeatmapTracking = initHeatmapTracking;
|
|
869
1276
|
exports.markTrackedVisit = markTrackedVisit;
|
|
870
1277
|
exports.readChoice = readChoice;
|
|
871
1278
|
exports.sendMetric = sendMetric;
|
|
1279
|
+
exports.stopHeatmapTracking = stopHeatmapTracking;
|
|
872
1280
|
exports.useExperiment = useExperiment;
|
|
873
1281
|
exports.useProbatContext = useProbatContext;
|
|
874
1282
|
exports.useProbatMetrics = useProbatMetrics;
|