@capillarytech/creatives-library 8.0.310-alpha.0 → 8.0.311

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 (29) hide show
  1. package/package.json +1 -1
  2. package/services/api.js +0 -17
  3. package/services/tests/api.test.js +0 -85
  4. package/utils/commonUtils.js +0 -10
  5. package/utils/tests/commonUtil.test.js +0 -169
  6. package/v2Components/CommonTestAndPreview/SendTestMessage.js +49 -78
  7. package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +34 -134
  8. package/v2Components/CommonTestAndPreview/actions.js +0 -10
  9. package/v2Components/CommonTestAndPreview/constants.js +1 -15
  10. package/v2Components/CommonTestAndPreview/index.js +19 -80
  11. package/v2Components/CommonTestAndPreview/messages.js +0 -94
  12. package/v2Components/CommonTestAndPreview/reducer.js +0 -10
  13. package/v2Components/CommonTestAndPreview/tests/SendTestMessage.test.js +0 -53
  14. package/v2Components/CommonTestAndPreview/tests/constants.test.js +1 -31
  15. package/v2Components/CommonTestAndPreview/tests/index.test.js +0 -36
  16. package/v2Components/CommonTestAndPreview/tests/reducer.test.js +0 -71
  17. package/v2Components/CommonTestAndPreview/tests/sagas.test.js +0 -377
  18. package/v2Components/CommonTestAndPreview/tests/selectors.test.js +0 -17
  19. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +1276 -1408
  20. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +288 -321
  21. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +4872 -5246
  22. package/v2Components/CommonTestAndPreview/AddTestCustomer.js +0 -42
  23. package/v2Components/CommonTestAndPreview/CustomerCreationModal.js +0 -284
  24. package/v2Components/CommonTestAndPreview/ExistingCustomerModal.js +0 -72
  25. package/v2Components/CommonTestAndPreview/tests/AddTestCustomer.test.js +0 -66
  26. package/v2Components/CommonTestAndPreview/tests/CommonTestAndPreview.addTestCustomer.test.js +0 -657
  27. package/v2Components/CommonTestAndPreview/tests/CustomValuesEditor.test.js +0 -172
  28. package/v2Components/CommonTestAndPreview/tests/CustomerCreationModal.test.js +0 -466
  29. package/v2Components/CommonTestAndPreview/tests/ExistingCustomerModal.test.js +0 -114
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@capillarytech/creatives-library",
3
3
  "author": "meharaj",
4
- "version": "8.0.310-alpha.0",
4
+ "version": "8.0.311",
5
5
  "description": "Capillary creatives ui",
6
6
  "main": "./index.js",
7
7
  "module": "./index.es.js",
package/services/api.js CHANGED
@@ -735,21 +735,4 @@ export const getBeePopupBuilderToken = () => {
735
735
  return request(url, getAPICallObject('GET'));
736
736
  };
737
737
 
738
- /**
739
- * Look up member by identifier (email or mobile) for add test customer flow.
740
- * Uses standard API auth headers (no x-cap-ct) to avoid CORS preflight issues with node layer.
741
- * @param {string} identifierType - 'email' or 'mobile'
742
- * @param {string} identifierValue - email address or mobile number
743
- * @returns {Promise} Promise resolving to { success, response: { exists, customerDetails } }
744
- */
745
- export const getMembersLookup = (identifierType, identifierValue) => {
746
- const url = `${API_ENDPOINT}/members?identifierType=${encodeURIComponent(identifierType)}&identifierValue=${encodeURIComponent(identifierValue)}`;
747
- return request(url, getAPICallObject('GET'));
748
- };
749
-
750
- export const createTestCustomer = (payload) => {
751
- const url = `${API_ENDPOINT}/testCustomers`;
752
- return request(url, getAPICallObject('POST', payload));
753
- };
754
-
755
738
  export {request, getAPICallObject};
@@ -28,8 +28,6 @@ import {
28
28
  getAssetStatus,
29
29
  getBeePopupBuilderToken,
30
30
  getCmsAccounts,
31
- getMembersLookup,
32
- createTestCustomer,
33
31
  } from '../api';
