@nuskin/product-components 3.0.4 → 3.1.0-product-offer.2
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/CHANGELOG.md +25 -0
- package/components/NsProductOffer.vue +1001 -0
- package/index.js +3 -1
- package/mixins/NsProductMixin.js +42 -6
- package/package.json +5 -4
- package/stories/GettingStarted.stories.mdx +1 -1
- package/stories/NsProductMixin.stories.mdx +1 -1
- package/stories/NsProductOffer.stories.js +51 -0
- package/stories/Releases.stories.mdx +1 -1
package/.releaserc
CHANGED
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,28 @@
|
|
|
1
|
+
# [3.1.0-product-offer.2](https://code.tls.nuskin.io/ns-am/ux/product-components/compare/v3.1.0-product-offer.1...v3.1.0-product-offer.2) (2022-04-22)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Fix
|
|
5
|
+
|
|
6
|
+
* added hide favorites and some more query parsing ([b490535](https://code.tls.nuskin.io/ns-am/ux/product-components/commit/b490535c98e03e3ab45ac4710810d01b68019e83))
|
|
7
|
+
|
|
8
|
+
# [3.1.0-product-offer.1](https://code.tls.nuskin.io/ns-am/ux/product-components/compare/v3.0.5...v3.1.0-product-offer.1) (2022-04-22)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Fix
|
|
12
|
+
|
|
13
|
+
* ignore node 12 attempt ([8846ec5](https://code.tls.nuskin.io/ns-am/ux/product-components/commit/8846ec5a237b2442a16a35955b1d7613071ee94e))
|
|
14
|
+
|
|
15
|
+
### New
|
|
16
|
+
|
|
17
|
+
* converted ns-personal-offer-landing to vue component NsProductOffer, storybook updates (#CX12-4584) ([e0b4636](https://code.tls.nuskin.io/ns-am/ux/product-components/commit/e0b463634118326caa3f02793fcb4105b721f569)), closes [#CX12-4584](https://code.tls.nuskin.io/ns-am/ux/product-components/issues/CX12-4584)
|
|
18
|
+
|
|
19
|
+
## [3.0.5](https://code.tls.nuskin.io/ns-am/ux/product-components/compare/v3.0.4...v3.0.5) (2022-04-21)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
### Fix
|
|
23
|
+
|
|
24
|
+
* Stop qualifications getting messed up and allow variants(#CX15-4531) ([a94c742](https://code.tls.nuskin.io/ns-am/ux/product-components/commit/a94c742c37f77afa3ae397926a3f5ab5661e3af2)), closes [#CX15-4531](https://code.tls.nuskin.io/ns-am/ux/product-components/issues/CX15-4531)
|
|
25
|
+
|
|
1
26
|
## [3.0.4](https://code.tls.nuskin.io/ns-am/ux/product-components/compare/v3.0.3...v3.0.4) (2022-04-20)
|
|
2
27
|
|
|
3
28
|
|
|
@@ -0,0 +1,1001 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div v-if="offerLoading" :class="$style.spinnerWrapper">
|
|
3
|
+
<NsSpinner />
|
|
4
|
+
</div>
|
|
5
|
+
<article v-else :class="$style.personalOfferDetails">
|
|
6
|
+
<template v-if="!showOffer && !productsLoading">
|
|
7
|
+
<!-- OFFER NOT FOUND -->
|
|
8
|
+
<section v-if="offerNotFound" :class="$style.offerNotFound">
|
|
9
|
+
{{ localTranslations.offerNotFound }}
|
|
10
|
+
</section>
|
|
11
|
+
|
|
12
|
+
<!-- OFFER EXPIRED -->
|
|
13
|
+
<section v-else-if="offerExpired" :class="$style.offerExpired">
|
|
14
|
+
{{ localTranslations.offerExpired }}
|
|
15
|
+
</section>
|
|
16
|
+
</template>
|
|
17
|
+
|
|
18
|
+
<div v-if="productsLoading" :class="$style.spinnerWrapper">
|
|
19
|
+
<NsSpinner />
|
|
20
|
+
</div>
|
|
21
|
+
|
|
22
|
+
<!-- OFFER TITLE -->
|
|
23
|
+
<section v-if="showOffer" :class="$style.offerTitleMessaging">
|
|
24
|
+
<div :class="$style.offerMessaging">
|
|
25
|
+
<h2 :class="$style.offerTitle">{{ localTranslations.welcome }}</h2>
|
|
26
|
+
<p :class="$style.offerMessage">
|
|
27
|
+
<span v-if="!offer.isGroupOffer">{{ offer.name }}, </span>
|
|
28
|
+
<span v-html="offer.greeting"></span>
|
|
29
|
+
</p>
|
|
30
|
+
<p :class="$style.storePreferredName">- {{ storePreferredName }}</p>
|
|
31
|
+
</div>
|
|
32
|
+
</section>
|
|
33
|
+
|
|
34
|
+
<!-- OFFER BODY -->
|
|
35
|
+
<section :class="[$style.offerBody, { [$style.showOffer]: showOffer }]">
|
|
36
|
+
<!-- TOP PURCHASE ALL -->
|
|
37
|
+
<div v-if="showOffer" :class="$style.offerPurchaseAll">
|
|
38
|
+
<!-- OFFER TOTAL -->
|
|
39
|
+
<div :class="$style.offerTotalContainer">
|
|
40
|
+
<span v-if="offerTotalBeforeHtml" v-html="offerTotalBeforeHtml" />
|
|
41
|
+
<template v-if="showOfferTotal">
|
|
42
|
+
<span v-if="!hideDiscount" :class="$style.originalOfferTotal">
|
|
43
|
+
<NsCurrency :amount="originalOfferTotal" strike-through />
|
|
44
|
+
</span>
|
|
45
|
+
<span :class="$style.offerTotal">
|
|
46
|
+
<NsCurrency :amount="offerTotal" />
|
|
47
|
+
</span>
|
|
48
|
+
</template>
|
|
49
|
+
<span v-if="offerTotalAfterHtml" v-html="offerTotalAfterHtml" />
|
|
50
|
+
</div>
|
|
51
|
+
|
|
52
|
+
<!-- OFFER SAVINGS -->
|
|
53
|
+
<div
|
|
54
|
+
v-if="!hideDiscount && showOfferSavings"
|
|
55
|
+
:class="$style.offerSavingsContainer"
|
|
56
|
+
>
|
|
57
|
+
<span v-if="offerSavingsBeforeHtml" v-html="offerSavingsBeforeHtml" />
|
|
58
|
+
<span :class="$style.offerSavings">
|
|
59
|
+
<NsCurrency :amount="offerSavings" />
|
|
60
|
+
</span>
|
|
61
|
+
<span v-if="offerSavingsAfterHtml" v-html="offerSavingsAfterHtml" />
|
|
62
|
+
</div>
|
|
63
|
+
|
|
64
|
+
<!-- OFFER ACTION -->
|
|
65
|
+
<div :class="$style.offerAction">
|
|
66
|
+
<button
|
|
67
|
+
class="primary-solid fluid"
|
|
68
|
+
:class="$style.button"
|
|
69
|
+
:disabled="disableAddAll"
|
|
70
|
+
@click="addFullOfferToCart"
|
|
71
|
+
>
|
|
72
|
+
<NsIcon :class="$style.addToCartIcon" icon-name="icon-bag" />
|
|
73
|
+
{{ localTranslations.addAllItems }}
|
|
74
|
+
</button>
|
|
75
|
+
</div>
|
|
76
|
+
</div>
|
|
77
|
+
|
|
78
|
+
<!-- OFFER PRODUCTS -->
|
|
79
|
+
<div :class="$style.offerProducts">
|
|
80
|
+
<NsProductCard
|
|
81
|
+
v-for="(sku, index) in Object.keys(products)"
|
|
82
|
+
:key="index"
|
|
83
|
+
:class="[
|
|
84
|
+
$style.offerProduct,
|
|
85
|
+
{ [$style.invalid]: !!products[sku].invalid }
|
|
86
|
+
]"
|
|
87
|
+
:sku="sku"
|
|
88
|
+
:quantity="products[sku].requestedQuantity"
|
|
89
|
+
mobile-side-by-side
|
|
90
|
+
hide-favorites
|
|
91
|
+
@active-sku="activeSku => modifyProduct(sku, { activeSku })"
|
|
92
|
+
@availability="availability => modifyProduct(sku, { availability })"
|
|
93
|
+
@product-update="handleProductUpdate"
|
|
94
|
+
/>
|
|
95
|
+
</div>
|
|
96
|
+
|
|
97
|
+
<!-- BOTTOM PURCHASE ALL -->
|
|
98
|
+
<div
|
|
99
|
+
v-if="showOffer && !hideBottomPurchaseAll"
|
|
100
|
+
:class="$style.offerPurchaseAll"
|
|
101
|
+
>
|
|
102
|
+
<!-- OFFER TOTAL -->
|
|
103
|
+
<div :class="$style.offerTotalContainer">
|
|
104
|
+
<span v-if="offerTotalBeforeHtml" v-html="offerTotalBeforeHtml" />
|
|
105
|
+
<template v-if="showOfferTotal">
|
|
106
|
+
<span v-if="!hideDiscount" :class="$style.originalOfferTotal">
|
|
107
|
+
<NsCurrency :amount="originalOfferTotal" strike-through />
|
|
108
|
+
</span>
|
|
109
|
+
<span :class="$style.offerTotal">
|
|
110
|
+
<NsCurrency :amount="offerTotal" />
|
|
111
|
+
</span>
|
|
112
|
+
</template>
|
|
113
|
+
<span v-if="offerTotalAfterHtml" v-html="offerTotalAfterHtml" />
|
|
114
|
+
</div>
|
|
115
|
+
|
|
116
|
+
<!-- OFFER SAVINGS -->
|
|
117
|
+
<div
|
|
118
|
+
v-if="!hideDiscount && showOfferSavings"
|
|
119
|
+
:class="$style.offerSavingsContainer"
|
|
120
|
+
>
|
|
121
|
+
<span v-if="offerSavingsBeforeHtml" v-html="offerSavingsBeforeHtml" />
|
|
122
|
+
<span :class="$style.offerSavings">
|
|
123
|
+
<NsCurrency :amount="offerSavings" />
|
|
124
|
+
</span>
|
|
125
|
+
<span v-if="offerSavingsAfterHtml" v-html="offerSavingsAfterHtml" />
|
|
126
|
+
</div>
|
|
127
|
+
|
|
128
|
+
<!-- OFFER ACTION -->
|
|
129
|
+
<div :class="$style.offerAction">
|
|
130
|
+
<button
|
|
131
|
+
class="primary-solid fluid"
|
|
132
|
+
:class="$style.button"
|
|
133
|
+
:disabled="disableAddAll"
|
|
134
|
+
@click="addFullOfferToCart"
|
|
135
|
+
>
|
|
136
|
+
<NsIcon :class="$style.addToCartIcon" icon-name="icon-bag" />
|
|
137
|
+
{{ localTranslations.addAllItems }}
|
|
138
|
+
</button>
|
|
139
|
+
</div>
|
|
140
|
+
</div>
|
|
141
|
+
</section>
|
|
142
|
+
</article>
|
|
143
|
+
</template>
|
|
144
|
+
|
|
145
|
+
<script>
|
|
146
|
+
import {
|
|
147
|
+
ShoppingContext,
|
|
148
|
+
PersonalOfferStorageService,
|
|
149
|
+
SponsorStorageService,
|
|
150
|
+
events,
|
|
151
|
+
StoreFrontSponsorStorageService
|
|
152
|
+
} from "@nuskin/ns-util";
|
|
153
|
+
import { MySiteRestService } from "@nuskin/my-site-api";
|
|
154
|
+
import {
|
|
155
|
+
CartService,
|
|
156
|
+
CurrencyService,
|
|
157
|
+
PersonalOfferService
|
|
158
|
+
} from "@nuskin/ns-shop";
|
|
159
|
+
import { AccountService } from "@nuskin/ns-account";
|
|
160
|
+
import { isNullOrEmpty, isNumber } from "@nuskin/ns-common-lib";
|
|
161
|
+
import { NsSpinner, NsIcon } from "@nuskin/design-components";
|
|
162
|
+
import debounce from "lodash/debounce";
|
|
163
|
+
|
|
164
|
+
import NsCurrency from "./NsCurrency.vue";
|
|
165
|
+
import NsProductCard from "./NsProductCard.vue";
|
|
166
|
+
|
|
167
|
+
export default {
|
|
168
|
+
name: "NsProductOffer",
|
|
169
|
+
components: {
|
|
170
|
+
NsSpinner,
|
|
171
|
+
NsIcon,
|
|
172
|
+
// eslint-disable-next-line vue/no-unused-components
|
|
173
|
+
NsCurrency,
|
|
174
|
+
NsProductCard
|
|
175
|
+
},
|
|
176
|
+
props: {
|
|
177
|
+
// the store owner/user id
|
|
178
|
+
storeId: {
|
|
179
|
+
type: String,
|
|
180
|
+
default: ""
|
|
181
|
+
},
|
|
182
|
+
|
|
183
|
+
// the personal/product offer - otherwise known as pitch - id
|
|
184
|
+
offerId: {
|
|
185
|
+
type: String,
|
|
186
|
+
default: ""
|
|
187
|
+
},
|
|
188
|
+
translations: {
|
|
189
|
+
type: Object,
|
|
190
|
+
default() {
|
|
191
|
+
return {
|
|
192
|
+
welcome: "Welcome! (welcome)",
|
|
193
|
+
bundleTotal: "Bundle Total - {price} (bundleTotal)",
|
|
194
|
+
discountPercentagePrice:
|
|
195
|
+
"{percent}% Off - {savings} Savings (discountPercentagePrice)",
|
|
196
|
+
addAllItems: "Add {qty} Items To Bag (addAllItems)",
|
|
197
|
+
offerNotFound:
|
|
198
|
+
"Offer Not Found. Please contact {user}. (offerNotFound)",
|
|
199
|
+
offerExpired: "Offer Expired. Please contact {user}. (offerExpired)",
|
|
200
|
+
offerName: "{name} (offerName)"
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
},
|
|
205
|
+
data() {
|
|
206
|
+
return {
|
|
207
|
+
currencyCode: "",
|
|
208
|
+
queryStoreId: "",
|
|
209
|
+
queryOfferId: "",
|
|
210
|
+
storeOwner: null,
|
|
211
|
+
storePreferredName: "",
|
|
212
|
+
offer: null,
|
|
213
|
+
discount: null,
|
|
214
|
+
isAdr: false,
|
|
215
|
+
|
|
216
|
+
products: {},
|
|
217
|
+
totalProducts: 0,
|
|
218
|
+
totalProductQuantity: 0,
|
|
219
|
+
localTranslations: { ...(this.translations || {}) },
|
|
220
|
+
|
|
221
|
+
offerLoading: true,
|
|
222
|
+
offerNotFound: false,
|
|
223
|
+
offerExpired: false,
|
|
224
|
+
hideDiscount: true,
|
|
225
|
+
someProductsValid: false,
|
|
226
|
+
|
|
227
|
+
offerSavings: 0,
|
|
228
|
+
showOfferSavings: false,
|
|
229
|
+
offerSavingsBeforeHtml: "",
|
|
230
|
+
offerSavingsAfterHtml: "",
|
|
231
|
+
|
|
232
|
+
originalOfferTotal: 0,
|
|
233
|
+
offerTotal: 0,
|
|
234
|
+
showOfferTotal: false,
|
|
235
|
+
offerTotalBeforeHtml: "",
|
|
236
|
+
offerTotalAfterHtml: ""
|
|
237
|
+
};
|
|
238
|
+
},
|
|
239
|
+
computed: {
|
|
240
|
+
runConfig() {
|
|
241
|
+
return this.$NsProductAppService.runConfig;
|
|
242
|
+
},
|
|
243
|
+
market() {
|
|
244
|
+
return this.runConfig.country;
|
|
245
|
+
},
|
|
246
|
+
language() {
|
|
247
|
+
return this.runConfig.language;
|
|
248
|
+
},
|
|
249
|
+
showWholeSalePricing() {
|
|
250
|
+
return this.$NsProductAppService.showWholeSalePricing;
|
|
251
|
+
},
|
|
252
|
+
isLoggedIn() {
|
|
253
|
+
return this.$NsProductUserService.isLoggedIn;
|
|
254
|
+
},
|
|
255
|
+
user() {
|
|
256
|
+
return this.$NsProductUserService.user;
|
|
257
|
+
},
|
|
258
|
+
userId() {
|
|
259
|
+
return this.$NsProductUserService.userId;
|
|
260
|
+
},
|
|
261
|
+
isDistributor() {
|
|
262
|
+
return this.$NsProductUserService.isDistributor;
|
|
263
|
+
},
|
|
264
|
+
isPreferredCustomer() {
|
|
265
|
+
return this.$NsProductUserService.isPreferredCustomer;
|
|
266
|
+
},
|
|
267
|
+
localStoreId() {
|
|
268
|
+
return (this.storeOwner || {}).sapId || this.storeId || this.queryStoreId;
|
|
269
|
+
},
|
|
270
|
+
isStorefront() {
|
|
271
|
+
return !!this.localStoreId;
|
|
272
|
+
},
|
|
273
|
+
localOfferId() {
|
|
274
|
+
return (this.offer || {}).pitchID || this.offerId || this.queryOfferId;
|
|
275
|
+
},
|
|
276
|
+
isPersonalOffer() {
|
|
277
|
+
return !!this.localOfferId && !!(this.offer || {}).active;
|
|
278
|
+
},
|
|
279
|
+
disableAddAll() {
|
|
280
|
+
return this.offerLoading || this.totalProductQuantity === 0;
|
|
281
|
+
},
|
|
282
|
+
productsLoading() {
|
|
283
|
+
return this.$NsProductDataService.batches.some(
|
|
284
|
+
batch =>
|
|
285
|
+
!["success", "failed"].includes(batch.status) &&
|
|
286
|
+
batch.skus.some(batchSku =>
|
|
287
|
+
Object.keys(this.products).includes(batchSku)
|
|
288
|
+
)
|
|
289
|
+
);
|
|
290
|
+
},
|
|
291
|
+
showOffer() {
|
|
292
|
+
return (
|
|
293
|
+
!this.offerLoading &&
|
|
294
|
+
!this.productsLoading &&
|
|
295
|
+
!this.offerNotFound &&
|
|
296
|
+
!this.offerExpired &&
|
|
297
|
+
this.someProductsValid
|
|
298
|
+
);
|
|
299
|
+
},
|
|
300
|
+
mobileOrPhablet() {
|
|
301
|
+
return this.$mq === "phablet" || this.$mq === "mobile";
|
|
302
|
+
},
|
|
303
|
+
hideBottomPurchaseAll() {
|
|
304
|
+
return (
|
|
305
|
+
(!this.mobileOrPhablet && this.totalProducts < 7) ||
|
|
306
|
+
(this.mobileOrPhablet && this.totalProducts < 3)
|
|
307
|
+
);
|
|
308
|
+
}
|
|
309
|
+
},
|
|
310
|
+
watch: {
|
|
311
|
+
productsLoading(productsLoading) {
|
|
312
|
+
if (!productsLoading) {
|
|
313
|
+
this.checkProductsHaveData();
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
},
|
|
317
|
+
async mounted() {
|
|
318
|
+
await this.addListeners();
|
|
319
|
+
},
|
|
320
|
+
beforeDestroy() {
|
|
321
|
+
this.removeListeners();
|
|
322
|
+
},
|
|
323
|
+
methods: {
|
|
324
|
+
async init() {
|
|
325
|
+
this.currencyCode = CurrencyService.getCurrency(this.market).currencyCode;
|
|
326
|
+
|
|
327
|
+
// in AEM, the storeId (userId) and offerId (pitchId) are query parameters
|
|
328
|
+
this.queryStoreId =
|
|
329
|
+
this.getSearchParameter("storeId") || this.getSearchParameter("userId");
|
|
330
|
+
this.queryOfferId =
|
|
331
|
+
this.getSearchParameter("offerId") ||
|
|
332
|
+
this.getSearchParameter("pitchId");
|
|
333
|
+
|
|
334
|
+
const shoppingContext = ShoppingContext.getShoppingContext();
|
|
335
|
+
if (isNullOrEmpty(shoppingContext) && !!this.localOfferId) {
|
|
336
|
+
ShoppingContext.setShoppingContext(ShoppingContext.PERSONAL_OFFER);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
this.storeOwner = StoreFrontSponsorStorageService.getStoreFrontSponsor();
|
|
340
|
+
if (
|
|
341
|
+
this.localStoreId &&
|
|
342
|
+
(isNullOrEmpty(this.storeOwner) ||
|
|
343
|
+
this.storeOwner.sapId !== this.localStoreId)
|
|
344
|
+
) {
|
|
345
|
+
let sponsor = null;
|
|
346
|
+
|
|
347
|
+
try {
|
|
348
|
+
// getSponsorData threw an error for accessing user id when the user/sponsor isn't found, catch it
|
|
349
|
+
sponsor = await MySiteRestService.getSponsorData(this.localStoreId);
|
|
350
|
+
} catch (err) {
|
|
351
|
+
console.error("Unable to get Store Owner. Error: ", err);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
if (sponsor) {
|
|
355
|
+
StoreFrontSponsorStorageService.setStoreFrontSponsor(sponsor);
|
|
356
|
+
this.storeOwner = StoreFrontSponsorStorageService.getStoreFrontSponsor();
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
if (!isNullOrEmpty(this.storeOwner) && !!this.localOfferId) {
|
|
361
|
+
// get store preferred name from account service if possible
|
|
362
|
+
const storeOwnerAccountInfo = await AccountService.getAccountInfo(
|
|
363
|
+
this.localStoreId
|
|
364
|
+
);
|
|
365
|
+
this.storePreferredName =
|
|
366
|
+
(storeOwnerAccountInfo || {}).preferredName ||
|
|
367
|
+
this.storeOwner.displayName;
|
|
368
|
+
|
|
369
|
+
this.setT(
|
|
370
|
+
"offerNotFound",
|
|
371
|
+
this.t("offerNotFound").replace("{user}", this.storePreferredName)
|
|
372
|
+
);
|
|
373
|
+
|
|
374
|
+
this.setT(
|
|
375
|
+
"offerExpired",
|
|
376
|
+
this.t("offerExpired").replace("{user}", this.storePreferredName)
|
|
377
|
+
);
|
|
378
|
+
|
|
379
|
+
await this.getOffer();
|
|
380
|
+
|
|
381
|
+
this.offerLoading = false;
|
|
382
|
+
} else {
|
|
383
|
+
this.offerNotFound = true;
|
|
384
|
+
this.showOfferSavings = false;
|
|
385
|
+
this.offerLoading = false;
|
|
386
|
+
}
|
|
387
|
+
},
|
|
388
|
+
|
|
389
|
+
getSearchParameter(name) {
|
|
390
|
+
const params = new URLSearchParams(window.location.search);
|
|
391
|
+
return params.get(name) || "";
|
|
392
|
+
},
|
|
393
|
+
|
|
394
|
+
async getOffer() {
|
|
395
|
+
// original source: https://code.tls.nuskin.io/ns-am/nu-skin-aem/wm/ns-shop-elements/-/blob/master/src/ns-personal-offer-landing/ns-personal-offer-landing.js
|
|
396
|
+
|
|
397
|
+
// "Stop Status"(05) showing "out of stock"(在庫切れ) message on PO welcome page, it should show “Stop selling“(販売停止).
|
|
398
|
+
// https://test.nuskin.com/content/markets/ja_JP/products/product.03003536.mysite.html
|
|
399
|
+
// https://test.nskn.co/vgR82L
|
|
400
|
+
// https://test.nuskin.com/content/markets/ja_JP/personal-offer.mysite.html?userId=JA10153135&pitchId=-MqCvG0kuPDgU1dYV6Dv
|
|
401
|
+
|
|
402
|
+
// base product scenario: CA00075667 ///// https://test.nskn.co/iDSndb
|
|
403
|
+
|
|
404
|
+
let offer = await PersonalOfferService.getOfferV2(
|
|
405
|
+
this.localStoreId,
|
|
406
|
+
this.localOfferId
|
|
407
|
+
);
|
|
408
|
+
|
|
409
|
+
if (isNullOrEmpty(offer)) {
|
|
410
|
+
this.offerExpired = false;
|
|
411
|
+
this.isAdr = false;
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
await this.getOfferPitch(offer);
|
|
416
|
+
|
|
417
|
+
const expirationDate = offer.expirationDate;
|
|
418
|
+
if (
|
|
419
|
+
expirationDate &&
|
|
420
|
+
new Date(expirationDate).getTime() <= new Date().getTime()
|
|
421
|
+
) {
|
|
422
|
+
this.offerExpired = true;
|
|
423
|
+
return;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
this.isAdr = !!offer.useADR;
|
|
427
|
+
|
|
428
|
+
const storageOffer = localStorage.getItem("personalOffer_v2");
|
|
429
|
+
const parsedOffer = !isNullOrEmpty(storageOffer)
|
|
430
|
+
? JSON.parse(storageOffer)
|
|
431
|
+
: null;
|
|
432
|
+
const currentSessionId = (parsedOffer || {}).sessionId;
|
|
433
|
+
const currentSession = currentSessionId
|
|
434
|
+
? (offer.sessions || {})[currentSessionId]
|
|
435
|
+
: null;
|
|
436
|
+
|
|
437
|
+
// new session
|
|
438
|
+
if (!currentSession) {
|
|
439
|
+
try {
|
|
440
|
+
const viewedResponse = await PersonalOfferService.callViewed(
|
|
441
|
+
this.localStoreId,
|
|
442
|
+
this.localOfferId
|
|
443
|
+
);
|
|
444
|
+
PersonalOfferStorageService.setSessionId(viewedResponse.sessionId);
|
|
445
|
+
} catch (err) {
|
|
446
|
+
console.error("Unable to set offer as viewed. Error: ", err);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
},
|
|
450
|
+
|
|
451
|
+
async getOfferPitch(offer) {
|
|
452
|
+
if (
|
|
453
|
+
(this.offerId && this.offerId !== offer.pitchID) ||
|
|
454
|
+
(this.queryOfferId && this.queryOfferId !== offer.pitchID)
|
|
455
|
+
) {
|
|
456
|
+
CartService.clearCart();
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
let response = await PersonalOfferService.getPitch(
|
|
460
|
+
this.localStoreId,
|
|
461
|
+
this.localOfferId
|
|
462
|
+
);
|
|
463
|
+
|
|
464
|
+
let responseObject =
|
|
465
|
+
(response || {}).data && Object.keys(response.data).length
|
|
466
|
+
? response.data
|
|
467
|
+
: null;
|
|
468
|
+
|
|
469
|
+
if (
|
|
470
|
+
isNullOrEmpty(responseObject) ||
|
|
471
|
+
(!responseObject.items && !responseObject.products)
|
|
472
|
+
) {
|
|
473
|
+
response = {
|
|
474
|
+
data: offer
|
|
475
|
+
};
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
responseObject =
|
|
479
|
+
response && response.data && Object.keys(response.data).length
|
|
480
|
+
? response.data
|
|
481
|
+
: null;
|
|
482
|
+
|
|
483
|
+
if (
|
|
484
|
+
responseObject &&
|
|
485
|
+
responseObject.active !== false &&
|
|
486
|
+
!responseObject.isDeleted
|
|
487
|
+
) {
|
|
488
|
+
responseObject.sapId = this.localStoreId;
|
|
489
|
+
responseObject.pitchID = this.localOfferId;
|
|
490
|
+
|
|
491
|
+
responseObject.congratulations =
|
|
492
|
+
responseObject.congratulations || responseObject.confirmationMessage;
|
|
493
|
+
responseObject.isGroupOffer =
|
|
494
|
+
responseObject.isGroupOffer || responseObject.isGroup;
|
|
495
|
+
responseObject.language =
|
|
496
|
+
responseObject.language || responseObject.languageCode;
|
|
497
|
+
responseObject.greeting =
|
|
498
|
+
responseObject.greeting || responseObject.message;
|
|
499
|
+
responseObject.items =
|
|
500
|
+
responseObject.items ||
|
|
501
|
+
responseObject.products.map(item => {
|
|
502
|
+
return {
|
|
503
|
+
SKU: item.sku,
|
|
504
|
+
RequestedQuantity: item.qty
|
|
505
|
+
};
|
|
506
|
+
});
|
|
507
|
+
|
|
508
|
+
const useMemberPricing = !!responseObject.useMemberPricing;
|
|
509
|
+
this.setMemberPricing(useMemberPricing);
|
|
510
|
+
|
|
511
|
+
responseObject.landingPageURL = window.location.href;
|
|
512
|
+
SponsorStorageService.setSponsor(responseObject.sapId);
|
|
513
|
+
|
|
514
|
+
this.setT(
|
|
515
|
+
"offerName",
|
|
516
|
+
this.t("offerName").replace("{name}", responseObject.name)
|
|
517
|
+
);
|
|
518
|
+
|
|
519
|
+
responseObject.name = this.localTranslations.offerName;
|
|
520
|
+
this.offer = responseObject;
|
|
521
|
+
|
|
522
|
+
if (this.isLoggedIn) {
|
|
523
|
+
if (this.isDistributor || this.isPreferredCustomer) {
|
|
524
|
+
this.discount = {
|
|
525
|
+
code: "RETAILPRICE",
|
|
526
|
+
description: "RetailPricing",
|
|
527
|
+
multiplier: 1
|
|
528
|
+
};
|
|
529
|
+
responseObject.discount = this.discount;
|
|
530
|
+
} else {
|
|
531
|
+
this.discount = responseObject.discount;
|
|
532
|
+
}
|
|
533
|
+
} else {
|
|
534
|
+
this.discount = responseObject.discount;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
PersonalOfferStorageService.setPersonalOffer(responseObject);
|
|
538
|
+
let multiplier = this.discount ? this.discount.multiplier : 1;
|
|
539
|
+
this.hideDiscount = Number(multiplier) == 1;
|
|
540
|
+
|
|
541
|
+
for (const item of responseObject.items) {
|
|
542
|
+
this.products[item.SKU] = {
|
|
543
|
+
sku: item.SKU,
|
|
544
|
+
requestedQuantity: item.RequestedQuantity,
|
|
545
|
+
data: {}
|
|
546
|
+
};
|
|
547
|
+
}
|
|
548
|
+
this.totalProducts = Object.keys(this.products).length;
|
|
549
|
+
|
|
550
|
+
events.publish(events.shop.PRODUCT_IMPRESSION, {
|
|
551
|
+
ecommerce: {
|
|
552
|
+
currencyCode: this.currencyCode,
|
|
553
|
+
impressions: responseObject.items.map((item, index) => {
|
|
554
|
+
return {
|
|
555
|
+
id: item.SKU,
|
|
556
|
+
name: item.name,
|
|
557
|
+
price: item.price,
|
|
558
|
+
quantity: item.RequestedQuantity,
|
|
559
|
+
// "category": item.category, // category comes from EDW categories
|
|
560
|
+
position: index,
|
|
561
|
+
list: "pitchapp"
|
|
562
|
+
};
|
|
563
|
+
})
|
|
564
|
+
}
|
|
565
|
+
});
|
|
566
|
+
} else {
|
|
567
|
+
this.offerNotFound = true;
|
|
568
|
+
this.showOfferSavings = false;
|
|
569
|
+
this.offerLoading = false;
|
|
570
|
+
}
|
|
571
|
+
},
|
|
572
|
+
|
|
573
|
+
t(key) {
|
|
574
|
+
return (this.translations || {})[key] || "";
|
|
575
|
+
},
|
|
576
|
+
|
|
577
|
+
setT(key, value) {
|
|
578
|
+
this.$set(this.localTranslations, key, value);
|
|
579
|
+
},
|
|
580
|
+
|
|
581
|
+
modifyProduct(sku, props) {
|
|
582
|
+
if (this.products[sku]) {
|
|
583
|
+
this.products[sku] = { ...this.products[sku], ...props };
|
|
584
|
+
} else {
|
|
585
|
+
this.products[sku] = props;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
this.checkProductsHaveData();
|
|
589
|
+
},
|
|
590
|
+
|
|
591
|
+
handleProductUpdate(product) {
|
|
592
|
+
if ((product || {}).sku && this.products[product.sku]) {
|
|
593
|
+
this.products[product.sku].data = product;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
this.checkProductsHaveData();
|
|
597
|
+
},
|
|
598
|
+
|
|
599
|
+
checkProductsHaveData: debounce(function() {
|
|
600
|
+
const products = Object.values(this.products);
|
|
601
|
+
if (products.every(product => !isNullOrEmpty(product.availability))) {
|
|
602
|
+
this.someProductsValid = products.some(
|
|
603
|
+
product =>
|
|
604
|
+
!isNullOrEmpty(product.availability) && !isNullOrEmpty(product.data)
|
|
605
|
+
);
|
|
606
|
+
|
|
607
|
+
this.getOfferPrice();
|
|
608
|
+
|
|
609
|
+
if (products.every(product => isNullOrEmpty(product.data))) {
|
|
610
|
+
this.offerNotFound = true;
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
}, 250),
|
|
614
|
+
|
|
615
|
+
async addFullOfferToCart() {
|
|
616
|
+
const validSkus = Object.keys(this.products).filter(sku => {
|
|
617
|
+
const product = this.products[sku];
|
|
618
|
+
return (
|
|
619
|
+
!product.invalid &&
|
|
620
|
+
!isNullOrEmpty(product.data) &&
|
|
621
|
+
!isNullOrEmpty(product.availability) &&
|
|
622
|
+
product.availability.addToCart
|
|
623
|
+
);
|
|
624
|
+
});
|
|
625
|
+
|
|
626
|
+
if (!validSkus.length) {
|
|
627
|
+
return;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
window.sessionStorage.setItem("nstoast-allowed", "true");
|
|
631
|
+
|
|
632
|
+
const countryCode = CartService.getCartProperty("cntryCd") || "";
|
|
633
|
+
|
|
634
|
+
let checkoutProducts = [];
|
|
635
|
+
|
|
636
|
+
for (const validSku of validSkus) {
|
|
637
|
+
const validProduct = this.products[validSku];
|
|
638
|
+
const selectedSku = validProduct.activeSku;
|
|
639
|
+
const selectedQuantity = validProduct.availability.selectedQuantity;
|
|
640
|
+
|
|
641
|
+
const cartOptions = {
|
|
642
|
+
sku: selectedSku,
|
|
643
|
+
product: validProduct.data,
|
|
644
|
+
qty: selectedQuantity,
|
|
645
|
+
userId: this.userId,
|
|
646
|
+
cntryCd: countryCode,
|
|
647
|
+
adr: false,
|
|
648
|
+
referrer: "myStore",
|
|
649
|
+
domain: window.location.hostname
|
|
650
|
+
};
|
|
651
|
+
|
|
652
|
+
CartService.addProductToCart(cartOptions)
|
|
653
|
+
.then(() => {
|
|
654
|
+
events.publish(events.shop.ADD_TO_CART, cartOptions);
|
|
655
|
+
|
|
656
|
+
events.publish(events.shop.ADD_TO_CART_NEW, {
|
|
657
|
+
event: "addToCart",
|
|
658
|
+
ecommerce: {
|
|
659
|
+
currencyCode: this.currencyCode,
|
|
660
|
+
add: {
|
|
661
|
+
actionField: {
|
|
662
|
+
list: "myStore"
|
|
663
|
+
},
|
|
664
|
+
products: [
|
|
665
|
+
{
|
|
666
|
+
name: validProduct.data.title,
|
|
667
|
+
id: selectedSku,
|
|
668
|
+
price: validProduct.availability.price,
|
|
669
|
+
quantity: selectedQuantity,
|
|
670
|
+
method: "personaloffer", // personal offer or quickView or productPage or cart
|
|
671
|
+
cartType: "Standard"
|
|
672
|
+
}
|
|
673
|
+
]
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
});
|
|
677
|
+
|
|
678
|
+
events.publish(events.shop.SHOW_ADD_TO_BAG, cartOptions);
|
|
679
|
+
|
|
680
|
+
checkoutProducts.push({
|
|
681
|
+
name: validProduct.data.title,
|
|
682
|
+
id: selectedSku,
|
|
683
|
+
price: validProduct.availability.price,
|
|
684
|
+
quantity: selectedQuantity
|
|
685
|
+
});
|
|
686
|
+
})
|
|
687
|
+
.catch(error => {
|
|
688
|
+
console.error(
|
|
689
|
+
`Failed to add '${selectedSku}' x${selectedQuantity} to cart. Error: `,
|
|
690
|
+
error
|
|
691
|
+
);
|
|
692
|
+
});
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
if (!checkoutProducts.length) {
|
|
696
|
+
return;
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
events.publish(events.shop.CART_CHECKOUT, {
|
|
700
|
+
event: "checkout",
|
|
701
|
+
ecommerce: {
|
|
702
|
+
currencyCode: this.currencyCode,
|
|
703
|
+
checkout: {
|
|
704
|
+
actionField: { step: 2 },
|
|
705
|
+
products: checkoutProducts
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
});
|
|
709
|
+
|
|
710
|
+
try {
|
|
711
|
+
await PersonalOfferService.callProduct(
|
|
712
|
+
this.localStoreId,
|
|
713
|
+
this.localOfferId
|
|
714
|
+
);
|
|
715
|
+
} catch (err) {
|
|
716
|
+
console.error("Failed updating product status to 'product_added'");
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
CartService.goToCartPage({ isMySite: true });
|
|
720
|
+
},
|
|
721
|
+
|
|
722
|
+
setMemberPricing(useMemberPricing) {
|
|
723
|
+
let contextOptions = {
|
|
724
|
+
showWholeSalePricing: useMemberPricing,
|
|
725
|
+
allowAnonymousShopping: !useMemberPricing,
|
|
726
|
+
preferredSignupOnly: useMemberPricing
|
|
727
|
+
};
|
|
728
|
+
|
|
729
|
+
ShoppingContext.setShoppingContext(
|
|
730
|
+
ShoppingContext.PERSONAL_OFFER,
|
|
731
|
+
contextOptions
|
|
732
|
+
);
|
|
733
|
+
},
|
|
734
|
+
|
|
735
|
+
getOfferPrice() {
|
|
736
|
+
let totalPrice = 0;
|
|
737
|
+
this.totalProductQuantity = 0;
|
|
738
|
+
|
|
739
|
+
for (const sku of Object.keys(this.products)) {
|
|
740
|
+
this.products[sku].invalid = true;
|
|
741
|
+
const availability = this.products[sku].availability;
|
|
742
|
+
if (availability) {
|
|
743
|
+
const selectedQuantity = availability.selectedQuantity || 0;
|
|
744
|
+
if (selectedQuantity > 0) {
|
|
745
|
+
const price = availability.price || 0;
|
|
746
|
+
if (availability.addToCart) {
|
|
747
|
+
totalPrice += price * selectedQuantity;
|
|
748
|
+
this.totalProductQuantity += selectedQuantity;
|
|
749
|
+
this.products[sku].invalid = false;
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
this.setT(
|
|
756
|
+
"addAllItems",
|
|
757
|
+
this.t("addAllItems").replace("{qty}", this.totalProductQuantity)
|
|
758
|
+
);
|
|
759
|
+
|
|
760
|
+
if (isNumber(totalPrice)) {
|
|
761
|
+
this.originalOfferTotal = totalPrice;
|
|
762
|
+
this.offerTotal = totalPrice;
|
|
763
|
+
|
|
764
|
+
if (
|
|
765
|
+
(this.discount && !this.user) ||
|
|
766
|
+
(this.user &&
|
|
767
|
+
(this.user.priceType === "RTL" ||
|
|
768
|
+
this.user.priceType === "WRTL" ||
|
|
769
|
+
this.user.isGuest))
|
|
770
|
+
) {
|
|
771
|
+
let multiplier = this.discount ? this.discount.multiplier : 1;
|
|
772
|
+
|
|
773
|
+
this.offerTotal = totalPrice * multiplier;
|
|
774
|
+
this.offerSavings = this.originalOfferTotal - this.offerTotal;
|
|
775
|
+
|
|
776
|
+
const discountNum = Math.round((1 - multiplier) * 100);
|
|
777
|
+
this.showOfferSavings =
|
|
778
|
+
this.offerSavings > 0 && parseInt(discountNum);
|
|
779
|
+
if (this.showOfferSavings) {
|
|
780
|
+
this.setT(
|
|
781
|
+
"discountPercentagePrice",
|
|
782
|
+
this.t("discountPercentagePrice").replace(
|
|
783
|
+
"{percent}",
|
|
784
|
+
discountNum
|
|
785
|
+
)
|
|
786
|
+
);
|
|
787
|
+
const discountPercentagePrice = this.localTranslations
|
|
788
|
+
.discountPercentagePrice;
|
|
789
|
+
const offerSavingsHtmlParts = discountPercentagePrice.split(
|
|
790
|
+
"{savings}"
|
|
791
|
+
);
|
|
792
|
+
this.offerSavingsBeforeHtml = offerSavingsHtmlParts[0];
|
|
793
|
+
this.offerSavingsAfterHtml =
|
|
794
|
+
offerSavingsHtmlParts.length > 1 ? offerSavingsHtmlParts[1] : "";
|
|
795
|
+
}
|
|
796
|
+
} else {
|
|
797
|
+
this.hideDiscount = true;
|
|
798
|
+
this.showOfferSavings = false;
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
const offerTotalHtmlParts = this.t("bundleTotal").split("{price}");
|
|
802
|
+
this.showOfferTotal = offerTotalHtmlParts.length > 1;
|
|
803
|
+
this.offerTotalBeforeHtml = offerTotalHtmlParts[0];
|
|
804
|
+
this.offerTotalAfterHtml = this.showOfferTotal
|
|
805
|
+
? offerTotalHtmlParts[1]
|
|
806
|
+
: "";
|
|
807
|
+
|
|
808
|
+
this.offerNotFound = false;
|
|
809
|
+
} else {
|
|
810
|
+
this.offerNotFound = true;
|
|
811
|
+
}
|
|
812
|
+
},
|
|
813
|
+
|
|
814
|
+
/**
|
|
815
|
+
* Handle product status events
|
|
816
|
+
*/
|
|
817
|
+
handleProductStatus(results) {
|
|
818
|
+
this.$NsProductDataService.handleProductStatus(results);
|
|
819
|
+
},
|
|
820
|
+
|
|
821
|
+
/**
|
|
822
|
+
* Subscribe to events, should only be called when mounted
|
|
823
|
+
*/
|
|
824
|
+
async addListeners() {
|
|
825
|
+
// watch app service finished loading
|
|
826
|
+
this.$watch(
|
|
827
|
+
"!$NsProductAppService.loading",
|
|
828
|
+
async () => await this.init(),
|
|
829
|
+
{
|
|
830
|
+
immediate: true
|
|
831
|
+
}
|
|
832
|
+
);
|
|
833
|
+
|
|
834
|
+
// listen to config changes
|
|
835
|
+
this.$NsProductAppService.$on("config-changed", async () => {
|
|
836
|
+
await this.init();
|
|
837
|
+
});
|
|
838
|
+
|
|
839
|
+
events.subscribe(events.authentication.REQUEST_FOR_LOGIN, function() {
|
|
840
|
+
CartService.clearCart();
|
|
841
|
+
});
|
|
842
|
+
|
|
843
|
+
// listen to product status changes
|
|
844
|
+
events.subscribe(
|
|
845
|
+
events.shop.PRODUCT_STATUS_RESULT,
|
|
846
|
+
this.handleProductStatus
|
|
847
|
+
);
|
|
848
|
+
},
|
|
849
|
+
|
|
850
|
+
/**
|
|
851
|
+
* Unsubscribe from events, should only be called when destroyed
|
|
852
|
+
*/
|
|
853
|
+
removeListeners() {
|
|
854
|
+
// stop listening to product app service events
|
|
855
|
+
this.$NsProductAppService.$off();
|
|
856
|
+
|
|
857
|
+
// stop listening to product status changes
|
|
858
|
+
events.unsubscribe(
|
|
859
|
+
events.shop.PRODUCT_STATUS_RESULT,
|
|
860
|
+
this.handleProductStatus
|
|
861
|
+
);
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
};
|
|
865
|
+
</script>
|
|
866
|
+
|
|
867
|
+
<style lang="scss" module>
|
|
868
|
+
@use "sass:map";
|
|
869
|
+
@use "~@nuskin/ns-core-styles/src/scss/colors";
|
|
870
|
+
@use "~@nuskin/ns-core-styles/src/scss/other";
|
|
871
|
+
@use "~@nuskin/ns-core-styles/src/scss/typographyVariables";
|
|
872
|
+
@use "~@nuskin/ns-core-styles/src/scss/global.mixins";
|
|
873
|
+
|
|
874
|
+
* {
|
|
875
|
+
box-sizing: border-box;
|
|
876
|
+
-webkit-font-smoothing: antialiased;
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
.personalOfferDetails {
|
|
880
|
+
width: 100%;
|
|
881
|
+
background-color: white;
|
|
882
|
+
font-family: map.get(typographyVariables.$typography, font-family-primary);
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
.addToCartIcon {
|
|
886
|
+
position: relative;
|
|
887
|
+
top: 1px;
|
|
888
|
+
stroke-width: 1.5px;
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
section {
|
|
892
|
+
max-width: 1000px;
|
|
893
|
+
margin: auto;
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
.offerNotFound {
|
|
897
|
+
padding: 30px;
|
|
898
|
+
text-align: center;
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
.offerTitleMessaging {
|
|
902
|
+
max-width: 100%;
|
|
903
|
+
background-color: #f6f7f7;
|
|
904
|
+
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.15);
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
.offerMessaging {
|
|
908
|
+
margin: auto;
|
|
909
|
+
padding: 12px 25px 25px 25px;
|
|
910
|
+
max-width: 600px;
|
|
911
|
+
color: rgb(68, 68, 68);
|
|
912
|
+
text-align: center;
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
.offerTitle {
|
|
916
|
+
font-size: 28px;
|
|
917
|
+
line-height: 34px;
|
|
918
|
+
font-weight: 100;
|
|
919
|
+
margin-block-start: 0.83em;
|
|
920
|
+
margin-block-end: 0.83em;
|
|
921
|
+
margin-inline-start: 0px;
|
|
922
|
+
margin-inline-end: 0px;
|
|
923
|
+
margin: 10px 0 10px 0;
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
.offerMessage {
|
|
927
|
+
margin: 0;
|
|
928
|
+
font-size: 16px;
|
|
929
|
+
line-height: 21px;
|
|
930
|
+
font-weight: 300;
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
.storePreferredName {
|
|
934
|
+
margin-top: 16px;
|
|
935
|
+
text-transform: uppercase;
|
|
936
|
+
font-weight: 400;
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
.offerBody {
|
|
940
|
+
&.showOffer {
|
|
941
|
+
padding-bottom: 50px;
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
.offerPurchaseAll {
|
|
946
|
+
display: flex;
|
|
947
|
+
flex-direction: column;
|
|
948
|
+
align-items: center;
|
|
949
|
+
margin: 40px 0;
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
.offerTotalContainer {
|
|
953
|
+
display: flex;
|
|
954
|
+
font-size: 18px;
|
|
955
|
+
line-height: 24px;
|
|
956
|
+
font-weight: 600;
|
|
957
|
+
gap: 5px;
|
|
958
|
+
|
|
959
|
+
.originalOfferTotal {
|
|
960
|
+
font-weight: 300;
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
.offerTotal {
|
|
964
|
+
font-weight: 600;
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
.offerSavingsContainer {
|
|
969
|
+
display: flex;
|
|
970
|
+
font-weight: 300;
|
|
971
|
+
gap: 4px;
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
.offerAction {
|
|
975
|
+
margin-top: 1em;
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
.offerProducts {
|
|
979
|
+
display: flex;
|
|
980
|
+
flex-wrap: wrap;
|
|
981
|
+
margin: 0 -7.5px;
|
|
982
|
+
justify-content: center;
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
.offerProduct {
|
|
986
|
+
margin: auto;
|
|
987
|
+
padding: 0 7.5px;
|
|
988
|
+
margin-bottom: 48px;
|
|
989
|
+
|
|
990
|
+
&.invalid {
|
|
991
|
+
box-shadow: #c23934 0px 1px 10px 1px;
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
.spinnerWrapper {
|
|
996
|
+
padding: 40px;
|
|
997
|
+
display: flex;
|
|
998
|
+
justify-content: center;
|
|
999
|
+
width: 100%;
|
|
1000
|
+
}
|
|
1001
|
+
</style>
|
package/index.js
CHANGED
|
@@ -32,6 +32,7 @@ import NsProductCarousel from "./components/NsProductCarousel.vue";
|
|
|
32
32
|
import NsProductList from "./components/NsProductList.vue";
|
|
33
33
|
import NsProductListSortable from "./components/NsProductListSortable.vue";
|
|
34
34
|
import NsProductLine from "./components/NsProductLine.vue";
|
|
35
|
+
import NsProductOffer from "./components/NsProductOffer.vue";
|
|
35
36
|
|
|
36
37
|
export {
|
|
37
38
|
// Services
|
|
@@ -50,5 +51,6 @@ export {
|
|
|
50
51
|
NsProductCarousel,
|
|
51
52
|
NsProductList,
|
|
52
53
|
NsProductListSortable,
|
|
53
|
-
NsProductLine
|
|
54
|
+
NsProductLine,
|
|
55
|
+
NsProductOffer
|
|
54
56
|
};
|
package/mixins/NsProductMixin.js
CHANGED
|
@@ -337,7 +337,10 @@ const NsProductMixin = {
|
|
|
337
337
|
this.resetProductData(sku);
|
|
338
338
|
|
|
339
339
|
this.activeSku = sku;
|
|
340
|
+
|
|
340
341
|
this.setFromProductData(sku);
|
|
342
|
+
|
|
343
|
+
this.$emit("active-sku", this.activeSku);
|
|
341
344
|
},
|
|
342
345
|
|
|
343
346
|
setFromProductData(sku, product) {
|
|
@@ -347,6 +350,7 @@ const NsProductMixin = {
|
|
|
347
350
|
|
|
348
351
|
if (!product) {
|
|
349
352
|
this.$NsProductDataService.queue(sku);
|
|
353
|
+
this.emitAvailability();
|
|
350
354
|
return;
|
|
351
355
|
}
|
|
352
356
|
|
|
@@ -536,8 +540,7 @@ const NsProductMixin = {
|
|
|
536
540
|
*/
|
|
537
541
|
setProductAvailability() {
|
|
538
542
|
if (this.product) {
|
|
539
|
-
//
|
|
540
|
-
// WARNING: There's a instanceof check on CartService.getAddToCartQty for the ns-shop Product
|
|
543
|
+
// WARNING: There's a instanceof check on CartService.getAddToCartQty for the ns-shop Product, previously different
|
|
541
544
|
const shopProduct = new ShopProduct(this.product);
|
|
542
545
|
// check if a user is qualified to purchase the product
|
|
543
546
|
CartService.getAddToCartQty(shopProduct)
|
|
@@ -556,6 +559,7 @@ const NsProductMixin = {
|
|
|
556
559
|
.finally(() => {
|
|
557
560
|
this.setStatus();
|
|
558
561
|
this.checkedQualifications = true;
|
|
562
|
+
this.emitAvailability();
|
|
559
563
|
});
|
|
560
564
|
}
|
|
561
565
|
},
|
|
@@ -578,10 +582,25 @@ const NsProductMixin = {
|
|
|
578
582
|
const productQualification = await QualificationService.getQualification(
|
|
579
583
|
this.activeSku
|
|
580
584
|
);
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
+
|
|
586
|
+
let cartQuantity;
|
|
587
|
+
if (!this.baseSku) {
|
|
588
|
+
const cartItem = await CartService.getFirstItemBySku(
|
|
589
|
+
this.activeSku,
|
|
590
|
+
{
|
|
591
|
+
cartOrderItems: true
|
|
592
|
+
}
|
|
593
|
+
);
|
|
594
|
+
cartQuantity = (cartItem || {}).qty || 0;
|
|
595
|
+
} else {
|
|
596
|
+
const cartData =
|
|
597
|
+
(await QualificationService.convertCartDataToBaseSkus(
|
|
598
|
+
CartService.getItemData(),
|
|
599
|
+
true
|
|
600
|
+
)) || {};
|
|
601
|
+
const cartItem = cartData[this.baseSku] || {};
|
|
602
|
+
cartQuantity = cartItem.qty || 0;
|
|
603
|
+
}
|
|
585
604
|
|
|
586
605
|
if (!productQualification) {
|
|
587
606
|
// the event has never been set up or is in the past (post-event/qualification not configured)
|
|
@@ -807,11 +826,28 @@ const NsProductMixin = {
|
|
|
807
826
|
) {
|
|
808
827
|
this.disableAddToCart = true;
|
|
809
828
|
this.disableAddToAdr = true;
|
|
829
|
+
|
|
830
|
+
this.$emit("disable-add-to-cart", quantity);
|
|
810
831
|
} else if (this.userIsQualified) {
|
|
811
832
|
this.checkAddToCartAndAdr();
|
|
812
833
|
}
|
|
813
834
|
|
|
814
835
|
this.$emit("quantity-select", quantity);
|
|
836
|
+
this.emitAvailability();
|
|
837
|
+
},
|
|
838
|
+
|
|
839
|
+
emitAvailability() {
|
|
840
|
+
this.$emit("availability", {
|
|
841
|
+
isBaseSku: this.isBaseSku,
|
|
842
|
+
isVariantSku: this.isVariantSku,
|
|
843
|
+
addToCart: !this.disableAddToCart,
|
|
844
|
+
addToAdr: !this.disableAddToCart,
|
|
845
|
+
selectedQuantity: Number(this.selectedQuantity),
|
|
846
|
+
maxQuantity: this.maxQuantity,
|
|
847
|
+
originalPrice: this.originalPrice,
|
|
848
|
+
price: this.price,
|
|
849
|
+
adrPrice: this.adrPrice
|
|
850
|
+
});
|
|
815
851
|
},
|
|
816
852
|
|
|
817
853
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nuskin/product-components",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.1.0-product-offer.2",
|
|
4
4
|
"description": "Nu Skin Product Components",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -24,10 +24,10 @@
|
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"@mdi/font": "3.9.97",
|
|
26
26
|
"@nuskin/design-components": "5.36.1",
|
|
27
|
-
"@nuskin/ns-common-lib": "1.
|
|
27
|
+
"@nuskin/ns-common-lib": "1.4.5",
|
|
28
28
|
"@nuskin/ns-core-styles": "2.11.2",
|
|
29
29
|
"@nuskin/ns-loyalty-web": "1.5.5",
|
|
30
|
-
"@nuskin/ns-product-lib": "1.
|
|
30
|
+
"@nuskin/ns-product-lib": "1.4.2",
|
|
31
31
|
"@nuskin/product-recommendation": "2.0.1",
|
|
32
32
|
"axios": "^0.19.2",
|
|
33
33
|
"lodash": "^4.17.15",
|
|
@@ -47,6 +47,7 @@
|
|
|
47
47
|
"@nuskin/ns-feature-flags": "1.x",
|
|
48
48
|
"@nuskin/ns-product": "3.x",
|
|
49
49
|
"@nuskin/ns-shop": "5.x",
|
|
50
|
-
"@nuskin/ns-util": "3.x"
|
|
50
|
+
"@nuskin/ns-util": "3.x",
|
|
51
|
+
"@nuskin/my-site-api": "3.x"
|
|
51
52
|
}
|
|
52
53
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Meta, Story, Preview, Props } from '@storybook/addon-docs
|
|
1
|
+
import { Meta, Story, Preview, Props } from '@storybook/addon-docs';
|
|
2
2
|
|
|
3
3
|
import NsStorybookToolbox from "./NsStorybookToolbox.vue";
|
|
4
4
|
import NsProductMixinPreview from "./NsProductMixinPreview.vue";
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import NsStorybookToolbox from "./NsStorybookToolbox.vue";
|
|
2
|
+
import NsProductOffer from "../components/NsProductOffer.vue";
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
title: "Product Components/Ns Product Offer",
|
|
6
|
+
component: NsProductOffer
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
const Template = (args, { argTypes }) => ({
|
|
10
|
+
components: { NsStorybookToolbox, NsProductOffer },
|
|
11
|
+
props: Object.keys(argTypes),
|
|
12
|
+
data() {
|
|
13
|
+
return {
|
|
14
|
+
key: Date.now(),
|
|
15
|
+
args
|
|
16
|
+
};
|
|
17
|
+
},
|
|
18
|
+
methods: {
|
|
19
|
+
reloadProductList() {
|
|
20
|
+
this.key = Date.now();
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
template: `
|
|
24
|
+
<div style="width: 100vw">
|
|
25
|
+
<ns-storybook-toolbox @changes="reloadProductList" />
|
|
26
|
+
<ns-product-offer
|
|
27
|
+
:key="key"
|
|
28
|
+
v-bind="$props"
|
|
29
|
+
v-on="args"
|
|
30
|
+
/>
|
|
31
|
+
</div>
|
|
32
|
+
`
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
export const Simple = Template.bind({});
|
|
36
|
+
Simple.args = {
|
|
37
|
+
storeId: "JA10153135",
|
|
38
|
+
offerId: "-MqCvG0kuPDgU1dYV6Dv"
|
|
39
|
+
};
|
|
40
|
+
Simple.parameters = {
|
|
41
|
+
docs: {
|
|
42
|
+
source: {
|
|
43
|
+
code: `
|
|
44
|
+
<NsProductOffer
|
|
45
|
+
store-id="JA10153135",
|
|
46
|
+
offer-id="-MqCvG0kuPDgU1dYV6Dv"
|
|
47
|
+
/>
|
|
48
|
+
`
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
};
|