@capillarytech/creatives-library 8.0.242-alpha.1 → 8.0.242-alpha.11

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 (255) hide show
  1. package/assets/Android.png +0 -0
  2. package/assets/iOS.png +0 -0
  3. package/config/app.js +1 -1
  4. package/constants/unified.js +2 -2
  5. package/initialReducer.js +0 -2
  6. package/package.json +1 -1
  7. package/services/api.js +5 -10
  8. package/services/tests/api.test.js +0 -18
  9. package/translations/en.json +4 -3
  10. package/utils/common.js +6 -5
  11. package/utils/commonUtils.js +1 -14
  12. package/utils/imageUrlUpload.js +141 -0
  13. package/utils/tests/commonUtil.test.js +0 -224
  14. package/utils/transformTemplateConfig.js +10 -0
  15. package/v2Components/CapDeviceContent/index.js +56 -61
  16. package/v2Components/CapImageUpload/constants.js +2 -0
  17. package/v2Components/CapImageUpload/index.js +65 -16
  18. package/v2Components/CapImageUpload/index.scss +4 -1
  19. package/v2Components/CapImageUpload/messages.js +5 -1
  20. package/v2Components/CapImageUrlUpload/constants.js +26 -0
  21. package/v2Components/CapImageUrlUpload/index.js +365 -0
  22. package/v2Components/CapImageUrlUpload/index.scss +35 -0
  23. package/v2Components/CapImageUrlUpload/messages.js +47 -0
  24. package/v2Components/CapTagList/index.js +1 -6
  25. package/v2Components/CapTagListWithInput/index.js +1 -5
  26. package/v2Components/CapTagListWithInput/messages.js +1 -1
  27. package/v2Components/CapWhatsappCTA/tests/index.test.js +0 -5
  28. package/v2Components/ErrorInfoNote/index.js +72 -412
  29. package/v2Components/ErrorInfoNote/messages.js +0 -22
  30. package/v2Components/ErrorInfoNote/style.scss +2 -279
  31. package/v2Components/HtmlEditor/HTMLEditor.js +89 -210
  32. package/v2Components/HtmlEditor/__tests__/HTMLEditor.test.js +133 -1132
  33. package/v2Components/HtmlEditor/__tests__/index.lazy.test.js +12 -17
  34. package/v2Components/HtmlEditor/_htmlEditor.scss +23 -8
  35. package/v2Components/HtmlEditor/_index.lazy.scss +1 -1
  36. package/v2Components/HtmlEditor/components/CodeEditorPane/_codeEditorPane.scss +101 -13
  37. package/v2Components/HtmlEditor/components/CodeEditorPane/index.js +139 -148
  38. package/v2Components/HtmlEditor/components/DeviceToggle/_deviceToggle.scss +1 -2
  39. package/v2Components/HtmlEditor/components/DeviceToggle/index.js +3 -3
  40. package/v2Components/HtmlEditor/components/EditorToolbar/index.js +1 -1
  41. package/v2Components/HtmlEditor/components/FullscreenModal/_fullscreenModal.scss +0 -1
  42. package/v2Components/HtmlEditor/components/InAppPreviewPane/DeviceFrame.js +7 -4
  43. package/v2Components/HtmlEditor/components/InAppPreviewPane/__tests__/DeviceFrame.test.js +45 -35
  44. package/v2Components/HtmlEditor/components/InAppPreviewPane/_inAppPreviewPane.scss +3 -1
  45. package/v2Components/HtmlEditor/components/InAppPreviewPane/constants.js +33 -33
  46. package/v2Components/HtmlEditor/components/InAppPreviewPane/index.js +6 -7
  47. package/v2Components/HtmlEditor/components/PreviewPane/_previewPane.scss +6 -3
  48. package/v2Components/HtmlEditor/components/PreviewPane/index.js +11 -10
  49. package/v2Components/HtmlEditor/components/SplitContainer/_splitContainer.scss +1 -1
  50. package/v2Components/HtmlEditor/components/ValidationErrorDisplay/__tests__/index.test.js +62 -87
  51. package/v2Components/HtmlEditor/components/ValidationErrorDisplay/index.js +31 -49
  52. package/v2Components/HtmlEditor/constants.js +20 -29
  53. package/v2Components/HtmlEditor/hooks/__tests__/useInAppContent.test.js +16 -373
  54. package/v2Components/HtmlEditor/hooks/useEditorContent.js +2 -5
  55. package/v2Components/HtmlEditor/hooks/useInAppContent.js +146 -88
  56. package/v2Components/HtmlEditor/index.js +1 -1
  57. package/v2Components/HtmlEditor/messages.js +85 -95
  58. package/v2Components/HtmlEditor/utils/liquidTemplateSupport.js +101 -99
  59. package/v2Components/HtmlEditor/utils/properSyntaxHighlighting.js +25 -23
  60. package/v2Components/HtmlEditor/utils/validationAdapter.js +41 -34
  61. package/v2Components/MobilePushPreviewV2/index.js +7 -32
  62. package/v2Components/TemplatePreview/_templatePreview.scss +24 -44
  63. package/v2Components/TemplatePreview/index.js +32 -47
  64. package/v2Components/TemplatePreview/messages.js +0 -4
  65. package/v2Components/TestAndPreviewSlidebox/index.js +25 -31
  66. package/v2Containers/App/constants.js +5 -0
  67. package/v2Containers/BeeEditor/index.js +80 -82
  68. package/v2Containers/Cap/tests/__snapshots__/index.test.js.snap +4 -3
  69. package/v2Containers/CreativesContainer/SlideBoxContent.js +118 -148
  70. package/v2Containers/CreativesContainer/SlideBoxFooter.js +3 -9
  71. package/v2Containers/CreativesContainer/SlideBoxHeader.js +2 -2
  72. package/v2Containers/CreativesContainer/constants.js +2 -1
  73. package/v2Containers/CreativesContainer/index.js +41 -173
  74. package/v2Containers/CreativesContainer/messages.js +4 -4
  75. package/v2Containers/CreativesContainer/tests/SlideBoxContent.test.js +210 -0
  76. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +354 -38
  77. package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +0 -36
  78. package/v2Containers/Email/actions.js +0 -7
  79. package/v2Containers/Email/constants.js +1 -5
  80. package/v2Containers/Email/index.js +0 -13
  81. package/v2Containers/Email/messages.js +0 -32
  82. package/v2Containers/Email/reducer.js +1 -12
  83. package/v2Containers/Email/sagas.js +6 -41
  84. package/v2Containers/Email/tests/__snapshots__/reducer.test.js.snap +0 -2
  85. package/v2Containers/EmailWrapper/components/EmailWrapperView.js +7 -193
  86. package/v2Containers/EmailWrapper/components/HTMLEditorTesting.js +74 -40
  87. package/v2Containers/EmailWrapper/components/__tests__/HTMLEditorTesting.test.js +67 -2
  88. package/v2Containers/EmailWrapper/constants.js +0 -2
  89. package/v2Containers/EmailWrapper/hooks/useEmailWrapper.js +67 -436
  90. package/v2Containers/EmailWrapper/index.js +23 -99
  91. package/v2Containers/EmailWrapper/messages.js +1 -61
  92. package/v2Containers/EmailWrapper/tests/EmailWrapperView.test.js +1 -26
  93. package/v2Containers/EmailWrapper/tests/useEmailWrapper.test.js +77 -111
  94. package/v2Containers/InApp/actions.js +0 -7
  95. package/v2Containers/InApp/constants.js +4 -20
  96. package/v2Containers/InApp/index.js +357 -800
  97. package/v2Containers/InApp/index.scss +3 -4
  98. package/v2Containers/InApp/messages.js +3 -7
  99. package/v2Containers/InApp/reducer.js +3 -21
  100. package/v2Containers/InApp/sagas.js +9 -29
  101. package/v2Containers/InApp/selectors.js +5 -25
  102. package/v2Containers/InApp/tests/index.test.js +50 -154
  103. package/v2Containers/InApp/tests/reducer.test.js +0 -34
  104. package/v2Containers/InApp/tests/sagas.test.js +9 -61
  105. package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/content.test.js.snap +12 -12
  106. package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/index.test.js.snap +8 -8
  107. package/v2Containers/Line/Container/Wrapper/tests/__snapshots__/index.test.js.snap +100 -77
  108. package/v2Containers/Line/Container/tests/__snapshots__/index.test.js.snap +72 -63
  109. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +184 -150
  110. package/v2Containers/SmsTrai/Create/tests/__snapshots__/index.test.js.snap +16 -12
  111. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +32 -28
  112. package/v2Containers/TagList/index.js +1 -67
  113. package/v2Containers/Templates/ChannelTypeIllustration.js +13 -1
  114. package/v2Containers/Templates/_templates.scss +202 -56
  115. package/v2Containers/Templates/actions.js +2 -1
  116. package/v2Containers/Templates/constants.js +1 -0
  117. package/v2Containers/Templates/index.js +278 -128
  118. package/v2Containers/Templates/messages.js +24 -4
  119. package/v2Containers/Templates/reducer.js +2 -0
  120. package/v2Containers/Templates/tests/index.test.js +10 -0
  121. package/v2Containers/TemplatesV2/index.js +8 -1
  122. package/v2Containers/TemplatesV2/messages.js +4 -0
  123. package/v2Containers/WebPush/Create/components/BrandIconSection.js +108 -0
  124. package/v2Containers/WebPush/Create/components/ButtonForm.js +172 -0
  125. package/v2Containers/WebPush/Create/components/ButtonItem.js +101 -0
  126. package/v2Containers/WebPush/Create/components/ButtonList.js +145 -0
  127. package/v2Containers/WebPush/Create/components/ButtonsLinksSection.js +164 -0
  128. package/v2Containers/WebPush/Create/components/ButtonsLinksSection.test.js +463 -0
  129. package/v2Containers/WebPush/Create/components/FormActions.js +54 -0
  130. package/v2Containers/WebPush/Create/components/FormActions.test.js +163 -0
  131. package/v2Containers/WebPush/Create/components/MediaSection.js +142 -0
  132. package/v2Containers/WebPush/Create/components/MediaSection.test.js +341 -0
  133. package/v2Containers/WebPush/Create/components/MessageSection.js +103 -0
  134. package/v2Containers/WebPush/Create/components/MessageSection.test.js +268 -0
  135. package/v2Containers/WebPush/Create/components/NotificationTitleSection.js +87 -0
  136. package/v2Containers/WebPush/Create/components/NotificationTitleSection.test.js +210 -0
  137. package/v2Containers/WebPush/Create/components/TemplateNameSection.js +54 -0
  138. package/v2Containers/WebPush/Create/components/TemplateNameSection.test.js +143 -0
  139. package/v2Containers/WebPush/Create/components/__snapshots__/ButtonsLinksSection.test.js.snap +86 -0
  140. package/v2Containers/WebPush/Create/components/__snapshots__/FormActions.test.js.snap +16 -0
  141. package/v2Containers/WebPush/Create/components/__snapshots__/MediaSection.test.js.snap +41 -0
  142. package/v2Containers/WebPush/Create/components/__snapshots__/MessageSection.test.js.snap +54 -0
  143. package/v2Containers/WebPush/Create/components/__snapshots__/NotificationTitleSection.test.js.snap +37 -0
  144. package/v2Containers/WebPush/Create/components/__snapshots__/TemplateNameSection.test.js.snap +21 -0
  145. package/v2Containers/WebPush/Create/components/_buttons.scss +246 -0
  146. package/v2Containers/WebPush/Create/components/tests/ButtonForm.test.js +554 -0
  147. package/v2Containers/WebPush/Create/components/tests/ButtonItem.test.js +607 -0
  148. package/v2Containers/WebPush/Create/components/tests/ButtonList.test.js +633 -0
  149. package/v2Containers/WebPush/Create/components/tests/__snapshots__/ButtonForm.test.js.snap +666 -0
  150. package/v2Containers/WebPush/Create/components/tests/__snapshots__/ButtonItem.test.js.snap +74 -0
  151. package/v2Containers/WebPush/Create/components/tests/__snapshots__/ButtonList.test.js.snap +78 -0
  152. package/v2Containers/WebPush/Create/hooks/useButtonManagement.js +138 -0
  153. package/v2Containers/WebPush/Create/hooks/useButtonManagement.test.js +406 -0
  154. package/v2Containers/WebPush/Create/hooks/useCharacterCount.js +30 -0
  155. package/v2Containers/WebPush/Create/hooks/useCharacterCount.test.js +151 -0
  156. package/v2Containers/WebPush/Create/hooks/useImageUpload.js +104 -0
  157. package/v2Containers/WebPush/Create/hooks/useImageUpload.test.js +538 -0
  158. package/v2Containers/WebPush/Create/hooks/useTagManagement.js +122 -0
  159. package/v2Containers/WebPush/Create/hooks/useTagManagement.test.js +633 -0
  160. package/v2Containers/WebPush/Create/index.js +1056 -0
  161. package/v2Containers/WebPush/Create/index.scss +134 -0
  162. package/v2Containers/WebPush/Create/messages.js +203 -0
  163. package/v2Containers/WebPush/Create/preview/DevicePreviewContent.js +228 -0
  164. package/v2Containers/WebPush/Create/preview/NotificationContainer.js +294 -0
  165. package/v2Containers/WebPush/Create/preview/PreviewContent.js +90 -0
  166. package/v2Containers/WebPush/Create/preview/PreviewControls.js +305 -0
  167. package/v2Containers/WebPush/Create/preview/PreviewDisclaimer.js +23 -0
  168. package/v2Containers/WebPush/Create/preview/WebPushPreview.js +150 -0
  169. package/v2Containers/WebPush/Create/preview/assets/Light.svg +53 -0
  170. package/v2Containers/WebPush/Create/preview/assets/Top.svg +5 -0
  171. package/v2Containers/WebPush/Create/preview/assets/android-arrow-down.svg +9 -0
  172. package/v2Containers/WebPush/Create/preview/assets/android-arrow-up.svg +9 -0
  173. package/v2Containers/WebPush/Create/preview/assets/chrome-icon.png +0 -0
  174. package/v2Containers/WebPush/Create/preview/assets/edge-icon.png +0 -0
  175. package/v2Containers/WebPush/Create/preview/assets/firefox-icon.svg +106 -0
  176. package/v2Containers/WebPush/Create/preview/assets/iOS.svg +26 -0
  177. package/v2Containers/WebPush/Create/preview/assets/macos-arrow-down-icon.svg +9 -0
  178. package/v2Containers/WebPush/Create/preview/assets/macos-triple-dot-icon.svg +9 -0
  179. package/v2Containers/WebPush/Create/preview/assets/opera-icon.svg +18 -0
  180. package/v2Containers/WebPush/Create/preview/assets/safari-icon.svg +29 -0
  181. package/v2Containers/WebPush/Create/preview/assets/windows-close-icon.svg +9 -0
  182. package/v2Containers/WebPush/Create/preview/assets/windows-triple-dot-icon.svg +9 -0
  183. package/v2Containers/WebPush/Create/preview/components/AndroidMobileChromeHeader.js +47 -0
  184. package/v2Containers/WebPush/Create/preview/components/AndroidMobileExpanded.js +141 -0
  185. package/v2Containers/WebPush/Create/preview/components/IOSHeader.js +45 -0
  186. package/v2Containers/WebPush/Create/preview/components/NotificationExpandedContent.js +68 -0
  187. package/v2Containers/WebPush/Create/preview/components/NotificationHeader.js +61 -0
  188. package/v2Containers/WebPush/Create/preview/components/WindowsChromeExpanded.js +99 -0
  189. package/v2Containers/WebPush/Create/preview/components/tests/AndroidMobileExpanded.test.js +733 -0
  190. package/v2Containers/WebPush/Create/preview/components/tests/WindowsChromeExpanded.test.js +571 -0
  191. package/v2Containers/WebPush/Create/preview/components/tests/__snapshots__/AndroidMobileExpanded.test.js.snap +81 -0
  192. package/v2Containers/WebPush/Create/preview/components/tests/__snapshots__/WindowsChromeExpanded.test.js.snap +81 -0
  193. package/v2Containers/WebPush/Create/preview/config/notificationMappings.js +50 -0
  194. package/v2Containers/WebPush/Create/preview/constants.js +637 -0
  195. package/v2Containers/WebPush/Create/preview/notification-container.scss +79 -0
  196. package/v2Containers/WebPush/Create/preview/preview.scss +351 -0
  197. package/v2Containers/WebPush/Create/preview/styles/_android-mobile-chrome.scss +370 -0
  198. package/v2Containers/WebPush/Create/preview/styles/_android-mobile-edge.scss +12 -0
  199. package/v2Containers/WebPush/Create/preview/styles/_android-mobile-firefox.scss +12 -0
  200. package/v2Containers/WebPush/Create/preview/styles/_android-mobile-opera.scss +12 -0
  201. package/v2Containers/WebPush/Create/preview/styles/_android-tablet-chrome.scss +47 -0
  202. package/v2Containers/WebPush/Create/preview/styles/_android-tablet-edge.scss +11 -0
  203. package/v2Containers/WebPush/Create/preview/styles/_android-tablet-firefox.scss +11 -0
  204. package/v2Containers/WebPush/Create/preview/styles/_android-tablet-opera.scss +11 -0
  205. package/v2Containers/WebPush/Create/preview/styles/_base.scss +207 -0
  206. package/v2Containers/WebPush/Create/preview/styles/_ios.scss +153 -0
  207. package/v2Containers/WebPush/Create/preview/styles/_ipados.scss +107 -0
  208. package/v2Containers/WebPush/Create/preview/styles/_macos-chrome.scss +101 -0
  209. package/v2Containers/WebPush/Create/preview/styles/_windows-chrome.scss +229 -0
  210. package/v2Containers/WebPush/Create/preview/tests/DevicePreviewContent.test.js +909 -0
  211. package/v2Containers/WebPush/Create/preview/tests/NotificationContainer.test.js +1081 -0
  212. package/v2Containers/WebPush/Create/preview/tests/PreviewControls.test.js +723 -0
  213. package/v2Containers/WebPush/Create/preview/tests/WebPushPreview.test.js +943 -0
  214. package/v2Containers/WebPush/Create/preview/tests/__snapshots__/DevicePreviewContent.test.js.snap +131 -0
  215. package/v2Containers/WebPush/Create/preview/tests/__snapshots__/NotificationContainer.test.js.snap +112 -0
  216. package/v2Containers/WebPush/Create/preview/tests/__snapshots__/PreviewControls.test.js.snap +144 -0
  217. package/v2Containers/WebPush/Create/preview/tests/__snapshots__/WebPushPreview.test.js.snap +129 -0
  218. package/v2Containers/WebPush/Create/utils/payloadBuilder.js +94 -0
  219. package/v2Containers/WebPush/Create/utils/payloadBuilder.test.js +390 -0
  220. package/v2Containers/WebPush/Create/utils/previewUtils.js +89 -0
  221. package/v2Containers/WebPush/Create/utils/urlValidation.js +115 -0
  222. package/v2Containers/WebPush/Create/utils/urlValidation.test.js +449 -0
  223. package/v2Containers/WebPush/Create/utils/validation.js +75 -0
  224. package/v2Containers/WebPush/Create/utils/validation.test.js +283 -0
  225. package/v2Containers/WebPush/actions.js +60 -0
  226. package/v2Containers/WebPush/constants.js +128 -0
  227. package/v2Containers/WebPush/index.js +2 -0
  228. package/v2Containers/WebPush/reducer.js +104 -0
  229. package/v2Containers/WebPush/sagas.js +119 -0
  230. package/v2Containers/WebPush/selectors.js +65 -0
  231. package/v2Containers/WebPush/tests/reducer.test.js +863 -0
  232. package/v2Containers/WebPush/tests/sagas.test.js +566 -0
  233. package/v2Containers/WebPush/tests/selectors.test.js +843 -0
  234. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +528 -431
  235. package/v2Components/HtmlEditor/components/ValidationTabs/_validationTabs.scss +0 -254
  236. package/v2Components/HtmlEditor/components/ValidationTabs/index.js +0 -362
  237. package/v2Components/HtmlEditor/components/ValidationTabs/messages.js +0 -51
  238. package/v2Containers/BeePopupEditor/constants.js +0 -10
  239. package/v2Containers/BeePopupEditor/index.js +0 -193
  240. package/v2Containers/BeePopupEditor/tests/index.test.js +0 -627
  241. package/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +0 -1045
  242. package/v2Containers/InApp/__tests__/InAppHTMLEditor.test.js +0 -376
  243. package/v2Containers/InApp/__tests__/sagas.test.js +0 -363
  244. package/v2Containers/InApp/tests/selectors.test.js +0 -612
  245. package/v2Containers/InAppWrapper/components/InAppWrapperView.js +0 -162
  246. package/v2Containers/InAppWrapper/components/__tests__/InAppWrapperView.test.js +0 -267
  247. package/v2Containers/InAppWrapper/components/inAppWrapperView.scss +0 -9
  248. package/v2Containers/InAppWrapper/constants.js +0 -16
  249. package/v2Containers/InAppWrapper/hooks/__tests__/useInAppWrapper.test.js +0 -473
  250. package/v2Containers/InAppWrapper/hooks/useInAppWrapper.js +0 -198
  251. package/v2Containers/InAppWrapper/index.js +0 -148
  252. package/v2Containers/InAppWrapper/messages.js +0 -49
  253. package/v2Containers/InappAdvance/index.js +0 -1099
  254. package/v2Containers/InappAdvance/index.scss +0 -10
  255. package/v2Containers/InappAdvance/tests/index.test.js +0 -448
