cap-creatives-ui 8.0.280 → 8.0.321

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 (247) hide show
  1. package/.github/workflows/pr-title-check.yml +88 -0
  2. package/app/constants/unified.js +21 -1
  3. package/app/containers/App/constants.js +0 -1
  4. package/app/containers/Login/test/index.test.js +123 -0
  5. package/app/containers/Login/test/selectors.test.js +165 -0
  6. package/app/initialState.js +0 -2
  7. package/app/services/api.js +6 -0
  8. package/app/services/tests/api.test.js +7 -0
  9. package/app/services/tests/getSchema.test.js +95 -0
  10. package/app/utils/common.js +23 -9
  11. package/app/utils/commonUtils.js +64 -93
  12. package/app/utils/tagValidations.js +83 -219
  13. package/app/utils/templateVarUtils.js +172 -0
  14. package/app/utils/tests/common.test.js +265 -323
  15. package/app/utils/tests/commonUtil.test.js +461 -118
  16. package/app/utils/tests/commonUtils.test.js +581 -0
  17. package/app/utils/tests/messageUtils.test.js +95 -0
  18. package/app/utils/tests/smsCharCount.test.js +304 -0
  19. package/app/utils/tests/smsCharCountV2.test.js +213 -10
  20. package/app/utils/tests/tagValidations.test.js +474 -357
  21. package/app/utils/tests/templateVarUtils.test.js +160 -0
  22. package/app/v2Components/CapDeviceContent/index.js +10 -7
  23. package/app/v2Components/CapTagList/index.js +32 -24
  24. package/app/v2Components/CapTagList/style.scss +48 -0
  25. package/app/v2Components/CapTagListWithInput/__tests__/CapTagListWithInput.test.js +63 -0
  26. package/app/v2Components/CapTagListWithInput/index.js +8 -0
  27. package/app/v2Components/CapWhatsappCTA/index.js +2 -0
  28. package/app/v2Components/CapWhatsappCarouselButton/index.js +32 -14
  29. package/app/v2Components/CapWhatsappCarouselButton/tests/index.test.js +120 -2
  30. package/app/v2Components/CommonTestAndPreview/CustomValuesEditor.js +70 -49
  31. package/app/v2Components/CommonTestAndPreview/DeliverySettings/DeliverySettings.scss +39 -0
  32. package/app/v2Components/CommonTestAndPreview/DeliverySettings/ModifyDeliverySettings.js +606 -0
  33. package/app/v2Components/CommonTestAndPreview/DeliverySettings/ModifyDeliverySettings.scss +36 -0
  34. package/app/v2Components/CommonTestAndPreview/DeliverySettings/constants.js +79 -0
  35. package/app/v2Components/CommonTestAndPreview/DeliverySettings/index.js +314 -0
  36. package/app/v2Components/CommonTestAndPreview/DeliverySettings/messages.js +141 -0
  37. package/app/v2Components/CommonTestAndPreview/DeliverySettings/utils/parseSenderDetailsResponse.js +156 -0
  38. package/app/v2Components/CommonTestAndPreview/SendTestMessage.js +57 -1
  39. package/app/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +20 -1
  40. package/app/v2Components/CommonTestAndPreview/UnifiedPreview/index.js +133 -4
  41. package/app/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +210 -4
  42. package/app/v2Components/CommonTestAndPreview/actions.js +20 -0
  43. package/app/v2Components/CommonTestAndPreview/constants.js +57 -1
  44. package/app/v2Components/CommonTestAndPreview/index.js +878 -156
  45. package/app/v2Components/CommonTestAndPreview/messages.js +41 -3
  46. package/app/v2Components/CommonTestAndPreview/previewApiUtils.js +59 -0
  47. package/app/v2Components/CommonTestAndPreview/reducer.js +47 -0
  48. package/app/v2Components/CommonTestAndPreview/sagas.js +75 -5
  49. package/app/v2Components/CommonTestAndPreview/selectors.js +51 -0
  50. package/app/v2Components/CommonTestAndPreview/tests/CustomValuesEditor.test.js +352 -0
  51. package/app/v2Components/CommonTestAndPreview/tests/DeliverySettings/ModifyDeliverySettings.test.js +1156 -0
  52. package/app/v2Components/CommonTestAndPreview/tests/DeliverySettings/index.test.js +334 -0
  53. package/app/v2Components/CommonTestAndPreview/tests/DeliverySettings/utils/parseSenderDetailsResponse.test.js +576 -0
  54. package/app/v2Components/CommonTestAndPreview/tests/SendTestMessage.test.js +156 -0
  55. package/app/v2Components/CommonTestAndPreview/tests/UnifiedPreview/index.test.js +199 -1
  56. package/app/v2Components/CommonTestAndPreview/tests/actions.test.js +50 -0
  57. package/app/v2Components/CommonTestAndPreview/tests/constants.test.js +18 -7
  58. package/app/v2Components/CommonTestAndPreview/tests/index.test.js +914 -5
  59. package/app/v2Components/CommonTestAndPreview/tests/previewApiUtils.test.js +67 -0
  60. package/app/v2Components/CommonTestAndPreview/tests/reducer.test.js +118 -0
  61. package/app/v2Components/CommonTestAndPreview/tests/sagas.test.js +146 -378
  62. package/app/v2Components/CommonTestAndPreview/tests/selectors.test.js +146 -0
  63. package/app/v2Components/ErrorInfoNote/index.js +24 -26
  64. package/app/v2Components/FormBuilder/index.js +182 -204
  65. package/app/v2Components/FormBuilder/messages.js +4 -8
  66. package/app/v2Components/HtmlEditor/HTMLEditor.js +7 -6
  67. package/app/v2Components/HtmlEditor/__tests__/HTMLEditor.apiErrors.test.js +1 -1
  68. package/app/v2Components/HtmlEditor/__tests__/HTMLEditor.test.js +928 -17
  69. package/app/v2Components/HtmlEditor/components/CodeEditorPane/index.js +4 -2
  70. package/app/v2Components/HtmlEditor/hooks/__tests__/useValidation.test.js +452 -3
  71. package/app/v2Components/HtmlEditor/hooks/useValidation.js +12 -9
  72. package/app/v2Components/HtmlEditor/utils/__tests__/htmlValidator.enhanced.test.js +132 -0
  73. package/app/v2Components/HtmlEditor/utils/htmlValidator.js +4 -2
  74. package/app/v2Components/SmsFallback/SmsFallbackLocalSelector.js +87 -0
  75. package/app/v2Components/SmsFallback/constants.js +73 -0
  76. package/app/v2Components/SmsFallback/index.js +956 -0
  77. package/app/v2Components/SmsFallback/index.scss +265 -0
  78. package/app/v2Components/SmsFallback/messages.js +78 -0
  79. package/app/v2Components/SmsFallback/smsFallbackUtils.js +107 -0
  80. package/app/v2Components/SmsFallback/tests/SmsFallbackLocalSelector.test.js +50 -0
  81. package/app/v2Components/SmsFallback/tests/rcsSmsFallback.acceptance.test.js +147 -0
  82. package/app/v2Components/SmsFallback/tests/smsFallbackHandlers.test.js +304 -0
  83. package/app/v2Components/SmsFallback/tests/smsFallbackUi.test.js +197 -0
  84. package/app/v2Components/SmsFallback/tests/smsFallbackUtils.test.js +261 -0
  85. package/app/v2Components/SmsFallback/tests/useLocalTemplateList.test.js +422 -0
  86. package/app/v2Components/SmsFallback/useLocalTemplateList.js +92 -0
  87. package/app/v2Components/TestAndPreviewSlidebox/index.js +22 -1
  88. package/app/v2Components/TestAndPreviewSlidebox/sagas.js +11 -4
  89. package/app/v2Components/TestAndPreviewSlidebox/tests/saga.test.js +3 -1
  90. package/app/v2Components/VarSegmentMessageEditor/constants.js +2 -0
  91. package/app/v2Components/VarSegmentMessageEditor/index.js +125 -0
  92. package/app/v2Components/VarSegmentMessageEditor/index.scss +46 -0
  93. package/app/v2Containers/BeeEditor/index.js +3 -0
  94. package/app/v2Containers/BeePopupEditor/index.js +9 -2
  95. package/app/v2Containers/Cap/mockData.js +0 -14
  96. package/app/v2Containers/Cap/reducer.js +3 -55
  97. package/app/v2Containers/Cap/tests/reducer.test.js +0 -102
  98. package/app/v2Containers/CommunicationFlow/CommunicationFlow.js +291 -0
  99. package/app/v2Containers/CommunicationFlow/CommunicationFlow.scss +25 -0
  100. package/app/v2Containers/CommunicationFlow/Tests/CommunicationFlow.test.js +255 -0
  101. package/app/v2Containers/CommunicationFlow/constants.js +200 -0
  102. package/app/v2Containers/CommunicationFlow/index.js +102 -0
  103. package/app/v2Containers/CommunicationFlow/messages.js +346 -0
  104. package/app/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/ChannelSelectionStep.js +522 -0
  105. package/app/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/ChannelSelectionStep.scss +170 -0
  106. package/app/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/Tests/ChannelSelectionStep.test.js +796 -0
  107. package/app/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/index.js +5 -0
  108. package/app/v2Containers/CommunicationFlow/steps/CommunicationStrategyStep/CommunicationStrategyStep.js +95 -0
  109. package/app/v2Containers/CommunicationFlow/steps/CommunicationStrategyStep/Tests/CommunicationStrategyStep.test.js +133 -0
  110. package/app/v2Containers/CommunicationFlow/steps/CommunicationStrategyStep/index.js +5 -0
  111. package/app/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/DeliverySettingsSection.js +289 -0
  112. package/app/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/DeliverySettingsSection.scss +70 -0
  113. package/app/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/SenderDetails.js +319 -0
  114. package/app/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/SenderDetails.scss +69 -0
  115. package/app/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/DeliverySettingsSection.test.js +616 -0
  116. package/app/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/SenderDetails.test.js +577 -0
  117. package/app/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/deliverySettingsConfig.test.js +1111 -0
  118. package/app/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/deliverySettingsConfig.js +696 -0
  119. package/app/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/index.js +7 -0
  120. package/app/v2Containers/CommunicationFlow/steps/DynamicControlsStep/DynamicControlsStep.js +102 -0
  121. package/app/v2Containers/CommunicationFlow/steps/DynamicControlsStep/DynamicControlsStep.scss +36 -0
  122. package/app/v2Containers/CommunicationFlow/steps/DynamicControlsStep/Tests/DynamicControlsStep.test.js +91 -0
  123. package/app/v2Containers/CommunicationFlow/steps/DynamicControlsStep/index.js +5 -0
  124. package/app/v2Containers/CommunicationFlow/steps/MessageTypeStep/MessageTypeStep.js +86 -0
  125. package/app/v2Containers/CommunicationFlow/steps/MessageTypeStep/Tests/MessageTypeStep.test.js +100 -0
  126. package/app/v2Containers/CommunicationFlow/steps/MessageTypeStep/index.js +5 -0
  127. package/app/v2Containers/CommunicationFlow/utils/getEnabledSteps.js +30 -0
  128. package/app/v2Containers/CreativesContainer/CreativesSlideBoxWrapper.js +43 -0
  129. package/app/v2Containers/CreativesContainer/SlideBoxContent.js +127 -11
  130. package/app/v2Containers/CreativesContainer/SlideBoxFooter.js +62 -9
  131. package/app/v2Containers/CreativesContainer/SlideBoxHeader.js +29 -4
  132. package/app/v2Containers/CreativesContainer/constants.js +24 -0
  133. package/app/v2Containers/CreativesContainer/embeddedSlideboxUtils.js +67 -0
  134. package/app/v2Containers/CreativesContainer/index.js +346 -71
  135. package/app/v2Containers/CreativesContainer/index.scss +51 -1
  136. package/app/v2Containers/CreativesContainer/messages.js +12 -0
  137. package/app/v2Containers/CreativesContainer/tests/SlideBoxContent.localTemplates.test.js +90 -0
  138. package/app/v2Containers/CreativesContainer/tests/SlideBoxContent.test.js +69 -1
  139. package/app/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +443 -0
  140. package/app/v2Containers/CreativesContainer/tests/SlideBoxHeader.test.js +110 -0
  141. package/app/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +147 -4
  142. package/app/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxHeader.test.js.snap +363 -0
  143. package/app/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +57 -10
  144. package/app/v2Containers/CreativesContainer/tests/embeddedSlideboxUtils.test.js +258 -0
  145. package/app/v2Containers/CreativesContainer/tests/index.test.js +71 -9
  146. package/app/v2Containers/CreativesContainer/tests/useLocalTemplatesProp.test.js +125 -0
  147. package/app/v2Containers/Email/index.js +2 -5
  148. package/app/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +58 -77
  149. package/app/v2Containers/EmailWrapper/components/EmailWrapperView.js +3 -0
  150. package/app/v2Containers/EmailWrapper/components/__tests__/EmailHTMLEditor.test.js +158 -89
  151. package/app/v2Containers/EmailWrapper/components/__tests__/EmailWrapperView.test.js +16 -1
  152. package/app/v2Containers/EmailWrapper/hooks/useEmailWrapper.js +17 -12
  153. package/app/v2Containers/EmailWrapper/index.js +4 -0
  154. package/app/v2Containers/EmailWrapper/tests/useEmailWrapper.edgeCases.test.js +1 -0
  155. package/app/v2Containers/EmailWrapper/tests/useEmailWrapper.test.js +133 -0
  156. package/app/v2Containers/FTP/index.js +2 -51
  157. package/app/v2Containers/FTP/messages.js +0 -4
  158. package/app/v2Containers/InApp/__tests__/InAppHTMLEditor.test.js +110 -155
  159. package/app/v2Containers/InApp/index.js +297 -118
  160. package/app/v2Containers/InApp/tests/index.test.js +17 -6
  161. package/app/v2Containers/InApp/tests/mockData.js +1 -1
  162. package/app/v2Containers/InAppWrapper/hooks/__tests__/useInAppWrapper.test.js +19 -0
  163. package/app/v2Containers/InAppWrapper/hooks/useInAppWrapper.js +3 -0
  164. package/app/v2Containers/InAppWrapper/index.js +3 -0
  165. package/app/v2Containers/InappAdvance/index.js +5 -104
  166. package/app/v2Containers/InappAdvance/tests/index.test.js +2 -0
  167. package/app/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/content.test.js.snap +24 -3
  168. package/app/v2Containers/Line/Container/Text/index.js +0 -1
  169. package/app/v2Containers/MobilePush/Create/index.js +105 -28
  170. package/app/v2Containers/MobilePush/Create/messages.js +4 -0
  171. package/app/v2Containers/MobilePush/Edit/index.js +250 -68
  172. package/app/v2Containers/MobilePush/Edit/messages.js +4 -0
  173. package/app/v2Containers/MobilePushNew/components/PlatformContentFields.js +36 -12
  174. package/app/v2Containers/MobilePushNew/components/tests/PlatformContentFields.test.js +68 -27
  175. package/app/v2Containers/MobilePushNew/index.js +78 -35
  176. package/app/v2Containers/MobilePushNew/messages.js +8 -0
  177. package/app/v2Containers/MobilepushWrapper/index.js +11 -1
  178. package/app/v2Containers/Rcs/constants.js +32 -1
  179. package/app/v2Containers/Rcs/index.js +963 -916
  180. package/app/v2Containers/Rcs/index.scss +85 -6
  181. package/app/v2Containers/Rcs/messages.js +10 -1
  182. package/app/v2Containers/Rcs/rcsLibraryHydrationUtils.js +205 -0
  183. package/app/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +41136 -1566
  184. package/app/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap +0 -5
  185. package/app/v2Containers/Rcs/tests/index.test.js +41 -38
  186. package/app/v2Containers/Rcs/tests/mockData.js +38 -0
  187. package/app/v2Containers/Rcs/tests/rcsLibraryHydrationUtils.test.js +251 -0
  188. package/app/v2Containers/Rcs/tests/utils.test.js +379 -1
  189. package/app/v2Containers/Rcs/utils.js +358 -10
  190. package/app/v2Containers/Sms/Create/index.js +122 -39
  191. package/app/v2Containers/Sms/Create/messages.js +4 -0
  192. package/app/v2Containers/Sms/Edit/index.js +37 -3
  193. package/app/v2Containers/Sms/commonMethods.js +3 -6
  194. package/app/v2Containers/Sms/smsFormDataHelpers.js +67 -0
  195. package/app/v2Containers/Sms/tests/commonMethods.test.js +122 -0
  196. package/app/v2Containers/Sms/tests/smsFormDataHelpers.test.js +253 -0
  197. package/app/v2Containers/SmsTrai/Create/index.js +9 -4
  198. package/app/v2Containers/SmsTrai/Create/index.scss +1 -1
  199. package/app/v2Containers/SmsTrai/Edit/constants.js +2 -0
  200. package/app/v2Containers/SmsTrai/Edit/index.js +667 -160
  201. package/app/v2Containers/SmsTrai/Edit/index.scss +121 -0
  202. package/app/v2Containers/SmsTrai/Edit/messages.js +9 -4
  203. package/app/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +4590 -2436
  204. package/app/v2Containers/SmsWrapper/index.js +41 -8
  205. package/app/v2Containers/TagList/index.js +63 -2
  206. package/app/v2Containers/TagList/messages.js +8 -0
  207. package/app/v2Containers/TagList/tests/TagList.test.js +122 -20
  208. package/app/v2Containers/TagList/tests/mockdata.js +17 -0
  209. package/app/v2Containers/Templates/TemplatesActionBar.js +101 -0
  210. package/app/v2Containers/Templates/_templates.scss +61 -2
  211. package/app/v2Containers/Templates/actions.js +11 -0
  212. package/app/v2Containers/Templates/constants.js +2 -0
  213. package/app/v2Containers/Templates/index.js +90 -40
  214. package/app/v2Containers/Templates/reducer.js +3 -1
  215. package/app/v2Containers/Templates/sagas.js +57 -12
  216. package/app/v2Containers/Templates/tests/TemplatesActionBar.test.js +120 -0
  217. package/app/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +1043 -1079
  218. package/app/v2Containers/Templates/tests/reducer.test.js +12 -0
  219. package/app/v2Containers/Templates/tests/sagas.test.js +193 -12
  220. package/app/v2Containers/Templates/tests/smsTemplatesListApi.test.js +180 -0
  221. package/app/v2Containers/Templates/utils/smsTemplatesListApi.js +79 -0
  222. package/app/v2Containers/TemplatesV2/TemplatesV2.style.js +72 -1
  223. package/app/v2Containers/TemplatesV2/index.js +147 -49
  224. package/app/v2Containers/TemplatesV2/tests/TemplatesV2.localTemplates.test.js +131 -0
  225. package/app/v2Containers/Viber/index.js +9 -10
  226. package/app/v2Containers/Viber/index.scss +1 -1
  227. package/app/v2Containers/WebPush/Create/components/BrandIconSection.test.js +264 -0
  228. package/app/v2Containers/WebPush/Create/components/MessageSection.js +78 -19
  229. package/app/v2Containers/WebPush/Create/components/MessageSection.test.js +82 -0
  230. package/app/v2Containers/WebPush/Create/components/__snapshots__/BrandIconSection.test.js.snap +187 -0
  231. package/app/v2Containers/WebPush/Create/components/__snapshots__/MessageSection.test.js.snap +25 -17
  232. package/app/v2Containers/WebPush/Create/hooks/useAiraTriggerPosition.js +80 -0
  233. package/app/v2Containers/WebPush/Create/hooks/useAiraTriggerPosition.test.js +210 -0
  234. package/app/v2Containers/WebPush/Create/hooks/useTagManagement.js +1 -5
  235. package/app/v2Containers/WebPush/Create/hooks/useTagManagement.test.js +0 -7
  236. package/app/v2Containers/WebPush/Create/index.js +36 -6
  237. package/app/v2Containers/WebPush/Create/index.scss +5 -0
  238. package/app/v2Containers/WebPush/Create/messages.js +8 -1
  239. package/app/v2Containers/WebPush/Create/preview/tests/NotificationContainer.test.js +269 -0
  240. package/app/v2Containers/WebPush/Create/utils/validation.js +31 -15
  241. package/app/v2Containers/WebPush/Create/utils/validation.test.js +72 -24
  242. package/app/v2Containers/Whatsapp/index.js +28 -53
  243. package/app/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +26939 -3982
  244. package/app/v2Containers/Whatsapp/tests/index.test.js +172 -0
  245. package/app/v2Containers/Zalo/index.js +5 -11
  246. package/package.json +2 -2
  247. package/version +9 -0
