@capillarytech/creatives-library 8.0.327 → 8.0.329

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 (72) hide show
  1. package/package.json +1 -1
  2. package/services/api.js +17 -0
  3. package/services/tests/api.test.js +85 -0
  4. package/utils/commonUtils.js +10 -0
  5. package/utils/tagValidations.js +2 -3
  6. package/utils/tests/commonUtil.test.js +169 -0
  7. package/utils/tests/tagValidations.test.js +1 -35
  8. package/v2Components/CapTagList/index.js +22 -14
  9. package/v2Components/CapTagList/style.scss +0 -48
  10. package/v2Components/CapTagListWithInput/index.js +0 -4
  11. package/v2Components/CapWhatsappCTA/index.js +0 -2
  12. package/v2Components/CommonTestAndPreview/AddTestCustomer.js +42 -0
  13. package/v2Components/CommonTestAndPreview/CustomerCreationModal.js +155 -0
  14. package/v2Components/CommonTestAndPreview/ExistingCustomerModal.js +93 -0
  15. package/v2Components/CommonTestAndPreview/SendTestMessage.js +79 -51
  16. package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +134 -34
  17. package/v2Components/CommonTestAndPreview/actions.js +10 -0
  18. package/v2Components/CommonTestAndPreview/constants.js +15 -1
  19. package/v2Components/CommonTestAndPreview/index.js +315 -15
  20. package/v2Components/CommonTestAndPreview/messages.js +106 -0
  21. package/v2Components/CommonTestAndPreview/reducer.js +10 -0
  22. package/v2Components/CommonTestAndPreview/tests/AddTestCustomer.test.js +66 -0
  23. package/v2Components/CommonTestAndPreview/tests/CommonTestAndPreview.addTestCustomer.test.js +648 -0
  24. package/v2Components/CommonTestAndPreview/tests/CustomValuesEditor.test.js +24 -0
  25. package/v2Components/CommonTestAndPreview/tests/CustomerCreationModal.test.js +174 -0
  26. package/v2Components/CommonTestAndPreview/tests/ExistingCustomerModal.test.js +114 -0
  27. package/v2Components/CommonTestAndPreview/tests/SendTestMessage.test.js +52 -0
  28. package/v2Components/CommonTestAndPreview/tests/constants.test.js +31 -1
  29. package/v2Components/CommonTestAndPreview/tests/index.test.js +36 -0
  30. package/v2Components/CommonTestAndPreview/tests/reducer.test.js +71 -0
  31. package/v2Components/CommonTestAndPreview/tests/selectors.test.js +17 -0
  32. package/v2Components/FormBuilder/index.js +0 -7
  33. package/v2Components/HtmlEditor/HTMLEditor.js +1 -6
  34. package/v2Components/HtmlEditor/__tests__/HTMLEditor.apiErrors.test.js +0 -1
  35. package/v2Components/HtmlEditor/__tests__/HTMLEditor.test.js +2 -927
  36. package/v2Components/HtmlEditor/components/CodeEditorPane/index.js +0 -3
  37. package/v2Containers/BeeEditor/index.js +0 -3
  38. package/v2Containers/CreativesContainer/SlideBoxContent.js +1 -28
  39. package/v2Containers/CreativesContainer/index.js +0 -3
  40. package/v2Containers/Email/index.js +0 -1
  41. package/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +1 -7
  42. package/v2Containers/EmailWrapper/components/EmailWrapperView.js +0 -3
  43. package/v2Containers/EmailWrapper/components/__tests__/EmailHTMLEditor.test.js +2 -20
  44. package/v2Containers/EmailWrapper/components/__tests__/EmailWrapperView.test.js +1 -16
  45. package/v2Containers/EmailWrapper/hooks/useEmailWrapper.js +0 -3
  46. package/v2Containers/EmailWrapper/index.js +0 -4
  47. package/v2Containers/EmailWrapper/tests/useEmailWrapper.edgeCases.test.js +0 -1
  48. package/v2Containers/EmailWrapper/tests/useEmailWrapper.test.js +0 -9
  49. package/v2Containers/InAppWrapper/hooks/__tests__/useInAppWrapper.test.js +0 -19
  50. package/v2Containers/InAppWrapper/hooks/useInAppWrapper.js +0 -3
  51. package/v2Containers/InAppWrapper/index.js +0 -3
  52. package/v2Containers/MobilePush/Create/index.js +0 -2
  53. package/v2Containers/MobilePush/Edit/index.js +0 -2
  54. package/v2Containers/MobilepushWrapper/index.js +1 -3
  55. package/v2Containers/Rcs/index.js +0 -1
  56. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +1886 -1754
  57. package/v2Containers/Sms/Create/index.js +0 -2
  58. package/v2Containers/Sms/Edit/index.js +0 -2
  59. package/v2Containers/SmsTrai/Edit/index.js +0 -2
  60. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +351 -318
  61. package/v2Containers/SmsWrapper/index.js +0 -2
  62. package/v2Containers/TagList/index.js +2 -41
  63. package/v2Containers/TagList/messages.js +0 -4
  64. package/v2Containers/TagList/tests/TagList.test.js +20 -122
  65. package/v2Containers/TagList/tests/mockdata.js +0 -17
  66. package/v2Containers/Viber/index.js +0 -5
  67. package/v2Containers/WebPush/Create/hooks/useTagManagement.js +2 -0
  68. package/v2Containers/WebPush/Create/index.js +1 -9
  69. package/v2Containers/Whatsapp/index.js +0 -5
  70. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +5586 -5232
  71. package/v2Containers/Zalo/index.js +0 -2
  72. package/v2Components/CapTagListWithInput/__tests__/CapTagListWithInput.test.js +0 -63
