@capillarytech/creatives-library 8.0.329 → 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.
Files changed (122) hide show
  1. package/constants/unified.js +0 -14
  2. package/package.json +1 -1
  3. package/services/api.js +0 -17
  4. package/services/tests/api.test.js +0 -85
  5. package/utils/commonUtils.js +0 -10
  6. package/utils/tests/commonUtil.test.js +0 -169
  7. package/v2Components/CapTagList/index.js +0 -10
  8. package/v2Components/CommonTestAndPreview/CustomValuesEditor.js +49 -70
  9. package/v2Components/CommonTestAndPreview/DeliverySettings/DeliverySettings.scss +2 -8
  10. package/v2Components/CommonTestAndPreview/DeliverySettings/ModifyDeliverySettings.js +21 -207
  11. package/v2Components/CommonTestAndPreview/DeliverySettings/constants.js +0 -16
  12. package/v2Components/CommonTestAndPreview/DeliverySettings/index.js +10 -85
  13. package/v2Components/CommonTestAndPreview/DeliverySettings/messages.js +0 -30
  14. package/v2Components/CommonTestAndPreview/DeliverySettings/utils/parseSenderDetailsResponse.js +11 -79
  15. package/v2Components/CommonTestAndPreview/SendTestMessage.js +53 -87
  16. package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +1 -20
  17. package/v2Components/CommonTestAndPreview/UnifiedPreview/index.js +4 -133
  18. package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +34 -145
  19. package/v2Components/CommonTestAndPreview/actions.js +0 -10
  20. package/v2Components/CommonTestAndPreview/constants.js +1 -53
  21. package/v2Components/CommonTestAndPreview/index.js +168 -1006
  22. package/v2Components/CommonTestAndPreview/messages.js +3 -147
  23. package/v2Components/CommonTestAndPreview/reducer.js +0 -10
  24. package/v2Components/CommonTestAndPreview/sagas.js +6 -15
  25. package/v2Components/CommonTestAndPreview/tests/CustomValuesEditor.test.js +286 -328
  26. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/ModifyDeliverySettings.test.js +65 -231
  27. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/index.test.js +5 -118
  28. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/utils/parseSenderDetailsResponse.test.js +0 -341
  29. package/v2Components/CommonTestAndPreview/tests/SendTestMessage.test.js +24 -65
  30. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/index.test.js +1 -199
  31. package/v2Components/CommonTestAndPreview/tests/constants.test.js +1 -31
  32. package/v2Components/CommonTestAndPreview/tests/index.test.js +4 -168
  33. package/v2Components/CommonTestAndPreview/tests/reducer.test.js +0 -71
  34. package/v2Components/CommonTestAndPreview/tests/sagas.test.js +2 -2
  35. package/v2Components/CommonTestAndPreview/tests/selectors.test.js +0 -17
  36. package/v2Components/FormBuilder/index.js +1 -7
  37. package/v2Components/TestAndPreviewSlidebox/index.js +1 -8
  38. package/v2Components/TestAndPreviewSlidebox/sagas.js +4 -11
  39. package/v2Components/TestAndPreviewSlidebox/tests/saga.test.js +1 -3
  40. package/v2Containers/CreativesContainer/SlideBoxContent.js +4 -36
  41. package/v2Containers/CreativesContainer/SlideBoxFooter.js +1 -10
  42. package/v2Containers/CreativesContainer/SlideBoxHeader.js +4 -29
  43. package/v2Containers/CreativesContainer/constants.js +0 -9
  44. package/v2Containers/CreativesContainer/index.js +93 -286
  45. package/v2Containers/CreativesContainer/index.scss +1 -51
  46. package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +34 -78
  47. package/v2Containers/CreativesContainer/tests/SlideBoxHeader.test.js +16 -79
  48. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +0 -8
  49. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxHeader.test.js.snap +98 -357
  50. package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +10 -20
  51. package/v2Containers/CreativesContainer/tests/index.test.js +9 -71
  52. package/v2Containers/Rcs/constants.js +1 -34
  53. package/v2Containers/Rcs/index.js +884 -999
  54. package/v2Containers/Rcs/index.scss +6 -85
  55. package/v2Containers/Rcs/messages.js +1 -10
  56. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +2453 -41456
  57. package/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap +5 -0
  58. package/v2Containers/Rcs/tests/index.test.js +38 -41
  59. package/v2Containers/Rcs/tests/mockData.js +0 -38
  60. package/v2Containers/Rcs/tests/utils.test.js +1 -379
  61. package/v2Containers/Rcs/utils.js +10 -358
  62. package/v2Containers/Sms/Create/index.js +38 -100
  63. package/v2Containers/SmsTrai/Create/index.js +4 -9
  64. package/v2Containers/SmsTrai/Edit/constants.js +0 -2
  65. package/v2Containers/SmsTrai/Edit/index.js +128 -609
  66. package/v2Containers/SmsTrai/Edit/messages.js +4 -9
  67. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +2600 -4586
  68. package/v2Containers/SmsWrapper/index.js +8 -37
  69. package/v2Containers/TagList/index.js +0 -6
  70. package/v2Containers/Templates/_templates.scss +2 -63
  71. package/v2Containers/Templates/actions.js +0 -11
  72. package/v2Containers/Templates/constants.js +0 -2
  73. package/v2Containers/Templates/index.js +40 -90
  74. package/v2Containers/Templates/sagas.js +12 -57
  75. package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +1079 -1043
  76. package/v2Containers/Templates/tests/sagas.test.js +123 -193
  77. package/v2Containers/TemplatesV2/TemplatesV2.style.js +1 -72
  78. package/v2Containers/TemplatesV2/index.js +23 -86
  79. package/v2Containers/Whatsapp/index.js +20 -3
  80. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +4872 -5790
  81. package/utils/templateVarUtils.js +0 -172
  82. package/utils/tests/templateVarUtils.test.js +0 -160
  83. package/v2Components/CommonTestAndPreview/AddTestCustomer.js +0 -42
  84. package/v2Components/CommonTestAndPreview/CustomerCreationModal.js +0 -155
  85. package/v2Components/CommonTestAndPreview/ExistingCustomerModal.js +0 -93
  86. package/v2Components/CommonTestAndPreview/previewApiUtils.js +0 -59
  87. package/v2Components/CommonTestAndPreview/tests/AddTestCustomer.test.js +0 -66
  88. package/v2Components/CommonTestAndPreview/tests/CommonTestAndPreview.addTestCustomer.test.js +0 -648
  89. package/v2Components/CommonTestAndPreview/tests/CustomerCreationModal.test.js +0 -174
  90. package/v2Components/CommonTestAndPreview/tests/ExistingCustomerModal.test.js +0 -114
  91. package/v2Components/CommonTestAndPreview/tests/previewApiUtils.test.js +0 -67
  92. package/v2Components/SmsFallback/SmsFallbackLocalSelector.js +0 -87
  93. package/v2Components/SmsFallback/constants.js +0 -73
  94. package/v2Components/SmsFallback/index.js +0 -955
  95. package/v2Components/SmsFallback/index.scss +0 -265
  96. package/v2Components/SmsFallback/messages.js +0 -78
  97. package/v2Components/SmsFallback/smsFallbackUtils.js +0 -107
  98. package/v2Components/SmsFallback/tests/SmsFallbackLocalSelector.test.js +0 -50
  99. package/v2Components/SmsFallback/tests/rcsSmsFallback.acceptance.test.js +0 -147
  100. package/v2Components/SmsFallback/tests/smsFallbackHandlers.test.js +0 -304
  101. package/v2Components/SmsFallback/tests/smsFallbackUi.test.js +0 -197
  102. package/v2Components/SmsFallback/tests/smsFallbackUtils.test.js +0 -261
  103. package/v2Components/SmsFallback/tests/useLocalTemplateList.test.js +0 -422
  104. package/v2Components/SmsFallback/useLocalTemplateList.js +0 -92
  105. package/v2Components/VarSegmentMessageEditor/constants.js +0 -2
  106. package/v2Components/VarSegmentMessageEditor/index.js +0 -125
  107. package/v2Components/VarSegmentMessageEditor/index.scss +0 -46
  108. package/v2Containers/CreativesContainer/CreativesSlideBoxWrapper.js +0 -43
  109. package/v2Containers/CreativesContainer/embeddedSlideboxUtils.js +0 -67
  110. package/v2Containers/CreativesContainer/tests/SlideBoxContent.localTemplates.test.js +0 -90
  111. package/v2Containers/CreativesContainer/tests/embeddedSlideboxUtils.test.js +0 -258
  112. package/v2Containers/CreativesContainer/tests/useLocalTemplatesProp.test.js +0 -125
  113. package/v2Containers/Rcs/rcsLibraryHydrationUtils.js +0 -205
  114. package/v2Containers/Rcs/tests/rcsLibraryHydrationUtils.test.js +0 -251
  115. package/v2Containers/Sms/smsFormDataHelpers.js +0 -67
  116. package/v2Containers/Sms/tests/smsFormDataHelpers.test.js +0 -253
  117. package/v2Containers/SmsTrai/Edit/index.scss +0 -121
  118. package/v2Containers/Templates/TemplatesActionBar.js +0 -101
  119. package/v2Containers/Templates/tests/TemplatesActionBar.test.js +0 -120
  120. package/v2Containers/Templates/tests/smsTemplatesListApi.test.js +0 -180
  121. package/v2Containers/Templates/utils/smsTemplatesListApi.js +0 -79
  122. package/v2Containers/TemplatesV2/tests/TemplatesV2.localTemplates.test.js +0 -131
