@capillarytech/creatives-library 7.17.93 → 7.17.94

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 (53) hide show
  1. package/containers/Ebill/index.js +1 -0
  2. package/containers/MobilePush/Edit/index.js +1 -0
  3. package/index.js +6 -0
  4. package/package.json +1 -1
  5. package/routes.js +5 -0
  6. package/v2Components/CapDeviceContent/index.js +310 -0
  7. package/v2Components/CapDeviceContent/index.scss +115 -0
  8. package/v2Components/CapDeviceContent/messages.js +94 -0
  9. package/v2Components/CapDeviceContent/tests/index.test.js +75 -0
  10. package/v2Components/CapImageUpload/index.js +11 -4
  11. package/v2Components/CapImageUpload/messages.js +6 -2
  12. package/v2Components/CapInAppCTA/constants.js +25 -0
  13. package/v2Components/CapInAppCTA/index.js +281 -0
  14. package/v2Components/CapInAppCTA/index.scss +93 -0
  15. package/v2Components/CapInAppCTA/messages.js +85 -0
  16. package/v2Components/FormBuilder/index.js +3 -4
  17. package/v2Components/MobilePushPreviewV2/index.js +78 -23
  18. package/v2Components/TemplatePreview/_templatePreview.scss +449 -0
  19. package/v2Components/TemplatePreview/assets/images/inapp_mobile_android_bottom.svg +11 -0
  20. package/v2Components/TemplatePreview/assets/images/inapp_mobile_android_full.svg +11 -0
  21. package/v2Components/TemplatePreview/assets/images/inapp_mobile_android_modal.svg +11 -0
  22. package/v2Components/TemplatePreview/assets/images/inapp_mobile_android_top.svg +11 -0
  23. package/v2Components/TemplatePreview/assets/images/inapp_mobile_ios_bottom.svg +6 -0
  24. package/v2Components/TemplatePreview/assets/images/inapp_mobile_ios_full.svg +18 -0
  25. package/v2Components/TemplatePreview/assets/images/inapp_mobile_ios_modal.svg +7 -0
  26. package/v2Components/TemplatePreview/assets/images/inapp_mobile_ios_top.svg +13 -0
  27. package/v2Components/TemplatePreview/index.js +660 -375
  28. package/v2Components/TemplatePreview/messages.js +4 -0
  29. package/v2Containers/App/constants.js +1 -0
  30. package/v2Containers/CreativesContainer/SlideBoxContent.js +43 -0
  31. package/v2Containers/CreativesContainer/SlideBoxHeader.js +4 -1
  32. package/v2Containers/CreativesContainer/constants.js +1 -0
  33. package/v2Containers/CreativesContainer/index.js +79 -7
  34. package/v2Containers/CreativesContainer/messages.js +4 -0
  35. package/v2Containers/InApp/actions.js +64 -0
  36. package/v2Containers/InApp/constants.js +126 -0
  37. package/v2Containers/InApp/index.js +769 -0
  38. package/v2Containers/InApp/index.scss +48 -0
  39. package/v2Containers/InApp/messages.js +99 -0
  40. package/v2Containers/InApp/reducer.js +109 -0
  41. package/v2Containers/InApp/sagas.js +143 -0
  42. package/v2Containers/InApp/selectors.js +12 -0
  43. package/v2Containers/InApp/tests/action.test.js +53 -0
  44. package/v2Containers/InApp/tests/index.test.js +142 -0
  45. package/v2Containers/InApp/tests/mockData.js +898 -0
  46. package/v2Containers/InApp/tests/reducer.test.js +177 -0
  47. package/v2Containers/InApp/tests/sagas.test.js +391 -0
  48. package/v2Containers/MobilePush/Edit/index.js +3 -1
  49. package/v2Containers/Templates/_templates.scss +14 -0
  50. package/v2Containers/Templates/index.js +86 -32
  51. package/v2Containers/Templates/messages.js +20 -0
  52. package/v2Containers/TemplatesV2/index.js +2 -1
  53. package/v2Containers/TemplatesV2/messages.js +4 -0
