@capillarytech/creatives-library 8.0.168-beta.1 → 8.0.169

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 (33) hide show
  1. package/package.json +1 -1
  2. package/utils/test-utils.js +6 -2
  3. package/v2Components/CapActionButton/index.js +52 -12
  4. package/v2Components/CapActionButton/messages.js +4 -0
  5. package/v2Components/CapActionButton/tests/index.test.js +135 -0
  6. package/v2Components/CapDeviceContent/index.js +5 -0
  7. package/v2Components/CapInAppCTA/index.js +29 -14
  8. package/v2Components/CapInAppCTA/index.scss +0 -2
  9. package/v2Components/CapInAppCTA/messages.js +4 -0
  10. package/v2Components/CapMpushCTA/index.js +54 -38
  11. package/v2Components/CapMpushCTA/index.scss +2 -2
  12. package/v2Components/CapMpushCTA/messages.js +4 -0
  13. package/v2Components/CapTagListWithInput/index.js +169 -0
  14. package/v2Components/CapTagListWithInput/messages.js +10 -0
  15. package/v2Components/FormBuilder/index.js +93 -1
  16. package/v2Components/TestAndPreviewSlidebox/PreviewSection.js +1 -1
  17. package/v2Components/TestAndPreviewSlidebox/index.js +24 -4
  18. package/v2Containers/CreativesContainer/index.js +1 -1
  19. package/v2Containers/Email/index.js +64 -3
  20. package/v2Containers/Email/initialSchema.js +7 -21
  21. package/v2Containers/EmailWrapper/hooks/useEmailWrapper.js +1 -1
  22. package/v2Containers/MobilePush/Create/index.js +24 -3
  23. package/v2Containers/MobilePush/commonMethods.js +25 -3
  24. package/v2Containers/MobilePushNew/components/CtaButtons.js +20 -0
  25. package/v2Containers/MobilePushNew/components/MediaUploaders.js +31 -3
  26. package/v2Containers/MobilePushNew/components/PlatformContentFields.js +34 -3
  27. package/v2Containers/MobilePushNew/components/tests/MediaUploaders.test.js +200 -5
  28. package/v2Containers/MobilePushNew/components/tests/PlatformContentFields.test.js +59 -1
  29. package/v2Containers/MobilePushNew/index.js +9 -0
  30. package/v2Containers/MobilePushNew/index.scss +2 -1
  31. package/v2Containers/Rcs/index.js +77 -71
  32. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +15270 -492
  33. package/v2Containers/Viber/index.js +102 -76
