@capillarytech/creatives-library 8.0.298 → 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.
- package/package.json +1 -1
- package/services/api.js +17 -0
- package/services/tests/api.test.js +85 -0
- package/utils/commonUtils.js +10 -0
- package/utils/tests/commonUtil.test.js +169 -0
- package/v2Components/CommonTestAndPreview/AddTestCustomer.js +42 -0
- package/v2Components/CommonTestAndPreview/CustomerCreationModal.js +284 -0
- package/v2Components/CommonTestAndPreview/ExistingCustomerModal.js +72 -0
- package/v2Components/CommonTestAndPreview/SendTestMessage.js +78 -49
- package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +189 -4
- package/v2Components/CommonTestAndPreview/actions.js +10 -0
- package/v2Components/CommonTestAndPreview/constants.js +18 -1
- package/v2Components/CommonTestAndPreview/index.js +259 -14
- package/v2Components/CommonTestAndPreview/messages.js +94 -0
- package/v2Components/CommonTestAndPreview/reducer.js +10 -0
- package/v2Components/CommonTestAndPreview/tests/AddTestCustomer.test.js +66 -0
- package/v2Components/CommonTestAndPreview/tests/CommonTestAndPreview.addTestCustomer.test.js +653 -0
- package/v2Components/CommonTestAndPreview/tests/CustomerCreationModal.test.js +316 -0
- package/v2Components/CommonTestAndPreview/tests/ExistingCustomerModal.test.js +114 -0
- package/v2Components/CommonTestAndPreview/tests/SendTestMessage.test.js +53 -0
- package/v2Components/CommonTestAndPreview/tests/constants.test.js +25 -2
- package/v2Components/CommonTestAndPreview/tests/index.test.js +7 -0
- package/v2Components/CommonTestAndPreview/tests/reducer.test.js +71 -0
- package/v2Components/CommonTestAndPreview/tests/selectors.test.js +17 -0
- package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +1408 -1276
- package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +321 -288
- package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +5246 -4872
|
@@ -17,6 +17,9 @@ import CapIcon from '@capillarytech/cap-ui-library/CapIcon';
|
|
|
17
17
|
import CapHeader from '@capillarytech/cap-ui-library/CapHeader';
|
|
18
18
|
import CapDivider from '@capillarytech/cap-ui-library/CapDivider';
|
|
19
19
|
import CapNotification from '@capillarytech/cap-ui-library/CapNotification';
|
|
20
|
+
import CapSpin from '@capillarytech/cap-ui-library/CapSpin';
|
|
21
|
+
import CustomerCreationModal from './CustomerCreationModal';
|
|
22
|
+
import { createTestCustomer } from '../../services/api';
|
|
20
23
|
|
|
21
24
|
// Import messages and styles
|
|
22
25
|
import messages from './messages';
|
|
@@ -27,10 +30,16 @@ import LeftPanelContent from './LeftPanelContent';
|
|
|
27
30
|
import CustomValuesEditor from './CustomValuesEditor';
|
|
28
31
|
import SendTestMessage from './SendTestMessage';
|
|
29
32
|
import PreviewSection from './PreviewSection';
|
|
30
|
-
|
|
33
|
+
import AddTestCustomerButton from './AddTestCustomer';
|
|
34
|
+
import ExistingCustomerModal from './ExistingCustomerModal';
|
|
31
35
|
// Import constants
|
|
32
36
|
import {
|
|
33
37
|
CHANNELS,
|
|
38
|
+
CUSTOMER_MODAL_NEW,
|
|
39
|
+
CUSTOMER_MODAL_EXISTING,
|
|
40
|
+
IDENTIFIER_TYPE_EMAIL,
|
|
41
|
+
IDENTIFIER_TYPE_MOBILE,
|
|
42
|
+
IDENTIFIER_TYPE_PHONE,
|
|
34
43
|
TEST,
|
|
35
44
|
DESKTOP,
|
|
36
45
|
ANDROID,
|
|
@@ -74,6 +83,8 @@ import {
|
|
|
74
83
|
|
|
75
84
|
// Import utilities
|
|
76
85
|
import { getCdnUrl } from '../../utils/cdnTransformation';
|
|
86
|
+
import { isValidEmail, isValidMobile } from '../../utils/commonUtils';
|
|
87
|
+
import { getMembersLookup } from '../../services/api';
|
|
77
88
|
|
|
78
89
|
/**
|
|
79
90
|
* Preview Component Factory - REMOVED IN PHASE 5
|
|
@@ -130,6 +141,11 @@ const CommonTestAndPreview = (props) => {
|
|
|
130
141
|
const [customValues, setCustomValues] = useState({});
|
|
131
142
|
const [showJSON, setShowJSON] = useState(false);
|
|
132
143
|
const [tagsExtracted, setTagsExtracted] = useState(false);
|
|
144
|
+
const [searchValue, setSearchValue] = useState("");
|
|
145
|
+
const [customerModal, setCustomerModal] = useState([false, ""]);
|
|
146
|
+
const [isCustomerDataLoading, setIsCustomerDataLoading] = useState(false);
|
|
147
|
+
const [customerData, setCustomerData] = useState({ name: '', email: '', mobile: '', customerId: '' });
|
|
148
|
+
|
|
133
149
|
// Initialize device based on channel: SMS uses Android/iOS, others use Desktop/Mobile
|
|
134
150
|
// Initialize device based on channel: SMS, WhatsApp, RCS, InApp, MobilePush, and Viber use Android/iOS, others use Desktop/Mobile
|
|
135
151
|
const initialDevice = (channel === CHANNELS.SMS || channel === CHANNELS.WHATSAPP || channel === CHANNELS.RCS || channel === CHANNELS.INAPP || channel === CHANNELS.MOBILEPUSH || channel === CHANNELS.VIBER) ? ANDROID : DESKTOP;
|
|
@@ -348,7 +364,7 @@ const CommonTestAndPreview = (props) => {
|
|
|
348
364
|
return content || '';
|
|
349
365
|
}, [channel, formData, currentTab, beeContent, content, beeInstance]);
|
|
350
366
|
|
|
351
|
-
// Build test entities tree data
|
|
367
|
+
// Build test entities tree data from testCustomers prop (includes customers added via addTestCustomer action)
|
|
352
368
|
const testEntitiesTreeData = useMemo(() => {
|
|
353
369
|
const groupsNode = {
|
|
354
370
|
title: 'Groups',
|
|
@@ -361,7 +377,10 @@ const CommonTestAndPreview = (props) => {
|
|
|
361
377
|
title: 'Individuals',
|
|
362
378
|
value: 'customers-node',
|
|
363
379
|
selectable: false,
|
|
364
|
-
children: testCustomers?.map((customer) => ({
|
|
380
|
+
children: testCustomers?.map((customer) => ({
|
|
381
|
+
title: customer?.name?.trim() || customer?.email?.trim() || customer?.mobile?.trim() || customer?.userId || customer?.customerId,
|
|
382
|
+
value: customer?.userId ?? customer?.customerId,
|
|
383
|
+
})) || [],
|
|
365
384
|
};
|
|
366
385
|
|
|
367
386
|
return [groupsNode, customersNode];
|
|
@@ -388,6 +407,91 @@ const CommonTestAndPreview = (props) => {
|
|
|
388
407
|
return resolvedText;
|
|
389
408
|
};
|
|
390
409
|
|
|
410
|
+
/**
|
|
411
|
+
* Common handler for saving test customers (both new and existing)
|
|
412
|
+
*/
|
|
413
|
+
const handleSaveTestCustomer = async (validationErrors = {},setIsLoading = false) => {
|
|
414
|
+
// Check for validation errors before saving (for new customers)
|
|
415
|
+
if (customerModal[1] === CUSTOMER_MODAL_NEW && (validationErrors.email || validationErrors.mobile)) {
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
setIsLoading(true);
|
|
420
|
+
|
|
421
|
+
try {
|
|
422
|
+
let payload;
|
|
423
|
+
|
|
424
|
+
if (customerModal[1] === CUSTOMER_MODAL_EXISTING) {
|
|
425
|
+
// For existing customers, use customerId
|
|
426
|
+
payload = {
|
|
427
|
+
customerId: customerData.customerId
|
|
428
|
+
};
|
|
429
|
+
} else {
|
|
430
|
+
// For new customers, use customer object
|
|
431
|
+
payload = {
|
|
432
|
+
customer: {
|
|
433
|
+
firstName: customerData.name || "",
|
|
434
|
+
mobile: customerData.mobile || "",
|
|
435
|
+
email: customerData.email || ""
|
|
436
|
+
}
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
const response = await createTestCustomer(payload);
|
|
441
|
+
|
|
442
|
+
|
|
443
|
+
// Handle success: add to test customers list and selection (existing and new)
|
|
444
|
+
if (response && response.success) {
|
|
445
|
+
CapNotification.success({
|
|
446
|
+
message: formatMessage(messages.newTestCustomerAddedSuccess),
|
|
447
|
+
});
|
|
448
|
+
// API may return customerId in response.response (e.g. { response: { customerId: 438845651 } })
|
|
449
|
+
const res = response?.response || response;
|
|
450
|
+
const addedId = customerModal[1] === CUSTOMER_MODAL_EXISTING
|
|
451
|
+
? customerData?.customerId
|
|
452
|
+
: res?.customerId;
|
|
453
|
+
if (addedId) {
|
|
454
|
+
actions.addTestCustomer({
|
|
455
|
+
userId: addedId,
|
|
456
|
+
customerId: addedId,
|
|
457
|
+
name: customerData?.name?.trim() || '',
|
|
458
|
+
email: customerData?.email || '',
|
|
459
|
+
mobile: customerData?.mobile || '',
|
|
460
|
+
});
|
|
461
|
+
setSelectedTestEntities((prev) => [...prev, addedId]);
|
|
462
|
+
}
|
|
463
|
+
handleCloseCustomerModal();
|
|
464
|
+
} else {
|
|
465
|
+
// Show error notification for unsuccessful response
|
|
466
|
+
CapNotification.error({
|
|
467
|
+
message: formatMessage(messages.errorTitle),
|
|
468
|
+
description: response?.message || formatMessage(messages.failedToAddTestCustomer),
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
} catch (error) {
|
|
472
|
+
if (customerModal[1] === CUSTOMER_MODAL_EXISTING) {
|
|
473
|
+
// Show error notification for caught exceptions (existing customers only)
|
|
474
|
+
CapNotification.error({
|
|
475
|
+
message: formatMessage(messages.errorTitle),
|
|
476
|
+
description: error?.message || formatMessage(messages.errorAddingTestCustomer),
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
} finally {
|
|
480
|
+
setIsLoading(false);
|
|
481
|
+
}
|
|
482
|
+
};
|
|
483
|
+
|
|
484
|
+
const handleCloseCustomerModal = () => {
|
|
485
|
+
setCustomerModal([false, ""]);
|
|
486
|
+
setSearchValue('');
|
|
487
|
+
setCustomerData({
|
|
488
|
+
name: '',
|
|
489
|
+
email: '',
|
|
490
|
+
mobile: '',
|
|
491
|
+
customerId: '',
|
|
492
|
+
});
|
|
493
|
+
};
|
|
494
|
+
|
|
391
495
|
/**
|
|
392
496
|
* Prepare payload for preview API based on channel
|
|
393
497
|
*/
|
|
@@ -2472,7 +2576,6 @@ const CommonTestAndPreview = (props) => {
|
|
|
2472
2576
|
* Handle extract tags
|
|
2473
2577
|
*/
|
|
2474
2578
|
const handleExtractTags = () => {
|
|
2475
|
-
// Get content based on channel
|
|
2476
2579
|
let contentToExtract = getCurrentContent;
|
|
2477
2580
|
|
|
2478
2581
|
if (channel === CHANNELS.EMAIL && formData) {
|
|
@@ -2512,6 +2615,107 @@ const CommonTestAndPreview = (props) => {
|
|
|
2512
2615
|
setSelectedTestEntities(value);
|
|
2513
2616
|
};
|
|
2514
2617
|
|
|
2618
|
+
/**
|
|
2619
|
+
* Map API customerDetails item to our customerData shape
|
|
2620
|
+
*/
|
|
2621
|
+
const mapCustomerDetailsToCustomerData = (detail, identifierValue) => {
|
|
2622
|
+
const firstName = detail.firstName || '';
|
|
2623
|
+
const lastName = detail.lastName || '';
|
|
2624
|
+
const name = [firstName, lastName].filter(Boolean).join(' ').trim() || '';
|
|
2625
|
+
const getIdentifierValue = (type) => {
|
|
2626
|
+
const fromIdentifiers = detail.identifiers?.find((i) => i.type === type)?.value;
|
|
2627
|
+
if (fromIdentifiers) return fromIdentifiers;
|
|
2628
|
+
const fromCommChannels = detail.commChannels?.find((c) => c.type === type)?.value;
|
|
2629
|
+
return fromCommChannels || (channel === CHANNELS.EMAIL && type === IDENTIFIER_TYPE_EMAIL ? identifierValue : channel === CHANNELS.SMS && type === IDENTIFIER_TYPE_MOBILE ? identifierValue : '');
|
|
2630
|
+
};
|
|
2631
|
+
return {
|
|
2632
|
+
name,
|
|
2633
|
+
email: channel === CHANNELS.EMAIL ? (getIdentifierValue(IDENTIFIER_TYPE_EMAIL) || identifierValue) : (getIdentifierValue(IDENTIFIER_TYPE_EMAIL) || ''),
|
|
2634
|
+
mobile: channel === CHANNELS.SMS ? (getIdentifierValue(IDENTIFIER_TYPE_MOBILE) || getIdentifierValue(IDENTIFIER_TYPE_PHONE) || identifierValue) : (getIdentifierValue(IDENTIFIER_TYPE_MOBILE) || getIdentifierValue(IDENTIFIER_TYPE_PHONE) || ''),
|
|
2635
|
+
customerId: detail.userId != null ? String(detail.userId) : '',
|
|
2636
|
+
};
|
|
2637
|
+
};
|
|
2638
|
+
|
|
2639
|
+
const handleAddTestCustomer = async () => {
|
|
2640
|
+
const identifierType = channel === CHANNELS.EMAIL ? IDENTIFIER_TYPE_EMAIL : IDENTIFIER_TYPE_MOBILE;
|
|
2641
|
+
const searchValueToCheck = searchValue || '';
|
|
2642
|
+
|
|
2643
|
+
// Check if this customer is already in the test customers list
|
|
2644
|
+
const existingTestCustomer = testCustomers?.find(customer => {
|
|
2645
|
+
if (channel === CHANNELS.EMAIL) {
|
|
2646
|
+
return customer.email === searchValueToCheck;
|
|
2647
|
+
} else if (channel === CHANNELS.SMS) {
|
|
2648
|
+
return customer.mobile === searchValueToCheck;
|
|
2649
|
+
}
|
|
2650
|
+
return false;
|
|
2651
|
+
});
|
|
2652
|
+
|
|
2653
|
+
if (existingTestCustomer) {
|
|
2654
|
+
const entityId = existingTestCustomer.userId ?? existingTestCustomer.customerId;
|
|
2655
|
+
if (entityId != null) {
|
|
2656
|
+
const id = String(entityId);
|
|
2657
|
+
setSelectedTestEntities((prev) =>
|
|
2658
|
+
prev.includes(id) ? prev : [...prev, id]
|
|
2659
|
+
);
|
|
2660
|
+
}
|
|
2661
|
+
setSearchValue('');
|
|
2662
|
+
CapNotification.success({
|
|
2663
|
+
message: formatMessage(messages.customerAlreadyInTestList),
|
|
2664
|
+
});
|
|
2665
|
+
return;
|
|
2666
|
+
}
|
|
2667
|
+
|
|
2668
|
+
setIsCustomerDataLoading(true);
|
|
2669
|
+
|
|
2670
|
+
try {
|
|
2671
|
+
const response = await getMembersLookup(identifierType, searchValueToCheck);
|
|
2672
|
+
const success = response?.success && !response?.status?.isError;
|
|
2673
|
+
const res = response?.response || {};
|
|
2674
|
+
const exists = res.exists || false;
|
|
2675
|
+
const details = res.customerDetails || [];
|
|
2676
|
+
|
|
2677
|
+
if (!success) {
|
|
2678
|
+
const errorMessage = response?.message || response?.status?.message || formatMessage(messages.memberLookupError);
|
|
2679
|
+
CapNotification.error({ title: formatMessage(messages.errorTitle), message: errorMessage });
|
|
2680
|
+
return;
|
|
2681
|
+
}
|
|
2682
|
+
|
|
2683
|
+
if (exists && details.length > 0) {
|
|
2684
|
+
const mapped = mapCustomerDetailsToCustomerData(details[0], searchValueToCheck);
|
|
2685
|
+
const customerIdFromLookup = mapped.customerId;
|
|
2686
|
+
const alreadyInTestListByCustomerId = customerIdFromLookup && testCustomers?.some(
|
|
2687
|
+
(c) => String(c?.customerId) === customerIdFromLookup || String(c?.userId) === customerIdFromLookup
|
|
2688
|
+
);
|
|
2689
|
+
if (alreadyInTestListByCustomerId) {
|
|
2690
|
+
setSelectedTestEntities((prev) =>
|
|
2691
|
+
prev.includes(customerIdFromLookup) ? prev : [...prev, customerIdFromLookup]
|
|
2692
|
+
);
|
|
2693
|
+
setSearchValue('');
|
|
2694
|
+
CapNotification.success({
|
|
2695
|
+
message: formatMessage(messages.customerAlreadyInTestList),
|
|
2696
|
+
});
|
|
2697
|
+
return;
|
|
2698
|
+
}
|
|
2699
|
+
setCustomerData(mapped);
|
|
2700
|
+
setCustomerModal([true, CUSTOMER_MODAL_EXISTING]);
|
|
2701
|
+
} else {
|
|
2702
|
+
setCustomerData({
|
|
2703
|
+
name: '',
|
|
2704
|
+
email: channel === CHANNELS.EMAIL ? searchValueToCheck : '',
|
|
2705
|
+
mobile: channel === CHANNELS.SMS ? searchValueToCheck : '',
|
|
2706
|
+
customerId: '',
|
|
2707
|
+
});
|
|
2708
|
+
setCustomerModal([true, CUSTOMER_MODAL_NEW]);
|
|
2709
|
+
}
|
|
2710
|
+
} catch {
|
|
2711
|
+
CapNotification.error({
|
|
2712
|
+
message: formatMessage(messages.memberLookupError),
|
|
2713
|
+
});
|
|
2714
|
+
} finally {
|
|
2715
|
+
setIsCustomerDataLoading(false);
|
|
2716
|
+
}
|
|
2717
|
+
};
|
|
2718
|
+
|
|
2515
2719
|
/**
|
|
2516
2720
|
* Handle send test message
|
|
2517
2721
|
*/
|
|
@@ -2630,6 +2834,7 @@ const CommonTestAndPreview = (props) => {
|
|
|
2630
2834
|
content={getCurrentContent}
|
|
2631
2835
|
channel={channel}
|
|
2632
2836
|
isSendingTestMessage={isSendingTestMessage}
|
|
2837
|
+
renderAddTestCustomerButton={renderAddTestCustomerButton}
|
|
2633
2838
|
formatMessage={formatMessage}
|
|
2634
2839
|
deliverySettings={testPreviewDeliverySettings[channel]}
|
|
2635
2840
|
senderDetailsOptions={senderDetailsByChannel[channel]}
|
|
@@ -2638,6 +2843,8 @@ const CommonTestAndPreview = (props) => {
|
|
|
2638
2843
|
isLoadingSenderDetails={isLoadingSenderDetails}
|
|
2639
2844
|
smsTraiDltEnabled={smsTraiDltEnabled}
|
|
2640
2845
|
registeredSenderIds={registeredSenderIds}
|
|
2846
|
+
searchValue={searchValue}
|
|
2847
|
+
setSearchValue={setSearchValue}
|
|
2641
2848
|
/>
|
|
2642
2849
|
);
|
|
2643
2850
|
|
|
@@ -2647,6 +2854,20 @@ const CommonTestAndPreview = (props) => {
|
|
|
2647
2854
|
/>
|
|
2648
2855
|
);
|
|
2649
2856
|
|
|
2857
|
+
const renderAddTestCustomerButton = () => {
|
|
2858
|
+
const value = searchValue || "";
|
|
2859
|
+
const showAddButton =
|
|
2860
|
+
[CHANNELS.EMAIL, CHANNELS.SMS].includes(channel) &&
|
|
2861
|
+
(channel === CHANNELS.EMAIL ? isValidEmail(value) : isValidMobile(value));
|
|
2862
|
+
if (!showAddButton) return null;
|
|
2863
|
+
return (
|
|
2864
|
+
<AddTestCustomerButton
|
|
2865
|
+
searchValue={value}
|
|
2866
|
+
handleAddTestCustomer={handleAddTestCustomer}
|
|
2867
|
+
/>
|
|
2868
|
+
);
|
|
2869
|
+
};
|
|
2870
|
+
|
|
2650
2871
|
// Header content for the slidebox
|
|
2651
2872
|
const slideboxHeader = (
|
|
2652
2873
|
<CapRow className="test-preview-header">
|
|
@@ -2666,14 +2887,18 @@ const CommonTestAndPreview = (props) => {
|
|
|
2666
2887
|
show={show}
|
|
2667
2888
|
size="size-xl"
|
|
2668
2889
|
content={(
|
|
2669
|
-
<
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2890
|
+
<CapSpin
|
|
2891
|
+
spinning={isCustomerDataLoading}
|
|
2892
|
+
className={`common-test-preview-lookup-spin ${isCustomerDataLoading ? 'common-test-preview-customer-loading' : ''}`}
|
|
2893
|
+
>
|
|
2894
|
+
<CapRow className="test-preview-container">
|
|
2895
|
+
<CapRow className="test-and-preview-panels">
|
|
2896
|
+
{/* Left Panel */}
|
|
2897
|
+
<CapRow className="left-panel">
|
|
2898
|
+
{channel === CHANNELS.ZALO ? null : renderLeftPanelContent()}
|
|
2899
|
+
<CapDivider className="panel-divider" />
|
|
2900
|
+
|
|
2901
|
+
{/* Send Test Message Section */}
|
|
2677
2902
|
{config.enableTestMessage !== false && (
|
|
2678
2903
|
<CapRow className="panel-section send-test-section">
|
|
2679
2904
|
{renderSendTestMessage()}
|
|
@@ -2687,7 +2912,27 @@ const CommonTestAndPreview = (props) => {
|
|
|
2687
2912
|
{renderPreview()}
|
|
2688
2913
|
</CapRow>
|
|
2689
2914
|
</CapRow>
|
|
2690
|
-
|
|
2915
|
+
{customerModal[0] && customerModal[1] === CUSTOMER_MODAL_EXISTING && (
|
|
2916
|
+
<ExistingCustomerModal
|
|
2917
|
+
customerData={customerData}
|
|
2918
|
+
setCustomerModal={setCustomerModal}
|
|
2919
|
+
customerModal={customerModal}
|
|
2920
|
+
channel={channel}
|
|
2921
|
+
onSave={handleSaveTestCustomer}
|
|
2922
|
+
/>
|
|
2923
|
+
)}
|
|
2924
|
+
{customerModal[0] && customerModal[1] === CUSTOMER_MODAL_NEW && (
|
|
2925
|
+
<CustomerCreationModal
|
|
2926
|
+
customerData={customerData}
|
|
2927
|
+
setCustomerData={setCustomerData}
|
|
2928
|
+
setCustomerModal={setCustomerModal}
|
|
2929
|
+
customerModal={customerModal}
|
|
2930
|
+
onSave={handleSaveTestCustomer}
|
|
2931
|
+
channel={channel}
|
|
2932
|
+
/>
|
|
2933
|
+
)}
|
|
2934
|
+
</CapRow>
|
|
2935
|
+
</CapSpin>
|
|
2691
2936
|
)}
|
|
2692
2937
|
/>
|
|
2693
2938
|
);
|
|
@@ -2789,4 +3034,4 @@ CommonTestAndPreview.defaultProps = {
|
|
|
2789
3034
|
// Note: Redux connection is handled by the wrapper components (e.g., TestAndPreviewSlidebox)
|
|
2790
3035
|
// This component receives all Redux props (including intl) from its parent
|
|
2791
3036
|
|
|
2792
|
-
export default CommonTestAndPreview;
|
|
3037
|
+
export default CommonTestAndPreview;
|
|
@@ -8,10 +8,72 @@ import { defineMessages } from 'react-intl';
|
|
|
8
8
|
export const scope = 'app.v2Components.TestAndPreviewSlidebox';
|
|
9
9
|
|
|
10
10
|
export default defineMessages({
|
|
11
|
+
mobileAlreadyExists: {
|
|
12
|
+
id: `${scope}.mobileAlreadyExists`,
|
|
13
|
+
defaultMessage: 'This phone number matches with already existing user profile. Please remove or use a different number.',
|
|
14
|
+
},
|
|
15
|
+
customerNamePlaceholder: {
|
|
16
|
+
id: `${scope}.customerNamePlaceholder`,
|
|
17
|
+
defaultMessage: 'Enter the name',
|
|
18
|
+
},
|
|
19
|
+
customerEmailPlaceholder: {
|
|
20
|
+
id: `${scope}.customerEmailPlaceholder`,
|
|
21
|
+
defaultMessage: 'Enter the Email',
|
|
22
|
+
},
|
|
23
|
+
customerMobilePlaceholder: {
|
|
24
|
+
id: `${scope}.customerMobilePlaceholder`,
|
|
25
|
+
defaultMessage: 'Enter the Mobile Number',
|
|
26
|
+
},
|
|
27
|
+
customerAlreadyInTestList: {
|
|
28
|
+
id: `${scope}.customerAlreadyInTestList`,
|
|
29
|
+
defaultMessage: 'This customer is already in the test customers list.',
|
|
30
|
+
},
|
|
31
|
+
emailAlreadyExists: {
|
|
32
|
+
id: `${scope}.emailAlreadyExists`,
|
|
33
|
+
defaultMessage: 'This email matches with already existing user profile. Please remove or use a different email.',
|
|
34
|
+
},
|
|
35
|
+
customerName: {
|
|
36
|
+
id: `${scope}.customerName`,
|
|
37
|
+
defaultMessage: 'Name',
|
|
38
|
+
},
|
|
39
|
+
customerEmail: {
|
|
40
|
+
id: `${scope}.customerEmail`,
|
|
41
|
+
defaultMessage: 'Email',
|
|
42
|
+
},
|
|
43
|
+
customerMobile: {
|
|
44
|
+
id: `${scope}.customerMobileNumber`,
|
|
45
|
+
defaultMessage: 'Mobile Number',
|
|
46
|
+
},
|
|
47
|
+
saveButton: {
|
|
48
|
+
id: `${scope}.saveButton`,
|
|
49
|
+
defaultMessage: 'Save',
|
|
50
|
+
},
|
|
51
|
+
cancelButton: {
|
|
52
|
+
id: `${scope}.cancelButton`,
|
|
53
|
+
defaultMessage: 'Cancel',
|
|
54
|
+
},
|
|
55
|
+
customerID: {
|
|
56
|
+
id: `${scope}.customerID`,
|
|
57
|
+
defaultMessage: 'Customer ID',
|
|
58
|
+
},
|
|
59
|
+
existingCustomerModalDescription: {
|
|
60
|
+
id: `${scope}.existingCustomerModalDescription`,
|
|
61
|
+
defaultMessage: 'This user profile already exists in the system. Would you like to add them to Test Customer list?'
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
customerCreationModalTitle: {
|
|
65
|
+
id: `${scope}.customerCreationModalTitle`,
|
|
66
|
+
defaultMessage: 'Add new test customer',
|
|
67
|
+
},
|
|
68
|
+
customerCreationModalDescription: {
|
|
69
|
+
id: `${scope}.customerCreationModalDescription`,
|
|
70
|
+
defaultMessage: 'This customer profile will be available for testing across multiple communication channels.',
|
|
71
|
+
},
|
|
11
72
|
testAndPreviewHeader: {
|
|
12
73
|
id: `${scope}.testAndPreviewHeader`,
|
|
13
74
|
defaultMessage: 'Preview and Test',
|
|
14
75
|
},
|
|
76
|
+
|
|
15
77
|
customerSearchTitle: {
|
|
16
78
|
id: `${scope}.customerSearchTitle`,
|
|
17
79
|
defaultMessage: 'Customer',
|
|
@@ -32,6 +94,34 @@ export default defineMessages({
|
|
|
32
94
|
id: `${scope}.showJSON`,
|
|
33
95
|
defaultMessage: 'Show JSON',
|
|
34
96
|
},
|
|
97
|
+
addTestCustomer: {
|
|
98
|
+
id: `${scope}.addTestCustomer`,
|
|
99
|
+
defaultMessage: 'Add as Test Customer',
|
|
100
|
+
},
|
|
101
|
+
addTestCustomerWithValue: {
|
|
102
|
+
id: `${scope}.addTestCustomerWithValue`,
|
|
103
|
+
defaultMessage: 'Add {searchValue} as Test Customer',
|
|
104
|
+
},
|
|
105
|
+
memberLookupError: {
|
|
106
|
+
id: `${scope}.memberLookupError`,
|
|
107
|
+
defaultMessage: 'Unable to look up customer. Please try again.',
|
|
108
|
+
},
|
|
109
|
+
newTestCustomerAddedSuccess: {
|
|
110
|
+
id: `${scope}.newTestCustomerAddedSuccess`,
|
|
111
|
+
defaultMessage: 'New test customer added successfully!',
|
|
112
|
+
},
|
|
113
|
+
errorTitle: {
|
|
114
|
+
id: `${scope}.errorTitle`,
|
|
115
|
+
defaultMessage: 'Error',
|
|
116
|
+
},
|
|
117
|
+
failedToAddTestCustomer: {
|
|
118
|
+
id: `${scope}.failedToAddTestCustomer`,
|
|
119
|
+
defaultMessage: 'Failed to add test customer',
|
|
120
|
+
},
|
|
121
|
+
errorAddingTestCustomer: {
|
|
122
|
+
id: `${scope}.errorAddingTestCustomer`,
|
|
123
|
+
defaultMessage: 'An error occurred while adding test customer',
|
|
124
|
+
},
|
|
35
125
|
discardCustomValues: {
|
|
36
126
|
id: `${scope}.discardCustomValues`,
|
|
37
127
|
defaultMessage: 'Discard custom values',
|
|
@@ -120,6 +210,10 @@ export default defineMessages({
|
|
|
120
210
|
id: `${scope}.testCustomersPlaceholder`,
|
|
121
211
|
defaultMessage: 'Search and select a group or individual test customers',
|
|
122
212
|
},
|
|
213
|
+
noMatchingOptions: {
|
|
214
|
+
id: `${scope}.noMatchingOptions`,
|
|
215
|
+
defaultMessage: 'No matching options',
|
|
216
|
+
},
|
|
123
217
|
updatingPreview: {
|
|
124
218
|
id: `${scope}.updatingPreview`,
|
|
125
219
|
defaultMessage: 'Updating preview with the latest changes',
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
GET_TEST_CUSTOMERS_REQUESTED,
|
|
21
21
|
GET_TEST_CUSTOMERS_SUCCESS,
|
|
22
22
|
GET_TEST_CUSTOMERS_FAILURE,
|
|
23
|
+
ADD_TEST_CUSTOMER,
|
|
23
24
|
GET_TEST_GROUPS_REQUESTED,
|
|
24
25
|
GET_TEST_GROUPS_SUCCESS,
|
|
25
26
|
GET_TEST_GROUPS_FAILURE,
|
|
@@ -216,6 +217,15 @@ const previewAndTestReducer = (state = initialState, action) => {
|
|
|
216
217
|
return state.set('isFetchingTestCustomers', false)
|
|
217
218
|
.set('fetchTestCustomersError', action.payload.error);
|
|
218
219
|
|
|
220
|
+
case ADD_TEST_CUSTOMER: {
|
|
221
|
+
const raw = state.get('testCustomers');
|
|
222
|
+
const list = Array.isArray(raw) ? raw : (raw && raw.toArray ? raw.toArray().map((i) => (i && i.toJS ? i.toJS() : i)) : []);
|
|
223
|
+
const customer = action.payload.customer;
|
|
224
|
+
const newId = customer.userId || customer.customerId;
|
|
225
|
+
if (list.some((c) => (c.userId || c.customerId) === newId)) return state;
|
|
226
|
+
return state.set('testCustomers', list.concat([customer]));
|
|
227
|
+
}
|
|
228
|
+
|
|
219
229
|
// Test Groups
|
|
220
230
|
case GET_TEST_GROUPS_REQUESTED:
|
|
221
231
|
return state.set('isFetchingTestGroups', true)
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for AddTestCustomerButton Component
|
|
3
|
+
*
|
|
4
|
+
* The parent (index.js) only renders this button when channel is EMAIL/SMS and value is valid.
|
|
5
|
+
* This component always renders the button when mounted; visibility is the parent's responsibility.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import React from 'react';
|
|
9
|
+
import { render, screen, fireEvent } from '@testing-library/react';
|
|
10
|
+
import { IntlProvider } from 'react-intl';
|
|
11
|
+
import AddTestCustomerButton from '../AddTestCustomer';
|
|
12
|
+
|
|
13
|
+
const mockMessages = {
|
|
14
|
+
'app.v2Components.TestAndPreviewSlidebox.addTestCustomerWithValue': 'Add {searchValue} as Test Customer',
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const TestWrapper = ({ children }) => (
|
|
18
|
+
<IntlProvider locale="en" messages={mockMessages}>
|
|
19
|
+
{children}
|
|
20
|
+
</IntlProvider>
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
describe('AddTestCustomerButton', () => {
|
|
24
|
+
const defaultProps = {
|
|
25
|
+
searchValue: 'user@example.com',
|
|
26
|
+
handleAddTestCustomer: jest.fn(),
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
beforeEach(() => {
|
|
30
|
+
jest.clearAllMocks();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('should render button with searchValue in message', () => {
|
|
34
|
+
render(
|
|
35
|
+
<TestWrapper>
|
|
36
|
+
<AddTestCustomerButton {...defaultProps} />
|
|
37
|
+
</TestWrapper>
|
|
38
|
+
);
|
|
39
|
+
const button = screen.getByRole('button', { name: /add.*test customer/i });
|
|
40
|
+
expect(button).toBeTruthy();
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('should show searchValue in button text', () => {
|
|
44
|
+
render(
|
|
45
|
+
<TestWrapper>
|
|
46
|
+
<AddTestCustomerButton {...defaultProps} searchValue="user@example.com" />
|
|
47
|
+
</TestWrapper>
|
|
48
|
+
);
|
|
49
|
+
expect(screen.getByRole('button', { name: /user@example.com/i })).toBeTruthy();
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('should call handleAddTestCustomer when button is clicked', () => {
|
|
53
|
+
const handleAddTestCustomer = jest.fn();
|
|
54
|
+
render(
|
|
55
|
+
<TestWrapper>
|
|
56
|
+
<AddTestCustomerButton
|
|
57
|
+
searchValue="user@example.com"
|
|
58
|
+
handleAddTestCustomer={handleAddTestCustomer}
|
|
59
|
+
/>
|
|
60
|
+
</TestWrapper>
|
|
61
|
+
);
|
|
62
|
+
const button = screen.getByRole('button', { name: /add.*test customer/i });
|
|
63
|
+
fireEvent.click(button);
|
|
64
|
+
expect(handleAddTestCustomer).toHaveBeenCalledTimes(1);
|
|
65
|
+
});
|
|
66
|
+
});
|