@capillarytech/creatives-library 8.0.254 → 8.0.255-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. package/assets/Android.png +0 -0
  2. package/assets/iOS.png +0 -0
  3. package/constants/unified.js +2 -1
  4. package/initialReducer.js +2 -0
  5. package/package.json +1 -1
  6. package/services/api.js +10 -0
  7. package/services/tests/api.test.js +34 -0
  8. package/utils/common.js +5 -0
  9. package/utils/commonUtils.js +28 -5
  10. package/utils/tests/commonUtil.test.js +224 -0
  11. package/utils/transformTemplateConfig.js +0 -10
  12. package/v2Components/CapDeviceContent/index.js +61 -56
  13. package/v2Components/CapTagList/index.js +6 -1
  14. package/v2Components/CapTagListWithInput/index.js +5 -1
  15. package/v2Components/CapTagListWithInput/messages.js +1 -1
  16. package/v2Components/CapWhatsappCTA/tests/index.test.js +5 -0
  17. package/v2Components/ErrorInfoNote/constants.js +1 -0
  18. package/v2Components/ErrorInfoNote/index.js +457 -72
  19. package/v2Components/ErrorInfoNote/messages.js +36 -6
  20. package/v2Components/ErrorInfoNote/style.scss +282 -6
  21. package/v2Components/FormBuilder/tests/index.test.js +13 -4
  22. package/v2Components/HtmlEditor/HTMLEditor.js +547 -94
  23. package/v2Components/HtmlEditor/__tests__/HTMLEditor.apiErrors.test.js +874 -0
  24. package/v2Components/HtmlEditor/__tests__/HTMLEditor.test.js +1358 -133
  25. package/v2Components/HtmlEditor/__tests__/index.lazy.test.js +27 -16
  26. package/v2Components/HtmlEditor/_htmlEditor.scss +108 -45
  27. package/v2Components/HtmlEditor/_index.lazy.scss +0 -1
  28. package/v2Components/HtmlEditor/components/CodeEditorPane/_codeEditorPane.scss +22 -101
  29. package/v2Components/HtmlEditor/components/CodeEditorPane/index.js +149 -140
  30. package/v2Components/HtmlEditor/components/DeviceToggle/_deviceToggle.scss +2 -1
  31. package/v2Components/HtmlEditor/components/DeviceToggle/index.js +3 -3
  32. package/v2Components/HtmlEditor/components/EditorToolbar/_editorToolbar.scss +9 -0
  33. package/v2Components/HtmlEditor/components/EditorToolbar/index.js +1 -1
  34. package/v2Components/HtmlEditor/components/FullscreenModal/_fullscreenModal.scss +22 -0
  35. package/v2Components/HtmlEditor/components/InAppPreviewPane/DeviceFrame.js +4 -7
  36. package/v2Components/HtmlEditor/components/InAppPreviewPane/__tests__/DeviceFrame.test.js +35 -45
  37. package/v2Components/HtmlEditor/components/InAppPreviewPane/_inAppPreviewPane.scss +1 -3
  38. package/v2Components/HtmlEditor/components/InAppPreviewPane/constants.js +33 -33
  39. package/v2Components/HtmlEditor/components/InAppPreviewPane/index.js +7 -6
  40. package/v2Components/HtmlEditor/components/PreviewPane/_previewPane.scss +3 -6
  41. package/v2Components/HtmlEditor/components/PreviewPane/index.js +24 -34
  42. package/v2Components/HtmlEditor/components/SplitContainer/_splitContainer.scss +1 -1
  43. package/v2Components/HtmlEditor/components/ValidationErrorDisplay/index.js +49 -31
  44. package/v2Components/HtmlEditor/components/ValidationPanel/_validationPanel.scss +50 -34
  45. package/v2Components/HtmlEditor/components/ValidationPanel/constants.js +6 -0
  46. package/v2Components/HtmlEditor/components/ValidationPanel/index.js +70 -41
  47. package/v2Components/HtmlEditor/components/ValidationTabs/_validationTabs.scss +254 -0
  48. package/v2Components/HtmlEditor/components/ValidationTabs/index.js +364 -0
  49. package/v2Components/HtmlEditor/components/ValidationTabs/messages.js +51 -0
  50. package/v2Components/HtmlEditor/constants.js +42 -20
  51. package/v2Components/HtmlEditor/hooks/__tests__/useInAppContent.test.js +373 -16
  52. package/v2Components/HtmlEditor/hooks/__tests__/useValidation.apiErrors.test.js +794 -0
  53. package/v2Components/HtmlEditor/hooks/useEditorContent.js +5 -2
  54. package/v2Components/HtmlEditor/hooks/useInAppContent.js +88 -146
  55. package/v2Components/HtmlEditor/hooks/useValidation.js +189 -53
  56. package/v2Components/HtmlEditor/index.js +1 -1
  57. package/v2Components/HtmlEditor/messages.js +95 -85
  58. package/v2Components/HtmlEditor/utils/__tests__/htmlValidator.enhanced.test.js +94 -45
  59. package/v2Components/HtmlEditor/utils/__tests__/validationAdapter.test.js +134 -0
  60. package/v2Components/HtmlEditor/utils/contentSanitizer.js +40 -41
  61. package/v2Components/HtmlEditor/utils/htmlValidator.js +71 -72
  62. package/v2Components/HtmlEditor/utils/liquidTemplateSupport.js +134 -102
  63. package/v2Components/HtmlEditor/utils/properSyntaxHighlighting.js +23 -25
  64. package/v2Components/HtmlEditor/utils/validationAdapter.js +66 -41
  65. package/v2Components/HtmlEditor/utils/validationConstants.js +40 -0
  66. package/v2Components/MobilePushPreviewV2/index.js +32 -7
  67. package/v2Components/TemplatePreview/_templatePreview.scss +55 -24
  68. package/v2Components/TemplatePreview/index.js +47 -32
  69. package/v2Components/TemplatePreview/messages.js +4 -0
  70. package/v2Components/TestAndPreviewSlidebox/_testAndPreviewSlidebox.scss +1 -0
  71. package/v2Containers/BeeEditor/index.js +172 -90
  72. package/v2Containers/BeePopupEditor/_beePopupEditor.scss +14 -0
  73. package/v2Containers/BeePopupEditor/constants.js +10 -0
  74. package/v2Containers/BeePopupEditor/index.js +194 -0
  75. package/v2Containers/BeePopupEditor/tests/index.test.js +627 -0
  76. package/v2Containers/CreativesContainer/SlideBoxContent.js +128 -51
  77. package/v2Containers/CreativesContainer/SlideBoxFooter.js +163 -13
  78. package/v2Containers/CreativesContainer/SlideBoxHeader.js +2 -1
  79. package/v2Containers/CreativesContainer/constants.js +1 -0
  80. package/v2Containers/CreativesContainer/index.js +239 -46
  81. package/v2Containers/CreativesContainer/messages.js +8 -0
  82. package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +11 -2
  83. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +38 -50
  84. package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +106 -0
  85. package/v2Containers/Email/actions.js +7 -0
  86. package/v2Containers/Email/constants.js +5 -1
  87. package/v2Containers/Email/index.js +234 -29
  88. package/v2Containers/Email/messages.js +32 -0
  89. package/v2Containers/Email/reducer.js +12 -1
  90. package/v2Containers/Email/sagas.js +61 -7
  91. package/v2Containers/Email/tests/__snapshots__/reducer.test.js.snap +2 -0
  92. package/v2Containers/Email/tests/reducer.test.js +46 -0
  93. package/v2Containers/Email/tests/sagas.test.js +320 -29
  94. package/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +1285 -0
  95. package/v2Containers/EmailWrapper/components/EmailWrapperView.js +207 -19
  96. package/v2Containers/EmailWrapper/components/HTMLEditorTesting.js +40 -74
  97. package/v2Containers/EmailWrapper/components/__tests__/EmailHTMLEditor.test.js +1870 -0
  98. package/v2Containers/EmailWrapper/components/__tests__/EmailWrapperView.test.js +520 -0
  99. package/v2Containers/EmailWrapper/components/__tests__/HTMLEditorTesting.test.js +2 -67
  100. package/v2Containers/EmailWrapper/constants.js +2 -0
  101. package/v2Containers/EmailWrapper/hooks/useEmailWrapper.js +629 -77
  102. package/v2Containers/EmailWrapper/index.js +103 -23
  103. package/v2Containers/EmailWrapper/messages.js +61 -1
  104. package/v2Containers/EmailWrapper/tests/useEmailWrapper.edgeCases.test.js +643 -0
  105. package/v2Containers/EmailWrapper/tests/useEmailWrapper.test.js +594 -77
  106. package/v2Containers/InApp/__tests__/InAppHTMLEditor.test.js +376 -0
  107. package/v2Containers/InApp/__tests__/sagas.test.js +363 -0
  108. package/v2Containers/InApp/actions.js +7 -0
  109. package/v2Containers/InApp/constants.js +20 -4
  110. package/v2Containers/InApp/index.js +802 -359
  111. package/v2Containers/InApp/index.scss +4 -3
  112. package/v2Containers/InApp/messages.js +7 -3
  113. package/v2Containers/InApp/reducer.js +21 -3
  114. package/v2Containers/InApp/sagas.js +29 -9
  115. package/v2Containers/InApp/selectors.js +25 -5
  116. package/v2Containers/InApp/tests/index.test.js +154 -50
  117. package/v2Containers/InApp/tests/reducer.test.js +34 -0
  118. package/v2Containers/InApp/tests/sagas.test.js +61 -9
  119. package/v2Containers/InApp/tests/selectors.test.js +612 -0
  120. package/v2Containers/InAppWrapper/components/InAppWrapperView.js +151 -0
  121. package/v2Containers/InAppWrapper/components/__tests__/InAppWrapperView.test.js +267 -0
  122. package/v2Containers/InAppWrapper/components/inAppWrapperView.scss +23 -0
  123. package/v2Containers/InAppWrapper/constants.js +16 -0
  124. package/v2Containers/InAppWrapper/hooks/__tests__/useInAppWrapper.test.js +473 -0
  125. package/v2Containers/InAppWrapper/hooks/useInAppWrapper.js +198 -0
  126. package/v2Containers/InAppWrapper/index.js +148 -0
  127. package/v2Containers/InAppWrapper/messages.js +49 -0
  128. package/v2Containers/InappAdvance/index.js +1099 -0
  129. package/v2Containers/InappAdvance/index.scss +10 -0
  130. package/v2Containers/InappAdvance/tests/index.test.js +448 -0
  131. package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/content.test.js.snap +3 -0
  132. package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/index.test.js.snap +2 -0
  133. package/v2Containers/Line/Container/Wrapper/tests/__snapshots__/index.test.js.snap +2 -0
  134. package/v2Containers/Line/Container/tests/__snapshots__/index.test.js.snap +9 -0
  135. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +12 -0
  136. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +4 -0
  137. package/v2Containers/TagList/index.js +62 -19
  138. package/v2Containers/Templates/_templates.scss +60 -1
  139. package/v2Containers/Templates/index.js +89 -4
  140. package/v2Containers/Templates/messages.js +4 -0
  141. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +34 -0
  142. package/v2Components/HtmlEditor/components/ValidationErrorDisplay/__tests__/index.test.js +0 -152
  143. package/v2Containers/EmailWrapper/tests/EmailWrapperView.test.js +0 -214