@@ -24,6 +24,7 @@ import {
24
24
  import messages from "../messages";
25
25
  import MediaUploaders from "./MediaUploaders";
26
26
  import CtaButtons from "./CtaButtons";
27
+ import { hasPersonalizationTags } from "../../../utils/commonUtils";
27
28
 
28
29
  const PlatformContentFields = ({
29
30
  deviceType,
@@ -40,8 +41,17 @@ const PlatformContentFields = ({
40
41
  tags,
41
42
  injectedTags,
42
43
  selectedOfferDetails,
44
+ // new prop to disable personalization features for anonymous users
45
+ restrictPersonalization = false,
43
46
  }) => {
44
47
  const { title: titleError, message: messageError } = errors;
48
+
49
+ const titleErrorToShow = titleError || (restrictPersonalization && hasPersonalizationTags(content.title)
50
+ ? formatMessage(messages.personalizationTagsErrorMessage)
51
+ : "");
52
+ const messageErrorToShow = messageError || (restrictPersonalization && hasPersonalizationTags(content.message)
53
+ ? formatMessage(messages.personalizationTagsErrorMessage)
54
+ : "");
45
55
  const {
46
56
  handleTitleChange,
47
57
  handleMessageChange,
@@ -139,14 +149,19 @@ const PlatformContentFields = ({
139
149
  );
140
150
 
141
151
  const getTagList = useCallback(
142
- (index) => (
143
- <TagList
144
- {...tagListProps}
145
- onTagSelect={(value) => onTagSelect(value, index)}
146
- onContextChange={handleOnTagsContextChange}
147
- />
148
- ),
149
- [tagListProps, onTagSelect, handleOnTagsContextChange]
152
+ (index) => {
153
+ const disableMsg = restrictPersonalization ? formatMessage(messages.personalizationNotSupportedAnonymous) : undefined;
154
+ return (
155
+ <TagList
156
+ {...tagListProps}
157
+ disabled={restrictPersonalization}
158
+ disableTooltipMsg={disableMsg}
159
+ onTagSelect={(value) => onTagSelect(value, index)}
160
+ onContextChange={handleOnTagsContextChange}
161
+ />
162
+ );
163
+ },
164
+ [tagListProps, onTagSelect, handleOnTagsContextChange, restrictPersonalization, formatMessage]
150
165
  );
151
166
 
152
167
  const onButtonTagSelect = useCallback(
@@ -178,9 +193,9 @@ const PlatformContentFields = ({
178
193
  size="default"
179
194
  isRequired
180
195
  errorMessage={
181
- titleError && (
196
+ titleErrorToShow && (
182
197
  <CapError className="mobile-push-template-title-error">
183
- {titleError}
198
+ {titleErrorToShow}
184
199
  </CapError>
185
200
  )
186
201
  }
@@ -202,9 +217,9 @@ const PlatformContentFields = ({
202
217
  size="default"
203
218
  isRequired
204
219
  errorMessage={
205
- messageError && (
220
+ messageErrorToShow && (
206
221
  <CapError className="mobile-push-template-message-error">
207
- {messageError}
222
+ {messageErrorToShow}
208
223
  </CapError>
209
224
  )
210
225
  }
@@ -314,6 +329,10 @@ const PlatformContentFields = ({
314
329
  tags={tags}
315
330
  injectedTags={injectedTags}
316
331
  selectedOfferDetails={selectedOfferDetails}
332
+ disabled={restrictPersonalization}
333
+ disableTooltipMsg={
334
+ restrictPersonalization ? formatMessage(messages.personalizationNotSupportedAnonymous) : undefined
335
+ }
317
336
  />
318
337
  </CapRow>
319
338
  <CapInput
@@ -389,6 +408,11 @@ PlatformContentFields.propTypes = {
389
408
  linkProps: PropTypes.object.isRequired,
390
409
  sameContent: PropTypes.bool.isRequired,
391
410
  formatMessage: PropTypes.func.isRequired,
411
+ restrictPersonalization: PropTypes.bool,
412
+ };
413
+
414
+ PlatformContentFields.defaultProps = {
415
+ restrictPersonalization: false,
392
416
  };
393
417
 
394
418
  export default PlatformContentFields;
@@ -11,25 +11,29 @@ import {
11
11
  jest.mock("@capillarytech/cap-ui-library/CapRow", () => ({ children }) => <div>{children}</div>);
12
12
  jest.mock("@capillarytech/cap-ui-library/CapColumn", () => ({ children }) => <div>{children}</div>);
13
13
  jest.mock("@capillarytech/cap-ui-library/CapInput", () => {
14
- const MockCapInput = ({ value, onChange, errorMessage, error, ...props }) => (
14
+ const MockCapInput = ({
15
+ value, onChange, errorMessage, error, ...props
16
+ }) => (
15
17
  <div>
16
18
  <input value={value || ""} onChange={onChange} error={error} {...props} />
17
19
  {(errorMessage || error) && <div data-testid="error-message">{errorMessage || error}</div>}
18
20
  </div>
19
21
  );
20
-
21
- MockCapInput.TextArea = ({ value, onChange, errorMessage, error, ...props }) => (
22
+
23
+ MockCapInput.TextArea = ({
24
+ value, onChange, errorMessage, error, ...props
25
+ }) => (
22
26
  <div>
23
- <textarea
24
- value={value || ""}
25
- onChange={onChange}
27
+ <textarea
28
+ value={value || ""}
29
+ onChange={onChange}
26
30
  data-testid="message-textarea"
27
31
  {...props}
28
32
  />
29
33
  {(errorMessage || error) && <div data-testid="error-message">{errorMessage || error}</div>}
30
34
  </div>
31
35
  );
32
-
36
+
33
37
  return MockCapInput;
34
38
  });
35
39
  jest.mock("@capillarytech/cap-ui-library/CapHeading", () => ({ children }) => <div>{children}</div>);
@@ -56,17 +60,19 @@ jest.mock("@capillarytech/cap-ui-library/CapLabel", () => ({ children }) => <lab
56
60
  jest.mock("@capillarytech/cap-ui-library/CapInfoNote", () => ({ message }) => <div data-testid="info-note">{message}</div>);
57
61
 
58
62
  // Mock child components
59
- jest.mock("../../../TagList", () => ({ onTagSelect, onContextChange, label, ...props }) => (
60
- <div data-testid="tag-list">
61
- <button
62
- type="button"
63
+ jest.mock("../../../TagList", () => ({
64
+ onTagSelect, onContextChange, label, disabled, disableTooltipMsg, ...props
65
+ }) => (
66
+ <div data-testid="tag-list" data-disabled={disabled || false} data-tooltip={disableTooltipMsg || ''}>
67
+ <button
68
+ type="button"
63
69
  onClick={() => onTagSelect && onTagSelect('test_tag')}
64
70
  data-testid="tag-select-button"
65
71
  >
66
72
  Select Tag
67
73
  </button>
68
- <button
69
- type="button"
74
+ <button
75
+ type="button"
70
76
  onClick={() => onContextChange && onContextChange('test_context')}
71
77
  data-testid="tag-context-button"
72
78
  >
@@ -99,6 +105,8 @@ jest.mock("../../messages", () => ({
99
105
  deepLinkKeysPlaceholder: { id: "deepLinkKeysPlaceholder", defaultMessage: "Enter {key}" },
100
106
  deepLinkKeysRequired: { id: "deepLinkKeysRequired", defaultMessage: "Deep link keys are required" },
101
107
  addLabels: { id: "addLabels", defaultMessage: "Add labels" },
108
+ personalizationTagsErrorMessage: { id: "personalizationTagsErrorMessage", defaultMessage: "Personalization tags are not supported for anonymous customers, please remove the tags." },
109
+ personalizationNotSupportedAnonymous: { id: "personalizationNotSupportedAnonymous", defaultMessage: "Personalization tags are not supported for anonymous customers" },
102
110
  }));
103
111
 
104
112
  // Mock constants
@@ -171,6 +179,7 @@ describe("PlatformContentFields", () => {
171
179
  tags: ["tag1", "tag2"],
172
180
  injectedTags: [],
173
181
  selectedOfferDetails: null,
182
+ restrictPersonalization: false,
174
183
  };
175
184
 
176
185
  const renderComponent = (props = {}) => render(
@@ -255,6 +264,38 @@ describe("PlatformContentFields", () => {
255
264
 
256
265
  expect(getByTestId("cap-error")).toHaveTextContent("Message is required");
257
266
  });
267
+
268
+ it("should show inline personalization error when restricted and tokens present", () => {
269
+ const personalizationErrorMsg = "Personalization tags are not supported for anonymous customers";
270
+ const formatMessageStub = jest.fn((msg) => msg?.defaultMessage ?? "");
271
+ const { container } = renderComponent({
272
+ restrictPersonalization: true,
273
+ content: { ...defaultProps.content, message: "Hello {{user}}" },
274
+ formatMessage: formatMessageStub,
275
+ });
276
+ expect(container.textContent).toContain(personalizationErrorMsg);
277
+ expect(formatMessageStub).toHaveBeenCalledWith(
278
+ expect.objectContaining({ defaultMessage: expect.stringContaining("Personalization tags") })
279
+ );
280
+ });
281
+ });
282
+
283
+ describe("Personalization restriction", () => {
284
+ it("disables tag list and shows tooltip when restrictPersonalization true", () => {
285
+ const formatMessageForTooltip = jest.fn((msg) => msg?.defaultMessage ?? "");
286
+ const { getAllByTestId } = renderComponent({
287
+ restrictPersonalization: true,
288
+ formatMessage: formatMessageForTooltip,
289
+ });
290
+ const tagLists = getAllByTestId("tag-list");
291
+ expect(tagLists.length).toBeGreaterThan(0);
292
+ tagLists.forEach((tag) => {
293
+ expect(tag).toHaveAttribute('data-disabled', 'true');
294
+ });
295
+ expect(formatMessageForTooltip).toHaveBeenCalledWith(
296
+ expect.objectContaining({ defaultMessage: "Personalization tags are not supported for anonymous customers" })
297
+ );
298
+ });
258
299
  });
259
300
 
260
301
  describe("Media Type Selection", () => {
@@ -390,7 +431,7 @@ describe("PlatformContentFields", () => {
390
431
  // Deep link keys handling - covering lines 109-120, 134, 297-303
391
432
  it('should handle deep link keys with array from selection', () => {
392
433
  const mockDeepLink = [
393
- { value: 'test-deep-link', keys: ['key1', 'key2'] }
434
+ { value: 'test-deep-link', keys: ['key1', 'key2'] },
394
435
  ];
395
436
  const mockLinkProps = {
396
437
  deepLink: mockDeepLink,
@@ -428,7 +469,7 @@ describe("PlatformContentFields", () => {
428
469
 
429
470
  it('should handle deep link keys with single key from selection', () => {
430
471
  const mockDeepLink = [
431
- { value: 'test-deep-link', keys: 'single-key' }
472
+ { value: 'test-deep-link', keys: 'single-key' },
432
473
  ];
433
474
  const mockLinkProps = {
434
475
  deepLink: mockDeepLink,
@@ -466,7 +507,7 @@ describe("PlatformContentFields", () => {
466
507
 
467
508
  it('should handle deep link keys with no keys from selection but existing keys', () => {
468
509
  const mockDeepLink = [
469
- { value: 'test-deep-link', keys: [] } // No keys from selection
510
+ { value: 'test-deep-link', keys: [] }, // No keys from selection
470
511
  ];
471
512
  const mockLinkProps = {
472
513
  deepLink: mockDeepLink,
@@ -503,7 +544,7 @@ describe("PlatformContentFields", () => {
503
544
 
504
545
  it('should handle deep link keys with no keys at all', () => {
505
546
  const mockDeepLink = [
506
- { value: 'test-deep-link', keys: [] } // No keys from selection
547
+ { value: 'test-deep-link', keys: [] }, // No keys from selection
507
548
  ];
508
549
  const mockLinkProps = {
509
550
  deepLink: mockDeepLink,
@@ -540,7 +581,7 @@ describe("PlatformContentFields", () => {
540
581
 
541
582
  it('should handle deep link keys with string value instead of array', () => {
542
583
  const mockDeepLink = [
543
- { value: 'test-deep-link', keys: 'single-key' }
584
+ { value: 'test-deep-link', keys: 'single-key' },
544
585
  ];
545
586
  const mockLinkProps = {
546
587
  deepLink: mockDeepLink,
@@ -577,7 +618,7 @@ describe("PlatformContentFields", () => {
577
618
 
578
619
  it('should handle deep link keys with undefined deepLinkKeysValue', () => {
579
620
  const mockDeepLink = [
580
- { value: 'test-deep-link', keys: ['key1', 'key2'] }
621
+ { value: 'test-deep-link', keys: ['key1', 'key2'] },
581
622
  ];
582
623
  const mockLinkProps = {
583
624
  deepLink: mockDeepLink,
@@ -615,7 +656,7 @@ describe("PlatformContentFields", () => {
615
656
 
616
657
  it('should handle deep link keys placeholder with fallback', () => {
617
658
  const mockDeepLink = [
618
- { value: 'test-deep-link', keys: ['key1', 'key2'] } // Need keys to trigger the section
659
+ { value: 'test-deep-link', keys: ['key1', 'key2'] }, // Need keys to trigger the section
619
660
  ];
620
661
  const mockLinkProps = {
621
662
  deepLink: mockDeepLink,
@@ -655,7 +696,7 @@ describe("PlatformContentFields", () => {
655
696
 
656
697
  it('should handle deep link keys error display', () => {
657
698
  const mockDeepLink = [
658
- { value: 'test-deep-link', keys: ['key1', 'key2'] }
699
+ { value: 'test-deep-link', keys: ['key1', 'key2'] },
659
700
  ];
660
701
  const mockLinkProps = {
661
702
  deepLink: mockDeepLink,
@@ -727,7 +768,7 @@ describe("PlatformContentFields", () => {
727
768
  describe('Deep link query parameter handling', () => {
728
769
  it('should match deep link with query parameters', () => {
729
770
  const mockDeepLink = [
730
- { value: 'myapp://profile', keys: ['userId'] }
771
+ { value: 'myapp://profile', keys: ['userId'] },
731
772
  ];
732
773
  const mockLinkProps = {
733
774
  deepLink: mockDeepLink,
@@ -766,7 +807,7 @@ describe("PlatformContentFields", () => {
766
807
 
767
808
  it('should not match deep link when no base match exists with query parameters', () => {
768
809
  const mockDeepLink = [
769
- { value: 'myapp://settings', keys: ['theme'] }
810
+ { value: 'myapp://settings', keys: ['theme'] },
770
811
  ];
771
812
  const mockLinkProps = {
772
813
  deepLink: mockDeepLink,
@@ -805,7 +846,7 @@ describe("PlatformContentFields", () => {
805
846
 
806
847
  it('should handle multiple query parameters in deep link value', () => {
807
848
  const mockDeepLink = [
808
- { value: 'testapp://dashboard', keys: ['category', 'filter'] }
849
+ { value: 'testapp://dashboard', keys: ['category', 'filter'] },
809
850
  ];
810
851
  const mockLinkProps = {
811
852
  deepLink: mockDeepLink,
@@ -844,7 +885,7 @@ describe("PlatformContentFields", () => {
844
885
 
845
886
  it('should handle empty query parameters in deep link value', () => {
846
887
  const mockDeepLink = [
847
- { value: 'myapp://search', keys: ['query'] }
888
+ { value: 'myapp://search', keys: ['query'] },
848
889
  ];
849
890
  const mockLinkProps = {
850
891
  deepLink: mockDeepLink,
@@ -860,7 +901,7 @@ describe("PlatformContentFields", () => {
860
901
  linkType: 'DEEP_LINK',
861
902
  };
862
903
 
863
- const { getByText } = render(
904
+ const { getByText } = render(
864
905
  <IntlProvider locale="en">
865
906
  <PlatformContentFields
866
907
  deviceType="ANDROID"
@@ -884,7 +925,7 @@ describe("PlatformContentFields", () => {
884
925
 
885
926
  it('should handle tag selection for external link in buttons', () => {
886
927
  const mockHandleExternalLinkChange = jest.fn();
887
-
928
+
888
929
  // Create a test that actually renders the component and triggers the useCallback
889
930
  const { container } = renderComponent({
890
931
  content: {
@@ -77,7 +77,6 @@ import { getContent } from "../MobilePush/commonMethods";
77
77
  import { getMessageObject } from "../../utils/messageUtils";
78
78
  import { gtmPush } from "../../utils/gtmTrackers";
79
79
  import mobilePushReducer from "./reducer";
80
- import { hasLiquidSupportFeature } from "../../utils/common";
81
80
  import formBuilderMessages from "../../v2Components/FormBuilder/messages";
82
81
  import { validateMobilePushContent } from "../../utils/commonUtils";
83
82
  import { getSingleTab } from "../InApp/utils";
@@ -89,13 +88,14 @@ import { PlatformContentFields } from "./components";
89
88
  import { CREATE, EDIT, TRACK_CREATE_MPUSH } from "../App/constants";
90
89
  import { validateExternalLink, validateDeepLink } from "./utils";
91
90
  import messages from "./messages";
92
- import { EXTERNAL_URL } from "../CreativesContainer/constants";
91
+ import { EXTERNAL_URL, MOBILE_PUSH } from "../CreativesContainer/constants";
93
92
  import createMobilePushPayloadWithIntl from "../../utils/createMobilePushPayload";
94
93
  import { MOBILEPUSH } from "../../v2Components/CapVideoUpload/constants";
95
94
  import { StyledCapTab } from "./style";
96
95
  import TestAndPreviewSlidebox from "../../v2Components/TestAndPreviewSlidebox";
97
- import { MOBILE_PUSH } from "../CreativesContainer/constants";
96
+
98
97
  import creativesMessages from "../CreativesContainer/messages";
98
+ import { error } from "jquery";
99
99
 
100
100
  // Helper function to extract deep link keys from URL where value equals key
101
101
  const extractDeepLinkKeys = (deepLinkValue) => {
@@ -506,6 +506,9 @@ const MobilePushNew = ({
506
506
  isGetFormData,
507
507
  getTemplateDetailsInProgress,
508
508
  onCreateComplete,
509
+ // new flag from parent - when true personalization via tags should be disabled
510
+ restrictPersonalization = false,
511
+ onPersonalizationTokensChange,
509
512
  }) => {
510
513
  const { formatMessage } = intl;
511
514
 
@@ -799,10 +802,9 @@ const MobilePushNew = ({
799
802
  (value) => {
800
803
  let errorTemplateDescMessage = "";
801
804
 
802
- const { unsupportedTags, isBraceError } = validateTags({
805
+ const { isBraceError } = validateTags({
803
806
  content: value,
804
807
  tagsParam: tags,
805
- injectedTagsParams: injectedTags,
806
808
  location,
807
809
  tagModule: getDefaultTags,
808
810
  isFullMode,
@@ -812,14 +814,6 @@ const MobilePushNew = ({
812
814
  messages.emptyTemplateDescErrorMessage
813
815
  );
814
816
  }
815
- if (unsupportedTags?.length > 0) {
816
- errorTemplateDescMessage = formatMessage(
817
- globalMessages.unsupportedTagsValidationError,
818
- {
819
- unsupportedTags,
820
- }
821
- );
822
- }
823
817
  if (isBraceError) {
824
818
  errorTemplateDescMessage = formatMessage(
825
819
  globalMessages.unbalanacedCurlyBraces
@@ -836,6 +830,14 @@ const MobilePushNew = ({
836
830
  if (!value || value.trim() === "") {
837
831
  error = formatMessage(messages.emptyTemplateDescErrorMessage);
838
832
  }
833
+ // personalization restriction check
834
+ if (
835
+ restrictPersonalization
836
+ && value
837
+ && ((value.includes("{{") && value.includes("}}")) || (value.includes("[") && value.includes("]")))
838
+ ) {
839
+ error = formatMessage(creativesMessages.personalizationTokensErrorMessage);
840
+ }
839
841
  return error || "";
840
842
  },
841
843
  [templateDescErrorHandler, formatMessage]
@@ -847,9 +849,17 @@ const MobilePushNew = ({
847
849
  if (!value || value.trim() === "") {
848
850
  error = formatMessage(messages.emptyTemplateDescErrorMessage);
849
851
  }
852
+ // personalization restriction check
853
+ if (
854
+ restrictPersonalization
855
+ && value
856
+ && ((value.includes("{{") && value.includes("}}")) || (value.includes("[") && value.includes("]")))
857
+ ) {
858
+ error = formatMessage(creativesMessages.personalizationTokensErrorMessage);
859
+ }
850
860
  return error || "";
851
861
  },
852
- [templateDescErrorHandler, formatMessage]
862
+ [templateDescErrorHandler, formatMessage, restrictPersonalization]
853
863
  );
854
864
 
855
865
  const handleOnTagsContextChange = useCallback(
@@ -1724,6 +1734,29 @@ const MobilePushNew = ({
1724
1734
  const isAndroidSupported = accountData?.configs?.android === '1';
1725
1735
  const isIosSupported = accountData?.configs?.ios === '1';
1726
1736
 
1737
+ // Notify parent when personalization tokens are added/removed (for anonymous user flow)
1738
+ useEffect(() => {
1739
+ if (!restrictPersonalization || typeof onPersonalizationTokensChange !== 'function') return;
1740
+ const hasToken = (value) => value && (
1741
+ (value.includes('{{') && value.includes('}}'))
1742
+ || (value.includes('[') && value.includes(']'))
1743
+ );
1744
+ const hasTokens = (
1745
+ (isAndroidSupported && (hasToken(androidContent?.title) || hasToken(androidContent?.message)))
1746
+ || (isIosSupported && (hasToken(iosContent?.title) || hasToken(iosContent?.message)))
1747
+ );
1748
+ onPersonalizationTokensChange(hasTokens);
1749
+ }, [
1750
+ restrictPersonalization,
1751
+ androidContent?.title,
1752
+ androidContent?.message,
1753
+ iosContent?.title,
1754
+ iosContent?.message,
1755
+ isAndroidSupported,
1756
+ isIosSupported,
1757
+ onPersonalizationTokensChange,
1758
+ ]);
1759
+
1727
1760
  // Validation logic for template creation/update
1728
1761
  const isAndroidFieldsMissing = isAndroidSupported && (!androidContent?.title?.trim() || !androidContent?.message?.trim());
1729
1762
  const isIosFieldsMissing = isIosSupported && (!iosContent?.title?.trim() || !iosContent?.message?.trim());
@@ -2001,6 +2034,11 @@ const MobilePushNew = ({
2001
2034
  tags: tags || [],
2002
2035
  injectedTags: injectedTags || {},
2003
2036
  selectedOfferDetails,
2037
+ // disable tag button when personalization is restricted
2038
+ disabled: restrictPersonalization,
2039
+ disableTooltipMsg: restrictPersonalization
2040
+ ? formatMessage(creativesMessages.personalizationNotSupportedAnonymous)
2041
+ : undefined,
2004
2042
  };
2005
2043
 
2006
2044
  // Fix nested ternary for videoAssetList and gifAssetList
@@ -2101,6 +2139,7 @@ const MobilePushNew = ({
2101
2139
  tags={tags}
2102
2140
  injectedTags={injectedTags}
2103
2141
  selectedOfferDetails={selectedOfferDetails}
2142
+ restrictPersonalization={restrictPersonalization}
2104
2143
  />
2105
2144
  );
2106
2145
  }, [androidContent, iosContent, androidTitleError, iosTitleError, androidMessageError, iosMessageError, androidExternalLinkError, iosExternalLinkError, androidDeepLinkError, iosDeepLinkError, androidDeepLinkKeysError, iosDeepLinkKeysError, formatMessage, activeTab, imageSrc, isFullMode, imageData, androidAssetList, iosAssetList, videoState, videoData, location, tags, injectedTags, selectedOfferDetails, primaryButtonAndroid, secondaryButtonAndroid, primaryButtonIos, secondaryButtonIos, ctaData, deepLink, mobilePushActions, carouselActiveTabIndex, carouselLinkErrors, handleTitleChange, handleMessageChange, handleMediaTypeChange, handleActionOnClickChange, handleLinkTypeChange, handleDeepLinkChange, handleDeepLinkKeysChange, handleExternalLinkChange, onTagSelect, handleOnTagsContextChange, setUpdateMpushImageSrc, updateOnMpushImageReUpload, setUpdateMpushVideoSrc, updateOnMpushVideoReUpload, clearImageDataByMediaType, handleCarouselDataChange, updateCarouselLinkError, sameContent, updateHandler, deleteHandler]
@@ -2136,6 +2175,9 @@ const MobilePushNew = ({
2136
2175
  return panes;
2137
2176
  }, [isAndroidSupported, isIosSupported, renderContentFields, formatMessage]);
2138
2177
 
2178
+ const errorInTitle = activeTab === ANDROID ? androidTitleError : iosTitleError;
2179
+ const errorInMessage = activeTab === ANDROID ? androidMessageError : iosMessageError;
2180
+
2139
2181
  // Save button disabled logic: only check enabled platforms
2140
2182
  const isSaveDisabled = (
2141
2183
  (isAndroidSupported && (!androidContent?.title?.trim() || !androidContent?.message?.trim()))
@@ -2143,6 +2185,7 @@ const MobilePushNew = ({
2143
2185
  || templateNameError
2144
2186
  || Object.values(carouselLinkErrors).some((error) => error !== null && error !== "")
2145
2187
  || !isCarouselDataValid()
2188
+ || errorInTitle || errorInMessage
2146
2189
  );
2147
2190
 
2148
2191
  // Validation in handleSave: only show errors for enabled platforms
@@ -2599,6 +2642,12 @@ const MobilePushNew = ({
2599
2642
  };
2600
2643
  const onSuccess = () => handleSave();
2601
2644
 
2645
+ // When liquid is enabled and isFullMode is true, skip liquid validation and proceed directly
2646
+ if (isFullMode) {
2647
+ onSuccess();
2648
+ return;
2649
+ }
2650
+
2602
2651
  validateMobilePushContent([androidContent, iosContent], {
2603
2652
  currentTab: activeTab === ANDROID ? 1 : 2,
2604
2653
  onError,
@@ -2606,20 +2655,6 @@ const MobilePushNew = ({
2606
2655
  getLiquidTags: globalActionsProps.getLiquidTags,
2607
2656
  formatMessage,
2608
2657
  messages: formBuilderMessages,
2609
- tagLookupMap: metaEntities?.tagLookupMap || {},
2610
- eventContextTags: metaEntities?.eventContextTags || [],
2611
- isLiquidFlow: hasLiquidSupportFeature(),
2612
- forwardedTags: {},
2613
- skipTags: (tag) => {
2614
- const skipRegexes = [
2615
- /dynamic_expiry_date_after_\d+_days\.FORMAT_\d/,
2616
- /unsubscribe\(#[a-zA-Z\d]{6}\)/,
2617
- /Link_to_[a-zA-z]/,
2618
- /SURVEY.*\.TOKEN/,
2619
- /^[A-Za-z].*\([a-zA-Z\d]*\)/,
2620
- ];
2621
- return skipRegexes.some((regex) => regex.test(tag));
2622
- },
2623
2658
  singleTab: getSingleTab(accountData),
2624
2659
  });
2625
2660
  }, [
@@ -2628,12 +2663,10 @@ const MobilePushNew = ({
2628
2663
  activeTab,
2629
2664
  globalActionsProps,
2630
2665
  formatMessage,
2631
- metaEntities,
2632
2666
  accountData,
2667
+ isFullMode,
2633
2668
  ]);
2634
2669
 
2635
- const isLiquidFlow = hasLiquidSupportFeature();
2636
-
2637
2670
  useEffect(() => {
2638
2671
  // Always map to { label } for both platforms
2639
2672
  const newButtons = Array.isArray(ctaData)
@@ -2880,16 +2913,22 @@ const MobilePushNew = ({
2880
2913
  setShowTestAndPreviewSlidebox(false);
2881
2914
  }, []);
2882
2915
 
2883
- // Add useEffect to handle isGetFormData prop changes
2916
+ // Add useEffect to handle isGetFormData prop changes (e.g. Done clicked in footer)
2917
+ // In library mode: run liquid validation (extractTags) first; on success liquidMiddleWare calls handleSave
2918
+ // In full mode: call handleSave directly
2884
2919
  useEffect(() => {
2885
2920
  if (isGetFormData) {
2886
- handleSave();
2921
+ if (!isFullMode) {
2922
+ liquidMiddleWare();
2923
+ } else {
2924
+ handleSave();
2925
+ }
2887
2926
  // Reset the flag to prevent infinite loop
2888
2927
  if (onValidationFail) {
2889
2928
  onValidationFail();
2890
2929
  }
2891
2930
  }
2892
- }, [isGetFormData, handleSave, onValidationFail]);
2931
+ }, [isGetFormData, handleSave, onValidationFail, isFullMode, liquidMiddleWare]);
2893
2932
 
2894
2933
  // Add message event listener to handle parent communication (like old MobilePush components)
2895
2934
  useEffect(() => {
@@ -3030,7 +3069,8 @@ const MobilePushNew = ({
3030
3069
  <CapButton
3031
3070
  type="primary"
3032
3071
  onClick={() => {
3033
- if (isLiquidFlow) {
3072
+ // Liquid validation (extractTags) only in library mode
3073
+ if (!isFullMode) {
3034
3074
  liquidMiddleWare();
3035
3075
  } else {
3036
3076
  handleSave();
@@ -3046,6 +3086,7 @@ const MobilePushNew = ({
3046
3086
  className="mobilepush-test-preview-btn"
3047
3087
  type="secondary"
3048
3088
  style={{ marginLeft: '8px' }}
3089
+ disabled={isSaveDisabled}
3049
3090
  >
3050
3091
  <FormattedMessage {...creativesMessages.testAndPreview} />
3051
3092
  </CapButton>
@@ -3106,6 +3147,7 @@ MobilePushNew.propTypes = {
3106
3147
  isGetFormData: PropTypes.bool,
3107
3148
  getTemplateDetailsInProgress: PropTypes.bool,
3108
3149
  onCreateComplete: PropTypes.func,
3150
+ onPersonalizationTokensChange: PropTypes.func,
3109
3151
  };
3110
3152
 
3111
3153
  MobilePushNew.defaultProps = {
@@ -3136,6 +3178,7 @@ MobilePushNew.defaultProps = {
3136
3178
  isGetFormData: false,
3137
3179
  getTemplateDetailsInProgress: false,
3138
3180
  onCreateComplete: () => {},
3181
+ onPersonalizationTokensChange: undefined,
3139
3182
  };
3140
3183
 
3141
3184
  const mapStateToProps = createStructuredSelector({
@@ -269,4 +269,12 @@ export default defineMessages({
269
269
  id: `${scope}.gifFileTypeError`,
270
270
  defaultMessage: 'Only GIF files are allowed',
271
271
  },
272
+ personalizationTagsErrorMessage: {
273
+ id: `${scope}.personalizationTagsErrorMessage`,
274
+ defaultMessage: 'Personalization tags are not supported for anonymous customers, please remove the tags.',
275
+ },
276
+ personalizationNotSupportedAnonymous: {
277
+ id: `${scope}.personalizationNotSupportedAnonymous`,
278
+ defaultMessage: `Personalization tags are not supported for anonymous customers`,
279
+ },
272
280
  });
@@ -72,7 +72,7 @@ export class MobilepushWrapper extends React.Component { // eslint-disable-line
72
72
  }
73
73
 
74
74
  render() {
75
- const {mobilePushCreateMode, step, getFormData, setIsLoadingContent, isGetFormData, query, isFullMode, showTemplateName, type, onValidationFail, onPreviewContentClicked, onTestContentClicked, templateData, eventContextTags = [], showTestAndPreviewSlidebox, handleTestAndPreview, handleCloseTestAndPreview} = this.props;
75
+ const {mobilePushCreateMode, step, getFormData, getLiquidTags, setIsLoadingContent, isGetFormData, query, isFullMode, showTemplateName, type, onValidationFail, onPreviewContentClicked, onTestContentClicked, templateData, eventContextTags = [], waitEventContextTags = {},showTestAndPreviewSlidebox, handleTestAndPreview, handleCloseTestAndPreview, restrictPersonalization, isAnonymousType, onPersonalizationTokensChange} = this.props;
76
76
  const {templateName} = this.state;
77
77
  const isShowMobilepushCreate = !isEmpty(mobilePushCreateMode);
78
78
  return (
@@ -102,6 +102,7 @@ export class MobilepushWrapper extends React.Component { // eslint-disable-line
102
102
  <div>
103
103
  {isShowMobilepushCreate && <MobilepushCreate
104
104
  getFormLibraryData={getFormData}
105
+ getLiquidTags={getLiquidTags}
105
106
  setIsLoadingContent={setIsLoadingContent}
106
107
  defaultData={{"template-name": templateName}}
107
108
  location={{
@@ -120,10 +121,14 @@ export class MobilepushWrapper extends React.Component { // eslint-disable-line
120
121
  templateData={templateData}
121
122
  hideTestAndPreviewBtn={this.props.hideTestAndPreviewBtn}
122
123
  eventContextTags={eventContextTags}
124
+ waitEventContextTags={waitEventContextTags}
123
125
  showLiquidErrorInFooter={this.props.showLiquidErrorInFooter}
124
126
  showTestAndPreviewSlidebox={showTestAndPreviewSlidebox}
125
127
  handleTestAndPreview={handleTestAndPreview}
126
128
  handleCloseTestAndPreview={handleCloseTestAndPreview}
129
+ restrictPersonalization={restrictPersonalization}
130
+ isAnonymousType={isAnonymousType}
131
+ onPersonalizationTokensChange={onPersonalizationTokensChange}
127
132
  />
128
133
 
129
134
 
@@ -140,6 +145,7 @@ MobilepushWrapper.propTypes = {
140
145
  mobilePushCreateMode: PropTypes.string,
141
146
  step: PropTypes.string,
142
147
  getFormData: PropTypes.string,
148
+ getLiquidTags: PropTypes.func,
143
149
  setIsLoadingContent: PropTypes.func,
144
150
  onEnterTemplateName: PropTypes.func,
145
151
  onRemoveTemplateName: PropTypes.func,
@@ -150,10 +156,14 @@ MobilepushWrapper.propTypes = {
150
156
  type: PropTypes.string,
151
157
  onValidationFail: PropTypes.func,
152
158
  eventContextTags: PropTypes.array,
159
+ waitEventContextTags: PropTypes.object,
153
160
  showLiquidErrorInFooter: PropTypes.func,
154
161
  showTestAndPreviewSlidebox: PropTypes.bool,
155
162
  handleTestAndPreview: PropTypes.func,
156
163
  handleCloseTestAndPreview: PropTypes.func,
164
+ restrictPersonalization: PropTypes.bool,
165
+ isAnonymousType: PropTypes.bool,
166
+ onPersonalizationTokensChange: PropTypes.func,
157
167
  };
158
168
 
159
169