@capillarytech/creatives-library 8.0.130 → 8.0.132

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 (77) hide show
  1. package/containers/App/constants.js +1 -0
  2. package/containers/Login/index.js +1 -2
  3. package/package.json +1 -1
  4. package/services/api.js +5 -0
  5. package/tests/integration/TemplateCreation/TemplateCreation.integration.test.js +8 -3
  6. package/tests/integration/TemplateCreation/api-response.js +5 -0
  7. package/tests/integration/TemplateCreation/msw-handler.js +42 -63
  8. package/utils/common.js +7 -0
  9. package/utils/commonUtils.js +2 -6
  10. package/utils/createMobilePushPayload.js +322 -0
  11. package/utils/tests/createMobilePushPayload.test.js +1054 -0
  12. package/v2Components/CapDeviceContent/index.js +1 -1
  13. package/v2Components/CapImageUpload/index.js +57 -44
  14. package/v2Components/CapInAppCTA/index.js +1 -0
  15. package/v2Components/CapMpushCTA/constants.js +25 -0
  16. package/v2Components/CapMpushCTA/index.js +403 -0
  17. package/v2Components/CapMpushCTA/index.scss +95 -0
  18. package/v2Components/CapMpushCTA/messages.js +101 -0
  19. package/v2Components/CapTagList/index.js +178 -121
  20. package/v2Components/CapVideoUpload/constants.js +3 -0
  21. package/v2Components/CapVideoUpload/index.js +182 -115
  22. package/v2Components/CapVideoUpload/messages.js +16 -0
  23. package/v2Components/Carousel/index.js +15 -13
  24. package/v2Components/ErrorInfoNote/style.scss +1 -0
  25. package/v2Components/MobilePushPreviewV2/index.js +57 -12
  26. package/v2Components/TemplatePreview/_templatePreview.scss +218 -74
  27. package/v2Components/TemplatePreview/assets/images/Android_With_date_and_time.svg +29 -0
  28. package/v2Components/TemplatePreview/assets/images/android.svg +9 -0
  29. package/v2Components/TemplatePreview/assets/images/iOS_With_date_and_time.svg +26 -0
  30. package/v2Components/TemplatePreview/assets/images/ios.svg +9 -0
  31. package/v2Components/TemplatePreview/index.js +234 -107
  32. package/v2Components/TemplatePreview/messages.js +4 -0
  33. package/v2Components/TemplatePreview/tests/__snapshots__/index.test.js.snap +10 -10
  34. package/v2Containers/CreativesContainer/SlideBoxContent.js +127 -62
  35. package/v2Containers/CreativesContainer/index.js +193 -136
  36. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +0 -22
  37. package/v2Containers/InApp/constants.js +1 -0
  38. package/v2Containers/InApp/index.js +13 -13
  39. package/v2Containers/Line/Container/tests/__snapshots__/index.test.js.snap +4748 -4658
  40. package/v2Containers/Login/index.js +1 -2
  41. package/v2Containers/MobilePush/Create/index.js +1 -0
  42. package/v2Containers/MobilePush/commonMethods.js +7 -14
  43. package/v2Containers/MobilePush/tests/commonMethods.test.js +401 -0
  44. package/v2Containers/MobilePushNew/actions.js +116 -0
  45. package/v2Containers/MobilePushNew/components/CtaButtons.js +183 -0
  46. package/v2Containers/MobilePushNew/components/MediaUploaders.js +835 -0
  47. package/v2Containers/MobilePushNew/components/PlatformContentFields.js +346 -0
  48. package/v2Containers/MobilePushNew/components/index.js +5 -0
  49. package/v2Containers/MobilePushNew/components/tests/CtaButtons.test.js +565 -0
  50. package/v2Containers/MobilePushNew/components/tests/MediaUploaders.test.js +3180 -0
  51. package/v2Containers/MobilePushNew/components/tests/PlatformContentFields.test.js +654 -0
  52. package/v2Containers/MobilePushNew/constants.js +116 -0
  53. package/v2Containers/MobilePushNew/hooks/tests/usePlatformSync.test.js +1462 -0
  54. package/v2Containers/MobilePushNew/hooks/tests/useUpload.test.js +1459 -0
  55. package/v2Containers/MobilePushNew/hooks/usePlatformSync.js +366 -0
  56. package/v2Containers/MobilePushNew/hooks/useUpload.js +740 -0
  57. package/v2Containers/MobilePushNew/index.js +2158 -0
  58. package/v2Containers/MobilePushNew/index.scss +308 -0
  59. package/v2Containers/MobilePushNew/messages.js +272 -0
  60. package/v2Containers/MobilePushNew/reducer.js +160 -0
  61. package/v2Containers/MobilePushNew/sagas.js +193 -0
  62. package/v2Containers/MobilePushNew/selectors.js +55 -0
  63. package/v2Containers/MobilePushNew/tests/reducer.test.js +741 -0
  64. package/v2Containers/MobilePushNew/tests/sagas.test.js +864 -0
  65. package/v2Containers/MobilePushNew/tests/selectors.test.js +665 -0
  66. package/v2Containers/MobilePushNew/tests/utils.test.js +421 -0
  67. package/v2Containers/MobilePushNew/utils.js +84 -0
  68. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +1176 -976
  69. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +684 -424
  70. package/v2Containers/TagList/index.js +56 -10
  71. package/v2Containers/Templates/_templates.scss +100 -1
  72. package/v2Containers/Templates/index.js +170 -31
  73. package/v2Containers/Templates/messages.js +8 -0
  74. package/v2Containers/Templates/sagas.js +1 -0
  75. package/v2Containers/Whatsapp/constants.js +1 -0
  76. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +3992 -3677
  77. package/assets/loading_img.gif +0 -0
