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
@@ -0,0 +1,88 @@
1
+ name: PR Title Check for JIRA Ticket
2
+
3
+ on:
4
+ pull_request:
5
+ types:
6
+ - opened
7
+ - synchronize
8
+ branches:
9
+ - main
10
+ - master
11
+
12
+ jobs:
13
+ check-pr-title:
14
+ runs-on: ubuntu-latest
15
+ permissions:
16
+ pull-requests: read
17
+ steps:
18
+ - name: Fetch current PR title
19
+ id: pr-title
20
+ run: |
21
+ set -euo pipefail
22
+
23
+ PR_NUMBER="${{ github.event.pull_request.number }}"
24
+ if [ -z "${PR_NUMBER}" ]; then
25
+ echo "❌ Error: PR number is not available"
26
+ exit 1
27
+ fi
28
+
29
+ echo "Fetching PR #${PR_NUMBER} details..."
30
+ API_RESPONSE=$(curl -s -w "\n%{http_code}" \
31
+ -H "Authorization: Bearer ${{ github.token }}" \
32
+ -H "Accept: application/vnd.github.v3+json" \
33
+ "https://api.github.com/repos/${{ github.repository }}/pulls/${PR_NUMBER}")
34
+
35
+ HTTP_CODE=$(echo "$API_RESPONSE" | tail -n1)
36
+ API_BODY=$(echo "$API_RESPONSE" | sed '$d')
37
+
38
+ if [ "${HTTP_CODE}" != "200" ]; then
39
+ echo "❌ Error: Failed to fetch PR details (HTTP ${HTTP_CODE})"
40
+ echo "Response: ${API_BODY}"
41
+ exit 1
42
+ fi
43
+
44
+ PR_TITLE=$(echo "$API_BODY" | python3 -c "
45
+ import sys, json
46
+ try:
47
+ data = json.load(sys.stdin)
48
+ title = data.get('title', '')
49
+ if not title:
50
+ print('ERROR: PR title is empty', file=sys.stderr)
51
+ sys.exit(1)
52
+ print(title)
53
+ except json.JSONDecodeError as e:
54
+ print(f'ERROR: Invalid JSON response: {e}', file=sys.stderr)
55
+ sys.exit(1)
56
+ except KeyError as e:
57
+ print(f'ERROR: Missing field in response: {e}', file=sys.stderr)
58
+ sys.exit(1)
59
+ except Exception as e:
60
+ print(f'ERROR: Unexpected error: {e}', file=sys.stderr)
61
+ sys.exit(1)
62
+ ")
63
+
64
+ if [ -z "${PR_TITLE}" ]; then
65
+ echo "❌ Error: PR title is empty"
66
+ exit 1
67
+ fi
68
+
69
+ echo "title=${PR_TITLE}" >> $GITHUB_OUTPUT
70
+ echo "Current PR title: ${PR_TITLE}"
71
+ - name: Validate PR title contains CAP-xxxx
72
+ run: |
73
+ set -euo pipefail
74
+
75
+ PR_TITLE="${{ steps.pr-title.outputs.title }}"
76
+
77
+ if [ -z "${PR_TITLE}" ]; then
78
+ echo "❌ Error: PR title is not available from previous step"
79
+ exit 1
80
+ fi
81
+
82
+ if ! echo "${PR_TITLE}" | grep -qE 'CAP-[0-9]+'; then
83
+ echo "❌ PR title must contain a JIRA ticket id like CAP-1234"
84
+ echo "Current PR title: ${PR_TITLE}"
85
+ exit 1
86
+ else
87
+ echo "✅ PR title is valid: ${PR_TITLE}"
88
+ fi
@@ -43,8 +43,8 @@ export const HOSPITALITY_BASED_SCOPE = 'HOSPITALITY_BASED_SCOPE';
43
43
  export const REGISTRATION_CUSTOM_FIELD = 'Registration custom fields';
44
44
  export const GIFT_CARDS = 'GIFT_CARDS';
45
45
  export const PROMO_ENGINE = 'PROMO_ENGINE';
46
- export const LIQUID_SUPPORT = 'ENABLE_LIQUID_SUPPORT';
47
46
  export const ENABLE_NEW_MPUSH = 'ENABLE_NEW_MPUSH';
47
+ export const ENABLE_NEW_EDITOR_FLOW_INAPP = 'ENABLE_NEW_EDITOR_FLOW_INAPP';
48
48
  export const SUPPORT_CK_EDITOR = 'SUPPORT_CK_EDITOR';
49
49
  export const CUSTOM_TAG = 'CustomTagMessage';
50
50
  export const CUSTOMER_EXTENDED_FIELD = 'Customer extended fields';
@@ -60,6 +60,7 @@ export const AI_CONTENT_BOT_DISABLED = 'AI_CONTENT_BOT_DISABLED';
60
60
  export const AI_DOCUMENTATION_BOT_ENABLED = 'AI_DOCUMENTATION_BOT_ENABLED';
61
61
  export const ENABLE_NEW_LEFT_NAVIGATION = 'ENABLE_NEW_LEFT_NAVIGATION';
62
62
  export const ENABLE_PRODUCT_SUPPORT_VIDEOS = 'ENABLE_PRODUCT_SUPPORT_VIDEOS';
63
+ export const SUPPORT_ENGAGEMENT_MODULE = 'SUPPORT_ENGAGEMENT_MODULE';
63
64
  export const EMBEDDED = 'embedded';
64
65
  // --- Tag/Validation Constants ---
65
66
  export const CARD_RELATED_TAGS = [
@@ -150,9 +151,28 @@ export const BADGES_ENROLL = 'BADGES_ENROLL';
150
151
  export const BADGES_ISSUE = 'BADGES_ISSUE';
151
152
  export const CUSTOMER_BARCODE_TAG = 'customer_barcode';
152
153
  export const COPY_OF = 'Copy of';
154
+ export const UNSUBSCRIBE_TAG = 'unsubscribe';
155
+ /** Whitespace-tolerant check for {{ unsubscribe }}-style tag in content. */
156
+ export const UNSUBSCRIBE_TAG_REGEX = new RegExp(`\\{\\{\\s*${UNSUBSCRIBE_TAG}\\s*\\}\\}`);
157
+ /** Matches {{ ... }} and captures the inner tag content (for scanning content for tags). */
158
+ export const TAG_CONTENT_REGEX = /{{([^}]+)}}/g;
153
159
  export const ENTRY_TRIGGER_TAG_REGEX = /\bentryTrigger\.\w+(?:\.\w+)?(?:\(\w+\))?/g;
154
160
  export const SKIP_TAGS_REGEX_GROUPS = ["dynamic_expiry_date_after_\\d+_days.FORMAT_\\d", "unsubscribe\\(#[a-zA-Z\\d]{6}\\)", "Link_to_[a-zA-Z]", "SURVEY.*.TOKEN", "^[A-Za-z].*\\([a-zA-Z\\d]*\\)", "referral_unique_(code|url).*userid"];
155
161
 
162
+ // --- Template variable tokens (`{{var}}`, DLT `{#var#}`) ---
163
+ /** Global: all `{{…}}` placeholders in a string. */
164
+ export const DEFAULT_MUSTACHE_VAR_REGEX = /\{\{[^}]+\}\}/g;
165
+ /** Global: `{{…}}` or DLT `{#…#}` tokens (SMS combined mode). */
166
+ export const COMBINED_SMS_TEMPLATE_VAR_REGEX = /\{\{[^}]+\}\}|\{\#[^#]*\#\}/g;
167
+ /** Full-string check: one mustache token. */
168
+ export const MUSTACHE_VAR_TOKEN_FULL_STRING_REGEX = /^\{\{[^}]+\}\}$/;
169
+ /** Full-string check: one DLT hash token. */
170
+ export const DLT_HASH_VAR_TOKEN_FULL_STRING_REGEX = /^\{\#[^#]*\#\}$/;
171
+ /** Global with capture group: inner name inside `{{name}}`. */
172
+ export const MUSTACHE_VAR_NAME_CAPTURE_REGEX = /\{\{([^}]+)\}\}/g;
173
+ /** Global with capture group: inner body inside `{#body#}`. */
174
+ export const DLT_VAR_BODY_CAPTURE_REGEX = /\{\#([^#]*)\#\}/g;
175
+
156
176
  export const GET_TRANSLATION_MAPPED = {
157
177
  'en': 'en-US',
158
178
  'zh-cn': 'zh',
@@ -11,7 +11,6 @@ export const HOSPITALITY_BASED_SCOPE = 'HOSPITALITY_BASED_SCOPE';
11
11
  export const REGISTRATION_CUSTOM_FIELD = 'Registration custom fields';
12
12
  export const GIFT_CARDS = 'GIFT_CARDS';
13
13
  export const PROMO_ENGINE = 'PROMO_ENGINE';
14
- export const LIQUID_SUPPORT = 'ENABLE_LIQUID_SUPPORT';
15
14
  export const CUSTOM_TAG = 'CustomTagMessage';
16
15
  export const CUSTOMER_EXTENDED_FIELD = 'Customer extended fields';
17
16
  export const EXTENDED_TAG = 'ExtendedTagMessage';
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Test suite for Login container
3
+ */
4
+ import React from 'react';
5
+ import { shallow, mount } from 'enzyme';
6
+ import { Provider } from 'react-redux';
7
+ import { configureStore } from '@capillarytech/vulcan-react-sdk/utils';
8
+ import { fromJS } from 'immutable';
9
+ import { Router } from 'react-router-dom';
10
+ import createHistory from 'history/createHashHistory';
11
+ import { IntlProvider } from 'react-intl';
12
+ import { Login } from '../index';
13
+ import initialReducer from '../../../initialReducer';
14
+
15
+ const history = createHistory();
16
+
17
+ describe('Login Container', () => {
18
+ let store;
19
+ let props;
20
+
21
+ beforeEach(() => {
22
+ store = configureStore(
23
+ {
24
+ cap: fromJS({
25
+ token: '',
26
+ login_progress: false,
27
+ fetching_userdata: false,
28
+ isError: false,
29
+ }),
30
+ },
31
+ initialReducer,
32
+ history,
33
+ );
34
+
35
+ props = {
36
+ Login: {
37
+ token: '',
38
+ login_progress: false,
39
+ fetching_userdata: false,
40
+ isError: false,
41
+ },
42
+ actions: {
43
+ authenticate: jest.fn(),
44
+ },
45
+ replace: jest.fn(),
46
+ location: {
47
+ query: {},
48
+ },
49
+ router: {
50
+ push: jest.fn(),
51
+ go: jest.fn(),
52
+ },
53
+ };
54
+ });
55
+
56
+ it('should render without crashing', () => {
57
+ const wrapper = shallow(<Login {...props} />);
58
+ expect(wrapper).toBeDefined();
59
+ });
60
+
61
+ it('should render the login container div', () => {
62
+ const wrapper = shallow(<Login {...props} />);
63
+ expect(wrapper.find('.login-container')).toHaveLength(1);
64
+ });
65
+
66
+ it('should render Helmet with page title', () => {
67
+ const wrapper = mount(
68
+ <Provider store={store}>
69
+ <Router history={history}>
70
+ <IntlProvider locale="en">
71
+ <Login {...props} />
72
+ </IntlProvider>
73
+ </Router>
74
+ </Provider>,
75
+ );
76
+ expect(wrapper.find('Helmet')).toBeDefined();
77
+ });
78
+
79
+ it('should apply opacity-class when fetching_userdata is true', () => {
80
+ const propsWithFetching = {
81
+ ...props,
82
+ Login: {
83
+ ...props.Login,
84
+ fetching_userdata: true,
85
+ },
86
+ };
87
+
88
+ const wrapper = mount(
89
+ <Provider store={store}>
90
+ <Router history={history}>
91
+ <IntlProvider locale="en">
92
+ <Login {...propsWithFetching} />
93
+ </IntlProvider>
94
+ </Router>
95
+ </Provider>,
96
+ );
97
+
98
+ const mainDiv = wrapper.find('.opacity-class');
99
+ expect(mainDiv).toHaveLength(1);
100
+ });
101
+
102
+ it('should not apply opacity-class when fetching_userdata is false', () => {
103
+ const wrapper = mount(
104
+ <Provider store={store}>
105
+ <Router history={history}>
106
+ <IntlProvider locale="en">
107
+ <Login {...props} />
108
+ </IntlProvider>
109
+ </Router>
110
+ </Provider>,
111
+ );
112
+
113
+ const mainDiv = wrapper.find('.opacity-class');
114
+ expect(mainDiv).toHaveLength(0);
115
+ });
116
+
117
+ it('should have propTypes validation', () => {
118
+ expect(Login.propTypes).toBeDefined();
119
+ expect(Login.propTypes.actions).toBeDefined();
120
+ expect(Login.propTypes.Login).toBeDefined();
121
+ expect(Login.propTypes.replace).toBeDefined();
122
+ });
123
+ });
@@ -0,0 +1,165 @@
1
+ /**
2
+ * Test suite for Login selectors
3
+ */
4
+ import { fromJS } from 'immutable';
5
+ import makeSelectLogin, { selectLoginDomain } from '../selectors';
6
+
7
+ describe('Login Selectors', () => {
8
+ describe('selectLoginDomain', () => {
9
+ it('should select the cap domain from state', () => {
10
+ const mockState = fromJS({
11
+ cap: {
12
+ token: 'test-token',
13
+ user: 'testuser',
14
+ },
15
+ });
16
+
17
+ const selector = selectLoginDomain();
18
+ const result = selector(mockState);
19
+
20
+ expect(result).toBeDefined();
21
+ expect(result.get('token')).toBe('test-token');
22
+ expect(result.get('user')).toBe('testuser');
23
+ });
24
+
25
+ it('should return the cap domain as an Immutable Map', () => {
26
+ const mockState = fromJS({
27
+ cap: {
28
+ login_progress: false,
29
+ fetching_userdata: false,
30
+ },
31
+ });
32
+
33
+ const selector = selectLoginDomain();
34
+ const result = selector(mockState);
35
+
36
+ expect(result.toJS).toBeDefined(); // Verify it's Immutable
37
+ expect(result.get('login_progress')).toBe(false);
38
+ });
39
+ });
40
+
41
+ describe('makeSelectLogin', () => {
42
+ it('should convert Immutable state to plain JavaScript object', () => {
43
+ const mockState = fromJS({
44
+ cap: {
45
+ token: 'test-token',
46
+ login_progress: false,
47
+ fetching_userdata: false,
48
+ },
49
+ });
50
+
51
+ const selector = makeSelectLogin();
52
+ const result = selector(mockState);
53
+
54
+ expect(typeof result).toBe('object');
55
+ expect(result.token).toBe('test-token');
56
+ expect(result.login_progress).toBe(false);
57
+ });
58
+
59
+ it('should return a plain object (not Immutable Map)', () => {
60
+ const mockState = fromJS({
61
+ cap: {
62
+ data: [1, 2, 3],
63
+ },
64
+ });
65
+
66
+ const selector = makeSelectLogin();
67
+ const result = selector(mockState);
68
+
69
+ expect(result.toJS).toBeUndefined(); // Not an Immutable Map
70
+ expect(Array.isArray(result.data)).toBe(true);
71
+ });
72
+
73
+ it('should create a new selector instance with memoization', () => {
74
+ const mockState = fromJS({
75
+ cap: {
76
+ token: 'test-token',
77
+ },
78
+ });
79
+
80
+ const selector1 = makeSelectLogin();
81
+ const selector2 = makeSelectLogin();
82
+
83
+ const result1 = selector1(mockState);
84
+ const result2 = selector2(mockState);
85
+
86
+ // Different selector instances should produce equal results
87
+ expect(result1).toEqual(result2);
88
+ });
89
+
90
+ it('should include all cap state properties in result', () => {
91
+ const mockState = fromJS({
92
+ cap: {
93
+ token: 'test-token',
94
+ login_progress: true,
95
+ fetching_userdata: false,
96
+ isError: true,
97
+ user: 'john',
98
+ email: 'john@example.com',
99
+ },
100
+ });
101
+
102
+ const selector = makeSelectLogin();
103
+ const result = selector(mockState);
104
+
105
+ expect(result.token).toBe('test-token');
106
+ expect(result.login_progress).toBe(true);
107
+ expect(result.fetching_userdata).toBe(false);
108
+ expect(result.isError).toBe(true);
109
+ expect(result.user).toBe('john');
110
+ expect(result.email).toBe('john@example.com');
111
+ });
112
+
113
+ it('should handle empty cap state', () => {
114
+ const mockState = fromJS({
115
+ cap: {},
116
+ });
117
+
118
+ const selector = makeSelectLogin();
119
+ const result = selector(mockState);
120
+
121
+ expect(result).toEqual({});
122
+ });
123
+
124
+ it('should memoize results (reselect behavior)', () => {
125
+ const mockState = fromJS({
126
+ cap: {
127
+ token: 'test-token',
128
+ },
129
+ });
130
+
131
+ const selector = makeSelectLogin();
132
+
133
+ // Call with same state object multiple times
134
+ const result1 = selector(mockState);
135
+ const result2 = selector(mockState);
136
+
137
+ // Results should be the same reference (memoized)
138
+ expect(result1).toBe(result2);
139
+ });
140
+
141
+ it('should recompute when cap state changes', () => {
142
+ const selector = makeSelectLogin();
143
+
144
+ const mockState1 = fromJS({
145
+ cap: {
146
+ token: 'token1',
147
+ },
148
+ });
149
+
150
+ const mockState2 = fromJS({
151
+ cap: {
152
+ token: 'token2',
153
+ },
154
+ });
155
+
156
+ const result1 = selector(mockState1);
157
+ const result2 = selector(mockState2);
158
+
159
+ // Results should be different when state changes
160
+ expect(result1).not.toBe(result2);
161
+ expect(result1.token).toBe('token1');
162
+ expect(result2.token).toBe('token2');
163
+ });
164
+ });
165
+ });
@@ -14,9 +14,7 @@ export default {
14
14
  metaEntities: {
15
15
  tags: [],
16
16
  layouts: [],
17
- tagLookupMap: {},
18
17
  },
19
- liquidTags: [],
20
18
  fetchingLiquidTags: false,
21
19
  fetchingSchema: true,
22
20
  fetchingSchemaError: '',
@@ -334,6 +334,12 @@ export const getTemplateDetails = async ({id, channel}) => {
334
334
  return { ...compressedTemplatesData, response: decompressData};
335
335
  };
336
336
 
337
+ export const getDomainProperties = (channels, orgUnitId) => {
338
+ const queryString = channels?.map((channel) => `channel=${channel?.toUpperCase()}`)?.join('&');
339
+ const url = `${CAMPAIGNS_API_ORG_ENDPOINT}/meta/domainProperties?${queryString}`;
340
+ return request(url, getAPICallObject('GET', null, false, true, orgUnitId));
341
+ };
342
+
337
343
  export const getMediaDetails = async ({ id }) => {
338
344
  const url = `${API_ENDPOINT}/media/${id}`;
339
345
  return request(url, getAPICallObject('GET'));
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  getSenderDetails,
3
+ getDomainProperties,
3
4
  uploadFile,
4
5
  getCdnTransformationConfig,
5
6
  createWhatsappTemplate,
@@ -52,6 +53,12 @@ describe('getSenderDetails -- Test with valid responses', () => {
52
53
  expect(getSenderDetails('WHATSAPP', -1)).toEqual(Promise.resolve()));
53
54
  });
54
55
 
56
+ describe('getDomainProperties -- Test with valid responses', () => {
57
+ it('Should return correct response', () => {
58
+ expect(getDomainProperties(['SMS', 'EMAIL'], null)).toEqual(Promise.resolve());
59
+ });
60
+ });
61
+
55
62
  describe('getCdnTransformationConfigs -- Test with valid responses', () => {
56
63
  it('Should return correct response', () =>
57
64
  expect(getCdnTransformationConfig()).toEqual(Promise.resolve()));
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Test suite for getSchema
3
+ */
4
+ import getSchema from '../getSchema';
5
+
6
+ describe('getSchema', () => {
7
+ it('should return a promise when called', () => {
8
+ const result = getSchema('sms');
9
+ expect(result instanceof Promise).toBe(true);
10
+ });
11
+
12
+ it('should return SMS schema when channel is "sms"', () => {
13
+ const result = getSchema('sms');
14
+ return result.then((module) => {
15
+ expect(module).toBeDefined();
16
+ });
17
+ });
18
+
19
+ it('should return SMS schema when channel is "SMS" (uppercase)', () => {
20
+ const result = getSchema('SMS');
21
+ return result.then((module) => {
22
+ expect(module).toBeDefined();
23
+ });
24
+ });
25
+
26
+ it('should return EMAIL schema when channel is "email"', () => {
27
+ const result = getSchema('email');
28
+ return result.then((module) => {
29
+ expect(module).toBeDefined();
30
+ });
31
+ });
32
+
33
+ it('should return EMAIL schema when channel is "EMAIL" (uppercase)', () => {
34
+ const result = getSchema('EMAIL');
35
+ return result.then((module) => {
36
+ expect(module).toBeDefined();
37
+ });
38
+ });
39
+
40
+ it('should return MOBILE_PUSH schema when channel is "mobile_push"', () => {
41
+ const result = getSchema('mobile_push');
42
+ return result.then((module) => {
43
+ expect(module).toBeDefined();
44
+ });
45
+ });
46
+
47
+ it('should return MOBILE_PUSH schema when channel is "MOBILE_PUSH" (uppercase)', () => {
48
+ const result = getSchema('MOBILE_PUSH');
49
+ return result.then((module) => {
50
+ expect(module).toBeDefined();
51
+ });
52
+ });
53
+
54
+ it('should return WECHAT schema when channel is "wechat"', () => {
55
+ const result = getSchema('wechat');
56
+ return result.then((module) => {
57
+ expect(module).toBeDefined();
58
+ });
59
+ });
60
+
61
+ it('should return WECHAT schema when channel is "WECHAT" (uppercase)', () => {
62
+ const result = getSchema('WECHAT');
63
+ return result.then((module) => {
64
+ expect(module).toBeDefined();
65
+ });
66
+ });
67
+
68
+ it('should return SMS schema (default) when channel is unknown', () => {
69
+ const result = getSchema('unknown_channel');
70
+ return result.then((module) => {
71
+ expect(module).toBeDefined();
72
+ });
73
+ });
74
+
75
+ it('should return SMS schema (default) when channel is "whatsapp"', () => {
76
+ const result = getSchema('whatsapp');
77
+ return result.then((module) => {
78
+ expect(module).toBeDefined();
79
+ });
80
+ });
81
+
82
+ it('should handle mixed case channel names', () => {
83
+ const result = getSchema('SmS');
84
+ return result.then((module) => {
85
+ expect(module).toBeDefined();
86
+ });
87
+ });
88
+
89
+ it('should handle whitespace in channel names', () => {
90
+ const result = getSchema(' sms ');
91
+ return result.then((module) => {
92
+ expect(module).toBeDefined();
93
+ });
94
+ });
95
+ });
@@ -16,15 +16,17 @@ import {
16
16
  REGISTRATION_CUSTOM_FIELD,
17
17
  JP_LOCALE_HIDE_FEATURE,
18
18
  ENABLE_CUSTOMER_BARCODE_TAG,
19
+ AI_CONTENT_BOT_DISABLED,
19
20
  BADGES_UI_ENABLED,
20
21
  BADGES_ENROLL,
21
22
  EMAIL_UNSUBSCRIBE_TAG_MANDATORY,
22
23
  BADGES_ISSUE,
23
24
  ENABLE_WECHAT,
24
25
  ENABLE_WEBPUSH,
25
- LIQUID_SUPPORT,
26
26
  SUPPORT_CK_EDITOR,
27
- ENABLE_NEW_MPUSH
27
+ ENABLE_NEW_MPUSH,
28
+ ENABLE_NEW_EDITOR_FLOW_INAPP,
29
+ SUPPORT_ENGAGEMENT_MODULE,
28
30
  } from '../constants/unified';
29
31
  import { apiMessageFormatHandler } from './commonUtils';
30
32
 
@@ -91,16 +93,14 @@ export const hasPromoFeature = Auth.hasFeatureAccess.bind(
91
93
  null,
92
94
  PROMO_ENGINE,
93
95
  );
94
-
95
- export const hasLiquidSupportFeature = Auth.hasFeatureAccess.bind(
96
- null,
97
- LIQUID_SUPPORT,
98
- );
99
-
100
96
  export const hasSupportCKEditor = Auth.hasFeatureAccess.bind(
101
97
  null,
102
98
  SUPPORT_CK_EDITOR,
103
99
  );
100
+ export const hasSupportEngagementModule = Auth.hasFeatureAccess.bind(
101
+ null,
102
+ SUPPORT_ENGAGEMENT_MODULE,
103
+ );
104
104
 
105
105
  export const hasGiftVoucherFeature = Auth.hasFeatureAccess.bind(
106
106
  null,
@@ -132,7 +132,16 @@ export const hasCustomerBarcodeFeatureEnabled = Auth.hasFeatureAccess.bind(
132
132
  ENABLE_CUSTOMER_BARCODE_TAG,
133
133
  );
134
134
 
135
- export const isEmailUnsubscribeTagMandatory = Auth.hasFeatureAccess.bind(
135
+ export const isAiContentBotDisabled = Auth.hasFeatureAccess.bind(
136
+ null,
137
+ AI_CONTENT_BOT_DISABLED,
138
+ );
139
+
140
+ // Note: The "EMAIL_UNSUBSCRIBE_TAG_MANDATORY" feature flag determines if the Unsubscribe tag in email is optional.
141
+ // When this flag is enabled for an org, the Unsubscribe tag is NOT mandatory in the email flow.
142
+ // This is as per the requirement in the tech doc:
143
+ // https://capillarytech.atlassian.net/wiki/spaces/CAM/pages/3941662838/Remove+mandate+for+Unsubscribe+tag+in+email+flow
144
+ export const isEmailUnsubscribeTagOptional = Auth.hasFeatureAccess.bind(
136
145
  null,
137
146
  EMAIL_UNSUBSCRIBE_TAG_MANDATORY,
138
147
  );
@@ -142,6 +151,11 @@ export const hasNewMobilePushFeatureEnabled = Auth.hasFeatureAccess.bind(
142
151
  ENABLE_NEW_MPUSH,
143
152
  );
144
153
 
154
+ export const hasNewEditorFlowInAppEnabled = Auth.hasFeatureAccess.bind(
155
+ null,
156
+ ENABLE_NEW_EDITOR_FLOW_INAPP,
157
+ );
158
+
145
159
  //filtering tags based on scope
146
160
  export const filterTags = (tagsToFilter, tagsList) => tagsList?.filter(
147
161
  (tag) => !tagsToFilter?.includes(tag?.definition?.value)