@cshah18/sdk 4.15.0 → 4.16.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 +84 -8
- package/dist/cobuy-sdk.esm.js.map +1 -1
- package/dist/cobuy-sdk.umd.js +84 -8
- package/dist/cobuy-sdk.umd.js.map +1 -1
- package/dist/types/core/api-client.d.ts +7 -1
- package/dist/types/core/endpoints.d.ts +1 -0
- package/dist/types/ui/lobby/lobby-modal.d.ts +5 -0
- package/package.json +1 -1
package/dist/cobuy-sdk.esm.js
CHANGED
|
@@ -2291,6 +2291,11 @@ class LobbyModal {
|
|
|
2291
2291
|
this.updateData(updatePayload);
|
|
2292
2292
|
// Also update team card to reflect new member count
|
|
2293
2293
|
this.updateTeamCard(participants, max);
|
|
2294
|
+
// If group just became complete and we need offline codes for THIS user, fetch them
|
|
2295
|
+
if (isComplete && !this.data.offlineRedemption) {
|
|
2296
|
+
this.logger.info("[Socket] Group fulfilled - fetching current user's offline codes");
|
|
2297
|
+
this.fetchCurrentUserOfflineCodesIfNeeded();
|
|
2298
|
+
}
|
|
2294
2299
|
};
|
|
2295
2300
|
this.logger = new Logger(debug);
|
|
2296
2301
|
this.apiClient = apiClient;
|
|
@@ -4534,6 +4539,33 @@ class LobbyModal {
|
|
|
4534
4539
|
window.removeEventListener("group:created", this.handleSocketGroupUpdate);
|
|
4535
4540
|
this.socketListenerRegistered = false;
|
|
4536
4541
|
}
|
|
4542
|
+
/**
|
|
4543
|
+
* Fetch current user's offline redemption codes when group becomes fulfilled
|
|
4544
|
+
* This ensures we show the current user's codes, not someone else's
|
|
4545
|
+
*/
|
|
4546
|
+
async fetchCurrentUserOfflineCodesIfNeeded() {
|
|
4547
|
+
var _a;
|
|
4548
|
+
if (!this.apiClient || !this.currentGroupId) {
|
|
4549
|
+
return;
|
|
4550
|
+
}
|
|
4551
|
+
try {
|
|
4552
|
+
this.logger.info("[Offline] Fetching current user's offline codes");
|
|
4553
|
+
const response = await this.apiClient.getGroupOfflineRedemption(this.currentGroupId);
|
|
4554
|
+
if (response.success && ((_a = response.data) === null || _a === void 0 ? void 0 : _a.offline_redemption)) {
|
|
4555
|
+
const offlineRedemption = response.data.offline_redemption;
|
|
4556
|
+
if (isValidOfflineRedemption(offlineRedemption)) {
|
|
4557
|
+
this.logger.info("[Offline] Successfully fetched current user's offline codes");
|
|
4558
|
+
this.updateData({ offlineRedemption });
|
|
4559
|
+
}
|
|
4560
|
+
}
|
|
4561
|
+
else {
|
|
4562
|
+
this.logger.warn("[Offline] Failed to fetch offline codes", response.error);
|
|
4563
|
+
}
|
|
4564
|
+
}
|
|
4565
|
+
catch (error) {
|
|
4566
|
+
this.logger.error("[Offline] Error fetching offline codes", error);
|
|
4567
|
+
}
|
|
4568
|
+
}
|
|
4537
4569
|
/**
|
|
4538
4570
|
* Create activity item from socket event data
|
|
4539
4571
|
*/
|
|
@@ -5215,6 +5247,7 @@ class WidgetRoot {
|
|
|
5215
5247
|
}
|
|
5216
5248
|
/** Fetch latest group data and re-render containers */
|
|
5217
5249
|
async refreshGroupDataFromRealtime() {
|
|
5250
|
+
var _a;
|
|
5218
5251
|
// Debounce rapid refreshes to prevent loops and reduce API load
|
|
5219
5252
|
const now = Date.now();
|
|
5220
5253
|
if (now - this.lastGroupDataRefreshTime < this.GROUP_REFRESH_DEBOUNCE) {
|
|
@@ -5231,19 +5264,24 @@ class WidgetRoot {
|
|
|
5231
5264
|
const groupData = await this.fetchPrimaryGroup(this.currentProductId);
|
|
5232
5265
|
this.currentGroupData = groupData;
|
|
5233
5266
|
this.currentGroupId = (groupData === null || groupData === void 0 ? void 0 : groupData.id) || this.currentGroupId;
|
|
5267
|
+
// If backend returned member-scoped offline redemption, this session is a member.
|
|
5268
|
+
const hasMemberOfflineRedemption = !!(groupData === null || groupData === void 0 ? void 0 : groupData.offline_redemption) && isValidOfflineRedemption(groupData.offline_redemption);
|
|
5269
|
+
if (hasMemberOfflineRedemption && !this.currentSessionId) {
|
|
5270
|
+
this.currentSessionId = ((_a = this.apiClient) === null || _a === void 0 ? void 0 : _a.getSessionId()) || null;
|
|
5271
|
+
}
|
|
5234
5272
|
// If backend signals completion via counts, reflect it — but only for actual members.
|
|
5235
5273
|
// Observers may see a full group returned by fetchPrimaryGroup; they should NOT enter
|
|
5236
5274
|
// the checkout flow. currentSessionId is the membership signal (non-null = joined).
|
|
5237
5275
|
if (groupData) {
|
|
5238
5276
|
const participants = Number(groupData.participants_count || 0);
|
|
5239
5277
|
const max = Number(groupData.max_participants || 0);
|
|
5240
|
-
|
|
5278
|
+
const isMember = !!this.currentSessionId || hasMemberOfflineRedemption;
|
|
5279
|
+
if (max > 0 && participants >= max && isMember) {
|
|
5241
5280
|
this.groupFulfilled = true;
|
|
5242
5281
|
// The primary group API returns offline_redemption for members of fulfilled groups.
|
|
5243
5282
|
// Extract it here so renderFulfilledSummary shows the "Redeem In-store" link
|
|
5244
5283
|
// (matching the UI users see on a full page reload).
|
|
5245
|
-
if (
|
|
5246
|
-
isValidOfflineRedemption(groupData.offline_redemption)) {
|
|
5284
|
+
if (hasMemberOfflineRedemption) {
|
|
5247
5285
|
this.offlineRedemption = groupData.offline_redemption;
|
|
5248
5286
|
}
|
|
5249
5287
|
if (groupData.campaign_redemption_method) {
|
|
@@ -6646,7 +6684,7 @@ class WidgetRoot {
|
|
|
6646
6684
|
* Handle CTA button click with analytics and modal opening
|
|
6647
6685
|
*/
|
|
6648
6686
|
async handleCTAClick(productId) {
|
|
6649
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
|
|
6687
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
|
|
6650
6688
|
this.logger.info(`CTA clicked for product: ${productId}`);
|
|
6651
6689
|
// Track analytics event asynchronously (fire-and-forget)
|
|
6652
6690
|
if (this.analyticsClient) {
|
|
@@ -6766,6 +6804,8 @@ class WidgetRoot {
|
|
|
6766
6804
|
}
|
|
6767
6805
|
}
|
|
6768
6806
|
this.logger.info("Successfully joined group", groupJoinData);
|
|
6807
|
+
// Mark this widget session as an active member for subsequent socket fulfillment handling.
|
|
6808
|
+
this.currentSessionId = ((_j = this.apiClient) === null || _j === void 0 ? void 0 : _j.getSessionId()) || this.currentSessionId;
|
|
6769
6809
|
if (this.analyticsClient && groupJoinData) {
|
|
6770
6810
|
this.analyticsClient
|
|
6771
6811
|
.trackJoinSuccess(productId, groupJoinData.group.id)
|
|
@@ -6777,7 +6817,7 @@ class WidgetRoot {
|
|
|
6777
6817
|
isValidOfflineRedemption(groupJoinData.offline_redemption)
|
|
6778
6818
|
? groupJoinData.offline_redemption
|
|
6779
6819
|
: null;
|
|
6780
|
-
const joinedGroupId = (
|
|
6820
|
+
const joinedGroupId = (_k = groupJoinData === null || groupJoinData === void 0 ? void 0 : groupJoinData.group) === null || _k === void 0 ? void 0 : _k.id;
|
|
6781
6821
|
// Trigger invite tracking before opening lobby (global for product)
|
|
6782
6822
|
if (joinedGroupId) {
|
|
6783
6823
|
try {
|
|
@@ -6809,7 +6849,7 @@ class WidgetRoot {
|
|
|
6809
6849
|
}
|
|
6810
6850
|
if (this.analyticsClient) {
|
|
6811
6851
|
this.analyticsClient
|
|
6812
|
-
.trackJoinFailure(productId, (
|
|
6852
|
+
.trackJoinFailure(productId, (_l = this.currentGroupId) !== null && _l !== void 0 ? _l : undefined, "EXCEPTION", error instanceof Error ? error.message : String(error))
|
|
6813
6853
|
.catch((e) => this.logger.warn("Analytics tracking failed", e));
|
|
6814
6854
|
}
|
|
6815
6855
|
this.setButtonLoadingState(false);
|
|
@@ -6826,7 +6866,7 @@ class WidgetRoot {
|
|
|
6826
6866
|
const progress = Math.round((groupJoinData.group.participants_count / groupJoinData.group.max_participants) * 100);
|
|
6827
6867
|
// Format discount based on reward type
|
|
6828
6868
|
let discountText = "";
|
|
6829
|
-
if ((
|
|
6869
|
+
if ((_o = (_m = this.currentRewardData) === null || _m === void 0 ? void 0 : _m.reward) === null || _o === void 0 ? void 0 : _o.value) {
|
|
6830
6870
|
const rewardType = this.currentRewardData.reward.type;
|
|
6831
6871
|
const rewardValue = this.currentRewardData.reward.value;
|
|
6832
6872
|
if (rewardType === "percentage" || rewardType === "cashback") {
|
|
@@ -6866,7 +6906,7 @@ class WidgetRoot {
|
|
|
6866
6906
|
shareMessage: shareMessageFromInvite,
|
|
6867
6907
|
isLocked: !isGroupFulfilled,
|
|
6868
6908
|
offlineRedemption: offlineRedemptionFromJoin,
|
|
6869
|
-
redemptionMethod: (
|
|
6909
|
+
redemptionMethod: (_p = groupJoinData.group.campaign_redemption_method) !== null && _p !== void 0 ? _p : this.campaignRedemptionMethod,
|
|
6870
6910
|
onShare: this.analyticsClient
|
|
6871
6911
|
? () => {
|
|
6872
6912
|
this.analyticsClient.trackShareClick(productId, groupJoinData.group.id, "other").catch((e) => this.logger.warn("Analytics tracking failed", e));
|
|
@@ -7044,6 +7084,7 @@ const API_ENDPOINTS = {
|
|
|
7044
7084
|
GROUP_CREATE_AND_JOIN: "/v1/sdk/groups/new/join",
|
|
7045
7085
|
PRODUCT_ACTIVE_GROUPS: "/v1/sdk/products/:productId/groups/active",
|
|
7046
7086
|
GROUP_INVITE: "/v1/sdk/groups/:groupId/invite",
|
|
7087
|
+
GROUP_OFFLINE_REDEMPTION: "/v1/sdk/groups/:groupId/offline-redemption",
|
|
7047
7088
|
GROUP_CHECKOUT_PREPARE: "/v1/sdk/groups/:groupId/checkout/prepare",
|
|
7048
7089
|
GROUP_CHECKOUT_VALIDATE: "/v1/sdk/groups/:groupId/checkout/validate",
|
|
7049
7090
|
GROUP_CHECKOUT_CONFIRM: "/v1/sdk/groups/:groupId/checkout/confirm",
|
|
@@ -8156,6 +8197,29 @@ class ApiClient {
|
|
|
8156
8197
|
},
|
|
8157
8198
|
};
|
|
8158
8199
|
}
|
|
8200
|
+
/**
|
|
8201
|
+
* Fetch current session member's offline redemption codes for a group.
|
|
8202
|
+
*/
|
|
8203
|
+
async getGroupOfflineRedemption(groupId) {
|
|
8204
|
+
const endpoint = buildApiUrl("", API_ENDPOINTS.GROUP_OFFLINE_REDEMPTION, {
|
|
8205
|
+
groupId,
|
|
8206
|
+
});
|
|
8207
|
+
const response = await this.get(endpoint);
|
|
8208
|
+
if (response.success) {
|
|
8209
|
+
const payload = response.data;
|
|
8210
|
+
return {
|
|
8211
|
+
success: true,
|
|
8212
|
+
data: (payload === null || payload === void 0 ? void 0 : payload.data) || {},
|
|
8213
|
+
};
|
|
8214
|
+
}
|
|
8215
|
+
return {
|
|
8216
|
+
success: false,
|
|
8217
|
+
error: response.error || {
|
|
8218
|
+
message: "Failed to fetch offline redemption",
|
|
8219
|
+
code: "OFFLINE_REDEMPTION_FETCH_ERROR",
|
|
8220
|
+
},
|
|
8221
|
+
};
|
|
8222
|
+
}
|
|
8159
8223
|
async recoverOrJoinGroup(productId, contact) {
|
|
8160
8224
|
var _a;
|
|
8161
8225
|
const endpoint = buildApiUrl("", API_ENDPOINTS.PRODUCT_RECOVER_OR_JOIN_GROUP, {
|
|
@@ -14435,6 +14499,18 @@ class CoBuy {
|
|
|
14435
14499
|
if ((_a = config.events) === null || _a === void 0 ? void 0 : _a.onModalClose) {
|
|
14436
14500
|
config.events.onModalClose(options.productId);
|
|
14437
14501
|
}
|
|
14502
|
+
// Sync widget state after lobby closes so fulfilled CTAs/group view
|
|
14503
|
+
// reflect socket-driven changes without requiring a page reload.
|
|
14504
|
+
const refreshPromises = [];
|
|
14505
|
+
this.widgets.forEach((w) => {
|
|
14506
|
+
const pid = typeof w.getProductId === "function"
|
|
14507
|
+
? w.getProductId()
|
|
14508
|
+
: null;
|
|
14509
|
+
if (pid === options.productId && typeof w.requestRefresh === "function") {
|
|
14510
|
+
refreshPromises.push(w.requestRefresh());
|
|
14511
|
+
}
|
|
14512
|
+
});
|
|
14513
|
+
void Promise.all(refreshPromises.map((p) => p.then(() => undefined).catch(() => undefined)));
|
|
14438
14514
|
},
|
|
14439
14515
|
onCopyLink: options.onCopyLink,
|
|
14440
14516
|
onShare: options.onShare,
|