@capillarytech/creatives-library 8.0.328 → 8.0.329

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 (32) hide show
  1. package/package.json +1 -1
  2. package/services/api.js +17 -0
  3. package/services/tests/api.test.js +85 -0
  4. package/utils/commonUtils.js +10 -0
  5. package/utils/tagValidations.js +2 -3
  6. package/utils/tests/commonUtil.test.js +169 -0
  7. package/utils/tests/tagValidations.test.js +1 -1
  8. package/v2Components/CommonTestAndPreview/AddTestCustomer.js +42 -0
  9. package/v2Components/CommonTestAndPreview/CustomerCreationModal.js +155 -0
  10. package/v2Components/CommonTestAndPreview/ExistingCustomerModal.js +93 -0
  11. package/v2Components/CommonTestAndPreview/SendTestMessage.js +79 -51
  12. package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +134 -34
  13. package/v2Components/CommonTestAndPreview/actions.js +10 -0
  14. package/v2Components/CommonTestAndPreview/constants.js +15 -1
  15. package/v2Components/CommonTestAndPreview/index.js +315 -15
  16. package/v2Components/CommonTestAndPreview/messages.js +106 -0
  17. package/v2Components/CommonTestAndPreview/reducer.js +10 -0
  18. package/v2Components/CommonTestAndPreview/tests/AddTestCustomer.test.js +66 -0
  19. package/v2Components/CommonTestAndPreview/tests/CommonTestAndPreview.addTestCustomer.test.js +648 -0
  20. package/v2Components/CommonTestAndPreview/tests/CustomValuesEditor.test.js +24 -0
  21. package/v2Components/CommonTestAndPreview/tests/CustomerCreationModal.test.js +174 -0
  22. package/v2Components/CommonTestAndPreview/tests/ExistingCustomerModal.test.js +114 -0
  23. package/v2Components/CommonTestAndPreview/tests/SendTestMessage.test.js +52 -0
  24. package/v2Components/CommonTestAndPreview/tests/constants.test.js +31 -1
  25. package/v2Components/CommonTestAndPreview/tests/index.test.js +36 -0
  26. package/v2Components/CommonTestAndPreview/tests/reducer.test.js +71 -0
  27. package/v2Components/CommonTestAndPreview/tests/selectors.test.js +17 -0
  28. package/v2Containers/CreativesContainer/index.js +6 -7
  29. package/v2Containers/Rcs/index.js +1 -8
  30. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +1886 -1754
  31. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +351 -318
  32. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +5586 -5212
