@probat/react 0.2.0 → 0.2.1
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/dist/index.d.mts +23 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.js +219 -45
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +219 -45
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -2
- package/src/context/ProbatContext.tsx +23 -3
- package/src/hoc/itrt-frontend.code-workspace +8 -36
- package/src/hoc/withExperiment.tsx +36 -15
- package/src/hooks/useExperiment.ts +28 -4
- package/src/utils/heatmapTracker.ts +213 -26
package/dist/index.d.mts
CHANGED
|
@@ -229,6 +229,29 @@ declare class HeatmapTracker {
|
|
|
229
229
|
private sendBatch;
|
|
230
230
|
init(): void;
|
|
231
231
|
stop(): void;
|
|
232
|
+
/**
|
|
233
|
+
* Enable visualization mode
|
|
234
|
+
*/
|
|
235
|
+
/**
|
|
236
|
+
* Enable visualization mode
|
|
237
|
+
*/
|
|
238
|
+
private enableVisualization;
|
|
239
|
+
/**
|
|
240
|
+
* Render heatmap overlay on valid points
|
|
241
|
+
*/
|
|
242
|
+
private renderHeatmapOverlay;
|
|
243
|
+
/**
|
|
244
|
+
* Draw points on canvas
|
|
245
|
+
*/
|
|
246
|
+
private renderPoints;
|
|
247
|
+
/**
|
|
248
|
+
* Get heatmap color based on intensity
|
|
249
|
+
*/
|
|
250
|
+
private getHeatmapColor;
|
|
251
|
+
/**
|
|
252
|
+
* Convert RGB to RGBA
|
|
253
|
+
*/
|
|
254
|
+
private rgbToRgba;
|
|
232
255
|
}
|
|
233
256
|
/**
|
|
234
257
|
* Initialize heatmap tracking for user websites
|
package/dist/index.d.ts
CHANGED
|
@@ -229,6 +229,29 @@ declare class HeatmapTracker {
|
|
|
229
229
|
private sendBatch;
|
|
230
230
|
init(): void;
|
|
231
231
|
stop(): void;
|
|
232
|
+
/**
|
|
233
|
+
* Enable visualization mode
|
|
234
|
+
*/
|
|
235
|
+
/**
|
|
236
|
+
* Enable visualization mode
|
|
237
|
+
*/
|
|
238
|
+
private enableVisualization;
|
|
239
|
+
/**
|
|
240
|
+
* Render heatmap overlay on valid points
|
|
241
|
+
*/
|
|
242
|
+
private renderHeatmapOverlay;
|
|
243
|
+
/**
|
|
244
|
+
* Draw points on canvas
|
|
245
|
+
*/
|
|
246
|
+
private renderPoints;
|
|
247
|
+
/**
|
|
248
|
+
* Get heatmap color based on intensity
|
|
249
|
+
*/
|
|
250
|
+
private getHeatmapColor;
|
|
251
|
+
/**
|
|
252
|
+
* Convert RGB to RGBA
|
|
253
|
+
*/
|
|
254
|
+
private rgbToRgba;
|
|
232
255
|
}
|
|
233
256
|
/**
|
|
234
257
|
* Initialize heatmap tracking for user websites
|
package/dist/index.js
CHANGED
|
@@ -44,25 +44,23 @@ var HeatmapTracker = class {
|
|
|
44
44
|
if (!this.config.enabled || !this.config.trackCursor) return;
|
|
45
45
|
if (typeof window === "undefined") return;
|
|
46
46
|
const stored = getStoredExperimentInfo();
|
|
47
|
-
const
|
|
48
|
-
const
|
|
47
|
+
const activeProposalId = this.config.proposalId || stored.proposalId;
|
|
48
|
+
const activeVariantLabel = stored.proposalId === activeProposalId ? stored.variantLabel || this.config.variantLabel : this.config.variantLabel || stored.variantLabel;
|
|
49
49
|
const now2 = Date.now();
|
|
50
50
|
const cursorThrottle = this.config.cursorThrottle ?? 100;
|
|
51
51
|
if (now2 - this.lastCursorTime < cursorThrottle) {
|
|
52
52
|
return;
|
|
53
53
|
}
|
|
54
54
|
this.lastCursorTime = now2;
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
});
|
|
61
|
-
}
|
|
55
|
+
console.log("[PROBAT Heatmap] Cursor movement captured:", {
|
|
56
|
+
x: event.pageX,
|
|
57
|
+
y: event.pageY,
|
|
58
|
+
movementsInBatch: this.movements.length + 1
|
|
59
|
+
});
|
|
62
60
|
const pageUrl = window.location.href;
|
|
63
61
|
const siteUrl = window.location.origin;
|
|
64
|
-
const x = event.
|
|
65
|
-
const y = event.
|
|
62
|
+
const x = event.pageX;
|
|
63
|
+
const y = event.pageY;
|
|
66
64
|
const viewportWidth = window.innerWidth;
|
|
67
65
|
const viewportHeight = window.innerHeight;
|
|
68
66
|
const movementEvent = {
|
|
@@ -73,8 +71,8 @@ var HeatmapTracker = class {
|
|
|
73
71
|
viewport_width: viewportWidth,
|
|
74
72
|
viewport_height: viewportHeight,
|
|
75
73
|
session_id: this.sessionId,
|
|
76
|
-
proposal_id:
|
|
77
|
-
variant_label:
|
|
74
|
+
proposal_id: activeProposalId,
|
|
75
|
+
variant_label: activeVariantLabel
|
|
78
76
|
};
|
|
79
77
|
this.movements.push(movementEvent);
|
|
80
78
|
const cursorBatchSize = this.config.cursorBatchSize ?? 50;
|
|
@@ -88,8 +86,8 @@ var HeatmapTracker = class {
|
|
|
88
86
|
if (!this.config.enabled) return;
|
|
89
87
|
if (typeof window === "undefined") return;
|
|
90
88
|
const stored = getStoredExperimentInfo();
|
|
91
|
-
const
|
|
92
|
-
const
|
|
89
|
+
const activeProposalId = this.config.proposalId || stored.proposalId;
|
|
90
|
+
const activeVariantLabel = stored.proposalId === activeProposalId ? stored.variantLabel || this.config.variantLabel : this.config.variantLabel || stored.variantLabel;
|
|
93
91
|
const target = event.target;
|
|
94
92
|
if (!target) return;
|
|
95
93
|
if (this.shouldExcludeElement(target)) {
|
|
@@ -97,8 +95,8 @@ var HeatmapTracker = class {
|
|
|
97
95
|
}
|
|
98
96
|
const pageUrl = window.location.href;
|
|
99
97
|
const siteUrl = window.location.origin;
|
|
100
|
-
const x = event.
|
|
101
|
-
const y = event.
|
|
98
|
+
const x = event.pageX;
|
|
99
|
+
const y = event.pageY;
|
|
102
100
|
const viewportWidth = window.innerWidth;
|
|
103
101
|
const viewportHeight = window.innerHeight;
|
|
104
102
|
const elementInfo = this.extractElementInfo(target);
|
|
@@ -113,8 +111,8 @@ var HeatmapTracker = class {
|
|
|
113
111
|
element_class: elementInfo.class,
|
|
114
112
|
element_id: elementInfo.id,
|
|
115
113
|
session_id: this.sessionId,
|
|
116
|
-
proposal_id:
|
|
117
|
-
variant_label:
|
|
114
|
+
proposal_id: activeProposalId,
|
|
115
|
+
variant_label: activeVariantLabel
|
|
118
116
|
};
|
|
119
117
|
this.clicks.push(clickEvent);
|
|
120
118
|
const batchSize = this.config.batchSize ?? 10;
|
|
@@ -264,6 +262,17 @@ var HeatmapTracker = class {
|
|
|
264
262
|
if (typeof window === "undefined") {
|
|
265
263
|
return;
|
|
266
264
|
}
|
|
265
|
+
const params = new URLSearchParams(window.location.search);
|
|
266
|
+
if (params.get("heatmap") === "true") {
|
|
267
|
+
console.log("[PROBAT Heatmap] Heatmap visualization mode detected");
|
|
268
|
+
const pageUrl = params.get("page_url");
|
|
269
|
+
if (pageUrl) {
|
|
270
|
+
this.config.enabled = false;
|
|
271
|
+
this.isInitialized = true;
|
|
272
|
+
this.enableVisualization(pageUrl);
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
267
276
|
document.addEventListener("click", this.handleClick, true);
|
|
268
277
|
if (this.config.trackCursor) {
|
|
269
278
|
document.addEventListener("mousemove", this.handleMouseMove, { passive: true });
|
|
@@ -272,12 +281,7 @@ var HeatmapTracker = class {
|
|
|
272
281
|
console.log("[PROBAT Heatmap] Cursor tracking disabled");
|
|
273
282
|
}
|
|
274
283
|
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
|
-
});
|
|
284
|
+
console.log("[PROBAT Heatmap] Tracker initialized");
|
|
281
285
|
window.addEventListener("beforeunload", () => {
|
|
282
286
|
if (this.clicks.length > 0) {
|
|
283
287
|
const siteUrl = this.clicks[0]?.site_url || window.location.origin;
|
|
@@ -331,6 +335,141 @@ var HeatmapTracker = class {
|
|
|
331
335
|
}
|
|
332
336
|
this.isInitialized = false;
|
|
333
337
|
}
|
|
338
|
+
/**
|
|
339
|
+
* Enable visualization mode
|
|
340
|
+
*/
|
|
341
|
+
/**
|
|
342
|
+
* Enable visualization mode
|
|
343
|
+
*/
|
|
344
|
+
async enableVisualization(pageUrl) {
|
|
345
|
+
console.log("[PROBAT Heatmap] Enabling visualization mode for:", pageUrl);
|
|
346
|
+
this.stop();
|
|
347
|
+
this.config.enabled = false;
|
|
348
|
+
try {
|
|
349
|
+
const url = new URL(`${this.config.apiBaseUrl}/api/heatmap/aggregate`);
|
|
350
|
+
url.searchParams.append("site_url", window.location.origin);
|
|
351
|
+
url.searchParams.append("page_url", pageUrl);
|
|
352
|
+
url.searchParams.append("days", "30");
|
|
353
|
+
const params = new URLSearchParams(window.location.search);
|
|
354
|
+
const proposalId = params.get("proposal_id");
|
|
355
|
+
const variantLabel = params.get("variant_label");
|
|
356
|
+
if (proposalId) url.searchParams.append("proposal_id", proposalId);
|
|
357
|
+
if (variantLabel) url.searchParams.append("variant_label", variantLabel);
|
|
358
|
+
const response = await fetch(url.toString());
|
|
359
|
+
if (!response.ok) throw new Error("Failed to fetch heatmap data");
|
|
360
|
+
const data = await response.json();
|
|
361
|
+
if (data && data.points) {
|
|
362
|
+
console.log(`[PROBAT Heatmap] Found ${data.points.length} points. Rendering...`);
|
|
363
|
+
this.renderHeatmapOverlay(data);
|
|
364
|
+
}
|
|
365
|
+
} catch (error) {
|
|
366
|
+
console.error("[PROBAT Heatmap] Visualization error:", error);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Render heatmap overlay on valid points
|
|
371
|
+
*/
|
|
372
|
+
renderHeatmapOverlay(data) {
|
|
373
|
+
const points = data.points;
|
|
374
|
+
const trackedWidth = data.viewport_width || 0;
|
|
375
|
+
const existing = document.getElementById("probat-heatmap-overlay");
|
|
376
|
+
if (existing) existing.remove();
|
|
377
|
+
const canvas = document.createElement("canvas");
|
|
378
|
+
canvas.id = "probat-heatmap-overlay";
|
|
379
|
+
canvas.style.position = "absolute";
|
|
380
|
+
canvas.style.top = "0";
|
|
381
|
+
canvas.style.left = "0";
|
|
382
|
+
canvas.style.zIndex = "999999";
|
|
383
|
+
canvas.style.pointerEvents = "none";
|
|
384
|
+
canvas.style.display = "block";
|
|
385
|
+
canvas.style.margin = "0";
|
|
386
|
+
canvas.style.padding = "0";
|
|
387
|
+
document.documentElement.appendChild(canvas);
|
|
388
|
+
const resize = () => {
|
|
389
|
+
const dpr = window.devicePixelRatio || 1;
|
|
390
|
+
const width = document.documentElement.scrollWidth;
|
|
391
|
+
const height = document.documentElement.scrollHeight;
|
|
392
|
+
const currentWidth = window.innerWidth;
|
|
393
|
+
let offsetX = 0;
|
|
394
|
+
if (trackedWidth > 0 && trackedWidth !== currentWidth) {
|
|
395
|
+
offsetX = (currentWidth - trackedWidth) / 2;
|
|
396
|
+
console.log(`[PROBAT Heatmap] Horizontal adjustment: offset=${offsetX}px (Tracked: ${trackedWidth}px, Current: ${currentWidth}px)`);
|
|
397
|
+
}
|
|
398
|
+
canvas.style.width = width + "px";
|
|
399
|
+
canvas.style.height = height + "px";
|
|
400
|
+
canvas.width = width * dpr;
|
|
401
|
+
canvas.height = height * dpr;
|
|
402
|
+
const ctx = canvas.getContext("2d");
|
|
403
|
+
if (ctx) {
|
|
404
|
+
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
|
405
|
+
this.renderPoints(ctx, points, offsetX);
|
|
406
|
+
}
|
|
407
|
+
};
|
|
408
|
+
window.addEventListener("resize", resize);
|
|
409
|
+
setTimeout(resize, 300);
|
|
410
|
+
setTimeout(resize, 1e3);
|
|
411
|
+
setTimeout(resize, 3e3);
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* Draw points on canvas
|
|
415
|
+
*/
|
|
416
|
+
renderPoints(ctx, points, offsetX) {
|
|
417
|
+
points.forEach((point) => {
|
|
418
|
+
const x = point.x + offsetX;
|
|
419
|
+
const y = point.y;
|
|
420
|
+
const intensity = point.intensity;
|
|
421
|
+
const radius = 20 + intensity * 12;
|
|
422
|
+
const color = this.getHeatmapColor(intensity);
|
|
423
|
+
const gradient = ctx.createRadialGradient(x, y, 0, x, y, radius);
|
|
424
|
+
gradient.addColorStop(0, this.rgbToRgba(color, 0.8));
|
|
425
|
+
gradient.addColorStop(1, this.rgbToRgba(color, 0));
|
|
426
|
+
ctx.beginPath();
|
|
427
|
+
ctx.arc(x, y, radius, 0, 2 * Math.PI);
|
|
428
|
+
ctx.fillStyle = gradient;
|
|
429
|
+
ctx.fill();
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
/**
|
|
433
|
+
* Get heatmap color based on intensity
|
|
434
|
+
*/
|
|
435
|
+
getHeatmapColor(intensity) {
|
|
436
|
+
const clamped = Math.max(0, Math.min(1, intensity));
|
|
437
|
+
if (clamped < 0.25) {
|
|
438
|
+
const t = clamped / 0.25;
|
|
439
|
+
const r = Math.floor(0 + t * 0);
|
|
440
|
+
const g = Math.floor(0 + t * 255);
|
|
441
|
+
const b = Math.floor(255 + t * (255 - 255));
|
|
442
|
+
return `rgb(${r}, ${g}, ${b})`;
|
|
443
|
+
} else if (clamped < 0.5) {
|
|
444
|
+
const t = (clamped - 0.25) / 0.25;
|
|
445
|
+
const r = Math.floor(0 + t * 0);
|
|
446
|
+
const g = 255;
|
|
447
|
+
const b = Math.floor(255 + t * (0 - 255));
|
|
448
|
+
return `rgb(${r}, ${g}, ${b})`;
|
|
449
|
+
} else if (clamped < 0.75) {
|
|
450
|
+
const t = (clamped - 0.5) / 0.25;
|
|
451
|
+
const r = Math.floor(0 + t * 255);
|
|
452
|
+
const g = 255;
|
|
453
|
+
const b = 0;
|
|
454
|
+
return `rgb(${r}, ${g}, ${b})`;
|
|
455
|
+
} else {
|
|
456
|
+
const t = (clamped - 0.75) / 0.25;
|
|
457
|
+
const r = 255;
|
|
458
|
+
const g = Math.floor(255 + t * (0 - 255));
|
|
459
|
+
const b = 0;
|
|
460
|
+
return `rgb(${r}, ${g}, ${b})`;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
/**
|
|
464
|
+
* Convert RGB to RGBA
|
|
465
|
+
*/
|
|
466
|
+
rgbToRgba(rgb, opacity) {
|
|
467
|
+
const match = rgb.match(/rgb\((\d+),\s*(\d+),\s*(\d+)\)/);
|
|
468
|
+
if (match) {
|
|
469
|
+
return `rgba(${match[1]}, ${match[2]}, ${match[3]}, ${opacity})`;
|
|
470
|
+
}
|
|
471
|
+
return rgb;
|
|
472
|
+
}
|
|
334
473
|
};
|
|
335
474
|
var trackerInstance = null;
|
|
336
475
|
function initHeatmapTracking(config) {
|
|
@@ -368,13 +507,24 @@ function ProbatProvider({
|
|
|
368
507
|
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";
|
|
369
508
|
const environment = explicitEnvironment || detectEnvironment();
|
|
370
509
|
const resolvedRepoFullName = explicitRepoFullName || typeof globalThis !== "undefined" && globalThis.process?.env?.NEXT_PUBLIC_PROBAT_REPO || 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_REPO || typeof window !== "undefined" && window.__PROBAT_REPO || void 0;
|
|
510
|
+
const params = typeof window !== "undefined" ? new URLSearchParams(window.location.search) : null;
|
|
511
|
+
const isHeatmapMode = params?.get("heatmap") === "true";
|
|
512
|
+
let urlProposalId;
|
|
513
|
+
let urlVariantLabel;
|
|
514
|
+
if (isHeatmapMode && params) {
|
|
515
|
+
urlProposalId = params.get("proposal_id") || void 0;
|
|
516
|
+
urlVariantLabel = params.get("variant_label") || void 0;
|
|
517
|
+
console.log("[PROBAT] Heatmap mode: Overriding variant from URL", { urlProposalId, urlVariantLabel });
|
|
518
|
+
}
|
|
519
|
+
const finalProposalId = urlProposalId || proposalId || (!isHeatmapMode ? storedProposalId : void 0);
|
|
520
|
+
const finalVariantLabel = urlVariantLabel || variantLabel || (!isHeatmapMode ? storedVariantLabel : void 0);
|
|
371
521
|
return {
|
|
372
522
|
apiBaseUrl: resolvedApiBaseUrl,
|
|
373
523
|
environment,
|
|
374
524
|
clientKey,
|
|
375
525
|
repoFullName: resolvedRepoFullName,
|
|
376
|
-
proposalId:
|
|
377
|
-
variantLabel:
|
|
526
|
+
proposalId: finalProposalId,
|
|
527
|
+
variantLabel: finalVariantLabel
|
|
378
528
|
};
|
|
379
529
|
}, [apiBaseUrl, clientKey, explicitEnvironment, explicitRepoFullName, proposalId, variantLabel, storedProposalId, storedVariantLabel]);
|
|
380
530
|
React4.useEffect(() => {
|
|
@@ -403,7 +553,7 @@ function ProbatProvider({
|
|
|
403
553
|
return () => {
|
|
404
554
|
stopHeatmapTracking();
|
|
405
555
|
};
|
|
406
|
-
}, [contextValue.apiBaseUrl]);
|
|
556
|
+
}, [contextValue.apiBaseUrl, contextValue.proposalId, contextValue.variantLabel]);
|
|
407
557
|
return /* @__PURE__ */ React4__default.default.createElement(ProbatContext.Provider, { value: contextValue }, children);
|
|
408
558
|
}
|
|
409
559
|
function useProbatContext() {
|
|
@@ -985,14 +1135,25 @@ function markTrackedVisit(proposalId, label) {
|
|
|
985
1135
|
|
|
986
1136
|
// src/hooks/useExperiment.ts
|
|
987
1137
|
function useExperiment(proposalId, options) {
|
|
988
|
-
const
|
|
1138
|
+
const context = useProbatContext();
|
|
1139
|
+
const { apiBaseUrl } = context;
|
|
989
1140
|
const [choice, setChoice] = React4.useState(null);
|
|
990
1141
|
const [isLoading, setIsLoading] = React4.useState(true);
|
|
991
1142
|
const [error, setError] = React4.useState(null);
|
|
992
1143
|
const autoTrackImpression = options?.autoTrackImpression !== false;
|
|
993
1144
|
React4.useEffect(() => {
|
|
994
1145
|
let alive = true;
|
|
995
|
-
const
|
|
1146
|
+
const isHeatmapMode = typeof window !== "undefined" && new URLSearchParams(window.location.search).get("heatmap") === "true";
|
|
1147
|
+
if (context.proposalId === proposalId && context.variantLabel) {
|
|
1148
|
+
console.log(`[PROBAT] Forced variant from context: ${context.variantLabel}`);
|
|
1149
|
+
setChoice({
|
|
1150
|
+
experiment_id: `forced_${proposalId}`,
|
|
1151
|
+
label: context.variantLabel
|
|
1152
|
+
});
|
|
1153
|
+
setIsLoading(false);
|
|
1154
|
+
return;
|
|
1155
|
+
}
|
|
1156
|
+
const cached = isHeatmapMode ? null : readChoice(proposalId);
|
|
996
1157
|
if (cached) {
|
|
997
1158
|
setChoice({ experiment_id: cached.experiment_id, label: cached.label });
|
|
998
1159
|
setIsLoading(false);
|
|
@@ -1005,7 +1166,9 @@ function useExperiment(proposalId, options) {
|
|
|
1005
1166
|
proposalId
|
|
1006
1167
|
);
|
|
1007
1168
|
if (!alive) return;
|
|
1008
|
-
|
|
1169
|
+
if (!isHeatmapMode) {
|
|
1170
|
+
writeChoice(proposalId, experiment_id, label);
|
|
1171
|
+
}
|
|
1009
1172
|
setChoice({ experiment_id, label });
|
|
1010
1173
|
setError(null);
|
|
1011
1174
|
} catch (e) {
|
|
@@ -1026,7 +1189,7 @@ function useExperiment(proposalId, options) {
|
|
|
1026
1189
|
return () => {
|
|
1027
1190
|
alive = false;
|
|
1028
1191
|
};
|
|
1029
|
-
}, [proposalId, apiBaseUrl]);
|
|
1192
|
+
}, [proposalId, apiBaseUrl, context.proposalId, context.variantLabel]);
|
|
1030
1193
|
React4.useEffect(() => {
|
|
1031
1194
|
if (!autoTrackImpression || !choice) return;
|
|
1032
1195
|
const exp = choice.experiment_id;
|
|
@@ -1162,14 +1325,23 @@ function withExperiment(Control, options) {
|
|
|
1162
1325
|
if (!proposalId) return;
|
|
1163
1326
|
if (useNewAPI && configLoading) return;
|
|
1164
1327
|
let alive = true;
|
|
1165
|
-
const
|
|
1328
|
+
const isHeatmapMode = typeof window !== "undefined" && new URLSearchParams(window.location.search).get("heatmap") === "true";
|
|
1329
|
+
if (context.proposalId === proposalId && context.variantLabel) {
|
|
1330
|
+
console.log(`[PROBAT HOC] Forced variant from context: ${context.variantLabel}`);
|
|
1331
|
+
setChoice({
|
|
1332
|
+
experiment_id: `forced_${proposalId}`,
|
|
1333
|
+
label: context.variantLabel
|
|
1334
|
+
});
|
|
1335
|
+
return;
|
|
1336
|
+
}
|
|
1337
|
+
const cached = isHeatmapMode ? null : readChoice(proposalId);
|
|
1166
1338
|
if (cached) {
|
|
1167
1339
|
const choiceData = {
|
|
1168
1340
|
experiment_id: cached.experiment_id,
|
|
1169
1341
|
label: cached.label
|
|
1170
1342
|
};
|
|
1171
1343
|
setChoice(choiceData);
|
|
1172
|
-
if (typeof window !== "undefined") {
|
|
1344
|
+
if (typeof window !== "undefined" && !isHeatmapMode) {
|
|
1173
1345
|
try {
|
|
1174
1346
|
window.localStorage.setItem("probat_active_proposal_id", proposalId);
|
|
1175
1347
|
window.localStorage.setItem("probat_active_variant_label", cached.label);
|
|
@@ -1182,17 +1354,19 @@ function withExperiment(Control, options) {
|
|
|
1182
1354
|
try {
|
|
1183
1355
|
const { experiment_id, label: label2 } = await fetchDecision(apiBaseUrl, proposalId);
|
|
1184
1356
|
if (!alive) return;
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1357
|
+
if (!isHeatmapMode) {
|
|
1358
|
+
writeChoice(proposalId, experiment_id, label2);
|
|
1359
|
+
if (typeof window !== "undefined") {
|
|
1360
|
+
try {
|
|
1361
|
+
window.localStorage.setItem("probat_active_proposal_id", proposalId);
|
|
1362
|
+
window.localStorage.setItem("probat_active_variant_label", label2);
|
|
1363
|
+
} catch (e) {
|
|
1364
|
+
console.warn("[PROBAT] Failed to set proposal/variant in localStorage:", e);
|
|
1365
|
+
}
|
|
1194
1366
|
}
|
|
1195
1367
|
}
|
|
1368
|
+
const choiceData = { experiment_id, label: label2 };
|
|
1369
|
+
setChoice(choiceData);
|
|
1196
1370
|
} catch (e) {
|
|
1197
1371
|
if (!alive) return;
|
|
1198
1372
|
const choiceData = {
|
|
@@ -1200,7 +1374,7 @@ function withExperiment(Control, options) {
|
|
|
1200
1374
|
label: "control"
|
|
1201
1375
|
};
|
|
1202
1376
|
setChoice(choiceData);
|
|
1203
|
-
if (typeof window !== "undefined") {
|
|
1377
|
+
if (typeof window !== "undefined" && !isHeatmapMode) {
|
|
1204
1378
|
try {
|
|
1205
1379
|
window.localStorage.setItem("probat_active_proposal_id", proposalId);
|
|
1206
1380
|
window.localStorage.setItem("probat_active_variant_label", "control");
|
|
@@ -1214,7 +1388,7 @@ function withExperiment(Control, options) {
|
|
|
1214
1388
|
return () => {
|
|
1215
1389
|
alive = false;
|
|
1216
1390
|
};
|
|
1217
|
-
}, [proposalId, apiBaseUrl, useNewAPI, configLoading]);
|
|
1391
|
+
}, [proposalId, apiBaseUrl, useNewAPI, configLoading, context.proposalId, context.variantLabel]);
|
|
1218
1392
|
React4.useEffect(() => {
|
|
1219
1393
|
if (!proposalId) return;
|
|
1220
1394
|
const lbl = choice?.label ?? "control";
|