34
32
  import { mockData } from './mockData';
35
33
  import getSchema from '../getSchema';
@@ -1009,86 +1007,3 @@ describe('getCmsAccounts', () => {
1009
1007
  expect(result).toBeInstanceOf(Promise);
1010
1008
  });
1011
1009
  });
1012
-
1013
- describe('getMembersLookup', () => {
1014
- beforeEach(() => {
1015
- global.fetch = jest.fn();
1016
- global.fetch.mockReturnValue(Promise.resolve({
1017
- status: 200,
1018
- json: () => Promise.resolve({ success: true, response: { exists: false, customerDetails: [] } }),
1019
- }));
1020
- });
1021
-
1022
- it('should return a Promise', () => {
1023
- const result = getMembersLookup('email', 'user@example.com');
1024
- expect(result).toBeInstanceOf(Promise);
1025
- });
1026
-
1027
- it('should be callable with identifierType and identifierValue', () => {
1028
- expect(typeof getMembersLookup).toBe('function');
1029
- const result = getMembersLookup('mobile', '9123456789');
1030
- expect(result).toBeInstanceOf(Promise);
1031
- });
1032
-
1033
- it('should call fetch with correct URL encoding and GET method', () => {
1034
- global.fetch.mockClear();
1035
- getMembersLookup('email', 'user+test@example.com');
1036
- expect(global.fetch).toHaveBeenCalled();
1037
- const calls = global.fetch.mock.calls;
1038
- const withEncoding = calls.find(
1039
- (c) =>
1040
- c[0] &&
1041
- String(c[0]).includes('members') &&
1042
- String(c[0]).includes('identifierType=email') &&
1043
- String(c[0]).includes('user%2Btest%40example.com')
1044
- );
1045
- if (withEncoding) {
1046
- const [url, options] = withEncoding;
1047
- expect(url).toContain('identifierType=email');
1048
- expect(url).toContain('identifierValue=user%2Btest%40example.com');
1049
- expect(options?.method || 'GET').toBe('GET');
1050
- }
1051
- const anyMembersCall = calls.find((c) => c[0] && String(c[0]).includes('members'));
1052
- expect(anyMembersCall).toBeDefined();
1053
- expect(anyMembersCall[0]).toContain('identifierType=');
1054
- expect(anyMembersCall[0]).toContain('identifierValue=');
1055
- expect(anyMembersCall[1]?.method || 'GET').toBe('GET');
1056
- });
1057
- });
1058
-
1059
- describe('createTestCustomer', () => {
1060
- beforeEach(() => {
1061
- global.fetch = jest.fn();
1062
- global.fetch.mockReturnValue(Promise.resolve({
1063
- status: 200,
1064
- json: () => Promise.resolve({ success: true }),
1065
- }));
1066
- });
1067
-
1068
- it('should return a Promise', () => {
1069
- const payload = { customer: { email: 'test@example.com', firstName: 'Test' } };
1070
- const result = createTestCustomer(payload);
1071
- expect(result).toBeInstanceOf(Promise);
1072
- });
1073
-
1074
- it('should accept customerId payload for existing customer', () => {
1075
- const payload = { customerId: 'cust-123' };
1076
- const result = createTestCustomer(payload);
1077
- expect(result).toBeInstanceOf(Promise);
1078
- expect(global.fetch).toHaveBeenCalled();
1079
- const lastCall = global.fetch.mock.calls[global.fetch.mock.calls.length - 1];
1080
- expect(lastCall[0]).toContain('testCustomers');
1081
- expect(lastCall[1].method).toBe('POST');
1082
- expect(lastCall[1].body).toBe(JSON.stringify(payload));
1083
- });
1084
-
1085
- it('should call fetch with /testCustomers URL and POST method', () => {
1086
- const payload = { customer: { email: 'n@b.co', firstName: 'N' } };
1087
- createTestCustomer(payload);
1088
- expect(global.fetch).toHaveBeenCalled();
1089
- const lastCall = global.fetch.mock.calls[global.fetch.mock.calls.length - 1];
1090
- expect(lastCall[0]).toContain('testCustomers');
1091
- expect(lastCall[1].method).toBe('POST');
1092
- expect(lastCall[1].body).toBe(JSON.stringify(payload));
1093
- });
1094
- });
@@ -17,8 +17,6 @@ import {
17
17
  } from "../v2Containers/CreativesContainer/constants";