@@ -39,12 +39,14 @@ export class TagList extends React.Component { // eslint-disable-line react/pref
39
39
  loading: false,
40
40
  tags: [],
41
41
  tagsError: false,
42
+ currentContext: null, // Track current context to detect changes
42
43
  };
43
44
  this.renderTags = this.renderTags.bind(this);
44
45
  this.populateTags = this.populateTags.bind(this);
45
46
  this.populateTagForChildren = this.populateTagForChildren.bind(this);
46
47
  this.transformInjectedTags = this.transformInjectedTags.bind(this);
47
48
  this.transformCouponTags = this.transformCouponTags.bind(this);
49
+ this.loadingTimeout = null; // Add timeout reference
48
50
  }
49
51
 
50
52
  componentDidMount() {
@@ -52,33 +54,72 @@ export class TagList extends React.Component { // eslint-disable-line react/pref
52
54
  }
53
55
 
54
56
  componentWillReceiveProps(nextProps) {
55
- if (_.isEmpty(this.props.injectedTags) && _.isEmpty(this.props.tags)) {
57
+ // Set loading to true in these scenarios:
58
+ // 1. Both injectedTags and tags are empty (initial load)
59
+ // 2. Context change is happening (detected by different tag arrays)
60
+ const { injectedTags: currentInjectedTags, tags: currentTags } = this.props;
61
+ const { injectedTags: nextInjectedTags, tags: nextTags, fetchingSchemaError } = nextProps;
62
+
63
+ const isInitialLoad = _.isEmpty(currentInjectedTags) && _.isEmpty(currentTags);
64
+ const isContextChange = !_.isEqual(nextTags, currentTags) && !_.isEmpty(currentTags);
65
+
66
+ if (isInitialLoad || isContextChange) {
56
67
  this.setState({loading: true});
57
68
  }
58
- if (!_.isEqual(nextProps.injectedTags, this.props.injectedTags)) {
69
+
70
+ // Only reset loading for injectedTags changes if we're not currently loading due to context change
71
+ if (!_.isEqual(nextInjectedTags, currentInjectedTags) && !this.state.loading) {
59
72
  this.setState({loading: false});
73
+ this.clearLoadingTimeout();
60
74
  }
61
- if (!_.isEqual(nextProps.tags, this.props.tags)) {
75
+
76
+ if (!_.isEqual(nextTags, currentTags)) {
62
77
  this.setState({loading: false});
78
+ this.clearLoadingTimeout();
63
79
  }
64
- if (nextProps?.fetchingSchemaError) {
65
- this.setState({tagsError: nextProps?.fetchingSchemaError});
80
+ if (fetchingSchemaError) {
81
+ this.setState({tagsError: fetchingSchemaError, loading: false});
82
+ this.clearLoadingTimeout();
66
83
  }
67
84
  }
68
85
 
69
86
  componentDidUpdate(prevProps) {
70
- if (this.props.tags !== prevProps.tags || this.props.injectedTags !== prevProps.injectedTags || this.props.selectedOfferDetails !== prevProps.selectedOfferDetails) {
87
+ const { tags, injectedTags, selectedOfferDetails } = this.props;
88
+ const { tags: prevTags, injectedTags: prevInjectedTags, selectedOfferDetails: prevSelectedOfferDetails } = prevProps;
89
+
90
+ if (tags !== prevTags || injectedTags !== prevInjectedTags || selectedOfferDetails !== prevSelectedOfferDetails) {
71
91
  this.generateTags(this.props);
72
92
  }
73
93
  }
74
94
 
95
+ componentWillUnmount() {
96
+ this.clearLoadingTimeout();
97
+ }
98
+
99
+ clearLoadingTimeout = () => {
100
+ if (this.loadingTimeout) {
101
+ clearTimeout(this.loadingTimeout);
102
+ this.loadingTimeout = null;
103
+ }
104
+ };
105
+
75
106
  onSelect = (selectedKeys) => {
76
- this.props.onTagSelect(selectedKeys[0]);
107
+ const { onTagSelect } = this.props;
108
+ onTagSelect(selectedKeys[0]);
77
109
  };
78
110
 
79
111
  getTagsforContext = (data) => {
80
- //this.setState({loading: true});
81
- this.props.onContextChange(data);
112
+ // Set loading state when context change is requested
113
+ this.setState({loading: true, currentContext: data});
114
+
115
+ // Set a timeout to prevent infinite loading (fallback safety)
116
+ this.clearLoadingTimeout();
117
+ this.loadingTimeout = setTimeout(() => {
118
+ this.setState({loading: false});
119
+ }, 5000); // Reduced timeout to 5 seconds for better UX
120
+
121
+ const { onContextChange } = this.props;
122
+ onContextChange(data);
82
123
  }
83
124
 
84
125
  generateTags = (props) => {
@@ -159,7 +200,8 @@ export class TagList extends React.Component { // eslint-disable-line react/pref
159
200
  //Form tags object with tag headers
160
201
  _.forEach(tagsList, (temp) => {
161
202
  const tag = temp.definition;
162
- const { locale: userLocale } = this.props?.intl || {};
203
+ const { intl } = this.props;
204
+ const { locale: userLocale } = intl || {};
163
205
 
164
206
  // Check if the tag.value should be skipped based on feature control
165
207
  if (_.includes(excludedTags, tag.value)) {
@@ -360,6 +402,10 @@ TagList.propTypes = {
360
402
  disabled: PropTypes.bool,
361
403
  fetchingSchemaError: PropTypes.bool,
362
404
  eventContextTags: PropTypes.array,
405
+ intl: PropTypes.shape({
406
+ formatMessage: PropTypes.func.isRequired,
407
+ locale: PropTypes.string,
408
+ }).isRequired,
363
409
  };
364
410
 
365
411
  const mapStateToProps = createStructuredSelector({
@@ -219,6 +219,105 @@
219
219
  }
220
220
  }
221
221
  }
222
+
223
+ .MOBILEPUSH {
224
+ .ant-card-body {
225
+ padding: 0;
226
+ background-color: $CAP_WHITE;
227
+ border-top: 1px solid $CAP_COLOR_16;
228
+ .ant-card-meta {
229
+ background-color: $CAP_G09;
230
+ .ant-card-meta-description {
231
+ .mobilepush-container {
232
+ background-color: $CAP_WHITE;
233
+ padding: $CAP_SPACE_12;
234
+ .app-header {
235
+ color: #5D5D5D;
236
+ font-weight: 600;
237
+ padding: 0.285rem 0.571rem 0.285rem 0.571rem;
238
+ display: flex;
239
+ align-items: center;
240
+ justify-content: space-between;
241
+ .app-header-left{
242
+ display: flex;
243
+ align-items: center;
244
+ div {
245
+ word-break: break-word;
246
+ }
247
+ .app-icon {
248
+ width: 0.857rem;
249
+ height: 0.857rem;
250
+ border-radius: 50%;
251
+ background-color: #737070;
252
+ }
253
+ }
254
+ }
255
+ .mobilepush-message {
256
+ padding-left: 1.65rem;
257
+ }
258
+ .mobilepush-image {
259
+ width: 100%;
260
+ height: 120px;
261
+ margin-top: 10px;
262
+ }
263
+ .mobilepush-image-padding {
264
+ padding-left: 1.65rem;
265
+ }
266
+ .scroll-container {
267
+ overflow-x: auto;
268
+ display: flex;
269
+ padding-top: $CAP_SPACE_06;
270
+ padding-right: $CAP_SPACE_06;
271
+ white-space: nowrap;
272
+ scrollbar-width: none; // Hide scrollbar in Firefox
273
+ &::-webkit-scrollbar {
274
+ display: none; // Hide scrollbar in Chrome/Safari/Opera
275
+ }
276
+ overflow: hidden;
277
+ height: 100%;
278
+ width: 100%;
279
+ margin-left: 1.65rem;
280
+ .whatsapp-carousel-container {
281
+ padding: $CAP_SPACE_04 0px $CAP_SPACE_08;
282
+ border-radius: $CAP_SPACE_06;
283
+ background-color: $CAP_WHITE;
284
+ width: 80%;
285
+ flex-shrink: 0;
286
+ white-space: pre-wrap;
287
+ word-break: break-word;
288
+ overflow: auto;
289
+ text-align: left;
290
+ margin: 0;
291
+ .whatsapp-carousel-card {
292
+ margin: $CAP_SPACE_02 $CAP_SPACE_02 $CAP_SPACE_01 $CAP_SPACE_02;
293
+ .whatsapp-carousel-body {
294
+ margin-bottom: $CAP_SPACE_08;
295
+ }
296
+ }
297
+ }
298
+ }
299
+ .actions{
300
+ background-color: #ffffff;
301
+ height: auto;
302
+ padding: $CAP_SPACE_08;
303
+ text-align: center;
304
+ display: flex;
305
+ flex-direction: column;
306
+ align-items: center;
307
+ justify-content: center;
308
+ gap: $CAP_SPACE_08;
309
+ .action{
310
+ font-size: $FONT_SIZE_S;
311
+ font-weight: 600;
312
+ color: #1970DA;
313
+ height: 1.25rem;
314
+ }
315
+ }
316
+ }
317
+ }
318
+ }
319
+ }
320
+ }
222
321
  }
223
322
 
224
323
  .create-new-link{
@@ -502,7 +601,7 @@
502
601
  }
503
602
  }
504
603
  .iphone-push-message-Container{
505
- top: 8%;
604
+ top: 10%;
506
605
  .message-pop{
507
606
  border-radius: 4px;
508
607
  word-wrap: break-word;
@@ -12,7 +12,7 @@ import styled from 'styled-components';
12
12
  import {injectIntl, intlShape, FormattedMessage } from 'react-intl';
13
13
  import { createStructuredSelector } from 'reselect';
14
14
  import moment from "moment";
15
- import Bugsnag from "@bugsnag/js";
15
+
16
16
  import get from 'lodash/get';
17
17
  import isEmpty from 'lodash/isEmpty';
18
18
  import isEqual from 'lodash/isEqual';
@@ -50,26 +50,26 @@ import {
50
50
  CapImage,
51
51
  CapStatus,
52
52
  CapColoredTag,
53
- CapSpin
53
+ CapSpin,
54
+ CapCard,
55
+ CapColumn,
56
+ CapCarousel
54
57
  } from "@capillarytech/cap-ui-library";
55
58
  import { makeSelectTemplates, makeSelectTemplatesResponse } from './selectors';
56
- import { makeSelectCreate as makeSelectCreateSms} from '../Sms/Create/selectors';
59
+ import { makeSelectCreate as makeSelectCreateSms } from '../Sms/Create/selectors';
57
60
  import { makeSelectCreate as makeSelectCreateMobilePush } from '../MobilePush/Create/selectors';
58
- import { makeSelectInApp } from '../InApp/selectors';
61
+ import { makeSelectMobilePushNew } from '../MobilePushNew/selectors';
59
62
  import { makeSelectEbill as makeSelectCreateEbill } from '../Ebill/selectors';
60
63
  import { makeSelectEmail as makeSelectCreateEmail } from '../Email/selectors';
61
64
  import { makeSelectEdit } from '../Sms/Edit/selectors';
62
65
  import { makeSelectLine } from '../Line/Container/selectors';
63
66
  import { makeSelectViber } from '../Viber/selectors';
64
67
  import { makeSelectZalo } from '../Zalo/selectors';
65
- import { UserIsAuthenticated } from '../../utils/authWrapper';
66
- import { getObjFromQueryParams } from '../../utils/v2common';
67
- import messages from './messages';
68
- import {checkUnicode} from '../../utils/smsCharCountV2';
68
+ import { makeSelectInApp } from '../InApp/selectors';
69
69
  import * as actions from './actions';
70
70
  import * as smsActions from '../Sms/Create/actions';
71
71
  import * as mobilepushActions from '../MobilePush/Create/actions';
72
- import * as inAppActions from '../InApp/actions';
72
+ import * as mobilePushNewActions from '../MobilePushNew/actions';
73
73
  import * as smsEditActions from '../Sms/Edit/actions';
74
74
  import * as ebillActions from '../Ebill/actions';
75
75
  import * as emailActions from '../Email/actions';
@@ -79,11 +79,16 @@ import * as facebookActions from '../Facebook/actions';
79
79
  import * as whatsappActions from '../Whatsapp/actions';
80
80
  import * as rcsActions from '../Rcs/actions';
81
81
  import * as zaloActions from '../Zalo/actions';
82
+ import * as inAppActions from '../InApp/actions';
83
+ import * as globalActions from '../Cap/actions';
84
+ import { makeSelectAuthenticated } from '../Cap/selectors';
85
+ import { UserIsAuthenticated } from '../../utils/authWrapper';
86
+ import { getObjFromQueryParams } from '../../utils/v2common';
87
+ import messages from './messages';
88
+ import {checkUnicode} from '../../utils/smsCharCountV2';
82
89
  import CardGrid from '../../v2Components/CardGrid';
83
90
  import config from '../../config/app';
84
91
  import './_templates.scss';
85
- import * as globalActions from '../Cap/actions';
86
- import { makeSelectAuthenticated } from '../Cap/selectors';
87
92
  import * as commonUtil from '../../utils/common';
88
93
  import Pagination from '../../v2Components/Pagination';
89
94
  import EmailPreview from '../../v2Components/EmailPreview';
@@ -117,6 +122,7 @@ import {
117
122
  HOST_ICS,
118
123
  IMAGE,
119
124
  VIDEO,
125
+ GIF,
120
126
  } from '../Whatsapp/constants';
121
127
  import { INAPP_LAYOUT_DETAILS } from '../InApp/constants';
122
128
  import { ZALO_STATUS_OPTIONS, ZALO_STATUSES } from '../Zalo/constants';
@@ -149,6 +155,9 @@ import { compose } from 'redux';
149
155
  import { v2TemplateSaga } from './sagas';
150
156
  import injectSaga from '../../utils/injectSaga';
151
157
  import { DAEMON } from '@capillarytech/vulcan-react-sdk/utils/sagaInjectorTypes';
158
+ import { v2MobilePushSagas } from '../MobilePushNew/sagas';
159
+ import { AUTO_CAROUSEL, BIG_PICTURE, FILMSTRIP_CAROUSEL, MANUAL_CAROUSEL } from '../MobilePushNew/constants';
160
+ const withMobilePushNewSaga = injectSaga({ key: 'mobilePushNew', saga: v2MobilePushSagas, mode: DAEMON });
152
161
 
153
162
  const { timeTracker } = GA;
154
163
  const {CapCustomCardList} = CapCustomCard;
@@ -237,28 +246,29 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
237
246
  searchedZaloTemplates: [],
238
247
  searchingZaloTemplate: false,
239
248
  zaloPreviewItemId: {},
249
+ uploadInProgress: false,
250
+ deleteConfirmationModal: false,
251
+ templateToDelete: {},
252
+ edmDefaultTemplateSelection: false,
253
+ edmSelectedTemplateId: '',
254
+ edmEmailPreview: false,
255
+ emailPreviewDevice: 'desktop',
256
+ // Add flag to prevent duplicate API calls
257
+ isProcessingEditResponse: false,
240
258
  };
241
-
259
+ this.getAllTemplates = this.getAllTemplates.bind(this);
260
+ this.createTemplate = this.createTemplate.bind(this);
261
+ this.searchTemplate = this.searchTemplate.bind(this);
262
+ this.handleSortBy = this.handleSortBy.bind(this);
263
+ this.duplicateTemplate = this.duplicateTemplate.bind(this);
264
+ this.deleteTemplate = this.deleteTemplate.bind(this);
242
265
  this.handleOnHoverItem = this.handleOnHoverItem.bind(this);
243
266
  this.handleOnItemClick = this.handleOnItemClick.bind(this);
244
267
  this.handleEditClick = this.handleEditClick.bind(this);
245
268
  this.handlePreviewClick = this.handlePreviewClick.bind(this);
246
- this.createTemplate = this.createTemplate.bind(this);
247
- this.duplicateTemplate = this.duplicateTemplate.bind(this);
248
- this.deleteTemplate = this.deleteTemplate.bind(this);
249
- this.togglePreview = this.togglePreview.bind(this);
250
- this.searchTemplate = this.searchTemplate.bind(this);
251
- this.handleSortBy = this.handleSortBy.bind(this);
252
- this.getCurrentWindow = this.getCurrentWindow.bind(this);
253
269
  this.handleFrameTasks = this.handleFrameTasks.bind(this);
254
- this.startTemplateCreation = this.startTemplateCreation.bind(this);
255
- this.startLoading = this.startLoading.bind(this);
256
- this.prepareWeChatPreviewData = this.prepareWeChatPreviewData.bind(this);
257
- this.prepareWeChatMappedPreviewData = this.prepareWeChatMappedPreviewData.bind(this);
258
270
  this.onAccountSelect = this.onAccountSelect.bind(this);
259
- this.prepareMobilePushPreviewData = this.prepareMobilePushPreviewData.bind(this);
260
- this.menuOnClickEvent = this.menuOnClickEvent.bind(this);
261
- this.delayTimer = 0;
271
+ this.onAccountSelect = this.onAccountSelect.bind(this);
262
272
  }
263
273
 
264
274
  // This function is to map the selected source account identifier and the connectionProperties data of domainPropertiesData object to get the hostName.
@@ -398,6 +408,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
398
408
  window.addEventListener("message", this.handleFrameTasks);
399
409
  }
400
410
  componentWillReceiveProps(nextProps = {}) {
411
+
401
412
  const params = {
402
413
  name: this.state.searchText,
403
414
  sortBy: this.state.sortBy,
@@ -567,6 +578,31 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
567
578
  this.props.lineActions.clearCreateResponse();
568
579
  }
569
580
 
581
+ // Check for MobilePushNew edit response - prevent duplicate calls
582
+ if (selectedChannel === MOBILE_PUSH_LOWERCASE && nextProps.MobilePushNew && nextProps.MobilePushNew.editResponse && nextProps.MobilePushNew.editResponse.templateId && nextProps.MobilePushNew.editResponse.templateId !== '' && !this.state.isProcessingEditResponse) {
583
+ // Set flag to prevent duplicate processing
584
+ this.setState({ isProcessingEditResponse: true });
585
+
586
+ const message = `${this.state.channel} ${this.props.intl.formatMessage(messages.templateUpdateSuccess)}`;
587
+
588
+ CapNotification.success({
589
+ key: 'editSuccess',
590
+ message
591
+ });
592
+
593
+ this.getAllTemplates({params, resetPage: true});
594
+
595
+ this.props.mobilePushNewActions.clearData();
596
+
597
+ this.props.mobilePushNewActions.clearEditResponse();
598
+
599
+ // Reset flag after a delay to allow for next edit
600
+ setTimeout(() => {
601
+ this.setState({ isProcessingEditResponse: false });
602
+ }, 1000);
603
+ }
604
+
605
+
570
606
 
571
607
  if (nextProps.Create && this.props.Create && nextProps.Create.createTemplateError && !isEqual(nextProps.Create.createTemplateError, this.props.Create.createTemplateError)) {
572
608
  const message = this.props.intl.formatMessage(messages.somethingWentWrong);
@@ -1238,6 +1274,99 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1238
1274
  break;
1239
1275
  }
1240
1276
  case MOBILE_PUSH:
1277
+ if (!commonUtil.hasNewMobilePushFeatureEnabled()) {
1278
+ templateData.content = template;
1279
+ } else {
1280
+ const mpushData = get(template, 'versions.base', template);
1281
+ const androidData = get(mpushData, 'ANDROID') || get(mpushData, 'androidContent');
1282
+ const iosData = get(mpushData, 'IOS') || get(mpushData, 'iosContent');
1283
+ let mpushListingData = androidData;
1284
+ if (isEmpty(androidData) || !androidData?.title) {
1285
+ mpushListingData = iosData;
1286
+ };
1287
+ const { title, message, expandableDetails: { style = '', image, carouselData = [], ctas = [] } = {} } = mpushListingData || {};
1288
+ templateData.content = (
1289
+ <CapCard className='mobilepush-container'>
1290
+ <CapHeader className="app-header">
1291
+ <CapRow className="app-header-left">
1292
+ <span className="app-icon">{""}</span>
1293
+ <CapLabel type="label4">{title}</CapLabel>
1294
+ </CapRow>
1295
+ </CapHeader>
1296
+ <CapLabel className="mobilepush-message" type="label1">{message}</CapLabel>
1297
+ {style === BIG_PICTURE && (
1298
+ <CapImage src={image} className="mobilepush-image mobilepush-image-padding" />
1299
+ )}
1300
+ {(style === MANUAL_CAROUSEL || style === AUTO_CAROUSEL || style === FILMSTRIP_CAROUSEL) && (
1301
+ <CapCarousel className="scroll-container">
1302
+ {carouselData.map((data, index) => {
1303
+ return (
1304
+ <CapCard
1305
+ key={`carousel-${index + 1}`}
1306
+ className="whatsapp-carousel-container"
1307
+ role="group"
1308
+ >
1309
+ <CapCard className="whatsapp-carousel-card">
1310
+ {data?.mediaType === IMAGE.toLowerCase() && (
1311
+ <CapImage
1312
+ src={data?.imageUrl ? data?.imageUrl : whatsappImageEmptyPreview}
1313
+ className="whatsapp-image"
1314
+ />
1315
+ )}
1316
+ {data?.mediaType === VIDEO.toLowerCase() && (
1317
+ <CapCard className="video-preview">
1318
+ <CapImage
1319
+ src={data?.videoPreviewUrl ? data?.videoPreviewUrl : data?.videoPreviewImg ? data?.videoPreviewImg : whatsappVideoEmptyPreview}
1320
+ className="whatsapp-image"
1321
+ />
1322
+ <CapCard className="icon-position">
1323
+ <CapImage
1324
+ className="video-icon"
1325
+ src={videoPlay}
1326
+ />
1327
+ </CapCard>
1328
+ </CapCard>
1329
+ )}
1330
+ {
1331
+ data?.mediaType === GIF.toLowerCase() && (
1332
+ <CapCard className="image-preview">
1333
+ <CapImage
1334
+ src={data?.imageUrl ? data?.imageUrl : whatsappImageEmptyPreview}
1335
+ className="whatsapp-image"
1336
+ />
1337
+ <CapCard className="icon-position">
1338
+ <CapImage
1339
+ className="video-icon"
1340
+ src={data?.imageUrl ? data?.imageUrl : whatsappImageEmptyPreview}
1341
+ />
1342
+ </CapCard>
1343
+ </CapCard>
1344
+ )
1345
+ }
1346
+ </CapCard>
1347
+ </CapCard>
1348
+ )
1349
+ })}
1350
+ </CapCarousel>
1351
+ )}
1352
+ {ctas.length > 0 && (
1353
+ <CapRow className="actions">
1354
+ {ctas.map((cta) => (
1355
+ <CapLabel
1356
+ className="action"
1357
+ key={`action-${cta?.actionText}`}
1358
+ type="label1"
1359
+ >
1360
+ {cta?.actionText && cta?.actionText.toUpperCase()}
1361
+ </CapLabel>
1362
+ ))}
1363
+ </CapRow>
1364
+ )}
1365
+ </CapCard>
1366
+ );
1367
+ templateData.isNewMobilePush = commonUtil.hasNewMobilePushFeatureEnabled();
1368
+ }
1369
+ break;
1241
1370
  case INAPP:
1242
1371
  templateData.content = template;
1243
1372
  break;
@@ -2053,12 +2182,18 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
2053
2182
  delete duplicateObj._id;
2054
2183
 
2055
2184
  if (this.state.channel.toLowerCase() === "mobilepush") {
2185
+ const params = {
2186
+ name: this.state.searchText,
2187
+ sortBy: this.state.sortBy,
2188
+ };
2056
2189
  duplicateObj.definition.accountId = this.props.Templates.selectedWeChatAccount.id;
2057
- this.props.mobilepushActions.createTemplate(duplicateObj, () => {
2058
- const message = `${this.state.channel} ${this.props.intl.formatMessage(messages.templateDuplicateSuccess)}`;
2059
- CapNotification.success({key: 'duplicateSuccess', message});
2060
- this.getAllTemplates({params: {}}, true);
2061
- this.props.mobilepushActions.clearCreateResponse();
2190
+ this.props.mobilePushNewActions.createTemplate(duplicateObj, (response) => {
2191
+ if (response && (response.templateId || response._id)) {
2192
+ const message = `${this.state.channel} ${this.props.intl.formatMessage(messages.templateDuplicateSuccess)}`;
2193
+ CapNotification.success({key: 'duplicateSuccess', message});
2194
+ this.getAllTemplates({params, resetPage: true});
2195
+ this.props.mobilePushNewActions.clearCreateResponse();
2196
+ }
2062
2197
  });
2063
2198
  } else if (this.state.channel.toLowerCase() === "inapp") {
2064
2199
  duplicateObj.definition.accountId = this.props.Templates.selectedWeChatAccount.id;
@@ -3399,6 +3534,7 @@ Templates.propTypes = {
3399
3534
  actions: PropTypes.object.isRequired,
3400
3535
  smsActions: PropTypes.object,
3401
3536
  mobilepushActions: PropTypes.object,
3537
+ mobilePushNewActions: PropTypes.object,
3402
3538
  smsEditActions: PropTypes.object,
3403
3539
  ebillActions: PropTypes.object,
3404
3540
  emailActions: PropTypes.object,
@@ -3431,6 +3567,7 @@ const mapStateToProps = createStructuredSelector({
3431
3567
  TemplatesList: makeSelectTemplatesResponse(),
3432
3568
  Create: makeSelectCreateSms(),
3433
3569
  CreateMobilePush: makeSelectCreateMobilePush(),
3570
+ MobilePushNew: makeSelectMobilePushNew(),
3434
3571
  CreateEbill: makeSelectCreateEbill(),
3435
3572
  Edit: makeSelectEdit(),
3436
3573
  EmailCreate: makeSelectCreateEmail(),
@@ -3446,6 +3583,7 @@ function mapDispatchToProps(dispatch) {
3446
3583
  actions: bindActionCreators(actions, dispatch),
3447
3584
  smsActions: bindActionCreators(smsActions, dispatch),
3448
3585
  mobilepushActions: bindActionCreators(mobilepushActions, dispatch),
3586
+ mobilePushNewActions: bindActionCreators(mobilePushNewActions, dispatch),
3449
3587
  inAppActions: bindActionCreators(inAppActions, dispatch),
3450
3588
  smsEditActions: bindActionCreators(smsEditActions, dispatch),
3451
3589
  ebillActions: bindActionCreators(ebillActions, dispatch),
@@ -3469,6 +3607,7 @@ const withSaga = injectSaga({ key: 'templates', saga: v2TemplateSaga, mode: DAEM
3469
3607
  export default compose(
3470
3608
  UserIsAuthenticated,
3471
3609
  withSaga,
3610
+ withMobilePushNewSaga,
3472
3611
  withReducer,
3473
3612
  withConnect,
3474
3613
  )(injectIntl(Templates));
@@ -538,4 +538,12 @@ export default defineMessages({
538
538
  id: `${scope}.zaloOnlyApprovedTemplates`,
539
539
  defaultMessage: 'Only enabled/approved templates are shown here',
540
540
  },
541
+ "templateCreateSuccess": {
542
+ id: `${scope}.templateCreateSuccess`,
543
+ defaultMessage: 'Template created successfully',
544
+ },
545
+ "templateUpdateSuccess": {
546
+ id: `${scope}.templateUpdateSuccess`,
547
+ defaultMessage: 'Template updated successfully',
548
+ },
541
549
  });
@@ -9,6 +9,7 @@ import { saveCdnConfigs, removeAllCdnLocalStorageItems } from '../../utils/cdnTr
9
9
  import { COPY_OF } from '../../containers/App/constants';
10
10
  import { ZALO_TEMPLATE_INFO_REQUEST } from '../Zalo/constants';
11
11
  import { getTemplateInfoById } from '../Zalo/saga';
12
+ import { watchCreateTemplate } from '../MobilePushNew/sagas';
12
13
  // Individual exports for testing
13
14
  export function* getAllTemplates(channel, queryParams) {
14
15
  try {
@@ -310,6 +310,7 @@ export const STATIC_URL = 'STATIC_URL';
310
310
  export const URL = 'URL';
311
311
  export const IMAGE = 'IMAGE';
312
312
  export const VIDEO = 'VIDEO';
313
+ export const GIF = 'GIF';
313
314
  export const INITIAL_CTA_DATA = [
314
315
  {
315
316
  index: 0,