@@ -0,0 +1,169 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { injectIntl, intlShape } from 'react-intl';
4
+ import CapRow from '@capillarytech/cap-ui-library/CapRow';
5
+ import CapHeading from '@capillarytech/cap-ui-library/CapHeading';
6
+ import CapInput from '@capillarytech/cap-ui-library/CapInput';
7
+ import CapColumn from '@capillarytech/cap-ui-library/CapColumn';
8
+ import TagList from '../../v2Containers/TagList';
9
+ import messages from './messages';
10
+
11
+ /**
12
+ * CapTagListWithInput - A reusable component that combines TagList and CapInput
13
+ * for external links/URLs that can be used across all channels
14
+ */
15
+ export const CapTagListWithInput = (props) => {
16
+ const {
17
+ intl,
18
+ // TagList props
19
+ tags = [],
20
+ injectedTags = {},
21
+ location = {},
22
+ selectedOfferDetails = [],
23
+ onTagSelect,
24
+ onContextChange,
25
+ moduleFilterEnabled = true,
26
+ className = '',
27
+ userLocale = 'en',
28
+ eventContextTags = [],
29
+ // CapInput props
30
+ inputId,
31
+ inputValue = '',
32
+ inputOnChange,
33
+ inputPlaceholder,
34
+ inputErrorMessage = '',
35
+ inputSize = 'default',
36
+ inputMaxLength,
37
+ inputRequired = false,
38
+ inputDisabled = false,
39
+ // Layout props
40
+ headingText,
41
+ headingType = 'h4',
42
+ headingStyle = {},
43
+ containerStyle = {},
44
+ tagListStyle = {},
45
+ inputStyle = {},
46
+ // Custom props
47
+ showHeading = true,
48
+ showTagList = true,
49
+ showInput = true,
50
+ inputProps = {},
51
+ } = props;
52
+
53
+ const { formatMessage } = intl;
54
+
55
+ return (
56
+ <CapColumn style={containerStyle}>
57
+ <CapRow style={{display: 'flex', flexDirection: 'row'}}>
58
+ {showHeading && headingText && (
59
+ <CapHeading type={headingType} style={headingStyle}>
60
+ {headingText}
61
+ </CapHeading>
62
+ )}
63
+
64
+ {showTagList && (
65
+ <TagList
66
+ key={`${inputId}_tags`}
67
+ className={className}
68
+ moduleFilterEnabled={moduleFilterEnabled}
69
+ label={formatMessage(messages.addLabels)}
70
+ onTagSelect={onTagSelect}
71
+ onContextChange={onContextChange}
72
+ location={location}
73
+ tags={tags}
74
+ injectedTags={injectedTags}
75
+ userLocale={userLocale}
76
+ selectedOfferDetails={selectedOfferDetails}
77
+ eventContextTags={eventContextTags}
78
+ style={tagListStyle}
79
+ />
80
+ )}
81
+ </CapRow>
82
+ {showInput && (
83
+ <CapInput
84
+ id={inputId}
85
+ onChange={inputOnChange}
86
+ placeholder={inputPlaceholder}
87
+ value={inputValue}
88
+ size={inputSize}
89
+ maxLength={inputMaxLength}
90
+ errorMessage={inputErrorMessage}
91
+ isRequired={inputRequired}
92
+ disabled={inputDisabled}
93
+ style={inputStyle}
94
+ {...inputProps}
95
+ />
96
+ )}
97
+ </CapColumn>
98
+ );
99
+ };
100
+
101
+ CapTagListWithInput.propTypes = {
102
+ intl: intlShape.isRequired,
103
+ // TagList props
104
+ tags: PropTypes.array,
105
+ injectedTags: PropTypes.object,
106
+ location: PropTypes.object,
107
+ selectedOfferDetails: PropTypes.array,
108
+ onTagSelect: PropTypes.func.isRequired,
109
+ onContextChange: PropTypes.func.isRequired,
110
+ moduleFilterEnabled: PropTypes.bool,
111
+ className: PropTypes.string,
112
+ userLocale: PropTypes.string,
113
+ eventContextTags: PropTypes.array,
114
+
115
+ // CapInput props
116
+ inputId: PropTypes.string.isRequired,
117
+ inputValue: PropTypes.string,
118
+ inputOnChange: PropTypes.func.isRequired,
119
+ inputPlaceholder: PropTypes.string,
120
+ inputErrorMessage: PropTypes.string,
121
+ inputSize: PropTypes.oneOf(['small', 'default', 'large']),
122
+ inputMaxLength: PropTypes.number,
123
+ inputRequired: PropTypes.bool,
124
+ inputDisabled: PropTypes.bool,
125
+ inputProps: PropTypes.object,
126
+
127
+ // Layout props
128
+ headingText: PropTypes.string,
129
+ headingType: PropTypes.oneOf(['h1', 'h2', 'h3', 'h4', 'h5', 'h6']),
130
+ headingStyle: PropTypes.object,
131
+ containerStyle: PropTypes.object,
132
+ tagListStyle: PropTypes.object,
133
+ inputStyle: PropTypes.object,
134
+
135
+ // Custom props
136
+ showHeading: PropTypes.bool,
137
+ showTagList: PropTypes.bool,
138
+ showInput: PropTypes.bool,
139
+ };
140
+
141
+ CapTagListWithInput.defaultProps = {
142
+ tags: [],
143
+ injectedTags: {},
144
+ location: {},
145
+ selectedOfferDetails: [],
146
+ moduleFilterEnabled: true,
147
+ className: '',
148
+ userLocale: 'en',
149
+ eventContextTags: [],
150
+ inputValue: '',
151
+ inputSize: 'default',
152
+ inputRequired: false,
153
+ inputDisabled: false,
154
+ inputPlaceholder: '',
155
+ inputErrorMessage: '',
156
+ inputMaxLength: undefined,
157
+ headingText: '',
158
+ headingType: 'h4',
159
+ headingStyle: {},
160
+ containerStyle: {},
161
+ tagListStyle: {},
162
+ inputStyle: {},
163
+ showHeading: true,
164
+ showTagList: true,
165
+ showInput: true,
166
+ inputProps: {},
167
+ };
168
+
169
+ export default injectIntl(CapTagListWithInput);
@@ -0,0 +1,10 @@
1
+ import { defineMessages } from 'react-intl';
2
+
3
+ const prefix = 'creatives.componentsV2.CapTagListWithInput';
4
+
5
+ export default defineMessages({
6
+ addLabels: {
7
+ id: `${prefix}.addLabels`,
8
+ defaultMessage: 'Add labels',
9
+ },
10
+ });
@@ -37,6 +37,7 @@ import { createStructuredSelector } from 'reselect';
37
37
  import { CAP_SPACE_12, CAP_SPACE_08, FONT_COLOR_05, FONT_COLOR_04 } from '@capillarytech/cap-ui-library/styled/variables';