18
18
  import { GLOBAL_CONVERT_OPTIONS } from "../v2Components/FormBuilder/constants";
19
19
  import { SMS_TRAI_VAR } from '../v2Containers/SmsTrai/Edit/constants';
20
- import { EMAIL_REGEX, PHONE_REGEX } from '../v2Components/CommonTestAndPreview/constants';
21
-
22
20
  export const apiMessageFormatHandler = (id, fallback) => (
23
21
  <FormattedMessage id={id} defaultMessage={fallback} />
24
22
  );
@@ -532,11 +530,3 @@ export const checkForPersonalizationTokens = (formData) => {
532
530
  }
533
531
  return false;
534
532
  };
535
-
536
- export const isValidEmail = (email) => EMAIL_REGEX.test(email);
537
- export const isValidMobile = (mobile) => PHONE_REGEX.test(mobile);
538
-
539
- export const formatPhoneNumber = (phone) => {
540
- if (!phone) return '';
541
- return String(phone).replace(/[^\d]/g, '');
542
- };
@@ -8,11 +8,6 @@ import {
8
8
  validateCarouselCards,
9
9
  hasPersonalizationTags,
10
10
  checkForPersonalizationTokens,
11
- isValidEmail,
12
- isValidMobile,
13
- formatPhoneNumber,
14
- getMessageForDevice,
15
- getTitleForDevice,
16
11
  } from "../commonUtils";
17
12
  import { skipTags } from "../tagValidations";
18
13
  import { SMS_TRAI_VAR } from '../../v2Containers/SmsTrai/Edit/constants';
@@ -634,170 +629,6 @@ describe("validateLiquidTemplateContent", () => {
634
629
  });
635
630
  });
636
631
 
