@capillarytech/creatives-library 8.0.287 → 8.0.289

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@capillarytech/creatives-library",
3
3
  "author": "meharaj",
4
- "version": "8.0.287",
4
+ "version": "8.0.289",
5
5
  "description": "Capillary creatives ui",
6
6
  "main": "./index.js",
7
7
  "module": "./index.es.js",
@@ -562,3 +562,28 @@ export const validateCarouselCards = (
562
562
 
563
563
  export const hasPersonalizationTags = (text = '') =>
564
564
  (text && ((text.includes('{{') && text.includes('}}')) || (text.includes('[') && text.includes(']'))));
565
+
566
+ export const getMessageForDevice = (templateData, device) => templateData?.versions?.[device]?.base?.expandableDetails?.message;
567
+
568
+ export const getTitleForDevice = (templateData, device) => templateData?.versions?.[device]?.base?.title || '';
569
+
570
+ export const checkForPersonalizationTokens = (formData) => {
571
+ // Check for {{ }} (liquid tags) and [ ] (event context tags) in mobile push content
572
+ const tokenRegex = /\{\{[\s\S]*?\}\}|\[[\s\S]*?\]/;
573
+
574
+ // Check all tabs/versions for personalization tokens
575
+ if (formData && typeof formData === 'object') {
576
+ for (const key in formData) {
577
+ const tabData = formData[key];
578
+ if (tabData && typeof tabData === 'object') {
579
+ for (const fieldKey in tabData) {
580
+ const fieldValue = tabData[fieldKey];
581
+ if (typeof fieldValue === 'string' && tokenRegex.test(fieldValue)) {
582
+ return true;
583
+ }
584
+ }
585
+ }
586
+ }
587
+ }
588
+ return false;
589
+ };
@@ -456,6 +456,7 @@ class CapTagList extends React.Component { // eslint-disable-line react/prefer-s
456
456
  </CapSpin>
457
457
  </CapRow>
458
458
  );
459
+ const {disableTooltipMsg, disabled} = this.props || {};
459
460
  return (
460
461
  <>
461
462
  {hidePopover ? (
@@ -476,8 +477,8 @@ class CapTagList extends React.Component { // eslint-disable-line react/prefer-s
476
477
  >
477
478
  <CapTooltip
478
479
  title={
479
- this?.props?.disableTooltipMsg && this?.props?.disabled ? (
480
- this?.props?.disableTooltipMsg
480
+ disableTooltipMsg && disabled ? (
481
+ disableTooltipMsg
481
482
  ) : fetchingSchemaError ? (
482
483
  <CapRow className="tooltip-text-container">
483
484
  <CapLabel className="tooltip-text1">
@@ -9,6 +9,7 @@ import ErrorInfoNote from '../../v2Components/ErrorInfoNote';
9
9
  import { PREVIEW } from './constants';
10
10
  import { EMAIL_CREATE_MODES } from '../EmailWrapper/constants';
11
11
  import { hasSupportCKEditor } from '../../utils/common';
12
+ import { getMessageForDevice, getTitleForDevice } from '../../utils/commonUtils';
12
13
 
13
14
  function getFullModeSaveBtn(slidBoxContent, isCreatingTemplate) {
14
15
  if (isCreatingTemplate) {
@@ -135,12 +136,11 @@ function SlideBoxFooter(props) {
135
136
  const hasPersonalizationTokens = () => {
136
137
  if (!isAnonymousType || !templateData) return false;
137
138
 
138
- const androidTitle = templateData?.versions?.ANDROID?.base?.title || '';
139
- const iosTitle = templateData?.versions?.IOS?.base?.title || '';
140
- const androidMessageBody = templateData?.versions?.ANDROID?.base?.expandibleDetails?.message
141
- || templateData?.versions?.ANDROID?.base?.expandableDetails?.message || '';
142
- const iosMessageBody = templateData?.versions?.IOS?.base?.expandibleDetails?.message
143
- || templateData?.versions?.IOS?.base?.expandableDetails?.message || '';
139
+ const androidTitle = getTitleForDevice(templateData, 'ANDROID') || '';
140
+ const iosTitle = getTitleForDevice(templateData, 'IOS') || '';
141
+ const androidMessageBody = getMessageForDevice(templateData, 'ANDROID') || '';
142
+ const iosMessageBody = getMessageForDevice(templateData, 'IOS') || '';
143
+ // Check for personalization tags {{ }} in title or message body
144
144
  const contentToCheck = `${androidTitle} ${iosTitle} ${androidMessageBody} ${iosMessageBody}`;
145
145
  // Check for liquid tags {{ }}
146
146
  if (contentToCheck.includes('{{') && contentToCheck.includes('}}')) {
@@ -1954,7 +1954,7 @@ export class Creatives extends React.Component {
1954
1954
 
1955
1955
  // Compute anonymous user type and channel restrictions
1956
1956
  const { customerType = '' } = this.props;
1957
- const isAnonymousType = customerType === constants.VISITOR || customerType === constants.MIXED;
1957
+ const isAnonymousType = [constants.VISITOR, constants.MIXED].includes(customerType);
1958
1958
  const restrictPersonalization = isAnonymousType;
1959
1959
 
1960
1960
  // For anonymous users, only MOBILEPUSH and WEBPUSH channels are allowed
@@ -388,6 +388,6 @@ export default defineMessages({
388
388
  },
389
389
  "personalizationTokensErrorMessage": {
390
390
  id: `${scope}.personalizationTokensErrorMessage`,
391
- defaultMessage: `Personalization tags are not supported for anonymous customers. Please remove the tags.`,
391
+ defaultMessage: `Personalization tags are not supported for anonymous customers, please remove the tags.`,
392
392
  },
393
393
  });
@@ -350,7 +350,7 @@ describe('shouldCheckValidation (line 79)', () => {
350
350
  IOS: {
351
351
  base: {
352
352
  title: 'Static',
353
- expandibleDetails: {
353
+ expandableDetails: {
354
354
  message: 'Message with {{variable}}',
355
355
  },
356
356
  },
@@ -3117,7 +3117,6 @@ new message content.",
3117
3117
  },
3118
3118
  ]
3119
3119
  }
3120
- showTruncatedTooltip={false}
3121
3120
  size="large"
3122
3121
  style={
3123
3122
  Object {
@@ -3136,7 +3135,6 @@ new message content.",
3136
3135
  />
3137
3136
  }
3138
3137
  onChange={[Function]}
3139
- onDropdownVisibleChange={[Function]}
3140
3138
  removeIcon={
3141
3139
  <CapIcon
3142
3140
  size="s"
@@ -3201,7 +3199,6 @@ new message content.",
3201
3199
  onBlur={[Function]}
3202
3200
  onChange={[Function]}
3203
3201
  onDeselect={[Function]}
3204
- onDropdownVisibleChange={[Function]}
3205
3202
  onFocus={[Function]}
3206
3203
  onInputKeyDown={[Function]}
3207
3204
  onSearch={[Function]}
@@ -3440,11 +3437,7 @@ new message content.",
3440
3437
  }
3441
3438
  }
3442
3439
  title=""
3443
- >
3444
- <div
3445
- className="cap-select-option-tooltip"
3446
- />
3447
- </div>
3440
+ />
3448
3441
  </div>
3449
3442
  <span
3450
3443
  className="ant-select-arrow"
@@ -6947,7 +6940,6 @@ new message content.",
6947
6940
  },
6948
6941
  ]
6949
6942
  }
6950
- showTruncatedTooltip={false}
6951
6943
  size="large"
6952
6944
  style={
6953
6945
  Object {
@@ -6966,7 +6958,6 @@ new message content.",
6966
6958
  />
6967
6959
  }
6968
6960
  onChange={[Function]}
6969
- onDropdownVisibleChange={[Function]}
6970
6961
  removeIcon={
6971
6962
  <CapIcon
6972
6963
  size="s"
@@ -7031,7 +7022,6 @@ new message content.",
7031
7022
  onBlur={[Function]}
7032
7023
  onChange={[Function]}
7033
7024
  onDeselect={[Function]}
7034
- onDropdownVisibleChange={[Function]}
7035
7025
  onFocus={[Function]}
7036
7026
  onInputKeyDown={[Function]}
7037
7027
  onSearch={[Function]}
@@ -7270,11 +7260,7 @@ new message content.",
7270
7260
  }
7271
7261
  }
7272
7262
  title=""
7273
- >
7274
- <div
7275
- className="cap-select-option-tooltip"
7276
- />
7277
- </div>
7263
+ />
7278
7264
  </div>
7279
7265
  <span
7280
7266
  className="ant-select-arrow"
@@ -10721,7 +10707,6 @@ new message content.",
10721
10707
  },
10722
10708
  ]
10723
10709
  }
10724
- showTruncatedTooltip={false}
10725
10710
  size="large"
10726
10711
  style={
10727
10712
  Object {
@@ -10740,7 +10725,6 @@ new message content.",
10740
10725
  />
10741
10726
  }
10742
10727
  onChange={[Function]}
10743
- onDropdownVisibleChange={[Function]}
10744
10728
  removeIcon={
10745
10729
  <CapIcon
10746
10730
  size="s"
@@ -10805,7 +10789,6 @@ new message content.",
10805
10789
  onBlur={[Function]}
10806
10790
  onChange={[Function]}
10807
10791
  onDeselect={[Function]}
10808
- onDropdownVisibleChange={[Function]}
10809
10792
  onFocus={[Function]}
10810
10793
  onInputKeyDown={[Function]}
10811
10794
  onSearch={[Function]}
@@ -11044,11 +11027,7 @@ new message content.",
11044
11027
  }
11045
11028
  }
11046
11029
  title=""
11047
- >
11048
- <div
11049
- className="cap-select-option-tooltip"
11050
- />
11051
- </div>
11030
+ />
11052
11031
  </div>
11053
11032
  <span
11054
11033
  className="ant-select-arrow"
@@ -38,6 +38,7 @@ import v2MobilePushCreateReducer from './reducer';
38
38
  import { v2MobilePushWatchDuplicateTemplateSaga } from './sagas';
39
39
  import { EXTERNAL_LINK_LOWERCASE } from './constants';
40
40
  import TestAndPreviewSlidebox from '../../../v2Components/TestAndPreviewSlidebox';
41
+ import { checkForPersonalizationTokens } from '../../../utils/commonUtils';
41
42
 
42
43
  const PrefixWrapper = styled.div`
43
44
  margin-right: 16px;
@@ -164,7 +165,7 @@ export class Create extends React.Component { // eslint-disable-line react/prefe
164
165
 
165
166
  // Check for personalization tokens if restriction is enabled and notify parent
166
167
  if (this.props.restrictPersonalization) {
167
- const hasTokens = this.checkForPersonalizationTokens(newFormData);
168
+ const hasTokens = checkForPersonalizationTokens(newFormData);
168
169
  if (this.props.onPersonalizationTokensChange) {
169
170
  this.props.onPersonalizationTokensChange(hasTokens);
170
171
  }
@@ -1511,33 +1512,12 @@ export class Create extends React.Component { // eslint-disable-line react/prefe
1511
1512
  this.injectEvents(schema);
1512
1513
  };
1513
1514
 
1514
- checkForPersonalizationTokens = (formData) => {
1515
- // Check for {{ }} (liquid tags) and [ ] (event context tags) in mobile push content
1516
- const tokenRegex = /\{\{[\s\S]*?\}\}|\[[\s\S]*?\]/;
1517
-
1518
- // Check all tabs/versions for personalization tokens
1519
- if (formData && typeof formData === 'object') {
1520
- for (const key in formData) {
1521
- const tabData = formData[key];
1522
- if (tabData && typeof tabData === 'object') {
1523
- for (const fieldKey in tabData) {
1524
- const fieldValue = tabData[fieldKey];
1525
- if (typeof fieldValue === 'string' && tokenRegex.test(fieldValue)) {
1526
- return true;
1527
- }
1528
- }
1529
- }
1530
- }
1531
- }
1532
- return false;
1533
- };
1534
-
1535
1515
  saveFormData = (formData) => {
1536
1516
  //this function gets called from form bulder only when the form data is valid
1537
1517
 
1538
1518
  // Check for personalization tokens if restriction is enabled
1539
1519
  if (this.props.restrictPersonalization) {
1540
- const hasTokens = this.checkForPersonalizationTokens(formData);
1520
+ const hasTokens = checkForPersonalizationTokens(formData);
1541
1521
  if (hasTokens) {
1542
1522
  const message = this.props.intl.formatMessage(messages.personalizationTokensErrorMessage);
1543
1523
  CapNotification.error({message, key: 'personalizationTokensError'});
@@ -344,6 +344,6 @@ export default defineMessages({
344
344
  },
345
345
  "personalizationTokensErrorMessage": {
346
346
  id: 'creatives.containersV2.MobilePush.Create.personalizationTokensErrorMessage',
347
- defaultMessage: 'Personalization tags are not supported for anonymous customers. Please remove the tags.',
347
+ defaultMessage: 'Personalization tags are not supported for anonymous customers, please remove the tags.',
348
348
  },
349
349
  });
@@ -39,7 +39,7 @@ import { v2MobilePushEditSagas } from './sagas';
39
39
  import v2MobilePushEditReducer from './reducer';
40
40
  import * as globalActions from '../../Cap/actions';
41
41
  import { MAPP_SDK } from './constants';
42
- import { isEmbeddedEditOrPreview } from '../../../utils/commonUtils';
42
+ import { checkForPersonalizationTokens, isEmbeddedEditOrPreview } from '../../../utils/commonUtils';
43
43
  import { EMBEDDED } from '../../Whatsapp/constants';
44
44
  import { OUTBOUND } from '../../../v2Components/FormBuilder/constants';
45
45
  import TestAndPreviewSlidebox from '../../../v2Components/TestAndPreviewSlidebox';
@@ -249,7 +249,7 @@ export class Edit extends React.Component { // eslint-disable-line react/prefer-
249
249
 
250
250
  // Check for personalization tokens if restriction is enabled and notify parent
251
251
  if (this.props.restrictPersonalization) {
252
- const hasTokens = this.checkForPersonalizationTokens(newFormData);
252
+ const hasTokens = checkForPersonalizationTokens(newFormData);
253
253
  if (this.props.onPersonalizationTokensChange) {
254
254
  this.props.onPersonalizationTokensChange(hasTokens);
255
255
  }
@@ -1698,7 +1698,7 @@ export class Edit extends React.Component { // eslint-disable-line react/prefer-
1698
1698
  saveFormData = (formData) => {
1699
1699
  // Check for personalization tokens if restriction is enabled
1700
1700
  if (this.props.restrictPersonalization) {
1701
- const hasTokens = this.checkForPersonalizationTokens(formData);
1701
+ const hasTokens = checkForPersonalizationTokens(formData);
1702
1702
  if (hasTokens) {
1703
1703
  const message = this.props.intl.formatMessage(messages.personalizationTokensErrorMessage);
1704
1704
  CapNotification.error({message, key: 'personalizationTokensError'});
@@ -1711,27 +1711,6 @@ export class Edit extends React.Component { // eslint-disable-line react/prefer-
1711
1711
  this.props.actions.editTemplate(obj, this.onUpdateTemplateComplete);
1712
1712
  };
1713
1713
 
1714
- checkForPersonalizationTokens = (formData) => {
1715
- // Check for {{ }} (liquid tags) and [ ] (event context tags) in mobile push content
1716
- const tokenRegex = /\{\{[\s\S]*?\}\}|\[[\s\S]*?\]/;
1717
-
1718
- // Check all tabs/versions for personalization tokens
1719
- if (formData && typeof formData === 'object') {
1720
- for (const key in formData) {
1721
- const tabData = formData[key];
1722
- if (tabData && typeof tabData === 'object') {
1723
- for (const fieldKey in tabData) {
1724
- const fieldValue = tabData[fieldKey];
1725
- if (typeof fieldValue === 'string' && tokenRegex.test(fieldValue)) {
1726
- return true;
1727
- }
1728
- }
1729
- }
1730
- }
1731
- }
1732
- return false;
1733
- };
1734
-
1735
1714
  handleFrameTasks = (e) => {
1736
1715
  //
1737
1716
  const type = e.data;
@@ -336,6 +336,6 @@ export default defineMessages({
336
336
  },
337
337
  "personalizationTokensErrorMessage": {
338
338
  id: 'creatives.containersV2.MobilePush.Edit.personalizationTokensErrorMessage',
339
- defaultMessage: 'Personalization tags are not supported for anonymous customers. Please remove the tags.',
339
+ defaultMessage: 'Personalization tags are not supported for anonymous customers, please remove the tags.',
340
340
  },
341
341
  });
@@ -509,6 +509,7 @@ const MobilePushNew = ({
509
509
  onCreateComplete,
510
510
  // new flag from parent - when true personalization via tags should be disabled
511
511
  restrictPersonalization = false,
512
+ onPersonalizationTokensChange,
512
513
  }) => {
513
514
  const { formatMessage } = intl;
514
515
 
@@ -1743,6 +1744,29 @@ const MobilePushNew = ({
1743
1744
  const isAndroidSupported = accountData?.configs?.android === '1';
1744
1745
  const isIosSupported = accountData?.configs?.ios === '1';
1745
1746
 
1747
+ // Notify parent when personalization tokens are added/removed (for anonymous user flow)
1748
+ useEffect(() => {
1749
+ if (!restrictPersonalization || typeof onPersonalizationTokensChange !== 'function') return;
1750
+ const hasToken = (value) => value && (
1751
+ (value.includes('{{') && value.includes('}}'))
1752
+ || (value.includes('[') && value.includes(']'))
1753
+ );
1754
+ const hasTokens = (
1755
+ (isAndroidSupported && (hasToken(androidContent?.title) || hasToken(androidContent?.message)))
1756
+ || (isIosSupported && (hasToken(iosContent?.title) || hasToken(iosContent?.message)))
1757
+ );
1758
+ onPersonalizationTokensChange(hasTokens);
1759
+ }, [
1760
+ restrictPersonalization,
1761
+ androidContent?.title,
1762
+ androidContent?.message,
1763
+ iosContent?.title,
1764
+ iosContent?.message,
1765
+ isAndroidSupported,
1766
+ isIosSupported,
1767
+ onPersonalizationTokensChange,
1768
+ ]);
1769
+
1746
1770
  // Validation logic for template creation/update
1747
1771
  const isAndroidFieldsMissing = isAndroidSupported && (!androidContent?.title?.trim() || !androidContent?.message?.trim());
1748
1772
  const isIosFieldsMissing = isIosSupported && (!iosContent?.title?.trim() || !iosContent?.message?.trim());
@@ -3075,6 +3099,7 @@ const MobilePushNew = ({
3075
3099
  className="mobilepush-test-preview-btn"
3076
3100
  type="secondary"
3077
3101
  style={{ marginLeft: '8px' }}
3102
+ disabled={isSaveDisabled}
3078
3103
  >
3079
3104
  <FormattedMessage {...creativesMessages.testAndPreview} />
3080
3105
  </CapButton>
@@ -3135,6 +3160,7 @@ MobilePushNew.propTypes = {
3135
3160
  isGetFormData: PropTypes.bool,
3136
3161
  getTemplateDetailsInProgress: PropTypes.bool,
3137
3162
  onCreateComplete: PropTypes.func,
3163
+ onPersonalizationTokensChange: PropTypes.func,
3138
3164
  };
3139
3165
 
3140
3166
  MobilePushNew.defaultProps = {
@@ -3165,6 +3191,7 @@ MobilePushNew.defaultProps = {
3165
3191
  isGetFormData: false,
3166
3192
  getTemplateDetailsInProgress: false,
3167
3193
  onCreateComplete: () => {},
3194
+ onPersonalizationTokensChange: undefined,
3168
3195
  };
3169
3196
 
3170
3197
  const mapStateToProps = createStructuredSelector({
@@ -86505,7 +86505,6 @@ new message content.",
86505
86505
  },
86506
86506
  ]
86507
86507
  }
86508
- showTruncatedTooltip={false}
86509
86508
  size="large"
86510
86509
  style={
86511
86510
  Object {
@@ -86525,7 +86524,6 @@ new message content.",
86525
86524
  />
86526
86525
  }
86527
86526
  onChange={[Function]}
86528
- onDropdownVisibleChange={[Function]}
86529
86527
  removeIcon={
86530
86528
  <_default
86531
86529
  size="s"
@@ -86591,7 +86589,6 @@ new message content.",
86591
86589
  onBlur={[Function]}
86592
86590
  onChange={[Function]}
86593
86591
  onDeselect={[Function]}
86594
- onDropdownVisibleChange={[Function]}
86595
86592
  onFocus={[Function]}
86596
86593
  onInputKeyDown={[Function]}
86597
86594
  onSearch={[Function]}
@@ -86830,13 +86827,9 @@ new message content.",
86830
86827
  "opacity": 1,
86831
86828
  }
86832
86829
  }
86833
- title=""
86830
+ title="Vertical Medium"
86834
86831
  >
86835
- <div
86836
- className="cap-select-option-tooltip"
86837
- >
86838
- Vertical Medium
86839
- </div>
86832
+ Vertical Medium
86840
86833
  </div>
86841
86834
  </div>
86842
86835
  <span
@@ -105854,7 +105847,6 @@ new message content.",
105854
105847
  },
105855
105848
  ]
105856
105849
  }
105857
- showTruncatedTooltip={false}
105858
105850
  size="large"
105859
105851
  style={
105860
105852
  Object {
@@ -105874,7 +105866,6 @@ new message content.",
105874
105866
  />
105875
105867
  }
105876
105868
  onChange={[Function]}
105877
- onDropdownVisibleChange={[Function]}
105878
105869
  removeIcon={
105879
105870
  <_default
105880
105871
  size="s"
@@ -105940,7 +105931,6 @@ new message content.",
105940
105931
  onBlur={[Function]}
105941
105932
  onChange={[Function]}
105942
105933
  onDeselect={[Function]}
105943
- onDropdownVisibleChange={[Function]}
105944
105934
  onFocus={[Function]}
105945
105935
  onInputKeyDown={[Function]}
105946
105936
  onSearch={[Function]}
@@ -106179,13 +106169,9 @@ new message content.",
106179
106169
  "opacity": 1,
106180
106170
  }
106181
106171
  }
106182
- title=""
106172
+ title="Vertical Medium"
106183
106173
  >
106184
- <div
106185
- className="cap-select-option-tooltip"
106186
- >
106187
- Vertical Medium
106188
- </div>
106174
+ Vertical Medium
106189
106175
  </div>
106190
106176
  </div>
106191
106177
  <span
@@ -209,7 +209,7 @@ export default defineMessages({
209
209
  },
210
210
  personalizationTokensErrorMessage: {
211
211
  id: `${scope}.personalizationTokensErrorMessage`,
212
- defaultMessage: 'Personalization tags are not supported for anonymous customers. Please remove the tags.',
212
+ defaultMessage: 'Personalization tags are not supported for anonymous customers, please remove the tags.',
213
213
  },
214
214
  personalizationNotSupportedAnonymous: {
215
215
  id: `${scope}.personalizationNotSupportedAnonymous`,