@capillarytech/creatives-library 8.0.297 → 8.0.299-alpha.3

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 (28) 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/tests/commonUtil.test.js +169 -0
  6. package/v2Components/CommonTestAndPreview/AddTestCustomer.js +42 -0
  7. package/v2Components/CommonTestAndPreview/CustomerCreationModal.js +284 -0
  8. package/v2Components/CommonTestAndPreview/ExistingCustomerModal.js +72 -0
  9. package/v2Components/CommonTestAndPreview/SendTestMessage.js +78 -49
  10. package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +189 -4
  11. package/v2Components/CommonTestAndPreview/actions.js +10 -0
  12. package/v2Components/CommonTestAndPreview/constants.js +18 -1
  13. package/v2Components/CommonTestAndPreview/index.js +259 -14
  14. package/v2Components/CommonTestAndPreview/messages.js +94 -0
  15. package/v2Components/CommonTestAndPreview/reducer.js +10 -0
  16. package/v2Components/CommonTestAndPreview/tests/AddTestCustomer.test.js +66 -0
  17. package/v2Components/CommonTestAndPreview/tests/CommonTestAndPreview.addTestCustomer.test.js +653 -0
  18. package/v2Components/CommonTestAndPreview/tests/CustomerCreationModal.test.js +316 -0
  19. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/ModifyDeliverySettings.test.js +0 -1
  20. package/v2Components/CommonTestAndPreview/tests/ExistingCustomerModal.test.js +114 -0
  21. package/v2Components/CommonTestAndPreview/tests/SendTestMessage.test.js +53 -0
  22. package/v2Components/CommonTestAndPreview/tests/constants.test.js +25 -2
  23. package/v2Components/CommonTestAndPreview/tests/index.test.js +7 -0
  24. package/v2Components/CommonTestAndPreview/tests/reducer.test.js +71 -0
  25. package/v2Components/CommonTestAndPreview/tests/selectors.test.js +17 -0
  26. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +1408 -1276
  27. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +321 -288
  28. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +5246 -4872