@@ -0,0 +1,769 @@
1
+ import React, { useState, useEffect } from "react";
2
+ import isEmpty from 'lodash/isEmpty';
3
+ import get from 'lodash/get';
4
+ import { bindActionCreators } from "redux";
5
+ import { createStructuredSelector } from "reselect";
6
+ import { injectIntl, FormattedMessage } from "react-intl";
7
+ import CapHeading from "@capillarytech/cap-ui-library/CapHeading";
8
+ import CapSpin from "@capillarytech/cap-ui-library/CapSpin";
9
+ import CapInput from "@capillarytech/cap-ui-library/CapInput";
10
+ import CapRadioGroup from "@capillarytech/cap-ui-library/CapRadioGroup";
11
+ import CapRow from "@capillarytech/cap-ui-library/CapRow";
12
+ import CapColumn from "@capillarytech/cap-ui-library/CapColumn";
13
+ import CapButton from "@capillarytech/cap-ui-library/CapButton";
14
+ import CapTab from "@capillarytech/cap-ui-library/CapTab";
15
+ import CapNotification from "@capillarytech/cap-ui-library/CapNotification";
16
+ import { makeSelectInApp, makeSelectAccount } from "./selectors";
17
+ import {
18
+ isLoadingMetaEntities,
19
+ makeSelectMetaEntities,
20
+ setInjectedTags,
21
+ } from "../Cap/selectors";
22
+ import * as inAppActions from "./actions";
23
+ import './index.scss';
24
+ import messages from "./messages";
25
+ import globalMessages from "../Cap/messages";
26
+ import withCreatives from "../../hoc/withCreatives";
27
+ import TemplatePreview from "../../v2Components/TemplatePreview";
28
+ import { validateTags } from "../../utils/tagValidations";
29
+
30
+ import {
31
+ ANDROID,
32
+ BIG_PICTURE,
33
+ BIG_TEXT,
34
+ DEEP_LINK,
35
+ INAPP_BUTTON_TYPES,
36
+ INAPP_MEDIA_TYPES,
37
+ INAPP_MESSAGE_LAYOUT_TYPES,
38
+ INITIAL_CTA_DATA,
39
+ IOS,
40
+ LAYOUT_RADIO_OPTIONS,
41
+ } from "./constants";
42
+ import { INAPP, SMS } from "../CreativesContainer/constants";
43
+ import { ALL, TAG, EMBEDDED, DEFAULT, FULL, LIBRARY } from "../Whatsapp/constants";
44
+ import CapDeviceContent from "../../v2Components/CapDeviceContent";
45
+ import { getCdnUrl } from "../../utils/cdnTransformation";
46
+ let editContent = {};
47
+
48
+ export const InApp = (props) => {
49
+ const {
50
+ intl,
51
+ actions,
52
+ isFullMode,
53
+ onCreateComplete,
54
+ params,
55
+ templateData = {},
56
+ editData = {},
57
+ accountData = {},
58
+ globalActions,
59
+ location,
60
+ getDefaultTags,
61
+ supportedTags,
62
+ metaEntities,
63
+ injectedTags,
64
+ getFormData,
65
+ selectedOfferDetails,
66
+ } = props || {};
67
+ const { formatMessage } = intl;
68
+ const [titleAndroid, setTitleAndroid] = useState("");
69
+ const [titleIos, setTitleIos] = useState("");
70
+ const [templateMediaTypeAndroid, setTemplateMediaTypeAndroid] = useState(
71
+ INAPP_MEDIA_TYPES.TEXT
72
+ );
73
+ const [templateMediaTypeIos, setTemplateMediaTypeIos] = useState(
74
+ INAPP_MEDIA_TYPES.TEXT
75
+ );
76
+ const [templateName, setTemplateName] = useState("");
77
+ const [templateLayoutType, setTemplateLayoutType] = useState(
78
+ INAPP_MESSAGE_LAYOUT_TYPES.MODAL
79
+ );
80
+ const [addActionLinkAndroid, setAddActionLinkAndroid] = useState(false);
81
+ const [addActionLinkIos, setAddActionLinkIos] = useState(false);
82
+ const [deepLinkValueAndroid, setDeepLinkValueAndroid] = useState('');
83
+ const [deepLinkValueIos, setDeepLinkValueIos] = useState('');
84
+ const [templateMessageAndroid, setTemplateMessageAndroid] = useState("");
85
+ const [templateMessageIos, setTemplateMessageIos] = useState("");
86
+ const [templateMessageErrorAndroid, setTemplateMessageErrorAndroid] = useState(false);
87
+ const [templateMessageErrorIos, setTemplateMessageErrorIos] = useState(false);
88
+ const [accountId, setAccountId] = useState("");
89
+ const [accessToken, setAccessToken] = useState("");
90
+ const [accountName, setAccountName] = useState("");
91
+ const [deepLink, setDeepLink] = useState("");
92
+ const [spin, setSpin] = useState(false);
93
+ const [inAppImageSrcAndroid, setInAppImageSrcAndroid] = useState("");
94
+ const [inAppImageSrcIos, setInAppImageSrcIos] = useState("");
95
+ const [panes, setPanes] = useState(ANDROID);
96
+ //for tag only
97
+ const [tags, updateTags] = useState([]);
98
+ //for edit only
99
+ const [isEditFlow, setEditFlow] = useState(false);
100
+ const [templateDate, setTemplateDate] = useState("");
101
+
102
+ //buttons
103
+ const [ctaDataAndroid, setCtaDataAndroid] = useState(INITIAL_CTA_DATA);
104
+ const [ctaDataIos, setCtaDataIos] = useState(INITIAL_CTA_DATA);
105
+ const [buttonTypeAndroid, setButtonTypeAndroid] = useState(
106
+ INAPP_BUTTON_TYPES.NONE
107
+ );
108
+ const [buttonTypeIos, setButtonTypeIos] = useState(INAPP_BUTTON_TYPES.NONE);
109
+ const isBtnTypeCta = buttonTypeAndroid === INAPP_BUTTON_TYPES.CTA;
110
+
111
+ //gets account details
112
+ useEffect(() => {
113
+ const accountObj = accountData?.selectedWeChatAccount || {};
114
+ const deepLinkObj = accountData?.weCrmAccounts[0]?.configs?.deeplink || {};
115
+ if (!isEmpty(accountObj)) {
116
+ const {
117
+ sourceAccountIdentifier = "",
118
+ configs = {},
119
+ name = "",
120
+ } = accountObj;
121
+ //get deep link keys in an array
122
+ const deepLinkKeys = Object.values(JSON.parse(deepLinkObj));
123
+ const keys = deepLinkKeys.map((link) => ({label: link.name, value: link.link, title: link.link }));
124
+ setDeepLink(keys);
125
+ setAccountId(sourceAccountIdentifier);
126
+ setAccessToken(configs.accessToken || "");
127
+ setAccountName(name);
128
+ }
129
+ }, [accountData?.selectedWeChatAccount, accountData?.weCrmAccounts[0]] );
130
+
131
+ //gets template details
132
+ const paramObj = params || {};
133
+ useEffect(() => {
134
+ const { id } = paramObj;
135
+ if (id) {
136
+ if (isFullMode) {
137
+ setSpin(true);
138
+ actions.getTemplateDetails(id, setSpin);
139
+ }
140
+ }
141
+ //cleanup code
142
+ return () => {
143
+ actions.resetEditTemplate();
144
+ };
145
+ }, [paramObj.id]);
146
+
147
+ useEffect(() => {
148
+ const {
149
+ name = "",
150
+ versions = {},
151
+ createdAt = "",
152
+ } = isFullMode ? editData.templateDetails || {} : templateData || {};
153
+ editContent = get(versions, `base.content`, {});
154
+ if (editContent && !isEmpty(editContent)) {
155
+ setEditFlow(true);
156
+ setTemplateName(name);
157
+ setTemplateDate(createdAt);
158
+ setTemplateLayoutType(editContent?.ANDROID?.bodyType);
159
+ const androidContent = editContent?.ANDROID;
160
+ const {
161
+ title: androidTitle,
162
+ message: androidMessage,
163
+ cta: androidCta,
164
+ expandableDetails: androidExpandableDetails,
165
+ } = androidContent;
166
+ setTitleAndroid(androidTitle);
167
+ setTemplateMessageAndroid(androidMessage);
168
+ setTemplateMediaTypeAndroid(
169
+ androidExpandableDetails?.style === BIG_TEXT
170
+ ? INAPP_MEDIA_TYPES.TEXT
171
+ : INAPP_MEDIA_TYPES.IMAGE
172
+ );
173
+ setInAppImageSrcAndroid(androidExpandableDetails?.image);
174
+ setDeepLinkValueAndroid(androidCta.actionLink);
175
+ setAddActionLinkAndroid(!isEmpty(androidCta) && true);
176
+ setButtonTypeAndroid(
177
+ androidExpandableDetails?.buttonType || INAPP_BUTTON_TYPES.NONE
178
+ );
179
+ if (androidExpandableDetails?.buttonType === INAPP_BUTTON_TYPES.CTA && androidExpandableDetails?.buttons.length > 0) {
180
+ setCtaDataAndroid(
181
+ androidExpandableDetails?.buttons?.map((cta) => {
182
+ const { index, type, text, url = "", label } = cta;
183
+ const obj = {
184
+ index,
185
+ text,
186
+ url,
187
+ urlType: type,
188
+ isSaved: true,
189
+ label,
190
+ };
191
+ return obj;
192
+ })
193
+ );
194
+ }
195
+ const iosContent = editContent?.IOS;
196
+ const {
197
+ title: iosTitle,
198
+ message: iosMessage,
199
+ cta: iosCta,
200
+ expandableDetails: iosExpandableDetails,
201
+ } = iosContent;
202
+ setTitleIos(iosTitle);
203
+ setTemplateMessageIos(iosMessage);
204
+ setTemplateMediaTypeIos(iosExpandableDetails?.style === BIG_TEXT ? INAPP_MEDIA_TYPES.TEXT : INAPP_MEDIA_TYPES.IMAGE);
205
+ setInAppImageSrcIos(iosExpandableDetails?.image);
206
+ setButtonTypeIos(iosExpandableDetails?.buttonType || INAPP_BUTTON_TYPES.NONE);
207
+ setAddActionLinkIos(!isEmpty(iosCta) && true);
208
+ setDeepLinkValueIos(iosCta?.actionLink);
209
+ if (iosExpandableDetails?.buttonType === INAPP_BUTTON_TYPES.CTA && iosExpandableDetails?.buttons.length > 0) {
210
+ setCtaDataIos(
211
+ iosExpandableDetails?.buttons?.map((cta) => {
212
+ const { index, type, text, url = "", label } = cta;
213
+ const obj = {
214
+ index,
215
+ text,
216
+ url,
217
+ urlType: type,
218
+ isSaved: true,
219
+ label,
220
+ };
221
+ return obj;
222
+ })
223
+ );
224
+ }
225
+ }
226
+ }, [editData.templateDetails || templateData]);
227
+
228
+ // tag Code start from here
229
+ useEffect(() => {
230
+ //fetching tags
231
+ const { type, module } = location.query || {};
232
+ const isEmbedded = type === EMBEDDED;
233
+ const context = isEmbedded ? module : DEFAULT;
234
+ const embedded = isEmbedded ? type : FULL;
235
+ const query = {
236
+ layout: SMS,
237
+ type: TAG,
238
+ context,
239
+ embedded,
240
+ };
241
+ if (getDefaultTags) {
242
+ query.context = getDefaultTags;
243
+ }
244
+ globalActions.fetchSchemaForEntity(query);
245
+ }, []);
246
+
247
+ useEffect(() => {
248
+ let tag = get(metaEntities, `tags.standard`, []);
249
+ const { type, module } = location.query || {};
250
+ if (type === EMBEDDED && module === LIBRARY && !getDefaultTags) {
251
+ tag = supportedTags;
252
+ }
253
+ updateTags(tag);
254
+ }, [metaEntities]);
255
+
256
+ const handleOnTagsContextChange = (data) => {
257
+ const { type } = location.query || {};
258
+ const tempData = (data || '').toLowerCase();
259
+ const isEmbedded = type === EMBEDDED;
260
+ const embedded = isEmbedded ? type : FULL;
261
+ const context = tempData === ALL ? DEFAULT : tempData;
262
+ const query = {
263
+ layout: SMS,
264
+ type: TAG,
265
+ context,
266
+ embedded,
267
+ };
268
+ globalActions.fetchSchemaForEntity(query);
269
+ };
270
+
271
+ const templateDescErrorHandler = (value) => {
272
+ let errorMessage = false;
273
+ const { unsupportedTags, isBraceError } =
274
+ validateTags({
275
+ content: value,
276
+ tagsParam: tags,
277
+ injectedTagsParams: injectedTags,
278
+ location,
279
+ tagModule: getDefaultTags,
280
+ }) || {};
281
+ if (value === '' && INAPP_MEDIA_TYPES.NONE) {
282
+ errorMessage = formatMessage(messages.emptyTemplateDescErrorMessage);
283
+ } else if (unsupportedTags?.length > 0) {
284
+ errorMessage = formatMessage(
285
+ globalMessages.unsupportedTagsValidationError,
286
+ {
287
+ unsupportedTags,
288
+ },
289
+ );
290
+ }
291
+ if (isBraceError) {
292
+ errorMessage = formatMessage(globalMessages.unbalanacedCurlyBraces);
293
+ }
294
+ return errorMessage;
295
+ };
296
+
297
+ const onTagSelect = (data, id) => {
298
+ if (id === 0) {
299
+ const tempTitle = `${panes === ANDROID ? titleAndroid : titleIos}{{${data}}}`;
300
+ if (panes === ANDROID) {
301
+ setTitleAndroid(tempTitle);
302
+ } else {
303
+ setTitleIos(tempTitle);
304
+ }
305
+ } else {
306
+ const tempMsg = `${panes === ANDROID ? templateMessageAndroid : templateMessageIos}{{${data}}}`;
307
+ const error = templateDescErrorHandler(tempMsg);
308
+ if (panes === ANDROID) {
309
+ setTemplateMessageAndroid(tempMsg);
310
+ setTemplateMessageErrorAndroid(error);
311
+ } else {
312
+ setTemplateMessageIos(tempMsg);
313
+ setTemplateMessageErrorIos(error);
314
+ }
315
+ }
316
+ };
317
+
318
+ // tag Code end
319
+
320
+ const onTemplateNameChange = ({ target: { value } }) => {
321
+ setTemplateName(value);
322
+ };
323
+
324
+ const onTemplateLayoutTypeChange = ({ target: { value } }) => {
325
+ setTemplateLayoutType(value);
326
+ };
327
+
328
+ const onCopyTitleAndContent = () => {
329
+ if (panes === ANDROID) {
330
+ setTitleAndroid(titleIos);
331
+ setTemplateMessageAndroid(templateMessageIos);
332
+ } else {
333
+ setTitleIos(titleAndroid);
334
+ setTemplateMessageIos(templateMessageAndroid);
335
+ }
336
+ };
337
+
338
+
339
+ const PANES = [
340
+ {
341
+ content: (
342
+ <CapDeviceContent
343
+ intl={intl}
344
+ location={location}
345
+ injectedTags={injectIntl}
346
+ selectedOfferDetails={selectedOfferDetails}
347
+ panes={panes}
348
+ actions={actions}
349
+ editData={editData}
350
+ isFullMode={isFullMode}
351
+ inAppImageSrc={inAppImageSrcAndroid}
352
+ setInAppImageSrc={setInAppImageSrcAndroid}
353
+ isEditFlow={isEditFlow}
354
+ ctaData={ctaDataAndroid}
355
+ setCtaData={setCtaDataAndroid}
356
+ buttonType={buttonTypeAndroid}
357
+ setButtonType={setButtonTypeAndroid}
358
+ accountId={accountId}
359
+ accessToken={accessToken}
360
+ templateMediaType={templateMediaTypeAndroid}
361
+ setTemplateMediaType={setTemplateMediaTypeAndroid}
362
+ title={titleAndroid}
363
+ setTitle={setTitleAndroid}
364
+ templateMessageError={templateMessageErrorAndroid}
365
+ templateMessage={templateMessageAndroid}
366
+ setTemplateMessage={setTemplateMessageAndroid}
367
+ setTemplateMessageError={setTemplateMessageErrorAndroid}
368
+ addActionLink={addActionLinkAndroid}
369
+ setAddActionLink={setAddActionLinkAndroid}
370
+ deepLink={deepLink}
371
+ deepLinkValue={deepLinkValueAndroid}
372
+ setDeepLinkValue={setDeepLinkValueAndroid}
373
+ onCopyTitleAndContent={onCopyTitleAndContent}
374
+ tags={tags}
375
+ onTagSelect={onTagSelect}
376
+ handleOnTagsContextChange={handleOnTagsContextChange}
377
+ templateDescErrorHandler={templateDescErrorHandler}
378
+ />
379
+ ),
380
+ tab: <FormattedMessage {...messages.Android} />,
381
+ key: ANDROID,
382
+ },
383
+ {
384
+ content: (
385
+ <CapDeviceContent
386
+ intl={intl}
387
+ location={location}
388
+ injectedTags={injectIntl}
389
+ selectedOfferDetails={selectedOfferDetails}
390
+ panes={panes}
391
+ actions={actions}
392
+ editData={editData}
393
+ isFullMode={isFullMode}
394
+ inAppImageSrc={inAppImageSrcIos}
395
+ setInAppImageSrc={setInAppImageSrcIos}
396
+ isEditFlow={isEditFlow}
397
+ ctaData={ctaDataIos}
398
+ setCtaData={setCtaDataIos}
399
+ buttonType={buttonTypeIos}
400
+ setButtonType={setButtonTypeIos}
401
+ accountId={accountId}
402
+ accessToken={accessToken}
403
+ templateMediaType={templateMediaTypeIos}
404
+ setTemplateMediaType={setTemplateMediaTypeIos}
405
+ title={titleIos}
406
+ setTitle={setTitleIos}
407
+ templateMessageError={templateMessageErrorIos}
408
+ templateMessage={templateMessageIos}
409
+ setTemplateMessage={setTemplateMessageIos}
410
+ setTemplateMessageError={setTemplateMessageErrorIos}
411
+ addActionLink={addActionLinkIos}
412
+ setAddActionLink={setAddActionLinkIos}
413
+ deepLink={deepLink}
414
+ deepLinkValue={deepLinkValueIos}
415
+ setDeepLinkValue={setDeepLinkValueIos}
416
+ onCopyTitleAndContent={onCopyTitleAndContent}
417
+ tags={tags}
418
+ onTagSelect={onTagSelect}
419
+ handleOnTagsContextChange={handleOnTagsContextChange}
420
+ />
421
+ ),
422
+ tab: <FormattedMessage {...messages.Ios} />,
423
+ key: IOS,
424
+ },
425
+ ];
426
+
427
+ const createModeContent = (
428
+ <CapRow>
429
+ {/* template name */}
430
+ <CapRow>
431
+ <CapHeading type="h4">
432
+ <FormattedMessage {...messages.creativeName} />
433
+ </CapHeading>
434
+ <CapInput
435
+ id="inapp-template-name-input"
436
+ className="inapp-template-name-input"
437
+ onChange={onTemplateNameChange}
438
+ placeholder={formatMessage(globalMessages.templateNamePlaceholder)}
439
+ value={templateName}
440
+ size="default"
441
+ />
442
+ </CapRow>
443
+ {/* Creative layout type*/}
444
+ <CapRow className="inapp-creative-layout">
445
+ <CapHeading type="h4">
446
+ <FormattedMessage {...messages.creativeLayout} />
447
+ </CapHeading>
448
+ <CapHeading type="h6" className="inapp-creative-layout-desc">
449
+ <FormattedMessage {...messages.creativeLayoutDesc} />
450
+ </CapHeading>
451
+ </CapRow>
452
+ <CapRadioGroup
453
+ id="inapp-layout-radio"
454
+ options={LAYOUT_RADIO_OPTIONS}
455
+ value={templateLayoutType}
456
+ onChange={onTemplateLayoutTypeChange}
457
+ className="inapp-layout-radio"
458
+ />
459
+ </CapRow>
460
+ );
461
+ //create methods end
462
+
463
+ //used by create and edit
464
+ const getPreviewSection = () => {
465
+ const templateTitle = panes === ANDROID ? titleAndroid : titleIos;
466
+ const templateMsg = panes === ANDROID ? templateMessageAndroid : templateMessageIos;
467
+ const mediaPreview = {};
468
+ let ctaData = {};
469
+ if (panes === ANDROID) {
470
+ ctaData = ctaDataAndroid;
471
+ switch (templateMediaTypeAndroid) {
472
+ case INAPP_MEDIA_TYPES.IMAGE:
473
+ mediaPreview.inAppImageSrcAndroid = inAppImageSrcAndroid;
474
+ break;
475
+ default:
476
+ break;
477
+ }
478
+ } else {
479
+ ctaData = ctaDataIos;
480
+ switch (templateMediaTypeIos) {
481
+ case INAPP_MEDIA_TYPES.IMAGE:
482
+ mediaPreview.inAppImageSrcIos = inAppImageSrcIos;
483
+ break;
484
+ default:
485
+ break;
486
+ }
487
+ }
488
+ return (
489
+ <TemplatePreview
490
+ channel={INAPP}
491
+ content={{
492
+ inAppPreviewContent: {
493
+ mediaPreview,
494
+ templateTitle,
495
+ templateMsg,
496
+ ...(isBtnTypeCta && {
497
+ ctaData,
498
+ }),
499
+ },
500
+ }}
501
+ inAppAccountName={accountName}
502
+ templateLayoutType={templateLayoutType}
503
+ device={panes}
504
+ />
505
+ );
506
+ };
507
+
508
+ const isDisableDone = () => {
509
+ //if template name is not entered
510
+ if (templateName.trim() === '') {
511
+ return true;
512
+ }
513
+ //if template message is not entered
514
+ //for android
515
+ if (panes === ANDROID && (templateMessageAndroid.trim() === '' || templateMessageErrorAndroid)) {
516
+ return true;
517
+ }
518
+ if (panes === IOS && (templateMessageIos.trim() === '' || templateMessageErrorIos)) {
519
+ return true;
520
+ }//for ios
521
+ //if media type is image and the mediaType file is not uploaded
522
+ if (panes === ANDROID && !(templateMediaTypeAndroid === INAPP_MEDIA_TYPES.TEXT) && inAppImageSrcAndroid === '') {
523
+ return true;
524
+ }
525
+ if (panes === IOS && !(templateMediaTypeIos === INAPP_MEDIA_TYPES.TEXT) && inAppImageSrcIos === '') {
526
+ return true;
527
+ }
528
+ //cta
529
+ if (isBtnTypeCta) {
530
+ if (panes === ANDROID && ctaDataAndroid[0]?.isSaved) {
531
+ //if button type is cta and 1st button is saved
532
+ return false;
533
+ }
534
+ if (panes === IOS && ctaDataIos[0]?.isSaved) {
535
+ //if button type is cta and 1st button is saved
536
+ return false;
537
+ }
538
+ //if button type is cta and there are no buttons saved
539
+ return true;
540
+ }
541
+ return false;
542
+ };
543
+
544
+ const getCtaPayload = (type) =>
545
+ (type === ANDROID ? ctaDataAndroid : ctaDataIos).map((cta) => {
546
+ const { index, text, urlType, url, label } = cta;
547
+ return {
548
+ index,
549
+ type: urlType,
550
+ text,
551
+ url,
552
+ label,
553
+ };
554
+ });
555
+
556
+ const createPayload = () => {
557
+ let androidMediaParams = {};
558
+ let iosMediaParams = {};
559
+ const buttonType = panes === ANDROID ? buttonTypeAndroid : buttonTypeIos;
560
+ const commonDevicePayload = {
561
+ luid: "{{luid}}",
562
+ cuid: "{{cuid}}",
563
+ communicationId: "{{communicationId}}",
564
+ };
565
+ switch (templateMediaTypeAndroid) {
566
+ case INAPP_MEDIA_TYPES.IMAGE:
567
+ androidMediaParams = {
568
+ image: getCdnUrl({url: inAppImageSrcAndroid, channelName: INAPP }),
569
+ style: BIG_PICTURE,
570
+ };
571
+ break;
572
+ default:
573
+ break;
574
+ }
575
+ switch (templateMediaTypeIos) {
576
+ case INAPP_MEDIA_TYPES.IMAGE:
577
+ iosMediaParams = {
578
+ image: getCdnUrl({url: inAppImageSrcIos, channelName: INAPP }),
579
+ style: BIG_PICTURE,
580
+ };
581
+ break;
582
+ default:
583
+ break;
584
+ }
585
+ const accountObj = accountData?.selectedWeChatAccount || {};
586
+ const {
587
+ sourceAccountIdentifier = "",
588
+ id,
589
+ } = accountObj;
590
+ const data = {
591
+ name: templateName,
592
+ versions: {
593
+ base: {
594
+ content: {
595
+ ANDROID: {
596
+ ...commonDevicePayload,
597
+ title: titleAndroid,
598
+ message: templateMessageAndroid,
599
+ bodyType: templateLayoutType,
600
+ expandableDetails: {
601
+ style: BIG_TEXT,
602
+ message: templateMessageAndroid,
603
+ ...androidMediaParams,
604
+ ...(isBtnTypeCta && {
605
+ buttonType,
606
+ buttons: getCtaPayload(ANDROID),
607
+ }),
608
+ },
609
+ custom: [],
610
+ cta: deepLinkValueAndroid ? {type: DEEP_LINK, actionLink: deepLinkValueAndroid} : {},
611
+ },
612
+ IOS: {
613
+ ...commonDevicePayload,
614
+ title: titleIos,
615
+ message: templateMessageIos,
616
+ bodyType: templateLayoutType,
617
+ expandableDetails: {
618
+ style: BIG_TEXT,
619
+ message: templateMessageIos,
620
+ ...iosMediaParams,
621
+ ...(isBtnTypeCta && {
622
+ buttonType,
623
+ buttons: getCtaPayload(IOS),
624
+ }),
625
+ },
626
+ custom: [],
627
+ cta: deepLinkValueIos ? {type: DEEP_LINK, actionLink: deepLinkValueIos} : {},
628
+ },
629
+ },
630
+ },
631
+ },
632
+ type: INAPP,
633
+ definition: {
634
+ accountId: id,
635
+ licenseCode: sourceAccountIdentifier,
636
+ mode: "CREATE",
637
+ },
638
+ };
639
+ return data;
640
+ };
641
+
642
+ const actionCallback = ({ errorMessage }) => {
643
+ if (!errorMessage) {
644
+ CapNotification.success({
645
+ message: isEditFlow ? formatMessage(messages.inAppEditNotification, {
646
+ name: templateName,
647
+ }) : formatMessage(messages.inAppCreateNotification, {
648
+ name: templateName,
649
+ }),
650
+ });
651
+ actions.clearCreateResponse();
652
+ } else {
653
+ CapNotification.error({
654
+ message: JSON.stringify(errorMessage),
655
+ });
656
+ }
657
+ };
658
+
659
+ const onCreateInApp = () => {
660
+ setSpin(true);
661
+ actions.createInAppTemplate(createPayload(), (resp, errorMessage) => {
662
+ actionCallback({ resp, errorMessage });
663
+ if (!errorMessage) {
664
+ onCreateComplete();
665
+ } else {
666
+ setSpin(false);
667
+ }
668
+ });
669
+ };
670
+
671
+ const onEditInApp = () => {
672
+ setSpin(true);
673
+ actions.editTemplate(
674
+ {
675
+ ...createPayload(),
676
+ _id: params.id,
677
+ },
678
+ (resp, errorMessage) => {
679
+ actionCallback({ resp, errorMessage });
680
+ if (!errorMessage) {
681
+ onCreateComplete();
682
+ } else {
683
+ setSpin(false);
684
+ }
685
+ },
686
+ );
687
+ };
688
+
689
+
690
+ const onDoneCallback = () => {
691
+ if (isFullMode) {
692
+ if (isEditFlow) {
693
+ return onEditInApp;
694
+ }
695
+ return onCreateInApp;
696
+ }
697
+ return () =>
698
+ getFormData({
699
+ value: createPayload(),
700
+ _id: params && params.id,
701
+ validity: true,
702
+ type: INAPP,
703
+ });
704
+ };
705
+
706
+ return (
707
+ <CapSpin spinning={spin}>
708
+ <CapRow className="cap-inapp-creatives">
709
+ <CapColumn span={14}>
710
+ {isFullMode && createModeContent}
711
+ {/* device tab */}
712
+ <CapTab
713
+ panes={PANES}
714
+ onChange={(value) => {
715
+ setPanes(value);
716
+ }}
717
+ activeKey={panes}
718
+ defaultActiveKey={panes}
719
+ className="inapp-template-device-tab"
720
+ />
721
+ <div className="inapp-scroll-div" />
722
+ </CapColumn>
723
+ <CapColumn span={10} className="inapp-preview-container">
724
+ {getPreviewSection()}
725
+ </CapColumn>
726
+ </CapRow>
727
+ <div className="inapp-footer">
728
+ {
729
+ <>
730
+ <CapButton
731
+ onClick={onDoneCallback()}
732
+ disabled={isDisableDone()}
733
+ className="inapp-create-btn"
734
+ >
735
+ {isEditFlow ? (
736
+ isFullMode ? (
737
+ <FormattedMessage {...messages.update} />
738
+ ) : (
739
+ <FormattedMessage {...globalMessages.done} />
740
+ )
741
+ ) : (
742
+ <FormattedMessage {...messages.create} />
743
+ )}
744
+ </CapButton>
745
+ </>
746
+ }
747
+ </div>
748
+ </CapSpin>
749
+ );
750
+ };
751
+
752
+ const mapStateToProps = createStructuredSelector({
753
+ editData: makeSelectInApp(),
754
+ accountData: makeSelectAccount(),
755
+ metaEntities: makeSelectMetaEntities(),
756
+ loadingTags: isLoadingMetaEntities(),
757
+ injectedTags: setInjectedTags(),
758
+ });
759
+
760
+ const mapDispatchToProps = (dispatch) => ({
761
+ actions: bindActionCreators(inAppActions, dispatch),
762
+ });
763
+
764
+ export default withCreatives({
765
+ WrappedComponent: injectIntl(InApp),
766
+ mapStateToProps,
767
+ mapDispatchToProps,
768
+ userAuth: true,
769
+ });