@cshah18/sdk 4.13.0 → 4.14.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/dist/cobuy-sdk.esm.js +608 -26
- package/dist/cobuy-sdk.esm.js.map +1 -1
- package/dist/cobuy-sdk.umd.js +608 -26
- package/dist/cobuy-sdk.umd.js.map +1 -1
- package/dist/types/core/analytics.d.ts +55 -0
- package/dist/types/core/api-client.d.ts +17 -2
- package/dist/types/core/endpoints.d.ts +1 -0
- package/dist/types/core/types.d.ts +79 -3
- package/dist/types/ui/group-list/group-list-modal.d.ts +4 -0
- package/dist/types/ui/widget/widget-root.d.ts +6 -0
- package/package.json +1 -1
package/dist/cobuy-sdk.umd.js
CHANGED
|
@@ -455,6 +455,8 @@ var CoBuySDK = (function (exports) {
|
|
|
455
455
|
debug,
|
|
456
456
|
events: options.events,
|
|
457
457
|
performance,
|
|
458
|
+
geo: options.geo,
|
|
459
|
+
device: options.device,
|
|
458
460
|
};
|
|
459
461
|
return this.config;
|
|
460
462
|
}
|
|
@@ -822,6 +824,7 @@ var CoBuySDK = (function (exports) {
|
|
|
822
824
|
this.currentJoinedGroupId = null;
|
|
823
825
|
this.currentProductId = null;
|
|
824
826
|
this.currentSessionId = null;
|
|
827
|
+
this.analyticsClient = null;
|
|
825
828
|
this.onGroupJoined = null;
|
|
826
829
|
this.onViewProgress = null;
|
|
827
830
|
this.socketListenerRegistered = false;
|
|
@@ -836,6 +839,7 @@ var CoBuySDK = (function (exports) {
|
|
|
836
839
|
/** Handle group member joined socket event and update groups list */
|
|
837
840
|
this.onGroupMemberJoined = (event) => {
|
|
838
841
|
const detail = event.detail || {};
|
|
842
|
+
console.log("eventttttt", detail);
|
|
839
843
|
const productId = detail.product_id;
|
|
840
844
|
const groupData = detail.group;
|
|
841
845
|
// Only process if this is for the current product
|
|
@@ -868,6 +872,10 @@ var CoBuySDK = (function (exports) {
|
|
|
868
872
|
this.apiClient = apiClient;
|
|
869
873
|
this.injectStyles();
|
|
870
874
|
}
|
|
875
|
+
/** Set the analytics client for event tracking */
|
|
876
|
+
setAnalyticsClient(client) {
|
|
877
|
+
this.analyticsClient = client;
|
|
878
|
+
}
|
|
871
879
|
/** Set callback for when a group is joined successfully */
|
|
872
880
|
setOnGroupJoined(callback) {
|
|
873
881
|
this.onGroupJoined = callback;
|
|
@@ -1125,10 +1133,21 @@ var CoBuySDK = (function (exports) {
|
|
|
1125
1133
|
buttonElement.disabled = true;
|
|
1126
1134
|
const originalText = button.textContent;
|
|
1127
1135
|
button.textContent = "Joining...";
|
|
1136
|
+
const productId = this.currentProductId || "";
|
|
1137
|
+
if (this.analyticsClient) {
|
|
1138
|
+
this.analyticsClient
|
|
1139
|
+
.trackJoinAttempt(productId, groupId)
|
|
1140
|
+
.catch((e) => this.logger.warn("Analytics tracking failed", e));
|
|
1141
|
+
}
|
|
1128
1142
|
try {
|
|
1129
1143
|
const response = await this.apiClient.joinGroup(groupId);
|
|
1130
1144
|
if (response.success && response.data) {
|
|
1131
1145
|
this.logger.info(`[handleJoinGroup] Successfully joined group: ${groupId}`, response.data);
|
|
1146
|
+
if (this.analyticsClient) {
|
|
1147
|
+
this.analyticsClient
|
|
1148
|
+
.trackJoinSuccess(productId, groupId)
|
|
1149
|
+
.catch((e) => this.logger.warn("Analytics tracking failed", e));
|
|
1150
|
+
}
|
|
1132
1151
|
// Track the joined group
|
|
1133
1152
|
this.currentJoinedGroupId = groupId;
|
|
1134
1153
|
// Close the modal and trigger callback with the full join response data
|
|
@@ -1139,12 +1158,22 @@ var CoBuySDK = (function (exports) {
|
|
|
1139
1158
|
}
|
|
1140
1159
|
else {
|
|
1141
1160
|
this.logger.error("Failed to join group: API response unsuccessful");
|
|
1161
|
+
if (this.analyticsClient) {
|
|
1162
|
+
this.analyticsClient
|
|
1163
|
+
.trackJoinFailure(productId, groupId)
|
|
1164
|
+
.catch((e) => this.logger.warn("Analytics tracking failed", e));
|
|
1165
|
+
}
|
|
1142
1166
|
button.textContent = originalText;
|
|
1143
1167
|
buttonElement.disabled = false;
|
|
1144
1168
|
}
|
|
1145
1169
|
}
|
|
1146
1170
|
catch (error) {
|
|
1147
1171
|
this.logger.error("Error joining group", error);
|
|
1172
|
+
if (this.analyticsClient) {
|
|
1173
|
+
this.analyticsClient
|
|
1174
|
+
.trackJoinFailure(productId, groupId, "EXCEPTION", error instanceof Error ? error.message : String(error))
|
|
1175
|
+
.catch((e) => this.logger.warn("Analytics tracking failed", e));
|
|
1176
|
+
}
|
|
1148
1177
|
button.textContent = originalText;
|
|
1149
1178
|
buttonElement.disabled = false;
|
|
1150
1179
|
}
|
|
@@ -1165,10 +1194,21 @@ var CoBuySDK = (function (exports) {
|
|
|
1165
1194
|
this.setJoinButtonsDisabled(true);
|
|
1166
1195
|
const originalText = button.textContent;
|
|
1167
1196
|
button.textContent = "Creating...";
|
|
1197
|
+
const productId = this.currentProductId;
|
|
1198
|
+
if (this.analyticsClient) {
|
|
1199
|
+
this.analyticsClient
|
|
1200
|
+
.trackGroupCreateAttempt(productId)
|
|
1201
|
+
.catch((e) => this.logger.warn("Analytics tracking failed", e));
|
|
1202
|
+
}
|
|
1168
1203
|
try {
|
|
1169
1204
|
const response = await this.apiClient.createAndJoinGroup(this.currentProductId);
|
|
1170
1205
|
if (response.success && response.data) {
|
|
1171
1206
|
this.logger.info("Successfully created and joined new group", response.data);
|
|
1207
|
+
if (this.analyticsClient) {
|
|
1208
|
+
this.analyticsClient
|
|
1209
|
+
.trackGroupCreateSuccess(productId, response.data.group.id)
|
|
1210
|
+
.catch((e) => this.logger.warn("Analytics tracking failed", e));
|
|
1211
|
+
}
|
|
1172
1212
|
this.currentJoinedGroupId = response.data.group.id;
|
|
1173
1213
|
this.close();
|
|
1174
1214
|
if (this.onGroupJoined) {
|
|
@@ -1177,6 +1217,11 @@ var CoBuySDK = (function (exports) {
|
|
|
1177
1217
|
}
|
|
1178
1218
|
else {
|
|
1179
1219
|
this.logger.error("Failed to create group: API response unsuccessful");
|
|
1220
|
+
if (this.analyticsClient) {
|
|
1221
|
+
this.analyticsClient
|
|
1222
|
+
.trackGroupCreateFailure(productId)
|
|
1223
|
+
.catch((e) => this.logger.warn("Analytics tracking failed", e));
|
|
1224
|
+
}
|
|
1180
1225
|
button.textContent = originalText;
|
|
1181
1226
|
buttonElement.disabled = false;
|
|
1182
1227
|
this.setJoinButtonsDisabled(false);
|
|
@@ -1184,6 +1229,11 @@ var CoBuySDK = (function (exports) {
|
|
|
1184
1229
|
}
|
|
1185
1230
|
catch (error) {
|
|
1186
1231
|
this.logger.error("Error creating group", error);
|
|
1232
|
+
if (this.analyticsClient) {
|
|
1233
|
+
this.analyticsClient
|
|
1234
|
+
.trackGroupCreateFailure(productId, "EXCEPTION", error instanceof Error ? error.message : String(error))
|
|
1235
|
+
.catch((e) => this.logger.warn("Analytics tracking failed", e));
|
|
1236
|
+
}
|
|
1187
1237
|
button.textContent = originalText;
|
|
1188
1238
|
buttonElement.disabled = false;
|
|
1189
1239
|
this.setJoinButtonsDisabled(false);
|
|
@@ -3581,14 +3631,13 @@ var CoBuySDK = (function (exports) {
|
|
|
3581
3631
|
document.body.appendChild(this.modalElement);
|
|
3582
3632
|
// Subscribe to realtime socket events
|
|
3583
3633
|
this.subscribeToSocketEvents();
|
|
3584
|
-
//
|
|
3585
|
-
|
|
3586
|
-
|
|
3634
|
+
// Subscribe to the group room for targeted events.
|
|
3635
|
+
// Socket.IO client buffers emits until connected, so this is safe even
|
|
3636
|
+
// if the handshake hasn't completed yet.
|
|
3637
|
+
if (this.socketManager && this.currentGroupId) {
|
|
3638
|
+
this.logger.info(`[LobbyModal] Subscribing to group room ${this.currentGroupId}`);
|
|
3587
3639
|
this.socketManager.subscribeToGroup(this.currentGroupId);
|
|
3588
3640
|
}
|
|
3589
|
-
else if (this.socketManager && !this.socketManager.isConnected()) {
|
|
3590
|
-
this.logger.warn("[LobbyModal] Socket manager not connected yet");
|
|
3591
|
-
}
|
|
3592
3641
|
// Start timers and animations
|
|
3593
3642
|
this.startTimer();
|
|
3594
3643
|
this.startActivityAnimation();
|
|
@@ -4389,10 +4438,18 @@ var CoBuySDK = (function (exports) {
|
|
|
4389
4438
|
/** Handle backend fulfillment notifications */
|
|
4390
4439
|
this.handleGroupFulfilledEvent = (event) => {
|
|
4391
4440
|
const detail = event.detail;
|
|
4392
|
-
if (!detail || !detail.
|
|
4441
|
+
if (!detail || !detail.product_id) {
|
|
4442
|
+
return;
|
|
4443
|
+
}
|
|
4444
|
+
if (this.currentProductId && detail.product_id !== this.currentProductId) {
|
|
4393
4445
|
return;
|
|
4394
4446
|
}
|
|
4395
|
-
|
|
4447
|
+
// Only enter the fulfilled/checkout flow for users who have actively joined a group this
|
|
4448
|
+
// session. currentSessionId is set exclusively in the setOnGroupJoined callback (when the
|
|
4449
|
+
// user completes a join API call). Observers who never joined should re-fetch the primary
|
|
4450
|
+
// group API to display the updated group state instead.
|
|
4451
|
+
if (!this.currentSessionId) {
|
|
4452
|
+
void this.refreshGroupDataFromRealtime();
|
|
4396
4453
|
return;
|
|
4397
4454
|
}
|
|
4398
4455
|
this.processGroupFulfilled(detail);
|
|
@@ -4449,12 +4506,21 @@ var CoBuySDK = (function (exports) {
|
|
|
4449
4506
|
const groupData = await this.fetchPrimaryGroup(this.currentProductId);
|
|
4450
4507
|
this.currentGroupData = groupData;
|
|
4451
4508
|
this.currentGroupId = (groupData === null || groupData === void 0 ? void 0 : groupData.id) || this.currentGroupId;
|
|
4452
|
-
// If backend signals completion via counts, reflect it
|
|
4509
|
+
// If backend signals completion via counts, reflect it — but only for actual members.
|
|
4510
|
+
// Observers may see a full group returned by fetchPrimaryGroup; they should NOT enter
|
|
4511
|
+
// the checkout flow. currentSessionId is the membership signal (non-null = joined).
|
|
4453
4512
|
if (groupData) {
|
|
4454
4513
|
const participants = Number(groupData.participants_count || 0);
|
|
4455
4514
|
const max = Number(groupData.max_participants || 0);
|
|
4456
|
-
if (max > 0 && participants >= max) {
|
|
4515
|
+
if (max > 0 && participants >= max && this.currentSessionId) {
|
|
4457
4516
|
this.groupFulfilled = true;
|
|
4517
|
+
// The primary group API returns offline_redemption for members of fulfilled groups.
|
|
4518
|
+
// Extract it here so renderFulfilledSummary shows the "Redeem In-store" link
|
|
4519
|
+
// (matching the UI users see on a full page reload).
|
|
4520
|
+
if (groupData.offline_redemption &&
|
|
4521
|
+
isValidOfflineRedemption(groupData.offline_redemption)) {
|
|
4522
|
+
this.offlineRedemption = groupData.offline_redemption;
|
|
4523
|
+
}
|
|
4458
4524
|
}
|
|
4459
4525
|
}
|
|
4460
4526
|
else {
|
|
@@ -4799,6 +4865,9 @@ var CoBuySDK = (function (exports) {
|
|
|
4799
4865
|
getGroupListModal() {
|
|
4800
4866
|
if (!this.groupListModal) {
|
|
4801
4867
|
this.groupListModal = new GroupListModal(undefined, 8, this.config.debug, this.apiClient);
|
|
4868
|
+
if (this.analyticsClient) {
|
|
4869
|
+
this.groupListModal.setAnalyticsClient(this.analyticsClient);
|
|
4870
|
+
}
|
|
4802
4871
|
// Set callback to open lobby when a group is successfully joined
|
|
4803
4872
|
this.groupListModal.setOnGroupJoined((joinData) => {
|
|
4804
4873
|
var _a;
|
|
@@ -5024,6 +5093,11 @@ var CoBuySDK = (function (exports) {
|
|
|
5024
5093
|
if (max > 0 && participants >= max) {
|
|
5025
5094
|
this.groupFulfilled = true;
|
|
5026
5095
|
this.logger.info("Group is already fulfilled on initial load", { participants, max });
|
|
5096
|
+
if (this.analyticsClient) {
|
|
5097
|
+
this.analyticsClient
|
|
5098
|
+
.trackGroupFullView(options.productId, groupData.id, max)
|
|
5099
|
+
.catch((e) => this.logger.warn("Analytics tracking failed", e));
|
|
5100
|
+
}
|
|
5027
5101
|
}
|
|
5028
5102
|
}
|
|
5029
5103
|
}
|
|
@@ -5089,6 +5163,11 @@ var CoBuySDK = (function (exports) {
|
|
|
5089
5163
|
// LOADED state - render widget only if we have group data
|
|
5090
5164
|
this.createWidget(rewardData, container, options);
|
|
5091
5165
|
this.logger.info(`Widget rendered for product: ${options.productId}`);
|
|
5166
|
+
if (this.analyticsClient) {
|
|
5167
|
+
this.analyticsClient
|
|
5168
|
+
.trackCreativeView(options.productId)
|
|
5169
|
+
.catch((e) => this.logger.warn("Analytics tracking failed", e));
|
|
5170
|
+
}
|
|
5092
5171
|
}
|
|
5093
5172
|
else {
|
|
5094
5173
|
// No group data available - hide widget
|
|
@@ -5740,7 +5819,7 @@ var CoBuySDK = (function (exports) {
|
|
|
5740
5819
|
* Handle CTA button click with analytics and modal opening
|
|
5741
5820
|
*/
|
|
5742
5821
|
async handleCTAClick(productId) {
|
|
5743
|
-
var _a, _b, _c, _d;
|
|
5822
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
5744
5823
|
this.logger.info(`CTA clicked for product: ${productId}`);
|
|
5745
5824
|
// Track analytics event asynchronously (fire-and-forget)
|
|
5746
5825
|
if (this.analyticsClient) {
|
|
@@ -5758,14 +5837,31 @@ var CoBuySDK = (function (exports) {
|
|
|
5758
5837
|
if (this.apiClient && this.currentGroupId) {
|
|
5759
5838
|
try {
|
|
5760
5839
|
this.logger.info(`Joining group: ${this.currentGroupId}`);
|
|
5840
|
+
if (this.analyticsClient) {
|
|
5841
|
+
this.analyticsClient
|
|
5842
|
+
.trackJoinAttempt(productId, this.currentGroupId)
|
|
5843
|
+
.catch((e) => this.logger.warn("Analytics tracking failed", e));
|
|
5844
|
+
}
|
|
5761
5845
|
const joinResponse = await this.apiClient.joinGroup(this.currentGroupId);
|
|
5762
5846
|
if (!joinResponse.success) {
|
|
5763
5847
|
this.logger.error("Failed to join group, modal will not open", joinResponse.error);
|
|
5848
|
+
if (this.analyticsClient) {
|
|
5849
|
+
const errCode = (_c = joinResponse.error) === null || _c === void 0 ? void 0 : _c.code;
|
|
5850
|
+
const errMsg = (_d = joinResponse.error) === null || _d === void 0 ? void 0 : _d.message;
|
|
5851
|
+
this.analyticsClient
|
|
5852
|
+
.trackJoinFailure(productId, this.currentGroupId, errCode, errMsg)
|
|
5853
|
+
.catch((e) => this.logger.warn("Analytics tracking failed", e));
|
|
5854
|
+
}
|
|
5764
5855
|
this.setButtonLoadingState(false);
|
|
5765
5856
|
return; // Don't open modal on error
|
|
5766
5857
|
}
|
|
5767
5858
|
groupJoinData = joinResponse.data;
|
|
5768
5859
|
this.logger.info("Successfully joined group", groupJoinData);
|
|
5860
|
+
if (this.analyticsClient && groupJoinData) {
|
|
5861
|
+
this.analyticsClient
|
|
5862
|
+
.trackJoinSuccess(productId, groupJoinData.group.id)
|
|
5863
|
+
.catch((e) => this.logger.warn("Analytics tracking failed", e));
|
|
5864
|
+
}
|
|
5769
5865
|
this.offlineRedemption =
|
|
5770
5866
|
groupJoinData &&
|
|
5771
5867
|
groupJoinData.offline_redemption &&
|
|
@@ -5789,6 +5885,11 @@ var CoBuySDK = (function (exports) {
|
|
|
5789
5885
|
}
|
|
5790
5886
|
catch (error) {
|
|
5791
5887
|
this.logger.error("Group join failed, modal will not open", error);
|
|
5888
|
+
if (this.analyticsClient) {
|
|
5889
|
+
this.analyticsClient
|
|
5890
|
+
.trackJoinFailure(productId, (_e = this.currentGroupId) !== null && _e !== void 0 ? _e : undefined, "EXCEPTION", error instanceof Error ? error.message : String(error))
|
|
5891
|
+
.catch((e) => this.logger.warn("Analytics tracking failed", e));
|
|
5892
|
+
}
|
|
5792
5893
|
this.setButtonLoadingState(false);
|
|
5793
5894
|
return; // Don't open modal on error
|
|
5794
5895
|
}
|
|
@@ -5803,7 +5904,7 @@ var CoBuySDK = (function (exports) {
|
|
|
5803
5904
|
const progress = Math.round((groupJoinData.group.participants_count / groupJoinData.group.max_participants) * 100);
|
|
5804
5905
|
// Format discount based on reward type
|
|
5805
5906
|
let discountText = "";
|
|
5806
|
-
if ((
|
|
5907
|
+
if ((_g = (_f = this.currentRewardData) === null || _f === void 0 ? void 0 : _f.reward) === null || _g === void 0 ? void 0 : _g.value) {
|
|
5807
5908
|
const rewardType = this.currentRewardData.reward.type;
|
|
5808
5909
|
const rewardValue = this.currentRewardData.reward.value;
|
|
5809
5910
|
if (rewardType === "percentage" || rewardType === "cashback") {
|
|
@@ -5843,6 +5944,11 @@ var CoBuySDK = (function (exports) {
|
|
|
5843
5944
|
shareMessage: shareMessageFromInvite,
|
|
5844
5945
|
isLocked: !isGroupFulfilled,
|
|
5845
5946
|
offlineRedemption: offlineRedemptionFromJoin,
|
|
5947
|
+
onShare: this.analyticsClient
|
|
5948
|
+
? () => {
|
|
5949
|
+
this.analyticsClient.trackShareClick(productId, groupJoinData.group.id, "other").catch((e) => this.logger.warn("Analytics tracking failed", e));
|
|
5950
|
+
}
|
|
5951
|
+
: undefined,
|
|
5846
5952
|
activities: [
|
|
5847
5953
|
{
|
|
5848
5954
|
emoji: "👤",
|
|
@@ -5878,6 +5984,12 @@ var CoBuySDK = (function (exports) {
|
|
|
5878
5984
|
},
|
|
5879
5985
|
], // Will be populated from real-time data later
|
|
5880
5986
|
});
|
|
5987
|
+
// Track popup open after modal is launched
|
|
5988
|
+
if (this.analyticsClient) {
|
|
5989
|
+
this.analyticsClient
|
|
5990
|
+
.trackPopupOpen(productId, groupJoinData.group.id)
|
|
5991
|
+
.catch((e) => this.logger.warn("Analytics tracking failed", e));
|
|
5992
|
+
}
|
|
5881
5993
|
// Remove loading state after modal opens
|
|
5882
5994
|
this.setButtonLoadingState(false);
|
|
5883
5995
|
}
|
|
@@ -5984,6 +6096,14 @@ var CoBuySDK = (function (exports) {
|
|
|
5984
6096
|
getProductId() {
|
|
5985
6097
|
return this.currentProductId;
|
|
5986
6098
|
}
|
|
6099
|
+
/**
|
|
6100
|
+
* Returns the group ID that the current user has joined via this widget, or null if the user
|
|
6101
|
+
* is only an observer (has not actively joined a group this session).
|
|
6102
|
+
* Used by the CoBuy host class to check membership before preparing checkout.
|
|
6103
|
+
*/
|
|
6104
|
+
getJoinedGroupId() {
|
|
6105
|
+
return this.currentSessionId ? this.currentGroupId : null;
|
|
6106
|
+
}
|
|
5987
6107
|
}
|
|
5988
6108
|
|
|
5989
6109
|
/**
|
|
@@ -5991,6 +6111,7 @@ var CoBuySDK = (function (exports) {
|
|
|
5991
6111
|
*/
|
|
5992
6112
|
const API_ENDPOINTS = {
|
|
5993
6113
|
// Product endpoints
|
|
6114
|
+
PRODUCT_CONTEXT: "/v1/sdk/products/:productId/context",
|
|
5994
6115
|
PRODUCT_REWARD: "/v1/sdk/products/:productId/reward",
|
|
5995
6116
|
PRODUCT_PRIMARY_GROUP: "/v1/sdk/products/:productId/group/primary",
|
|
5996
6117
|
// Group endpoints
|
|
@@ -6041,8 +6162,11 @@ var CoBuySDK = (function (exports) {
|
|
|
6041
6162
|
var _a;
|
|
6042
6163
|
this.traceId = null;
|
|
6043
6164
|
this.rewardCache = new Map();
|
|
6165
|
+
this.productContextCache = new Map();
|
|
6044
6166
|
this.REWARD_CACHE_TTL = 60000; // 1 minute
|
|
6045
|
-
this.
|
|
6167
|
+
this.PRODUCT_CONTEXT_CACHE_TTL = 30000; // 30 seconds
|
|
6168
|
+
this.pendingRewardRequests = new Map();
|
|
6169
|
+
this.pendingContextRequests = new Map();
|
|
6046
6170
|
this.baseUrl = config.baseUrl;
|
|
6047
6171
|
this.authStrategy = config.authStrategy;
|
|
6048
6172
|
this.sessionId = config.sessionId;
|
|
@@ -6438,6 +6562,106 @@ var CoBuySDK = (function (exports) {
|
|
|
6438
6562
|
getTraceId() {
|
|
6439
6563
|
return this.traceId;
|
|
6440
6564
|
}
|
|
6565
|
+
getCachedProductContext(productId) {
|
|
6566
|
+
const cached = this.productContextCache.get(productId);
|
|
6567
|
+
if (cached && cached.expires > Date.now()) {
|
|
6568
|
+
return cached.data;
|
|
6569
|
+
}
|
|
6570
|
+
if (cached) {
|
|
6571
|
+
this.productContextCache.delete(productId);
|
|
6572
|
+
}
|
|
6573
|
+
return null;
|
|
6574
|
+
}
|
|
6575
|
+
normalizePrimaryGroup(groupData) {
|
|
6576
|
+
if (!groupData) {
|
|
6577
|
+
return null;
|
|
6578
|
+
}
|
|
6579
|
+
const nestedGroup = groupData.group;
|
|
6580
|
+
if (nestedGroup && typeof nestedGroup === "object" && "id" in nestedGroup) {
|
|
6581
|
+
return nestedGroup;
|
|
6582
|
+
}
|
|
6583
|
+
return groupData;
|
|
6584
|
+
}
|
|
6585
|
+
buildRewardDataFromContext(productId, context) {
|
|
6586
|
+
var _a, _b, _c, _d, _e;
|
|
6587
|
+
const primaryGroup = this.normalizePrimaryGroup(context.primary_group);
|
|
6588
|
+
return {
|
|
6589
|
+
productId,
|
|
6590
|
+
reward: (_a = context.reward) !== null && _a !== void 0 ? _a : null,
|
|
6591
|
+
campaign_mode: (_c = (_b = context.campaign) === null || _b === void 0 ? void 0 : _b.campaign_mode) !== null && _c !== void 0 ? _c : "group",
|
|
6592
|
+
campaign_creative_id: (_d = primaryGroup === null || primaryGroup === void 0 ? void 0 : primaryGroup.campaign_creative_id) !== null && _d !== void 0 ? _d : null,
|
|
6593
|
+
eligibility: (_e = context.eligibility) !== null && _e !== void 0 ? _e : { isEligible: false },
|
|
6594
|
+
};
|
|
6595
|
+
}
|
|
6596
|
+
setProductContextCache(productId, context) {
|
|
6597
|
+
this.productContextCache.set(productId, {
|
|
6598
|
+
data: context,
|
|
6599
|
+
expires: Date.now() + this.PRODUCT_CONTEXT_CACHE_TTL,
|
|
6600
|
+
});
|
|
6601
|
+
this.rewardCache.set(productId, {
|
|
6602
|
+
data: this.buildRewardDataFromContext(productId, context),
|
|
6603
|
+
expires: Date.now() + this.REWARD_CACHE_TTL,
|
|
6604
|
+
});
|
|
6605
|
+
}
|
|
6606
|
+
/**
|
|
6607
|
+
* Get product context information
|
|
6608
|
+
*
|
|
6609
|
+
* Uses the consolidated bootstrap endpoint so the SDK can resolve campaign,
|
|
6610
|
+
* reward, primary group, and active groups in a single request.
|
|
6611
|
+
*/
|
|
6612
|
+
async getProductContext(productId) {
|
|
6613
|
+
if (!validateProductId(productId)) {
|
|
6614
|
+
return {
|
|
6615
|
+
success: false,
|
|
6616
|
+
error: {
|
|
6617
|
+
message: "Invalid productId format. Must be a non-empty string (max 200 chars).",
|
|
6618
|
+
code: "INVALID_PRODUCT_ID",
|
|
6619
|
+
},
|
|
6620
|
+
};
|
|
6621
|
+
}
|
|
6622
|
+
// const cached = this.getCachedProductContext(productId);
|
|
6623
|
+
// if (cached) {
|
|
6624
|
+
// this.logger.info(`Using cached product context for product: ${productId}`);
|
|
6625
|
+
// return {
|
|
6626
|
+
// success: true,
|
|
6627
|
+
// data: cached,
|
|
6628
|
+
// };
|
|
6629
|
+
// }
|
|
6630
|
+
const cacheKey = `context:${productId}`;
|
|
6631
|
+
const pending = this.pendingContextRequests.get(cacheKey);
|
|
6632
|
+
if (pending) {
|
|
6633
|
+
this.logger.info(`Deduplicating product context request for: ${productId}`);
|
|
6634
|
+
return pending;
|
|
6635
|
+
}
|
|
6636
|
+
const promise = this.fetchProductContext(productId).then((response) => {
|
|
6637
|
+
if (response.success && response.data) {
|
|
6638
|
+
this.setProductContextCache(productId, response.data);
|
|
6639
|
+
}
|
|
6640
|
+
return response;
|
|
6641
|
+
});
|
|
6642
|
+
this.pendingContextRequests.set(cacheKey, promise);
|
|
6643
|
+
promise.then(() => this.pendingContextRequests.delete(cacheKey), () => this.pendingContextRequests.delete(cacheKey));
|
|
6644
|
+
return promise;
|
|
6645
|
+
}
|
|
6646
|
+
async fetchProductContext(productId) {
|
|
6647
|
+
var _a;
|
|
6648
|
+
const endpoint = buildApiUrl("", API_ENDPOINTS.PRODUCT_CONTEXT, { productId });
|
|
6649
|
+
this.logger.info(`Fetching product context for product: ${productId}`);
|
|
6650
|
+
const response = await this.get(endpoint);
|
|
6651
|
+
if (response.success && ((_a = response.data) === null || _a === void 0 ? void 0 : _a.data)) {
|
|
6652
|
+
return {
|
|
6653
|
+
success: true,
|
|
6654
|
+
data: response.data.data,
|
|
6655
|
+
};
|
|
6656
|
+
}
|
|
6657
|
+
return {
|
|
6658
|
+
success: false,
|
|
6659
|
+
error: response.error || {
|
|
6660
|
+
message: "Failed to fetch product context data",
|
|
6661
|
+
code: "PRODUCT_CONTEXT_FETCH_ERROR",
|
|
6662
|
+
},
|
|
6663
|
+
};
|
|
6664
|
+
}
|
|
6441
6665
|
/**
|
|
6442
6666
|
* Get product reward information
|
|
6443
6667
|
*
|
|
@@ -6482,13 +6706,31 @@ var CoBuySDK = (function (exports) {
|
|
|
6482
6706
|
data: cached.data,
|
|
6483
6707
|
};
|
|
6484
6708
|
}
|
|
6709
|
+
const cachedContext = this.getCachedProductContext(productId);
|
|
6710
|
+
if (cachedContext) {
|
|
6711
|
+
this.logger.info(`Using cached product context reward for product: ${productId}`);
|
|
6712
|
+
return {
|
|
6713
|
+
success: true,
|
|
6714
|
+
data: this.buildRewardDataFromContext(productId, cachedContext),
|
|
6715
|
+
};
|
|
6716
|
+
}
|
|
6485
6717
|
const cacheKey = `reward:${productId}`;
|
|
6486
|
-
const pending = this.
|
|
6718
|
+
const pending = this.pendingRewardRequests.get(cacheKey);
|
|
6487
6719
|
if (pending) {
|
|
6488
6720
|
this.logger.info(`Deduplicating request for product: ${productId}`);
|
|
6489
6721
|
return pending;
|
|
6490
6722
|
}
|
|
6491
|
-
const promise =
|
|
6723
|
+
const promise = (async () => {
|
|
6724
|
+
const contextResponse = await this.getProductContext(productId);
|
|
6725
|
+
if (contextResponse.success && contextResponse.data) {
|
|
6726
|
+
return {
|
|
6727
|
+
success: true,
|
|
6728
|
+
data: this.buildRewardDataFromContext(productId, contextResponse.data),
|
|
6729
|
+
};
|
|
6730
|
+
}
|
|
6731
|
+
this.logger.info(`Falling back to legacy reward endpoint for product: ${productId}`);
|
|
6732
|
+
return this.fetchProductReward(productId);
|
|
6733
|
+
})().then((response) => {
|
|
6492
6734
|
if (response.success && response.data) {
|
|
6493
6735
|
this.rewardCache.set(productId, {
|
|
6494
6736
|
data: response.data,
|
|
@@ -6497,8 +6739,8 @@ var CoBuySDK = (function (exports) {
|
|
|
6497
6739
|
}
|
|
6498
6740
|
return response;
|
|
6499
6741
|
});
|
|
6500
|
-
this.
|
|
6501
|
-
promise.then(() => this.
|
|
6742
|
+
this.pendingRewardRequests.set(cacheKey, promise);
|
|
6743
|
+
promise.then(() => this.pendingRewardRequests.delete(cacheKey), () => this.pendingRewardRequests.delete(cacheKey));
|
|
6502
6744
|
return promise;
|
|
6503
6745
|
}
|
|
6504
6746
|
/**
|
|
@@ -6514,6 +6756,7 @@ var CoBuySDK = (function (exports) {
|
|
|
6514
6756
|
*/
|
|
6515
6757
|
clearRewardCache() {
|
|
6516
6758
|
this.rewardCache.clear();
|
|
6759
|
+
this.productContextCache.clear();
|
|
6517
6760
|
}
|
|
6518
6761
|
async fetchProductReward(productId) {
|
|
6519
6762
|
var _a;
|
|
@@ -6561,7 +6804,7 @@ var CoBuySDK = (function (exports) {
|
|
|
6561
6804
|
* ```
|
|
6562
6805
|
*/
|
|
6563
6806
|
async getProductPrimaryGroup(productId) {
|
|
6564
|
-
var _a;
|
|
6807
|
+
var _a, _b;
|
|
6565
6808
|
if (!validateProductId(productId)) {
|
|
6566
6809
|
return {
|
|
6567
6810
|
success: false,
|
|
@@ -6571,15 +6814,38 @@ var CoBuySDK = (function (exports) {
|
|
|
6571
6814
|
},
|
|
6572
6815
|
};
|
|
6573
6816
|
}
|
|
6574
|
-
const
|
|
6575
|
-
|
|
6576
|
-
|
|
6577
|
-
|
|
6817
|
+
// const cachedContext = this.getCachedProductContext(productId);
|
|
6818
|
+
// const cachedGroup = this.normalizePrimaryGroup(cachedContext?.primary_group);
|
|
6819
|
+
// if (cachedGroup) {
|
|
6820
|
+
// this.logger.info(`Using cached primary group from product context for product: ${productId}`);
|
|
6821
|
+
// return {
|
|
6822
|
+
// success: true,
|
|
6823
|
+
// data: {
|
|
6824
|
+
// ...cachedGroup,
|
|
6825
|
+
// group: cachedGroup,
|
|
6826
|
+
// },
|
|
6827
|
+
// };
|
|
6828
|
+
// }
|
|
6829
|
+
const contextResponse = await this.getProductContext(productId);
|
|
6830
|
+
const contextGroup = this.normalizePrimaryGroup((_a = contextResponse.data) === null || _a === void 0 ? void 0 : _a.primary_group);
|
|
6831
|
+
if (contextResponse.success && contextGroup) {
|
|
6578
6832
|
return {
|
|
6579
6833
|
success: true,
|
|
6580
|
-
data:
|
|
6834
|
+
data: Object.assign(Object.assign({}, contextGroup), { group: contextGroup }),
|
|
6581
6835
|
};
|
|
6582
6836
|
}
|
|
6837
|
+
const endpoint = buildApiUrl("", API_ENDPOINTS.PRODUCT_PRIMARY_GROUP, { productId }, { allowAutoCreate: true });
|
|
6838
|
+
this.logger.info(`Fetching primary group for product: ${productId}`);
|
|
6839
|
+
const response = await this.get(endpoint);
|
|
6840
|
+
if (response.success && ((_b = response.data) === null || _b === void 0 ? void 0 : _b.data)) {
|
|
6841
|
+
const normalizedGroup = this.normalizePrimaryGroup(response.data.data);
|
|
6842
|
+
if (normalizedGroup) {
|
|
6843
|
+
return {
|
|
6844
|
+
success: true,
|
|
6845
|
+
data: Object.assign(Object.assign({}, normalizedGroup), { group: normalizedGroup }),
|
|
6846
|
+
};
|
|
6847
|
+
}
|
|
6848
|
+
}
|
|
6583
6849
|
return {
|
|
6584
6850
|
success: false,
|
|
6585
6851
|
error: response.error || {
|
|
@@ -6627,6 +6893,25 @@ var CoBuySDK = (function (exports) {
|
|
|
6627
6893
|
},
|
|
6628
6894
|
};
|
|
6629
6895
|
}
|
|
6896
|
+
const cachedContext = this.getCachedProductContext(productId);
|
|
6897
|
+
if (cachedContext) {
|
|
6898
|
+
this.logger.info(`Using cached active groups from product context for product: ${productId}`);
|
|
6899
|
+
return {
|
|
6900
|
+
success: true,
|
|
6901
|
+
data: {
|
|
6902
|
+
groups: (cachedContext.active_groups || []),
|
|
6903
|
+
},
|
|
6904
|
+
};
|
|
6905
|
+
}
|
|
6906
|
+
const contextResponse = await this.getProductContext(productId);
|
|
6907
|
+
if (contextResponse.success && contextResponse.data) {
|
|
6908
|
+
return {
|
|
6909
|
+
success: true,
|
|
6910
|
+
data: {
|
|
6911
|
+
groups: (contextResponse.data.active_groups || []),
|
|
6912
|
+
},
|
|
6913
|
+
};
|
|
6914
|
+
}
|
|
6630
6915
|
const endpoint = buildApiUrl("", API_ENDPOINTS.PRODUCT_ACTIVE_GROUPS, { productId });
|
|
6631
6916
|
this.logger.info(`Fetching active groups for product: ${productId}`);
|
|
6632
6917
|
const response = await this.get(endpoint);
|
|
@@ -7131,6 +7416,7 @@ var CoBuySDK = (function (exports) {
|
|
|
7131
7416
|
const event = {
|
|
7132
7417
|
event: "CTA_CLICKED",
|
|
7133
7418
|
productId,
|
|
7419
|
+
sessionId: this.sessionId,
|
|
7134
7420
|
timestamp: new Date().toISOString(),
|
|
7135
7421
|
context: {
|
|
7136
7422
|
pageUrl: typeof window !== "undefined" ? window.location.href : undefined,
|
|
@@ -7183,12 +7469,32 @@ var CoBuySDK = (function (exports) {
|
|
|
7183
7469
|
throw new CoBuyApiError(error instanceof Error ? error.message : "Unknown analytics error", "ANALYTICS_ERROR", { originalError: error });
|
|
7184
7470
|
}
|
|
7185
7471
|
}
|
|
7472
|
+
/**
|
|
7473
|
+
* Track session init event fired when the SDK initializes
|
|
7474
|
+
*/
|
|
7475
|
+
async trackSessionInit(geo, device) {
|
|
7476
|
+
const event = {
|
|
7477
|
+
event: "SESSION_INIT",
|
|
7478
|
+
sessionId: this.sessionId,
|
|
7479
|
+
timestamp: new Date().toISOString(),
|
|
7480
|
+
context: Object.assign(Object.assign({ pageUrl: typeof window !== "undefined" ? window.location.href : undefined, userAgent: typeof navigator !== "undefined" ? navigator.userAgent : undefined, sdkVersion: this.sdkVersion }, (geo ? { geo } : {})), (device ? { device } : {})),
|
|
7481
|
+
};
|
|
7482
|
+
try {
|
|
7483
|
+
await this.sendEvent(event);
|
|
7484
|
+
this.logger.info("[Analytics] Session init tracked");
|
|
7485
|
+
}
|
|
7486
|
+
catch (error) {
|
|
7487
|
+
// Non-blocking
|
|
7488
|
+
this.logger.error("[Analytics] Failed to track session init", error);
|
|
7489
|
+
}
|
|
7490
|
+
}
|
|
7186
7491
|
/**
|
|
7187
7492
|
* Track page view event
|
|
7188
7493
|
*/
|
|
7189
7494
|
async trackPageView() {
|
|
7190
7495
|
const event = {
|
|
7191
7496
|
event: "PAGE_VIEW",
|
|
7497
|
+
sessionId: this.sessionId,
|
|
7192
7498
|
timestamp: new Date().toISOString(),
|
|
7193
7499
|
context: {
|
|
7194
7500
|
pageUrl: typeof window !== "undefined" ? window.location.href : undefined,
|
|
@@ -7205,6 +7511,273 @@ var CoBuySDK = (function (exports) {
|
|
|
7205
7511
|
this.logger.error("[Analytics] Failed to track page view", error);
|
|
7206
7512
|
}
|
|
7207
7513
|
}
|
|
7514
|
+
/**
|
|
7515
|
+
* Track creative view event (widget rendered and visible)
|
|
7516
|
+
*/
|
|
7517
|
+
async trackCreativeView(productId) {
|
|
7518
|
+
const event = {
|
|
7519
|
+
event: "CREATIVE_VIEW",
|
|
7520
|
+
productId,
|
|
7521
|
+
sessionId: this.sessionId,
|
|
7522
|
+
timestamp: new Date().toISOString(),
|
|
7523
|
+
context: {
|
|
7524
|
+
pageUrl: typeof window !== "undefined" ? window.location.href : undefined,
|
|
7525
|
+
userAgent: typeof navigator !== "undefined" ? navigator.userAgent : undefined,
|
|
7526
|
+
sdkVersion: this.sdkVersion,
|
|
7527
|
+
},
|
|
7528
|
+
};
|
|
7529
|
+
try {
|
|
7530
|
+
await this.sendEvent(event);
|
|
7531
|
+
this.logger.info(`[Analytics] Creative view tracked for product: ${productId}`);
|
|
7532
|
+
}
|
|
7533
|
+
catch (error) {
|
|
7534
|
+
this.logger.error("[Analytics] Failed to track creative view", error);
|
|
7535
|
+
}
|
|
7536
|
+
}
|
|
7537
|
+
/**
|
|
7538
|
+
* Track popup/lobby modal open event
|
|
7539
|
+
*/
|
|
7540
|
+
async trackPopupOpen(productId, groupId) {
|
|
7541
|
+
const event = {
|
|
7542
|
+
event: "POPUP_OPEN",
|
|
7543
|
+
productId,
|
|
7544
|
+
sessionId: this.sessionId,
|
|
7545
|
+
timestamp: new Date().toISOString(),
|
|
7546
|
+
context: {
|
|
7547
|
+
pageUrl: typeof window !== "undefined" ? window.location.href : undefined,
|
|
7548
|
+
userAgent: typeof navigator !== "undefined" ? navigator.userAgent : undefined,
|
|
7549
|
+
sdkVersion: this.sdkVersion,
|
|
7550
|
+
groupId,
|
|
7551
|
+
},
|
|
7552
|
+
};
|
|
7553
|
+
try {
|
|
7554
|
+
await this.sendEvent(event);
|
|
7555
|
+
this.logger.info(`[Analytics] Popup open tracked for product: ${productId}`);
|
|
7556
|
+
}
|
|
7557
|
+
catch (error) {
|
|
7558
|
+
this.logger.error("[Analytics] Failed to track popup open", error);
|
|
7559
|
+
}
|
|
7560
|
+
}
|
|
7561
|
+
/**
|
|
7562
|
+
* Track join attempt event (before API call)
|
|
7563
|
+
*/
|
|
7564
|
+
async trackJoinAttempt(productId, groupId) {
|
|
7565
|
+
const event = {
|
|
7566
|
+
event: "JOIN_ATTEMPT",
|
|
7567
|
+
productId,
|
|
7568
|
+
sessionId: this.sessionId,
|
|
7569
|
+
timestamp: new Date().toISOString(),
|
|
7570
|
+
context: {
|
|
7571
|
+
pageUrl: typeof window !== "undefined" ? window.location.href : undefined,
|
|
7572
|
+
userAgent: typeof navigator !== "undefined" ? navigator.userAgent : undefined,
|
|
7573
|
+
sdkVersion: this.sdkVersion,
|
|
7574
|
+
groupId,
|
|
7575
|
+
},
|
|
7576
|
+
};
|
|
7577
|
+
try {
|
|
7578
|
+
await this.sendEvent(event);
|
|
7579
|
+
this.logger.info(`[Analytics] Join attempt tracked for product: ${productId}`);
|
|
7580
|
+
}
|
|
7581
|
+
catch (error) {
|
|
7582
|
+
this.logger.error("[Analytics] Failed to track join attempt", error);
|
|
7583
|
+
}
|
|
7584
|
+
}
|
|
7585
|
+
/**
|
|
7586
|
+
* Track successful group join event
|
|
7587
|
+
*/
|
|
7588
|
+
async trackJoinSuccess(productId, groupId) {
|
|
7589
|
+
const event = {
|
|
7590
|
+
event: "JOIN_SUCCESS",
|
|
7591
|
+
productId,
|
|
7592
|
+
sessionId: this.sessionId,
|
|
7593
|
+
timestamp: new Date().toISOString(),
|
|
7594
|
+
context: {
|
|
7595
|
+
pageUrl: typeof window !== "undefined" ? window.location.href : undefined,
|
|
7596
|
+
userAgent: typeof navigator !== "undefined" ? navigator.userAgent : undefined,
|
|
7597
|
+
sdkVersion: this.sdkVersion,
|
|
7598
|
+
groupId,
|
|
7599
|
+
},
|
|
7600
|
+
};
|
|
7601
|
+
try {
|
|
7602
|
+
await this.sendEvent(event);
|
|
7603
|
+
this.logger.info(`[Analytics] Join success tracked for product: ${productId}`);
|
|
7604
|
+
}
|
|
7605
|
+
catch (error) {
|
|
7606
|
+
this.logger.error("[Analytics] Failed to track join success", error);
|
|
7607
|
+
}
|
|
7608
|
+
}
|
|
7609
|
+
/**
|
|
7610
|
+
* Track join failure event
|
|
7611
|
+
*/
|
|
7612
|
+
async trackJoinFailure(productId, groupId, errorCode, message) {
|
|
7613
|
+
const event = {
|
|
7614
|
+
event: "JOIN_FAILURE",
|
|
7615
|
+
productId,
|
|
7616
|
+
sessionId: this.sessionId,
|
|
7617
|
+
timestamp: new Date().toISOString(),
|
|
7618
|
+
context: {
|
|
7619
|
+
pageUrl: typeof window !== "undefined" ? window.location.href : undefined,
|
|
7620
|
+
userAgent: typeof navigator !== "undefined" ? navigator.userAgent : undefined,
|
|
7621
|
+
sdkVersion: this.sdkVersion,
|
|
7622
|
+
groupId,
|
|
7623
|
+
errorCode,
|
|
7624
|
+
errorMessage: message,
|
|
7625
|
+
},
|
|
7626
|
+
};
|
|
7627
|
+
try {
|
|
7628
|
+
await this.sendEvent(event);
|
|
7629
|
+
this.logger.info(`[Analytics] Join failure tracked for product: ${productId}`);
|
|
7630
|
+
}
|
|
7631
|
+
catch (error) {
|
|
7632
|
+
this.logger.error("[Analytics] Failed to track join failure", error);
|
|
7633
|
+
}
|
|
7634
|
+
}
|
|
7635
|
+
/**
|
|
7636
|
+
* Track already joined event (user attempts to join a group they're already in)
|
|
7637
|
+
*/
|
|
7638
|
+
async trackAlreadyJoined(productId, groupId) {
|
|
7639
|
+
const event = {
|
|
7640
|
+
event: "ALREADY_JOINED",
|
|
7641
|
+
productId,
|
|
7642
|
+
sessionId: this.sessionId,
|
|
7643
|
+
timestamp: new Date().toISOString(),
|
|
7644
|
+
context: {
|
|
7645
|
+
pageUrl: typeof window !== "undefined" ? window.location.href : undefined,
|
|
7646
|
+
userAgent: typeof navigator !== "undefined" ? navigator.userAgent : undefined,
|
|
7647
|
+
sdkVersion: this.sdkVersion,
|
|
7648
|
+
groupId,
|
|
7649
|
+
},
|
|
7650
|
+
};
|
|
7651
|
+
try {
|
|
7652
|
+
await this.sendEvent(event);
|
|
7653
|
+
this.logger.info(`[Analytics] Already joined tracked for product: ${productId}`);
|
|
7654
|
+
}
|
|
7655
|
+
catch (error) {
|
|
7656
|
+
this.logger.error("[Analytics] Failed to track already joined", error);
|
|
7657
|
+
}
|
|
7658
|
+
}
|
|
7659
|
+
/**
|
|
7660
|
+
* Track group full view event (user sees a fulfilled/full group)
|
|
7661
|
+
*/
|
|
7662
|
+
async trackGroupFullView(productId, groupId, totalMembers) {
|
|
7663
|
+
const event = {
|
|
7664
|
+
event: "GROUP_FULL_VIEW",
|
|
7665
|
+
productId,
|
|
7666
|
+
sessionId: this.sessionId,
|
|
7667
|
+
timestamp: new Date().toISOString(),
|
|
7668
|
+
context: {
|
|
7669
|
+
pageUrl: typeof window !== "undefined" ? window.location.href : undefined,
|
|
7670
|
+
userAgent: typeof navigator !== "undefined" ? navigator.userAgent : undefined,
|
|
7671
|
+
sdkVersion: this.sdkVersion,
|
|
7672
|
+
groupId,
|
|
7673
|
+
totalMembers,
|
|
7674
|
+
},
|
|
7675
|
+
};
|
|
7676
|
+
try {
|
|
7677
|
+
await this.sendEvent(event);
|
|
7678
|
+
this.logger.info(`[Analytics] Group full view tracked for product: ${productId}`);
|
|
7679
|
+
}
|
|
7680
|
+
catch (error) {
|
|
7681
|
+
this.logger.error("[Analytics] Failed to track group full view", error);
|
|
7682
|
+
}
|
|
7683
|
+
}
|
|
7684
|
+
/**
|
|
7685
|
+
* Track share click event
|
|
7686
|
+
*/
|
|
7687
|
+
async trackShareClick(productId, groupId, channel) {
|
|
7688
|
+
const event = {
|
|
7689
|
+
event: "SHARE_CLICK",
|
|
7690
|
+
productId,
|
|
7691
|
+
sessionId: this.sessionId,
|
|
7692
|
+
timestamp: new Date().toISOString(),
|
|
7693
|
+
context: {
|
|
7694
|
+
pageUrl: typeof window !== "undefined" ? window.location.href : undefined,
|
|
7695
|
+
userAgent: typeof navigator !== "undefined" ? navigator.userAgent : undefined,
|
|
7696
|
+
sdkVersion: this.sdkVersion,
|
|
7697
|
+
groupId,
|
|
7698
|
+
channel,
|
|
7699
|
+
},
|
|
7700
|
+
};
|
|
7701
|
+
try {
|
|
7702
|
+
await this.sendEvent(event);
|
|
7703
|
+
this.logger.info(`[Analytics] Share click tracked for product: ${productId}`);
|
|
7704
|
+
}
|
|
7705
|
+
catch (error) {
|
|
7706
|
+
this.logger.error("[Analytics] Failed to track share click", error);
|
|
7707
|
+
}
|
|
7708
|
+
}
|
|
7709
|
+
/**
|
|
7710
|
+
* Track group creation attempt event
|
|
7711
|
+
*/
|
|
7712
|
+
async trackGroupCreateAttempt(productId) {
|
|
7713
|
+
const event = {
|
|
7714
|
+
event: "GROUP_CREATE_ATTEMPT",
|
|
7715
|
+
productId,
|
|
7716
|
+
sessionId: this.sessionId,
|
|
7717
|
+
timestamp: new Date().toISOString(),
|
|
7718
|
+
context: {
|
|
7719
|
+
pageUrl: typeof window !== "undefined" ? window.location.href : undefined,
|
|
7720
|
+
userAgent: typeof navigator !== "undefined" ? navigator.userAgent : undefined,
|
|
7721
|
+
sdkVersion: this.sdkVersion,
|
|
7722
|
+
},
|
|
7723
|
+
};
|
|
7724
|
+
try {
|
|
7725
|
+
await this.sendEvent(event);
|
|
7726
|
+
this.logger.info(`[Analytics] Group create attempt tracked for product: ${productId}`);
|
|
7727
|
+
}
|
|
7728
|
+
catch (error) {
|
|
7729
|
+
this.logger.error("[Analytics] Failed to track group create attempt", error);
|
|
7730
|
+
}
|
|
7731
|
+
}
|
|
7732
|
+
/**
|
|
7733
|
+
* Track successful group creation event
|
|
7734
|
+
*/
|
|
7735
|
+
async trackGroupCreateSuccess(productId, groupId) {
|
|
7736
|
+
const event = {
|
|
7737
|
+
event: "GROUP_CREATE_SUCCESS",
|
|
7738
|
+
productId,
|
|
7739
|
+
sessionId: this.sessionId,
|
|
7740
|
+
timestamp: new Date().toISOString(),
|
|
7741
|
+
context: {
|
|
7742
|
+
pageUrl: typeof window !== "undefined" ? window.location.href : undefined,
|
|
7743
|
+
userAgent: typeof navigator !== "undefined" ? navigator.userAgent : undefined,
|
|
7744
|
+
sdkVersion: this.sdkVersion,
|
|
7745
|
+
groupId,
|
|
7746
|
+
},
|
|
7747
|
+
};
|
|
7748
|
+
try {
|
|
7749
|
+
await this.sendEvent(event);
|
|
7750
|
+
this.logger.info(`[Analytics] Group create success tracked for product: ${productId}`);
|
|
7751
|
+
}
|
|
7752
|
+
catch (error) {
|
|
7753
|
+
this.logger.error("[Analytics] Failed to track group create success", error);
|
|
7754
|
+
}
|
|
7755
|
+
}
|
|
7756
|
+
/**
|
|
7757
|
+
* Track group creation failure event
|
|
7758
|
+
*/
|
|
7759
|
+
async trackGroupCreateFailure(productId, errorCode, message) {
|
|
7760
|
+
const event = {
|
|
7761
|
+
event: "GROUP_CREATE_FAILURE",
|
|
7762
|
+
productId,
|
|
7763
|
+
sessionId: this.sessionId,
|
|
7764
|
+
timestamp: new Date().toISOString(),
|
|
7765
|
+
context: {
|
|
7766
|
+
pageUrl: typeof window !== "undefined" ? window.location.href : undefined,
|
|
7767
|
+
userAgent: typeof navigator !== "undefined" ? navigator.userAgent : undefined,
|
|
7768
|
+
sdkVersion: this.sdkVersion,
|
|
7769
|
+
errorCode,
|
|
7770
|
+
errorMessage: message,
|
|
7771
|
+
},
|
|
7772
|
+
};
|
|
7773
|
+
try {
|
|
7774
|
+
await this.sendEvent(event);
|
|
7775
|
+
this.logger.info(`[Analytics] Group create failure tracked for product: ${productId}`);
|
|
7776
|
+
}
|
|
7777
|
+
catch (error) {
|
|
7778
|
+
this.logger.error("[Analytics] Failed to track group create failure", error);
|
|
7779
|
+
}
|
|
7780
|
+
}
|
|
7208
7781
|
/**
|
|
7209
7782
|
* Track custom event (extensible for future use)
|
|
7210
7783
|
*/
|
|
@@ -12650,8 +13223,14 @@ var CoBuySDK = (function (exports) {
|
|
|
12650
13223
|
participantsCount !== undefined &&
|
|
12651
13224
|
participantsCount >= maxParticipants);
|
|
12652
13225
|
if (isFulfilled && productId && groupId) {
|
|
12653
|
-
//
|
|
12654
|
-
this.
|
|
13226
|
+
// Only prepare checkout if the current user is actually a member of this group.
|
|
13227
|
+
// this.widgets tracks all WidgetRoot instances; getJoinedGroupId() returns the
|
|
13228
|
+
// joined group ID only when the user has completed a join this session (not for
|
|
13229
|
+
// observers). Calling prepareCheckout for non-members would generate spurious 403s.
|
|
13230
|
+
const userIsInGroup = [...this.widgets].some((w) => w.getJoinedGroupId() === groupId);
|
|
13231
|
+
if (userIsInGroup) {
|
|
13232
|
+
this.prepareCheckoutIfNotDone(productId, groupId);
|
|
13233
|
+
}
|
|
12655
13234
|
}
|
|
12656
13235
|
// Emit user-defined callback if provided
|
|
12657
13236
|
if ((_a = config.events) === null || _a === void 0 ? void 0 : _a.onGroupMemberJoined) {
|
|
@@ -12664,8 +13243,11 @@ var CoBuySDK = (function (exports) {
|
|
|
12664
13243
|
this.logger.warn("[SDK] Failed to initialize sockets", e);
|
|
12665
13244
|
}
|
|
12666
13245
|
}
|
|
12667
|
-
// Track page view
|
|
13246
|
+
// Track session init + page view events after successful initialization
|
|
12668
13247
|
if (this.analyticsClient) {
|
|
13248
|
+
this.analyticsClient.trackSessionInit(config.geo, config.device).catch((error) => {
|
|
13249
|
+
this.logger.warn("[SDK] Failed to track session init", error);
|
|
13250
|
+
});
|
|
12669
13251
|
this.analyticsClient.trackPageView().catch((error) => {
|
|
12670
13252
|
// Non-blocking: Analytics failure should not affect SDK initialization
|
|
12671
13253
|
this.logger.warn("[SDK] Failed to track page view", error);
|