pb-sxp-ui 1.20.28 → 1.20.30

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.
Files changed (33) hide show
  1. package/dist/index.cjs +204 -50
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.js +204 -50
  4. package/dist/index.js.map +1 -1
  5. package/dist/index.min.cjs +9 -9
  6. package/dist/index.min.cjs.map +1 -1
  7. package/dist/index.min.js +9 -9
  8. package/dist/index.min.js.map +1 -1
  9. package/dist/pb-ui.js +204 -50
  10. package/dist/pb-ui.js.map +1 -1
  11. package/dist/pb-ui.min.js +9 -9
  12. package/dist/pb-ui.min.js.map +1 -1
  13. package/es/core/components/StructurePage/index.js +25 -12
  14. package/es/core/context/SxpDataSourceProvider.d.ts +1 -1
  15. package/es/core/utils/materials.js +30 -7
  16. package/es/core/utils/tool.d.ts +1 -0
  17. package/es/core/utils/tool.js +43 -0
  18. package/es/materials/sxp/popup/AddToCart/index.js +27 -8
  19. package/es/materials/sxp/popup/AddToCart/index.new.js +27 -6
  20. package/es/materials/sxp/popup/AddToCart/index.old.js +13 -2
  21. package/es/materials/sxp/popup/CommodityDetail/index.js +21 -16
  22. package/es/materials/sxp/popup/CommodityDetailDiroNew/index.js +25 -0
  23. package/lib/core/components/StructurePage/index.js +25 -12
  24. package/lib/core/context/SxpDataSourceProvider.d.ts +1 -1
  25. package/lib/core/utils/materials.js +29 -6
  26. package/lib/core/utils/tool.d.ts +1 -0
  27. package/lib/core/utils/tool.js +45 -1
  28. package/lib/materials/sxp/popup/AddToCart/index.js +27 -8
  29. package/lib/materials/sxp/popup/AddToCart/index.new.js +27 -6
  30. package/lib/materials/sxp/popup/AddToCart/index.old.js +12 -1
  31. package/lib/materials/sxp/popup/CommodityDetail/index.js +21 -16
  32. package/lib/materials/sxp/popup/CommodityDetailDiroNew/index.js +25 -0
  33. package/package.json +1 -1
@@ -81,7 +81,8 @@ const baseStyles = {
81
81
  },
82
82
  carouselSection: {
83
83
  width: '100%',
84
- position: 'relative'
84
+ position: 'relative',
85
+ padding: '20px 16px'
85
86
  },
