@capillarytech/creatives-library 8.0.331 → 8.0.332

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 (31) hide show
  1. package/package.json +2 -2
  2. package/services/api.js +17 -0
  3. package/services/tests/api.test.js +72 -0
  4. package/utils/commonUtils.js +10 -0
  5. package/utils/tests/commonUtil.test.js +169 -0
  6. package/v2Components/CommonTestAndPreview/AddTestCustomer.js +42 -0
  7. package/v2Components/CommonTestAndPreview/CustomerCreationModal.js +155 -0
  8. package/v2Components/CommonTestAndPreview/ExistingCustomerModal.js +94 -0
  9. package/v2Components/CommonTestAndPreview/SendTestMessage.js +78 -49
  10. package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +134 -34
  11. package/v2Components/CommonTestAndPreview/actions.js +10 -0
  12. package/v2Components/CommonTestAndPreview/constants.js +17 -1
  13. package/v2Components/CommonTestAndPreview/index.js +356 -22
  14. package/v2Components/CommonTestAndPreview/messages.js +106 -0
  15. package/v2Components/CommonTestAndPreview/reducer.js +12 -0
  16. package/v2Components/CommonTestAndPreview/sagas.js +2 -1
  17. package/v2Components/CommonTestAndPreview/tests/AddTestCustomer.test.js +66 -0
  18. package/v2Components/CommonTestAndPreview/tests/CommonTestAndPreview.addTestCustomer.test.js +648 -0
  19. package/v2Components/CommonTestAndPreview/tests/CustomValuesEditor.test.js +23 -5
  20. package/v2Components/CommonTestAndPreview/tests/CustomerCreationModal.test.js +174 -0
  21. package/v2Components/CommonTestAndPreview/tests/ExistingCustomerModal.test.js +114 -0
  22. package/v2Components/CommonTestAndPreview/tests/SendTestMessage.test.js +39 -19
  23. package/v2Components/CommonTestAndPreview/tests/constants.test.js +31 -1
  24. package/v2Components/CommonTestAndPreview/tests/index.test.js +36 -0
  25. package/v2Components/CommonTestAndPreview/tests/reducer.test.js +71 -0
  26. package/v2Components/CommonTestAndPreview/tests/selectors.test.js +17 -0
  27. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +1408 -1276
  28. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +321 -288
  29. package/v2Containers/TagList/index.js +11 -15
  30. package/v2Containers/WebPush/Create/index.js +1 -1
  31. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +5246 -4872
@@ -24,6 +24,9 @@ const SendTestMessage = ({
24
24
  channel,
25
25
  isSendingTestMessage,
26
26
  formatMessage,
27
+ renderAddTestCustomerButton,
28
+ searchValue,
29
+ setSearchValue,
27
30
  deliverySettings,
28
31
  senderDetailsOptions,
29
32
  wecrmAccounts,
@@ -31,57 +34,77 @@ const SendTestMessage = ({
31
34
  isLoadingSenderDetails,
32
35
  smsTraiDltEnabled,
33
36
  registeredSenderIds,
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)}
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} />}
57
51
  />
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
- }
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}
74
75
  />
75
- )}
76
- <CapButton onClick={handleSendTestMessage} disabled={isEmpty(selectedTestEntities) || isSendingTestMessage}>
77
- <FormattedMessage {...messages.sendTestButton} />
78
- </CapButton>
79
- </CapRow>),
80
- key: 1,
81
- },
82
- ]}
83
- />
84
- );
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
+ };
85
108
 
