@capillarytech/creatives-library 8.0.125 → 8.0.127-alpha.0

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 (110) hide show
  1. package/config/app.js +6 -0
  2. package/containers/App/constants.js +1 -0
  3. package/index.html +3 -1
  4. package/initialReducer.js +2 -0
  5. package/package.json +1 -1
  6. package/services/api.js +94 -1
  7. package/services/tests/api.test.js +191 -0
  8. package/tests/integration/TemplateCreation/TemplateCreation.integration.test.js +8 -3
  9. package/tests/integration/TemplateCreation/api-response.js +5 -0
  10. package/tests/integration/TemplateCreation/msw-handler.js +42 -63
  11. package/utils/common.js +7 -0
  12. package/utils/commonUtils.js +2 -6
  13. package/utils/createPayload.js +272 -0
  14. package/utils/tests/createPayload.test.js +761 -0
  15. package/v2Components/CapImageUpload/index.js +59 -46
  16. package/v2Components/CapInAppCTA/index.js +1 -0
  17. package/v2Components/CapMpushCTA/constants.js +25 -0
  18. package/v2Components/CapMpushCTA/index.js +332 -0
  19. package/v2Components/CapMpushCTA/index.scss +95 -0
  20. package/v2Components/CapMpushCTA/messages.js +89 -0
  21. package/v2Components/CapTagList/index.js +177 -120
  22. package/v2Components/CapVideoUpload/constants.js +3 -0
  23. package/v2Components/CapVideoUpload/index.js +167 -110
  24. package/v2Components/CapVideoUpload/messages.js +16 -0
  25. package/v2Components/Carousel/index.js +15 -13
  26. package/v2Components/CustomerSearchSection/_customerSearch.scss +309 -0
  27. package/v2Components/CustomerSearchSection/constants.js +5 -0
  28. package/v2Components/CustomerSearchSection/index.js +367 -0
  29. package/v2Components/CustomerSearchSection/messages.js +20 -0
  30. package/v2Components/CustomerSearchSection/tests/utils.test.js +334 -0
  31. package/v2Components/CustomerSearchSection/utils.js +49 -0
  32. package/v2Components/ErrorInfoNote/style.scss +1 -0
  33. package/v2Components/MobilePushPreviewV2/index.js +37 -5
  34. package/v2Components/TemplatePreview/_templatePreview.scss +114 -72
  35. package/v2Components/TemplatePreview/assets/images/Android _ With date and time.svg +29 -0
  36. package/v2Components/TemplatePreview/assets/images/android.svg +9 -0
  37. package/v2Components/TemplatePreview/assets/images/iOS _ With date and time.svg +26 -0
  38. package/v2Components/TemplatePreview/assets/images/ios.svg +9 -0
  39. package/v2Components/TemplatePreview/index.js +178 -50
  40. package/v2Components/TemplatePreview/messages.js +4 -0
  41. package/v2Components/TestAndPreviewSlidebox/CustomValuesEditor.js +169 -0
  42. package/v2Components/TestAndPreviewSlidebox/LeftPanelContent.js +95 -0
  43. package/v2Components/TestAndPreviewSlidebox/PreviewSection.js +69 -0
  44. package/v2Components/TestAndPreviewSlidebox/SendTestMessage.js +68 -0
  45. package/v2Components/TestAndPreviewSlidebox/_testAndPreviewSlidebox.scss +543 -0
  46. package/v2Components/TestAndPreviewSlidebox/actions.js +67 -0
  47. package/v2Components/TestAndPreviewSlidebox/constants.js +67 -0
  48. package/v2Components/TestAndPreviewSlidebox/index.js +592 -0
  49. package/v2Components/TestAndPreviewSlidebox/messages.js +147 -0
  50. package/v2Components/TestAndPreviewSlidebox/reducer.js +233 -0
  51. package/v2Components/TestAndPreviewSlidebox/sagas.js +258 -0
  52. package/v2Components/TestAndPreviewSlidebox/selectors.js +142 -0
  53. package/v2Components/TestAndPreviewSlidebox/tests/CustomValuesEditor.test.js +425 -0
  54. package/v2Components/TestAndPreviewSlidebox/tests/LeftPanelContent.test.js +400 -0
  55. package/v2Components/TestAndPreviewSlidebox/tests/SendTestMessage.test.js +448 -0
  56. package/v2Components/TestAndPreviewSlidebox/tests/actions.test.js +80 -0
  57. package/v2Components/TestAndPreviewSlidebox/tests/reducer.test.js +367 -0
  58. package/v2Components/TestAndPreviewSlidebox/tests/saga.rtl.test.js +192 -0
  59. package/v2Components/TestAndPreviewSlidebox/tests/saga.test.js +652 -0
  60. package/v2Components/TestAndPreviewSlidebox/tests/selector.test.js +182 -0
  61. package/v2Containers/CreativesContainer/SlideBoxContent.js +22 -10
  62. package/v2Containers/CreativesContainer/SlideBoxFooter.js +23 -2
  63. package/v2Containers/CreativesContainer/index.js +216 -136
  64. package/v2Containers/CreativesContainer/messages.js +4 -0
  65. package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +21 -0
  66. package/v2Containers/Email/index.js +27 -2
  67. package/v2Containers/EmailWrapper/hooks/useEmailWrapper.js +10 -0
  68. package/v2Containers/EmailWrapper/index.js +6 -0
  69. package/v2Containers/InApp/constants.js +1 -0
  70. package/v2Containers/InApp/index.js +13 -13
  71. package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/content.test.js.snap +3 -0
  72. package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/index.test.js.snap +2 -0
  73. package/v2Containers/Line/Container/Wrapper/tests/__snapshots__/index.test.js.snap +2 -0
  74. package/v2Containers/Line/Container/tests/__snapshots__/index.test.js.snap +9 -0
  75. package/v2Containers/MobilePush/Create/index.js +1 -0
  76. package/v2Containers/MobilePush/commonMethods.js +7 -14
  77. package/v2Containers/MobilePushNew/actions.js +116 -0
  78. package/v2Containers/MobilePushNew/components/CtaButtons.js +170 -0
  79. package/v2Containers/MobilePushNew/components/MediaUploaders.js +754 -0
  80. package/v2Containers/MobilePushNew/components/PlatformContentFields.js +279 -0
  81. package/v2Containers/MobilePushNew/components/index.js +5 -0
  82. package/v2Containers/MobilePushNew/components/tests/CtaButtons.test.js +779 -0
  83. package/v2Containers/MobilePushNew/components/tests/MediaUploaders.test.js +2114 -0
  84. package/v2Containers/MobilePushNew/components/tests/PlatformContentFields.test.js +343 -0
  85. package/v2Containers/MobilePushNew/constants.js +115 -0
  86. package/v2Containers/MobilePushNew/hooks/tests/usePlatformSync.test.js +1299 -0
  87. package/v2Containers/MobilePushNew/hooks/tests/useUpload.test.js +1223 -0
  88. package/v2Containers/MobilePushNew/hooks/usePlatformSync.js +246 -0
  89. package/v2Containers/MobilePushNew/hooks/useUpload.js +726 -0
  90. package/v2Containers/MobilePushNew/index.js +2280 -0
  91. package/v2Containers/MobilePushNew/index.scss +308 -0
  92. package/v2Containers/MobilePushNew/messages.js +226 -0
  93. package/v2Containers/MobilePushNew/reducer.js +160 -0
  94. package/v2Containers/MobilePushNew/sagas.js +198 -0
  95. package/v2Containers/MobilePushNew/selectors.js +55 -0
  96. package/v2Containers/MobilePushNew/tests/reducer.test.js +741 -0
  97. package/v2Containers/MobilePushNew/tests/sagas.test.js +863 -0
  98. package/v2Containers/MobilePushNew/tests/selectors.test.js +425 -0
  99. package/v2Containers/MobilePushNew/tests/utils.test.js +322 -0
  100. package/v2Containers/MobilePushNew/utils.js +33 -0
  101. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +23 -5
  102. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +4 -0
  103. package/v2Containers/TagList/index.js +56 -10
  104. package/v2Containers/Templates/_templates.scss +101 -1
  105. package/v2Containers/Templates/index.js +147 -35
  106. package/v2Containers/Templates/messages.js +8 -0
  107. package/v2Containers/Templates/sagas.js +2 -0
  108. package/v2Containers/Whatsapp/constants.js +1 -0
  109. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +35 -0
  110. package/v2Containers/Email/tests/index.test.js +0 -35