86
87
  carouselImageContainer: {
87
88
  width: '100%',
@@ -112,7 +113,7 @@ const baseStyles = {
112
113
  },
113
114
  carouselInfoSection: {
114
115
  width: '100%',
115
- padding: '20px',
116
+ padding: '10px 20px',
116
117
  backgroundColor: '#000',
117
118
  color: '#fff',
118
119
  textAlign: 'center'
@@ -305,7 +306,7 @@ const baseStyles = {
305
306
  }
306
307
  };
307
308
  const StructurePage = (_a) => {
308
- var _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
309
+ var _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s;
309
310
  var { containerStyle, containerHeight = 664, containerWidth = 375, className = '', apiUrl = 'https://bff-be-dev.chatlabs.net/api/v1/recommend/list', requestBody, editorMode = false, multiCTAConfig: propMultiCTAConfig, videoPlayIcon: propVideoPlayIcon, isCmsMode = false, storyId, customHeaders } = _a, rest = __rest(_a, ["containerStyle", "containerHeight", "containerWidth", "className", "apiUrl", "requestBody", "editorMode", "multiCTAConfig", "videoPlayIcon", "isCmsMode", "storyId", "customHeaders"]);
310
311
  const [data, setData] = useState(null);
311
312
  const [loading, setLoading] = useState(true);
@@ -372,7 +373,7 @@ const StructurePage = (_a) => {
372
373
  }
373
374
  const config = multiCTAConfig[configKey];
374
375
  const styleKeys = [
375
- 'fontSize', 'color', 'textAlign', 'fontWeight',
376
+ 'fontSize', 'color', 'textAlign', 'fontWeight', 'fontStyle', 'textDecoration',
376
377
  'backgroundColor', 'padding', 'margin', 'borderRadius',
377
378
  'showBorder', 'borderWidth', 'borderColor',
378
379
  'buttonBackgroundColor', 'buttonTextColor', 'buttonWidth', 'buttonHeight'
@@ -396,8 +397,16 @@ const StructurePage = (_a) => {
396
397
  if (styleConfig.textAlign && typeof styleConfig.textAlign === 'string') {
397
398
  customStyle.textAlign = styleConfig.textAlign;
398
399
  }
399
- if (styleConfig.fontWeight && typeof styleConfig.fontWeight === 'number') {
400
- customStyle.fontWeight = styleConfig.fontWeight;
400
+ if (styleConfig.fontWeight) {
401
+ customStyle.fontWeight = typeof styleConfig.fontWeight === 'number'
402
+ ? styleConfig.fontWeight
403
+ : styleConfig.fontWeight;
404
+ }
405
+ if (styleConfig.fontStyle && typeof styleConfig.fontStyle === 'string') {
406
+ customStyle.fontStyle = styleConfig.fontStyle;
407
+ }
408
+ if (styleConfig.textDecoration && typeof styleConfig.textDecoration === 'string') {
409
+ customStyle.textDecoration = styleConfig.textDecoration;
401
410
  }
402
411
  if (styleConfig.backgroundColor && typeof styleConfig.backgroundColor === 'string') {
403
412
  customStyle.backgroundColor = styleConfig.backgroundColor;
@@ -639,8 +648,8 @@ const StructurePage = (_a) => {
639
648
  React.createElement("video", { ref: heroVideoRef, src: data.heroSection.url, style: baseStyles.heroVideo, autoPlay: true, muted: true, loop: true, playsInline: true, controls: false }),
640
649
  isHeroVideoPaused && (React.createElement(FormatImage, { className: 'clc-pb-video-pause', src: videoPlayIcon, alt: 'play' })))) : ((_e = data.heroSection.imgUrls) === null || _e === void 0 ? void 0 : _e[0]) ? (React.createElement("img", { src: data.heroSection.imgUrls[0], alt: 'Hero', style: baseStyles.heroImage })) : null,
641
650
  React.createElement("div", { style: baseStyles.heroOverlay }, renderCTA('heroButton', (_g = (_f = data.heroSection.bindProducts) === null || _f === void 0 ? void 0 : _f[0]) === null || _g === void 0 ? void 0 : _g.bindCta, (_h = data.heroSection.bindProducts) === null || _h === void 0 ? void 0 : _h[0], baseStyles.heroButton))))),
642
- data.carouselSection && data.carouselSection.length > 0 && (React.createElement("div", { style: mergeStyles(baseStyles.carouselSection, 'carouselSection') },
643
- React.createElement("div", { style: baseStyles.carouselImageContainer },
651
+ data.carouselSection && data.carouselSection.length > 0 && (React.createElement("div", { style: Object.assign(Object.assign({}, mergeStyles(baseStyles.carouselSection, 'carouselSection')), (((_j = multiCTAConfig === null || multiCTAConfig === void 0 ? void 0 : multiCTAConfig.carouselContainerStyle) === null || _j === void 0 ? void 0 : _j.padding) && { padding: multiCTAConfig.carouselContainerStyle.padding })) },
652
+ React.createElement("div", { style: Object.assign(Object.assign({}, baseStyles.carouselImageContainer), (((_k = multiCTAConfig === null || multiCTAConfig === void 0 ? void 0 : multiCTAConfig.carouselContainerStyle) === null || _k === void 0 ? void 0 : _k.aspectRatio) && { aspectRatio: multiCTAConfig.carouselContainerStyle.aspectRatio })) },
644
653
  React.createElement("div", { style: Object.assign(Object.assign({}, baseStyles.carouselContainer), { transform: `translateX(-${carouselIndex * 100}%)` }) }, data.carouselSection.map((item, index) => {
645
654
  var _a;
646
655
  return (React.createElement("div", { key: item.itemId, style: baseStyles.carouselSlide }, item.url ? (React.createElement("div", { style: { position: 'relative', width: '100%', height: '100%' }, onClick: () => handleCarouselVideoClick(index) },
@@ -652,13 +661,14 @@ const StructurePage = (_a) => {
652
661
  React.createElement("button", { style: Object.assign(Object.assign({}, baseStyles.arrowButton), { left: '10px' }), onClick: handleCarouselPrev }, "\u2039"),
653
662
  React.createElement("button", { style: Object.assign(Object.assign({}, baseStyles.arrowButton), { right: '10px' }), onClick: handleCarouselNext }, "\u203A")),
654
663
  React.createElement("div", { style: mergeStyles(baseStyles.carouselInfoSection, 'carouselSection') },
655
- ((_j = data.carouselSection[carouselIndex]) === null || _j === void 0 ? void 0 : _j.text) && (React.createElement("div", { style: mergeStyles(baseStyles.carouselText, 'carouselSection') }, (_k = data.carouselSection[carouselIndex]) === null || _k === void 0 ? void 0 : _k.text)),
656
- renderCTA('carouselButton', (_o = (_m = (_l = data.carouselSection[carouselIndex]) === null || _l === void 0 ? void 0 : _l.bindProducts) === null || _m === void 0 ? void 0 : _m[0]) === null || _o === void 0 ? void 0 : _o.bindCta, (_q = (_p = data.carouselSection[carouselIndex]) === null || _p === void 0 ? void 0 : _p.bindProducts) === null || _q === void 0 ? void 0 : _q[0], baseStyles.carouselButton)))),
664
+ ((_l = data.carouselSection[carouselIndex]) === null || _l === void 0 ? void 0 : _l.text) && (React.createElement("div", { style: mergeStyles(baseStyles.carouselText, 'carouselSection') }, (_m = data.carouselSection[carouselIndex]) === null || _m === void 0 ? void 0 : _m.text)),
665
+ renderCTA('carouselButton', (_q = (_p = (_o = data.carouselSection[carouselIndex]) === null || _o === void 0 ? void 0 : _o.bindProducts) === null || _p === void 0 ? void 0 : _p[0]) === null || _q === void 0 ? void 0 : _q.bindCta, (_s = (_r = data.carouselSection[carouselIndex]) === null || _r === void 0 ? void 0 : _r.bindProducts) === null || _s === void 0 ? void 0 : _s[0], baseStyles.carouselButton)))),
657
666
  data.highlightRevealSection && (React.createElement("div", { style: mergeStyles(baseStyles.highlightSection, 'highlightSection') },
658
667
  React.createElement("div", { style: baseStyles.highlightImageContainer },
659
668
  React.createElement("img", { src: data.highlightRevealSection.landingImageUrl || data.highlightRevealSection.cover, alt: data.highlightRevealSection.title, style: baseStyles.highlightImage })),
660
669
  React.createElement("div", { style: mergeStyles(baseStyles.highlightInfoSection, 'highlightSection') },
661
- React.createElement("div", { style: mergeStyles(baseStyles.highlightTitle, 'highlightSection') }, data.highlightRevealSection.title),
670
+ data.highlightRevealSection.text && (React.createElement("div", { style: { marginBottom: '10px' } },
671
+ React.createElement("div", { style: mergeStyles(baseStyles.highlightTitle, 'highlightSection') }, data.highlightRevealSection.text))),
662
672
  renderCTA('highlightButton', data.highlightRevealSection.bindCta, data.highlightRevealSection, baseStyles.highlightButton)))),
663
673
  data.productGridSection && data.productGridSection.length > 0 && (React.createElement("div", { style: mergeStyles(baseStyles.productGrid, 'productGrid') }, (() => {
664
674
  const gridItems = [null, null, null, null, null, null];
@@ -700,7 +710,10 @@ const StructurePage = (_a) => {
700
710
  });
701
711
  })())),
702
712
  data.footerSection && (React.createElement("div", { style: mergeStyles(baseStyles.footerSection, 'footerSection') },
703
- React.createElement("div", { style: mergeStyles(baseStyles.footerInfoSection, 'footerSection') }, renderCTA('footerButton', data.footerSection.bindCta, data.footerSection, baseStyles.footerButton)),
713
+ React.createElement("div", { style: mergeStyles(baseStyles.footerInfoSection, 'footerSection') },
714
+ data.footerSection.text && (React.createElement("div", { style: { marginBottom: '15px' } },
715
+ React.createElement("div", { style: mergeStyles(baseStyles.footerText, 'footerSection') }, data.footerSection.text))),
716
+ renderCTA('footerButton', data.footerSection.bindCta, data.footerSection, baseStyles.footerButton)),
704
717
  React.createElement("div", { style: baseStyles.footerImageContainer },
705
718
  React.createElement("img", { src: data.footerSection.landingImageUrl || data.footerSection.cover, alt: data.footerSection.title, style: baseStyles.footerImage }))))));
706
719
  };
@@ -12,7 +12,7 @@ export interface IEventTimeType {
12
12
  time: Date;
13
13
  target: EventTarget;
14
14
  }
15
- export type ICapiEventNameType = 'PageView' | 'ProductView' | 'ViewContent' | 'ClickCTA' | 'ContentSwipe' | 'Engagement' | 'ExitFeed' | 'AddToCart';
15
+ export type ICapiEventNameType = 'PageView' | 'ProductView' | 'ViewContent' | 'ClickCTA' | 'ContentSwipe' | 'Engagement' | 'ExitFeed' | 'AddToCart' | 'sessionCompleted';
16
16
  export interface ISxpDataSourceContext {
17
17
  rtcList: RecItemType[];
18
18
  setRtcList?: React.Dispatch<React.SetStateAction<RecItemType[]>>;
@@ -1,4 +1,4 @@
1
- import { setFontForText } from './tool';
1
+ import { setFontForText, getCurrencySymbol } from './tool';
2
2
  export const getMediaValueByMode = (obj) => {
3
3
  var _a;
4
4
  if (!obj || typeof obj !== 'object') {
@@ -31,7 +31,7 @@ export const getBgStyleByImg = (data) => {
31
31
  return getBgStyle(imgSrc);
32
32
  };
33
33
  export const getPriceText = ({ product, enableFormattedPrice, globalConfig, isHiddenDef, style }) => {
34
- var _a, _b, _c, _d, _e, _f, _g, _h;
34
+ var _a, _b, _c, _d, _e;
35
35
  let text = '';
36
36
  if ((!(product === null || product === void 0 ? void 0 : product.currency) || !(product === null || product === void 0 ? void 0 : product.price)) && isHiddenDef)
37
37
  return null;
@@ -39,18 +39,41 @@ export const getPriceText = ({ product, enableFormattedPrice, globalConfig, isHi
39
39
  if (typeof price !== 'number')
40
40
  return text;
41
41
  let priceSymbol = globalConfig === null || globalConfig === void 0 ? void 0 : globalConfig.priceSymbol;
42
- let currency = (product === null || product === void 0 ? void 0 : product.currency) ? (_c = (_b = (_a = product === null || product === void 0 ? void 0 : product.currency) === null || _a === void 0 ? void 0 : _a.split('-')[1]) === null || _b === void 0 ? void 0 : _b.toUpperCase()) !== null && _c !== void 0 ? _c : '' : '$';
42
+ let currencyCode = '';
43
+ if (product === null || product === void 0 ? void 0 : product.currency) {
44
+ const parts = product.currency.split('-');
45
+ if (parts.length > 1) {
46
+ const firstPart = parts[0].toUpperCase();
47
+ const secondPart = parts[1].toUpperCase();
48
+ if (/^[A-Z]{3}$/.test(firstPart)) {
49
+ currencyCode = firstPart;
50
+ }
51
+ else if (/^[A-Z]{3}$/.test(secondPart)) {
52
+ currencyCode = secondPart;
53
+ }
54
+ else {
55
+ currencyCode = firstPart;
56
+ }
57
+ }
58
+ else {
59
+ currencyCode = parts[0].toUpperCase();
60
+ }
61
+ }
62
+ else {
63
+ currencyCode = 'USD';
64
+ }
65
+ let currency = getCurrencySymbol(currencyCode);
43
66
  const isToLocStr = enableFormattedPrice === undefined || enableFormattedPrice;
44
67
  let decPic = price === null || price === void 0 ? void 0 : price.toString();
45
68
  if (priceSymbol === null || priceSymbol === void 0 ? void 0 : priceSymbol.showTwoDecimalPoint) {
46
69
  decPic = price === null || price === void 0 ? void 0 : price.toFixed(2);
47
70
  }
48
- let decInd = (_d = decPic === null || decPic === void 0 ? void 0 : decPic.indexOf('.')) !== null && _d !== void 0 ? _d : -1;
71
+ let decInd = (_a = decPic === null || decPic === void 0 ? void 0 : decPic.indexOf('.')) !== null && _a !== void 0 ? _a : -1;
49
72
  if (isToLocStr) {
50
73
  text =
51
- (_e = price === null || price === void 0 ? void 0 : price.toLocaleString('zh', {
74
+ (_b = price === null || price === void 0 ? void 0 : price.toLocaleString('zh', {
52
75
  minimumFractionDigits: (priceSymbol === null || priceSymbol === void 0 ? void 0 : priceSymbol.showTwoDecimalPoint) ? 2 : 0
53
- })) !== null && _e !== void 0 ? _e : '';
76
+ })) !== null && _b !== void 0 ? _b : '';
54
77
  let startIndex = 0;
55
78
  let endIndex = decInd !== null && decInd !== void 0 ? decInd : text === null || text === void 0 ? void 0 : text.length;
56
79
  if ((priceSymbol === null || priceSymbol === void 0 ? void 0 : priceSymbol.millesimalSymbol) === '.') {
@@ -80,7 +103,7 @@ export const getPriceText = ({ product, enableFormattedPrice, globalConfig, isHi
80
103
  }
81
104
  });
82
105
  }
83
- currency = `<span style="font-family:${(_h = (_g = (_f = globalConfig === null || globalConfig === void 0 ? void 0 : globalConfig.priceSymbol) === null || _f === void 0 ? void 0 : _f.fontFamily) !== null && _g !== void 0 ? _g : style === null || style === void 0 ? void 0 : style['fontFamily-en']) !== null && _h !== void 0 ? _h : 'inherit'}">${currency}</span>`;
106
+ currency = `<span style="font-family:${(_e = (_d = (_c = globalConfig === null || globalConfig === void 0 ? void 0 : globalConfig.priceSymbol) === null || _c === void 0 ? void 0 : _c.fontFamily) !== null && _d !== void 0 ? _d : style === null || style === void 0 ? void 0 : style['fontFamily-en']) !== null && _e !== void 0 ? _e : 'inherit'}">${currency}</span>`;
84
107
  text = setFontForText(text, style);
85
108
  if ((priceSymbol === null || priceSymbol === void 0 ? void 0 : priceSymbol.currencyPosition) && (priceSymbol === null || priceSymbol === void 0 ? void 0 : priceSymbol.currencyPosition) !== 'none') {
86
109
  text = (priceSymbol === null || priceSymbol === void 0 ? void 0 : priceSymbol.currencyPosition) === 'left' ? currency + text : text + currency;
@@ -16,4 +16,5 @@ declare function splitUrlParams(urlParams: string): string[] | undefined;
16
16
  declare function deleteCookie(name: string, path?: string, domain?: string): void;
17
17
  declare function setCookie(name: string, value: string, days?: number, path?: string, domain?: string, secure?: boolean, sameSite?: string): void;
18
18
  declare function getUrlParamByKey(key: string): any;
19
+ export declare function getCurrencySymbol(currencyCode: string): string;
19
20
  export { uuid, getIndexByblockType, getBrowserInfo, getDevice, getSystem, getCookie, getScreenReader, splitUrlParams, deleteCookie, setCookie, getUrlParamByKey };
@@ -233,4 +233,47 @@ function getUrlParamByKey(key) {
233
233
  }
234
234
  return (_b = params[key]) !== null && _b !== void 0 ? _b : '';
235
235
  }
236
+ export function getCurrencySymbol(currencyCode) {
237
+ const currencySymbolMap = {
238
+ USD: '$',
239
+ EUR: '€',
240
+ GBP: '£',
241
+ JPY: '¥',
242
+ CNY: '¥',
243
+ KRW: '₩',
244
+ INR: '₹',
245
+ RUB: '₽',
246
+ BRL: 'R$',
247
+ CAD: 'CA$',
248
+ AUD: 'A$',
249
+ CHF: 'CHF',
250
+ SEK: 'kr',
251
+ NOK: 'kr',
252
+ DKK: 'kr',
253
+ PLN: 'zł',
254
+ THB: '฿',
255
+ IDR: 'Rp',
256
+ MYR: 'RM',
257
+ PHP: '₱',
258
+ SGD: 'S$',
259
+ HKD: 'HK$',
260
+ TWD: 'NT$',
261
+ NZD: 'NZ$',
262
+ MXN: 'MX$',
263
+ ZAR: 'R',
264
+ TRY: '₺',
265
+ AED: 'AED',
266
+ SAR: 'SAR',
267
+ ILS: '₪',
268
+ ARS: 'AR$',
269
+ CLP: 'CL$',
270
+ COP: 'CO$',
271
+ VND: '₫',
272
+ EGP: 'E£',
273
+ NGN: '₦',
274
+ PKR: '₨',
275
+ BDT: '৳',
276
+ };
277
+ return currencySymbolMap[currencyCode.toUpperCase()] || currencyCode;
278
+ }
236
279
  export { uuid, getIndexByblockType, getBrowserInfo, getDevice, getSystem, getCookie, getScreenReader, splitUrlParams, deleteCookie, setCookie, getUrlParamByKey };
@@ -2,10 +2,11 @@ import { __awaiter } from "tslib";
2
2
  import React, { useState, useEffect, useCallback } from 'react';
3
3
  import { useSxpDataSource } from '../../../../core/hooks';
4
4
  import Modal from '../../../../core/components/SxpPageRender/Modal';
5
+ import { getCurrencySymbol } from '../../../../core/utils/tool';
5
6
  import './index.less';
6
7
  const AddToCartPopup = ({ isActive = true }) => {
7
8
  var _a, _b, _c, _d, _e, _f, _g, _h, _j;
8
- const { popupDetailData, globalConfig } = useSxpDataSource();
9
+ const { popupDetailData, globalConfig, bffFbReport } = useSxpDataSource();
9
10
  const [productData, setProductData] = useState(null);
10
11
  const [selectedOptions, setSelectedOptions] = useState({});
11
12
  const [selectedVariant, setSelectedVariant] = useState(null);
@@ -137,6 +138,17 @@ const AddToCartPopup = ({ isActive = true }) => {
137
138
  shopifyDomain,
138
139
  selectedVariant
139
140
  });
141
+ bffFbReport === null || bffFbReport === void 0 ? void 0 : bffFbReport({
142
+ eventName: 'sessionCompleted',
143
+ product: product ? [product] : undefined,
144
+ contentType: 'product',
145
+ rec: data,
146
+ position: data === null || data === void 0 ? void 0 : data.index,
147
+ cta_text: 'Add To Cart',
148
+ cta_action_type: 'add_to_cart',
149
+ target_content_id: product === null || product === void 0 ? void 0 : product.itemId,
150
+ target_url: `https://${shopifyDomain}/cart/add`
151
+ });
140
152
  const params = new URLSearchParams({
141
153
  id: variantId,
142
154
  quantity: quantity.toString()
@@ -148,6 +160,9 @@ const AddToCartPopup = ({ isActive = true }) => {
148
160
  const totalPrice = selectedVariant
149
161
  ? (parseFloat(selectedVariant.price.amount) * quantity).toFixed(2)
150
162
  : '0.00';
163
+ const displayPrice = selectedVariant
164
+ ? `${getCurrencySymbol(selectedVariant.price.currencyCode)}${totalPrice}`
165
+ : totalPrice;
151
166
  if (loading) {
152
167
  return (React.createElement("div", { className: 'add-to-cart-popup' },
153
168
  React.createElement("div", { className: 'loading' }, "Loading...")));
@@ -181,9 +196,7 @@ const AddToCartPopup = ({ isActive = true }) => {
181
196
  "Available: ",
182
197
  selectedVariant.quantityAvailable))),
183
198
  React.createElement("div", { className: 'variant-price-row' },
184
- React.createElement("div", { className: 'price' },
185
- "$",
186
- totalPrice),
199
+ React.createElement("div", { className: 'price' }, displayPrice),
187
200
  React.createElement("div", { className: 'quantity-selector' },
188
201
  React.createElement("button", { className: 'qty-btn', onClick: () => setQuantity(Math.max(1, quantity - 1)), disabled: quantity <= 1 }, "\u2212"),
189
202
  React.createElement("span", { className: 'qty-value' }, quantity),
@@ -193,11 +206,17 @@ const AddToCartPopup = ({ isActive = true }) => {
193
206
  React.createElement("div", { className: 'option-values' }, option.values.map(value => {
194
207
  const isAvailable = productData.variants.edges.some(({ node: variant }) => {
195
208
  const hasThisOption = variant.selectedOptions.some(opt => opt.name === option.name && opt.value === value);
196
- if (!hasThisOption || !variant.availableForSale)
209
+ if (!hasThisOption)
210
+ return false;
211
+ const matchesOtherSelections = Object.entries(selectedOptions).every(([key, val]) => {
212
+ if (key === option.name)
213
+ return true;
214
+ return variant.selectedOptions.some(opt => opt.name === key && opt.value === val);
215
+ });
216
+ if (!matchesOtherSelections)
217
+ return false;
218
+ if (!variant.availableForSale)
197
219
  return false;
198
- if (variant.quantityAvailable !== undefined) {
199
- return variant.quantityAvailable > 0;
200
- }
201
220
  return true;
202
221
  });
203
222
  const isSelected = selectedOptions[option.name] === value;
@@ -1,10 +1,11 @@
1
1
  import { __awaiter } from "tslib";
2
2
  import React, { useState, useEffect, useCallback } from 'react';
3
3
  import { useSxpDataSource } from '../../../../core/hooks';
4
+ import { getCurrencySymbol } from '../../../../core/utils/tool';
4
5
  import './index.less';
5
6
  const AddToCartPopup = ({ isActive = true, onClose }) => {
6
7
  var _a, _b, _c, _d, _e, _f, _g;
7
- const { popupDetailData, globalConfig } = useSxpDataSource();
8
+ const { popupDetailData, globalConfig, bffFbReport } = useSxpDataSource();
8
9
  const [productData, setProductData] = useState(null);
9
10
  const [selectedOptions, setSelectedOptions] = useState({});
10
11
  const [selectedVariant, setSelectedVariant] = useState(null);
@@ -120,6 +121,17 @@ const AddToCartPopup = ({ isActive = true, onClose }) => {
120
121
  alert('Please select all options');
121
122
  return;
122
123
  }
124
+ bffFbReport === null || bffFbReport === void 0 ? void 0 : bffFbReport({
125
+ eventName: 'sessionCompleted',
126
+ product: product ? [product] : undefined,
127
+ contentType: 'product',
128
+ rec: data,
129
+ position: data === null || data === void 0 ? void 0 : data.index,
130
+ cta_text: 'Add To Cart',
131
+ cta_action_type: 'add_to_cart',
132
+ target_content_id: product === null || product === void 0 ? void 0 : product.itemId,
133
+ target_url: `https://${shopifyDomain}/cart`
134
+ });
123
135
  const variantId = selectedVariant.id.split('/').pop();
124
136
  const checkoutUrl = `https://${shopifyDomain}/cart/${variantId}:${quantity}`;
125
137
  window.location.href = checkoutUrl;
@@ -127,7 +139,9 @@ const AddToCartPopup = ({ isActive = true, onClose }) => {
127
139
  const totalPrice = selectedVariant
128
140
  ? (parseFloat(selectedVariant.price.amount) * quantity).toFixed(2)
129
141
  : '0.00';
130
- const currency = (selectedVariant === null || selectedVariant === void 0 ? void 0 : selectedVariant.price.currencyCode) || 'USD';
142
+ const displayPrice = selectedVariant
143
+ ? `${getCurrencySymbol(selectedVariant.price.currencyCode)}${totalPrice}`
144
+ : totalPrice;
131
145
  if (loading) {
132
146
  return (React.createElement("div", { className: "add-to-cart-popup" },
133
147
  React.createElement("div", { className: "loading" }, "Loading...")));
@@ -152,9 +166,7 @@ const AddToCartPopup = ({ isActive = true, onClose }) => {
152
166
  React.createElement("div", { className: "variant-info" },
153
167
  React.createElement("div", { className: "variant-specs" }, Object.keys(selectedOptions).length > 0 ? (Object.entries(selectedOptions).map(([key, value]) => (React.createElement("span", { key: key, className: "spec-item" }, value)))) : (React.createElement("span", { className: "spec-placeholder" }, "Please select options"))),
154
168
  React.createElement("div", { className: "variant-price-row" },
155
- React.createElement("div", { className: "price" },
156
- "$",
157
- totalPrice),
169
+ React.createElement("div", { className: "price" }, displayPrice),
158
170
  React.createElement("div", { className: "quantity-selector" },
159
171
  React.createElement("button", { className: "qty-btn", onClick: () => setQuantity(Math.max(1, quantity - 1)), disabled: quantity <= 1 }, "\u2212"),
160
172
  React.createElement("span", { className: "qty-value" }, quantity),
@@ -164,7 +176,16 @@ const AddToCartPopup = ({ isActive = true, onClose }) => {
164
176
  React.createElement("div", { className: "option-values" }, option.values.map(value => {
165
177
  const isAvailable = productData.variants.edges.some(({ node: variant }) => {
166
178
  const hasThisOption = variant.selectedOptions.some(opt => opt.name === option.name && opt.value === value);
167
- return hasThisOption && variant.availableForSale;
179
+ if (!hasThisOption)
180
+ return false;
181
+ const matchesOtherSelections = Object.entries(selectedOptions).every(([key, val]) => {
182
+ if (key === option.name)
183
+ return true;
184
+ return variant.selectedOptions.some(opt => opt.name === key && opt.value === val);
185
+ });
186
+ if (!matchesOtherSelections)
187
+ return false;
188
+ return variant.availableForSale;
168
189
  });
169
190
  const isSelected = selectedOptions[option.name] === value;
170
191
  return (React.createElement("button", { key: value, className: `option-btn ${isSelected ? 'selected' : ''} ${!isAvailable ? 'disabled' : ''}`, onClick: () => isAvailable && handleOptionSelect(option.name, value), disabled: !isAvailable }, value));
@@ -2,7 +2,7 @@ import { __awaiter, __rest } from "tslib";
2
2
  import React, { useState, useEffect, useCallback, useMemo, useRef } from 'react';
3
3
  import { useSxpDataSource } from '../../../../core/hooks';
4
4
  import { useEventReport } from '../../../../core/hooks/useEventReport';
5
- import { setFontForText } from '../../../../core/utils/tool';
5
+ import { setFontForText, getCurrencySymbol } from '../../../../core/utils/tool';
6
6
  import './index.less';
7
7
  const AddToCartPopup = (_a) => {
8
8
  var _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
@@ -227,6 +227,17 @@ const AddToCartPopup = (_a) => {
227
227
  quantity
228
228
  }]
229
229
  });
230
+ bffFbReport === null || bffFbReport === void 0 ? void 0 : bffFbReport({
231
+ eventName: 'sessionCompleted',
232
+ product: product ? [product] : undefined,
233
+ contentType: 'product',
234
+ rec: data,
235
+ position,
236
+ cta_text: 'Add To Cart',
237
+ cta_action_type: 'add_to_cart',
238
+ target_content_id: product === null || product === void 0 ? void 0 : product.itemId,
239
+ target_url: cartUrl
240
+ });
230
241
  window.location.href = cartUrl;
231
242
  }, [selectedVariant, quantity, finalShopifyDomain, bffFbReport, product, data, position]);
232
243
  const totalPrice = useMemo(() => {
@@ -275,7 +286,7 @@ const AddToCartPopup = (_a) => {
275
286
  option.value)))),
276
287
  React.createElement("div", { className: "price-display" },
277
288
  React.createElement("span", { className: "price-value", style: variantStyles.price, dangerouslySetInnerHTML: {
278
- __html: setFontForText(`${selectedVariant.price.currencyCode} $${totalPrice}`, variantStyles.price)
289
+ __html: setFontForText(selectedVariant ? `${getCurrencySymbol(selectedVariant.price.currencyCode)}${totalPrice}` : totalPrice, variantStyles.price)
279
290
  } })),
280
291
  React.createElement("div", { className: "quantity-selector-wrapper", style: quantityStyle },
281
292
  React.createElement("button", { className: "quantity-btn quantity-decrease", onClick: () => handleQuantityChange(-1), disabled: quantity <= 1, "aria-label": "Decrease quantity" }, "-"),
@@ -39,25 +39,30 @@ const CommodityDetail = (_a) => {
39
39
  }
40
40
  const handleLink = (e) => {
41
41
  if (enableAddToCart && addToCartPopupId) {
42
- setPopupDetailData === null || setPopupDetailData === void 0 ? void 0 : setPopupDetailData(Object.assign(Object.assign({}, data), { index: position }));
43
- bffFbReport === null || bffFbReport === void 0 ? void 0 : bffFbReport({
44
- eventName: 'ClickCTA',
45
- product: product ? [product] : undefined,
46
- contentType: 'product',
47
- data,
48
- position,
49
- cta_text: cta === null || cta === void 0 ? void 0 : cta.enTitle,
50
- cta_action_type: 'open_internal_popup',
51
- target_content_id: product === null || product === void 0 ? void 0 : product.itemId
52
- });
53
- console.log('[CommodityDetail] 打开加购弹窗:', addToCartPopupId);
54
- if (typeof window !== 'undefined' && window.sxpPopup) {
55
- window.sxpPopup(addToCartPopupId);
42
+ if (!(product === null || product === void 0 ? void 0 : product.shopifyId)) {
43
+ console.warn('[CommodityDetail] Shopify 商品,跳过加购弹窗');
56
44
  }
57
45
  else {
58
- console.warn('[CommodityDetail] sxpPopup 方法不存在');
46
+ setPopupDetailData === null || setPopupDetailData === void 0 ? void 0 : setPopupDetailData(Object.assign(Object.assign({}, data), { index: position }));
47
+ bffFbReport === null || bffFbReport === void 0 ? void 0 : bffFbReport({
48
+ eventName: 'ClickCTA',
49
+ product: product ? [product] : undefined,
50
+ contentType: 'product',
51
+ data,
52
+ position,
53
+ cta_text: cta === null || cta === void 0 ? void 0 : cta.enTitle,
54
+ cta_action_type: 'open_internal_popup',
55
+ target_content_id: product === null || product === void 0 ? void 0 : product.itemId
56
+ });
57
+ console.log('[CommodityDetail] 打开加购弹窗:', addToCartPopupId);
58
+ if (typeof window !== 'undefined' && window.sxpPopup) {
59
+ window.sxpPopup(addToCartPopupId);
60
+ }
61
+ else {
62
+ console.warn('[CommodityDetail] sxpPopup 方法不存在');
63
+ }
64
+ return;
59
65
  }
60
- return;
61
66
  }
62
67
  if (product === null || product === void 0 ? void 0 : product.link) {
63
68
  jumpToWeb(e, data, product, cta, position);
@@ -41,6 +41,31 @@ const CommodityDetailDiroNew = (_a) => {
41
41
  }
42
42
  const handleLink = (e) => {
43
43
  e.preventDefault();
44
+ if (!(product === null || product === void 0 ? void 0 : product.shopifyId)) {
45
+ console.warn('[CommodityDetailDiroNew] 非 Shopify 商品,跳转到外链');
46
+ ctaEvent === null || ctaEvent === void 0 ? void 0 : ctaEvent({
47
+ eventSubject: 'clickCta',
48
+ eventDescription: 'User clicked the CTA'
49
+ }, data, product, position);
50
+ bffFbReport === null || bffFbReport === void 0 ? void 0 : bffFbReport({
51
+ eventName: 'ClickCTA',
52
+ product: product ? [product] : undefined,
53
+ contentType: 'product',
54
+ data,
55
+ position,
56
+ cta_text: cta === null || cta === void 0 ? void 0 : cta.enTitle,
57
+ cta_action_type: 'open_external_link',
58
+ target_content_id: product === null || product === void 0 ? void 0 : product.itemId,
59
+ target_url: product === null || product === void 0 ? void 0 : product.link
60
+ });
61
+ if (!isPost) {
62
+ productView(data, product, cta, viewTime || curTimeRef.current, position);
63
+ }
64
+ if (product === null || product === void 0 ? void 0 : product.link) {
65
+ window.location.href = window.getJointUtmLink(product.link);
66
+ }
67
+ return;
68
+ }
44
69
  ctaEvent === null || ctaEvent === void 0 ? void 0 : ctaEvent({
45
70
  eventSubject: 'clickCta',
46
71
  eventDescription: 'User clicked the CTA'
@@ -83,7 +83,8 @@ const baseStyles = {
83
83
  },
84
84
  carouselSection: {
85
85
  width: '100%',
86
- position: 'relative'
86
+ position: 'relative',
87
+ padding: '20px 16px'
87
88
  },
88
89
  carouselImageContainer: {
89
90
  width: '100%',
@@ -114,7 +115,7 @@ const baseStyles = {
114
115
  },
115
116
  carouselInfoSection: {
116
117
  width: '100%',
117
- padding: '20px',
118
+ padding: '10px 20px',
118
119
  backgroundColor: '#000',
119
120
  color: '#fff',
120
121
  textAlign: 'center'
@@ -307,7 +308,7 @@ const baseStyles = {
307
308
  }
308
309
  };
309
310
  const StructurePage = (_a) => {
310
- var _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
311
+ var _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s;
311
312
  var { containerStyle, containerHeight = 664, containerWidth = 375, className = '', apiUrl = 'https://bff-be-dev.chatlabs.net/api/v1/recommend/list', requestBody, editorMode = false, multiCTAConfig: propMultiCTAConfig, videoPlayIcon: propVideoPlayIcon, isCmsMode = false, storyId, customHeaders } = _a, rest = tslib_1.__rest(_a, ["containerStyle", "containerHeight", "containerWidth", "className", "apiUrl", "requestBody", "editorMode", "multiCTAConfig", "videoPlayIcon", "isCmsMode", "storyId", "customHeaders"]);
312
313
  const [data, setData] = (0, react_1.useState)(null);
313
314
  const [loading, setLoading] = (0, react_1.useState)(true);
@@ -374,7 +375,7 @@ const StructurePage = (_a) => {
374
375
  }
375
376
  const config = multiCTAConfig[configKey];
376
377
  const styleKeys = [
377
- 'fontSize', 'color', 'textAlign', 'fontWeight',
378
+ 'fontSize', 'color', 'textAlign', 'fontWeight', 'fontStyle', 'textDecoration',
378
379
  'backgroundColor', 'padding', 'margin', 'borderRadius',
379
380
  'showBorder', 'borderWidth', 'borderColor',
380
381
  'buttonBackgroundColor', 'buttonTextColor', 'buttonWidth', 'buttonHeight'
@@ -398,8 +399,16 @@ const StructurePage = (_a) => {
398
399
  if (styleConfig.textAlign && typeof styleConfig.textAlign === 'string') {
399
400
  customStyle.textAlign = styleConfig.textAlign;
400
401
  }
401
- if (styleConfig.fontWeight && typeof styleConfig.fontWeight === 'number') {
402
- customStyle.fontWeight = styleConfig.fontWeight;
402
+ if (styleConfig.fontWeight) {
403
+ customStyle.fontWeight = typeof styleConfig.fontWeight === 'number'
404
+ ? styleConfig.fontWeight
405
+ : styleConfig.fontWeight;
406
+ }
407
+ if (styleConfig.fontStyle && typeof styleConfig.fontStyle === 'string') {
408
+ customStyle.fontStyle = styleConfig.fontStyle;
409
+ }
410
+ if (styleConfig.textDecoration && typeof styleConfig.textDecoration === 'string') {
411
+ customStyle.textDecoration = styleConfig.textDecoration;
403
412
  }
404
413
  if (styleConfig.backgroundColor && typeof styleConfig.backgroundColor === 'string') {
405
414
  customStyle.backgroundColor = styleConfig.backgroundColor;
@@ -641,8 +650,8 @@ const StructurePage = (_a) => {
641
650
  react_1.default.createElement("video", { ref: heroVideoRef, src: data.heroSection.url, style: baseStyles.heroVideo, autoPlay: true, muted: true, loop: true, playsInline: true, controls: false }),
642
651
  isHeroVideoPaused && (react_1.default.createElement(FormatImage_1.default, { className: 'clc-pb-video-pause', src: videoPlayIcon, alt: 'play' })))) : ((_e = data.heroSection.imgUrls) === null || _e === void 0 ? void 0 : _e[0]) ? (react_1.default.createElement("img", { src: data.heroSection.imgUrls[0], alt: 'Hero', style: baseStyles.heroImage })) : null,
643
652
  react_1.default.createElement("div", { style: baseStyles.heroOverlay }, renderCTA('heroButton', (_g = (_f = data.heroSection.bindProducts) === null || _f === void 0 ? void 0 : _f[0]) === null || _g === void 0 ? void 0 : _g.bindCta, (_h = data.heroSection.bindProducts) === null || _h === void 0 ? void 0 : _h[0], baseStyles.heroButton))))),
644
- data.carouselSection && data.carouselSection.length > 0 && (react_1.default.createElement("div", { style: mergeStyles(baseStyles.carouselSection, 'carouselSection') },
645
- react_1.default.createElement("div", { style: baseStyles.carouselImageContainer },
653
+ data.carouselSection && data.carouselSection.length > 0 && (react_1.default.createElement("div", { style: Object.assign(Object.assign({}, mergeStyles(baseStyles.carouselSection, 'carouselSection')), (((_j = multiCTAConfig === null || multiCTAConfig === void 0 ? void 0 : multiCTAConfig.carouselContainerStyle) === null || _j === void 0 ? void 0 : _j.padding) && { padding: multiCTAConfig.carouselContainerStyle.padding })) },
654
+ react_1.default.createElement("div", { style: Object.assign(Object.assign({}, baseStyles.carouselImageContainer), (((_k = multiCTAConfig === null || multiCTAConfig === void 0 ? void 0 : multiCTAConfig.carouselContainerStyle) === null || _k === void 0 ? void 0 : _k.aspectRatio) && { aspectRatio: multiCTAConfig.carouselContainerStyle.aspectRatio })) },
646
655
  react_1.default.createElement("div", { style: Object.assign(Object.assign({}, baseStyles.carouselContainer), { transform: `translateX(-${carouselIndex * 100}%)` }) }, data.carouselSection.map((item, index) => {
647
656
  var _a;
648
657
  return (react_1.default.createElement("div", { key: item.itemId, style: baseStyles.carouselSlide }, item.url ? (react_1.default.createElement("div", { style: { position: 'relative', width: '100%', height: '100%' }, onClick: () => handleCarouselVideoClick(index) },
@@ -654,13 +663,14 @@ const StructurePage = (_a) => {
654
663
  react_1.default.createElement("button", { style: Object.assign(Object.assign({}, baseStyles.arrowButton), { left: '10px' }), onClick: handleCarouselPrev }, "\u2039"),
655
664
  react_1.default.createElement("button", { style: Object.assign(Object.assign({}, baseStyles.arrowButton), { right: '10px' }), onClick: handleCarouselNext }, "\u203A")),
656
665
  react_1.default.createElement("div", { style: mergeStyles(baseStyles.carouselInfoSection, 'carouselSection') },
657
- ((_j = data.carouselSection[carouselIndex]) === null || _j === void 0 ? void 0 : _j.text) && (react_1.default.createElement("div", { style: mergeStyles(baseStyles.carouselText, 'carouselSection') }, (_k = data.carouselSection[carouselIndex]) === null || _k === void 0 ? void 0 : _k.text)),
658
- renderCTA('carouselButton', (_o = (_m = (_l = data.carouselSection[carouselIndex]) === null || _l === void 0 ? void 0 : _l.bindProducts) === null || _m === void 0 ? void 0 : _m[0]) === null || _o === void 0 ? void 0 : _o.bindCta, (_q = (_p = data.carouselSection[carouselIndex]) === null || _p === void 0 ? void 0 : _p.bindProducts) === null || _q === void 0 ? void 0 : _q[0], baseStyles.carouselButton)))),
666
+ ((_l = data.carouselSection[carouselIndex]) === null || _l === void 0 ? void 0 : _l.text) && (react_1.default.createElement("div", { style: mergeStyles(baseStyles.carouselText, 'carouselSection') }, (_m = data.carouselSection[carouselIndex]) === null || _m === void 0 ? void 0 : _m.text)),
667
+ renderCTA('carouselButton', (_q = (_p = (_o = data.carouselSection[carouselIndex]) === null || _o === void 0 ? void 0 : _o.bindProducts) === null || _p === void 0 ? void 0 : _p[0]) === null || _q === void 0 ? void 0 : _q.bindCta, (_s = (_r = data.carouselSection[carouselIndex]) === null || _r === void 0 ? void 0 : _r.bindProducts) === null || _s === void 0 ? void 0 : _s[0], baseStyles.carouselButton)))),
659
668
  data.highlightRevealSection && (react_1.default.createElement("div", { style: mergeStyles(baseStyles.highlightSection, 'highlightSection') },
660
669
  react_1.default.createElement("div", { style: baseStyles.highlightImageContainer },
661
670
  react_1.default.createElement("img", { src: data.highlightRevealSection.landingImageUrl || data.highlightRevealSection.cover, alt: data.highlightRevealSection.title, style: baseStyles.highlightImage })),
662
671
  react_1.default.createElement("div", { style: mergeStyles(baseStyles.highlightInfoSection, 'highlightSection') },
663
- react_1.default.createElement("div", { style: mergeStyles(baseStyles.highlightTitle, 'highlightSection') }, data.highlightRevealSection.title),
672
+ data.highlightRevealSection.text && (react_1.default.createElement("div", { style: { marginBottom: '10px' } },
673
+ react_1.default.createElement("div", { style: mergeStyles(baseStyles.highlightTitle, 'highlightSection') }, data.highlightRevealSection.text))),
664
674
  renderCTA('highlightButton', data.highlightRevealSection.bindCta, data.highlightRevealSection, baseStyles.highlightButton)))),
665
675
  data.productGridSection && data.productGridSection.length > 0 && (react_1.default.createElement("div", { style: mergeStyles(baseStyles.productGrid, 'productGrid') }, (() => {
666
676
  const gridItems = [null, null, null, null, null, null];
@@ -702,7 +712,10 @@ const StructurePage = (_a) => {
702
712
  });
703
713
  })())),
704
714
  data.footerSection && (react_1.default.createElement("div", { style: mergeStyles(baseStyles.footerSection, 'footerSection') },
705
- react_1.default.createElement("div", { style: mergeStyles(baseStyles.footerInfoSection, 'footerSection') }, renderCTA('footerButton', data.footerSection.bindCta, data.footerSection, baseStyles.footerButton)),
715
+ react_1.default.createElement("div", { style: mergeStyles(baseStyles.footerInfoSection, 'footerSection') },
716
+ data.footerSection.text && (react_1.default.createElement("div", { style: { marginBottom: '15px' } },
717
+ react_1.default.createElement("div", { style: mergeStyles(baseStyles.footerText, 'footerSection') }, data.footerSection.text))),
718
+ renderCTA('footerButton', data.footerSection.bindCta, data.footerSection, baseStyles.footerButton)),
706
719
  react_1.default.createElement("div", { style: baseStyles.footerImageContainer },
707
720
  react_1.default.createElement("img", { src: data.footerSection.landingImageUrl || data.footerSection.cover, alt: data.footerSection.title, style: baseStyles.footerImage }))))));
708
721
  };
@@ -12,7 +12,7 @@ export interface IEventTimeType {
12
12
  time: Date;
13
13
  target: EventTarget;
14
14
  }
15
- export type ICapiEventNameType = 'PageView' | 'ProductView' | 'ViewContent' | 'ClickCTA' | 'ContentSwipe' | 'Engagement' | 'ExitFeed' | 'AddToCart';
15
+ export type ICapiEventNameType = 'PageView' | 'ProductView' | 'ViewContent' | 'ClickCTA' | 'ContentSwipe' | 'Engagement' | 'ExitFeed' | 'AddToCart' | 'sessionCompleted';
16
16
  export interface ISxpDataSourceContext {
17
17
  rtcList: RecItemType[];
18
18
  setRtcList?: React.Dispatch<React.SetStateAction<RecItemType[]>>;