@@ -0,0 +1,155 @@
1
+ import CapModal from "@capillarytech/cap-ui-library/CapModal";
2
+ import CapButton from "@capillarytech/cap-ui-library/CapButton";
3
+ import CapInput from "@capillarytech/cap-ui-library/CapInput";
4
+ import CapLabel from "@capillarytech/cap-ui-library/CapLabel";
5
+ import CapRow from "@capillarytech/cap-ui-library/CapRow";
6
+ import CapColumn from "@capillarytech/cap-ui-library/CapColumn";
7
+ import { FormattedMessage, injectIntl, intlShape } from "react-intl";
8
+ import messages from "./messages";
9
+ import React, { useState } from "react";
10
+ import PropTypes from "prop-types";
11
+ import { CHANNELS } from "./constants";
12
+
13
+ /** Identifier validation runs in the parent before this modal opens; email/mobile are read-only here. */
14
+ const EMPTY_VALIDATION = { email: "", mobile: "" };
15
+
16
+ const CustomerCreationModal = ({
17
+ customerModal,
18
+ onCloseCustomerModal,
19
+ channel,
20
+ customerData,
21
+ setCustomerData,
22
+ onSave,
23
+ intl,
24
+ }) => {
25
+ const [isLoading, setIsLoading] = useState(false);
26
+
27
+ // Check if required fields are filled based on channel
28
+ const isRequiredFieldMissing = () => {
29
+ if (channel === CHANNELS.EMAIL && !customerData.email) {
30
+ return true;
31
+ }
32
+ if (channel === CHANNELS.SMS && !customerData.mobile) {
33
+ return true;
34
+ }
35
+ return false;
36
+ };
37
+
38
+ const isSaveDisabled = () => isRequiredFieldMissing() || isLoading;
39
+
40
+ return (
41
+ <CapModal
42
+ visible={customerModal[0]}
43
+ onCancel={onCloseCustomerModal}
44
+ centered={true}
45
+ width={500}
46
+ maskStyle={{ backgroundColor: 'rgba(244, 245, 247, 0.9)' }}
47
+ footer={
48
+ <CapRow justify="start" gutter={8}>
49
+ <CapButton
50
+ type="primary"
51
+ onClick={() => onSave(EMPTY_VALIDATION, setIsLoading)}
52
+ disabled={isSaveDisabled()}
53
+ loading={isLoading}
54
+ >
55
+ <FormattedMessage {...messages.saveButton} />
56
+ </CapButton>
57
+ <CapButton
58
+ type="secondary"
59
+ onClick={onCloseCustomerModal}
60
+ disabled={isLoading}
61
+ >
62
+ <FormattedMessage {...messages.cancelButton} />
63
+ </CapButton>
64
+ </CapRow>
65
+ }
66
+ title={<FormattedMessage {...messages.customerCreationModalTitle} />}
67
+ wrapClassName="common-test-preview-modal-wrap"
68
+ className="common-test-preview-modal"
69
+ >
70
+ <CapRow className="customer-creation-modal-row">
71
+ <CapColumn span={24}>
72
+ <span className="customer-creation-modal-description">
73
+ <FormattedMessage {...messages.customerCreationModalDescription} />
74
+ </span>
75
+ </CapColumn>
76
+ </CapRow>
77
+
78
+ <CapRow className="customer-creation-modal-row">
79
+ <CapColumn span={24}>
80
+ <CapLabel type="label1" className="customer-creation-modal-label">
81
+ <FormattedMessage {...messages.customerName} />{' '}
82
+ <span className="customer-creation-modal-optional">
83
+ <FormattedMessage {...messages.customerNameOptional} />
84
+ </span>
85
+ </CapLabel>
86
+ <CapInput
87
+ value={customerData.name || ""}
88
+ onChange={e =>
89
+ setCustomerData({ ...customerData, name: e.target.value })
90
+ }
91
+ placeholder={intl.formatMessage(messages.customerNamePlaceholder)}
92
+ size="default"
93
+ className="customer-creation-modal-input"
94
+ />
95
+ </CapColumn>
96
+ </CapRow>
97
+
98
+ {channel===CHANNELS.EMAIL && <CapRow className="customer-creation-modal-row">
99
+ <CapColumn span={24}>
100
+ <CapLabel type="label1" className="customer-creation-modal-label">
101
+ <FormattedMessage {...messages.customerEmail} />
102
+ </CapLabel>
103
+ <CapInput
104
+ value={customerData.email || ""}
105
+ onChange={() => {}}
106
+ placeholder={intl.formatMessage(messages.customerEmailPlaceholder)}
107
+ size="default"
108
+ className="customer-creation-modal-input"
109
+ disabled={true}
110
+ />
111
+ </CapColumn>
112
+ </CapRow>}
113
+
114
+ {channel===CHANNELS.SMS &&
115
+ <CapRow className="customer-creation-modal-row customer-creation-modal-row--last">
116
+ <CapColumn span={24}>
117
+ <CapLabel type="label1" className="customer-creation-modal-label">
118
+ <FormattedMessage {...messages.customerMobile} />
119
+ </CapLabel>
120
+ <CapInput
121
+ value={customerData.mobile || ""}
122
+ onChange={() => {}}
123
+ placeholder={intl.formatMessage(messages.customerMobilePlaceholder)}
124
+ size="default"
125
+ className="customer-creation-modal-input"
126
+ disabled={true}
127
+ />
128
+ </CapColumn>
129
+ </CapRow>}
130
+ </CapModal>
131
+ );
132
+ };
133
+
134
+ CustomerCreationModal.propTypes = {
135
+ customerModal: PropTypes.arrayOf(
136
+ PropTypes.oneOfType([PropTypes.bool, PropTypes.string, PropTypes.number]),
137
+ ).isRequired,
138
+ onCloseCustomerModal: PropTypes.func.isRequired,
139
+ channel: PropTypes.oneOf(Object.values(CHANNELS)).isRequired,
140
+ customerData: PropTypes.shape({
141
+ name: PropTypes.string,
142
+ email: PropTypes.string,
143
+ mobile: PropTypes.string,
144
+ customerId: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
145
+ }),
146
+ setCustomerData: PropTypes.func.isRequired,
147
+ onSave: PropTypes.func.isRequired,
148
+ intl: intlShape.isRequired,
149
+ };
150
+
151
+ CustomerCreationModal.defaultProps = {
152
+ customerData: {},
153
+ };
154
+
155
+ export default injectIntl(CustomerCreationModal);
@@ -0,0 +1,93 @@
1
+ import CapModal from "@capillarytech/cap-ui-library/CapModal";
2
+ import { FormattedMessage, injectIntl, intlShape } from "react-intl";
3
+ import messages from "./messages";
4
+ import React, { useState } from "react";
5
+ import PropTypes from "prop-types";
6
+ import { CapCard, CapRow, CapColumn } from "@capillarytech/cap-ui-library";
7
+ import { CHANNELS } from "./constants";
8
+ import CapButton from "@capillarytech/cap-ui-library/CapButton";
9
+ import CapIcon from "@capillarytech/cap-ui-library/CapIcon";
10
+
11
+
12
+ const ExistingCustomerModal = ({ customerModal, onCloseCustomerModal, customerData, channel, onSave, intl }) => {
13
+ const [isLoading, setIsLoading] = useState(false);
14
+ return (
15
+ <CapModal
16
+ visible={customerModal[0]}
17
+ onCancel={onCloseCustomerModal}
18
+ centered={true}
19
+ width={500}
20
+ maskStyle={{ backgroundColor: 'rgba(244, 245, 247, 0.9)' }}
21
+ footer={
22
+ <CapRow justify="start">
23
+ <CapButton
24
+ type="primary"
25
+ onClick={() => onSave({}, setIsLoading)}
26
+ loading={isLoading}
27
+ >
28
+ <FormattedMessage {...messages.saveButton} />
29
+ </CapButton>
30
+ <CapButton
31
+ type="secondary"
32
+ onClick={onCloseCustomerModal}
33
+ disabled={isLoading}
34
+ >
35
+ <FormattedMessage {...messages.cancelButton} />
36
+ </CapButton>
37
+ </CapRow>
38
+ }
39
+ title={intl.formatMessage(messages.customerCreationModalTitle)}
40
+ wrapClassName="common-test-preview-modal-wrap existing-customer-modal-wrap"
41
+ className="common-test-preview-modal"
42
+ >
43
+ <div className="existing-customer-modal">
44
+ <CapRow className="existing-customer-modal-intro-row">
45
+ <FormattedMessage {...messages.existingCustomerModalDescription} />
46
+ </CapRow>
47
+ <CapCard className="existing-customer-modal-card">
48
+ <CapRow className="existing-customer-modal-card-row">
49
+ <CapColumn className="existing-customer-modal-avatar">
50
+ <CapIcon type="user-profile" className="existing-customer-modal-avatar-icon" />
51
+ </CapColumn>
52
+ <CapColumn className="existing-customer-modal-details">
53
+ <CapRow className="existing-customer-modal-name">
54
+ {customerData.name || "-"}
55
+ </CapRow>
56
+ <CapColumn className="existing-customer-modal-meta">
57
+ {channel === CHANNELS.EMAIL && customerData.email && (
58
+ <CapRow><span className="existing-customer-modal-meta-label"><FormattedMessage {...messages.customerEmail} />: </span> {customerData.email}</CapRow>
59
+ )}
60
+ {channel === CHANNELS.SMS && customerData.mobile && (
61
+ <CapRow><span className="existing-customer-modal-meta-label"><FormattedMessage {...messages.customerMobile} />: </span>{customerData.mobile}</CapRow>
62
+ )}
63
+ <CapRow><span className="existing-customer-modal-meta-label"><FormattedMessage {...messages.customerID} />: </span>{customerData.customerId}</CapRow>
64
+ </CapColumn>
65
+ </CapColumn>
66
+ </CapRow>
67
+ </CapCard>
68
+ </div>
69
+ </CapModal>
70
+ );
71
+ };
72
+
73
+ ExistingCustomerModal.propTypes = {
74
+ customerModal: PropTypes.arrayOf(
75
+ PropTypes.oneOfType([PropTypes.bool, PropTypes.string, PropTypes.number]),
76
+ ).isRequired,
77
+ onCloseCustomerModal: PropTypes.func.isRequired,
78
+ customerData: PropTypes.shape({
79
+ name: PropTypes.string,
80
+ email: PropTypes.string,
81
+ mobile: PropTypes.string,
82
+ customerId: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
83
+ }),
84
+ channel: PropTypes.oneOf(Object.values(CHANNELS)).isRequired,
85
+ onSave: PropTypes.func.isRequired,
86
+ intl: intlShape.isRequired,
87
+ };
88
+
89
+ ExistingCustomerModal.defaultProps = {
90
+ customerData: {},
91
+ };
92
+
93
+ export default injectIntl(ExistingCustomerModal);
@@ -25,6 +25,9 @@ const SendTestMessage = ({
25
25
  channel,
26
26
  isSendingTestMessage,
27
27
  formatMessage,
28
+ renderAddTestCustomerButton,
29
+ searchValue,
30
+ setSearchValue,
28
31
  deliverySettings,
29
32
  senderDetailsByChannel = {},
30
33
  wecrmAccounts,
@@ -33,59 +36,78 @@ const SendTestMessage = ({
33
36
  smsTraiDltEnabled,
34
37
  registeredSenderIds,
35
38
  isChannelSmsFallbackPreviewEnabled,
36
- }) => (
37
- <CapStepsAccordian
38
- showNumberSteps={false}
39
- isChevronIcon
40
- expandIconPosition="right"
41
- items={[
42
- {
43
- header: <CapHeader
44
- size="regular"
45
- description={<FormattedMessage {...messages.testMessageDescription} />}
46
- title={<FormattedMessage {...messages.sendTestMessage} />}
47
- />,
48
- content: (
49
- <CapRow className="send-test-content">
50
- <CapHeader size="label1" title={<FormattedMessage {...messages.testCustomers} />} />
51
- <CapTreeSelect
52
- className="test-customers-tree-select"
53
- loading={isFetchingTestCustomers || isFetchingTestGroups}
54
- treeData={testEntitiesTreeData}
55
- onChange={handleTestEntitiesChange}
56
- value={selectedTestEntities}
57
- multiple
58
- placeholder={formatMessage(messages.testCustomersPlaceholder)}
59
- getPopupContainer={(triggerNode) => triggerNode.parentNode}
39
+ }) => {
40
+ const addCustomerContent = renderAddTestCustomerButton ? renderAddTestCustomerButton() : null;
41
+ return (
42
+ <CapStepsAccordian
43
+ showNumberSteps={false}
44
+ isChevronIcon
45
+ expandIconPosition="right"
46
+ items={[
47
+ {
48
+ header: (
49
+ <CapHeader
50
+ size="regular"
51
+ description={<FormattedMessage {...messages.testMessageDescription} />}
52
+ title={<FormattedMessage {...messages.sendTestMessage} />}
60
53
  />
61
- {CHANNELS_WITH_DELIVERY_SETTINGS.includes(channel) && (
62
- <DeliverySettings
63
- channel={channel}
64
- deliverySettings={deliverySettings || {}}
65
- senderDetailsByChannel={senderDetailsByChannel}
66
- wecrmAccounts={wecrmAccounts || []}
67
- onSaveDeliverySettings={onSaveDeliverySettings}
68
- isLoadingSenderDetails={isLoadingSenderDetails}
69
- formatMessage={formatMessage}
70
- smsTraiDltEnabled={smsTraiDltEnabled}
71
- registeredSenderIds={registeredSenderIds}
72
- isChannelSmsFallbackPreviewEnabled={isChannelSmsFallbackPreviewEnabled}
73
- whatsappAccountFromForm={
74
- channel === CHANNELS.WHATSAPP && formData?.accountName
75
- ? { accountName: formData.accountName }
76
- : undefined
77
- }
54
+ ),
55
+ content: (
56
+ <CapRow className="send-test-content">
57
+ <CapHeader size="label1" title={<FormattedMessage {...messages.testCustomers} />} />
58
+ <CapTreeSelect
59
+ className="test-customers-tree-select"
60
+ dropdownMatchSelectWidth
61
+ dropdownClassName="test-customers-tree-select-dropdown"
62
+ listHeight={280}
63
+ loading={isFetchingTestCustomers || isFetchingTestGroups}
64
+ treeData={testEntitiesTreeData}
65
+ onChange={handleTestEntitiesChange}
66
+ value={selectedTestEntities}
67
+ showSearch
68
+ searchValue={searchValue}
69
+ onSearch={setSearchValue}
70
+ treeNodeFilterProp="title"
71
+ {...(addCustomerContent ? { notFoundContent: addCustomerContent } : {})}
72
+ multiple
73
+ placeholder={formatMessage(messages.testCustomersPlaceholder)}
74
+ dropdownStyle={{ zIndex: 900, maxHeight: 320 }}
75
+ placement="bottomLeft"
76
+ getPopupContainer={(node) => node.closest('.send-test-section') || document.body}
78
77
  />
79
- )}
80
- <CapButton onClick={handleSendTestMessage} disabled={isEmpty(selectedTestEntities) || isSendingTestMessage}>
81
- <FormattedMessage {...messages.sendTestButton} />
82
- </CapButton>
83
- </CapRow>),
84
- key: 1,
85
- },
86
- ]}
87
- />
88
- );
78
+ {CHANNELS_WITH_DELIVERY_SETTINGS.includes(channel) && (
79
+ <DeliverySettings
80
+ channel={channel}
81
+ deliverySettings={deliverySettings || {}}
82
+ senderDetailsByChannel={senderDetailsByChannel}
83
+ wecrmAccounts={wecrmAccounts || []}
84
+ onSaveDeliverySettings={onSaveDeliverySettings}
85
+ isLoadingSenderDetails={isLoadingSenderDetails}
86
+ formatMessage={formatMessage}
87
+ smsTraiDltEnabled={smsTraiDltEnabled}
88
+ registeredSenderIds={registeredSenderIds}
89
+ isChannelSmsFallbackPreviewEnabled={isChannelSmsFallbackPreviewEnabled}
90
+ whatsappAccountFromForm={
91
+ channel === CHANNELS.WHATSAPP && formData?.accountName
92
+ ? { accountName: formData.accountName }
93
+ : undefined
94
+ }
95
+ />
96
+ )}
97
+ <CapButton
98
+ onClick={handleSendTestMessage}
99
+ disabled={isEmpty(selectedTestEntities) || isSendingTestMessage}
100
+ >
101
+ <FormattedMessage {...messages.sendTestButton} />
102
+ </CapButton>
103
+ </CapRow>
104
+ ),
105
+ key: 1,
106
+ },
107
+ ]}
108
+ />
109
+ );
110
+ };
89
111
 
90
112
  SendTestMessage.propTypes = {
91
113
  isFetchingTestCustomers: PropTypes.bool.isRequired,
@@ -98,6 +120,9 @@ SendTestMessage.propTypes = {
98
120
  channel: PropTypes.string,
99
121
  isSendingTestMessage: PropTypes.bool.isRequired,
100
122
  formatMessage: PropTypes.func.isRequired,
123
+ renderAddTestCustomerButton: PropTypes.func,
124
+ searchValue: PropTypes.string,
125
+ setSearchValue: PropTypes.func,
101
126
  deliverySettings: PropTypes.object,
102
127
  senderDetailsByChannel: PropTypes.object,
103
128
  wecrmAccounts: PropTypes.array,
@@ -111,6 +136,9 @@ SendTestMessage.propTypes = {
111
136
  SendTestMessage.defaultProps = {
112
137
  formData: undefined,
113
138
  channel: undefined,
139
+ renderAddTestCustomerButton: undefined,
140
+ searchValue: '',
141
+ setSearchValue: () => {}, // no-op when not provided; required by TreeSelect when showSearch is true
114
142
  deliverySettings: {},
115
143
  senderDetailsByChannel: {},
116
144
  wecrmAccounts: [],