@@ -0,0 +1,309 @@
1
+ @import '~@capillarytech/cap-ui-library/styles/_variables.scss';
2
+
3
+ .customer-search-section {
4
+ margin-top: $CAP_SPACE_08;
5
+ display: flex;
6
+ flex-direction: column;
7
+ gap: $CAP_SPACE_04;
8
+ padding: 0;
9
+ position: relative;
10
+
11
+ .customer-search-label {
12
+ font-weight: bold;
13
+ margin-bottom: $CAP_SPACE_04;
14
+ font-size: $CAP_SPACE_16;
15
+ }
16
+
17
+ .search-container {
18
+ position: relative;
19
+ }
20
+
21
+ .search-dropdown-container {
22
+ position: absolute;
23
+ width: 100%;
24
+ z-index: 1000;
25
+ }
26
+
27
+ .search-result-card {
28
+ width: 100%;
29
+ margin: 0;
30
+ box-shadow: 0 $CAP_SPACE_04 $CAP_SPACE_12 rgba(0, 0, 0, 0.15);
31
+ border-radius: $CAP_SPACE_08;
32
+ border: 1px solid #d9d9d9;
33
+
34
+ .ant-card-body {
35
+ padding: $CAP_SPACE_08;
36
+ }
37
+ }
38
+
39
+ .link-result {
40
+ .ant-row.ant-row-flex {
41
+ padding: $CAP_SPACE_08 $CAP_SPACE_12;
42
+ border-radius: $CAP_SPACE_06;
43
+
44
+ &:hover {
45
+ background-color: #f0f0f0;
46
+ }
47
+ }
48
+ }
49
+
50
+ .customer-common-profile {
51
+ width: $CAP_SPACE_40;
52
+ height: $CAP_SPACE_40;
53
+ border-radius: 50%;
54
+ background-color: #E7D0FB;
55
+ color: $CAP_PURPLE;
56
+ display: flex;
57
+ align-items: center;
58
+ justify-content: center;
59
+ font-weight: bold;
60
+ font-size: $CAP_SPACE_16;
61
+ }
62
+
63
+ .search-text {
64
+ &.ant-typography {
65
+ font-weight: bold;
66
+ color: #262626;
67
+ margin-bottom: 0;
68
+ }
69
+ }
70
+
71
+
72
+ .identifiers-text {
73
+ margin-top: -0.6rem;
74
+ color: #8c8c8c;
75
+ .highlight-background-color {
76
+ background-color: transparent;
77
+ font-weight: bold;
78
+ }
79
+ }
80
+ .identifier-desc {
81
+ margin-top: 0.6rem;
82
+ }
83
+
84
+ .link-result-divider {
85
+ margin: 0 $CAP_SPACE_12;
86
+ }
87
+
88
+ .selected-customer-view {
89
+ display: flex;
90
+ align-items: center;
91
+ justify-content: space-between;
92
+ padding: $CAP_SPACE_04 $CAP_SPACE_12;
93
+ border: 1px solid #d9d9d9;
94
+ border-radius: $CAP_SPACE_06;
95
+ background-color: $CAP_WHITE;
96
+ height: $CAP_SPACE_40;
97
+ cursor: default;
98
+
99
+ .customer-common-profile {
100
+ width: $CAP_SPACE_24;
101
+ height: $CAP_SPACE_24;
102
+ font-size: $CAP_SPACE_12;
103
+ }
104
+
105
+ .search-text, .identifiers-text {
106
+ margin-bottom: 0;
107
+ }
108
+ .multiple-identifiers {
109
+ margin: 0.25rem 0;
110
+ }
111
+
112
+ .clear-icon {
113
+ cursor: pointer;
114
+ color: #8c8c8c;
115
+ }
116
+ }
117
+
118
+ .search-input-container {
119
+ position: relative;
120
+ .search-error {
121
+ margin-top: $CAP_SPACE_08;
122
+ }
123
+ }
124
+
125
+ .validation-card {
126
+ min-width: 100%;
127
+ max-width: 100%;
128
+ margin: 0;
129
+ box-shadow: 0 $CAP_SPACE_04 $CAP_SPACE_08 -2px rgba(9, 30, 66, 0.25),
130
+ 0 0 1px 0 rgba(9, 30, 66, 0.31);
131
+ border-top-left-radius: unset;
132
+ border-top-right-radius: unset;
133
+ border-top-color: transparent;
134
+ .ant-card-body {
135
+ padding: $CAP_SPACE_12 $CAP_SPACE_20;
136
+ }
137
+ }
138
+
139
+ .search-dropdown {
140
+ position: absolute;
141
+ top: 100%;
142
+ left: 0;
143
+ right: 0;
144
+ background: #fff;
145
+ border: 1px solid #d9d9d9;
146
+ border-top: none;
147
+ border-radius: 0 0 $CAP_SPACE_06 $CAP_SPACE_06;
148
+ box-shadow: 0 $CAP_SPACE_02 $CAP_SPACE_08 rgba(0, 0, 0, 0.1);
149
+ max-height: 200px;
150
+ overflow-y: auto;
151
+ z-index: 1050;
152
+
153
+ &.no-results {
154
+ .no-results-message {
155
+ display: flex;
156
+ align-items: center;
157
+ gap: $CAP_SPACE_08;
158
+ padding: $CAP_SPACE_16;
159
+ color: #8c8c8c;
160
+ font-size: 1rem;
161
+
162
+ .cap-icon {
163
+ color: #d9d9d9;
164
+ }
165
+ }
166
+ }
167
+ }
168
+
169
+ .customer-result-item {
170
+ display: flex;
171
+ align-items: center;
172
+ gap: $CAP_SPACE_12;
173
+ padding: $CAP_SPACE_12 $CAP_SPACE_16;
174
+ cursor: pointer;
175
+ border-bottom: 1px solid #f0f0f0;
176
+ transition: background-color 0.2s ease;
177
+
178
+ &:hover {
179
+ background-color: #f5f5f5;
180
+ }
181
+
182
+ &:last-child {
183
+ border-bottom: none;
184
+ }
185
+
186
+ .customer-avatar {
187
+ flex-shrink: 0;
188
+ }
189
+
190
+ .customer-details {
191
+ flex: 1;
192
+ min-width: 0;
193
+
194
+ .customer-name {
195
+ font-weight: 500;
196
+ font-size: 1rem;
197
+ color: #262626;
198
+ margin-bottom: 2px;
199
+ }
200
+
201
+ .customer-email {
202
+ font-size: $CAP_SPACE_12;
203
+ color: #8c8c8c;
204
+ margin-bottom: 2px;
205
+ }
206
+
207
+ .customer-phone {
208
+ font-size: $CAP_SPACE_12;
209
+ color: #8c8c8c;
210
+ }
211
+ }
212
+
213
+ .customer-id {
214
+ flex-shrink: 0;
215
+ font-size: $CAP_SPACE_12;
216
+ color: #8c8c8c;
217
+ font-weight: 500;
218
+ }
219
+ }
220
+
221
+ .selected-customer {
222
+ display: flex;
223
+ flex-direction: column;
224
+ gap: $CAP_SPACE_16;
225
+ padding: $CAP_SPACE_16;
226
+ background: #f8f9fa;
227
+ border: 1px solid #e8e8e8;
228
+ border-radius: $CAP_SPACE_06;
229
+
230
+ .selected-customer-info {
231
+ display: flex;
232
+ align-items: center;
233
+ gap: $CAP_SPACE_12;
234
+
235
+ .customer-details {
236
+ flex: 1;
237
+
238
+ .customer-name {
239
+ font-weight: 500;
240
+ font-size: 1rem;
241
+ color: #262626;
242
+ margin-bottom: $CAP_SPACE_04;
243
+ }
244
+
245
+ .customer-email {
246
+ font-size: $CAP_SPACE_12;
247
+ color: #8c8c8c;
248
+ margin-bottom: $CAP_SPACE_04;
249
+ }
250
+
251
+ .customer-id {
252
+ font-size: $CAP_SPACE_12;
253
+ color: #8c8c8c;
254
+ font-weight: 500;
255
+ }
256
+ }
257
+ }
258
+
259
+ .selected-customer-actions {
260
+ display: flex;
261
+ justify-content: flex-end;
262
+ align-items: center;
263
+ }
264
+
265
+ .tag-extraction-success {
266
+ .extraction-summary {
267
+ display: flex;
268
+ align-items: center;
269
+ gap: $CAP_SPACE_08;
270
+ margin-bottom: $CAP_SPACE_12;
271
+ font-size: 1rem;
272
+ font-weight: 500;
273
+ color: #52c41a;
274
+ }
275
+
276
+ .required-tags,
277
+ .optional-tags {
278
+ margin-bottom: $CAP_SPACE_12;
279
+
280
+ .tag-category {
281
+ display: flex;
282
+ align-items: center;
283
+ gap: $CAP_SPACE_08;
284
+ margin-bottom: $CAP_SPACE_08;
285
+ font-size: 13px;
286
+ font-weight: 500;
287
+ }
288
+
289
+ .tag-list {
290
+ display: flex;
291
+ flex-wrap: wrap;
292
+ gap: $CAP_SPACE_06;
293
+ }
294
+ }
295
+
296
+ .required-tags .tag-category {
297
+ color: #fa8c16;
298
+ }
299
+
300
+ .optional-tags .tag-category {
301
+ color: #52c41a;
302
+ }
303
+
304
+ .api-warnings {
305
+ margin-top: $CAP_SPACE_08;
306
+ }
307
+ }
308
+ }
309
+ }
@@ -0,0 +1,5 @@
1
+ export const FIRSTNAME = 'firstname';
2
+ export const LASTNAME = 'lastname';
3
+ export const INSTORE = 'instore';
4
+ export const NA = 'N/A';
5
+ export const MINIMUM_IDENTIFIER_LENGTH = 1;
@@ -0,0 +1,367 @@
1
+ /**
2
+ *
3
+ * CustomerSearchSection
4
+ *
5
+ */
6
+ import React, {
7
+ useState, useCallback, useRef, useEffect,
8
+ } from 'react';
9
+ import PropTypes from 'prop-types';
10
+ import { injectIntl } from 'react-intl';
11
+ import CapInput from '@capillarytech/cap-ui-library/CapInput';
12
+ import CapSpin from '@capillarytech/cap-ui-library/CapSpin';
13
+ import CapAlert from '@capillarytech/cap-ui-library/CapAlert';
14
+ import CapLabel from '@capillarytech/cap-ui-library/CapLabel';
15
+ import CapRow from '@capillarytech/cap-ui-library/CapRow';
16
+ import CapCard from '@capillarytech/cap-ui-library/CapCard';
17
+ import CapHeading from '@capillarytech/cap-ui-library/CapHeading';
18
+ import CapLink from '@capillarytech/cap-ui-library/CapLink';
19
+ import CapColumn from '@capillarytech/cap-ui-library/CapColumn';
20
+ import CapIcon from '@capillarytech/cap-ui-library/CapIcon';
21
+ import { debounce } from 'lodash';
22
+ import './_customerSearch.scss';
23
+ import {
24
+ FIRSTNAME,
25
+ LASTNAME,
26
+ INSTORE,
27
+ NA,
28
+ MINIMUM_IDENTIFIER_LENGTH,
29
+ } from './constants';
30
+ import {
31
+ checkForData,
32
+ removeExtraIdentifiers,
33
+ identifiersHighlight,
34
+ getNamingIcon,
35
+ } from './utils';
36
+ import messages from './messages';
37
+
38
+
39
+ const CustomerSearchSection = ({
40
+ intl: { formatMessage },
41
+ onCustomerSelect,
42
+ onSearch,
43
+ onClearSearch,
44
+ onClearSelection,
45
+ customers,
46
+ isSearchingCustomer,
47
+ searchError,
48
+ selectedCustomer,
49
+ hasSearched,
50
+ disabled,
51
+ }) => {
52
+ const [showDropdown, setShowDropdown] = useState(false);
53
+ const [customerSearchValue, setCustomerSearchValue] = useState('');
54
+ const [processedSearchData, setProcessedSearchData] = useState([]);
55
+
56
+ const searchContainerRef = useRef(null);
57
+
58
+ const debouncedSearch = useCallback(
59
+ debounce((query) => {
60
+ onSearch(query);
61
+ }, 300),
62
+ [onSearch],
63
+ );
64
+
65
+ const handleSearchChange = useCallback(
66
+ (e) => {
67
+ const { value } = e.target;
68
+ setCustomerSearchValue(value);
69
+ if (value?.trim()) {
70
+ debouncedSearch(value.trim());
71
+ } else {
72
+ onClearSearch();
73
+ setShowDropdown(false);
74
+ }
75
+ },
76
+ [debouncedSearch, onClearSearch],
77
+ );
78
+
79
+ const handleCustomerSelectItem = useCallback(
80
+ (customer) => {
81
+ setShowDropdown(false);
82
+ onCustomerSelect(customer);
83
+ },
84
+ [onCustomerSelect],
85
+ );
86
+
87
+ const handleClickOutside = useCallback((event) => {
88
+ if (
89
+ searchContainerRef.current
90
+ && !searchContainerRef.current.contains(event.target)
91
+ ) {
92
+ setShowDropdown(false);
93
+ }
94
+ }, []);
95
+
96
+ useEffect(() => {
97
+ document.addEventListener('mousedown', handleClickOutside);
98
+ return () => {
99
+ document.removeEventListener('mousedown', handleClickOutside);
100
+ };
101
+ }, [handleClickOutside]);
102
+
103
+ useEffect(
104
+ () => () => {
105
+ debouncedSearch.cancel();
106
+ },
107
+ [debouncedSearch],
108
+ );
109
+
110
+ useEffect(() => {
111
+ if (customerSearchValue.trim() === '') {
112
+ setShowDropdown(false);
113
+ } else if (isSearchingCustomer || hasSearched) {
114
+ setShowDropdown(true);
115
+ }
116
+ }, [customerSearchValue, isSearchingCustomer, hasSearched]);
117
+
118
+ // Compute search data when dependencies change
119
+ useEffect(() => {
120
+ const computedData = searchData();
121
+ setProcessedSearchData(computedData);
122
+ }, [isSearchingCustomer, customers, customerSearchValue]);
123
+
124
+ const getCompleteName = (data) => {
125
+ const { firstName = '', lastName = '' } = data?.[0] || {};
126
+ return `${firstName} ${lastName}`.trim() || NA;
127
+ };
128
+
129
+ const searchData = () => {
130
+ let finalSearchData;
131
+ if (!isSearchingCustomer && customers?.length > 0) {
132
+ finalSearchData = customers?.map((dataToFetch) => {
133
+ const checkForInstoreData = checkForData(dataToFetch?.profiles, INSTORE);
134
+ const identifiers = removeExtraIdentifiers(
135
+ dataToFetch?.matchedIdentifiers,
136
+ FIRSTNAME,
137
+ LASTNAME,
138
+ );
139
+ const fullName = getCompleteName( checkForInstoreData?.length ? checkForInstoreData : dataToFetch?.profiles);
140
+ return {
141
+ customerId: dataToFetch?.userId,
142
+ name: fullName,
143
+ identifiers: identifiersHighlight(customerSearchValue, identifiers),
144
+ };
145
+ });
146
+ }
147
+ return finalSearchData || [];
148
+ };
149
+
150
+ const showIdentifiers = (data) => {
151
+ if (!Array.isArray(data) || data.length === 0) return null;
152
+
153
+ const renderLabel = (item, index, isLast) => (
154
+ <CapLabel
155
+ type="label1"
156
+ key={`${item?.firstString || ''}-${item?.secondString || ''}-${item?.thirdString || ''}-${index}`}
157
+ className={`identifiers-text ${data.length > MINIMUM_IDENTIFIER_LENGTH ? 'multiple-identifiers' : ''}`}
158
+ >
159
+ {item?.firstString}
160
+ <span className="highlight-background-color">{item?.secondString}</span>
161
+ {item?.thirdString}
162
+ {!isLast && (
163
+ <>
164
+ ,
165
+ <span className="comma-separated-align"> </span>
166
+ </>
167
+ )}
168
+ </CapLabel>
169
+ );
170
+
171
+ if (data.length > MINIMUM_IDENTIFIER_LENGTH) {
172
+ return data.map((item, index) => renderLabel(item, index, index === data.length - 1));
173
+ }
174
+
175
+ return renderLabel(data[0], 0, true);
176
+ };
177
+
178
+ const onClear = () => {
179
+ setCustomerSearchValue('');
180
+ onClearSelection();
181
+ };
182
+
183
+ return (
184
+ <CapRow className="customer-search-section">
185
+ <CapRow className="search-container" ref={searchContainerRef}>
186
+ {selectedCustomer ? (
187
+ <SelectedCustomerView
188
+ customer={selectedCustomer}
189
+ onClear={onClear}
190
+ showIdentifiers={showIdentifiers}
191
+ />
192
+ ) : (
193
+ <SearchInput
194
+ value={customerSearchValue}
195
+ onChange={handleSearchChange}
196
+ isLoading={isSearchingCustomer}
197
+ error={searchError}
198
+ disabled={disabled}
199
+ placeholder={formatMessage(messages.searchPlaceholder)}
200
+ />
201
+ )}
202
+ {showDropdown && !selectedCustomer && (
203
+ <CapRow className="search-dropdown-container">
204
+ <CapRow type="flex" justify="center" align="middle">
205
+ {(processedSearchData?.length === 0 && customerSearchValue != null && !isSearchingCustomer) && (
206
+ <CapCard className="validation-card">
207
+ <CapHeading type="h6">
208
+ {formatMessage(messages.noCustomersFound)}
209
+ </CapHeading>
210
+ </CapCard>
211
+ )}
212
+ {(isSearchingCustomer) && (
213
+ <CapCard className="validation-card">
214
+ <CapRow className="spin-card-align">
215
+ <CapSpin />
216
+ </CapRow>
217
+ </CapCard>
218
+ )}
219
+ </CapRow>
220
+ <CapRow type="flex" justify="center" align="middle">
221
+ {processedSearchData?.length > 0 && (
222
+ <CapCard className="search-result-card scroll-bar">
223
+ {
224
+ <CapRow className="identifier-row">
225
+ {processedSearchData?.map((d) => (
226
+ <>
227
+ <CapLink
228
+ key={d?.customerId}
229
+ className="search-results-height link-result"
230
+ title={(
231
+ <>
232
+ <CapRow type="flex" align="middle" gutter={16}>
233
+ <CapColumn data-testid="namingIcon">
234
+ <CapRow className="customer-common-profile">{getNamingIcon(d?.name)}</CapRow>
235
+ </CapColumn>
236
+ <CapColumn>
237
+ <CapRow type="flex" gutter={12}>
238
+ <CapColumn data-testid="searchResultName">
239
+ <CapHeading className="search-text" type="h4">
240
+ {d?.name}
241
+ </CapHeading>
242
+ </CapColumn>
243
+ </CapRow>
244
+ {(
245
+ <CapRow type="flex">
246
+ {showIdentifiers(d?.identifiers)}
247
+ </CapRow>
248
+ )}
249
+ </CapColumn>
250
+ </CapRow>
251
+ </>
252
+ )}
253
+ onClick={() => handleCustomerSelectItem(d)}
254
+ />
255
+ </>
256
+ ))}
257
+ </CapRow>
258
+ }
259
+ </CapCard>
260
+ )}
261
+ </CapRow>
262
+ </CapRow>
263
+ )}
264
+ </CapRow>
265
+ </CapRow>
266
+ );
267
+ };
268
+
269
+ // Search Input Component
270
+ const SearchInput = ({
271
+ value,
272
+ onChange,
273
+ isLoading,
274
+ error,
275
+ disabled,
276
+ placeholder,
277
+ }) => (
278
+ <CapRow className="search-input-container">
279
+ <CapInput
280
+ value={value}
281
+ onChange={onChange}
282
+ placeholder={placeholder}
283
+ disabled={disabled || isLoading}
284
+ status={error ? 'error' : undefined}
285
+ allowClear
286
+ autoComplete="off"
287
+ />
288
+ {error && (
289
+ <CapRow className="search-error">
290
+ <CapAlert
291
+ message={error}
292
+ type="error"
293
+ size="small"
294
+ showIcon
295
+ />
296
+ </CapRow>
297
+ )}
298
+ </CapRow>
299
+ );
300
+
301
+ const SelectedCustomerView = ({ customer, onClear, showIdentifiers }) => (
302
+ <div className="selected-customer-view">
303
+ <CapRow type="flex" align="middle" gutter={8}>
304
+ <CapColumn>
305
+ <CapRow className="customer-common-profile">{getNamingIcon(customer?.name)}</CapRow>
306
+ </CapColumn>
307
+ <CapColumn>
308
+ <CapHeading className="search-text" type="h4">
309
+ {customer.name}
310
+ </CapHeading>
311
+ </CapColumn>
312
+ <CapColumn>
313
+ <CapLabel type="label1" className="identifier-desc">
314
+ {showIdentifiers(customer?.identifiers)}
315
+ </CapLabel>
316
+ </CapColumn>
317
+ </CapRow>
318
+ <CapIcon size="s" type="close" className="clear-icon" onClick={onClear} />
319
+ </div>
320
+ );
321
+
322
+ SelectedCustomerView.propTypes = {
323
+ customer: PropTypes.object.isRequired,
324
+ onClear: PropTypes.func.isRequired,
325
+ showIdentifiers: PropTypes.func.isRequired,
326
+ };
327
+
328
+ SearchInput.propTypes = {
329
+ value: PropTypes.string,
330
+ onChange: PropTypes.func,
331
+ isLoading: PropTypes.bool,
332
+ error: PropTypes.string,
333
+ disabled: PropTypes.bool,
334
+ placeholder: PropTypes.string.isRequired,
335
+ };
336
+ SearchInput.defaultProps = {
337
+ value: '',
338
+ onChange: () => {},
339
+ isLoading: false,
340
+ error: null,
341
+ disabled: false,
342
+ };
343
+
344
+ CustomerSearchSection.propTypes = {
345
+ onCustomerSelect: PropTypes.func.isRequired,
346
+ onSearch: PropTypes.func.isRequired,
347
+ onClearSearch: PropTypes.func.isRequired,
348
+ onClearSelection: PropTypes.func.isRequired,
349
+ customers: PropTypes.array,
350
+ isSearchingCustomer: PropTypes.bool,
351
+ searchError: PropTypes.string,
352
+ selectedCustomer: PropTypes.object,
353
+ hasSearched: PropTypes.bool,
354
+ disabled: PropTypes.bool,
355
+ intl: PropTypes.object.isRequired,
356
+ };
357
+
358
+ CustomerSearchSection.defaultProps = {
359
+ customers: [],
360
+ isSearchingCustomer: false,
361
+ searchError: null,
362
+ selectedCustomer: null,
363
+ hasSearched: false,
364
+ disabled: false,
365
+ };
366
+
367
+ export default injectIntl(CustomerSearchSection);
@@ -0,0 +1,20 @@
1
+ /*
2
+ * CustomerSearchSection Messages
3
+ *
4
+ * This contains all the text for the CustomerSearchSection component.
5
+ */
6
+
7
+ import { defineMessages } from 'react-intl';
8
+
9
+ export const scope = 'app.v2Components.CustomerSearchSection';
10
+
11
+ export default defineMessages({
12
+ searchPlaceholder: {
13
+ id: `${scope}.searchPlaceholder`,
14
+ defaultMessage: 'Search customers by their identifiers',
15
+ },
16
+ noCustomersFound: {
17
+ id: `${scope}.noCustomersFound`,
18
+ defaultMessage: 'No customers found',
19
+ },
20
+ });