@@ -79,6 +79,7 @@ import * as whatsappActions from '../Whatsapp/actions';
79
79
  import * as rcsActions from '../Rcs/actions';
80
80
  import * as zaloActions from '../Zalo/actions';
81
81
  import * as inAppActions from '../InApp/actions';
82
+ import * as webpushActions from '../WebPush/actions';
82
83
  import * as globalActions from '../Cap/actions';
83
84
  import { makeSelectAuthenticated } from '../Cap/selectors';
84
85
  import { UserIsAuthenticated } from '../../utils/authWrapper';
@@ -125,18 +126,17 @@ import {
125
126
  VIDEO,
126
127
  GIF,
127
128
  } from '../Whatsapp/constants';
128
- import { INAPP_LAYOUT_DETAILS, INAPP_MESSAGE_LAYOUT_TYPES, INAPP_MEDIA_TYPES, BIG_HTML, ANDROID, IOS } from '../InApp/constants';
129
+ import { INAPP_LAYOUT_DETAILS } from '../InApp/constants';
129
130
  import { ZALO_STATUS_OPTIONS, ZALO_STATUSES } from '../Zalo/constants';
130
131
  import { getWhatsappContent, getWhatsappStatus, getWhatsappCategory, getWhatsappCta, getWhatsappQuickReply, getWhatsappAutoFill, getWhatsappCarouselButtonView } from '../Whatsapp/utils';
