@capillarytech/creatives-library 8.0.299-alpha.1 → 8.0.299-alpha.10

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.
@@ -45,7 +45,6 @@ export const GIFT_CARDS = 'GIFT_CARDS';
45
45
  export const PROMO_ENGINE = 'PROMO_ENGINE';
46
46
  export const LIQUID_SUPPORT = 'ENABLE_LIQUID_SUPPORT';
47
47
  export const ENABLE_NEW_MPUSH = 'ENABLE_NEW_MPUSH';
48
- export const ENABLE_NEW_EDITOR_FLOW_INAPP = 'ENABLE_NEW_EDITOR_FLOW_INAPP';
49
48
  export const SUPPORT_CK_EDITOR = 'SUPPORT_CK_EDITOR';
50
49
  export const CUSTOM_TAG = 'CustomTagMessage';
51
50
  export const CUSTOMER_EXTENDED_FIELD = 'Customer extended fields';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@capillarytech/creatives-library",
3
3
  "author": "meharaj",
4
- "version": "8.0.299-alpha.1",
4
+ "version": "8.0.299-alpha.10",
5
5
  "description": "Capillary creatives ui",
6
6
  "main": "./index.js",
7
7
  "module": "./index.es.js",
package/utils/common.js CHANGED
@@ -24,8 +24,7 @@ import {
24
24
  ENABLE_WEBPUSH,
25
25
  LIQUID_SUPPORT,
26
26
  SUPPORT_CK_EDITOR,
27
- ENABLE_NEW_MPUSH,
28
- ENABLE_NEW_EDITOR_FLOW_INAPP
27
+ ENABLE_NEW_MPUSH
29
28
  } from '../constants/unified';
30
29
  import { apiMessageFormatHandler } from './commonUtils';
31
30
 
@@ -143,10 +142,6 @@ export const hasNewMobilePushFeatureEnabled = Auth.hasFeatureAccess.bind(
143
142
  ENABLE_NEW_MPUSH,
144
143
  );
145
144
 
146
- export const hasNewEditorFlowInAppEnabled = ()=>{
147
- return true
148
- }
149
-
150
145
  //filtering tags based on scope