637
- describe("isValidEmail", () => {
638
- it("returns true for valid email addresses", () => {
639
- expect(isValidEmail("user@example.com")).toBe(true);
640
- expect(isValidEmail("test.user@domain.co")).toBe(true);
641
- expect(isValidEmail("a@b.co")).toBe(true);
642
- });
643
-
644
- it("returns false for invalid email addresses", () => {
645
- expect(isValidEmail("")).toBe(false);
646
- expect(isValidEmail("invalid")).toBe(false);
647
- expect(isValidEmail("@nodomain.com")).toBe(false);
648
- expect(isValidEmail("noatsign.com")).toBe(false);
649
- expect(isValidEmail("missingtld@domain")).toBe(false);
650
- expect(isValidEmail(" user@example.com ")).toBe(false);
651
- });
652
-
653
- it("returns true for edge case single-char local part", () => {
654
- expect(isValidEmail("x@y.co")).toBe(true);
655
- });
656
- });
657
-
658
- describe("isValidMobile", () => {
659
- it("returns true for valid mobile numbers (8-15 digits, no leading zero)", () => {
660
- expect(isValidMobile("12345678")).toBe(true);
661
- expect(isValidMobile("9123456789")).toBe(true);
662
- expect(isValidMobile("123456789012345")).toBe(true);
663
- });
664
-
665
- it("returns false for invalid mobile numbers", () => {
666
- expect(isValidMobile("")).toBe(false);
667
- expect(isValidMobile("01234567")).toBe(false);
668
- expect(isValidMobile("1234567")).toBe(false);
669
- expect(isValidMobile("1234567890123456")).toBe(false);
670
- expect(isValidMobile("abc12345678")).toBe(false);
671
- expect(isValidMobile("12345 67890")).toBe(false);
672
- });
673
-
674
- it("returns true for exactly 8 and exactly 15 digits", () => {
675
- expect(isValidMobile("12345678")).toBe(true);
676
- expect(isValidMobile("123456789012345")).toBe(true);
677
- });
678
- });
679
-
680
- describe("formatPhoneNumber", () => {
681
- it("returns empty string for falsy input", () => {
682
- expect(formatPhoneNumber("")).toBe("");
683
- expect(formatPhoneNumber(null)).toBe("");
684
- expect(formatPhoneNumber(undefined)).toBe("");
685
- });
686
-
687
- it("strips non-digit characters", () => {
688
- expect(formatPhoneNumber("91 234 567 890")).toBe("91234567890");
689
- expect(formatPhoneNumber("+91-234567890")).toBe("91234567890");
690
- expect(formatPhoneNumber("(123) 456-7890")).toBe("1234567890");
691
- });
692
-
693
- it("returns digits-only string unchanged", () => {
694
- expect(formatPhoneNumber("9123456789")).toBe("9123456789");
695
- });
696
-
697
- it("returns empty string for whitespace-only input", () => {
698
- expect(formatPhoneNumber(" ")).toBe("");
699
- });
700
- });
701
-
702
- describe("hasPersonalizationTags", () => {
703
- it("returns true when text has liquid tags {{ }}", () => {
704
- expect(hasPersonalizationTags("Hello {{name}}")).toBe(true);
705
- expect(hasPersonalizationTags("{{foo}}")).toBe(true);
706
- });
707
-
708
- it("returns true when text has bracket tags [ ]", () => {
709
- expect(hasPersonalizationTags("Hello [event.name]")).toBe(true);
710
- expect(hasPersonalizationTags("[tag]")).toBe(true);
711
- });
712
-
713
- it("returns false for empty or no tags", () => {
714
- expect(hasPersonalizationTags("")).toBeFalsy();
715
- expect(hasPersonalizationTags()).toBeFalsy();
716
- expect(hasPersonalizationTags("plain text")).toBe(false);
717
- expect(hasPersonalizationTags("only {{ open")).toBe(false);
718
- });
719
-
720
- it("returns false when only [ or only ] present without matching pair", () => {
721
- expect(hasPersonalizationTags("only [ open")).toBe(false);
722
- expect(hasPersonalizationTags("only ] close")).toBe(false);
723
- });
724
-
725
- it("returns true when liquid tags have content with spaces", () => {
726
- expect(hasPersonalizationTags("Hello {{ customer.name }}")).toBe(true);
727
- });
728
- });
729
-
730
- describe("getMessageForDevice", () => {
731
- it("returns message for device from templateData", () => {
732
- const templateData = {
733
- versions: {
734
- android: { base: { expandableDetails: { message: "Android msg" } } },
735
- ios: { base: { expandableDetails: { message: "iOS msg" } } },
736
- },
737
- };
738
- expect(getMessageForDevice(templateData, "android")).toBe("Android msg");
739
- expect(getMessageForDevice(templateData, "ios")).toBe("iOS msg");
740
- });
741
-
742
- it("returns undefined for missing path", () => {
743
- expect(getMessageForDevice(null, "android")).toBeUndefined();
744
- expect(getMessageForDevice({}, "android")).toBeUndefined();
745
- expect(getMessageForDevice({ versions: {} }, "android")).toBeUndefined();
746
- });
747
-
748
- it("returns undefined when base exists but expandableDetails is missing", () => {
749
- expect(getMessageForDevice({ versions: { android: { base: {} } } }, "android")).toBeUndefined();
750
- });
751
- });
752
-
753
- describe("getTitleForDevice", () => {
754
- it("returns title for device from templateData", () => {
755
- const templateData = {
756
- versions: {
757
- android: { base: { title: "Android title" } },
758
- ios: { base: { title: "iOS title" } },
759
- },
760
- };
761
- expect(getTitleForDevice(templateData, "android")).toBe("Android title");
762
- expect(getTitleForDevice(templateData, "ios")).toBe("iOS title");
763
- });
764
-
765
- it("returns empty string for missing title", () => {
766
- expect(getTitleForDevice(null, "android")).toBe("");
767
- expect(getTitleForDevice({}, "android")).toBe("");
768
- expect(getTitleForDevice({ versions: { android: { base: {} } } }, "android")).toBe("");
769
- });
770
-
771
- it("returns empty string when base.title is undefined", () => {
772
- expect(getTitleForDevice({ versions: { android: { base: { title: undefined } } } }, "android")).toBe("");
773
- });
774
- });
775
-
776
- describe("checkForPersonalizationTokens", () => {
777
- it("returns true when formData contains liquid or bracket tokens", () => {
778
- expect(checkForPersonalizationTokens({ 0: { content: "Hi {{name}}" } })).toBe(true);
779
- expect(checkForPersonalizationTokens({ tab1: { message: "Hello [event.id]" } })).toBe(true);
780
- });
781
-
782
- it("returns false for empty or no tokens", () => {
783
- expect(checkForPersonalizationTokens(null)).toBe(false);
784
- expect(checkForPersonalizationTokens({})).toBe(false);
785
- expect(checkForPersonalizationTokens({ 0: { content: "plain" } })).toBe(false);
786
- });
787
-
788
- it("returns false for non-object formData", () => {
789
- expect(checkForPersonalizationTokens("string")).toBe(false);
790
- });
791
-
792
- it("returns true for two-level nested object containing token", () => {
793
- expect(checkForPersonalizationTokens({ tab: { body: "Hi {{name}}" } })).toBe(true);
794
- });
795
-
796
- it("returns false for formData with array value (no string tokens)", () => {
797
- expect(checkForPersonalizationTokens({ 0: { items: ["a", "b"] } })).toBe(false);
798
- });
799
- });
800
-
801
632
  describe("validateMobilePushContent", () => {
802
633
  const formatMessage = jest.fn(msg => msg.id);
803
634
  const messages = {
@@ -24,9 +24,6 @@ const SendTestMessage = ({
24
24
  channel,
25
25
  isSendingTestMessage,
26
26
  formatMessage,
27
- renderAddTestCustomerButton,
28
- searchValue,
29
- setSearchValue,
30
27
  deliverySettings,
31
28
  senderDetailsOptions,
32
29
  wecrmAccounts,
@@ -34,77 +31,57 @@ const SendTestMessage = ({
34
31
  isLoadingSenderDetails,
35
32
  smsTraiDltEnabled,
36
33
  registeredSenderIds,
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} />}
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)}
51
57
  />
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}
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
+ }
75
74
  />
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
- };
75
+ )}
76
+ <CapButton onClick={handleSendTestMessage} disabled={isEmpty(selectedTestEntities) || isSendingTestMessage}>
77
+ <FormattedMessage {...messages.sendTestButton} />
78
+ </CapButton>
79
+ </CapRow>),
80
+ key: 1,
81
+ },
82
+ ]}
83
+ />
84
+ );
108
85
 
109
86
  SendTestMessage.propTypes = {
110
87
  isFetchingTestCustomers: PropTypes.bool.isRequired,
@@ -117,9 +94,6 @@ SendTestMessage.propTypes = {
117
94
  channel: PropTypes.string,
118
95
  isSendingTestMessage: PropTypes.bool.isRequired,
119
96
  formatMessage: PropTypes.func.isRequired,
120
- renderAddTestCustomerButton: PropTypes.func,
121
- searchValue: PropTypes.string,
122
- setSearchValue: PropTypes.func,
123
97
  deliverySettings: PropTypes.object,
124
98
  senderDetailsOptions: PropTypes.array,
125
99
  wecrmAccounts: PropTypes.array,
@@ -132,9 +106,6 @@ SendTestMessage.propTypes = {
132
106
  SendTestMessage.defaultProps = {
133
107
  formData: undefined,
134
108
  channel: undefined,
135
- renderAddTestCustomerButton: undefined,
136
- searchValue: '',
137
- setSearchValue: () => {}, // no-op when not provided; required by TreeSelect when showSearch is true
138
109
  deliverySettings: {},
139
110
  senderDetailsOptions: [],
140
111
  wecrmAccounts: [],