@@ -0,0 +1,72 @@
1
+ import CapModal from "@capillarytech/cap-ui-library/CapModal";
2
+ import { FormattedMessage, injectIntl } from "react-intl";
3
+ import messages from "./messages";
4
+ import React, { useState } from "react";
5
+ import { CapCard, CapRow, CapColumn, CapLabel } from "@capillarytech/cap-ui-library";
6
+ import { CHANNELS } from "./constants";
7
+ import CapButton from "@capillarytech/cap-ui-library/CapButton";
8
+ import CapIcon from "@capillarytech/cap-ui-library/CapIcon";
9
+
10
+
11
+ const ExistingCustomerModal = ({ customerModal, setCustomerModal, customerData, channel, onSave, intl }) => {
12
+ const [isLoading, setIsLoading] = useState(false);
13
+ return (
14
+ <CapModal
15
+ visible={customerModal[0]}
16
+ onCancel={() => setCustomerModal([false, ""])}
17
+ centered={true}
18
+ width={500}
19
+ maskStyle={{ backgroundColor: 'rgba(244, 245, 247, 0.9)' }}
20
+ footer={
21
+ <CapRow justify="start">
22
+ <CapButton
23
+ type="primary"
24
+ onClick={() => onSave({},setIsLoading)}
25
+ loading={isLoading}
26
+ >
27
+ <FormattedMessage {...messages.saveButton} />
28
+ </CapButton>
29
+ <CapButton
30
+ type="secondary"
31
+ onClick={() => setCustomerModal([false, ""])}
32
+ disabled={isLoading}
33
+ >
34
+ <FormattedMessage {...messages.cancelButton} />
35
+ </CapButton>
36
+ </CapRow>
37
+ }
38
+ title={intl.formatMessage(messages.customerCreationModalTitle)}
39
+ wrapClassName="common-test-preview-modal-wrap existing-customer-modal-wrap"
40
+ className="common-test-preview-modal"
41
+ >
42
+ <div className="existing-customer-modal">
43
+ <CapRow className="existing-customer-modal-intro-row">
44
+ <FormattedMessage {...messages.existingCustomerModalDescription} />
45
+ </CapRow>
46
+ <CapCard className="existing-customer-modal-card">
47
+ <CapRow className="existing-customer-modal-card-row">
48
+ <CapColumn className="existing-customer-modal-avatar">
49
+ <CapIcon type="user-profile" className="existing-customer-modal-avatar-icon" />
50
+ </CapColumn>
51
+ <CapColumn className="existing-customer-modal-details">
52
+ <CapRow className="existing-customer-modal-name">
53
+ {customerData.name || "-"}
54
+ </CapRow>
55
+ <CapColumn className="existing-customer-modal-meta">
56
+ {channel === CHANNELS.EMAIL && customerData.email && (
57
+ <CapRow><span className="existing-customer-modal-meta-label"><FormattedMessage {...messages.customerEmail} />: </span> {customerData.email}</CapRow>
58
+ )}
59
+ {channel === CHANNELS.SMS && customerData.mobile && (
60
+ <CapRow><span className="existing-customer-modal-meta-label"><FormattedMessage {...messages.customerMobile} />: </span>{customerData.mobile}</CapRow>
61
+ )}
62
+ <CapRow><span className="existing-customer-modal-meta-label"><FormattedMessage {...messages.customerID} />: </span>{customerData.customerId}</CapRow>
63
+ </CapColumn>
64
+ </CapColumn>
65
+ </CapRow>
66
+ </CapCard>
67
+ </div>
68
+ </CapModal>
69
+ );
70
+ };
71
+
72
+ export default injectIntl(ExistingCustomerModal);
@@ -24,6 +24,9 @@ const SendTestMessage = ({
24
24
  channel,
25
25
  isSendingTestMessage,
26
26
  formatMessage,
27
+ renderAddTestCustomerButton,
28
+ searchValue,
29
+ setSearchValue,
27
30
  deliverySettings,
28
31
  senderDetailsOptions,
29
32
  wecrmAccounts,
@@ -31,57 +34,77 @@ const SendTestMessage = ({
31
34
  isLoadingSenderDetails,
32
35
  smsTraiDltEnabled,
33
36
  registeredSenderIds,
34
- }) => (
35
- <CapStepsAccordian
36
- showNumberSteps={false}
37
- isChevronIcon
38
- expandIconPosition="right"
39
- items={[
40
- {
41
- header: <CapHeader
42
- size="regular"
43
- description={<FormattedMessage {...messages.testMessageDescription} />}
44
- title={<FormattedMessage {...messages.sendTestMessage} />}
45
- />,
46
- content: (
47
- <CapRow className="send-test-content">
48
- <CapHeader size="label1" title={<FormattedMessage {...messages.testCustomers} />} />
49
- <CapTreeSelect
50
- className="test-customers-tree-select"
51
- loading={isFetchingTestCustomers || isFetchingTestGroups}
52
- treeData={testEntitiesTreeData}
53
- onChange={handleTestEntitiesChange}
54
- value={selectedTestEntities}
55
- multiple
56
- placeholder={formatMessage(messages.testCustomersPlaceholder)}
37
+ }) => {
38
+ const addCustomerContent = renderAddTestCustomerButton ? renderAddTestCustomerButton() : null;
39
+ return (
40
+ <CapStepsAccordian
41
+ showNumberSteps={false}
42
+ isChevronIcon
43
+ expandIconPosition="right"
44
+ items={[
45
+ {
46
+ header: (
47
+ <CapHeader
48
+ size="regular"
49
+ description={<FormattedMessage {...messages.testMessageDescription} />}
50
+ title={<FormattedMessage {...messages.sendTestMessage} />}
57
51
  />
58
- {CHANNELS_WITH_DELIVERY_SETTINGS.includes(channel) && (
59
- <DeliverySettings
60
- channel={channel}
61
- deliverySettings={deliverySettings || {}}
62
- senderDetailsOptions={senderDetailsOptions || []}
63
- wecrmAccounts={wecrmAccounts || []}
64
- onSaveDeliverySettings={onSaveDeliverySettings}
65
- isLoadingSenderDetails={isLoadingSenderDetails}
66
- formatMessage={formatMessage}
67
- smsTraiDltEnabled={smsTraiDltEnabled}
68
- registeredSenderIds={registeredSenderIds}
69
- whatsappAccountFromForm={
70
- channel === CHANNELS.WHATSAPP && formData?.accountName
71
- ? { accountName: formData.accountName }
72
- : undefined
73
- }
52
+ ),
53
+ content: (
54
+ <CapRow className="send-test-content">
55
+ <CapHeader size="label1" title={<FormattedMessage {...messages.testCustomers} />} />
56
+ <CapTreeSelect
57
+ className="test-customers-tree-select"
58
+ dropdownMatchSelectWidth
59
+ dropdownClassName="test-customers-tree-select-dropdown"
60
+ listHeight={280}
61
+ loading={isFetchingTestCustomers || isFetchingTestGroups}
62
+ treeData={testEntitiesTreeData}
63
+ onChange={handleTestEntitiesChange}
64
+ value={selectedTestEntities}
65
+ showSearch
66
+ searchValue={searchValue}
67
+ onSearch={setSearchValue}
68
+ treeNodeFilterProp="title"
69
+ {...(addCustomerContent ? { notFoundContent: addCustomerContent } : {})}
70
+ multiple
71
+ placeholder={formatMessage(messages.testCustomersPlaceholder)}
72
+ dropdownStyle={{ zIndex: 900, maxHeight: 320 }}
73
+ placement="bottomLeft"
74
+ getPopupContainer={(node) => node.closest('.send-test-section') || document.body}
74
75
  />
75
- )}
76
- <CapButton onClick={handleSendTestMessage} disabled={isEmpty(selectedTestEntities) || isSendingTestMessage}>
77
- <FormattedMessage {...messages.sendTestButton} />
78
- </CapButton>
79
- </CapRow>),
80
- key: 1,
81
- },
82
- ]}
83
- />
84
- );
76
+ {CHANNELS_WITH_DELIVERY_SETTINGS.includes(channel) && (
77
+ <DeliverySettings
78
+ channel={channel}
79
+ deliverySettings={deliverySettings || {}}
80
+ senderDetailsOptions={senderDetailsOptions || []}
81
+ wecrmAccounts={wecrmAccounts || []}
82
+ onSaveDeliverySettings={onSaveDeliverySettings}
83
+ isLoadingSenderDetails={isLoadingSenderDetails}
84
+ formatMessage={formatMessage}
85
+ smsTraiDltEnabled={smsTraiDltEnabled}
86
+ registeredSenderIds={registeredSenderIds}
87
+ whatsappAccountFromForm={
88
+ channel === CHANNELS.WHATSAPP && formData?.accountName
89
+ ? { accountName: formData.accountName }
90
+ : undefined
91
+ }
92
+ />
93
+ )}
94
+ <CapButton
95
+ onClick={handleSendTestMessage}
96
+ disabled={isEmpty(selectedTestEntities) || isSendingTestMessage}
97
+ >
98
+ <FormattedMessage {...messages.sendTestButton} />
99
+ </CapButton>
100
+ </CapRow>
101
+ ),
102
+ key: 1,
103
+ },
104
+ ]}
105
+ />
106
+ );
107
+ };
85
108
 
