@nuskin/product-components 3.20.0 → 3.20.1-it-17535.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/.releaserc +1 -1
- package/components/NsProductOffer.vue +61 -0
- package/docs/CHANGELOG.md +1 -1
- package/package.json +1 -1
- package/services/NsProductAppService.js +243 -11
package/.releaserc
CHANGED
|
@@ -426,6 +426,17 @@ export default {
|
|
|
426
426
|
const storeId = this.storeId || queryStoreId;
|
|
427
427
|
const offerId = this.offerId || queryOfferId;
|
|
428
428
|
|
|
429
|
+
// Log initialization check
|
|
430
|
+
console.log("[NsProductOffer] checkToInit called:", {
|
|
431
|
+
timestamp: new Date().toISOString(),
|
|
432
|
+
appServiceLoading: this.$NsProductAppService.loading,
|
|
433
|
+
hasTranslations: !isNullOrEmpty(this.translations),
|
|
434
|
+
storeId: storeId,
|
|
435
|
+
offerId: offerId,
|
|
436
|
+
market: this.market,
|
|
437
|
+
language: this.language
|
|
438
|
+
});
|
|
439
|
+
|
|
429
440
|
// app service needs to be loaded, market and language need to be different, and translations are required
|
|
430
441
|
if (
|
|
431
442
|
!this.$NsProductAppService.loading &&
|
|
@@ -437,6 +448,14 @@ export default {
|
|
|
437
448
|
(!!offerId && offerId !== this.localOfferId)) &&
|
|
438
449
|
!isNullOrEmpty(this.translations)
|
|
439
450
|
) {
|
|
451
|
+
console.log("[NsProductOffer] Initializing offer:", {
|
|
452
|
+
timestamp: new Date().toISOString(),
|
|
453
|
+
market: this.runConfig.country,
|
|
454
|
+
language: this.runConfig.language,
|
|
455
|
+
storeId: storeId,
|
|
456
|
+
offerId: offerId
|
|
457
|
+
});
|
|
458
|
+
|
|
440
459
|
this.market = this.runConfig.country;
|
|
441
460
|
this.language = this.runConfig.language;
|
|
442
461
|
|
|
@@ -454,10 +473,25 @@ export default {
|
|
|
454
473
|
|
|
455
474
|
await waitForConfig();
|
|
456
475
|
await this.init();
|
|
476
|
+
} else {
|
|
477
|
+
// Log why initialization was skipped
|
|
478
|
+
console.log("[NsProductOffer] Init skipped:", {
|
|
479
|
+
timestamp: new Date().toISOString(),
|
|
480
|
+
appServiceLoading: this.$NsProductAppService.loading,
|
|
481
|
+
marketMatch: this.market === this.runConfig.country,
|
|
482
|
+
languageMatch: this.language === this.runConfig.language,
|
|
483
|
+
hasTranslations: !isNullOrEmpty(this.translations)
|
|
484
|
+
});
|
|
457
485
|
}
|
|
458
486
|
},
|
|
459
487
|
|
|
460
488
|
async init() {
|
|
489
|
+
console.log("[NsProductOffer] init() started:", {
|
|
490
|
+
timestamp: new Date().toISOString(),
|
|
491
|
+
localStoreId: this.localStoreId,
|
|
492
|
+
localOfferId: this.localOfferId
|
|
493
|
+
});
|
|
494
|
+
|
|
461
495
|
this.storeOwner = StoreFrontSponsorStorageService.getStoreFrontSponsor();
|
|
462
496
|
|
|
463
497
|
const shoppingContext = ShoppingContext.getShoppingContext();
|
|
@@ -524,6 +558,7 @@ export default {
|
|
|
524
558
|
|
|
525
559
|
this.offerLoading = false;
|
|
526
560
|
} else {
|
|
561
|
+
console.log("[NsProductOffer] Offer not found - no offer ID provided");
|
|
527
562
|
this.offerNotFound = true;
|
|
528
563
|
this.showOfferSavings = false;
|
|
529
564
|
this.offerLoading = false;
|
|
@@ -531,12 +566,19 @@ export default {
|
|
|
531
566
|
},
|
|
532
567
|
|
|
533
568
|
async getOffer() {
|
|
569
|
+
console.log("[NsProductOffer] getOffer() called:", {
|
|
570
|
+
timestamp: new Date().toISOString(),
|
|
571
|
+
storeId: this.localStoreId,
|
|
572
|
+
offerId: this.localOfferId
|
|
573
|
+
});
|
|
574
|
+
|
|
534
575
|
const offer = await PersonalOfferService.getOfferV2(
|
|
535
576
|
this.localStoreId,
|
|
536
577
|
this.localOfferId
|
|
537
578
|
);
|
|
538
579
|
|
|
539
580
|
if (isNullOrEmpty(offer)) {
|
|
581
|
+
console.log("[NsProductOffer] Offer not found in API response");
|
|
540
582
|
this.offerNotFound = true;
|
|
541
583
|
return;
|
|
542
584
|
}
|
|
@@ -750,6 +792,20 @@ export default {
|
|
|
750
792
|
|
|
751
793
|
checkProductsHaveData: debounce(function(dataAvailable) {
|
|
752
794
|
const products = Object.values(this.products);
|
|
795
|
+
const productsWithData = products.filter(p => !isNullOrEmpty(p.data));
|
|
796
|
+
const productsWithAvailability = products.filter(
|
|
797
|
+
p => !isNullOrEmpty(p.availability)
|
|
798
|
+
);
|
|
799
|
+
|
|
800
|
+
console.log("[NsProductOffer] checkProductsHaveData:", {
|
|
801
|
+
timestamp: new Date().toISOString(),
|
|
802
|
+
totalProducts: products.length,
|
|
803
|
+
productsWithData: productsWithData.length,
|
|
804
|
+
productsWithAvailability: productsWithAvailability.length,
|
|
805
|
+
dataAvailable: dataAvailable,
|
|
806
|
+
productSkus: products.map(p => p.sku)
|
|
807
|
+
});
|
|
808
|
+
|
|
753
809
|
if (products.every(product => !isNullOrEmpty(product.availability))) {
|
|
754
810
|
this.someProductsValid = products.some(
|
|
755
811
|
product =>
|
|
@@ -762,8 +818,13 @@ export default {
|
|
|
762
818
|
dataAvailable &&
|
|
763
819
|
products.every(product => isNullOrEmpty(product.data))
|
|
764
820
|
) {
|
|
821
|
+
console.log(
|
|
822
|
+
"[NsProductOffer] Offer not found - all products have empty data"
|
|
823
|
+
);
|
|
765
824
|
this.offerNotFound = true;
|
|
766
825
|
}
|
|
826
|
+
} else {
|
|
827
|
+
console.log("[NsProductOffer] Some products missing availability data");
|
|
767
828
|
}
|
|
768
829
|
}, 250),
|
|
769
830
|
|
package/docs/CHANGELOG.md
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
## [3.20.1-it-17535.1](https://code.tls.nuskin.io/ns-am/ux/product-components/compare/v3.20.0...v3.20.1-it-17535.1) (2026-03-20)
|
package/package.json
CHANGED
|
@@ -13,6 +13,82 @@ import debounce from "lodash/debounce";
|
|
|
13
13
|
const fallbackImagePath =
|
|
14
14
|
"etc/designs/hello/packaged/img/product-basic__fallback-image.svg";
|
|
15
15
|
|
|
16
|
+
/**
|
|
17
|
+
* Utility to detect browser type
|
|
18
|
+
*/
|
|
19
|
+
const getBrowserInfo = () => {
|
|
20
|
+
// Check if running in browser environment
|
|
21
|
+
if (typeof navigator === "undefined" || !navigator.userAgent) {
|
|
22
|
+
return {
|
|
23
|
+
userAgent: "",
|
|
24
|
+
isInstagram: false,
|
|
25
|
+
isFacebook: false,
|
|
26
|
+
isTwitter: false,
|
|
27
|
+
isLinkedIn: false,
|
|
28
|
+
isTikTok: false,
|
|
29
|
+
isInAppBrowser: false
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const ua = navigator.userAgent || "";
|
|
34
|
+
return {
|
|
35
|
+
userAgent: ua,
|
|
36
|
+
isInstagram: ua.includes("Instagram"),
|
|
37
|
+
isFacebook:
|
|
38
|
+
ua.includes("FBAN") || ua.includes("FBAV") || ua.includes("FB_IAB"),
|
|
39
|
+
isTwitter: ua.includes("Twitter"),
|
|
40
|
+
isLinkedIn: ua.includes("LinkedInApp"),
|
|
41
|
+
isTikTok: ua.includes("TikTok"),
|
|
42
|
+
isInAppBrowser:
|
|
43
|
+
ua.includes("Instagram") ||
|
|
44
|
+
ua.includes("FBAN") ||
|
|
45
|
+
ua.includes("FBAV") ||
|
|
46
|
+
ua.includes("FB_IAB") ||
|
|
47
|
+
ua.includes("Twitter") ||
|
|
48
|
+
ua.includes("LinkedInApp")
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Log to console and send to analytics/monitoring
|
|
54
|
+
*/
|
|
55
|
+
const logProductComponentEvent = (eventName, data) => {
|
|
56
|
+
// Check if running in browser environment
|
|
57
|
+
if (typeof window === "undefined" || typeof document === "undefined") {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const browserInfo = getBrowserInfo();
|
|
62
|
+
const logData = {
|
|
63
|
+
timestamp: new Date().toISOString(),
|
|
64
|
+
event: eventName,
|
|
65
|
+
component: "NsProductAppService",
|
|
66
|
+
url: window.location?.href || "",
|
|
67
|
+
referrer: document.referrer || "",
|
|
68
|
+
visibilityState: document.visibilityState || "visible",
|
|
69
|
+
...browserInfo,
|
|
70
|
+
...data
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
// Log to console for debugging
|
|
74
|
+
console.log(`[ProductComponents] ${eventName}:`, logData);
|
|
75
|
+
|
|
76
|
+
// Send to analytics if available (customize based on your analytics setup)
|
|
77
|
+
if (window.dataLayer) {
|
|
78
|
+
window.dataLayer.push({
|
|
79
|
+
event: "product_component_event",
|
|
80
|
+
...logData
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Send to custom logging endpoint if available
|
|
85
|
+
if (window.nsLogger && typeof window.nsLogger.log === "function") {
|
|
86
|
+
window.nsLogger.log(logData);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return logData;
|
|
90
|
+
};
|
|
91
|
+
|
|
16
92
|
let NsProductAppService = Vue.prototype.$NsProductAppService;
|
|
17
93
|
if (!NsProductAppService) {
|
|
18
94
|
const newEl = document.createElement("div");
|
|
@@ -25,7 +101,8 @@ if (!NsProductAppService) {
|
|
|
25
101
|
return {
|
|
26
102
|
runConfig: {},
|
|
27
103
|
runConfigInterval: null,
|
|
28
|
-
runConfigTries:
|
|
104
|
+
runConfigTries: 10,
|
|
105
|
+
initialRunConfigTries: 10,
|
|
29
106
|
locale: "",
|
|
30
107
|
marketConfig: {},
|
|
31
108
|
awsUrl: "",
|
|
@@ -34,7 +111,12 @@ if (!NsProductAppService) {
|
|
|
34
111
|
showWholeSalePricing: false,
|
|
35
112
|
loadingConfig: true,
|
|
36
113
|
translations: {},
|
|
37
|
-
loadingTranslations: true
|
|
114
|
+
loadingTranslations: true,
|
|
115
|
+
configLoadedSuccessfully: false,
|
|
116
|
+
initStartTime: Date.now(),
|
|
117
|
+
configLoadAttempts: 0,
|
|
118
|
+
visibilityChangeTriggered: false,
|
|
119
|
+
pageShowTriggered: false
|
|
38
120
|
};
|
|
39
121
|
},
|
|
40
122
|
computed: {
|
|
@@ -43,21 +125,139 @@ if (!NsProductAppService) {
|
|
|
43
125
|
}
|
|
44
126
|
},
|
|
45
127
|
async mounted() {
|
|
128
|
+
// Log initial state using modern Performance API
|
|
129
|
+
let pageLoadTime = 0;
|
|
130
|
+
if (typeof performance !== "undefined") {
|
|
131
|
+
// Use modern performance.now() which gives time since page load
|
|
132
|
+
pageLoadTime = Math.round(performance.now());
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
logProductComponentEvent("service_mounted", {
|
|
136
|
+
initialVisibilityState:
|
|
137
|
+
typeof document !== "undefined"
|
|
138
|
+
? document.visibilityState
|
|
139
|
+
: "visible",
|
|
140
|
+
pageLoadTime
|
|
141
|
+
});
|
|
142
|
+
|
|
46
143
|
await this.setConfig();
|
|
47
|
-
this.
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
144
|
+
this.startConfigRetryInterval();
|
|
145
|
+
|
|
146
|
+
// Handle Instagram and other in-app browsers that load pages in hidden state
|
|
147
|
+
// Re-initialize when page becomes visible
|
|
148
|
+
this.handleVisibilityChange = async () => {
|
|
149
|
+
const previousState = this.visibilityChangeTriggered;
|
|
150
|
+
this.visibilityChangeTriggered = true;
|
|
151
|
+
|
|
152
|
+
const visibilityState =
|
|
153
|
+
typeof document !== "undefined" && document.visibilityState
|
|
154
|
+
? document.visibilityState
|
|
155
|
+
: "visible";
|
|
156
|
+
|
|
157
|
+
logProductComponentEvent("visibility_changed", {
|
|
158
|
+
visibilityState,
|
|
159
|
+
configLoadedSuccessfully: this.configLoadedSuccessfully,
|
|
160
|
+
firstVisibilityChange: !previousState,
|
|
161
|
+
retryAttemptsRemaining: this.runConfigTries
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
if (visibilityState === "visible" && !this.configLoadedSuccessfully) {
|
|
165
|
+
logProductComponentEvent("visibility_retry_triggered", {
|
|
166
|
+
reason: "page_became_visible",
|
|
167
|
+
attemptsUsed: this.initialRunConfigTries - this.runConfigTries
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
await this.setConfig();
|
|
171
|
+
if (!this.configLoadedSuccessfully) {
|
|
172
|
+
this.startConfigRetryInterval();
|
|
173
|
+
}
|
|
54
174
|
}
|
|
55
|
-
}
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
if (typeof document !== "undefined") {
|
|
178
|
+
document.addEventListener(
|
|
179
|
+
"visibilitychange",
|
|
180
|
+
this.handleVisibilityChange
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Handle page restoration from bfcache (back/forward cache)
|
|
185
|
+
this.handlePageShow = async event => {
|
|
186
|
+
this.pageShowTriggered = true;
|
|
187
|
+
|
|
188
|
+
logProductComponentEvent("page_show_event", {
|
|
189
|
+
persisted: event.persisted,
|
|
190
|
+
configLoadedSuccessfully: this.configLoadedSuccessfully
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
if (event.persisted && !this.configLoadedSuccessfully) {
|
|
194
|
+
logProductComponentEvent("pageshow_retry_triggered", {
|
|
195
|
+
reason: "bfcache_restore"
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
await this.setConfig();
|
|
199
|
+
if (!this.configLoadedSuccessfully) {
|
|
200
|
+
this.startConfigRetryInterval();
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
if (typeof window !== "undefined") {
|
|
206
|
+
window.addEventListener("pageshow", this.handlePageShow);
|
|
207
|
+
}
|
|
56
208
|
},
|
|
57
209
|
beforeDestroy() {
|
|
58
|
-
|
|
210
|
+
this.clearConfigRetryInterval();
|
|
211
|
+
if (this.handleVisibilityChange && typeof document !== "undefined") {
|
|
212
|
+
document.removeEventListener(
|
|
213
|
+
"visibilitychange",
|
|
214
|
+
this.handleVisibilityChange
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
if (this.handlePageShow && typeof window !== "undefined") {
|
|
218
|
+
window.removeEventListener("pageshow", this.handlePageShow);
|
|
219
|
+
}
|
|
59
220
|
},
|
|
60
221
|
methods: {
|
|
222
|
+
/**
|
|
223
|
+
* Start the configuration retry interval
|
|
224
|
+
*/
|
|
225
|
+
startConfigRetryInterval() {
|
|
226
|
+
this.clearConfigRetryInterval();
|
|
227
|
+
|
|
228
|
+
logProductComponentEvent("retry_interval_started", {
|
|
229
|
+
retriesRemaining: this.runConfigTries,
|
|
230
|
+
configLoadedSuccessfully: this.configLoadedSuccessfully
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
this.runConfigInterval = setInterval(async () => {
|
|
234
|
+
if (this.runConfigTries <= 0 || this.configLoadedSuccessfully) {
|
|
235
|
+
if (this.runConfigTries <= 0 && !this.configLoadedSuccessfully) {
|
|
236
|
+
logProductComponentEvent("config_load_failed", {
|
|
237
|
+
reason: "max_retries_exceeded",
|
|
238
|
+
totalAttempts: this.configLoadAttempts,
|
|
239
|
+
timeSinceInit: Date.now() - this.initStartTime,
|
|
240
|
+
finalVisibilityState: document.visibilityState
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
this.clearConfigRetryInterval();
|
|
244
|
+
} else {
|
|
245
|
+
await this.checkConfig();
|
|
246
|
+
this.runConfigTries--;
|
|
247
|
+
}
|
|
248
|
+
}, 350);
|
|
249
|
+
},
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Clear the configuration retry interval
|
|
253
|
+
*/
|
|
254
|
+
clearConfigRetryInterval() {
|
|
255
|
+
if (this.runConfigInterval) {
|
|
256
|
+
clearInterval(this.runConfigInterval);
|
|
257
|
+
this.runConfigInterval = null;
|
|
258
|
+
}
|
|
259
|
+
},
|
|
260
|
+
|
|
61
261
|
/**
|
|
62
262
|
* Gets the full content path for an image or content src
|
|
63
263
|
*/
|
|
@@ -85,10 +285,22 @@ if (!NsProductAppService) {
|
|
|
85
285
|
*/
|
|
86
286
|
async setConfig(runConfig) {
|
|
87
287
|
this.loadingConfig = true;
|
|
288
|
+
this.configLoadAttempts++;
|
|
289
|
+
|
|
290
|
+
const attemptStartTime = Date.now();
|
|
88
291
|
|
|
89
292
|
runConfig = runConfig || RunConfigService.getRunConfig() || {};
|
|
90
293
|
if (!runConfig.language || !runConfig.country) {
|
|
91
294
|
// the runconfig isn't ready yet
|
|
295
|
+
logProductComponentEvent("config_not_ready", {
|
|
296
|
+
attempt: this.configLoadAttempts,
|
|
297
|
+
hasLanguage: !!runConfig.language,
|
|
298
|
+
hasCountry: !!runConfig.country,
|
|
299
|
+
runConfigKeys: Object.keys(runConfig),
|
|
300
|
+
timeSinceInit: Date.now() - this.initStartTime
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
this.loadingConfig = false;
|
|
92
304
|
return;
|
|
93
305
|
}
|
|
94
306
|
|
|
@@ -115,6 +327,26 @@ if (!NsProductAppService) {
|
|
|
115
327
|
};
|
|
116
328
|
|
|
117
329
|
this.loadingConfig = false;
|
|
330
|
+
this.configLoadedSuccessfully = true;
|
|
331
|
+
|
|
332
|
+
const loadTime = Date.now() - attemptStartTime;
|
|
333
|
+
const totalTime = Date.now() - this.initStartTime;
|
|
334
|
+
|
|
335
|
+
// Log successful configuration
|
|
336
|
+
logProductComponentEvent("config_loaded_successfully", {
|
|
337
|
+
attempt: this.configLoadAttempts,
|
|
338
|
+
loadTime: loadTime,
|
|
339
|
+
totalTime: totalTime,
|
|
340
|
+
retriesUsed: this.initialRunConfigTries - this.runConfigTries,
|
|
341
|
+
locale: this.locale,
|
|
342
|
+
hasMarketConfig: !!this.marketConfig,
|
|
343
|
+
visibilityChangeWasNeeded: this.visibilityChangeTriggered,
|
|
344
|
+
pageShowWasNeeded: this.pageShowTriggered
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
// Stop retry interval once config is successfully loaded
|
|
348
|
+
this.clearConfigRetryInterval();
|
|
349
|
+
|
|
118
350
|
this.$emit("config-changed", configData);
|
|
119
351
|
},
|
|
120
352
|
|