@@ -2281,6 +2281,7 @@ new message content.",
2281
2281
  "email": [Function],
2282
2282
  "facebookPreview": [Function],
2283
2283
  "gallery": [Function],
2284
+ "inApp": [Function],
2284
2285
  "language": [Function],
2285
2286
  "navigationConfig": [Function],
2286
2287
  "previewAndTest": [Function],
@@ -13327,6 +13328,7 @@ new message content.",
13327
13328
  "email": [Function],
13328
13329
  "facebookPreview": [Function],
13329
13330
  "gallery": [Function],
13331
+ "inApp": [Function],
13330
13332
  "language": [Function],
13331
13333
  "navigationConfig": [Function],
13332
13334
  "previewAndTest": [Function],
@@ -24119,6 +24121,7 @@ new message content.",
24119
24121
  "email": [Function],
24120
24122
  "facebookPreview": [Function],
24121
24123
  "gallery": [Function],
24124
+ "inApp": [Function],
24122
24125
  "language": [Function],
24123
24126
  "navigationConfig": [Function],
24124
24127
  "previewAndTest": [Function],
@@ -34911,6 +34914,7 @@ new message content.",
34911
34914
  "email": [Function],
34912
34915
  "facebookPreview": [Function],
34913
34916
  "gallery": [Function],