86
109
  SendTestMessage.propTypes = {
87
110
  isFetchingTestCustomers: PropTypes.bool.isRequired,
@@ -94,6 +117,9 @@ SendTestMessage.propTypes = {
94
117
  channel: PropTypes.string,
95
118
  isSendingTestMessage: PropTypes.bool.isRequired,
96
119
  formatMessage: PropTypes.func.isRequired,
120
+ renderAddTestCustomerButton: PropTypes.func,
121
+ searchValue: PropTypes.string,
122
+ setSearchValue: PropTypes.func,
97
123
  deliverySettings: PropTypes.object,
98
124
  senderDetailsOptions: PropTypes.array,
99
125
  wecrmAccounts: PropTypes.array,
@@ -106,6 +132,9 @@ SendTestMessage.propTypes = {
106
132
  SendTestMessage.defaultProps = {
107
133
  formData: undefined,
108
134
  channel: undefined,
135
+ renderAddTestCustomerButton: undefined,
136
+ searchValue: '',
137
+ setSearchValue: undefined,
109
138
  deliverySettings: {},
110
139
  senderDetailsOptions: [],
111
140
  wecrmAccounts: [],
@@ -5,6 +5,151 @@
5
5
  */
6
6
  @import '~@capillarytech/cap-ui-library/styles/_variables.scss';
7
7
 
8
+ /* All ant overrides scoped under wrapper to avoid affecting other modals. */
9
+ .common-test-preview-modal-wrap {
10
+ z-index: 10000 !important;
11
+ }
12
+
13
+ /* Lookup spinner overlay above test-customers dropdown. */
14
+ .common-test-preview-lookup-spin {
15
+ position: relative;
16
+ z-index: 1000;
17
+
18
+ .ant-spin-container::after {
19
+ z-index: 1;
20
+ }
21
+ }
22
+
23
+ /* When customer lookup is loading, dropdown renders inside .send-test-section; lower it so spinner is on top. */
24
+ .common-test-preview-customer-loading .test-customers-tree-select-dropdown {
25
+ z-index: 0 !important;
26
+ }
27
+
28
+ /* Customer creation modal content – avoid inline styles */
29
+ .common-test-preview-modal {
30
+ color: $CAP_G01 !important;
31
+ .ant-modal-content {
32
+ width: 28.5rem; /* 456px at 16px root */
33
+ }
34
+ .ant-modal-footer {
35
+ text-align: left;
36
+ margin-left: 0.625rem; /* 10px at 16px root */
37
+ margin-top: 0.9375rem; /* 15px at 16px root */
38
+ }
39
+
40
+ .customer-creation-modal-row {
41
+ margin-bottom: $CAP_SPACE_16;
42
+
43
+ &--last {
44
+ margin-bottom: $CAP_SPACE_24;
45
+ }
46
+ }
47
+
48
+ .customer-creation-modal-description {
49
+ color: $CAP_G01;
50
+ font-size: $FONT_SIZE_M;
51
+ }
52
+
53
+ .customer-creation-modal-label {
54
+ margin-bottom: $CAP_SPACE_04;
55
+ display: block;
56
+ font-size: $FONT_SIZE_M;
57
+ font-weight: bold;
58
+ color: $CAP_G01;
59
+ }
60
+
61
+ .customer-creation-modal-optional {
62
+ color: $CAP_G06;
63
+ font-weight: normal;
64
+ }
65
+
66
+ .customer-creation-modal-input {
67
+ width: 100%;
68
+ color: $CAP_G01;
69
+
70
+ &.has-error {
71
+ color: $CAP_COLOR_03 !important;
72
+ .ant-input {
73
+ border-color: $CAP_COLOR_03 !important;
74
+ }
75
+ }
76
+ }
77
+
78
+ .customer-creation-modal-validation-error {
79
+ color: $CAP_COLOR_03;
80
+ font-size: $FONT_SIZE_S;
81
+ margin-top: $CAP_SPACE_04;
82
+ }
83
+
84
+ /* Existing customer modal content */
85
+ .existing-customer-modal {
86
+ &-intro-row {
87
+ margin-bottom: $CAP_SPACE_16;
88
+ }
89
+
90
+ .ant-card-body {
91
+ padding: 1rem;
92
+ }
93
+
94
+ &-card {
95
+ border-radius: $CAP_SPACE_08;
96
+ border: $CAP_SPACE_01 solid $CAP_G06;
97
+ padding: 0;
98
+ }
99
+
100
+ &-card-row {
101
+ display: flex;
102
+ align-items: flex-start;
103
+ padding: 0;
104
+ }
105
+
106
+ &-avatar {
107
+ width: 3rem; /* 48px at 16px root */
108
+ height: 3rem;
109
+ border-radius: 1.25rem; /* 20px at 16px root */
110
+ background-color: $CAP_G07;
111
+ display: flex;
112
+ align-items: center;
113
+ justify-content: center;
114
+ margin-top: $CAP_SPACE_04;
115
+ margin-right: $CAP_SPACE_16;
116
+ flex-shrink: 0;
117
+ }
118
+
119
+ &-avatar-icon {
120
+ font-size: $CAP_SPACE_24;
121
+ color: $CAP_G01;
122
+ }
123
+
124
+ &-details {
125
+ flex: 1;
126
+ }
127
+
128
+ &-name {
129
+ font-size: $FONT_SIZE_L;
130
+ font-weight: 500;
131
+ color: $CAP_G01;
132
+ margin-bottom: $CAP_SPACE_04;
133
+ }
134
+
135
+ &-meta {
136
+ font-size: $FONT_SIZE_M;
137
+ color: $CAP_G01;
138
+ line-height: 1.4;
139
+ }
140
+
141
+ &-meta-label {
142
+ color: $CAP_G04;
143
+ }
144
+ }
145
+ }
146
+
147
+
148
+ .add-test-customer-icon {
149
+ margin-top: 0.3rem;
150
+ margin-right: 0.5rem;
151
+ }
152
+
8
153
  .common-test-and-preview-slidebox {
9
154
  .test-preview-header {
10
155
  display: flex;
@@ -363,14 +508,25 @@
363
508
  .ant-collapse-content-box {
364
509
  padding-left: 0 !important;
365
510
  }
511
+ .send-test-content {
512
+ flex-direction: column;
513
+ align-items: stretch;
514
+ gap: 0;
515
+ }
366
516
  .test-customers-tree-select {
367
517
  width: 100%;
368
- height: $CAP_SPACE_40;
369
- margin: $CAP_SPACE_12 0;
370
- .ant-select-selection {
371
- height: inherit;
518
+ min-height: $CAP_SPACE_40;
519
+ margin: $CAP_SPACE_12 0 $CAP_SPACE_08;
520
+ .ant-select-selection,
521
+ .ant-select-selector {
522
+ min-height: $CAP_SPACE_40;
523
+ height: auto !important;
372
524
  }
373
525
  }
526
+ .send-test-content .ant-btn {
527
+ margin-top: $CAP_SPACE_04;
528
+ flex-shrink: 0;
529
+ }
374
530
  .ant-select-selection__choice {
375
531
  background-color: $CAP_G08;
376
532
  border-radius: $CAP_SPACE_04;
@@ -378,6 +534,35 @@
378
534
  }
379
535
  }
380
536
 
537
+ // Test customers TreeSelect dropdown: limit height and make scrollable (dropdown renders in portal)
538
+ .test-customers-tree-select-dropdown {
539
+ max-height: 20rem !important; /* 320px */
540
+ overflow-y: auto !important;
541
+ .ant-select-tree-list-holder-inner {
542
+ overflow: visible;
543
+ }
544
+ }
545
+
546
+ .test-customer-add-btn {
547
+ width: 100%;
548
+ background-color: transparent !important;
549
+ color: $FONT_COLOR_05 !important;
550
+ white-space: normal;
551
+ word-break: normal;
552
+ overflow-wrap: break-word;
553
+ height: auto;
554
+
555
+ /* Truncate long email/phone in label so it doesn't break mid-string */
556
+ .test-customer-add-btn-value {
557
+ display: inline-block;
558
+ max-width: 12rem;
559
+ overflow: hidden;
560
+ text-overflow: ellipsis;
561
+ white-space: nowrap;
562
+ vertical-align: bottom;
563
+ }
564
+ }
565
+
381
566
  // Responsive design for smaller screens
382
567
  @media (max-width: 54.857rem) {
383
568
  .common-test-and-preview-slidebox {
@@ -20,6 +20,7 @@ import {
20
20
  CLEAR_PREVIEW_ERRORS,
21
21
  GET_SENDER_DETAILS_REQUESTED,
22
22
  GET_WECRM_ACCOUNTS_REQUESTED,
23
+ ADD_TEST_CUSTOMER,
23
24
  } from './constants';
24
25
 
25
26
  // ============================================
@@ -92,6 +93,15 @@ export const getTestGroupsRequested = () => ({
92
93
  type: GET_TEST_GROUPS_REQUESTED,
93
94
  });
94
95
 
96
+ /**
97
+ * Add a test customer to the list (e.g. after adding existing customer so tree shows name)
98
+ * @param {Object} customer - { userId, customerId, name, email, mobile }
99
+ */
100
+ export const addTestCustomer = (customer) => ({
101
+ type: ADD_TEST_CUSTOMER,
102
+ payload: { customer },
103
+ });
104
+
95
105
  /**
96
106
  * Create or update message meta configuration
97
107
  * @param {Object} payload - Message meta payload
@@ -33,6 +33,7 @@ export const SEND_TEST_MESSAGE_FAILURE = 'app/CommonTestAndPreview/SEND_TEST_MES
33
33
  export const GET_TEST_CUSTOMERS_REQUESTED = 'app/CommonTestAndPreview/GET_TEST_CUSTOMERS_REQUESTED';
34
34
  export const GET_TEST_CUSTOMERS_SUCCESS = 'app/CommonTestAndPreview/GET_TEST_CUSTOMERS_SUCCESS';
35
35
  export const GET_TEST_CUSTOMERS_FAILURE = 'app/CommonTestAndPreview/GET_TEST_CUSTOMERS_FAILURE';
36
+ export const ADD_TEST_CUSTOMER = 'app/CommonTestAndPreview/ADD_TEST_CUSTOMER';
36
37
 
37
38
  // Test Groups
38
39
  export const GET_TEST_GROUPS_REQUESTED = 'app/CommonTestAndPreview/GET_TEST_GROUPS_REQUESTED';
@@ -65,6 +66,12 @@ export const GET_WECRM_ACCOUNTS_REQUESTED = 'app/CommonTestAndPreview/GET_WECRM_
65
66
  export const GET_WECRM_ACCOUNTS_SUCCESS = 'app/CommonTestAndPreview/GET_WECRM_ACCOUNTS_SUCCESS';
66
67
  export const GET_WECRM_ACCOUNTS_FAILURE = 'app/CommonTestAndPreview/GET_WECRM_ACCOUNTS_FAILURE';
67
68
 
69
+ // ============================================
70
+ // CUSTOMER MODAL TYPE (test customer addition)
71
+ // ============================================
72
+ export const CUSTOMER_MODAL_NEW = 'new';
73
+ export const CUSTOMER_MODAL_EXISTING = 'existing';
74
+
68
75
  // ============================================
69
76
  // CHANNEL CONSTANTS
70
77
  // ============================================
@@ -100,11 +107,21 @@ export const CLIENT_NAME_CREATIVES = 'CREATIVES';
100
107
  // Note: API uses "PUSH" instead of "MOBILEPUSH" for MobilePush channel
101
108
  export const API_CHANNEL_PUSH = 'PUSH';
102
109
 
110
+ // ============================================
111
+ // VALIDATION REGEX (shared with commonUtils for email/mobile validation)
112
+ // ============================================
113
+ export const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
114
+ export const PHONE_REGEX = /^[1-9]\d{7,14}$/;
115
+
103
116
  // ============================================
104
117
  // IDENTIFIER TYPE CONSTANTS
105
118
  // ============================================
106
- export const IDENTIFIER_TYPE_MOBILE = 'mobile';
107
119
  export const IDENTIFIER_TYPE_EMAIL = 'email';
120
+ export const IDENTIFIER_TYPE_MOBILE = 'mobile';
121
+ export const IDENTIFIER_TYPE_PHONE = 'phone';
122
+
123
+ // Customer creation modal – input error state class suffix (used with customer-creation-modal-input)
124
+ export const INPUT_HAS_ERROR_CLASS = ' has-error';
108
125
 
109
126
  // ============================================
110
127
  // CHANNEL NAME CONSTANTS (for CDN and other utilities)