131
132
  import { getRCSContent } from '../Rcs/utils';
132
133
  import {RCS_STATUSES} from '../Rcs/constants';
133
134
  import zaloMessages from '../Zalo/messages';
134
135
  import rcsMessages from '../Rcs/messages';
135
- import inAppMessages from '../InApp/messages';
136
136
  import globalMessages from '../../v2Containers/Cap/messages';
137
137
  import { handlePreviewInNewTab } from '../../utils/common';
138
138
 
139
- import { MOBILE_PUSH, WECHAT, SMS, EMAIL, EBILL, LINE, VIBER, FACEBOOK, WHATSAPP, RCS, ZALO, INAPP } from '../CreativesContainer/constants';
139
+ import { MOBILE_PUSH, WECHAT, SMS, EMAIL, EBILL, LINE, VIBER, FACEBOOK, WHATSAPP, RCS, ZALO, INAPP, WEBPUSH } from '../CreativesContainer/constants';
140
140
 
141
141
  import {CREATIVE} from '../Facebook/constants';
142
142
  import videoPlay from '../../assets/videoPlay.svg';
@@ -154,10 +154,13 @@ import { DAEMON } from '@capillarytech/vulcan-react-sdk/utils/sagaInjectorTypes'
154
154
  import { Rcs } from '../Rcs';
155
155
  import { makeSelectRcs } from '../Rcs/selectors';
156
156
  import { getRcsStatusType } from '../Rcs/utils';
157
+ import { makeSelectWebPush } from '../WebPush/selectors';
157
158
  import { v2MobilePushSagas } from '../MobilePushNew/sagas';
158
159
  import { AUTO_CAROUSEL, BIG_PICTURE, FILMSTRIP_CAROUSEL, MANUAL_CAROUSEL } from '../MobilePushNew/constants';
159
160
  import CapPageSpinner from '../../v2Components/CapPageSpinner';
