@capillarytech/creatives-library 8.0.330-alpha.0 → 8.0.330
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/constants/unified.js +0 -18
- package/package.json +1 -1
- package/services/api.js +0 -17
- package/services/tests/api.test.js +0 -85
- package/utils/commonUtils.js +0 -28
- package/utils/tests/commonUtil.test.js +0 -169
- package/v2Components/CapTagList/index.js +0 -10
- package/v2Components/CommonTestAndPreview/CustomValuesEditor.js +49 -70
- package/v2Components/CommonTestAndPreview/DeliverySettings/DeliverySettings.scss +2 -8
- package/v2Components/CommonTestAndPreview/DeliverySettings/ModifyDeliverySettings.js +21 -207
- package/v2Components/CommonTestAndPreview/DeliverySettings/constants.js +0 -16
- package/v2Components/CommonTestAndPreview/DeliverySettings/index.js +10 -85
- package/v2Components/CommonTestAndPreview/DeliverySettings/messages.js +0 -30
- package/v2Components/CommonTestAndPreview/DeliverySettings/utils/parseSenderDetailsResponse.js +11 -79
- package/v2Components/CommonTestAndPreview/SendTestMessage.js +53 -87
- package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +1 -20
- package/v2Components/CommonTestAndPreview/UnifiedPreview/index.js +4 -133
- package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +34 -145
- package/v2Components/CommonTestAndPreview/actions.js +0 -10
- package/v2Components/CommonTestAndPreview/constants.js +1 -53
- package/v2Components/CommonTestAndPreview/index.js +168 -998
- package/v2Components/CommonTestAndPreview/messages.js +3 -147
- package/v2Components/CommonTestAndPreview/reducer.js +0 -10
- package/v2Components/CommonTestAndPreview/sagas.js +6 -15
- package/v2Components/CommonTestAndPreview/tests/CustomValuesEditor.test.js +286 -328
- package/v2Components/CommonTestAndPreview/tests/DeliverySettings/ModifyDeliverySettings.test.js +65 -231
- package/v2Components/CommonTestAndPreview/tests/DeliverySettings/index.test.js +5 -118
- package/v2Components/CommonTestAndPreview/tests/DeliverySettings/utils/parseSenderDetailsResponse.test.js +0 -341
- package/v2Components/CommonTestAndPreview/tests/SendTestMessage.test.js +24 -65
- package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/index.test.js +1 -199
- package/v2Components/CommonTestAndPreview/tests/constants.test.js +1 -31
- package/v2Components/CommonTestAndPreview/tests/index.test.js +4 -168
- package/v2Components/CommonTestAndPreview/tests/reducer.test.js +0 -71
- package/v2Components/CommonTestAndPreview/tests/sagas.test.js +2 -2
- package/v2Components/CommonTestAndPreview/tests/selectors.test.js +0 -17
- package/v2Components/FormBuilder/index.js +1 -7
- package/v2Components/TestAndPreviewSlidebox/index.js +1 -13
- package/v2Components/TestAndPreviewSlidebox/sagas.js +4 -11
- package/v2Components/TestAndPreviewSlidebox/tests/saga.test.js +1 -3
- package/v2Containers/CreativesContainer/SlideBoxContent.js +4 -36
- package/v2Containers/CreativesContainer/SlideBoxFooter.js +1 -10
- package/v2Containers/CreativesContainer/SlideBoxHeader.js +4 -29
- package/v2Containers/CreativesContainer/constants.js +0 -9
- package/v2Containers/CreativesContainer/index.js +93 -292
- package/v2Containers/CreativesContainer/index.scss +1 -51
- package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +34 -78
- package/v2Containers/CreativesContainer/tests/SlideBoxHeader.test.js +16 -79
- package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +0 -8
- package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxHeader.test.js.snap +98 -357
- package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +10 -20
- package/v2Containers/CreativesContainer/tests/index.test.js +9 -71
- package/v2Containers/Rcs/constants.js +3 -40
- package/v2Containers/Rcs/index.js +895 -1145
- package/v2Containers/Rcs/index.scss +6 -85
- package/v2Containers/Rcs/messages.js +2 -12
- package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +2236 -41719
- package/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap +5 -0
- package/v2Containers/Rcs/tests/index.test.js +38 -41
- package/v2Containers/Rcs/tests/mockData.js +0 -38
- package/v2Containers/Rcs/tests/utils.test.js +1 -435
- package/v2Containers/Rcs/utils.js +10 -405
- package/v2Containers/Sms/Create/index.js +38 -100
- package/v2Containers/SmsTrai/Create/index.js +4 -9
- package/v2Containers/SmsTrai/Edit/constants.js +0 -2
- package/v2Containers/SmsTrai/Edit/index.js +128 -636
- package/v2Containers/SmsTrai/Edit/messages.js +4 -14
- package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +2604 -4590
- package/v2Containers/SmsWrapper/index.js +8 -37
- package/v2Containers/TagList/index.js +0 -6
- package/v2Containers/Templates/_templates.scss +2 -63
- package/v2Containers/Templates/actions.js +0 -11
- package/v2Containers/Templates/constants.js +0 -2
- package/v2Containers/Templates/index.js +40 -90
- package/v2Containers/Templates/sagas.js +12 -57
- package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +1079 -1043
- package/v2Containers/Templates/tests/sagas.test.js +123 -193
- package/v2Containers/TemplatesV2/TemplatesV2.style.js +1 -72
- package/v2Containers/TemplatesV2/index.js +23 -86
- package/v2Containers/Whatsapp/index.js +20 -3
- package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +4872 -5790
- package/utils/templateVarUtils.js +0 -201
- package/utils/tests/templateVarUtils.test.js +0 -204
- package/v2Components/CommonTestAndPreview/AddTestCustomer.js +0 -42
- package/v2Components/CommonTestAndPreview/CustomerCreationModal.js +0 -155
- package/v2Components/CommonTestAndPreview/ExistingCustomerModal.js +0 -93
- package/v2Components/CommonTestAndPreview/previewApiUtils.js +0 -59
- package/v2Components/CommonTestAndPreview/tests/AddTestCustomer.test.js +0 -66
- package/v2Components/CommonTestAndPreview/tests/CommonTestAndPreview.addTestCustomer.test.js +0 -648
- package/v2Components/CommonTestAndPreview/tests/CustomerCreationModal.test.js +0 -174
- package/v2Components/CommonTestAndPreview/tests/ExistingCustomerModal.test.js +0 -114
- package/v2Components/CommonTestAndPreview/tests/previewApiUtils.test.js +0 -67
- package/v2Components/SmsFallback/SmsFallbackLocalSelector.js +0 -87
- package/v2Components/SmsFallback/constants.js +0 -73
- package/v2Components/SmsFallback/index.js +0 -955
- package/v2Components/SmsFallback/index.scss +0 -265
- package/v2Components/SmsFallback/messages.js +0 -78
- package/v2Components/SmsFallback/smsFallbackUtils.js +0 -118
- package/v2Components/SmsFallback/tests/SmsFallbackLocalSelector.test.js +0 -50
- package/v2Components/SmsFallback/tests/rcsSmsFallback.acceptance.test.js +0 -147
- package/v2Components/SmsFallback/tests/smsFallbackHandlers.test.js +0 -304
- package/v2Components/SmsFallback/tests/smsFallbackUi.test.js +0 -197
- package/v2Components/SmsFallback/tests/smsFallbackUtils.test.js +0 -277
- package/v2Components/SmsFallback/tests/useLocalTemplateList.test.js +0 -422
- package/v2Components/SmsFallback/useLocalTemplateList.js +0 -92
- package/v2Components/VarSegmentMessageEditor/constants.js +0 -2
- package/v2Components/VarSegmentMessageEditor/index.js +0 -125
- package/v2Components/VarSegmentMessageEditor/index.scss +0 -46
- package/v2Containers/CreativesContainer/CreativesSlideBoxWrapper.js +0 -43
- package/v2Containers/CreativesContainer/embeddedSlideboxUtils.js +0 -67
- package/v2Containers/CreativesContainer/tests/SlideBoxContent.localTemplates.test.js +0 -90
- package/v2Containers/CreativesContainer/tests/embeddedSlideboxUtils.test.js +0 -258
- package/v2Containers/CreativesContainer/tests/useLocalTemplatesProp.test.js +0 -125
- package/v2Containers/Rcs/rcsLibraryHydrationUtils.js +0 -225
- package/v2Containers/Rcs/tests/rcsLibraryHydrationUtils.test.js +0 -318
- package/v2Containers/Sms/smsFormDataHelpers.js +0 -67
- package/v2Containers/Sms/tests/smsFormDataHelpers.test.js +0 -253
- package/v2Containers/SmsTrai/Edit/index.scss +0 -121
- package/v2Containers/Templates/TemplatesActionBar.js +0 -101
- package/v2Containers/Templates/tests/TemplatesActionBar.test.js +0 -120
- package/v2Containers/Templates/tests/smsTemplatesListApi.test.js +0 -180
- package/v2Containers/Templates/utils/smsTemplatesListApi.js +0 -79
- package/v2Containers/TemplatesV2/tests/TemplatesV2.localTemplates.test.js +0 -131
package/constants/unified.js
CHANGED
|
@@ -159,24 +159,6 @@ export const TAG_CONTENT_REGEX = /{{([^}]+)}}/g;
|
|
|
159
159
|
export const ENTRY_TRIGGER_TAG_REGEX = /\bentryTrigger\.\w+(?:\.\w+)?(?:\(\w+\))?/g;
|
|
160
160
|
export const SKIP_TAGS_REGEX_GROUPS = ["dynamic_expiry_date_after_\\d+_days.FORMAT_\\d", "unsubscribe\\(#[a-zA-Z\\d]{6}\\)", "Link_to_[a-zA-Z]", "SURVEY.*.TOKEN", "^[A-Za-z].*\\([a-zA-Z\\d]*\\)", "referral_unique_(code|url).*userid"];
|
|
161
161
|
|
|
162
|
-
// --- Template variable tokens (`{{var}}`, DLT `{#var#}`) ---
|
|
163
|
-
/** Global: all `{{…}}` placeholders in a string. */
|
|
164
|
-
export const DEFAULT_MUSTACHE_VAR_REGEX = /\{\{[^}]+\}\}/g;
|
|
165
|
-
/** Global: `{{…}}` or DLT `{#…#}` tokens (SMS combined mode). */
|
|
166
|
-
export const COMBINED_SMS_TEMPLATE_VAR_REGEX = /\{\{[^}]+\}\}|\{\#[^#]*\#\}/g;
|
|
167
|
-
/** Full-string check: one mustache token. */
|
|
168
|
-
export const MUSTACHE_VAR_TOKEN_FULL_STRING_REGEX = /^\{\{[^}]+\}\}$/;
|
|
169
|
-
/** Full-string check: one DLT hash token. */
|
|
170
|
-
export const DLT_HASH_VAR_TOKEN_FULL_STRING_REGEX = /^\{\#[^#]*\#\}$/;
|
|
171
|
-
/** Full-string with capture group: inner name for `{{ name }}`-style tokens (whitespace-tolerant). */
|
|
172
|
-
export const MUSTACHE_TOKEN_INNER_NAME_REGEX = /^\{\{\s*([^}]+?)\s*\}\}$/;
|
|
173
|
-
/** Full-string with capture group: inner name/body for `{# name #}` DLT tokens (whitespace-tolerant). */
|
|
174
|
-
export const DLT_HASH_TOKEN_INNER_NAME_REGEX = /^\{#\s*(.*?)\s*#\}$/;
|
|
175
|
-
/** Global with capture group: inner name inside `{{name}}`. */
|
|
176
|
-
export const MUSTACHE_VAR_NAME_CAPTURE_REGEX = /\{\{([^}]+)\}\}/g;
|
|
177
|
-
/** Global with capture group: inner body inside `{#body#}`. */
|
|
178
|
-
export const DLT_VAR_BODY_CAPTURE_REGEX = /\{\#([^#]*)\#\}/g;
|
|
179
|
-
|
|
180
162
|
export const GET_TRANSLATION_MAPPED = {
|
|
181
163
|
'en': 'en-US',
|
|
182
164
|
'zh-cn': 'zh',
|
package/package.json
CHANGED
package/services/api.js
CHANGED
|
@@ -741,21 +741,4 @@ export const getBeePopupBuilderToken = () => {
|
|
|
741
741
|
return request(url, getAPICallObject('GET'));
|
|
742
742
|
};
|
|
743
743
|
|
|
744
|
-
/**
|
|
745
|
-
* Look up member by identifier (email or mobile) for add test customer flow.
|
|
746
|
-
* Uses standard API auth headers (no x-cap-ct) to avoid CORS preflight issues with node layer.
|
|
747
|
-
* @param {string} identifierType - 'email' or 'mobile'
|
|
748
|
-
* @param {string} identifierValue - email address or mobile number
|
|
749
|
-
* @returns {Promise} Promise resolving to { success, response: { exists, customerDetails } }
|
|
750
|
-
*/
|
|
751
|
-
export const getMembersLookup = (identifierType, identifierValue) => {
|
|
752
|
-
const url = `${API_ENDPOINT}/members?identifierType=${encodeURIComponent(identifierType)}&identifierValue=${encodeURIComponent(identifierValue)}`;
|
|
753
|
-
return request(url, getAPICallObject('GET'));
|
|
754
|
-
};
|
|
755
|
-
|
|
756
|
-
export const createTestCustomer = (payload) => {
|
|
757
|
-
const url = `${API_ENDPOINT}/testCustomers`;
|
|
758
|
-
return request(url, getAPICallObject('POST', payload));
|
|
759
|
-
};
|
|
760
|
-
|
|
761
744
|
export {request, getAPICallObject};
|
|
@@ -29,8 +29,6 @@ import {
|
|
|
29
29
|
getAssetStatus,
|
|
30
30
|
getBeePopupBuilderToken,
|
|
31
31
|
getCmsAccounts,
|
|
32
|
-
getMembersLookup,
|
|
33
|
-
createTestCustomer,
|
|
34
32
|
} from '../api';
|
|
35
33
|
import { mockData } from './mockData';
|
|
36
34
|
import getSchema from '../getSchema';
|
|
@@ -1016,86 +1014,3 @@ describe('getCmsAccounts', () => {
|
|
|
1016
1014
|
expect(result).toBeInstanceOf(Promise);
|
|
1017
1015
|
});
|
|
1018
1016
|
});
|
|
1019
|
-
|
|
1020
|
-
describe('getMembersLookup', () => {
|
|
1021
|
-
beforeEach(() => {
|
|
1022
|
-
global.fetch = jest.fn();
|
|
1023
|
-
global.fetch.mockReturnValue(Promise.resolve({
|
|
1024
|
-
status: 200,
|
|
1025
|
-
json: () => Promise.resolve({ success: true, response: { exists: false, customerDetails: [] } }),
|
|
1026
|
-
}));
|
|
1027
|
-
});
|
|
1028
|
-
|
|
1029
|
-
it('should return a Promise', () => {
|
|
1030
|
-
const result = getMembersLookup('email', 'user@example.com');
|
|
1031
|
-
expect(result).toBeInstanceOf(Promise);
|
|
1032
|
-
});
|
|
1033
|
-
|
|
1034
|
-
it('should be callable with identifierType and identifierValue', () => {
|
|
1035
|
-
expect(typeof getMembersLookup).toBe('function');
|
|
1036
|
-
const result = getMembersLookup('mobile', '9123456789');
|
|
1037
|
-
expect(result).toBeInstanceOf(Promise);
|
|
1038
|
-
});
|
|
1039
|
-
|
|
1040
|
-
it('should call fetch with correct URL encoding and GET method', () => {
|
|
1041
|
-
global.fetch.mockClear();
|
|
1042
|
-
getMembersLookup('email', 'user+test@example.com');
|
|
1043
|
-
expect(global.fetch).toHaveBeenCalled();
|
|
1044
|
-
const calls = global.fetch.mock.calls;
|
|
1045
|
-
const withEncoding = calls.find(
|
|
1046
|
-
(c) =>
|
|
1047
|
-
c[0] &&
|
|
1048
|
-
String(c[0]).includes('members') &&
|
|
1049
|
-
String(c[0]).includes('identifierType=email') &&
|
|
1050
|
-
String(c[0]).includes('user%2Btest%40example.com')
|
|
1051
|
-
);
|
|
1052
|
-
if (withEncoding) {
|
|
1053
|
-
const [url, options] = withEncoding;
|
|
1054
|
-
expect(url).toContain('identifierType=email');
|
|
1055
|
-
expect(url).toContain('identifierValue=user%2Btest%40example.com');
|
|
1056
|
-
expect(options?.method || 'GET').toBe('GET');
|
|
1057
|
-
}
|
|
1058
|
-
const anyMembersCall = calls.find((c) => c[0] && String(c[0]).includes('members'));
|
|
1059
|
-
expect(anyMembersCall).toBeDefined();
|
|
1060
|
-
expect(anyMembersCall[0]).toContain('identifierType=');
|
|
1061
|
-
expect(anyMembersCall[0]).toContain('identifierValue=');
|
|
1062
|
-
expect(anyMembersCall[1]?.method || 'GET').toBe('GET');
|
|
1063
|
-
});
|
|
1064
|
-
});
|
|
1065
|
-
|
|
1066
|
-
describe('createTestCustomer', () => {
|
|
1067
|
-
beforeEach(() => {
|
|
1068
|
-
global.fetch = jest.fn();
|
|
1069
|
-
global.fetch.mockReturnValue(Promise.resolve({
|
|
1070
|
-
status: 200,
|
|
1071
|
-
json: () => Promise.resolve({ success: true }),
|
|
1072
|
-
}));
|
|
1073
|
-
});
|
|
1074
|
-
|
|
1075
|
-
it('should return a Promise', () => {
|
|
1076
|
-
const payload = { customer: { email: 'test@example.com', firstName: 'Test' } };
|
|
1077
|
-
const result = createTestCustomer(payload);
|
|
1078
|
-
expect(result).toBeInstanceOf(Promise);
|
|
1079
|
-
});
|
|
1080
|
-
|
|
1081
|
-
it('should accept customerId payload for existing customer', () => {
|
|
1082
|
-
const payload = { customerId: 'cust-123' };
|
|
1083
|
-
const result = createTestCustomer(payload);
|
|
1084
|
-
expect(result).toBeInstanceOf(Promise);
|
|
1085
|
-
expect(global.fetch).toHaveBeenCalled();
|
|
1086
|
-
const lastCall = global.fetch.mock.calls[global.fetch.mock.calls.length - 1];
|
|
1087
|
-
expect(lastCall[0]).toContain('testCustomers');
|
|
1088
|
-
expect(lastCall[1].method).toBe('POST');
|
|
1089
|
-
expect(lastCall[1].body).toBe(JSON.stringify(payload));
|
|
1090
|
-
});
|
|
1091
|
-
|
|
1092
|
-
it('should call fetch with /testCustomers URL and POST method', () => {
|
|
1093
|
-
const payload = { customer: { email: 'n@b.co', firstName: 'N' } };
|
|
1094
|
-
createTestCustomer(payload);
|
|
1095
|
-
expect(global.fetch).toHaveBeenCalled();
|
|
1096
|
-
const lastCall = global.fetch.mock.calls[global.fetch.mock.calls.length - 1];
|
|
1097
|
-
expect(lastCall[0]).toContain('testCustomers');
|
|
1098
|
-
expect(lastCall[1].method).toBe('POST');
|
|
1099
|
-
expect(lastCall[1].body).toBe(JSON.stringify(payload));
|
|
1100
|
-
});
|
|
1101
|
-
});
|
package/utils/commonUtils.js
CHANGED
|
@@ -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,29 +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
|
-
};
|
|
543
|
-
|
|
544
|
-
/**
|
|
545
|
-
* TRAI sender IDs on persisted RCS SMS fallback: may live on the merged object, under
|
|
546
|
-
* `templateConfigs`, or (legacy) `templateConfigs.header`. Same resolution order as
|
|
547
|
-
* `createPayload` in `Rcs/index.js`.
|
|
548
|
-
*/
|
|
549
|
-
export function extractRegisteredSenderIdsFromSmsFallbackRecord(record) {
|
|
550
|
-
if (!record || typeof record !== 'object') return null;
|
|
551
|
-
const tc = record.templateConfigs && typeof record.templateConfigs === 'object'
|
|
552
|
-
? record.templateConfigs
|
|
553
|
-
: {};
|
|
554
|
-
const candidates = [record.registeredSenderIds, tc.registeredSenderIds, tc.header];
|
|
555
|
-
for (let i = 0; i < candidates.length; i += 1) {
|
|
556
|
-
const a = candidates[i];
|
|
557
|
-
if (Array.isArray(a) && a.length > 0) return a;
|
|
558
|
-
}
|
|
559
|
-
return null;
|
|
560
|
-
}
|
|
@@ -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 = {
|
|
@@ -382,9 +382,6 @@ class CapTagList extends React.Component { // eslint-disable-line react/prefer-s
|
|
|
382
382
|
render() {
|
|
383
383
|
const {
|
|
384
384
|
hidePopover = false, intl = {}, moduleFilterEnabled, label, modalProps, channel, fetchingSchemaError = false,
|
|
385
|
-
overlayStyle,
|
|
386
|
-
overlayClassName,
|
|
387
|
-
getPopupContainer,
|
|
388
385
|
} = this.props;
|
|
389
386
|
const {formatMessage} = intl;
|
|
390
387
|
const {
|
|
@@ -477,9 +474,6 @@ class CapTagList extends React.Component { // eslint-disable-line react/prefer-s
|
|
|
477
474
|
content={contentSection}
|
|
478
475
|
trigger="click"
|
|
479
476
|
placement={this.props.popoverPlacement || (channel === EMAIL.toUpperCase() ? "leftTop" : "rightTop")}
|
|
480
|
-
overlayStyle={overlayStyle}
|
|
481
|
-
overlayClassName={overlayClassName}
|
|
482
|
-
getPopupContainer={getPopupContainer}
|
|
483
477
|
>
|
|
484
478
|
<CapTooltip
|
|
485
479
|
title={
|
|
@@ -551,10 +545,6 @@ CapTagList.propTypes = {
|
|
|
551
545
|
disableTooltipMsg: PropTypes.string,
|
|
552
546
|
fetchingSchemaError: PropTypes.bool,
|
|
553
547
|
popoverPlacement: PropTypes.string,
|
|
554
|
-
overlayStyle: PropTypes.object,
|
|
555
|
-
overlayClassName: PropTypes.string,
|
|
556
|
-
/** e.g. () => document.body — avoids overflow/stacking issues inside slideboxes */
|
|
557
|
-
getPopupContainer: PropTypes.func,
|
|
558
548
|
};
|
|
559
549
|
|
|
560
550
|
CapTagList.defaultValue = {
|
|
@@ -8,7 +8,6 @@ import CapButton from '@capillarytech/cap-ui-library/CapButton';
|
|
|
8
8
|
import CapInput from '@capillarytech/cap-ui-library/CapInput';
|
|
9
9
|
import CapLabel from '@capillarytech/cap-ui-library/CapLabel';
|
|
10
10
|
import messages from './messages';
|
|
11
|
-
import { CUSTOM_VALUES_EDITOR_SECTION_FALLBACK_KEY } from './constants';
|
|
12
11
|
|
|
13
12
|
const CustomValuesEditor = ({
|
|
14
13
|
isExtractingTags,
|
|
@@ -17,16 +16,15 @@ const CustomValuesEditor = ({
|
|
|
17
16
|
setShowJSON,
|
|
18
17
|
customValues,
|
|
19
18
|
handleJSONTextChange,
|
|
20
|
-
|
|
19
|
+
extractedTags,
|
|
20
|
+
requiredTags,
|
|
21
|
+
optionalTags,
|
|
21
22
|
handleCustomValueChange,
|
|
22
23
|
handleDiscardCustomValues,
|
|
23
24
|
handleUpdatePreview,
|
|
24
25
|
isUpdatingPreview,
|
|
25
26
|
formatMessage,
|
|
26
27
|
}) => {
|
|
27
|
-
/** Same as SMS Test & Preview: show token path from extract-tags (fullPath or name). */
|
|
28
|
-
const getPersonalizationTagColumnLabel = (tagNode) => tagNode?.fullPath ?? tagNode?.name ?? '';
|
|
29
|
-
|
|
30
28
|
if (isExtractingTags) {
|
|
31
29
|
return (
|
|
32
30
|
<CapRow className="loading-container">
|
|
@@ -79,68 +77,52 @@ const CustomValuesEditor = ({
|
|
|
79
77
|
</CapRow>
|
|
80
78
|
) : (
|
|
81
79
|
<>
|
|
82
|
-
{
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
{typeof section.title === 'string' ? section.title : <FormattedMessage {...section.title} />}
|
|
80
|
+
{extractedTags?.length > 0 && (
|
|
81
|
+
<CapRow className="values-table">
|
|
82
|
+
<CapRow className="table-header">
|
|
83
|
+
<CapLabel type="label31" className="header-cell">
|
|
84
|
+
<FormattedMessage {...messages.personalizationTags} />
|
|
88
85
|
</CapLabel>
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
<
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
</CapRow>
|
|
99
|
-
{(section?.requiredTags || []).map((tag, tagIndex) => {
|
|
100
|
-
const personalizationTagColumnText = getPersonalizationTagColumnLabel(tag);
|
|
101
|
-
return (
|
|
102
|
-
<CapRow key={tag?.fullPath ?? `required-${tagIndex}`} className="value-row">
|
|
103
|
-
<CapRow className="tag-name">
|
|
104
|
-
{personalizationTagColumnText}
|
|
105
|
-
<span className="required-tag-indicator">*</span>
|
|
106
|
-
</CapRow>
|
|
107
|
-
<CapRow className="tag-input">
|
|
108
|
-
<CapInput
|
|
109
|
-
type="text"
|
|
110
|
-
isRequired
|
|
111
|
-
className="tag-input-field"
|
|
112
|
-
value={customValues?.[tag?.fullPath] ?? ''}
|
|
113
|
-
onChange={(e) => handleCustomValueChange(tag?.fullPath, e.target.value)}
|
|
114
|
-
placeholder={formatMessage(messages.enterValue)}
|
|
115
|
-
size="small"
|
|
116
|
-
/>
|
|
117
|
-
</CapRow>
|
|
86
|
+
<CapLabel type="label31" className="header-cell">
|
|
87
|
+
<FormattedMessage {...messages.customValues} />
|
|
88
|
+
</CapLabel>
|
|
89
|
+
</CapRow>
|
|
90
|
+
{requiredTags.map((tag) => (
|
|
91
|
+
<CapRow key={tag.fullPath} className="value-row">
|
|
92
|
+
<CapRow className="tag-name">
|
|
93
|
+
{tag.fullPath}
|
|
94
|
+
<span className="required-tag-indicator">*</span>
|
|
118
95
|
</CapRow>
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
{
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
<CapInput
|
|
130
|
-
type="text"
|
|
131
|
-
className="tag-input-field"
|
|
132
|
-
value={customValues?.[tag?.fullPath] ?? ''}
|
|
133
|
-
onChange={(e) => handleCustomValueChange(tag?.fullPath, e.target.value)}
|
|
134
|
-
placeholder={formatMessage(messages.enterValue)}
|
|
135
|
-
size="small"
|
|
136
|
-
/>
|
|
137
|
-
</CapRow>
|
|
96
|
+
<CapRow className="tag-input">
|
|
97
|
+
<CapInput
|
|
98
|
+
type="text"
|
|
99
|
+
isRequired
|
|
100
|
+
className="tag-input-field"
|
|
101
|
+
value={customValues[tag.fullPath] || ''}
|
|
102
|
+
onChange={(e) => handleCustomValueChange(tag.fullPath, e.target.value)}
|
|
103
|
+
placeholder={formatMessage(messages.enterValue)}
|
|
104
|
+
size="small"
|
|
105
|
+
/>
|
|
138
106
|
</CapRow>
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
107
|
+
</CapRow>
|
|
108
|
+
))}
|
|
109
|
+
{optionalTags?.map((tag) => (
|
|
110
|
+
<CapRow key={tag.fullPath} className="value-row">
|
|
111
|
+
<CapRow className="tag-name">{tag.fullPath}</CapRow>
|
|
112
|
+
<CapRow className="tag-input">
|
|
113
|
+
<CapInput
|
|
114
|
+
type="text"
|
|
115
|
+
className="tag-input-field"
|
|
116
|
+
value={customValues[tag.fullPath] || ''}
|
|
117
|
+
onChange={(e) => handleCustomValueChange(tag.fullPath, e.target.value)}
|
|
118
|
+
placeholder={formatMessage(messages.enterValue)}
|
|
119
|
+
size="small"
|
|
120
|
+
/>
|
|
121
|
+
</CapRow>
|
|
122
|
+
</CapRow>
|
|
123
|
+
))}
|
|
124
|
+
</CapRow>
|
|
125
|
+
)}
|
|
144
126
|
</>
|
|
145
127
|
)}
|
|
146
128
|
<CapRow className="editor-actions">
|
|
@@ -174,12 +156,9 @@ CustomValuesEditor.propTypes = {
|
|
|
174
156
|
setShowJSON: PropTypes.func.isRequired,
|
|
175
157
|
customValues: PropTypes.object.isRequired,
|
|
176
158
|
handleJSONTextChange: PropTypes.func.isRequired,
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
requiredTags: PropTypes.array,
|
|
181
|
-
optionalTags: PropTypes.array,
|
|
182
|
-
})).isRequired,
|
|
159
|
+
extractedTags: PropTypes.array.isRequired,
|
|
160
|
+
requiredTags: PropTypes.array.isRequired,
|
|
161
|
+
optionalTags: PropTypes.array.isRequired,
|
|
183
162
|
handleCustomValueChange: PropTypes.func.isRequired,
|
|
184
163
|
handleDiscardCustomValues: PropTypes.func.isRequired,
|
|
185
164
|
handleUpdatePreview: PropTypes.func.isRequired,
|
|
@@ -18,17 +18,11 @@
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
&__summary-entry {
|
|
21
|
-
display: flex;
|
|
22
|
-
align-items: baseline;
|
|
23
|
-
gap: 0;
|
|
24
21
|
margin-right: $CAP_SPACE_18;
|
|
25
22
|
}
|
|
26
23
|
|
|
27
|
-
&__summary-key
|
|
28
|
-
|
|
29
|
-
line-height: 1.4;
|
|
30
|
-
margin-top: 0;
|
|
31
|
-
margin-bottom: 0;
|
|
24
|
+
&__summary-key {
|
|
25
|
+
line-height: $CAP_SPACE_16;
|
|
32
26
|
}
|
|
33
27
|
|
|
34
28
|
&__edit-icon {
|