@@ -159,20 +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
- /** Global with capture group: inner name inside `{{name}}`. */
172
- export const MUSTACHE_VAR_NAME_CAPTURE_REGEX = /\{\{([^}]+)\}\}/g;
173
- /** Global with capture group: inner body inside `{#body#}`. */
174
- export const DLT_VAR_BODY_CAPTURE_REGEX = /\{\#([^#]*)\#\}/g;
175
-
176
162
  export const GET_TRANSLATION_MAPPED = {
177
163
  'en': 'en-US',
178
164
  'zh-cn': 'zh',
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@capillarytech/creatives-library",
3
3
  "author": "meharaj",
4
- "version": "8.0.329",
4
+ "version": "8.0.330",
5
5
  "description": "Capillary creatives ui",
6
6
  "main": "./index.js",
7
7
  "module": "./index.es.js",
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
- });
@@ -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 = {
@@ -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
- sections,
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
- {(sections || []).filter((tagsSection) =>
83
- (tagsSection?.requiredTags?.length || 0) + (tagsSection?.optionalTags?.length || 0) > 0).map((section) => (
84
- <React.Fragment key={section.key || section.title?.id || section.title || CUSTOM_VALUES_EDITOR_SECTION_FALLBACK_KEY}>
85
- {section.title ? (
86
- <CapLabel type="label2" className="tags-section-title">
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
- ) : null}
90
- <CapRow className="values-table">
91
- <CapRow className="table-header">
92
- <CapLabel type="label31" className="header-cell">
93
- <FormattedMessage {...messages.personalizationTags} />
94
- </CapLabel>
95
- <CapLabel type="label31" className="header-cell">
96
- <FormattedMessage {...messages.customValues} />
97
- </CapLabel>
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
- {(section?.optionalTags || []).map((tag, tagIndex) => {
122
- const personalizationTagColumnText = getPersonalizationTagColumnLabel(tag);
123
- return (
124
- <CapRow key={tag?.fullPath ?? `optional-${tagIndex}`} className="value-row">
125
- <CapRow className="tag-name">
126
- {personalizationTagColumnText}
127
- </CapRow>
128
- <CapRow className="tag-input">
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
- </CapRow>
142
- </React.Fragment>
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
- sections: PropTypes.arrayOf(PropTypes.shape({
178
- key: PropTypes.string,
179
- title: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
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
- &__summary-value {
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 {