86
109
  SendTestMessage.propTypes = {
87
110
  isFetchingTestCustomers: PropTypes.bool.isRequired,
@@ -94,6 +117,9 @@ SendTestMessage.propTypes = {
94
117
  channel: PropTypes.string,
95
118
  isSendingTestMessage: PropTypes.bool.isRequired,
96
119
  formatMessage: PropTypes.func.isRequired,
120
+ renderAddTestCustomerButton: PropTypes.func,
121
+ searchValue: PropTypes.string,
122
+ setSearchValue: PropTypes.func,
97
123
  deliverySettings: PropTypes.object,
98
124
  senderDetailsOptions: PropTypes.array,
99
125
  wecrmAccounts: PropTypes.array,
@@ -106,6 +132,9 @@ SendTestMessage.propTypes = {
106
132
  SendTestMessage.defaultProps = {
107
133
  formData: undefined,
108
134
  channel: undefined,
135
+ renderAddTestCustomerButton: undefined,
136
+ searchValue: '',
137
+ setSearchValue: () => {}, // no-op when not provided; required by TreeSelect when showSearch is true
109
138
  deliverySettings: {},
110
139
  senderDetailsOptions: [],
111
140
  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;
@@ -151,9 +214,6 @@
151
214
  }
152
215
 
153
216
  .common-test-and-preview-slidebox {
154
- .common-test-preview-modal-wrap {
155
- z-index: 10003;
156
- }
157
217
 
158
218
  .ant-modal-mask,
159
219
  .ant-modal-wrap {
@@ -196,6 +256,7 @@
196
256
 
197
257
  .tag-input-field {
198
258
  width: 14.714rem;
259
+
199
260
  .input {
200
261
  height: $CAP_SPACE_40;
201
262
  }
@@ -208,12 +269,16 @@
208
269
 
209
270
  .left-panel {
210
271
  width: 40%;
272
+ min-width: 20rem;
273
+ /* Consistent width in campaigns/library mode so test customer dropdown matches creatives */
211
274
  padding-right: 1rem;
212
275
  border-right: $CAP_SPACE_01 solid $CAP_G12;
213
276
  overflow-y: auto;
277
+
214
278
  .panel-divider {
215
279
  margin: 0;
216
280
  }
281
+
217
282
  .no-tags-extracted-info-note {
218
283
  .note-text {
219
284
  white-space: unset;
@@ -262,6 +327,7 @@
262
327
  .values-missing-message {
263
328
  margin: $CAP_SPACE_16 0;
264
329
  }
330
+
265
331
  .editor-header {
266
332
  display: flex;
267
333
  justify-content: flex-end;
@@ -345,7 +411,7 @@
345
411
  align-items: center;
346
412
  min-height: $CAP_SPACE_20;
347
413
  padding-left: $CAP_SPACE_08;
348
-
414
+
349
415
  &:hover {
350
416
  background-color: #f5f5f5;
351
417
  }
@@ -411,6 +477,7 @@
411
477
  color: #bfbfbf;
412
478
  font-style: italic;
413
479
  }
480
+
414
481
  .ant-input {
415
482
  margin: $CAP_SPACE_06 0;
416
483
  height: $CAP_SPACE_36;
@@ -471,10 +538,11 @@
471
538
  justify-content: space-between;
472
539
  margin-bottom: $CAP_SPACE_16;
473
540
  }
541
+
474
542
  .editor-actions::before,
475
543
  .editor-actions::after {
476
- content: none;
477
- display: none;
544
+ content: none;
545
+ display: none;
478
546
  }
479
547
 
480
548
  .optional-tags-section {
@@ -483,9 +551,10 @@
483
551
  color: #666;
484
552
  }
485
553
 
486
- z-index: -1;
554
+ z-index: -1;
555
+
487
556
  .optional-tags-content {
488
- padding: $CAP_SPACE_16;
557
+ padding: $CAP_SPACE_16;
489
558
  background: #f5f5f5;
490
559
  border-radius: $CAP_SPACE_04;
491
560
  }
@@ -511,31 +580,40 @@
511
580
  margin-bottom: $CAP_SPACE_16;
512
581
  color: #666;
513
582
  }
583
+
514
584
  .ant-collapse-header {
515
585
  padding-left: 0;
516
586
  }
587
+
517
588
  .ant-collapse-content-box {
518
589
  padding-left: 0 !important;
519
590
  }
591
+
520
592
  .send-test-content {
521
593
  flex-direction: column;
522
594
  align-items: stretch;
523
595
  gap: 0;
524
596
  }
597
+
525
598
  .test-customers-tree-select {
526
599
  width: 100%;
600
+ min-width: 18rem;
601
+ /* Consistent dropdown width in campaigns and creatives */
527
602
  min-height: $CAP_SPACE_40;
528
603
  margin: $CAP_SPACE_12 0 $CAP_SPACE_08;
604
+
529
605
  .ant-select-selection,
530
606
  .ant-select-selector {
531
607
  min-height: $CAP_SPACE_40;
532
608
  height: auto !important;
533
609
  }
534
610
  }
611
+
535
612
  .send-test-content .ant-btn {
536
613
  margin-top: $CAP_SPACE_04;
537
614
  flex-shrink: 0;
538
615
  }
616
+
539
617
  .ant-select-selection__choice {
540
618
  background-color: $CAP_G08;
541
619
  border-radius: $CAP_SPACE_04;
@@ -545,8 +623,11 @@
545
623
 
546
624
  // Test customers TreeSelect dropdown: limit height and make scrollable (dropdown renders in portal)
547
625
  .test-customers-tree-select-dropdown {
548
- max-height: 20rem !important; /* 320px */
549
- overflow-y: auto !important;
626
+ min-width: 18rem;
627
+ max-height: 20rem;
628
+ /* 320px */
629
+ overflow-y: auto;
630
+
550
631
  .ant-select-tree-list-holder-inner {
551
632
  overflow: visible;
552
633
  }
@@ -554,8 +635,10 @@
554
635
 
555
636
  .test-customer-add-btn {
556
637
  width: 100%;
557
- background-color: transparent !important;
558
- color: $FONT_COLOR_05 !important;
638
+ &.ant-btn.cap-button-v2.flat-btn {
639
+ background-color: transparent;
640
+ color: $FONT_COLOR_05;
641
+ }
559
642
  white-space: normal;
560
643
  word-break: normal;
561
644
  overflow-wrap: break-word;
@@ -583,7 +666,7 @@
583
666
  padding-bottom: $CAP_SPACE_16;
584
667
  margin-bottom: $CAP_SPACE_16;
585
668
  }
586
-
669
+
587
670
  .right-panel {
588
671
  padding-left: 0;
589
672
  padding-top: $CAP_SPACE_16;
@@ -599,6 +682,7 @@
599
682
  .preview-divider {
600
683
  margin: 0;
601
684
  }
685
+
602
686
  .preview-header {
603
687
  display: flex;
604
688
  justify-content: space-between;
@@ -610,6 +694,7 @@
610
694
  font-size: $FONT_SIZE_M;
611
695
  color: #595959;
612
696
  gap: $CAP_SPACE_04;
697
+
613
698
  b {
614
699
  color: #262626;
615
700
  margin-left: $CAP_SPACE_04;
@@ -640,10 +725,12 @@
640
725
 
641
726
  .preview-body {
642
727
  border-radius: $CAP_SPACE_08;
643
- box-shadow: 0 $CAP_SPACE_04 $CAP_SPACE_12 rgba(0,0,0,0.1);
728
+ box-shadow: 0 $CAP_SPACE_04 $CAP_SPACE_12 rgba(0, 0, 0, 0.1);
729
+
644
730
  &.mobile {
645
731
  width: 26.786rem;
646
732
  margin: 0 auto;
733
+
647
734
  .browser-bar {
648
735
  background: $CAP_G08;
649
736
  }
@@ -659,13 +746,16 @@
659
746
  display: flex;
660
747
  gap: $CAP_SPACE_08;
661
748
  color: $CAP_G06;
749
+
662
750
  .refresh-icon {
663
751
  margin-top: $CAP_SPACE_04;
752
+
664
753
  svg path {
665
754
  fill: $CAP_G06;
666
755
  }
667
756
  }
668
757
  }
758
+
669
759
  .address-bar {
670
760
  flex-grow: 1;
671
761
  background: $CAP_WHITE;
@@ -675,13 +765,16 @@
675
765
  color: #595959;
676
766
  text-align: center;
677
767
  border: $CAP_SPACE_01 solid $CAP_G07;
768
+
678
769
  .address-bar-label {
679
770
  float: left;
680
771
  }
772
+
681
773
  .browser-address-bar-icon {
682
774
  float: right;
683
775
  }
684
776
  }
777
+
685
778
  .browser-actions {
686
779
  color: #8c8c8c;
687
780
  }
@@ -692,9 +785,11 @@
692
785
  align-items: center;
693
786
  padding: $CAP_SPACE_16;
694
787
  gap: $CAP_SPACE_16;
788
+
695
789
  .back-arrow {
696
790
  font-size: $CAP_SPACE_16;
697
791
  }
792
+
698
793
  .email-meta {
699
794
  margin-left: auto;
700
795
  display: flex;
@@ -702,6 +797,7 @@
702
797
  gap: $CAP_SPACE_08;
703
798
  font-size: $FONT_SIZE_S;
704
799
  color: #8c8c8c;
800
+
705
801
  .dots {
706
802
  width: $CAP_SPACE_06;
707
803
  height: $CAP_SPACE_06;
@@ -716,16 +812,19 @@
716
812
  align-items: center;
717
813
  padding: 0 $CAP_SPACE_16 $CAP_SPACE_16;
718
814
  gap: $CAP_SPACE_12;
815
+
719
816
  .sender-avatar {
720
817
  width: $CAP_SPACE_40;
721
818
  height: $CAP_SPACE_40;
722
819
  border-radius: 50%;
723
820
  background: $CAP_G12;
724
821
  }
822
+
725
823
  .sender-info {
726
824
  .sender-name {
727
825
  font-weight: 500;
728
826
  }
827
+
729
828
  .recipient-info {
730
829
  font-size: $FONT_SIZE_S;
731
830
  color: #8c8c8c;
@@ -736,6 +835,7 @@
736
835
  .email-content {
737
836
  border-top: $CAP_SPACE_01 solid $CAP_G12;
738
837
  padding: 0 $CAP_SPACE_16;
838
+
739
839
  iframe {
740
840
  border: none;
741
841
  }
@@ -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,13 @@ 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
+ // ============================================
71
+ // CUSTOMER MODAL TYPE (test customer addition)
72
+ // ============================================
73
+ export const CUSTOMER_MODAL_NEW = 'new';
74
+ export const CUSTOMER_MODAL_EXISTING = 'existing';
75
+
68
76
  // ============================================
69
77
  // CHANNEL CONSTANTS
70
78
  // ============================================
@@ -78,6 +86,7 @@ export const CHANNELS = {
78
86
  VIBER: 'VIBER',
79
87
  ZALO: 'ZALO',
80
88
  };
89
+ export const CHANNELS_USING_ANDROID_PREVIEW_DEVICE = [CHANNELS.SMS, CHANNELS.RCS, CHANNELS.WHATSAPP, CHANNELS.VIBER, CHANNELS.ZALO, CHANNELS.INAPP, CHANNELS.MOBILEPUSH];
81
90
 
82
91
  // ============================================
83
92
  // DEVICE CONSTANTS
@@ -100,14 +109,21 @@ export const CLIENT_NAME_CREATIVES = 'CREATIVES';
100
109
  // Note: API uses "PUSH" instead of "MOBILEPUSH" for MobilePush channel
101
110
  export const API_CHANNEL_PUSH = 'PUSH';
102
111
 
112
+ // ============================================
113
+ // VALIDATION REGEX (shared with commonUtils for email/mobile validation)
114
+ // ============================================
115
+ export const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
116
+ export const PHONE_REGEX = /^[1-9]\d{7,14}$/;
117
+
103
118
  // ============================================
104
119
  // IDENTIFIER TYPE CONSTANTS
105
120
  // ============================================
121
+ export const IDENTIFIER_TYPE_EMAIL = 'email';
106
122
  export const IDENTIFIER_TYPE_MOBILE = 'mobile';
107
123
  export const IDENTIFIER_TYPE_PHONE = 'phone';
108
124
 
109
125
  // Customer creation modal – input error state class suffix (used with customer-creation-modal-input)
110
- export const INPUT_HAS_ERROR_CLASS = ' has-error';
126
+ export const INPUT_HAS_ERROR_CLASS = ' has-input-error';
111
127
 
112
128
  // ============================================
113
129
  // CHANNEL NAME CONSTANTS (for CDN and other utilities)