161
+ import webPushSagas from '../WebPush/sagas';
160
162
  const withMobilePushNewSaga = injectSaga({ key: 'mobilePushNew', saga: v2MobilePushSagas, mode: DAEMON });
163
+ const withWebPushSaga = injectSaga({ key: 'webPush', saga: webPushSagas, mode: DAEMON });
161
164
 
162
165
  const { timeTracker } = GA;
163
166
  const {CapCustomCardList} = CapCustomCard;
@@ -199,29 +202,6 @@ const SMS_FILTERS = {
199
202
  SERVICE_IMPLICIT: 'implicit',
200
203
  };
201
204
 
202
- const INAPP_LAYOUT_OPTIONS = [
203
- {
204
- key: 'popup',
205
- value: INAPP_MESSAGE_LAYOUT_TYPES.MODAL,
206
- label: <FormattedMessage {...inAppMessages.layoutModal} />,
207
- },
208
- {
209
- key: 'topBanner',
210
- value: INAPP_MESSAGE_LAYOUT_TYPES.TOPBANNER,
211
- label: <FormattedMessage {...inAppMessages.layoutTopBanner} />,
212
- },
213
- {
214
- key: 'bottomBanner',
215
- value: INAPP_MESSAGE_LAYOUT_TYPES.BOTTOMBANNER,
216
- label: <FormattedMessage {...inAppMessages.layoutBottomBanner} />,
217
- },
218
- {
219
- key: 'fullScreen',
220
- value: INAPP_MESSAGE_LAYOUT_TYPES.FULLSCREEN,
221
- label: <FormattedMessage {...inAppMessages.layoutFullScreen} />,
222
- },
223
- ];
224
-
225
205
  const WHATSAPP_LOWERCASE = WHATSAPP.toLowerCase();
226
206
  const RCS_LOWERCASE = RCS.toLowerCase();
227
207
  const SMS_LOWERCASE = SMS.toLowerCase();
@@ -234,6 +214,7 @@ const EBILL_LOWERCASE = EBILL.toLowerCase();
234
214
  const LINE_LOWERCASE = LINE.toLowerCase();
235
215
  const ZALO_LOWERCASE = ZALO.toLowerCase();
236
216
  const WECHAT_LOWERCASE = WECHAT.toLowerCase();
217
+ const WEBPUSH_LOWERCASE = WEBPUSH.toLowerCase();
237
218
  const duplicateEnum = {
238
219
  sms: "smsActions",
239
220
  line: "lineActions",
@@ -272,7 +253,6 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
272
253
  selectedWhatsappStatus: '',
273
254
  selectedWhatsappCategory: '',
274
255
  selectedZaloStatus: '',
275
- selectedInAppLayout: '',
276
256
  hostName: '',
277
257
  searchedZaloTemplates: [],
278
258
  searchingZaloTemplate: false,
@@ -287,6 +267,9 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
287
267
  // Add flag to prevent duplicate API calls
288
268
  isProcessingEditResponse: false,
289
269
  };
270
+ // Timeout IDs for cleanup
271
+ this._clearEditTimeout = null;
272
+ this._clearCreateTimeout = null;
290
273
  this.getAllTemplates = this.getAllTemplates.bind(this);
291
274
  this.createTemplate = this.createTemplate.bind(this);
292
275
  this.searchTemplate = this.searchTemplate.bind(this);
@@ -409,10 +392,19 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
409
392
  orgUnitId: -1,
410
393
  });
411
394
  break;
395
+ case WEBPUSH:
396
+ channel = 'Webpush';
397
+ activeMode = ACCOUNT_SELECTION_MODE;
398
+ this.props.actions.getWeCrmAccounts('WebPush');
399
+ break;
412
400
  default:
413
401
  channel = '';
414
402
  }
415
403
  this.setState({ channel, activeMode });
404
+ // Clear templates when entering account selection mode to prevent showing old channel templates
405
+ if (activeMode === ACCOUNT_SELECTION_MODE) {
406
+ this.props.actions.resetTemplate();
407
+ }
416
408
  if (isEmpty(this.props.Templates?.userList)) {
417
409
  this.props.actions.getUserList();
418
410
  }
@@ -510,6 +502,10 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
510
502
  channel = 'Facebook';
511
503
  this.setState({defaultAccount: true});
512
504
  nextProps.actions.getAccountsSettings();
505
+ } else if (nextProps.route.name.toLowerCase() === WEBPUSH_LOWERCASE) {
506
+ this.setState({defaultAccount: true});
507
+ channel = 'Webpush';
508
+ nextProps.actions.getWeCrmAccounts('WebPush');
513
509
  }
514
510
 
515
511
  this.setState({ channel }, () => {
@@ -655,6 +651,50 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
655
651
  }, 1000);
656
652
  }
657
653
 
654
+ // Check for WebPush edit response - prevent duplicate calls
655
+ if (selectedChannel === WEBPUSH_LOWERCASE && nextProps.WebPush?.editResponse && (nextProps.WebPush.editResponse.templateId || nextProps.WebPush.editResponse._id) && !isEqual(nextProps.WebPush.editResponse, this.props.WebPush?.editResponse)) {
656
+ const channelLabel = this.props.intl.formatMessage(messages.webpushHeader);
657
+ const message = `${channelLabel} ${this.props.intl.formatMessage(messages.templateUpdateSuccess)}`;
658
+ CapNotification.success({
659
+ key: 'webpushEditSuccess',
660
+ message
661
+ });
662
+ // Clear previous state before loading newer templates
663
+ this.props.actions.resetTemplate();
664
+ this.getAllTemplates({params, resetPage: true});
665
+ // Delay clearing to allow drawer to close first
666
+ // Clear any existing timeout before setting a new one
667
+ if (this._clearEditTimeout) {
668
+ clearTimeout(this._clearEditTimeout);
669
+ }
670
+ this._clearEditTimeout = setTimeout(() => {
671
+ this.props.webpushActions.clearEditResponse();
672
+ this._clearEditTimeout = null;
673
+ }, 200);
674
+ }
675
+
676
+ // Check for WebPush create response (for new template creation)
677
+ if (selectedChannel === WEBPUSH_LOWERCASE && nextProps.WebPush?.response && (nextProps.WebPush.response?.templateId || nextProps.WebPush.response?._id) && !isEqual(nextProps.WebPush.response, this.props.WebPush?.response)) {
678
+ // Skip showing generic "created" toast when this is a duplicate operation
679
+ if (!nextProps.WebPush.response.meta?.isDuplicate) {
680
+ const channelLabel = this.props.intl.formatMessage(messages.webpushHeader);
681
+ const message = `${channelLabel} ${this.props.intl.formatMessage(messages.templateCreateSuccess)}`;
682
+ CapNotification.success({key: 'webpushCreateSuccess', message});
683
+ // Clear previous state before loading newer templates
684
+ this.props.actions.resetTemplate();
685
+ this.getAllTemplates({params, resetPage: true});
686
+ // Delay clearing to allow drawer to close first
687
+ // Clear any existing timeout before setting a new one
688
+ if (this._clearCreateTimeout) {
689
+ clearTimeout(this._clearCreateTimeout);
690
+ }
691
+ this._clearCreateTimeout = setTimeout(() => {
692
+ this.props.webpushActions.clearCreateResponse();
693
+ this._clearCreateTimeout = null;
694
+ }, 200);
695
+ }
696
+ }
697
+
658
698
 
659
699
 
660
700
  if (nextProps.Create && this.props.Create && nextProps.Create.createTemplateError && !isEqual(nextProps.Create.createTemplateError, this.props.Create.createTemplateError)) {
@@ -662,7 +702,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
662
702
  if ((this.state.channel || '').toLowerCase() !== "sms") {
663
703
  CapNotification.error({key: 'somethingWrong', message});
664
704
  }
665
- const { smsActions, mobilepushActions, ebillActions, lineActions, viberActions, facebookActions, whatsappActions, inAppActions, rcsActions } = this.props;
705
+ const { smsActions, mobilepushActions, ebillActions, lineActions, viberActions, facebookActions, whatsappActions, inAppActions, rcsActions, webpushActions } = this.props;
666
706
  switch (selectedChannel) {
667
707
  case SMS_LOWERCASE:
668
708
  smsActions.clearCreateResponse();
@@ -691,6 +731,9 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
691
731
  case inAppActions:
692
732
  inAppActions.clearCreateResponse();
693
733
  break;
734
+ case WEBPUSH_LOWERCASE:
735
+ webpushActions.clearCreateResponse();
736
+ break;
694
737
  default:
695
738
  break;
696
739
  }
@@ -703,6 +746,10 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
703
746
  nextProps.Templates.deleteResponse) {
704
747
  const message = `${this.state.channel} ${this.props.intl.formatMessage(messages['Template deleted successfully'])}`;
705
748
  CapNotification.success({key: 'deleteSucess', message});
749
+ // Clear previous state before loading newer templates for web push channel
750
+ if (selectedChannel === WEBPUSH_LOWERCASE) {
751
+ this.props.actions.resetTemplate();
752
+ }
706
753
  this.getAllTemplates({params, resetPage: true});
707
754
  }