38
38
  import TemplatePreview from '../TemplatePreview';
39
39
  import TagList from '../../v2Containers/TagList';
40
+ import CapTagListWithInput from '../CapTagListWithInput';
40
41
  import SlideBox from '../SlideBox';
41
42
  import CardGrid from '../CardGrid';
42
43
  import CKEditor from "../Ckeditor/";
@@ -2743,6 +2744,40 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
2743
2744
  </CapColumn>
2744
2745
  );
2745
2746
  break;
2747
+ case "cap-tag-list-with-input":
2748
+ ifError = this.state.checkValidation && this.state.errorData[val.id];
2749
+ columns.push(
2750
+ <CapColumn key={`input-${val.id}`} span={val.width || 10} offset={val.offset}>
2751
+ <CapTagListWithInput
2752
+ key={`input-${val.id}`}
2753
+ inputId={val.id}
2754
+ inputValue={this.state.formData[val.id] || ''}
2755
+ inputOnChange={(e) => this.updateFormData(e.target.value, val)}
2756
+ inputPlaceholder={val.placeholder || ''}
2757
+ inputErrorMessage={val.errorMessage && ifError ? val.errorMessage : ''}
2758
+ inputRequired={val.required || false}
2759
+ inputDisabled={val.disabled || false}
2760
+ headingText={val.label || ''}
2761
+ headingStyle={val.headingStyle || { marginTop: '3%', marginRight: '79%' }}
2762
+ headingType="h4"
2763
+ onTagSelect={(data) => this.callChildEvent(data, val, 'onTagSelect')}
2764
+ onContextChange={this.props.onContextChange}
2765
+ location={this.props.location}
2766
+ tags={this.props.tags ? this.props.tags : []}
2767
+ injectedTags={this.props.injectedTags ? this.props.injectedTags : {}}
2768
+ className={val.className ? val.className : ''}
2769
+ userLocale={this.props.userLocale}
2770
+ selectedOfferDetails={this.props.selectedOfferDetails}
2771
+ eventContextTags={this.props?.eventContextTags}
2772
+ moduleFilterEnabled={this.props.location && this.props.location.query && this.props.location.query.type !== 'embedded'}
2773
+ containerStyle={val.style || {}}
2774
+ inputProps={val.inputProps || {}}
2775
+ showInput={val.showInput !== false}
2776
+ showTagList={val.showTagList !== false}
2777
+ />
2778
+ </CapColumn>
2779
+ );
2780
+ break;
2746
2781
  case "tabs":