151
146
  export const filterTags = (tagsToFilter, tagsList) => tagsList?.filter(
152
147
  (tag) => !tagsToFilter?.includes(tag?.definition?.value)
@@ -63,7 +63,6 @@ const CapDeviceContent = (props) => {
63
63
  deepLinkValue,
64
64
  setDeepLinkValue,
65
65
  onCopyTitleAndContent,
66
- isOtherDeviceSupported,
67
66
  tags,
68
67
  onTagSelect,
69
68
  handleOnTagsContextChange,
@@ -168,15 +167,13 @@ const CapDeviceContent = (props) => {
168
167
  return (
169
168
  <>
170
169
  <CapRow className="creatives-device-content">
171
- {isOtherDeviceSupported && (
172
- <CapLink
173
- title={isAndroid
174
- ? formatMessage(messages.copyContentFromIOS)
175
- : formatMessage(messages.copyCotentFromAndroid)}
176
- className="inapp-copy-content"
177
- onClick={onCopyTitleAndContent}
178
- />
179
- )}
170
+ <CapLink
171
+ title={isAndroid
172
+ ? formatMessage(messages.copyContentFromIOS)
173
+ : formatMessage(messages.copyCotentFromAndroid)}
174
+ className="inapp-copy-content"
175
+ onClick={onCopyTitleAndContent}
176
+ />
180
177
  <CapRow className="creatives-inapp-title">
181
178
  <CapColumn
182
179
  className="inapp-content-main"
@@ -74,7 +74,7 @@ const CustomerCreationModal = ({
74
74
  const details = res.customerDetails || [];
75
75
  if (!success) {
76
76
  const errorMessage = response?.message || response?.status?.message
77
- if (errorMessage.toLowerCase().includes("merged customer found")) {
77
+ if (errorMessage?.toLowerCase().includes("merged customer found")) {
78
78
  return { success: true, exists: true, details: [] };
79
79
  }
80
80
  }
@@ -195,7 +195,7 @@ const CustomerCreationModal = ({
195
195
  width={500}
196
196
  maskStyle={{ backgroundColor: 'rgba(244, 245, 247, 0.9)' }}
197
197
  footer={
198
- <CapRow justify="start" text-align="left" gutter={8}>
198
+ <CapRow justify="start" gutter={8}>
199
199
  <CapButton
200
200
  type="primary"
201
201
  onClick={() => onSave(validationErrors, setIsLoading)}
@@ -215,7 +215,7 @@ const CustomerCreationModal = ({
215
215
  }
216
216
  title={<FormattedMessage {...messages.customerCreationModalTitle} />}
217
217
  wrapClassName="common-test-preview-modal-wrap"
218
- className="common-test-preview-modal"
218
+ className={`common-test-preview-modal${lookupLoading ? ' customer-creation-modal--lookup-loading' : ''}`}
219
219
  >
220
220
  <CapRow className="customer-creation-modal-row">
221
221
  <CapColumn span={24}>
@@ -242,7 +242,7 @@ const CustomerCreationModal = ({
242
242
  </CapColumn>
243
243
  </CapRow>
244
244
 
245
- <CapRow className="customer-creation-modal-row">
245
+ <CapRow className="customer-creation-modal-row customer-creation-modal-row--email">
246
246
  <CapColumn span={24}>
247
247
  <CapLabel type="label1" className="customer-creation-modal-label">
248
248
  <FormattedMessage {...messages.customerEmail} /> {channel !== CHANNELS.EMAIL && <span className="customer-creation-modal-optional">(Optional)</span>}
@@ -2,7 +2,7 @@ import CapModal from "@capillarytech/cap-ui-library/CapModal";
2
2
  import { FormattedMessage, injectIntl } from "react-intl";
3
3
  import messages from "./messages";
4
4
  import React, { useState } from "react";
5
- import { CapCard, CapRow, CapColumn, CapLabel } from "@capillarytech/cap-ui-library";
5
+ import { CapCard, CapRow, CapColumn } from "@capillarytech/cap-ui-library";
6
6
  import { CHANNELS } from "./constants";
7
7
  import CapButton from "@capillarytech/cap-ui-library/CapButton";
8
8
  import CapIcon from "@capillarytech/cap-ui-library/CapIcon";
@@ -134,7 +134,7 @@ SendTestMessage.defaultProps = {
134
134
  channel: undefined,
135
135
  renderAddTestCustomerButton: undefined,
136
136
  searchValue: '',
137
- setSearchValue: undefined,
137
+ setSearchValue: () => {}, // no-op when not provided; required by TreeSelect when showSearch is true
138
138
  deliverySettings: {},
139
139
  senderDetailsOptions: [],
140
140
  wecrmAccounts: [],
@@ -5,9 +5,28 @@
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;
14
+ }
15
+
16
+ /* Modals rendered inside slidebox via getContainer (lib mode) – mask and wrap above content */
17
+ .common-test-and-preview-notification-container {
18
+ .ant-notification {
19
+ z-index: 10001;
20
+ }
21
+
22
+ .common-test-preview-modal-wrap {
23
+ z-index: 10003;
24
+ }
25
+
26
+ .ant-modal-mask,
27
+ .ant-modal-wrap {
28
+ z-index: 10003;
29
+ }
11
30
  }
12
31
 
13
32
  /* Lookup spinner overlay above test-customers dropdown. */
@@ -22,19 +41,23 @@
22
41
 
23
42
  /* When customer lookup is loading, dropdown renders inside .send-test-section; lower it so spinner is on top. */
24
43
  .common-test-preview-customer-loading .test-customers-tree-select-dropdown {
25
- z-index: 0 !important;
44
+ z-index: 0;
26
45
  }
27
46
 
28
47
  /* Customer creation modal content – avoid inline styles */
29
48
  .common-test-preview-modal {
30
49
  color: $CAP_G01 !important;
31
- .ant-modal-content {
32
- width: 28.5rem; /* 456px at 16px root */
33
- }
50
+ width: 32.571em !important;
51
+ margin-left: auto;
52
+ margin-right: auto;
53
+ max-width: 32.571em !important;
54
+
34
55
  .ant-modal-footer {
35
56
  text-align: left;
36
- margin-left: 0.625rem; /* 10px at 16px root */
37
- margin-top: 0.9375rem; /* 15px at 16px root */
57
+ margin-left: 0.625rem;
58
+ /* 10px at 16px root */
59
+ margin-top: 0.9375rem;
60
+ /* 15px at 16px root */
38
61
  }
39
62
 
40
63
  .customer-creation-modal-row {
@@ -63,22 +86,78 @@
63
86
  font-weight: normal;
64
87
  }
65
88
 
89
+ /* Input text color and font to match standalone (lib mode can lose host styles) */
66
90
  .customer-creation-modal-input {
67
- width: 100%;
68
- color: $CAP_G01;
91
+ border: none !important;
92
+
93
+ /* Sizes in em for 14px base: same px as .5rem 2.5rem 1.5rem .875rem at 16px (8px, 40px, 24px, 14px) */
94
+ .ant-input {
95
+ color: $CAP_G01;
96
+ font-size: $FONT_SIZE_M;
97
+ font-weight: normal;
98
+ padding: 0.571em;
99
+ /* 8px */
100
+ height: 2.857em;
101
+ /* 40px */
102
+ line-height: 1.714em;
103
+ /* 24px */
104
+ }
105
+
106
+ .ant-input:hover,
107
+ .ant-input:focus {
108
+ border: 1px solid $CAP_G01 !important;
109
+ }
110
+
111
+ .ant-input::placeholder {
112
+ color: $CAP_G06;
113
+ }
69
114
 
115
+ /* Error state: single clean red border, no double line or focus ring */
70
116
  &.has-error {
71
- color: $CAP_COLOR_03 !important;
117
+ .ant-input-affix-wrapper {
118
+ border: 1px solid $CAP_COLOR_03;
119
+ border-radius: $CAP_SPACE_04;
120
+ }
121
+
122
+ .ant-input-affix-wrapper:hover,
123
+ .ant-input-affix-wrapper:focus,
124
+ .ant-input-affix-wrapper-focused {
125
+ border: 1px solid $CAP_COLOR_03;
126
+ }
127
+
128
+ .ant-input-affix-wrapper .ant-input {
129
+ border: none !important;
130
+ }
131
+
72
132
  .ant-input {
73
- border-color: $CAP_COLOR_03 !important;
133
+ border: 1px solid $CAP_COLOR_03;
74
134
  }
75
135
  }
76
136
  }
77
137
 
138
+ /* Error message only – no extra border/line below the input */
78
139
  .customer-creation-modal-validation-error {
79
140
  color: $CAP_COLOR_03;
80
141
  font-size: $FONT_SIZE_S;
81
142
  margin-top: $CAP_SPACE_04;
143
+ border: none;
144
+ border-top: none;
145
+ box-shadow: none;
146
+
147
+ &::before,
148
+ &::after {
149
+ display: none;
150
+ content: none;
151
+ border: none;
152
+ }
153
+ }
154
+
155
+ /* Lookup loading: make Email and Mobile fields look disabled (wrapper-only) */
156
+ .customer-creation-modal--lookup-loading .customer-creation-modal-row--email .customer-creation-modal-input,
157
+ .customer-creation-modal--lookup-loading .customer-creation-modal-row--last .customer-creation-modal-input {
158
+ opacity: 0.65;
159
+ cursor: not-allowed;
160
+ background-color: $CAP_G09;
82
161
  }
83
162
 
84
163
  /* Existing customer modal content */
@@ -87,13 +166,17 @@
87
166
  margin-bottom: $CAP_SPACE_16;
88
167
  }
89
168
 
169
+ .ant-card.cap-card-v2 {
170
+ border: none
171
+ }
172
+
90
173
  .ant-card-body {
91
174
  padding: 1rem;
175
+ border-radius: $CAP_SPACE_08;
176
+ border: $CAP_SPACE_01 solid $CAP_G06;
92
177
  }
93
178
 
94
179
  &-card {
95
- border-radius: $CAP_SPACE_08;
96
- border: $CAP_SPACE_01 solid $CAP_G06;
97
180
  padding: 0;
98
181
  }
99
182
 
@@ -104,9 +187,11 @@
104
187
  }
105
188
 
106
189
  &-avatar {
107
- width: 3rem; /* 48px at 16px root */
190
+ width: 3rem;
191
+ /* 48px at 16px root */
108
192
  height: 3rem;
109
- border-radius: 1.25rem; /* 20px at 16px root */
193
+ border-radius: 1.25rem;
194
+ /* 20px at 16px root */
110
195
  background-color: $CAP_G07;
111
196
  display: flex;
112
197
  align-items: center;
@@ -187,6 +272,7 @@
187
272
 
188
273
  .tag-input-field {
189
274
  width: 14.714rem;
275
+
190
276
  .input {
191
277
  height: $CAP_SPACE_40;
192
278
  }
@@ -199,12 +285,16 @@
199
285
 
200
286
  .left-panel {
201
287
  width: 40%;
288
+ min-width: 20rem;
289
+ /* Consistent width in campaigns/library mode so test customer dropdown matches creatives */
202
290
  padding-right: 1rem;
203
291
  border-right: $CAP_SPACE_01 solid $CAP_G12;
204
292
  overflow-y: auto;
293
+
205
294
  .panel-divider {
206
295
  margin: 0;
207
296
  }
297
+
208
298
  .no-tags-extracted-info-note {
209
299
  .note-text {
210
300
  white-space: unset;
@@ -253,6 +343,7 @@
253
343
  .values-missing-message {
254
344
  margin: $CAP_SPACE_16 0;
255
345
  }
346
+
256
347
  .editor-header {
257
348
  display: flex;
258
349
  justify-content: flex-end;
@@ -336,7 +427,7 @@
336
427
  align-items: center;
337
428
  min-height: $CAP_SPACE_20;
338
429
  padding-left: $CAP_SPACE_08;
339
-
430
+
340
431
  &:hover {
341
432
  background-color: #f5f5f5;
342
433
  }
@@ -402,6 +493,7 @@
402
493
  color: #bfbfbf;
403
494
  font-style: italic;
404
495
  }
496
+
405
497
  .ant-input {
406
498
  margin: $CAP_SPACE_06 0;
407
499
  height: $CAP_SPACE_36;
@@ -462,10 +554,11 @@
462
554
  justify-content: space-between;
463
555
  margin-bottom: $CAP_SPACE_16;
464
556
  }
557
+
465
558
  .editor-actions::before,
466
559
  .editor-actions::after {
467
- content: none;
468
- display: none;
560
+ content: none;
561
+ display: none;
469
562
  }
470
563
 
471
564
  .optional-tags-section {
@@ -474,9 +567,10 @@
474
567
  color: #666;
475
568
  }
476
569
 
477
- z-index: -1;
570
+ z-index: -1;
571
+
478
572
  .optional-tags-content {
479
- padding: $CAP_SPACE_16;
573
+ padding: $CAP_SPACE_16;
480
574
  background: #f5f5f5;
481
575
  border-radius: $CAP_SPACE_04;
482
576
  }
@@ -502,31 +596,40 @@
502
596
  margin-bottom: $CAP_SPACE_16;
503
597
  color: #666;
504
598
  }
599
+
505
600
  .ant-collapse-header {
506
601
  padding-left: 0;
507
602
  }
603
+
508
604
  .ant-collapse-content-box {
509
605
  padding-left: 0 !important;
510
606
  }
607
+
511
608
  .send-test-content {
512
609
  flex-direction: column;
513
610
  align-items: stretch;
514
611
  gap: 0;
515
612
  }
613
+
516
614
  .test-customers-tree-select {
517
615
  width: 100%;
616
+ min-width: 18rem;
617
+ /* Consistent dropdown width in campaigns and creatives */
518
618
  min-height: $CAP_SPACE_40;
519
619
  margin: $CAP_SPACE_12 0 $CAP_SPACE_08;
620
+
520
621
  .ant-select-selection,
521
622
  .ant-select-selector {
522
623
  min-height: $CAP_SPACE_40;
523
624
  height: auto !important;
524
625
  }
525
626
  }
627
+
526
628
  .send-test-content .ant-btn {
527
629
  margin-top: $CAP_SPACE_04;
528
630
  flex-shrink: 0;
529
631
  }
632
+
530
633
  .ant-select-selection__choice {
531
634
  background-color: $CAP_G08;
532
635
  border-radius: $CAP_SPACE_04;
@@ -536,8 +639,11 @@
536
639
 
537
640
  // Test customers TreeSelect dropdown: limit height and make scrollable (dropdown renders in portal)
538
641
  .test-customers-tree-select-dropdown {
539
- max-height: 20rem !important; /* 320px */
540
- overflow-y: auto !important;
642
+ min-width: 18rem;
643
+ max-height: 20rem;
644
+ /* 320px */
645
+ overflow-y: auto;
646
+
541
647
  .ant-select-tree-list-holder-inner {
542
648
  overflow: visible;
543
649
  }
@@ -574,7 +680,7 @@
574
680
  padding-bottom: $CAP_SPACE_16;
575
681
  margin-bottom: $CAP_SPACE_16;
576
682
  }
577
-
683
+
578
684
  .right-panel {
579
685
  padding-left: 0;
580
686
  padding-top: $CAP_SPACE_16;
@@ -590,6 +696,7 @@
590
696
  .preview-divider {
591
697
  margin: 0;
592
698
  }
699
+
593
700
  .preview-header {
594
701
  display: flex;
595
702
  justify-content: space-between;
@@ -601,6 +708,7 @@
601
708
  font-size: $FONT_SIZE_M;
602
709
  color: #595959;
603
710
  gap: $CAP_SPACE_04;
711
+
604
712
  b {
605
713
  color: #262626;
606
714
  margin-left: $CAP_SPACE_04;
@@ -631,10 +739,12 @@
631
739
 
632
740
  .preview-body {
633
741
  border-radius: $CAP_SPACE_08;
634
- box-shadow: 0 $CAP_SPACE_04 $CAP_SPACE_12 rgba(0,0,0,0.1);
742
+ box-shadow: 0 $CAP_SPACE_04 $CAP_SPACE_12 rgba(0, 0, 0, 0.1);
743
+
635
744
  &.mobile {
636
745
  width: 26.786rem;
637
746
  margin: 0 auto;
747
+
638
748
  .browser-bar {
639
749
  background: $CAP_G08;
640
750
  }
@@ -650,13 +760,16 @@
650
760
  display: flex;
651
761
  gap: $CAP_SPACE_08;
652
762
  color: $CAP_G06;
763
+
653
764
  .refresh-icon {
654
765
  margin-top: $CAP_SPACE_04;
766
+
655
767
  svg path {
656
768
  fill: $CAP_G06;
657
769
  }
658
770
  }
659
771
  }
772
+
660
773
  .address-bar {
661
774
  flex-grow: 1;
662
775
  background: $CAP_WHITE;
@@ -666,13 +779,16 @@
666
779
  color: #595959;
667
780
  text-align: center;
668
781
  border: $CAP_SPACE_01 solid $CAP_G07;
782
+
669
783
  .address-bar-label {
670
784
  float: left;
671
785
  }
786
+
672
787
  .browser-address-bar-icon {
673
788
  float: right;
674
789
  }
675
790
  }
791
+
676
792
  .browser-actions {
677
793
  color: #8c8c8c;
678
794
  }
@@ -683,9 +799,11 @@
683
799
  align-items: center;
684
800
  padding: $CAP_SPACE_16;
685
801
  gap: $CAP_SPACE_16;
802
+
686
803
  .back-arrow {
687
804
  font-size: $CAP_SPACE_16;
688
805
  }
806
+
689
807
  .email-meta {
690
808
  margin-left: auto;
691
809
  display: flex;
@@ -693,6 +811,7 @@
693
811
  gap: $CAP_SPACE_08;
694
812
  font-size: $FONT_SIZE_S;
695
813
  color: #8c8c8c;
814
+
696
815
  .dots {
697
816
  width: $CAP_SPACE_06;
698
817
  height: $CAP_SPACE_06;
@@ -707,16 +826,19 @@
707
826
  align-items: center;
708
827
  padding: 0 $CAP_SPACE_16 $CAP_SPACE_16;
709
828
  gap: $CAP_SPACE_12;
829
+
710
830
  .sender-avatar {
711
831
  width: $CAP_SPACE_40;
712
832
  height: $CAP_SPACE_40;
713
833
  border-radius: 50%;
714
834
  background: $CAP_G12;
715
835
  }
836
+
716
837
  .sender-info {
717
838
  .sender-name {
718
839
  font-weight: 500;
719
840
  }
841
+
720
842
  .recipient-info {
721
843
  font-size: $FONT_SIZE_S;
722
844
  color: #8c8c8c;
@@ -727,6 +849,7 @@
727
849
  .email-content {
728
850
  border-top: $CAP_SPACE_01 solid $CAP_G12;
729
851
  padding: 0 $CAP_SPACE_16;
852
+
730
853
  iframe {
731
854
  border: none;
732
855
  }
@@ -410,7 +410,7 @@ const CommonTestAndPreview = (props) => {
410
410
  /**
411
411
  * Common handler for saving test customers (both new and existing)
412
412
  */
413
- const handleSaveTestCustomer = async (validationErrors = {},setIsLoading = false) => {
413
+ const handleSaveTestCustomer = async (validationErrors = {},setIsLoading = () => {}) => {
414
414
  // Check for validation errors before saving (for new customers)
415
415
  if (customerModal[1] === CUSTOMER_MODAL_NEW && (validationErrors.email || validationErrors.mobile)) {
416
416
  return;
@@ -2676,7 +2676,10 @@ const CommonTestAndPreview = (props) => {
2676
2676
 
2677
2677
  if (!success) {
2678
2678
  const errorMessage = response?.message || response?.status?.message || formatMessage(messages.memberLookupError);
2679
- CapNotification.error({ title: formatMessage(messages.errorTitle), message: errorMessage });
2679
+ CapNotification.error({
2680
+ message: formatMessage(messages.memberLookupError),
2681
+ description: errorMessage,
2682
+ });
2680
2683
  return;
2681
2684
  }
2682
2685
 
@@ -2710,6 +2713,7 @@ const CommonTestAndPreview = (props) => {
2710
2713
  } catch {
2711
2714
  CapNotification.error({
2712
2715
  message: formatMessage(messages.memberLookupError),
2716
+ description: formatMessage(messages.memberLookupError),
2713
2717
  });
2714
2718
  } finally {
2715
2719
  setIsCustomerDataLoading(false);
@@ -361,6 +361,7 @@ describe('CommonTestAndPreview – Add Test Customer flow', () => {
361
361
  await waitFor(() => expect(mockGetMembersLookup).toHaveBeenCalled());
362
362
  await waitFor(() => expect(CapNotification.error).toHaveBeenCalledWith(expect.objectContaining({
363
363
  message: 'Unable to look up customer. Please try again.',
364
+ description: 'Unable to look up customer. Please try again.',
364
365
  })));
365
366
  expect(screen.queryByText(/add new test customer/i)).toBeFalsy();
366
367
  });
@@ -573,7 +574,8 @@ describe('CommonTestAndPreview – Add Test Customer flow', () => {
573
574
  await clickAddTestCustomer();
574
575
  await waitFor(() => expect(mockGetMembersLookup).toHaveBeenCalled());
575
576
  await waitFor(() => expect(CapNotification.error).toHaveBeenCalledWith(expect.objectContaining({
576
- message: 'Merged customer found',
577
+ message: 'Unable to look up customer. Please try again.',
578
+ description: 'Merged customer found',
577
579
  })));
578
580
  expect(screen.queryByText(/add new test customer/i)).toBeFalsy();
579
581
  expect(screen.queryByText(/this user profile already exists/i)).toBeFalsy();
@@ -595,7 +597,8 @@ describe('CommonTestAndPreview – Add Test Customer flow', () => {
595
597
  await clickAddTestCustomer();
596
598
  await waitFor(() => expect(mockGetMembersLookup).toHaveBeenCalled());
597
599
  await waitFor(() => expect(CapNotification.error).toHaveBeenCalledWith(expect.objectContaining({
598
- message: 'Server error',
600
+ message: 'Unable to look up customer. Please try again.',
601
+ description: 'Server error',
599
602
  })));
600
603
  expect(screen.queryByText(/add new test customer/i)).toBeFalsy();
601
604
  expect(screen.queryByText(/this user profile already exists/i)).toBeFalsy();
@@ -617,6 +620,7 @@ describe('CommonTestAndPreview – Add Test Customer flow', () => {
617
620
  await clickAddTestCustomer();
618
621
  await waitFor(() => expect(CapNotification.error).toHaveBeenCalledWith(expect.objectContaining({
619
622
  message: 'Unable to look up customer. Please try again.',
623
+ description: 'Unable to look up customer. Please try again.',
620
624
  })));
621
625
  });
622
626
  });
@@ -40,11 +40,6 @@ function BeePopupEditor(props) {
40
40
  const savedCallback = useRef();
41
41
  const beeInstanceRef = useRef(null);
42
42
  const isInitializedRef = useRef(false);
43
- const beeJsonRef = useRef(beeJson);
44
-
45
- useEffect(() => {
46
- beeJsonRef.current = beeJson;
47
- }, [beeJson]);
48
43
 
49
44
  const [visibleTaglist, setVisibleTaglist] = useState(false);
50
45
  const [selectedTag, setSelectedTag] = useState({});
@@ -116,10 +111,8 @@ function BeePopupEditor(props) {
116
111
  window.BeePlugin.create(tokenData, beeConfig, (instance) => {
117
112
  beePluginInstance = instance;
118
113
  beeInstanceRef.current = instance;
119
- // Use ref to get the latest beeJson the closure captures the value at effect time,
120
- // but beeJsonRef.current is always up-to-date (e.g. when template data loads async)
121
- const latestBeeJson = beeJsonRef.current;
122
- const parseJson = typeof latestBeeJson === 'string' ? JSON.parse(latestBeeJson) : latestBeeJson;
114
+ // Check if beeJson is already an object (happens when layout type changes)
115
+ const parseJson = typeof beeJson === 'string' ? JSON.parse(beeJson) : beeJson;
123
116
  beePluginInstance.start(parseJson);
124
117
  saveBeeInstance(beePluginInstance, device);
125
118
  isInitializedRef.current = true;
@@ -1078,38 +1078,8 @@ export function SlideBoxContent(props) {
1078
1078
  )}
1079
1079
 
1080
1080
  {isCreateInApp && (
1081
- (isFullMode && !commonUtil.hasNewEditorFlowInAppEnabled()) ||
1082
- (!isFullMode && isLoyaltyModule) ||
1083
- (!isFullMode && !isLoyaltyModule && !commonUtil.hasNewEditorFlowInAppEnabled()) ? (
1084
- <InApp
1085
- key="creatives-inapp-create"
1086
- location={{ pathname: '/inapp/create', query, search: '' }}
1087
- setIsLoadingContent={setIsLoadingContent}
1088
- isGetFormData={isGetFormData}
1089
- getFormData={getFormData}
1090
- getDefaultTags={type}
1091
- isFullMode={isFullMode}
1092
- templateData={templateData}
1093
- cap={cap}
1094
- showTemplateName={showTemplateName}
1095
- showLiquidErrorInFooter={showLiquidErrorInFooter}
1096
- onValidationFail={onValidationFail}
1097
- forwardedTags={forwardedTags}
1098
- selectedOfferDetails={selectedOfferDetails}
1099
- onPreviewContentClicked={onPreviewContentClicked}
1100
- onTestContentClicked={onTestContentClicked}
1101
- eventContextTags={eventContextTags}
1102
- onCreateComplete={onCreateComplete}
1103
- handleClose={handleClose}
1104
- moduleType={moduleType}
1105
- showTestAndPreviewSlidebox={showTestAndPreviewSlidebox}
1106
- handleTestAndPreview={handleTestAndPreview}
1107
- handleCloseTestAndPreview={handleCloseTestAndPreview}
1108
- isTestAndPreviewMode={isTestAndPreviewMode}
1109
- />
1110
- ) : (
1111
- <InAppWrapper
1112
- key="creatives-inapp-wrapper"
1081
+ <InAppWrapper
1082
+ key="creatives-inapp-wrapper"
1113
1083
  date={new Date().getMilliseconds()}
1114
1084
  setIsLoadingContent={setIsLoadingContent}
1115
1085
  onInAppEditorTypeChange={onInAppEditorTypeChange}
@@ -1144,12 +1114,10 @@ export function SlideBoxContent(props) {
1144
1114
  handleCloseTestAndPreview={handleCloseTestAndPreview}
1145
1115
  isTestAndPreviewMode={isTestAndPreviewMode}
1146
1116
  />
1147
- )
1148
1117
  )}
1149
-
1118
+
1150
1119
  {isEditInApp && (<InApp
1151
1120
  isFullMode={isFullMode}
1152
- isLoyaltyModule={isLoyaltyModule}
1153
1121
  templateData={templateData}
1154
1122
  getFormData={getFormData}
1155
1123
  getDefaultTags={type}
@@ -14,8 +14,6 @@ import CapRow from "@capillarytech/cap-ui-library/CapRow";
14
14
  import CapColumn from "@capillarytech/cap-ui-library/CapColumn";
15
15
  import CapButton from "@capillarytech/cap-ui-library/CapButton";
16
16
  import CapNotification from "@capillarytech/cap-ui-library/CapNotification";
17
- import CapTab from "@capillarytech/cap-ui-library/CapTab";
18
- import CapInput from "@capillarytech/cap-ui-library/CapInput";
19
17
  import { makeSelectInApp, makeSelectAccount, makeSelectGetTemplateDetailsInProgress } from "./selectors";
20
18
  import * as globalActions from '../Cap/actions';
21
19
  import {
@@ -54,20 +52,18 @@ import {
54
52
  IOS_CAPITAL,
55
53
  } from "./constants";
56
54
  import { INAPP, SMS } from "../CreativesContainer/constants";
57
- import { AI_CONTENT_BOT_DISABLED } from "../../constants/unified";
58
55
  import {
59
56
  ALL, TAG, EMBEDDED, DEFAULT, FULL, LIBRARY,
60
57
  } from "../Whatsapp/constants";
61
58
  import { getCdnUrl } from "../../utils/cdnTransformation";
62
59
  import { getCtaObject, hasAnyErrors, getSingleTab } from "./utils";
63
60
  import { validateInAppContent } from "../../utils/commonUtils";
64
- import { hasLiquidSupportFeature, hasNewEditorFlowInAppEnabled } from "../../utils/common";
61
+ import { hasLiquidSupportFeature } from "../../utils/common";
65
62
  import formBuilderMessages from "../../v2Components/FormBuilder/messages";
66
63
  import HTMLEditor from "../../v2Components/HtmlEditor";
67
64
  import { HTML_EDITOR_VARIANTS } from "../../v2Components/HtmlEditor/constants";
68
65
  import { INAPP_EDITOR_TYPES } from "../InAppWrapper/constants";
69
66
  import InappAdvanced from "../InappAdvance/index";
70
- import CapDeviceContent from "../../v2Components/CapDeviceContent";
71
67
  import { ErrorInfoNote } from "../../v2Components/ErrorInfoNote";
72
68
 
73
69
  let editContent = {};
@@ -78,7 +74,6 @@ export const InApp = (props) => {
78
74
  actions,
79
75
  globalActions,
80
76
  isFullMode,
81
- isLoyaltyModule,
82
77
  onCreateComplete,
83
78
  params,
84
79
  templateData = {},
@@ -1245,13 +1240,11 @@ export const InApp = (props) => {
1245
1240
  && !isBEEeditor
1246
1241
  && !isBeeFreeTemplate;
1247
1242
 
1248
- const isNewEditorFlowEnabled = !isLoyaltyModule && hasNewEditorFlowInAppEnabled();
1249
-
1250
1243
  // Use state if available, otherwise fall back to direct data check
1251
- const shouldUseHTMLEditor = isNewEditorFlowEnabled && (isHTMLTemplate || isHTMLTemplateFromData);
1244
+ const shouldUseHTMLEditor = isHTMLTemplate || isHTMLTemplateFromData;
1252
1245
 
1253
1246
  // Only route to Bee editor if it's explicitly a Bee editor AND not an HTML template
1254
- const shouldUseBeeEditor = isNewEditorFlowEnabled && (isBEEeditor || isBeeFreeTemplate) && !shouldUseHTMLEditor;
1247
+ const shouldUseBeeEditor = (isBEEeditor || isBeeFreeTemplate) && !shouldUseHTMLEditor;
1255
1248
 
1256
1249
  // Early returns to avoid nested ternary
1257
1250
  if (isEditInApp && getTemplateDetailsInProgress) {
@@ -1287,157 +1280,10 @@ export const InApp = (props) => {
1287
1280
  );
1288
1281
  }
1289
1282
 
1290
- // ── Old-flow helpers (used by CapDeviceContent when flag is disabled) ──────
1291
- const isAiContentBotDisabled = currentOrgDetails?.accessibleFeatures?.includes(AI_CONTENT_BOT_DISABLED);
1292
-
1293
- const templateDescErrorHandler = (value) => {
1294
- const { unsupportedTags, isBraceError } = validateTags({
1295
- content: value,
1296
- tagsParam: tags,
1297
- injectedTagsParams: injectedTags,
1298
- location,
1299
- tagModule: getDefaultTags,
1300
- isFullMode,
1301
- }) || {};
1302
- if (unsupportedTags?.length > 0) {
1303
- return formatMessage(globalMessages.unsupportedTagsValidationError, { unsupportedTags });
1304
- }
1305
- if (isBraceError) {
1306
- return formatMessage(globalMessages.braceValidationError);
1307
- }
1308
- return '';
1309
- };
1310
-
1311
- const onCopyTitleAndContent = () => {
1312
- if (panes === ANDROID) {
1313
- setTitleAndroid(titleIos);
1314
- setTemplateMessageAndroid(templateMessageIos);
1315
- setInAppImageSrcAndroid(inAppImageSrcIos);
1316
- } else {
1317
- setTitleIos(titleAndroid);
1318
- setTemplateMessageIos(templateMessageAndroid);
1319
- setInAppImageSrcIos(inAppImageSrcAndroid);
1320
- }
1321
- };
1322
-
1323
- const onTagSelect = (value, index) => {
1324
- const tag = `{{${value}}}`;
1325
- if (panes === ANDROID) {
1326
- if (index === 0) setTitleAndroid((prev) => prev + tag);
1327
- else setTemplateMessageAndroid((prev) => prev + tag);
1328
- } else if (index === 0) {
1329
- setTitleIos((prev) => prev + tag);
1330
- } else {
1331
- setTemplateMessageIos((prev) => prev + tag);
1332
- }
1333
- };
1334
-
1335
- // Device support flags (same derivation as InappAdvance)
1336
- const isAndroidSupported = get(accountData, 'selectedWeChatAccount.configs.android') === DEVICE_SUPPORTED || get(editContent, 'ANDROID.deviceType') === ANDROID;
1337
- const isIosSupported = get(accountData, 'selectedWeChatAccount.configs.ios') === DEVICE_SUPPORTED || get(editContent, 'IOS.deviceType') === IOS_CAPITAL;
1338
-
1339
- // CapDeviceContent tab panes (old flow)
1340
- const DEVICE_PANES = [
1341
- {
1342
- content: (
1343
- <CapDeviceContent
1344
- panes={ANDROID}
1345
- actions={actions}
1346
- editData={editData}
1347
- isFullMode={isFullMode}
1348
- inAppImageSrc={inAppImageSrcAndroid}
1349
- setInAppImageSrc={setInAppImageSrcAndroid}
1350
- isEditFlow={isEditFlow}
1351
- ctaData={ctaDataAndroid}
1352
- setCtaData={setCtaDataAndroid}
1353
- buttonType={buttonTypeAndroid}
1354
- setButtonType={setButtonTypeAndroid}
1355
- templateMediaType={templateMediaType}
1356
- setTemplateMediaType={setTemplateMediaType}
1357
- title={titleAndroid}
1358
- setTitle={setTitleAndroid}
1359
- templateMessageError={templateMessageErrorAndroid}
1360
- templateMessage={templateMessageAndroid}
1361
- setTemplateMessage={setTemplateMessageAndroid}
1362
- setTemplateMessageError={setTemplateMessageErrorAndroid}
1363
- addActionLink={addActionLinkAndroid}
1364
- setAddActionLink={setAddActionLinkAndroid}
1365
- deepLink={deepLink}
1366
- deepLinkValue={deepLinkValueAndroid}
1367
- setDeepLinkValue={setDeepLinkValueAndroid}
1368
- onCopyTitleAndContent={onCopyTitleAndContent}
1369
- isOtherDeviceSupported={isIosSupported}
1370
- tags={tags}
1371
- onTagSelect={onTagSelect}
1372
- handleOnTagsContextChange={handleOnTagsContextChange}
1373
- templateDescErrorHandler={templateDescErrorHandler}
1374
- templateTitleError={templateTitleErrorAndroid}
1375
- setTemplateTitleError={setTemplateTitleErrorAndroid}
1376
- isAiContentBotDisabled={isAiContentBotDisabled}
1377
- injectedTags={injectedTags}
1378
- selectedOfferDetails={selectedOfferDetails}
1379
- location={location}
1380
- />
1381
- ),
1382
- tab: <FormattedMessage {...messages.Android} />,
1383
- key: ANDROID,
1384
- isSupported: isAndroidSupported,
1385
- },
1386
- {
1387
- content: (
1388
- <CapDeviceContent
1389
- panes={IOS}
1390
- actions={actions}
1391
- editData={editData}
1392
- isFullMode={isFullMode}
1393
- inAppImageSrc={inAppImageSrcIos}
1394
- setInAppImageSrc={setInAppImageSrcIos}
1395
- isEditFlow={isEditFlow}
1396
- ctaData={ctaDataIos}
1397
- setCtaData={setCtaDataIos}
1398
- buttonType={buttonTypeIos}
1399
- setButtonType={setButtonTypeIos}
1400
- templateMediaType={templateMediaType}
1401
- setTemplateMediaType={setTemplateMediaType}
1402
- title={titleIos}
1403
- setTitle={setTitleIos}
1404
- templateMessageError={templateMessageErrorIos}
1405
- templateMessage={templateMessageIos}
1406
- setTemplateMessage={setTemplateMessageIos}
1407
- setTemplateMessageError={setTemplateMessageErrorIos}
1408
- addActionLink={addActionLinkIos}
1409
- setAddActionLink={setAddActionLinkIos}
1410
- deepLink={deepLink}
1411
- deepLinkValue={deepLinkValueIos}
1412
- setDeepLinkValue={setDeepLinkValueIos}
1413
- onCopyTitleAndContent={onCopyTitleAndContent}
1414
- isOtherDeviceSupported={isAndroidSupported}
1415
- tags={tags}
1416
- onTagSelect={onTagSelect}
1417
- handleOnTagsContextChange={handleOnTagsContextChange}
1418
- templateDescErrorHandler={templateDescErrorHandler}
1419
- templateTitleError={templateTitleErrorIos}
1420
- setTemplateTitleError={setTemplateTitleErrorIos}
1421
- isAiContentBotDisabled={isAiContentBotDisabled}
1422
- injectedTags={injectedTags}
1423
- selectedOfferDetails={selectedOfferDetails}
1424
- location={location}
1425
- />
1426
- ),
1427
- tab: <FormattedMessage {...messages.Ios} />,
1428
- key: IOS,
1429
- isSupported: isIosSupported,
1430
- },
1431
- ];
1432
- // ─────────────────────────────────────────────────────────────────────────
1433
-
1434
- // Calculate column span: HTML editor = 18, everything else = 24
1435
- const editorColumnSpan = shouldUseHTMLEditor ? 18 : 24;
1436
-
1437
1283
  return (
1438
1284
  <CapSpin spinning={spin || fetchingLiquidValidation} tip={fetchingLiquidValidation ? <FormattedMessage {...formBuilderMessages.liquidSpinText} /> : ""}>
1439
1285
  <CapRow className="cap-inapp-creatives">
1440
- <CapColumn span={editorColumnSpan}>
1286
+ <CapColumn span={shouldUseHTMLEditor ? 18 : 24}>
1441
1287
  {/* Creative layout type */}
1442
1288
  {shouldUseHTMLEditor && (
1443
1289
  <>
@@ -1458,7 +1304,7 @@ export const InApp = (props) => {
1458
1304
  />
1459
1305
  </>
1460
1306
  )}
1461
- {shouldUseHTMLEditor && (
1307
+ {shouldUseHTMLEditor ? (
1462
1308
  <HTMLEditor
1463
1309
  key={`inapp-html-editor-v${htmlEditorContentVersion}`}
1464
1310
  variant={HTML_EDITOR_VARIANTS.INAPP}
@@ -1474,16 +1320,19 @@ export const InApp = (props) => {
1474
1320
  location={location}
1475
1321
  selectedOfferDetails={selectedOfferDetails}
1476
1322
  onTagSelect={() => {
1477
- // Tag insertion handled by HTMLEditor's CodeEditorPane at cursor position
1323
+ // Tag insertion is handled by HTMLEditor's CodeEditorPane at cursor position
1324
+ // Content updates will be propagated via onContentChange callback
1478
1325
  }}
1326
+ // Don't pass globalActions to prevent duplicate API calls
1327
+ // HTMLEditor will use onContextChange instead
1479
1328
  onContextChange={handleOnTagsContextChange}
1329
+ // Pass validation errors to HTMLEditor for display
1480
1330
  errors={errorMessage}
1481
1331
  isFullMode={isFullMode}
1482
1332
  data-test="inapp-html-editor"
1483
1333
  style={{ width: '138%' }}
1484
1334
  />
1485
- )}
1486
- {!shouldUseHTMLEditor && isNewEditorFlowEnabled && (
1335
+ ) : (
1487
1336
  <InappAdvanced
1488
1337
  getFormData={getFormData}
1489
1338
  setIsLoadingContent={setIsLoadingContent}
@@ -1508,29 +1357,11 @@ export const InApp = (props) => {
1508
1357
  onCreateComplete={onCreateComplete}
1509
1358
  />
1510
1359
  )}
1511
- {!shouldUseHTMLEditor && !isNewEditorFlowEnabled && (
1512
- <>
1513
- <CapInput
1514
- label={<FormattedMessage {...messages.creativeName} />}
1515
- onChange={({ target: { value } }) => setTempName(value)}
1516
- value={tempName}
1517
- labelPosition="top"
1518
- size="default"
1519
- />
1520
- <CapTab
1521
- panes={DEVICE_PANES.filter((devicePane) => devicePane?.isSupported === true)}
1522
- onChange={(value) => setPanes(value)}
1523
- activeKey={panes}
1524
- defaultActiveKey={panes}
1525
- className="inapp-template-device-tab"
1526
- />
1527
- </>
1528
- )}
1529
1360
  </CapColumn>
1530
1361
  </CapRow>
1531
- {/* Footer with Done/Update button - show for HTML editor and old CapDeviceContent flow */}
1362
+ {/* Footer with Done/Update button - Only show for HTML editor, bee editor has its own footer */}
1532
1363
  {
1533
- (shouldUseHTMLEditor || !isNewEditorFlowEnabled) && (
1364
+ shouldUseHTMLEditor && (
1534
1365
  <>
1535
1366
  {hasAnyErrors(errorMessage) && (
1536
1367
  <ErrorInfoNote