@capillarytech/creatives-library 7.17.138 → 7.17.140

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.
@@ -5,10 +5,11 @@ import React, { useState, useEffect, useCallback } from 'react';
5
5
  import { bindActionCreators } from 'redux';
6
6
  import { createStructuredSelector } from 'reselect';
7
7
  import { injectIntl, FormattedMessage } from 'react-intl';
8
- import { get, isEmpty, cloneDeep } from 'lodash';
8
+ import { get, isEmpty, cloneDeep, isEqual } from 'lodash';
9
9
  import styled from 'styled-components';
10
10
  import CapSpin from '@capillarytech/cap-ui-library/CapSpin';
11
11
  import CapRow from '@capillarytech/cap-ui-library/CapRow';
12
+ import CapIcon from '@capillarytech/cap-ui-library/CapIcon';
12
13
  import CapColumn from '@capillarytech/cap-ui-library/CapColumn';
13
14
  import CapHeader from '@capillarytech/cap-ui-library/CapHeader';
14
15
  import CapTooltipWithInfo from '@capillarytech/cap-ui-library/CapTooltipWithInfo';
@@ -25,7 +26,7 @@ import CapAlert from '@capillarytech/cap-ui-library/CapAlert';
25
26
  import CapCheckbox from '@capillarytech/cap-ui-library/CapCheckbox';
26
27
  import CapLink from '@capillarytech/cap-ui-library/CapLink';
27
28
  import moment from 'moment';