@@ -25,6 +25,9 @@ const SendTestMessage = ({
25
25
  channel,
26
26
  isSendingTestMessage,
27
27
  formatMessage,
28
+ renderAddTestCustomerButton,
29
+ searchValue,
30
+ setSearchValue,
28
31
  deliverySettings,
29
32
  senderDetailsByChannel = {},
30
33
  wecrmAccounts,
@@ -33,59 +36,78 @@ const SendTestMessage = ({
33
36
  smsTraiDltEnabled,
34
37
  registeredSenderIds,
35
38
  isChannelSmsFallbackPreviewEnabled,
36
- }) => (
37
- <CapStepsAccordian
38
- showNumberSteps={false}
39
- isChevronIcon
40
- expandIconPosition="right"
41
- items={[
42
- {
43
- header: <CapHeader
44
- size="regular"
45
- description={<FormattedMessage {...messages.testMessageDescription} />}
46
- title={<FormattedMessage {...messages.sendTestMessage} />}
47
- />,
48
- content: (
49
- <CapRow className="send-test-content">
50
- <CapHeader size="label1" title={<FormattedMessage {...messages.testCustomers} />} />
51
- <CapTreeSelect
52
- className="test-customers-tree-select"
53
- loading={isFetchingTestCustomers || isFetchingTestGroups}
54
- treeData={testEntitiesTreeData}
55
- onChange={handleTestEntitiesChange}
56
- value={selectedTestEntities}
57
- multiple
58
- placeholder={formatMessage(messages.testCustomersPlaceholder)}
59
- getPopupContainer={(triggerNode) => triggerNode.parentNode}
39
+ }) => {
40
+ const addCustomerContent = renderAddTestCustomerButton ? renderAddTestCustomerButton() : null;
41
+ return (
42
+ <CapStepsAccordian
43
+ showNumberSteps={false}
44
+ isChevronIcon
45
+ expandIconPosition="right"
46
+ items={[
47
+ {
48
+ header: (
49
+ <CapHeader
50
+ size="regular"
51
+ description={<FormattedMessage {...messages.testMessageDescription} />}
52
+ title={<FormattedMessage {...messages.sendTestMessage} />}
60
53
  />
61
- {CHANNELS_WITH_DELIVERY_SETTINGS.includes(channel) && (
62
- <DeliverySettings
63
- channel={channel}
64
- deliverySettings={deliverySettings || {}}
65
- senderDetailsByChannel={senderDetailsByChannel}
66
- wecrmAccounts={wecrmAccounts || []}
67
- onSaveDeliverySettings={onSaveDeliverySettings}
68
- isLoadingSenderDetails={isLoadingSenderDetails}
69
- formatMessage={formatMessage}
70
- smsTraiDltEnabled={smsTraiDltEnabled}
71
- registeredSenderIds={registeredSenderIds}
72
- isChannelSmsFallbackPreviewEnabled={isChannelSmsFallbackPreviewEnabled}
73
- whatsappAccountFromForm={
74
- channel === CHANNELS.WHATSAPP && formData?.accountName
75
- ? { accountName: formData.accountName }
76
- : undefined
77
- }
54
+ ),
55
+ content: (
56
+ <CapRow className="send-test-content">
57
+ <CapHeader size="label1" title={<FormattedMessage {...messages.testCustomers} />} />
58
+ <CapTreeSelect
59
+ className="test-customers-tree-select"
60
+ dropdownMatchSelectWidth
61
+ dropdownClassName="test-customers-tree-select-dropdown"
62
+ listHeight={280}
63
+ loading={isFetchingTestCustomers || isFetchingTestGroups}
64
+ treeData={testEntitiesTreeData}
65
+ onChange={handleTestEntitiesChange}
66
+ value={selectedTestEntities}
67
+ showSearch
68
+ searchValue={searchValue}
69
+ onSearch={setSearchValue}
70
+ treeNodeFilterProp="title"
71
+ {...(addCustomerContent ? { notFoundContent: addCustomerContent } : {})}
72
+ multiple
73
+ placeholder={formatMessage(messages.testCustomersPlaceholder)}
74
+ dropdownStyle={{ zIndex: 900, maxHeight: 320 }}
75
+ placement="bottomLeft"
76
+ getPopupContainer={(node) => node.closest('.send-test-section') || document.body}
78
77
  />
79
- )}
80
- <CapButton onClick={handleSendTestMessage} disabled={isEmpty(selectedTestEntities) || isSendingTestMessage}>
81
- <FormattedMessage {...messages.sendTestButton} />
82
- </CapButton>
83
- </CapRow>),
84
- key: 1,
85
- },
86
- ]}
87
- />
88
- );
78
+ {CHANNELS_WITH_DELIVERY_SETTINGS.includes(channel) && (
79
+ <DeliverySettings
80
+ channel={channel}
81
+ deliverySettings={deliverySettings || {}}
82
+ senderDetailsByChannel={senderDetailsByChannel}
83
+ wecrmAccounts={wecrmAccounts || []}
84
+ onSaveDeliverySettings={onSaveDeliverySettings}
85
+ isLoadingSenderDetails={isLoadingSenderDetails}
86
+ formatMessage={formatMessage}
87
+ smsTraiDltEnabled={smsTraiDltEnabled}
88
+ registeredSenderIds={registeredSenderIds}
89
+ isChannelSmsFallbackPreviewEnabled={isChannelSmsFallbackPreviewEnabled}
90
+ whatsappAccountFromForm={
91
+ channel === CHANNELS.WHATSAPP && formData?.accountName
92
+ ? { accountName: formData.accountName }
93
+ : undefined
94
+ }
95
+ />
96
+ )}
97
+ <CapButton
98
+ onClick={handleSendTestMessage}
99
+ disabled={isEmpty(selectedTestEntities) || isSendingTestMessage}
100
+ >
101
+ <FormattedMessage {...messages.sendTestButton} />
102
+ </CapButton>
103
+ </CapRow>
104
+ ),
105
+ key: 1,
106
+ },
107
+ ]}
108
+ />
109
+ );
110
+ };
89
111
 