34917
+ "inApp": [Function],
34914
34918
  "language": [Function],
34915
34919
  "navigationConfig": [Function],
34916
34920
  "previewAndTest": [Function],
@@ -40,6 +40,7 @@ export class TagList extends React.Component { // eslint-disable-line react/pref
40
40
  tags: [],
41
41
  tagsError: false,
42
42
  currentContext: null, // Track current context to detect changes
43
+ hasTriggeredInitialApiCall: false, // Track if we've triggered API call when popover opens
43
44
  };
44
45
  this.renderTags = this.renderTags.bind(this);
45
46
  this.populateTags = this.populateTags.bind(this);
@@ -51,6 +52,14 @@ export class TagList extends React.Component { // eslint-disable-line react/pref
51
52
 
52
53
  componentDidMount() {
53
54
  this.generateTags(this.props);
55
+ // Trigger initial API call if tags are empty (similar to Email/SMS behavior)
56
+ const { tags, injectedTags, onContextChange } = this.props;
57
+ const hasNoTags = (!tags || tags.length === 0) && _.isEmpty(injectedTags);
58
+ if (hasNoTags && onContextChange) {
59
+ // Trigger API call with default 'Outbound' context to match CapTagList default
60
+ // This ensures tags are loaded when component mounts
61
+ this.getTagsforContext('Outbound');
62
+ }
54
63
  }
55
64
 
56
65
  componentWillReceiveProps(nextProps) {
@@ -59,23 +68,27 @@ export class TagList extends React.Component { // eslint-disable-line react/pref
59
68
  // 2. Context change is happening (detected by different tag arrays)
60
69
  const { injectedTags: currentInjectedTags, tags: currentTags } = this.props;
61
70
  const { injectedTags: nextInjectedTags, tags: nextTags, fetchingSchemaError } = nextProps;
62
-
71
+
63
72
  const isInitialLoad = _.isEmpty(currentInjectedTags) && _.isEmpty(currentTags);
64
73
  const isContextChange = !_.isEqual(nextTags, currentTags) && !_.isEmpty(currentTags);
65
-
74
+
66
75
  if (isInitialLoad || isContextChange) {
67
76
  this.setState({loading: true});
68
77
  }
69
-
78
+
70
79
  // Only reset loading for injectedTags changes if we're not currently loading due to context change
71
80
  if (!_.isEqual(nextInjectedTags, currentInjectedTags) && !this.state.loading) {
72
81
  this.setState({loading: false});
73
82
  this.clearLoadingTimeout();
74
83
  }
75
-
84
+
76
85
  if (!_.isEqual(nextTags, currentTags)) {
77
86
  this.setState({loading: false});
78
87
  this.clearLoadingTimeout();
88
+ // Reset the flag when tags are received, so we can trigger API call again if needed
89
+ if (nextTags && nextTags.length > 0) {
90
+ this.setState({ hasTriggeredInitialApiCall: false });
91
+ }
79
92
  }
80
93
  if (fetchingSchemaError) {
81
94
  this.setState({tagsError: fetchingSchemaError, loading: false});
@@ -86,7 +99,7 @@ export class TagList extends React.Component { // eslint-disable-line react/pref
86
99
  componentDidUpdate(prevProps) {
87
100
  const { tags, injectedTags, selectedOfferDetails } = this.props;
88
101
  const { tags: prevTags, injectedTags: prevInjectedTags, selectedOfferDetails: prevSelectedOfferDetails } = prevProps;
89
-
102
+
90
103
  if (tags !== prevTags || injectedTags !== prevInjectedTags || selectedOfferDetails !== prevSelectedOfferDetails) {
91
104
  this.generateTags(this.props);
92
105
  }
@@ -111,17 +124,44 @@ export class TagList extends React.Component { // eslint-disable-line react/pref
111
124
  getTagsforContext = (data) => {
112
125
  // Set loading state when context change is requested
113
126
  this.setState({loading: true, currentContext: data});
114
-
127
+
115
128
  // Set a timeout to prevent infinite loading (fallback safety)
116
129
  this.clearLoadingTimeout();
117
130
  this.loadingTimeout = setTimeout(() => {
118
131
  this.setState({loading: false});
119
132
  }, 5000); // Reduced timeout to 5 seconds for better UX
120
-
133
+
121
134
  const { onContextChange } = this.props;
122
- onContextChange(data);
135
+ // Always call onContextChange if available - this triggers the API call
136
+ // The API call will fetch tags from /meta/TAG endpoint
137
+ if (onContextChange) {
138
+ onContextChange(data);
139
+ } else {
140
+ console.warn('TagList: onContextChange is not available. API call will not be triggered.');
141
+ }
123
142
  }
124
143
 
144
+ handlePopoverVisibilityChange = (visible) => {
145
+ // When popover opens, trigger API call if tags are empty or if we haven't triggered it yet
146
+ // This ensures API call happens when user clicks "Add Label" button
147
+ if (visible && this.props.onContextChange) {
148
+ const { tags, injectedTags } = this.props;
149
+ // Check if tags array is empty or if state tags are empty
150
+ const hasNoTags = (!tags || tags.length === 0) && _.isEmpty(injectedTags);
151
+ const hasNoStateTags = _.isEmpty(this.state.tags);
152
+ const hasNotTriggeredApiCall = !this.state.hasTriggeredInitialApiCall;
153
+
154
+ // Trigger API call if tags are not loaded yet OR if we haven't triggered it yet
155
+ if ((hasNoTags || hasNoStateTags || hasNotTriggeredApiCall)) {
156
+ // Mark that we've triggered the API call
157
+ this.setState({ hasTriggeredInitialApiCall: true });
158
+ // Trigger API call with default 'Outbound' context to match CapTagList default
159
+ // This will call onContextChange which triggers handleOnTagsContextChange in InApp
160
+ this.getTagsforContext('Outbound');
161
+ }
162
+ }
163
+ };
164
+
125
165
  generateTags = (props) => {
126
166
  let tags = {};
127
167
  let injectedTags = {};
@@ -149,11 +189,11 @@ export class TagList extends React.Component { // eslint-disable-line react/pref
149
189
  if (eventContextTags?.length) {
150
190
  const TAG_HEADER_MSG_LABEL = this.props.intl.formatMessage(messages.entryEvent);
151
191
  eventContextTagsObj.eventContextTags = {
152
- name: TAG_HEADER_MSG_LABEL,
153
- desc: TAG_HEADER_MSG_LABEL,
154
- resolved: true,
192
+ "name": TAG_HEADER_MSG_LABEL,
193
+ "desc": TAG_HEADER_MSG_LABEL,
194
+ "resolved": true,
155
195
  'tag-header': true,
156
- subtags: {},
196
+ "subtags": {},
157
197
  };
158
198
 
159
199
  eventContextTags.forEach((tag) => {
@@ -164,11 +204,11 @@ export class TagList extends React.Component { // eslint-disable-line react/pref
164
204
  // Initializing the tags profile if it doesn't exist
165
205
  if (!eventContextTagsObj?.eventContextTags?.subtags?.[profileId]) {
166
206
  eventContextTagsObj.eventContextTags.subtags[profileId] = {
167
- name: profileName,
168
- desc: profileName,
169
- resolved: true,
207
+ "name": profileName,
208
+ "desc": profileName,
209
+ "resolved": true,
170
210
  'tag-header': true,
171
- subtags: {},
211
+ "subtags": {},
172
212
  };
173
213
  }
174
214
  // Adding the current tag to the profile group
@@ -201,7 +241,7 @@ export class TagList extends React.Component { // eslint-disable-line react/pref
201
241
  _.forEach(tagsList, (temp) => {
202
242
  const tag = temp.definition;
203
243
  const { intl } = this.props;
204
- const { locale: userLocale } = intl || {};
244
+ const { locale: userLocale } = intl || {};
205
245
 
206
246
  // Check if the tag.value should be skipped based on feature control
207
247
  if (_.includes(excludedTags, tag.value)) {
@@ -209,8 +249,8 @@ export class TagList extends React.Component { // eslint-disable-line react/pref
209
249
  }
210
250
  if (!tag['tag-header']) {
211
251
  mainTags[tag.value] = {
212
- "name": tag?.label[userLocale] ? tag?.label[userLocale] : tag?.label?.en,
213
- "desc": tag?.label[userLocale] ? tag?.label[userLocale] : tag?.label?.en,
252
+ name: tag?.label[userLocale] ? tag?.label[userLocale] : tag?.label?.en,
253
+ desc: tag?.label[userLocale] ? tag?.label[userLocale] : tag?.label?.en,
214
254
  };
215
255
  } else if (tag['tag-header'] && mainTags[tag.value]) {
216
256
  mainTags[tag.value].subtags = _.concat(mainTags[tag.value].subtags, tag.subtags);
@@ -366,12 +406,14 @@ export class TagList extends React.Component { // eslint-disable-line react/pref
366
406
  visibleTaglist={this.props.visibleTaglist}
367
407
  hidePopover={this.props.hidePopover}
368
408
  onContextChange={this.getTagsforContext}
409
+ onVisibleChange={this.handlePopoverVisibilityChange}
369
410
  moduleFilterEnabled={this.props.moduleFilterEnabled}
370
411
  modalProps={this.props.modalProps}
371
412
  currentOrgDetails={this.props.currentOrgDetails}
372
413
  channel={this.props.channel}
373
414
  disabled={this.props.disabled}
374
415
  fetchingSchemaError={this?.state?.tagsError}
416
+ popoverPlacement={this.props.popoverPlacement}
375
417
  />
376
418
  </div>
377
419
  );
@@ -402,6 +444,7 @@ TagList.propTypes = {
402
444
  disabled: PropTypes.bool,
403
445
  fetchingSchemaError: PropTypes.bool,
404
446
  eventContextTags: PropTypes.array,
447
+ popoverPlacement: PropTypes.string,
405
448
  intl: PropTypes.shape({
406
449
  formatMessage: PropTypes.func.isRequired,
407
450
  locale: PropTypes.string,
@@ -313,6 +313,64 @@
313
313
  }
314
314
  }
315
315
  }
316
+ .INAPP {
317
+ margin-bottom: $CAP_SPACE_12;
318
+ .inapp-container {
319
+ margin-top: $CAP_SPACE_24;
320
+ }
321
+ // Modal Layout - centered
322
+ .inapp-modal-layout {
323
+ position: absolute;
324
+ display: flex;
325
+ width: 12rem;
326
+ top: 10%;
327
+ left: 10%;
328
+ bottom: 5%;
329
+ overflow: hidden;
330
+ background-color: $CAP_WHITE;
331
+ border-radius: $CAP_SPACE_08;
332
+ }
333
+
334
+ // Full Screen Layout
335
+ .inapp-fullscreen-layout {
336
+ position: absolute;
337
+ display: flex;
338
+ width: 12rem;
339
+ left: 10%;
340
+ top: 1%;
341
+ bottom: 1%;
342
+ overflow: hidden;
343
+ background-color: $CAP_WHITE;
344
+ border-radius: $CAP_SPACE_08;
345
+ }
346
+
347
+ // Top Banner Layout
348
+ .inapp-top-banner-layout {
349
+ position: absolute;
350
+ display: flex;
351
+ width: 12rem;
352
+ left: 10%;
353
+ top: 1%;
354
+ bottom: 20%;
355
+ overflow: hidden;
356
+ background-color: $CAP_WHITE;
357
+ border-radius: $CAP_SPACE_08;
358
+ }
359
+
360
+ // Bottom Banner Layout
361
+ .inapp-bottom-banner-layout {
362
+ position: absolute;
363
+ display: flex;
364
+ justify-content: center;
365
+ width: 12rem;
366
+ left: 10%;
367
+ top: 50%;
368
+ bottom: 2%;
369
+ overflow: hidden;
370
+ background-color: $CAP_WHITE;
371
+ border-radius: $CAP_SPACE_08;
372
+ }
373
+ }
316
374
  }
317
375
 
318
376
  .create-new-link{
@@ -695,7 +753,8 @@
695
753
  }
696
754
 
697
755
  .whatsapp-filters,
698
- .zalo-filters {
756
+ .zalo-filters,
757
+ .inapp-filters {
699
758
  display: flex;
700
759
  width: 100%;
701
760
  padding-left: 8px;
@@ -127,13 +127,14 @@ import {
127
127
  VIDEO,
128
128
  GIF,
129
129
  } from '../Whatsapp/constants';
130
- import { INAPP_LAYOUT_DETAILS } from '../InApp/constants';
130
+ import { INAPP_LAYOUT_DETAILS, INAPP_MESSAGE_LAYOUT_TYPES, INAPP_MEDIA_TYPES, BIG_HTML, ANDROID, IOS } from '../InApp/constants';
131
131
  import { ZALO_STATUS_OPTIONS, ZALO_STATUSES } from '../Zalo/constants';
132
132
  import { getWhatsappContent, getWhatsappStatus, getWhatsappCategory, getWhatsappCta, getWhatsappQuickReply, getWhatsappAutoFill, getWhatsappCarouselButtonView } from '../Whatsapp/utils';
133
133
  import { getRCSContent } from '../Rcs/utils';
134
134
  import {RCS_STATUSES} from '../Rcs/constants';
135
135
  import zaloMessages from '../Zalo/messages';
136
136
  import rcsMessages from '../Rcs/messages';
137
+ import inAppMessages from '../InApp/messages';
137
138
  import globalMessages from '../../v2Containers/Cap/messages';
138
139
  import { handlePreviewInNewTab } from '../../utils/common';
139
140
 
@@ -200,6 +201,29 @@ const SMS_FILTERS = {
200
201
  SERVICE_IMPLICIT: 'implicit',
201
202
  };
202
203
 
204
+ const INAPP_LAYOUT_OPTIONS = [
205
+ {
206
+ key: 'popup',
207
+ value: INAPP_MESSAGE_LAYOUT_TYPES.MODAL,
208
+ label: <FormattedMessage {...inAppMessages.layoutModal} />,
209
+ },
210
+ {
211
+ key: 'topBanner',
212
+ value: INAPP_MESSAGE_LAYOUT_TYPES.TOPBANNER,
213
+ label: <FormattedMessage {...inAppMessages.layoutTopBanner} />,
214
+ },
215
+ {
216
+ key: 'bottomBanner',
217
+ value: INAPP_MESSAGE_LAYOUT_TYPES.BOTTOMBANNER,
218
+ label: <FormattedMessage {...inAppMessages.layoutBottomBanner} />,
219
+ },
220
+ {
221
+ key: 'fullScreen',
222
+ value: INAPP_MESSAGE_LAYOUT_TYPES.FULLSCREEN,
223
+ label: <FormattedMessage {...inAppMessages.layoutFullScreen} />,
224
+ },
225
+ ];
226
+
203
227
  const WHATSAPP_LOWERCASE = WHATSAPP.toLowerCase();
204
228
  const RCS_LOWERCASE = RCS.toLowerCase();
205
229
  const SMS_LOWERCASE = SMS.toLowerCase();
@@ -250,6 +274,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
250
274
  selectedWhatsappStatus: '',
251
275
  selectedWhatsappCategory: '',
252
276
  selectedZaloStatus: '',
277
+ selectedInAppLayout: '',
253
278
  hostName: '',
254
279
  searchedZaloTemplates: [],
255
280
  searchingZaloTemplate: false,
@@ -1648,6 +1673,19 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1648
1673
  return templates;
1649
1674
  }
1650
1675
 
1676
+ filterInAppTemplates = (templates) => {
1677
+ const { selectedInAppLayout } = this.state;
1678
+ if (!selectedInAppLayout) {
1679
+ return templates;
1680
+ }
1681
+ return templates.filter((template) => {
1682
+ const androidBodyType = get(template, 'versions.base.content.ANDROID.bodyType');
1683
+ const iosBodyType = get(template, 'versions.base.content.IOS.bodyType');
1684
+ const inappBodyType = androidBodyType || iosBodyType;
1685
+ return inappBodyType === selectedInAppLayout;
1686
+ });
1687
+ }
1688
+
1651
1689
  filterSMSTemplates = (templates) => {
1652
1690
  const { smsFilter } = this.state;
1653
1691
  if (SMS_FILTERS.ALL === smsFilter) {
@@ -1734,6 +1772,9 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1734
1772
  case ZALO:
1735
1773
  filteredTemplates = this.filterZaloTemplates(templates);
1736
1774
  break;
1775
+ case INAPP:
1776
+ filteredTemplates = this.filterInAppTemplates(templates);
1777
+ break;
1737
1778
  default:
1738
1779
  break;
1739
1780
  }
@@ -2023,9 +2064,12 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
2023
2064
  templateData.isNewMobilePush = commonUtil.hasNewMobilePushFeatureEnabled();
2024
2065
  }
2025
2066
  break;
2026
- case INAPP:
2067
+ case INAPP: {
2068
+ // Pass the full template object to CapCustomCard so getInAppContent can extract the data
2069
+ // Similar to how MOBILE_PUSH passes the full template when new feature is enabled
2027
2070
  templateData.content = template;
2028
2071
  break;
2072
+ }
2029
2073
  case WECHAT:
2030
2074
  templateData.content = this.prepareWeChatPreviewData(template);
2031
2075
  break;
@@ -2339,7 +2383,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
2339
2383
  return (<div>
2340
2384
  {[WECHAT, MOBILE_PUSH, INAPP, WHATSAPP, ZALO,RCS].includes(currentChannel) && this.showAccountName()}
2341
2385
  {filterContent}
2342
- {[WHATSAPP, ZALO,RCS].includes(currentChannel) && this.selectedFilters()}
2386
+ {[WHATSAPP, ZALO, INAPP,RCS].includes(currentChannel) && this.selectedFilters()}
2343
2387
  {<div>
2344
2388
  {!isEmpty(filteredTemplates) || !isEmpty(this.state.searchText) || !isEmpty(this.props.Templates.templateError) ? (
2345
2389
  <div className={!isEmpty(this.state.searchText) && isEmpty(cardDataList) ? '' : this.isFullMode() ? "v2-pagination-container" : "v2-pagination-container-half"}>
@@ -2549,6 +2593,9 @@ return (<div>
2549
2593
 
2550
2594
  prepareWeChatMappedPreviewData(content, templateTags, tagData) {
2551
2595
  let formattedContent = cloneDeep(content);
2596
+ if (!formattedContent || typeof formattedContent !== 'string') {
2597
+ return formattedContent || '';
2598
+ }
2552
2599
  forEach(templateTags, (tag) => {
2553
2600
  if (tagData[tag].value !== undefined) {
2554
2601
  formattedContent = formattedContent.replace(`{{${tag}.DATA}}`, tagData[tag].value);
@@ -3746,14 +3793,19 @@ return (<div>
3746
3793
  }
3747
3794
 
3748
3795
  removeZaloFilter = () => this.setState({ selectedZaloStatus: null });
3796
+ removeInAppLayoutFilter = () => this.setState({ selectedInAppLayout: '' });
3749
3797
 
3750
3798
  selectedFilters = () => {
3751
- const { selectedWhatsappStatus, selectedWhatsappCategory, selectedZaloStatus } = this.state;
3799
+ const { selectedWhatsappStatus, selectedWhatsappCategory, selectedZaloStatus, selectedInAppLayout } = this.state;
3752
3800
  const {
3753
3801
  intl: {
3754
3802
  formatMessage,
3755
3803
  },
3756
3804
  } = this.props;
3805
+ const getInAppLayoutLabel = (layoutValue) => {
3806
+ const layoutOption = INAPP_LAYOUT_OPTIONS.find(opt => opt.value === layoutValue);
3807
+ return layoutOption ? layoutOption.label : layoutValue;
3808
+ };
3757
3809
  return (
3758
3810
  <CapRow type="flex" align="middle" className="selected-whatsapp-filters">
3759
3811
  {
@@ -3788,6 +3840,23 @@ return (<div>
3788
3840
  </CapTag>
3789
3841
  ) : null
3790
3842
  }
3843
+ {
3844
+ selectedInAppLayout ? (
3845
+ <CapTag closable onClose={this.removeInAppLayoutFilter}>
3846
+ {formatMessage(messages.layout)}: {getInAppLayoutLabel(selectedInAppLayout)}
3847
+ </CapTag>
3848
+ ) : null
3849
+ }
3850
+ {
3851
+ selectedInAppLayout ? (
3852
+ <CapLink
3853
+ onClick={() => {
3854
+ this.removeInAppLayoutFilter();
3855
+ }}
3856
+ title={this.props.intl.formatMessage(messages.clearAll)}
3857
+ />
3858
+ ) : null
3859
+ }
3791
3860
  </CapRow>
3792
3861
  );
3793
3862
  }
@@ -3798,6 +3867,7 @@ return (<div>
3798
3867
  setLineFilter = (e) => this.setState({lineFilter: e.target.value});
3799
3868
  setSMSFilter = (e) => this.setState({smsFilter: e.target.value});
3800
3869
  setZaloStatus = (value) => this.setState({selectedZaloStatus: value?.item?.props?.value});
3870
+ setInAppLayout = (value) => this.setState({selectedInAppLayout: value?.item?.props?.value});
3801
3871
 
3802
3872
  openCreativesFullMode = () => {
3803
3873
  const channelLowerCase = this.state.channel.toLowerCase();
@@ -4050,6 +4120,21 @@ return (<div>
4050
4120
  )
4051
4121
  : () => null
4052
4122
  }
4123
+ {
4124
+ channel.toUpperCase() === INAPP && (
4125
+ <div className="inapp-filters">
4126
+ <CapSelectFilter
4127
+ placement="bottomLeft"
4128
+ data={INAPP_LAYOUT_OPTIONS}
4129
+ onSelect={this.setInAppLayout}
4130
+ selectedValue={this.state.selectedInAppLayout}
4131
+ placeholder="Layout"
4132
+ showBadge
4133
+ width="120px"
4134
+ />
4135
+ </div>
4136
+ )
4137
+ }
4053
4138
  <div style={{display: "flex", justifyContent: "space-between", alignItems: 'center'}}>
4054
4139
  {
4055
4140
  this.state?.channel?.toLowerCase() === WHATSAPP_LOWERCASE && (isWhatsappCountExeeded)? (
@@ -534,6 +534,10 @@ export default defineMessages({
534
534
  id: `${scope}.rcsOnlyApprovedTemplates`,
535
535
  defaultMessage: 'Only "Approved" templates are available here, as you can use those templates to create a message.',
536
536
  },
537
+ "layout": {
538
+ id: `${scope}.layout`,
539
+ defaultMessage: `Layout`,
540
+ },
537
541
  "status": {
538
542
  id: `${scope}.status`,
539
543
  defaultMessage: 'Status',