28
- import debounce from 'lodash/debounce';
29
+
29
30
  import {
30
31
  CAP_SPACE_04,
31
32
  CAP_SPACE_16,
@@ -75,7 +76,9 @@ import {
75
76
  HEADER_TEXT,
76
77
  FOOTER_TEXT,
77
78
  MESSAGE_TEXT,
78
- BUTTON_TEXT
79
+ BUTTON_TEXT,
80
+ OTP_CONFIG_URI,
81
+ WHATSAPP_CATEGORIES,
79
82
  } from './constants';
80
83
  import { DATE_DISPLAY_FORMAT, TIME_DISPLAY_FORMAT } from '../App/constants';
81
84
  import messages from './messages';
@@ -149,6 +152,8 @@ export const Whatsapp = (props) => {
149
152
  const [host, setHost] = useState('');
150
153
  const [spin, setSpin] = useState(false);
151
154
  const [unsubscribeRequired, setUnsubscribeRequired] = useState(false);
155
+ const [securityWarningReqd, setSecurityWarningReqd] = useState(false);
156
+ const [expiryMinutes, setExpiryMinutes] = useState(0);
152
157
  const [whatsappImageSrc, setWhatsappImageSrc] = useState('');
153
158
  const [whatsappVideoSrcAndPreview, setWhatsappVideoSrcAndPreview] = useState({
154
159
  whatsappVideoSrc: '',
@@ -406,7 +411,10 @@ export const Whatsapp = (props) => {
406
411
  }
407
412
  setUnsubscribeRequired(true);
408
413
  }
409
- //conerting msg string to variable arr
414
+ if(msg?.includes(formatMessage(messages.securitySuffix))){
415
+ setUnsubscribeRequired(true);
416
+ }
417
+ //converting msg string to variable arr
410
418
  const templateMessageArray = converStringToVarArr(validVarArr, msg);
411
419
  updateTempMsgArray(templateMessageArray.filter((i) => i === 0 || i));
412
420
  };
@@ -698,7 +706,7 @@ export const Whatsapp = (props) => {
698
706
  disabled: host === HOST_TWILIO,
699
707
  },
700
708
  ];
701
-
709
+ const isAuthenticationTemplate = isEqual(templateCategory, WHATSAPP_CATEGORIES.authentication);
702
710
  const onChangeButtonType = ({ target: { value } }) => {
703
711
  setButtonType(value);
704
712
  setCtadata(INITIAL_CTA_DATA);
@@ -829,19 +837,45 @@ export const Whatsapp = (props) => {
829
837
 
830
838
  const unsubscribeHandler = ({ target: { checked } }) => {
831
839
  setUnsubscribeRequired(checked);
840
+ if (isAuthenticationTemplate) {
841
+ if (checked && !templateMessage?.includes(formatMessage(messages.securitySuffix))) {
842
+ setTemplateMessage(`${templateMessage}${formatMessage(messages.securitySuffix)}`);
843
+ } else {
844
+ setTemplateMessage(formatMessage(messages.authenticationMsg));
845
+ }
846
+ }
847
+ const error = templateMessageErrorHandler(templateMessage);
848
+ setTemplateMessageError(error);
849
+ };
850
+
851
+ const extraSecurityFooter = ({ target: { checked = false } } = {}) => {
852
+ setSecurityWarningReqd(checked);
853
+ setExpiryMinutes(0);
854
+ if (checked) {
855
+ setTemplateFooter(formatMessage(messages.codeExpiryNumMinutes));
856
+ } else {
857
+ setTemplateFooter("");
858
+ }
832
859
  const error = templateMessageErrorHandler(templateMessage);
833
860
  setTemplateMessageError(error);
834
861
  };
835
862
 
836
- const previewUrlHandler = ({ target: { checked } }) => {
837
- setShowUrlPreview(checked);
863
+ const changeExpiryMinutes =(event) => {
864
+ setExpiryMinutes(event);
865
+ const footerContent =`${formatMessage(
866
+ messages.codeExpiryNumMinutes,
867
+ )}` ;
868
+ setTemplateFooter(footerContent.replace(formatMessage(messages.numMinutes), event));
838
869
  }
839
870
 
840
871
  const renderUnsubscribeText = () => {
841
872
  const isDisabled =
842
873
  isEditFlow ||
843
- templateMessage.length + UNSUBSCRIBE_TEXT_LENGTH >
874
+ templateMessage?.length + UNSUBSCRIBE_TEXT_LENGTH >
844
875
  TEMPLATE_MESSAGE_MAX_LENGTH;
876
+ const messageFooter = isAuthenticationTemplate ?
877
+ formatMessage(messages.templateSecurityDisclaimer):
878
+ formatMessage(messages.templateMessageUnsubscribeText, { unsubscribe: '{{unsubscribe}}' });
845
879
  return (
846
880
  <>
847
881
  <CapColumn span={12}>
@@ -866,12 +900,11 @@ export const Whatsapp = (props) => {
866
900
  }
867
901
  >
868
902
  <span className="whatsapp-render-unsubscribe-text">
869
- {formatMessage(messages.templateMessageUnsubscribeText, { unsubscribe: '{{unsubscribe}}'})}
903
+ {messageFooter}
870
904
  </span>
871
905
  </CapTooltip>
872
906
  </CapCheckbox>
873
907
  </CapColumn>
874
- <CapColumn span={12}></CapColumn>
875
908
  </>
876
909
  );
877
910
  };
@@ -897,21 +930,25 @@ export const Whatsapp = (props) => {
897
930
  <CapHeading type="h4">
898
931
  {formatMessage(messages.btnLabel)}
899
932
  </CapHeading>
900
- <CapHeading className="whatsapp-optional-label">
933
+ {!isAuthenticationTemplate && <CapHeading className="whatsapp-optional-label">
901
934
  {formatMessage(messages.optional)}
902
- </CapHeading>
935
+ </CapHeading>}
903
936
  </CapRow>
904
937
  }
905
938
  description={
906
939
  <CapLabel type="label3">{formatMessage(messages.btnDesc)}</CapLabel>
907
940
  }
908
941
  />
909
- <CapRadioGroup
910
- options={buttonRadioOptions}
911
- value={buttonType}
912
- onChange={onChangeButtonType}
913
- className="whatsapp-btn-radio-group"
914
- />
942
+ {isAuthenticationTemplate ?
943
+ <CapWhatsappQuickReply
944
+ authenticationFlow={true}
945
+ /> :
946
+ <CapRadioGroup
947
+ options={buttonRadioOptions}
948
+ value={buttonType}
949
+ onChange={onChangeButtonType}
950
+ className="whatsapp-btn-radio-group"
951
+ />}
915
952
  </>
916
953
  );
917
954
 
@@ -1052,6 +1089,15 @@ export const Whatsapp = (props) => {
1052
1089
  };
1053
1090
 
1054
1091
  const onTemplateCategoryChange = (value) => {
1092
+ if (value === WHATSAPP_CATEGORIES.authentication) {
1093
+ setTemplateHeader('');
1094
+ setTemplateFooter('');
1095
+ setExpiryMinutes(0);
1096
+ setUnsubscribeRequired(false);
1097
+ setTemplateMessage(formatMessage(messages.authenticationMsg));
1098
+ } else {
1099
+ setTemplateMessage('');
1100
+ }
1055
1101
  setTemplateCategory(value);
1056
1102
  };
1057
1103
 
@@ -1127,6 +1173,10 @@ export const Whatsapp = (props) => {
1127
1173
  } else {
1128
1174
  errorMessage = variableErrorHandling(value, MESSAGE_TEXT);
1129
1175
  }
1176
+ //we are not allowing user input incase of authentication flow
1177
+ if(isAuthenticationTemplate){
1178
+ errorMessage = false;
1179
+ }
1130
1180
  return errorMessage;
1131
1181
  };
1132
1182
 
@@ -1190,7 +1240,7 @@ export const Whatsapp = (props) => {
1190
1240
  }
1191
1241
  //campaigns edit flow + unsubscribeNotRequired
1192
1242
  return updatedSmsEditor.join('');
1193
- } else if (unsubscribeRequired) {
1243
+ } else if (unsubscribeRequired && !isAuthenticationTemplate) {
1194
1244
  //creatives create and edit flow + unsubscribeRequired
1195
1245
  return `${templateMessage}\n${formatMessage(
1196
1246
  messages.modifiedUnsubscribeText,
@@ -1299,7 +1349,9 @@ export const Whatsapp = (props) => {
1299
1349
  },
1300
1350
  ...(isEditFlow && !isFullMode && {
1301
1351
  isPreviewUrl: showUrlPreview
1302
- })
1352
+ }),
1353
+ securityRecommendation: unsubscribeRequired,
1354
+ codeExpiryMinutes: expiryMinutes,
1303
1355
  },
1304
1356
  },
1305
1357
  },
@@ -1364,7 +1416,7 @@ export const Whatsapp = (props) => {
1364
1416
  return true;
1365
1417
  }
1366
1418
  //if template message is not entered
1367
- if (templateMessage.trim() === '' || templateMessageError) {
1419
+ if (templateMessage?.trim() === '' || templateMessageError) {
1368
1420
  return true;
1369
1421
  }
1370
1422
  //if media type is image, video or document and the mediaType file is not uploaded
@@ -1391,6 +1443,9 @@ export const Whatsapp = (props) => {
1391
1443
  return true;
1392
1444
  }
1393
1445
 
1446
+ if(isAuthenticationTemplate && securityWarningReqd && !expiryMinutes){
1447
+ return true;
1448
+ }
1394
1449
  if (isBtnTypeQuickReply) {
1395
1450
  return !quickReplyData.every((quickReply) => quickReply?.isSaved === true);
1396
1451
  }
@@ -1399,14 +1454,15 @@ export const Whatsapp = (props) => {
1399
1454
 
1400
1455
  const renderMediaSection = () => (
1401
1456
  <>
1402
- {renderLabel('mediaLabel')}
1403
- <CapRadioGroup
1404
- id="whatsapp-media-radio"
1405
- options={mediaRadioOptions}
1406
- value={templateMediaType}
1407
- onChange={onTemplateMediaTypeChange}
1408
- className="whatsapp-media-radio"
1409
- />
1457
+ {!isAuthenticationTemplate && <>{renderLabel('mediaLabel')}
1458
+ <CapRadioGroup
1459
+ id="whatsapp-media-radio"
1460
+ options={mediaRadioOptions}
1461
+ value={templateMediaType}
1462
+ onChange={onTemplateMediaTypeChange}
1463
+ className="whatsapp-media-radio"
1464
+ />
1465
+ </>}
1410
1466
  </>
1411
1467
  );
1412
1468
 
@@ -1471,7 +1527,7 @@ export const Whatsapp = (props) => {
1471
1527
  {renderMediaSection()}
1472
1528
  {renderMediaComponent()}
1473
1529
  {/* this section is for header section in create mode */}
1474
- {isMediaTypeText && isHostIsNotTwilio && (
1530
+ {isMediaTypeText && isHostIsNotTwilio && !isAuthenticationTemplate && (
1475
1531
  <>
1476
1532
  <CapHeader
1477
1533
  title={
@@ -1548,16 +1604,22 @@ export const Whatsapp = (props) => {
1548
1604
  </CapHeading>
1549
1605
  }
1550
1606
  suffix={
1551
- <CapButton
1552
- type="flat"
1553
- isAddBtn
1554
- onClick={onMessageAddVar}
1555
- disabled={
1556
- (addedVarCount >= 19 || templateMessageError) && templateMessage
1557
- }
1558
- >
1559
- {formatMessage(messages.addVar)}
1560
- </CapButton>
1607
+ <>
1608
+ {!isAuthenticationTemplate && (
1609
+ <CapButton
1610
+ data-testid="suffix-button"
1611
+ type="flat"
1612
+ isAddBtn
1613
+ onClick={onMessageAddVar}
1614
+ disabled={
1615
+ (addedVarCount >= 19 || templateMessageError) &&
1616
+ templateMessage
1617
+ }
1618
+ >
1619
+ {formatMessage(messages.addVar)}
1620
+ </CapButton>
1621
+ )}
1622
+ </>
1561
1623
  }
1562
1624
  />
1563
1625
  <CapRow className="whatsapp-create-template-message-input">
@@ -1574,6 +1636,7 @@ export const Whatsapp = (props) => {
1574
1636
  )
1575
1637
  }
1576
1638
  value={templateMessage || ""}
1639
+ disabled={isAuthenticationTemplate}
1577
1640
  />
1578
1641
  {renderUnsubscribeText()}
1579
1642
  </CapRow>
@@ -1601,20 +1664,87 @@ export const Whatsapp = (props) => {
1601
1664
  }
1602
1665
  />
1603
1666
  <CapRow className="whatsapp-create-template-message-input whatsapp-create-template-header-input">
1604
- <TextArea
1605
- id="whatsapp-create-template-message-input"
1606
- autosize={{ minRows: 1, maxRows: 5 }}
1607
- placeholder={formatMessage(messages.templateMessagePlaceholder)}
1608
- onChange={onTemplateFooterChanges}
1609
- errorMessage={
1610
- templateFooterError && (
1611
- <CapError className="whatsapp-template-message-error">
1612
- {templateFooterError}
1613
- </CapError>
1614
- )
1615
- }
1616
- value={templateFooter || ""}
1617
- />
1667
+ {isAuthenticationTemplate ? (
1668
+ <CapRow className="whatsapp-extra-security-footer">
1669
+ <CapCheckbox
1670
+ onChange={extraSecurityFooter}
1671
+ checked={securityWarningReqd}
1672
+ disabled={isEditFlow}
1673
+ autoFocus={true}
1674
+ className="whatsapp-unsubscribe-checkbox"
1675
+ >
1676
+ <CapLabel.CapLabelInline
1677
+ type="label2"
1678
+ className="whatsapp-render-unsubscribe-text"
1679
+ >
1680
+ {formatMessage(messages.expirationWarning)}
1681
+ </CapLabel.CapLabelInline>
1682
+ </CapCheckbox>
1683
+ {securityWarningReqd && (
1684
+ <>
1685
+ <CapRow className="whatsapp-extra-security-footer-input">
1686
+ {formatMessage(messages.codeExpiresIn)}
1687
+ <CapInput.Number
1688
+ min={0}
1689
+ max={90}
1690
+ placeholder={formatMessage(messages.enterExpiryTime)}
1691
+ onChange={(event) => changeExpiryMinutes(event)}
1692
+ />
1693
+ {formatMessage(messages.minutes)}
1694
+ </CapRow>
1695
+ <CapRow className="whatsapp-extra-security-footer-note">
1696
+ {formatMessage(messages.expiryCodeNote)}
1697
+ <CapTooltip
1698
+ title={
1699
+ <CapHeader
1700
+ titleClass="whatsapp-tooltip-header"
1701
+ descriptionClass="whatsapp-tooltip-header"
1702
+ size="small"
1703
+ title={formatMessage(
1704
+ messages.checkExpiryTooltipHeader
1705
+ )}
1706
+ description={formatMessage(
1707
+ messages.checkExpiryTooltipDesc
1708
+ )}
1709
+ />
1710
+ }
1711
+ >
1712
+
1713
+ <CapButton
1714
+ type="flat"
1715
+ className="add-btn"
1716
+ onClick={() =>
1717
+ window.open(
1718
+ `${window.location.origin}${OTP_CONFIG_URI}`,
1719
+ "_blank"
1720
+ )
1721
+ }
1722
+ >
1723
+ {formatMessage(messages.checkExpiryCode)}
1724
+ <CapIcon size="s" type="launch"/>
1725
+ </CapButton>
1726
+ </CapTooltip>
1727
+ </CapRow>
1728
+ </>
1729
+ )}
1730
+ </CapRow>
1731
+ ) : (
1732
+ <TextArea
1733
+ id="whatsapp-create-template-message-input"
1734
+ autosize={{ minRows: 1, maxRows: 5 }}
1735
+ placeholder={formatMessage(messages.templateMessagePlaceholder)}
1736
+ onChange={onTemplateFooterChanges}
1737
+ errorMessage={
1738
+ templateFooterError && (
1739
+ <CapError className="whatsapp-template-message-error">
1740
+ {templateFooterError}
1741
+ </CapError>
1742
+ )
1743
+ }
1744
+ value={templateFooter || ""}
1745
+ disabled={isAuthenticationTemplate}
1746
+ />
1747
+ )}
1618
1748
  </CapRow>
1619
1749
  {renderMessageLength(FOOTER_TEXT)}
1620
1750
  </>
@@ -1959,10 +2089,17 @@ export const Whatsapp = (props) => {
1959
2089
  <CapLabel type="label15">
1960
2090
  {isBtnTypeCta
1961
2091
  ? formatMessage(messages.btnTypeCTA)
1962
- : formatMessage(messages.btnTypeNone)}
2092
+ : !isAuthenticationTemplate && formatMessage(messages.btnTypeNone)}
1963
2093
  </CapLabel>
1964
2094
  </>
1965
2095
  )}
2096
+ {isAuthenticationTemplate && (
2097
+ <CapLabel type="label15">
2098
+ <CapWhatsappQuickReply
2099
+ authenticationFlow={true}
2100
+ />
2101
+ </CapLabel>
2102
+ )}
1966
2103
  {isBtnTypeCta && (
1967
2104
  <CapWhatsappCTA
1968
2105
  ctaData={ctaData}
@@ -1996,7 +2133,7 @@ export const Whatsapp = (props) => {
1996
2133
  <CapLabel type="label5">
1997
2134
  {isEditFlow ? updatedSmsEditor.join("") : templateMessage}
1998
2135
  </CapLabel>
1999
- {unsubscribeRequired && (
2136
+ {unsubscribeRequired && !isAuthenticationTemplate && (
2000
2137
  <CapLabel type="label5">
2001
2138
  {formatMessage(messages.templateMessageUnsubscribeText)}
2002
2139
  </CapLabel>
@@ -2039,6 +2176,9 @@ export const Whatsapp = (props) => {
2039
2176
  ...(isBtnTypeCta && {
2040
2177
  ctaData,
2041
2178
  }),
2179
+ ...(isAuthenticationTemplate && {
2180
+ ctaData : [{text: formatMessage(globalMessages.autofill), type: WHATSAPP_CATEGORIES.authentication}]
2181
+ }),
2042
2182
  ...(isBtnTypeQuickReply && {
2043
2183
  quickReplyData,
2044
2184
  }),
@@ -204,4 +204,39 @@
204
204
  font-size: $FONT_SIZE_M;
205
205
  font-weight: $FONT_WEIGHT_MEDIUM;
206
206
  }
207
+ .whatsapp-extra-security-footer {
208
+ margin-bottom: 1rem;
209
+ display: flex;
210
+ justify-content: flex-start;
211
+ flex-direction: column;
212
+ padding-left: $CAP_SPACE_16;
213
+ gap: $CAP_SPACE_08;
214
+
215
+ .whatsapp-extra-security-footer-note {
216
+ font-size: small;
217
+ margin-top: $CAP_SPACE_04;
218
+ display: inline-flex;
219
+ padding-left: $CAP_SPACE_12;
220
+ border-radius: $CAP_SPACE_04 0px 0px 0px;
221
+ background: $CAP_PURPLE04;
222
+ align-items: center;
223
+ justify-content: space-between;
224
+
225
+ .add-btn {
226
+ display: flex;
227
+ background-color: inherit;
228
+ align-items: center;
229
+ }
230
+ }
231
+
232
+ .whatsapp-extra-security-footer-input {
233
+ display: inline-flex;
234
+ align-items: center;
235
+ gap: $CAP_SPACE_04;
236
+ }
237
+ }
238
+
239
+ }
240
+ .whatsapp-tooltip-header{
241
+ color: $CAP_G10;
207
242
  }
@@ -664,4 +664,56 @@ export default defineMessages({
664
664
  id: `${prefix}.showUrlPreview`,
665
665
  defaultMessage: 'Show URL preview',
666
666
  },
667
+ templateSecurityDisclaimer: {
668
+ id: `${prefix}.templateSecurityDisclaimer`,
669
+ defaultMessage: 'Add security disclaimer to your message',
670
+ },
671
+ expirationWarning: {
672
+ id: `${prefix}.expirationWarning`,
673
+ defaultMessage: 'Add expiry warning for code',
674
+ },
675
+ checkExpiryCode: {
676
+ id: `${prefix}.checkExpiryCode`,
677
+ defaultMessage: 'OTP settings',
678
+ },
679
+ expiryCodeNote:{
680
+ id: `${prefix}.expiryCodeNote`,
681
+ defaultMessage: 'We suggest using the same expiry time as specified in the OTP settings.',
682
+ },
683
+ codeExpiresIn:{
684
+ id: `${prefix}.codeExpiresIn`,
685
+ defaultMessage: 'This code expires in',
686
+ },
687
+ minutes:{
688
+ id: `${prefix}.minutes`,
689
+ defaultMessage: 'minutes',
690
+ },
691
+ codeExpiryNumMinutes:{
692
+ id: `${prefix}.codeExpiryNumMinutes`,
693
+ defaultMessage: 'This code expires in <num_minutes> minutes.',
694
+ },
695
+ authenticationMsg:{
696
+ id: `${prefix}.authenticationMsg`,
697
+ defaultMessage: "{{verification_code}} is your verification code.",
698
+ },
699
+ securitySuffix:{
700
+ id: `${prefix}.securitySuffix`,
701
+ defaultMessage: 'For your security, do not share this code.',
702
+ },
703
+ numMinutes:{
704
+ id: `${prefix}.numMinutes`,
705
+ defaultMessage: '<num_minutes>',
706
+ },
707
+ enterExpiryTime:{
708
+ id: `${prefix}.enterExpiryTime`,
709
+ defaultMessage: "Enter expiry time"
710
+ },
711
+ checkExpiryTooltipHeader: {
712
+ id: `${prefix}.checkExpiryTooltipHeader`,
713
+ defaultMessage: 'Open OTP settings in new tab',
714
+ },
715
+ checkExpiryTooltipDesc: {
716
+ id: `${prefix}.checkExpiryTooltipDesc`,
717
+ defaultMessage: 'To check the configured expiry time that is same for all OTPs',
718
+ },
667
719
  });
@@ -69,4 +69,9 @@
69
69
 
70
70
  .whatsapp-list-view-divider {
71
71
  margin: 0;
72
+ }
73
+
74
+ .whatsapp-autofill-btn {
75
+ text-align: center;
76
+ margin-top: $CAP_SPACE_12;
72
77
  }