2747
2782
  columns.push(
2748
2783
  <CapColumn key="input" span={10}>
@@ -3343,7 +3378,13 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
3343
3378
  const currentLang = (!_.isEmpty(this.state.formData) && !_.isEmpty(this.state.formData[`${this.state.currentTab - 1}`]) && !_.isEmpty(this.state.formData[`${this.state.currentTab - 1}`].selectedLanguages) && this.state.formData[`${this.state.currentTab - 1}`].selectedLanguages[langIndex]) ? this.state.formData[`${this.state.currentTab - 1}`].selectedLanguages[langIndex] : this.props.baseLanguage;
3344
3379
  const isBEEAppEnable = this.checkBeeEditorAllowedForLibrary();
3345
3380
 
3346
- if (!(_.get(this.state, `formData[${this.state.currentTab - 1}][${currentLang}].is_drag_drop`, false)) || isBEEAppEnable === false || channel !== 'EMAIL') {
3381
+ // Always render Subject TagList (title-tagList) even when Bee editor is active for EMAIL channel
3382
+ if (
3383
+ val.id === 'title-tagList' ||
3384
+ !(_.get(this.state, `formData[${this.state.currentTab - 1}][${currentLang}].is_drag_drop`, false)) ||
3385
+ isBEEAppEnable === false ||
3386
+ channel !== 'EMAIL'
3387
+ ) {
3347
3388
  columns.push(
3348
3389
  <CapColumn key={`input-${val.id}`} offset={val.offset} span={val.width ? val.width : ''} style={val.style ? val.style : {marginBottom: '16px'}}>
3349
3390
  <TagList
@@ -3366,6 +3407,57 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
3366
3407
  );
3367
3408
  }
3368
3409
  break;
3410
+ case "cap-tag-list-with-input":
3411
+ let moduleFilterEnabledForCapTagList = this.props.location && this.props.location.query && this.props.location.query.type !== 'embedded';
3412
+ const channelForCapTagList = _.get(this.props, 'schema.channel', "");
3413
+ if (channelForCapTagList === 'EMAIL') {
3414
+ moduleFilterEnabledForCapTagList = this.props.isFullMode;
3415
+ }
3416
+ const langIndexForCapTagList = 0;
3417
+ const currentLangForCapTagList = (!_.isEmpty(this.state.formData) && !_.isEmpty(this.state.formData[`${this.state.currentTab - 1}`]) && !_.isEmpty(this.state.formData[`${this.state.currentTab - 1}`].selectedLanguages) && this.state.formData[`${this.state.currentTab - 1}`].selectedLanguages[langIndexForCapTagList]) ? this.state.formData[`${this.state.currentTab - 1}`].selectedLanguages[langIndexForCapTagList] : this.props.baseLanguage;
3418
+ const isBEEAppEnableForCapTagList = this.checkBeeEditorAllowedForLibrary();
3419
+ ifError = this.state.checkValidation && (isVersionEnable ? this.state.errorData[`${this.state.currentTab - 1}`][val.id] : this.state.errorData[val.id]);
3420
+
3421
+ // Always render Subject CapTagListWithInput even when Bee editor is active for EMAIL channel
3422
+ if (
3423
+ val.id === 'template-subject' ||
3424
+ !(_.get(this.state, `formData[${this.state.currentTab - 1}][${currentLangForCapTagList}].is_drag_drop`, false)) ||
3425
+ isBEEAppEnableForCapTagList === false ||
3426
+ channelForCapTagList !== 'EMAIL'
3427
+ ) {
3428
+ columns.push(
3429
+ <CapColumn key={`input-${val.id}`} offset={val.offset} span={val.width ? val.width : ''} style={val.style ? val.style : {marginBottom: '16px'}}>
3430
+ <CapTagListWithInput
3431
+ key={`input-${val.id}`}
3432
+ inputId={val.id}
3433
+ inputValue={this.state.formData[val.id] || ''}
3434
+ inputOnChange={(e) => this.updateFormData(e.target.value, val)}
3435
+ inputPlaceholder={val.placeholder || ''}
3436
+ inputErrorMessage={val.errorMessage && ifError ? val.errorMessage : ''}
3437
+ inputRequired={val.required || false}
3438
+ inputDisabled={val.disabled || false}
3439
+ headingText={val.label || ''}
3440
+ headingStyle={val.headingStyle || { marginTop: '3%', marginRight: '79%' }}
3441
+ headingType="h4"
3442
+ onTagSelect={(data) => this.callChildEvent(data, val, 'onTagSelect')}
3443
+ onContextChange={this.props.onContextChange}
3444
+ location={this.props.location}
3445
+ tags={this.props.tags ? this.props.tags : []}
3446
+ injectedTags={this.props.injectedTags ? this.props.injectedTags : {}}
3447
+ className={val.className ? val.className : ''}
3448
+ userLocale={this.state.translationLang}
3449
+ selectedOfferDetails={this.props.selectedOfferDetails}
3450
+ eventContextTags={this.props?.eventContextTags}
3451
+ moduleFilterEnabled={moduleFilterEnabledForCapTagList}
3452
+ containerStyle={val.style || {}}
3453
+ inputProps={val.inputProps || {}}
3454
+ showInput={val.showInput !== false}
3455
+ showTagList={val.showTagList !== false}
3456
+ />
3457
+ </CapColumn>
3458
+ );
3459
+ }
3460
+ break;
3369
3461
  case "button":
3370
3462
  if (styling === 'semantic' && val.metaType === 'submit-button') {
3371
3463
  columns.push(
@@ -20,7 +20,7 @@ const PreviewSection = ({
20
20
  device={previewDevice}
21
21
  onDeviceChange={setPreviewDevice}
22
22
  customer={selectedCustomer}
23
- subject={formData['template-subject']}
23
+ subject={previewDataHtml?.resolvedTitle || formData['template-subject']}
24
24
  >
25
25
  {isUpdatingPreview && (
26
26
  <CapRow className="loading-container">
@@ -108,6 +108,21 @@ const TestAndPreviewSlidebox = (props) => {
108
108
  requiredTags.some((tag) => !customValues[tag.fullPath])
109
109
  ), [requiredTags, customValues]);
110
110
 
111
+ // Function to resolve tags in text with custom values
112
+ const resolveTagsInText = (text, tagValues) => {
113
+ if (!text) return text;
114
+ let resolvedText = text;
115
+
116
+ // Replace each tag with its custom value
117
+ Object.keys(tagValues).forEach((tagPath) => {
118
+ const tagName = tagPath.split('.').pop(); // Get the actual tag name from the path
119
+ const tagRegex = new RegExp(`{{${tagName}}}`, 'g');
120
+ resolvedText = resolvedText.replace(tagRegex, tagValues[tagPath] || `{{${tagName}}}`);
121
+ });
122
+
123
+ return resolvedText;
124
+ };
125
+
111
126
  // Get the current content based on editor type
112
127
  const getCurrentContent = useMemo(() => {
113
128
  const currentTabData = formData[currentTab - 1];
@@ -380,9 +395,10 @@ const TestAndPreviewSlidebox = (props) => {
380
395
  setCustomValues(updatedValues);
381
396
 
382
397
  // Update preview with prefilled values
398
+ const resolvedSubject = resolveTagsInText(formData['template-subject'], updatedValues);
383
399
  const payload = {
384
400
  channel: EMAIL,
385
- messageTitle: formData['template-subject'],
401
+ messageTitle: resolvedSubject,
386
402
  messageBody: getCurrentContent,
387
403
  resolvedTags: updatedValues,
388
404
  userId: selectedCustomer?.customerId,
@@ -501,9 +517,10 @@ const TestAndPreviewSlidebox = (props) => {
501
517
  setCustomValues(emptyValues);
502
518
 
503
519
  // Update preview with empty values
520
+ const resolvedSubject = resolveTagsInText(formData['template-subject'], emptyValues);
504
521
  const payload = {
505
522
  channel: EMAIL,
506
- messageTitle: formData['template-subject'],
523
+ messageTitle: resolvedSubject,
507
524
  messageBody: getCurrentContent,
508
525
  resolvedTags: emptyValues,
509
526
  userId: selectedCustomer?.customerId,
@@ -517,9 +534,12 @@ const TestAndPreviewSlidebox = (props) => {
517
534
  // Include unsubscribe tag if content contains it
518
535
  const resolvedTags = { ...customValues };
519
536
 
537
+ // Resolve subject tags with custom values
538
+ const resolvedSubject = resolveTagsInText(formData['template-subject'], customValues);
539
+
520
540
  const payload = {
521
541
  channel: EMAIL,
522
- messageTitle: formData['template-subject'],
542
+ messageTitle: resolvedSubject,
523
543
  messageBody: getCurrentContent,
524
544
  resolvedTags,
525
545
  userId: selectedCustomer?.customerId,
@@ -585,7 +605,7 @@ const TestAndPreviewSlidebox = (props) => {
585
605
  emailMessageContent: {
586
606
  channel: EMAIL,
587
607
  messageBody: previewData?.resolvedBody || getCurrentContent,
588
- messageSubject: previewData?.resolvedTitle || formData['template-subject'],
608
+ messageSubject: previewData?.resolvedTitle || resolveTagsInText(formData['template-subject'], customValues),
589
609
  },
590
610
  }, messageMetaConfigId, (response) => {
591
611
  const payload = {
@@ -1715,6 +1715,6 @@ const withConnect = connect(mapStatesToProps, mapDispatchToProps);
1715
1715
  const withReducer = injectReducer({ key: 'creativesContainer', reducer: creativesContainerReducer });
1716
1716
  const withSaga = injectSaga({ key: 'cap', saga: capSagaForFetchSchemaForEntity });
1717
1717
  const withLiquidSaga = injectSaga({ key: 'liquid', saga: capSagaLiquidEntity, mode: DAEMON });
1718
- const withDefaultTempSaga = injectSaga({ key: 'creativesContainer', saga: v2TemplateSagaWatchGetDefaultBeeTemplates, mode: DAEMON});
1718
+ const withDefaultTempSaga = injectSaga({ key: 'creativesContainer', saga: v2TemplateSagaWatchGetDefaultBeeTemplates });
1719
1719
 
1720
1720
  export default compose(withSaga, withLiquidSaga, withDefaultTempSaga, withReducer, withConnect)(injectIntl(Creatives));
@@ -100,6 +100,14 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
100
100
  "template-name": {
101
101
  onChange: this.onTemplateNameChange,
102
102
  },
103
+ // allow tag insertion into Subject input field via a dedicated TagList
104
+ "title-tagList": {
105
+ onTagSelect: this.onTagSelect,
106
+ },
107
+ // unified subject field with input and tag selection
108
+ "template-subject": {
109
+ onTagSelect: this.onTagSelect,
110
+ },
103
111
  "template-version": {
104
112
  onSelect: this.handleVersionSelect,
105
113
  addVersion: this.addVersion,
@@ -646,7 +654,44 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
646
654
 
647
655
  }
648
656
 
649
- onTagSelect = (data) => {
657
+ onTagSelect = (data, currentTab, sourceVal) => {
658
+ // If tag is selected from Subject's TagList or unified subject field, insert into subject input
659
+ if (sourceVal && (sourceVal.id === 'title-tagList' || sourceVal.id === 'template-subject')) {
660
+ const tagToInsert = `{{${data}}}`;
661
+ const formData = _.cloneDeep(this.state.formData);
662
+ const fieldId = 'template-subject';
663
+ const input = document.getElementById(fieldId) || document.querySelector(`#${fieldId} input`);
664
+ let subjectValue = formData[fieldId] || '';
665
+ try {
666
+ if (input && (typeof input.selectionStart === 'number')) {
667
+ const startPos = input.selectionStart;
668
+ const endPos = input.selectionEnd;
669
+ subjectValue = `${subjectValue.substring(0, startPos)}${tagToInsert}${subjectValue.substring(endPos)}`;
670
+ formData[fieldId] = subjectValue;
671
+ this.setState({ formData }, () => {
672
+ try {
673
+ input.focus();
674
+ const newPos = startPos + tagToInsert.length;
675
+ input.selectionStart = newPos;
676
+ input.selectionEnd = newPos;
677
+ } catch (e) {}
678
+ });
679
+ } else {
680
+ subjectValue = `${subjectValue}${tagToInsert}`;
681
+ formData[fieldId] = subjectValue;
682
+ this.setState({ formData });
683
+ if (input) {
684
+ try { input.value = subjectValue; } catch (e) {}
685
+ }
686
+ }
687
+ } catch (e) {
688
+ // fallback safe append
689
+ formData[fieldId] = `${subjectValue}${tagToInsert}`;
690
+ this.setState({ formData });
691
+ }
692
+ return;
693
+ }
694
+
650
695
  const isDragDrop = this.state.formData[`${this.state.currentTab - 1}`][this.state.formData[`${this.state.currentTab - 1}`].activeTab].is_drag_drop;
651
696
  const formData = _.cloneDeep(this.state.formData);
652
697
  const isBEESupport = (this.props.location.query.isBEESupport !== "false") || false;
@@ -1546,8 +1591,24 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
1546
1591
  if (schema.standalone) {
1547
1592
  if ((this.props.location.query.module === "loyalty" || this.props.location.query.module === "dvs" || this.props.location.query.module === "timeline" || this.props.location.query.module === "coupon_expiry" ||
1548
1593
  this.props.location.query.module === "library")) {
1549
- _.set(schema, 'standalone.sections[0].inputFields[0].style.display', "" );
1550
- _.set(schema, 'standalone.sections[0].inputFields[0].labelStyle.display', "");
1594
+ // For the new unified cap-tag-list-with-input component, we want to show the Add label button
1595
+ // but hide the input field in library mode
1596
+ const subjectField = schema.standalone.sections[0].inputFields[0];
1597
+ if (subjectField && subjectField.type === "cap-tag-list-with-input") {
1598
+ // Only hide input in library mode, keep it visible in other modes
1599
+ if (this.props.location.query.module === "library") {
1600
+ _.set(schema, 'standalone.sections[0].inputFields[0].showInput', false);
1601
+ _.set(schema, 'standalone.sections[0].inputFields[0].showTagList', true);
1602
+ } else {
1603
+ // In other modes (loyalty, dvs, timeline, coupon_expiry), keep both input and tag list visible
1604
+ _.set(schema, 'standalone.sections[0].inputFields[0].showInput', true);
1605
+ _.set(schema, 'standalone.sections[0].inputFields[0].showTagList', true);
1606
+ }
1607
+ } else {
1608
+ // For backward compatibility with old separate fields
1609
+ _.set(schema, 'standalone.sections[0].inputFields[0].style.display', "" );
1610
+ _.set(schema, 'standalone.sections[0].inputFields[0].labelStyle.display', "");
1611
+ }
1551
1612
  }
1552
1613
  this.setState({schema});
1553
1614
  }
@@ -20,7 +20,7 @@ export const response = {
20
20
  {
21
21
  id: "template-subject",
22
22
  placeholder: "Enter Email Subject",
23
- type: "input",
23
+ type: "cap-tag-list-with-input",
24
24
  metaType: "text",
25
25
  datatype: "string",
26
26
  required: true,
@@ -28,29 +28,15 @@ export const response = {
28
28
  styling: "semantic",
29
29
  order: 1,
30
30
  width: 16,
31
- customComponent: false,
31
+ customComponent: true,
32
32
  standalone: true,
33
33
  onlyDisplay: false,
34
34
  errorMessage: "Email Subject cannot be empty.",
35
+ supportedEvents: [
36
+ "onChange",
37
+ "onTagSelect",
38
+ ],
35
39
  },
36
- // {
37
- // offset: 20,
38
- // id: "title-tagList",
39
- // label: "Tags",
40
- // target: "message-title2",
41
- // type: "tag-list",
42
- // metaType: "List",
43
- // datatype: "select",
44
- // required: true,
45
- // fluid: true,
46
- // onlyDisplay: true,
47
- // styling: "semantic",
48
- // order: 1,
49
- // customComponent: true,
50
- // supportedEvents: [
51
- // "onTagSelect",
52
- // ],
53
- // },
54
40
  ],
55
41
  },
56
42
  ],
@@ -283,7 +269,7 @@ export const response = {
283
269
  metaType: "label",
284
270
  type: "div",
285
271
  primitive: true,
286
- value: <CapButton isAddBtn type="flat"><FormattedMessage {...messages.image}/></CapButton>,
272
+ value: <CapButton isAddBtn type="flat"><FormattedMessage {...messages.image} /></CapButton>,
287
273
  fluid: true,
288
274
  onlyDisplay: false,
289
275
  styling: "semantic",
@@ -251,7 +251,7 @@ const useEmailWrapper = ({
251
251
  getCmsTemplatesInProgress,
252
252
  modeContent.id,
253
253
  SelectedEdmDefaultTemplate,
254
- // templatesActions,
254
+ templatesActions,
255
255
  handleEdmDefaultTemplateSelection
256
256
  ]);
257
257
 
@@ -291,7 +291,29 @@ export class Create extends React.Component { // eslint-disable-line react/prefe
291
291
  rowClassName: 'mobile-push-row',
292
292
  id: 'cta-external-link',
293
293
  cols: [
294
-
294
+ {
295
+ // Add label control for external link input
296
+ id: "title-tagList",
297
+ label: "Add label",
298
+ type: "tag-list",
299
+ metaType: "List",
300
+ datatype: "select",
301
+ required: true,
302
+ fluid: true,
303
+ onlyDisplay: true,
304
+ style: {
305
+ marginRight: "10%",
306
+ marginTop: "-3%",
307
+ },
308
+ styling: "semantic",
309
+ order: 2,
310
+ customComponent: true,
311
+ // Helps handler identify the destination input
312
+ target: inputId,
313
+ supportedEvents: [
314
+ "onTagSelect",
315
+ ],
316
+ },
295
317
  {
296
318
  id: inputId,
297
319
  placeholder: this.props.intl.formatMessage(messages.externalLink),
@@ -304,8 +326,7 @@ export class Create extends React.Component { // eslint-disable-line react/prefe
304
326
  width: 18,
305
327
  offset: 1,
306
328
  style: {
307
- width: '100%',
308
-
329
+ width: '95%',
309
330
  },
310
331
  styling: "semantic",
311
332
  order: 1,
@@ -202,11 +202,33 @@ function getLinkTypeFields({inputFieldsArgs, fieldIndex, deepLinkOptions, formDa
202
202
  identifier: inputId,
203
203
 
204
204
  cols: [
205
-
205
+ {
206
+ // Add label control for external link input
207
+ id: "title-tagList",
208
+ label: "Add label",
209
+ type: "tag-list",
210
+ metaType: "List",
211
+ datatype: "select",
212
+ required: true,
213
+ fluid: true,
214
+ onlyDisplay: true,
215
+ style: {
216
+ marginRight: "10%",
217
+ marginTop: "-3%",
218
+ },
219
+ styling: "semantic",
220
+ order: 2,
221
+ customComponent: true,
222
+ // Helps handler identify the destination input
223
+ target: inputId,
224
+ supportedEvents: [
225
+ "onTagSelect",
226
+ ],
227
+ },
206
228
  {
207
229
  id: inputId,
208
230
  placeholder: "External Link",
209
- type: "input", //Resembles component to be used
231
+ type: "input", //Resembles component to be used
210
232
  metaType: "text",
211
233
  datatype: "string",
212
234
  errorMessage: <FormattedMessage {...messages.invalidExternalLink}/>,
@@ -215,7 +237,7 @@ function getLinkTypeFields({inputFieldsArgs, fieldIndex, deepLinkOptions, formDa
215
237
  width: 18,
216
238
  offset: 1,
217
239
  style: {
218
- width: '100%',
240
+ width: '95%',
219
241
  },
220
242
  styling: "semantic",
221
243
  order: 1,
@@ -19,6 +19,11 @@ const CtaButtons = ({
19
19
  updateHandler,
20
20
  deleteHandler,
21
21
  deepLink,
22
+ location,
23
+ tags,
24
+ injectedTags,
25
+ selectedOfferDetails,
26
+ handleOnTagsContextChange,
22
27
  }) => {
23
28
  // Local state to control CTA form visibility
24
29
  const [showPrimaryCTA, setShowPrimaryCTA] = useState(false);
@@ -130,6 +135,11 @@ const CtaButtons = ({
130
135
  deleteHandler={handleDelete}
131
136
  deepLink={deepLink}
132
137
  buttonType={PRIMARY}
138
+ location={location}
139
+ tags={tags}
140
+ injectedTags={injectedTags}
141
+ selectedOfferDetails={selectedOfferDetails}
142
+ handleOnTagsContextChange={handleOnTagsContextChange}
133
143
  />
134
144
  )}
135
145
  {isPrimaryButtonSaved && !isSecondaryButtonSaved && !shouldShowSecondaryCTA && (
@@ -174,10 +184,20 @@ CtaButtons.propTypes = {
174
184
  updateHandler: PropTypes.func.isRequired,
175
185
  deleteHandler: PropTypes.func.isRequired,
176
186
  deepLink: PropTypes.array,
187
+ handleOnTagsContextChange: PropTypes.func,
188
+ location: PropTypes.object,
189
+ tags: PropTypes.array,
190
+ injectedTags: PropTypes.object,
191
+ selectedOfferDetails: PropTypes.array,
177
192
  };
178
193
 
179
194
  CtaButtons.defaultProps = {
180
195
  deepLink: [],
196
+ handleOnTagsContextChange: () => {},
197
+ location: {},
198
+ tags: [],
199
+ injectedTags: {},
200
+ selectedOfferDetails: [],
181
201
  };
182
202
 
183
203
  export default memo(CtaButtons);