708
755
 
@@ -765,7 +812,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
765
812
  this.setState({ previewTemplate: nextProps.Templates.templateDetails });
766
813
  }
767
814
  const { weCrmAccounts: weCrmAccountsList = [], senderDetails = {} } = get(nextProps, 'Templates', {});
768
- const weCrmChannels = [WHATSAPP_LOWERCASE, ZALO_LOWERCASE, RCS_LOWERCASE];
815
+ const weCrmChannels = [WHATSAPP_LOWERCASE, ZALO_LOWERCASE, RCS_LOWERCASE, WEBPUSH_LOWERCASE];
769
816
 
770
817
  // Keeping the wechat flow separate as it has different logic for setting the account. Currently we don't support wechat but still keeping the flow.
771
818
  if (weCrmAccountsList?.length === 1 && this.state?.defaultAccount && selectedChannel === WECHAT_LOWERCASE && !isEmpty(senderDetails?.hostName)) {
@@ -792,9 +839,9 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
792
839
  const isSingleAccount = weCrmAccountsList?.length === 1;
793
840
  const selectedAccount = this.props.Templates[ACCOUNT_MAPPING_ON_CHANNEL[selectedChannel]] || {};
794
841
  const hostName = this.getHostName(isSingleAccount ? weCrmAccountsList[0]?.sourceAccountIdentifier : selectedAccount?.sourceAccountIdentifier, senderDetails?.domainProperties);
795
- if (!isEmpty(hostName)) {
842
+ if (!isEmpty(hostName) || selectedChannel === WEBPUSH_LOWERCASE) {
796
843
  const paramsDefault = {};
797
- const {name, sourceAccountIdentifier, configs } = weCrmAccountsList?.[0] || {};
844
+ const {name, sourceAccountIdentifier, configs, id } = weCrmAccountsList?.[0] || {};
798
845
  if (isSingleAccount) {
799
846
  weCrmAccountsList[0].hostName = hostName;
800
847
  this.setState({ selectedAccount: name });
@@ -819,6 +866,9 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
819
866
  paramsDefault.accessToken = configs?.accessToken;
820
867
  paramsDefault.host = hostName || this.props.Templates?.selectedRcsAccount?.hostName;
821
868
  }
869
+ if (selectedChannel === WEBPUSH_LOWERCASE) {
870
+ paramsDefault.accountId = id;
871
+ }
822
872
  this.setState({ defaultAccount: false });
823
873
  /**
824
874
  * Incase of multiple accounts, getAllTemplates is called on selecting the account. It's handled in onAccountSelect function.
@@ -841,12 +891,23 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
841
891
  window.removeEventListener("message", this.handleFrameTasks);
842
892
  this.props.actions.resetTemplateStoreData();
843
893
  this.props.globalActions.clearMetaEntities();
894
+ // Clear any pending timeouts to prevent memory leaks
895
+ if (this._clearEditTimeout) {
896
+ clearTimeout(this._clearEditTimeout);
897
+ this._clearEditTimeout = null;
898
+ }
899
+ if (this._clearCreateTimeout) {
900
+ clearTimeout(this._clearCreateTimeout);
901
+ this._clearCreateTimeout = null;
902
+ }
844
903
  // this.props.actions.resetAccount();
845
904
  // this.setState({defaultAccount: false});
846
905
  }
847
906
 
848
- onAccountSelect(e) {
907
+ onAccountSelect(e) {
849
908
  const value = e.target.value;
909
+ // Clear templates immediately when account is selected to prevent showing old channel templates
910
+ this.props.actions.resetTemplate();
850
911
  this.setState({selectedAccount: value}, () => {
851
912
  const params = {};
852
913
  if (this.state.channel.toLowerCase() !== ZALO_LOWERCASE) {
@@ -874,7 +935,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
874
935
  } else {
875
936
  this.setState({ selectedAccountError: false });
876
937
  }
877
- } else if ([LINE.toLowerCase(), RCS_LOWERCASE, ZALO_LOWERCASE, WHATSAPP_LOWERCASE].includes(selectedChannel)) {
938
+ } else if ([LINE.toLowerCase(), RCS_LOWERCASE, ZALO_LOWERCASE, WHATSAPP_LOWERCASE, WEBPUSH_LOWERCASE].includes(selectedChannel)) {
878
939
  const setAcc = this.props?.Templates?.weCrmAccounts?.find((item) => item?.name === this.state.selectedAccount);
879
940
  const { domainProperties = [] } = this.props?.Templates?.senderDetails || {};
880
941
  let hostName = '';
@@ -901,6 +962,9 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
901
962
  params.accountId = sourceAccountIdentifier;
902
963
  params.host = hostName;
903
964
  }
965
+ if (selectedChannel === WEBPUSH_LOWERCASE && setAcc) {
966
+ params.accountId = setAcc.accountId || setAcc.id;
967
+ }
904
968
  if (selectedChannel === RCS_LOWERCASE && hostName) {
905
969
  const { configs: { accessToken = "" } = {} } = setAcc || {};
906
970
  params.accountId = sourceAccountIdentifier;
@@ -942,37 +1006,41 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
942
1006
  let creativesParams = {mode: ''};
943
1007
  if (!isEmpty(routeParams)) {
944
1008
  const { pathname } = routeParams;
945
- if (pathname.includes('create')) {
946
- creativesParams.mode = 'create';
947
- } else if (pathname.includes('edit') || pathname.includes('overview')) {
948
- creativesParams.mode = 'edit';
949
- if (pathname.includes('richmedia')) {
950
- creativesParams._id = pathname.split('/')[4];
951
- creativesParams.definition = {
952
- msgcontent: "RICH_MEDIA_WECHAT",
953
- };
954
- } else {
955
- creativesParams._id = pathname.split('/')[3];
956
- creativesParams.modeType = pathname.split('/')[4];
957
- creativesParams.account = pathname.split('/')[5];
958
- if (this.state.channel.toLowerCase() === WHATSAPP_LOWERCASE) {
959
- const whatsappSelectedTemplateData = this.selectTemplate(creativesParams._id) || {};
960
- const { name = '', versions = {} } = whatsappSelectedTemplateData;
961
- creativesParams.whatsappTemplateName = name;
962
- creativesParams.whatsappTemplateCategory = get(versions, `base.content.whatsapp.category`, '');
963
- creativesParams.whatsappTemplateLanguageCode = get(versions, `base.content.whatsapp.languages[0].language`, '');
964
- } else if (this.state.channel.toLocaleLowerCase() === ZALO_LOWERCASE) {
965
- const zaloSelectedTemplateData = this.selectTemplate(parseInt(creativesParams._id, 10)) || {};
966
- const { name = '' } = zaloSelectedTemplateData;
967
- creativesParams.name = name
968
- } else if (this.state.channel?.toLowerCase() === EMAIL_LOWERCASE) {
969
- const emailSelectedTemplateData = this.selectTemplate(creativesParams._id) || {};
970
- const activeTab = get(emailSelectedTemplateData, 'versions.base.activeTab', 'en');
971
- const isDragDrop = get(emailSelectedTemplateData, `versions.base.${activeTab}.is_drag_drop`, false);
972
- creativesParams.is_drag_drop = isDragDrop;
1009
+ if (pathname) {
1010
+ if (pathname.includes('create')) {
1011
+ creativesParams.mode = 'create';
1012
+ } else if (pathname.includes('edit') || pathname.includes('overview')) {
1013
+ creativesParams.mode = 'edit';
1014
+ if (pathname.includes('richmedia')) {
1015
+ creativesParams._id = pathname.split('/')[4];
1016
+ creativesParams.definition = {
1017
+ msgcontent: "RICH_MEDIA_WECHAT",
1018
+ };
1019
+ } else {
1020
+ creativesParams._id = pathname.split('/')[3];
1021
+ creativesParams.modeType = pathname.split('/')[4];
1022
+ creativesParams.account = pathname.split('/')[5];
1023
+ if (this.state.channel.toLowerCase() === WHATSAPP_LOWERCASE) {
1024
+ const whatsappSelectedTemplateData = this.selectTemplate(creativesParams._id) || {};
1025
+ const { name = '', versions = {} } = whatsappSelectedTemplateData;
1026
+ creativesParams.whatsappTemplateName = name;
1027
+ creativesParams.whatsappTemplateCategory = get(versions, `base.content.whatsapp.category`, '');
1028
+ creativesParams.whatsappTemplateLanguageCode = get(versions, `base.content.whatsapp.languages[0].language`, '');
1029
+ } else if (this.state.channel.toLocaleLowerCase() === ZALO_LOWERCASE) {
1030
+ const zaloSelectedTemplateData = this.selectTemplate(parseInt(creativesParams._id, 10)) || {};
1031
+ const { name = '' } = zaloSelectedTemplateData;
1032
+ creativesParams.name = name
1033
+ } else if (this.state.channel.toLowerCase() === WEBPUSH_LOWERCASE) {
1034
+ // For WebPush, extract only the fields the component uses
1035
+ const webpushSelectedTemplateData = this.selectTemplate(creativesParams._id) || {};
1036
+ const { name = '', definition = {}, versions = {} } = webpushSelectedTemplateData;
1037
+ creativesParams.name = name;
1038
+ creativesParams.definition = definition;
1039
+ creativesParams.versions = versions;
1040
+ }
973
1041
  }
1042
+ creativesParams.type = this.state.channel.toUpperCase();
974
1043
  }
975
- creativesParams.type = this.state.channel.toUpperCase();
976
1044
  }
977
1045
  }
978
1046
  if (this.state.previewOpen) {
@@ -1006,6 +1074,13 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1006
1074
  queryParams.host = params?.host || this.state?.hostName;
1007
1075
  };
1008
1076
 
1077
+ setWebpushQueryParams = (queryParams, params) => {
1078
+ const selectedAccount = this.props.Templates?.selectedWebPushAccount;
1079
+ queryParams.accountId = params?.accountId
1080
+ || selectedAccount?.accountId
1081
+ || selectedAccount?.id;
1082
+ };
1083
+
1009
1084
  getAllTemplates = ({params, getNextPage, resetPage}, resetTemplates) => {
1010
1085
  let queryParams = params || {};
1011
1086
  let page = this.state.page;
@@ -1037,6 +1112,9 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1037
1112
  if (this.state?.channel?.toLowerCase() === WHATSAPP_LOWERCASE) {
1038
1113
  this.setWhatsappQueryParams(queryParams, params);
1039
1114
  }
1115
+ if (this.state?.channel?.toLowerCase() === WEBPUSH_LOWERCASE) {
1116
+ this.setWebpushQueryParams(queryParams, params);
1117
+ }
1040
1118
  if (this.state?.channel?.toLowerCase() === RCS_LOWERCASE && !isEmpty(this.props.Templates?.selectedRcsAccount)) {
1041
1119
  const { sourceAccountIdentifier = '', configs: { accessToken = '' } = {}, hostName = '' } = this.props.Templates.selectedRcsAccount;
1042
1120
  if (sourceAccountIdentifier) {
@@ -1101,6 +1179,9 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1101
1179
  if (this.state?.channel?.toLowerCase() === WHATSAPP_LOWERCASE) {
1102
1180
  this.setWhatsappQueryParams(queryParams, params);
1103
1181
  }
1182
+ if (this.state?.channel?.toLowerCase() === WEBPUSH_LOWERCASE) {
1183
+ this.setWebpushQueryParams(queryParams, params);
1184
+ }
1104
1185
  if (this.state?.channel?.toLowerCase() === RCS_LOWERCASE && !isEmpty(this.props.Templates?.selectedRcsAccount)) {
1105
1186
  const { sourceAccountIdentifier = '', configs: { accessToken = '' } = {}, hostName = '' } = this.props.Templates.selectedRcsAccount;
1106
1187
  if (sourceAccountIdentifier) {
@@ -1193,19 +1274,6 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1193
1274
  return templates;
1194
1275
  }
1195
1276
 
1196
- filterInAppTemplates = (templates) => {
1197
- const { selectedInAppLayout } = this.state;
1198
- if (!selectedInAppLayout) {
1199
- return templates;
1200
- }
1201
- return templates.filter((template) => {
1202
- const androidBodyType = get(template, 'versions.base.content.ANDROID.bodyType');
1203
- const iosBodyType = get(template, 'versions.base.content.IOS.bodyType');
1204
- const inappBodyType = androidBodyType || iosBodyType;
1205
- return inappBodyType === selectedInAppLayout;
1206
- });
1207
- }
1208
-
1209
1277
  filterSMSTemplates = (templates) => {
1210
1278
  const { smsFilter } = this.state;
1211
1279
  if (SMS_FILTERS.ALL === smsFilter) {
@@ -1292,9 +1360,6 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1292
1360
  case ZALO:
1293
1361
  filteredTemplates = this.filterZaloTemplates(templates);
1294
1362
  break;
1295
- case INAPP:
1296
- filteredTemplates = this.filterInAppTemplates(templates);
1297
- break;
1298
1363
  default:
1299
1364
  break;
1300
1365
  }
@@ -1514,12 +1579,9 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1514
1579
  templateData.isNewMobilePush = commonUtil.hasNewMobilePushFeatureEnabled();
1515
1580
  }
1516
1581
  break;
1517
- case INAPP: {
1518
- // Pass the full template object to CapCustomCard so getInAppContent can extract the data
1519
- // Similar to how MOBILE_PUSH passes the full template when new feature is enabled
1582
+ case INAPP:
1520
1583
  templateData.content = template;
1521
1584
  break;
1522
- }
1523
1585
  case WECHAT:
1524
1586
  templateData.content = this.prepareWeChatPreviewData(template);
1525
1587
  break;
@@ -1796,6 +1858,70 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1796
1858
  templateData.content = <CapLabel type="label19" className="zalo-listing-content desc">{template.name}</CapLabel>;
1797
1859
  break;
1798
1860
  }
1861
+ case WEBPUSH: {
1862
+ const webpushContent = get(template, 'versions.base.content.webpush', {});
1863
+ const notificationTitle = webpushContent?.title || '';
1864
+ const notificationMessage = webpushContent?.message || '';
1865
+ const brandIcon = webpushContent?.brandIcon || '';
1866
+ const mediaImage = webpushContent?.image || '';
1867
+ const ctaButtons = Array.isArray(webpushContent?.ctas)
1868
+ ? webpushContent.ctas.filter(
1869
+ (cta) => typeof cta?.actionText === 'string' && cta.actionText.trim()
1870
+ )
1871
+ : [];
1872
+ const visibleCtas = ctaButtons.slice(0, 2);
1873
+ templateData.content = (
1874
+ <div className="sms-template-content webpush-template-content">
1875
+ <div className="webpush-template-card">
1876
+ <div className="webpush-template-meta">
1877
+ {brandIcon ? (
1878
+ <img
1879
+ src={brandIcon}
1880
+ alt="Brand Icon"
1881
+ className="webpush-brand-icon"
1882
+ />
1883
+ ) : (
1884
+ <span className="webpush-brand-icon-placeholder" />
1885
+ )}
1886
+ <div className="webpush-template-text">
1887
+ <div className="webpush-template-header">
1888
+ <div className="webpush-template-title">
1889
+ {notificationTitle}
1890
+ </div>
1891
+ <div className="webpush-template-time">2:29 PM</div>
1892
+ </div>
1893
+ {notificationMessage ? (
1894
+ <div className="webpush-template-message">
1895
+ {notificationMessage}
1896
+ </div>
1897
+ ) : null}
1898
+ {mediaImage ? (
1899
+ <img
1900
+ src={mediaImage}
1901
+ alt="Media"
1902
+ className="webpush-media-image"
1903
+ />
1904
+ ) : null}
1905
+ </div>
1906
+ </div>
1907
+ {visibleCtas.length ? (
1908
+ <div
1909
+ className={`webpush-template-cta-section${
1910
+ visibleCtas.length === 1 ? ' single-cta' : ''
1911
+ }`}
1912
+ >
1913
+ {visibleCtas.map((cta, index) => (
1914
+ <div className="webpush-template-cta-item" key={`webpush-cta-${index}`}>
1915
+ <span className="webpush-template-cta-text">{cta.actionText}</span>
1916
+ </div>
1917
+ ))}
1918
+ </div>
1919
+ ) : null}
1920
+ </div>
1921
+ </div>
1922
+ );
1923
+ break;
1924
+ }
1799
1925
  default:
1800
1926
  templateData.content = "";
1801
1927
  }
@@ -1831,9 +1957,9 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1831
1957
  const noLoaderAndSearchText = isEmpty(this.state.searchText) && !isLoading;
1832
1958
 
1833
1959
  return (<div>
1834
- {[WECHAT, MOBILE_PUSH, INAPP, WHATSAPP, ZALO,RCS].includes(currentChannel) && this.showAccountName()}
1960
+ {[WECHAT, MOBILE_PUSH, WEBPUSH, INAPP, WHATSAPP, ZALO, RCS].includes(currentChannel) && this.showAccountName()}
1835
1961
  {filterContent}
1836
- {[WHATSAPP, ZALO, INAPP,RCS].includes(currentChannel) && this.selectedFilters()}
1962
+ {[WHATSAPP, ZALO,RCS].includes(currentChannel) && this.selectedFilters()}
1837
1963
  {<div>
1838
1964
  {!isEmpty(filteredTemplates) || !isEmpty(this.state.searchText) || !isEmpty(this.props.Templates.templateError) ? (
1839
1965
  <div className={!isEmpty(this.state.searchText) && isEmpty(cardDataList) ? '' : this.isFullMode() ? "v2-pagination-container" : "v2-pagination-container-half"}>
@@ -1905,6 +2031,11 @@ return (<div>
1905
2031
  <ChannelTypeIllustration isFullMode={this.props.isFullMode} createTemplate={this.createTemplate} currentChannel={currentChannel}/>
1906
2032
  </div>
1907
2033
  }
2034
+ {showIllustrationForChannel(WEBPUSH_LOWERCASE) &&
2035
+ <div style={this.isFullMode() ? { height: "calc(100vh - 20.3125rem)", overflow: 'auto' } : {}}>
2036
+ <ChannelTypeIllustration isFullMode={this.props.isFullMode} createTemplate={this.createTemplate} currentChannel={currentChannel} hostName={this.state?.hostName}/>
2037
+ </div>
2038
+ }
1908
2039
  {<CapCustomSkeleton loader={isInitialLoading && (isLoading || getAllTemplatesInProgress)} />}
1909
2040
  {<CapPageSpinner spinning={!isInitialLoading && (isLoading || getAllTemplatesInProgress)} />}
1910
2041
  </div>
@@ -2043,9 +2174,6 @@ return (<div>
2043
2174
 
2044
2175
  prepareWeChatMappedPreviewData(content, templateTags, tagData) {
2045
2176
  let formattedContent = cloneDeep(content);
2046
- if (!formattedContent || typeof formattedContent !== 'string') {
2047
- return formattedContent || '';
2048
- }
2049
2177
  forEach(templateTags, (tag) => {
2050
2178
  if (tagData[tag].value !== undefined) {
2051
2179
  formattedContent = formattedContent.replace(`{{${tag}.DATA}}`, tagData[tag].value);
@@ -2301,6 +2429,10 @@ return (<div>
2301
2429
  params.host = this.state?.hostName;
2302
2430
  }
2303
2431
  this.delay(() => {
2432
+ // Clear previous state before loading newer templates for web push channel
2433
+ if (this.state.channel.toLowerCase() === WEBPUSH_LOWERCASE) {
2434
+ this.props.actions.resetTemplate();
2435
+ }
2304
2436
  this.getAllTemplates({params, resetPage: true});
2305
2437
  }, 500);
2306
2438
  });
@@ -2464,6 +2596,32 @@ return (<div>
2464
2596
  this.getAllTemplates({params: {}}, true);
2465
2597
  this.props.inAppActions.clearCreateResponse();
2466
2598
  });
2599
+ } else if (this.state.channel.toLowerCase() === "webpush") {
2600
+ const params = {
2601
+ name: this.state.searchText,
2602
+ sortBy: this.state.sortBy,
2603
+ };
2604
+
2605
+ // Get the selected WebPush account
2606
+ const selectedAccount = this.props.Templates?.selectedWebPushAccount;
2607
+
2608
+ // Set the accountId in the duplicate object
2609
+ if (duplicateObj.definition) {
2610
+ duplicateObj.definition.accountId = selectedAccount?.accountId || selectedAccount?.id;
2611
+ }
2612
+
2613
+ const channelLabel = this.props.intl.formatMessage(messages.webpushHeader);
2614
+ this.props.webpushActions.createTemplate(duplicateObj, (response) => {
2615
+ if (response && (response.templateId || response._id)) {
2616
+ // Clear response immediately to prevent componentWillReceiveProps from showing "created" notification
2617
+ this.props.webpushActions.clearCreateResponse();
2618
+ // Clear previous state before loading newer templates
2619
+ this.props.actions.resetTemplate();
2620
+ const message = `${channelLabel} ${this.props.intl.formatMessage(messages.templateDuplicateSuccess)}`;
2621
+ CapNotification.success({key: 'duplicateSuccess', message});
2622
+ this.getAllTemplates({params, resetPage: true});
2623
+ }
2624
+ }, { isDuplicate: true });
2467
2625
  } else if (this.state.channel.toLowerCase() === "ebill") {
2468
2626
  this.props.ebillActions.createTemplate(duplicateObj);
2469
2627
  } else if (this.state.channel.toLowerCase() === "email") {
@@ -2596,6 +2754,10 @@ return (<div>
2596
2754
  pathName = `/inapp/edit/${id}/`;
2597
2755
  break;
2598
2756
  }
2757
+ case WEBPUSH: {
2758
+ pathName = `/webpush/edit/${id}`;
2759
+ break;
2760
+ }
2599
2761
  default:
2600
2762
  break;
2601
2763
  }
@@ -2943,6 +3105,14 @@ return (<div>
2943
3105
  fetchingWeCrmAccounts,
2944
3106
  sendingFile,
2945
3107
  } = Templates;
3108
+
3109
+ // Show loading when in account selection or account change mode
3110
+ const isAccountSelectionMode = this.state.activeMode === ACCOUNT_SELECTION_MODE ||
3111
+ this.state.activeMode === ACCOUNT_CHANGE_MODE;
3112
+ if (isAccountSelectionMode) {
3113
+ return false;
3114
+ }
3115
+
2946
3116
  const lineLoader = this.checkLoader('line');
2947
3117
  const smsLoader = this.checkLoader('sms');
2948
3118
  const viberLoader = this.checkLoader(VIBER_CHANNEL);
@@ -2965,6 +3135,10 @@ return (<div>
2965
3135
  fetchingWeCrmAccounts ) &&
2966
3136
  this.state.channel.toLowerCase() === ZALO_LOWERCASE;
2967
3137
  const mobilePushLoader = (((getAllTemplatesInProgress) || (fetchingWeCrmAccounts)) && this.state.channel.toLowerCase() === 'mobilepush');
3138
+ const webpushLoader = (
3139
+ (getAllTemplatesInProgress && this.state.channel.toLowerCase() === WEBPUSH_LOWERCASE) ||
3140
+ (fetchingWeCrmAccounts && this.state.channel.toLowerCase() === WEBPUSH_LOWERCASE)
3141
+ );
2968
3142
  const inAppLoader = (((this.state.selectedAccount !== '' && getAllTemplatesInProgress) || (fetchingWeCrmAccounts)) && this.state.channel.toLowerCase() === INAPP_LOWERCASE);
2969
3143
  const emailLoader = (
2970
3144
  (getAllTemplatesInProgress ||
@@ -2979,6 +3153,7 @@ return (<div>
2979
3153
  (emailLoader !== undefined ? emailLoader : false) ||
2980
3154
  (ebillLoader !== undefined ? ebillLoader : false) ||
2981
3155
  (mobilePushLoader !== undefined ? mobilePushLoader : false) ||
3156
+ (webpushLoader !== undefined ? webpushLoader : false) ||
2982
3157
  (lineLoader !== undefined ? lineLoader : false) ||
2983
3158
  (facebookLoader !== undefined ? facebookLoader : false) ||
2984
3159
  (viberLoader !== undefined ? viberLoader : false) ||
@@ -3049,9 +3224,9 @@ return (<div>
3049
3224
  const isMobilePushChannel = channel === MOBILE_PUSH;
3050
3225
  const isInAppChannel = channel === INAPP;
3051
3226
  const isFacebookChannel = channel === FACEBOOK;
3052
- if ([WECHAT, MOBILE_PUSH, INAPP, LINE, ZALO, WHATSAPP, RCS].includes(channel) && !isEmpty(weCrmAccounts) && !isFacebookChannel) {
3227
+ if ([WECHAT, MOBILE_PUSH, INAPP, LINE, ZALO, WHATSAPP, WEBPUSH, RCS].includes(channel) && !isEmpty(weCrmAccounts) && !isFacebookChannel) {
3053
3228
  forEach(weCrmAccounts, (account) => {
3054
- if ((isWechatChannel && account.configs && account.configs.is_wecrm_enabled) || [MOBILE_PUSH, INAPP, LINE, ZALO, WHATSAPP, RCS].includes(channel)) {
3229
+ if ((isWechatChannel && account.configs && account.configs.is_wecrm_enabled) || [MOBILE_PUSH, INAPP, LINE, ZALO, WHATSAPP, WEBPUSH, RCS].includes(channel)) {
3055
3230
  if (query.type === 'embedded' && (!query.module || (query.module && query.module !== 'library'))) {
3056
3231
  if (query.source_account_id && account.sourceAccountIdentifier === query.source_account_id) {
3057
3232
  accountOptions.push(
@@ -3130,6 +3305,11 @@ return (<div>
3130
3305
  noAccountHeader = messages.noAccountsPresentZalo;
3131
3306
  break;
3132
3307
  }
3308
+ case WEBPUSH: {
3309
+ accountHeader = formatMessage(messages.webpushAccount);
3310
+ noAccountHeader = messages.noAccountsPresentWebpush;
3311
+ break;
3312
+ }
3133
3313
  case RCS: {
3134
3314
  accountHeader = formatMessage(messages.rcsAccount);
3135
3315
  noAccountHeader = messages.noAccountsPresentRcs;
@@ -3186,10 +3366,12 @@ return (<div>
3186
3366
  };
3187
3367
 
3188
3368
  setAccountSelectionMode = () => {
3369
+ this.props.actions.resetTemplate();
3189
3370
  this.setState({activeMode: ACCOUNT_SELECTION_MODE});
3190
3371
  }
3191
3372
 
3192
3373
  setAccountChangeMode = () => {
3374
+ this.props.actions.resetTemplate();
3193
3375
  this.setState({activeMode: ACCOUNT_CHANGE_MODE});
3194
3376
  }
3195
3377
 
@@ -3206,6 +3388,7 @@ return (<div>
3206
3388
 
3207
3389
  const channelObj = {
3208
3390
  [MOBILE_PUSH]: 'mobilepushAccount',
3391
+ [WEBPUSH]: 'webpushAccount',
3209
3392
  [WECHAT]: 'wechatAccount',
3210
3393
  [WHATSAPP]: 'whatsappAccount',
3211
3394
  [INAPP]: 'inappAccount',
@@ -3241,19 +3424,14 @@ return (<div>
3241
3424
  }
3242
3425
 
3243
3426
  removeZaloFilter = () => this.setState({ selectedZaloStatus: null });
3244
- removeInAppLayoutFilter = () => this.setState({ selectedInAppLayout: '' });
3245
3427
 
3246
3428
  selectedFilters = () => {
3247
- const { selectedWhatsappStatus, selectedWhatsappCategory, selectedZaloStatus, selectedInAppLayout } = this.state;
3429
+ const { selectedWhatsappStatus, selectedWhatsappCategory, selectedZaloStatus } = this.state;
3248
3430
  const {
3249
3431
  intl: {
3250
3432
  formatMessage,
3251
3433
  },
3252
3434
  } = this.props;
3253
- const getInAppLayoutLabel = (layoutValue) => {
3254
- const layoutOption = INAPP_LAYOUT_OPTIONS.find(opt => opt.value === layoutValue);
3255
- return layoutOption ? layoutOption.label : layoutValue;
3256
- };
3257
3435
  return (
3258
3436
  <CapRow type="flex" align="middle" className="selected-whatsapp-filters">
3259
3437
  {
@@ -3288,23 +3466,6 @@ return (<div>
3288
3466
  </CapTag>
3289
3467
  ) : null
3290
3468
  }
3291
- {
3292
- selectedInAppLayout ? (
3293
- <CapTag closable onClose={this.removeInAppLayoutFilter}>
3294
- {formatMessage(messages.layout)}: {getInAppLayoutLabel(selectedInAppLayout)}
3295
- </CapTag>
3296
- ) : null
3297
- }
3298
- {
3299
- selectedInAppLayout ? (
3300
- <CapLink
3301
- onClick={() => {
3302
- this.removeInAppLayoutFilter();
3303
- }}
3304
- title={this.props.intl.formatMessage(messages.clearAll)}
3305
- />
3306
- ) : null
3307
- }
3308
3469
  </CapRow>
3309
3470
  );
3310
3471
  }
@@ -3315,7 +3476,6 @@ return (<div>
3315
3476
  setLineFilter = (e) => this.setState({lineFilter: e.target.value});
3316
3477
  setSMSFilter = (e) => this.setState({smsFilter: e.target.value});
3317
3478
  setZaloStatus = (value) => this.setState({selectedZaloStatus: value?.item?.props?.value});
3318
- setInAppLayout = (value) => this.setState({selectedInAppLayout: value?.item?.props?.value});
3319
3479
 
3320
3480
  openCreativesFullMode = () => {
3321
3481
  const channelLowerCase = this.state.channel.toLowerCase();
@@ -3568,21 +3728,6 @@ return (<div>
3568
3728
  )
3569
3729
  : () => null
3570
3730
  }
3571
- {
3572
- channel.toUpperCase() === INAPP && (
3573
- <div className="inapp-filters">
3574
- <CapSelectFilter
3575
- placement="bottomLeft"
3576
- data={INAPP_LAYOUT_OPTIONS}
3577
- onSelect={this.setInAppLayout}
3578
- selectedValue={this.state.selectedInAppLayout}
3579
- placeholder="Layout"
3580
- showBadge
3581
- width="120px"
3582
- />
3583
- </div>
3584
- )
3585
- }
3586
3731
  <div style={{display: "flex", justifyContent: "space-between", alignItems: 'center'}}>
3587
3732
  {
3588
3733
  this.state?.channel?.toLowerCase() === WHATSAPP_LOWERCASE && (isWhatsappCountExeeded)? (
@@ -3803,6 +3948,8 @@ Templates.propTypes = {
3803
3948
  rcsActions: PropTypes.object,
3804
3949
  zaloActions: PropTypes.object,
3805
3950
  inAppActions: PropTypes.object,
3951
+ webpushActions: PropTypes.object,
3952
+ WebPush: PropTypes.object,
3806
3953
  smsRegister: PropTypes.any,
3807
3954
  isDltFromRcs: PropTypes.bool,
3808
3955
  };
@@ -3822,6 +3969,7 @@ const mapStateToProps = createStructuredSelector({
3822
3969
  Rcs: makeSelectRcs(),
3823
3970
  Zalo: makeSelectZalo(),
3824
3971
  InApp: makeSelectInApp(),
3972
+ WebPush: makeSelectWebPush(),
3825
3973
  });
3826
3974
 
3827
3975
  function mapDispatchToProps(dispatch) {
@@ -3841,6 +3989,7 @@ function mapDispatchToProps(dispatch) {
3841
3989
  whatsappActions: bindActionCreators(whatsappActions, dispatch),
3842
3990
  rcsActions: bindActionCreators(rcsActions, dispatch),
3843
3991
  zaloActions: bindActionCreators(zaloActions, dispatch),
3992
+ webpushActions: bindActionCreators(webpushActions, dispatch),
3844
3993
  };
3845
3994
  }
3846
3995
 
@@ -3854,6 +4003,7 @@ export default compose(
3854
4003
  UserIsAuthenticated,
3855
4004
  withSaga,
3856
4005
  withMobilePushNewSaga,
4006
+ withWebPushSaga,
3857
4007
  withReducer,
3858
4008
  withConnect,
3859
4009
  )(injectIntl(Templates));