90
112
  SendTestMessage.propTypes = {
91
113
  isFetchingTestCustomers: PropTypes.bool.isRequired,
@@ -98,6 +120,9 @@ SendTestMessage.propTypes = {
98
120
  channel: PropTypes.string,
99
121
  isSendingTestMessage: PropTypes.bool.isRequired,
100
122
  formatMessage: PropTypes.func.isRequired,
123
+ renderAddTestCustomerButton: PropTypes.func,
124
+ searchValue: PropTypes.string,
125
+ setSearchValue: PropTypes.func,
101
126
  deliverySettings: PropTypes.object,
102
127
  senderDetailsByChannel: PropTypes.object,
103
128
  wecrmAccounts: PropTypes.array,
@@ -111,6 +136,9 @@ SendTestMessage.propTypes = {
111
136
  SendTestMessage.defaultProps = {
112
137
  formData: undefined,
113
138
  channel: undefined,
139
+ renderAddTestCustomerButton: undefined,
140
+ searchValue: '',
141
+ setSearchValue: () => {}, // no-op when not provided; required by TreeSelect when showSearch is true
114
142
  deliverySettings: {},
115
143
  senderDetailsByChannel: {},
116
144
  wecrmAccounts: [],
@@ -5,9 +5,12 @@
5
5
  */
6
6
  @import '~@capillarytech/cap-ui-library/styles/_variables.scss';
7
7
 
8
- /* All ant overrides scoped under wrapper to avoid affecting other modals. */
8
+ /* All ant overrides scoped under wrapper to avoid affecting other modals. */
9
9
  .common-test-preview-modal-wrap {
10
- z-index: 10000 !important;
10
+ z-index: 10000;
11
+ display: flex;
12
+ justify-content: center;
13
+ align-items: center;
11
14
  }
12
15
 
13
16
  /* Lookup spinner overlay above test-customers dropdown. */
@@ -22,19 +25,23 @@
22
25
 
23
26
  /* When customer lookup is loading, dropdown renders inside .send-test-section; lower it so spinner is on top. */
24
27
  .common-test-preview-customer-loading .test-customers-tree-select-dropdown {
25
- z-index: 0 !important;
28
+ z-index: 0;
26
29
  }
27
30
 
28
31
  /* Customer creation modal content – avoid inline styles */
29
- .common-test-preview-modal {
30
- color: $CAP_G01 !important;
31
- .ant-modal-content {
32
- width: 28.5rem; /* 456px at 16px root */
33
- }
32
+ .common-test-preview-modal.common-test-preview-modal {
33
+ color: $CAP_G01;
34
+ width: 32.571em;
35
+ margin-left: auto;
36
+ margin-right: auto;
37
+ max-width: 32.571em;
38
+
34
39
  .ant-modal-footer {
35
40
  text-align: left;
36
- margin-left: 0.625rem; /* 10px at 16px root */
37
- margin-top: 0.9375rem; /* 15px at 16px root */
41
+ margin-left: 0.625rem;
42
+ /* 10px at 16px root */
43
+ margin-top: 0.9375rem;
44
+ /* 15px at 16px root */
38
45
  }
39
46
 
40
47
  .customer-creation-modal-row {
@@ -63,22 +70,72 @@
63
70
  font-weight: normal;
64
71
  }
65
72
 
73
+ /* Input text color and font to match standalone (lib mode can lose host styles) */
66
74
  .customer-creation-modal-input {
67
- width: 100%;
68
- color: $CAP_G01;
75
+ /* Sizes in em for 14px base: same px as .5rem 2.5rem 1.5rem .875rem at 16px (8px, 40px, 24px, 14px) */
76
+ .ant-input {
77
+ color: $CAP_G01;
78
+ font-size: $FONT_SIZE_M;
79
+ font-weight: normal;
80
+ padding: 0.571em;
81
+ /* 8px */
82
+ height: 2.857em;
83
+ /* 40px */
84
+ line-height: 1.714em;
85
+ /* 24px */
86
+ }
87
+
88
+ .ant-input:hover,
89
+ .ant-input:focus {
90
+ border: 1px solid $CAP_G01;
91
+ }
69
92
 
70
- &.has-error {
71
- color: $CAP_COLOR_03 !important;
72
- .ant-input {
73
- border-color: $CAP_COLOR_03 !important;
93
+ .ant-input::placeholder {
94
+ color: $CAP_G06;
95
+ }
96
+
97
+ /* Error state: single clean red border, no double line or focus ring */
98
+ &.has-input-error {
99
+ .ant-input-affix-wrapper {
100
+ border: 1px solid $CAP_COLOR_03;
101
+ border-radius: $CAP_SPACE_04;
102
+ }
103
+
104
+ .ant-input-affix-wrapper:hover,
105
+ .ant-input-affix-wrapper:focus,
106
+ .ant-input-affix-wrapper-focused {
107
+ border: 1px solid $CAP_COLOR_03;
108
+ }
109
+
110
+ .ant-input-affix-wrapper .ant-input {
111
+ border: none !important;
74
112
  }
75
113
  }
76
114
  }
77
115
 
116
+ /* Error message only – no extra border/line below the input */
78
117
  .customer-creation-modal-validation-error {
79
118
  color: $CAP_COLOR_03;
80
119
  font-size: $FONT_SIZE_S;
81
120
  margin-top: $CAP_SPACE_04;
121
+ border: none;
122
+ border-top: none;
123
+ box-shadow: none;
124
+
125
+ &::before,
126
+ &::after {
127
+ display: none;
128
+ content: none;
129
+ border: none;
130
+ }
131
+ }
132
+
133
+ /* Lookup loading: make Email and Mobile fields look disabled (wrapper-only) */
134
+ .customer-creation-modal--lookup-loading .customer-creation-modal-row--email .customer-creation-modal-input,
135
+ .customer-creation-modal--lookup-loading .customer-creation-modal-row--last .customer-creation-modal-input {
136
+ opacity: 0.65;
137
+ cursor: not-allowed;
138
+ background-color: $CAP_G09;
82
139
  }
83
140
 
84
141
  /* Existing customer modal content */
@@ -87,13 +144,17 @@
87
144
  margin-bottom: $CAP_SPACE_16;
88
145
  }
89
146
 
147
+ .ant-card.cap-card-v2 {
148
+ border: none
149
+ }
150
+
90
151
  .ant-card-body {
91
152
  padding: 1rem;
153
+ border-radius: $CAP_SPACE_08;
154
+ border: $CAP_SPACE_01 solid $CAP_G06;
92
155
  }
93
156
 
94
157
  &-card {
95
- border-radius: $CAP_SPACE_08;
96
- border: $CAP_SPACE_01 solid $CAP_G06;
97
158
  padding: 0;
98
159
  }
99
160
 
@@ -104,9 +165,11 @@
104
165
  }
105
166
 
106
167
  &-avatar {
107
- width: 3rem; /* 48px at 16px root */
168
+ width: 3rem;
169
+ /* 48px at 16px root */
108
170
  height: 3rem;
109
- border-radius: 1.25rem; /* 20px at 16px root */
171
+ border-radius: 1.25rem;
172
+ /* 20px at 16px root */
110
173
  background-color: $CAP_G07;
111
174
  display: flex;
112
175
  align-items: center;
@@ -155,9 +218,6 @@
155
218
  }
156
219
 
157
220
  .common-test-and-preview-slidebox {
158
- .common-test-preview-modal-wrap {
159
- z-index: 10003;
160
- }
161
221
 
162
222
  .ant-modal-mask,
163
223
  .ant-modal-wrap {
@@ -200,6 +260,7 @@
200
260
 
201
261
  .tag-input-field {
202
262
  width: 14.714rem;
263
+
203
264
  .input {
204
265
  height: $CAP_SPACE_40;
205
266
  }
@@ -212,12 +273,16 @@
212
273
 
213
274
  .left-panel {
214
275
  width: 40%;
276
+ min-width: 20rem;
277
+ /* Consistent width in campaigns/library mode so test customer dropdown matches creatives */
215
278
  padding-right: 1rem;
216
279
  border-right: $CAP_SPACE_01 solid $CAP_G12;
217
280
  overflow-y: auto;
281
+
218
282
  .panel-divider {
219
283
  margin: 0;
220
284
  }
285
+
221
286
  .no-tags-extracted-info-note {
222
287
  .note-text {
223
288
  white-space: unset;
@@ -266,6 +331,7 @@
266
331
  .values-missing-message {
267
332
  margin: $CAP_SPACE_16 0;
268
333
  }
334
+
269
335
  .editor-header {
270
336
  display: flex;
271
337
  justify-content: flex-end;
@@ -349,7 +415,7 @@
349
415
  align-items: center;
350
416
  min-height: $CAP_SPACE_20;
351
417
  padding-left: $CAP_SPACE_08;
352
-
418
+
353
419
  &:hover {
354
420
  background-color: #f5f5f5;
355
421
  }
@@ -415,6 +481,7 @@
415
481
  color: #bfbfbf;
416
482
  font-style: italic;
417
483
  }
484
+
418
485
  .ant-input {
419
486
  margin: $CAP_SPACE_06 0;
420
487
  height: $CAP_SPACE_36;
@@ -482,10 +549,11 @@
482
549
  justify-content: space-between;
483
550
  margin-bottom: $CAP_SPACE_16;
484
551
  }
552
+
485
553
  .editor-actions::before,
486
554
  .editor-actions::after {
487
- content: none;
488
- display: none;
555
+ content: none;
556
+ display: none;
489
557
  }
490
558
 
491
559
  .optional-tags-section {
@@ -494,9 +562,10 @@
494
562
  color: #666;
495
563
  }
496
564
 
497
- z-index: -1;
565
+ z-index: -1;
566
+
498
567
  .optional-tags-content {
499
- padding: $CAP_SPACE_16;
568
+ padding: $CAP_SPACE_16;
500
569
  background: #f5f5f5;
501
570
  border-radius: $CAP_SPACE_04;
502
571
  }
@@ -522,31 +591,40 @@
522
591
  margin-bottom: $CAP_SPACE_16;
523
592
  color: #666;
524
593
  }
594
+
525
595
  .ant-collapse-header {
526
596
  padding-left: 0;
527
597
  }
598
+
528
599
  .ant-collapse-content-box {
529
600
  padding-left: 0 !important;
530
601
  }
602
+
531
603
  .send-test-content {
532
604
  flex-direction: column;
533
605
  align-items: stretch;
534
606
  gap: 0;
535
607
  }
608
+
536
609
  .test-customers-tree-select {
537
610
  width: 100%;
611
+ min-width: 18rem;
612
+ /* Consistent dropdown width in campaigns and creatives */
538
613
  min-height: $CAP_SPACE_40;
539
614
  margin: $CAP_SPACE_12 0 $CAP_SPACE_08;
615
+
540
616
  .ant-select-selection,
541
617
  .ant-select-selector {
542
618
  min-height: $CAP_SPACE_40;
543
619
  height: auto !important;
544
620
  }
545
621
  }
622
+
546
623
  .send-test-content .ant-btn {
547
624
  margin-top: $CAP_SPACE_04;
548
625
  flex-shrink: 0;
549
626
  }
627
+
550
628
  .ant-select-selection__choice {
551
629
  background-color: $CAP_G08;
552
630
  border-radius: $CAP_SPACE_04;
@@ -556,8 +634,11 @@
556
634
 
557
635
  // Test customers TreeSelect dropdown: limit height and make scrollable (dropdown renders in portal)
558
636
  .test-customers-tree-select-dropdown {
559
- max-height: 20rem !important; /* 320px */
560
- overflow-y: auto !important;
637
+ min-width: 18rem;
638
+ max-height: 20rem;
639
+ /* 320px */
640
+ overflow-y: auto;
641
+
561
642
  .ant-select-tree-list-holder-inner {
562
643
  overflow: visible;
563
644
  }
@@ -565,8 +646,10 @@
565
646
 
566
647
  .test-customer-add-btn {
567
648
  width: 100%;
568
- background-color: transparent !important;
569
- color: $FONT_COLOR_05 !important;
649
+ &.ant-btn.cap-button-v2.flat-btn {
650
+ background-color: transparent;
651
+ color: $FONT_COLOR_05;
652
+ }
570
653
  white-space: normal;
571
654
  word-break: normal;
572
655
  overflow-wrap: break-word;
@@ -594,7 +677,7 @@
594
677
  padding-bottom: $CAP_SPACE_16;
595
678
  margin-bottom: $CAP_SPACE_16;
596
679
  }
597
-
680
+
598
681
  .right-panel {
599
682
  padding-left: 0;
600
683
  padding-top: $CAP_SPACE_16;
@@ -610,6 +693,7 @@
610
693
  .preview-divider {
611
694
  margin: 0;
612
695
  }
696
+
613
697
  .preview-header {
614
698
  display: flex;
615
699
  justify-content: space-between;
@@ -621,6 +705,7 @@
621
705
  font-size: $FONT_SIZE_M;
622
706
  color: #595959;
623
707
  gap: $CAP_SPACE_04;
708
+
624
709
  b {
625
710
  color: #262626;
626
711
  margin-left: $CAP_SPACE_04;
@@ -651,10 +736,12 @@
651
736
 
652
737
  .preview-body {
653
738
  border-radius: $CAP_SPACE_08;
654
- box-shadow: 0 $CAP_SPACE_04 $CAP_SPACE_12 rgba(0,0,0,0.1);
739
+ box-shadow: 0 $CAP_SPACE_04 $CAP_SPACE_12 rgba(0, 0, 0, 0.1);
740
+
655
741
  &.mobile {
656
742
  width: 26.786rem;
657
743
  margin: 0 auto;
744
+
658
745
  .browser-bar {
659
746
  background: $CAP_G08;
660
747
  }
@@ -670,13 +757,16 @@
670
757
  display: flex;
671
758
  gap: $CAP_SPACE_08;
672
759
  color: $CAP_G06;
760
+
673
761
  .refresh-icon {
674
762
  margin-top: $CAP_SPACE_04;
763
+
675
764
  svg path {
676
765
  fill: $CAP_G06;
677
766
  }
678
767
  }
679
768
  }
769
+
680
770
  .address-bar {
681
771
  flex-grow: 1;
682
772
  background: $CAP_WHITE;
@@ -686,13 +776,16 @@
686
776
  color: #595959;
687
777
  text-align: center;
688
778
  border: $CAP_SPACE_01 solid $CAP_G07;
779
+
689
780
  .address-bar-label {
690
781
  float: left;
691
782
  }
783
+
692
784
  .browser-address-bar-icon {
693
785
  float: right;
694
786
  }
695
787
  }
788
+
696
789
  .browser-actions {
697
790
  color: #8c8c8c;
698
791
  }
@@ -703,9 +796,11 @@
703
796
  align-items: center;
704
797
  padding: $CAP_SPACE_16;
705
798
  gap: $CAP_SPACE_16;
799
+
706
800
  .back-arrow {
707
801
  font-size: $CAP_SPACE_16;
708
802
  }
803
+
709
804
  .email-meta {
710
805
  margin-left: auto;
711
806
  display: flex;
@@ -713,6 +808,7 @@
713
808
  gap: $CAP_SPACE_08;
714
809
  font-size: $FONT_SIZE_S;
715
810
  color: #8c8c8c;
811
+
716
812
  .dots {
717
813
  width: $CAP_SPACE_06;
718
814
  height: $CAP_SPACE_06;
@@ -727,16 +823,19 @@
727
823
  align-items: center;
728
824
  padding: 0 $CAP_SPACE_16 $CAP_SPACE_16;
729
825
  gap: $CAP_SPACE_12;
826
+
730
827
  .sender-avatar {
731
828
  width: $CAP_SPACE_40;
732
829
  height: $CAP_SPACE_40;
733
830
  border-radius: 50%;
734
831
  background: $CAP_G12;
735
832
  }
833
+
736
834
  .sender-info {
737
835
  .sender-name {
738
836
  font-weight: 500;
739
837
  }
838
+
740
839
  .recipient-info {
741
840
  font-size: $FONT_SIZE_S;
742
841
  color: #8c8c8c;
@@ -747,6 +846,7 @@
747
846
  .email-content {
748
847
  border-top: $CAP_SPACE_01 solid $CAP_G12;
749
848
  padding: 0 $CAP_SPACE_16;
849
+
750
850
  iframe {
751
851
  border: none;
752
852
  }
@@ -20,6 +20,7 @@ import {
20
20
  CLEAR_PREVIEW_ERRORS,
21
21
  GET_SENDER_DETAILS_REQUESTED,
22
22
  GET_WECRM_ACCOUNTS_REQUESTED,
23
+ ADD_TEST_CUSTOMER,
23
24
  } from './constants';
24
25
 
25
26
  // ============================================
@@ -92,6 +93,15 @@ export const getTestGroupsRequested = () => ({
92
93
  type: GET_TEST_GROUPS_REQUESTED,
93
94
  });
94
95
 
96
+ /**
97
+ * Add a test customer to the list (e.g. after adding existing customer so tree shows name)
98
+ * @param {Object} customer - { userId, customerId, name, email, mobile }
99
+ */
100
+ export const addTestCustomer = (customer) => ({
101
+ type: ADD_TEST_CUSTOMER,
102
+ payload: { customer },
103
+ });
104
+
95
105
  /**
96
106
  * Create or update message meta configuration
97
107
  * @param {Object} payload - Message meta payload
@@ -33,6 +33,7 @@ export const SEND_TEST_MESSAGE_FAILURE = 'app/CommonTestAndPreview/SEND_TEST_MES
33
33
  export const GET_TEST_CUSTOMERS_REQUESTED = 'app/CommonTestAndPreview/GET_TEST_CUSTOMERS_REQUESTED';
34
34
  export const GET_TEST_CUSTOMERS_SUCCESS = 'app/CommonTestAndPreview/GET_TEST_CUSTOMERS_SUCCESS';
35
35
  export const GET_TEST_CUSTOMERS_FAILURE = 'app/CommonTestAndPreview/GET_TEST_CUSTOMERS_FAILURE';
36
+ export const ADD_TEST_CUSTOMER = 'app/CommonTestAndPreview/ADD_TEST_CUSTOMER';
36
37
 
37
38
  // Test Groups
38
39
  export const GET_TEST_GROUPS_REQUESTED = 'app/CommonTestAndPreview/GET_TEST_GROUPS_REQUESTED';
@@ -65,6 +66,12 @@ export const GET_WECRM_ACCOUNTS_REQUESTED = 'app/CommonTestAndPreview/GET_WECRM_
65
66
  export const GET_WECRM_ACCOUNTS_SUCCESS = 'app/CommonTestAndPreview/GET_WECRM_ACCOUNTS_SUCCESS';
66
67
  export const GET_WECRM_ACCOUNTS_FAILURE = 'app/CommonTestAndPreview/GET_WECRM_ACCOUNTS_FAILURE';
67
68
 
69
+ // ============================================
70
+ // CUSTOMER MODAL TYPE (test customer addition)
71
+ // ============================================
72
+ export const CUSTOMER_MODAL_NEW = 'new';
73
+ export const CUSTOMER_MODAL_EXISTING = 'existing';
74
+
68
75
  // ============================================
69
76
  // CHANNEL CONSTANTS
70
77
  // ============================================
@@ -138,14 +145,21 @@ export const CLIENT_NAME_CREATIVES = 'CREATIVES';
138
145
  // Note: API uses "PUSH" instead of "MOBILEPUSH" for MobilePush channel
139
146
  export const API_CHANNEL_PUSH = 'PUSH';
140
147
 
148
+ // ============================================
149
+ // VALIDATION REGEX (shared with commonUtils for email/mobile validation)
150
+ // ============================================
151
+ export const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
152
+ export const PHONE_REGEX = /^[1-9]\d{7,14}$/;
153
+
141
154
  // ============================================
142
155
  // IDENTIFIER TYPE CONSTANTS
143
156
  // ============================================
157
+ export const IDENTIFIER_TYPE_EMAIL = 'email';
144
158
  export const IDENTIFIER_TYPE_MOBILE = 'mobile';
145
159
  export const IDENTIFIER_TYPE_PHONE = 'phone';
146
160
 
147
161
  // Customer creation modal – input error state class suffix (used with customer-creation-modal-input)
148
- export const INPUT_HAS_ERROR_CLASS = ' has-error';
162
+ export const INPUT_HAS_ERROR_CLASS = ' has-input-error';
149
163
 
150
164
  // ============================================
151
165
  // CHANNEL NAME CONSTANTS (for CDN and other utilities)