@capillarytech/creatives-library 9.0.14 → 9.0.15-alpha.1

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 (175) hide show
  1. package/constants/unified.js +32 -0
  2. package/global-styles.js +1 -1
  3. package/package.json +1 -1
  4. package/services/tests/api.test.js +35 -20
  5. package/utils/common.js +8 -0
  6. package/utils/commonUtils.js +19 -1
  7. package/utils/rcsPayloadUtils.js +92 -0
  8. package/utils/templateVarUtils.js +201 -0
  9. package/utils/tests/rcsPayloadUtils.test.js +226 -0
  10. package/utils/tests/templateVarUtils.test.js +204 -0
  11. package/v2Components/CapActionButton/constants.js +7 -0
  12. package/v2Components/CapActionButton/index.js +168 -109
  13. package/v2Components/CapActionButton/index.scss +156 -5
  14. package/v2Components/CapActionButton/messages.js +19 -3
  15. package/v2Components/CapActionButton/tests/index.test.js +42 -18
  16. package/v2Components/CapImageUpload/index.js +2 -2
  17. package/v2Components/CapTagList/index.js +10 -0
  18. package/v2Components/CommonTestAndPreview/CustomValuesEditor.js +70 -48
  19. package/v2Components/CommonTestAndPreview/DeliverySettings/DeliverySettings.scss +8 -2
  20. package/v2Components/CommonTestAndPreview/DeliverySettings/ModifyDeliverySettings.js +214 -21
  21. package/v2Components/CommonTestAndPreview/DeliverySettings/constants.js +16 -0
  22. package/v2Components/CommonTestAndPreview/DeliverySettings/index.js +83 -9
  23. package/v2Components/CommonTestAndPreview/DeliverySettings/messages.js +30 -0
  24. package/v2Components/CommonTestAndPreview/DeliverySettings/utils/parseSenderDetailsResponse.js +60 -11
  25. package/v2Components/CommonTestAndPreview/SendTestMessage.js +10 -5
  26. package/v2Components/CommonTestAndPreview/UnifiedPreview/RcsPreviewContent.js +201 -23
  27. package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +355 -76
  28. package/v2Components/CommonTestAndPreview/UnifiedPreview/index.js +155 -8
  29. package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +11 -0
  30. package/v2Components/CommonTestAndPreview/constants.js +38 -2
  31. package/v2Components/CommonTestAndPreview/index.js +761 -222
  32. package/v2Components/CommonTestAndPreview/messages.js +45 -3
  33. package/v2Components/CommonTestAndPreview/previewApiUtils.js +59 -0
  34. package/v2Components/CommonTestAndPreview/sagas.js +25 -6
  35. package/v2Components/CommonTestAndPreview/tests/CommonTestAndPreview.addTestCustomer.test.js +1 -0
  36. package/v2Components/CommonTestAndPreview/tests/CustomValuesEditor.test.js +284 -267
  37. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/ModifyDeliverySettings.test.js +231 -65
  38. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/index.test.js +118 -5
  39. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/utils/parseSenderDetailsResponse.test.js +341 -0
  40. package/v2Components/CommonTestAndPreview/tests/PreviewSection.test.js +8 -1
  41. package/v2Components/CommonTestAndPreview/tests/SendTestMessage.test.js +34 -13
  42. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/RcsPreviewContent.test.js +281 -283
  43. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/index.test.js +199 -1
  44. package/v2Components/CommonTestAndPreview/tests/index.test.js +133 -4
  45. package/v2Components/CommonTestAndPreview/tests/previewApiUtils.test.js +67 -0
  46. package/v2Components/CommonTestAndPreview/tests/sagas.test.js +31 -24
  47. package/v2Components/CommonTestAndPreview/utils.js +84 -0
  48. package/v2Components/FormBuilder/Classic.js +4487 -0
  49. package/v2Components/FormBuilder/Functional/FormBuilderShell.js +366 -0
  50. package/v2Components/FormBuilder/Functional/channels/registry.js +17 -0
  51. package/v2Components/FormBuilder/Functional/channels/sms/buildSubmitPayload.js +9 -0
  52. package/v2Components/FormBuilder/Functional/channels/sms/config.js +30 -0
  53. package/v2Components/FormBuilder/Functional/channels/sms/getEditorErrorDescriptor.js +46 -0
  54. package/v2Components/FormBuilder/Functional/channels/sms/getLiquidContent.js +13 -0
  55. package/v2Components/FormBuilder/Functional/channels/sms/index.js +22 -0
  56. package/v2Components/FormBuilder/Functional/channels/sms/tests/getEditorErrorDescriptor.test.js +52 -0
  57. package/v2Components/FormBuilder/Functional/channels/sms/tests/getLiquidContent.test.js +25 -0
  58. package/v2Components/FormBuilder/Functional/channels/sms/tests/validate.test.js +87 -0
  59. package/v2Components/FormBuilder/Functional/channels/sms/validate.js +89 -0
  60. package/v2Components/FormBuilder/Functional/constants.js +42 -0
  61. package/v2Components/FormBuilder/Functional/core/schema/fieldRegistry.js +38 -0
  62. package/v2Components/FormBuilder/Functional/core/schema/initializeFormState.js +85 -0
  63. package/v2Components/FormBuilder/Functional/core/store/formReducer.js +81 -0
  64. package/v2Components/FormBuilder/Functional/core/store/selectors.js +30 -0
  65. package/v2Components/FormBuilder/Functional/core/store/toLegacyFormData.js +91 -0
  66. package/v2Components/FormBuilder/Functional/index.js +26 -0
  67. package/v2Components/FormBuilder/Functional/layout/FieldSlot.js +59 -0
  68. package/v2Components/FormBuilder/Functional/layout/SchemaForm.js +31 -0
  69. package/v2Components/FormBuilder/Functional/layout/Section.js +116 -0
  70. package/v2Components/FormBuilder/Functional/renderers/smsRenderers.js +258 -0
  71. package/v2Components/FormBuilder/Functional/tests/channelRegistry.test.js +21 -0
  72. package/v2Components/FormBuilder/Functional/tests/fieldRegistry.test.js +65 -0
  73. package/v2Components/FormBuilder/Functional/tests/fieldSlot.test.js +97 -0
  74. package/v2Components/FormBuilder/Functional/tests/fixtures/smsParityCases.js +192 -0
  75. package/v2Components/FormBuilder/Functional/tests/formReducer.test.js +129 -0
  76. package/v2Components/FormBuilder/Functional/tests/initializeFormState.test.js +132 -0
  77. package/v2Components/FormBuilder/Functional/tests/schemaForm.test.js +40 -0
  78. package/v2Components/FormBuilder/Functional/tests/section.test.js +99 -0
  79. package/v2Components/FormBuilder/Functional/tests/selectors.test.js +67 -0
  80. package/v2Components/FormBuilder/Functional/tests/sms.crossFlowParity.test.js +155 -0
  81. package/v2Components/FormBuilder/Functional/tests/sms.liquid.test.js +172 -0
  82. package/v2Components/FormBuilder/Functional/tests/sms.rollout.test.js +122 -0
  83. package/v2Components/FormBuilder/Functional/tests/sms.shell.parity.test.js +329 -0
  84. package/v2Components/FormBuilder/Functional/tests/smsRenderers.test.js +160 -0
  85. package/v2Components/FormBuilder/Functional/tests/toLegacyFormData.test.js +95 -0
  86. package/v2Components/FormBuilder/_formBuilder.scss +5 -0
  87. package/v2Components/FormBuilder/index.js +41 -4479
  88. package/v2Components/FormBuilder/tests/__snapshots__/sms.characterization.test.js.snap +114 -0
  89. package/v2Components/FormBuilder/tests/entryGate.test.js +106 -0
  90. package/v2Components/FormBuilder/tests/sms.characterization.test.js +336 -0
  91. package/v2Components/SmsFallback/SmsFallbackLocalSelector.js +91 -0
  92. package/v2Components/SmsFallback/constants.js +94 -0
  93. package/v2Components/SmsFallback/index.js +958 -0
  94. package/v2Components/SmsFallback/index.scss +266 -0
  95. package/v2Components/SmsFallback/messages.js +78 -0
  96. package/v2Components/SmsFallback/smsFallbackUtils.js +120 -0
  97. package/v2Components/SmsFallback/tests/SmsFallbackLocalSelector.test.js +50 -0
  98. package/v2Components/SmsFallback/tests/rcsSmsFallback.acceptance.test.js +147 -0
  99. package/v2Components/SmsFallback/tests/smsFallbackHandlers.test.js +304 -0
  100. package/v2Components/SmsFallback/tests/smsFallbackUi.test.js +208 -0
  101. package/v2Components/SmsFallback/tests/smsFallbackUtils.test.js +309 -0
  102. package/v2Components/SmsFallback/tests/useLocalTemplateList.test.js +422 -0
  103. package/v2Components/SmsFallback/useLocalTemplateList.js +92 -0
  104. package/v2Components/TemplatePreview/_templatePreview.scss +41 -22
  105. package/v2Components/TemplatePreview/constants.js +2 -0
  106. package/v2Components/TemplatePreview/index.js +143 -31
  107. package/v2Components/TemplatePreview/tests/index.test.js +142 -0
  108. package/v2Components/TestAndPreviewSlidebox/CustomValuesEditor.js +6 -6
  109. package/v2Components/TestAndPreviewSlidebox/index.js +13 -1
  110. package/v2Components/TestAndPreviewSlidebox/sagas.js +11 -4
  111. package/v2Components/TestAndPreviewSlidebox/tests/saga.test.js +3 -1
  112. package/v2Components/VarSegmentMessageEditor/constants.js +2 -0
  113. package/v2Components/VarSegmentMessageEditor/index.js +125 -0
  114. package/v2Components/VarSegmentMessageEditor/index.scss +46 -0
  115. package/v2Containers/CommunicationFlow/Tests/CommunicationFlow.test.js +4 -1
  116. package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/DeliverySettingsSection.test.js +4 -0
  117. package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/SenderDetails.test.js +20 -20
  118. package/v2Containers/CreativesContainer/CreativesSlideBoxWrapper.js +17 -0
  119. package/v2Containers/CreativesContainer/SlideBoxContent.js +36 -4
  120. package/v2Containers/CreativesContainer/SlideBoxFooter.js +14 -5
  121. package/v2Containers/CreativesContainer/SlideBoxHeader.js +36 -5
  122. package/v2Containers/CreativesContainer/constants.js +11 -0
  123. package/v2Containers/CreativesContainer/embeddedSlideboxUtils.js +79 -0
  124. package/v2Containers/CreativesContainer/index.js +323 -104
  125. package/v2Containers/CreativesContainer/index.scss +83 -1
  126. package/v2Containers/CreativesContainer/tests/SlideBoxContent.localTemplates.test.js +90 -0
  127. package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +79 -34
  128. package/v2Containers/CreativesContainer/tests/SlideBoxHeader.test.js +79 -16
  129. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +8 -0
  130. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxHeader.test.js.snap +333 -90
  131. package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +20 -15
  132. package/v2Containers/CreativesContainer/tests/embeddedSlideboxUtils.test.js +258 -0
  133. package/v2Containers/CreativesContainer/tests/index.test.js +71 -9
  134. package/v2Containers/CreativesContainer/tests/useLocalTemplatesProp.test.js +125 -0
  135. package/v2Containers/MobilePush/Create/test/saga.test.js +2 -2
  136. package/v2Containers/Rcs/constants.js +131 -11
  137. package/v2Containers/Rcs/index.js +2602 -807
  138. package/v2Containers/Rcs/index.scss +289 -8
  139. package/v2Containers/Rcs/messages.js +34 -3
  140. package/v2Containers/Rcs/rcsLibraryHydrationUtils.js +227 -0
  141. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +73535 -38537
  142. package/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap +0 -8
  143. package/v2Containers/Rcs/tests/index.test.js +147 -36
  144. package/v2Containers/Rcs/tests/mockData.js +38 -0
  145. package/v2Containers/Rcs/tests/rcsLibraryHydrationUtils.test.js +318 -0
  146. package/v2Containers/Rcs/tests/utils.test.js +646 -30
  147. package/v2Containers/Rcs/utils.js +478 -11
  148. package/v2Containers/Sms/Create/index.js +115 -48
  149. package/v2Containers/Sms/smsFormDataHelpers.js +67 -0
  150. package/v2Containers/Sms/tests/smsFormDataHelpers.test.js +253 -0
  151. package/v2Containers/SmsTrai/Create/index.js +9 -4
  152. package/v2Containers/SmsTrai/Edit/constants.js +2 -0
  153. package/v2Containers/SmsTrai/Edit/index.js +678 -169
  154. package/v2Containers/SmsTrai/Edit/index.scss +126 -0
  155. package/v2Containers/SmsTrai/Edit/messages.js +14 -4
  156. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +5615 -3014
  157. package/v2Containers/SmsWrapper/index.js +37 -8
  158. package/v2Containers/TagList/index.js +6 -0
  159. package/v2Containers/Templates/TemplatesActionBar.js +101 -0
  160. package/v2Containers/Templates/_templates.scss +171 -12
  161. package/v2Containers/Templates/actions.js +11 -0
  162. package/v2Containers/Templates/constants.js +2 -0
  163. package/v2Containers/Templates/index.js +125 -55
  164. package/v2Containers/Templates/sagas.js +57 -13
  165. package/v2Containers/Templates/tests/TemplatesActionBar.test.js +120 -0
  166. package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +1060 -1015
  167. package/v2Containers/Templates/tests/sagas.test.js +199 -16
  168. package/v2Containers/Templates/tests/smsTemplatesListApi.test.js +180 -0
  169. package/v2Containers/Templates/utils/smsTemplatesListApi.js +79 -0
  170. package/v2Containers/TemplatesV2/TemplatesV2.style.js +72 -1
  171. package/v2Containers/TemplatesV2/index.js +86 -23
  172. package/v2Containers/TemplatesV2/tests/TemplatesV2.localTemplates.test.js +131 -0
  173. package/v2Containers/WeChat/MapTemplates/test/saga.test.js +9 -9
  174. package/v2Containers/Whatsapp/index.js +3 -20
  175. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +578 -34
@@ -0,0 +1,4487 @@
1
+ /* eslint-disable no-case-declarations */
2
+
3
+ /**
4
+ *
5
+ * FormBuilder
6
+ *
7
+ */
8
+
9
+ import PropTypes from 'prop-types';
10
+
11
+ import React from 'react';
12
+ import _ from 'lodash';
13
+ import { Table, Modal} from 'antd';
14
+ import { connect } from 'react-redux';
15
+ import CapSpin from '@capillarytech/cap-ui-library/CapSpin';
16
+ import CapDrawer from '@capillarytech/cap-ui-library/CapDrawer';
17
+ import CapButton from '@capillarytech/cap-ui-library/CapButton';
18
+ import CapInput from '@capillarytech/cap-ui-library/CapInput';
19
+ import CapPopover from '@capillarytech/cap-ui-library/CapPopover';
20
+ import CapImage from '@capillarytech/cap-ui-library/CapImage';
21
+ import CapCheckbox from '@capillarytech/cap-ui-library/CapCheckbox';
22
+ import CapRadio from '@capillarytech/cap-ui-library/CapRadio';
23
+ import CapSelect from '@capillarytech/cap-ui-library/CapSelect';
24
+ import CapTable from '@capillarytech/cap-ui-library/CapTable';
25
+ import CapRow from '@capillarytech/cap-ui-library/CapRow';
26
+ import CapColumn from '@capillarytech/cap-ui-library/CapColumn';
27
+ import CapTab from '@capillarytech/cap-ui-library/CapTab';
28
+ import CapNotification from '@capillarytech/cap-ui-library/CapNotification';
29
+ import CapUploader from '@capillarytech/cap-ui-library/CapUploader';
30
+ import CapHeading from '@capillarytech/cap-ui-library/CapHeading';
31
+ import CapIcon from '@capillarytech/cap-ui-library/CapIcon';
32
+ import CapTooltip from '@capillarytech/cap-ui-library/CapTooltip';
33
+ import CapAskAira from '@capillarytech/cap-ui-library/CapAskAira';
34
+
35
+ import { injectIntl, intlShape, FormattedMessage } from 'react-intl';
36
+ import LabelHOC from '@capillarytech/cap-ui-library/assets/HOCs/ComponentWithLabelHOC';
37
+ import { createStructuredSelector } from 'reselect';
38
+ import { CAP_SPACE_12, CAP_SPACE_08, FONT_COLOR_05, FONT_COLOR_04 } from '@capillarytech/cap-ui-library/styled/variables';
39
+ import UnifiedPreview from '../CommonTestAndPreview/UnifiedPreview';
40
+ import TemplatePreview from '../TemplatePreview';
41
+ import { ANDROID } from '../CommonTestAndPreview/constants';
42
+ import TagList from '../../v2Containers/TagList';
43
+ import CapTagListWithInput from '../CapTagListWithInput';
44
+ import SlideBox from '../SlideBox';
45
+ import CardGrid from '../CardGrid';
46
+ import CKEditor from "../Ckeditor/";
47
+ import EDMEditor from "../Edmeditor";
48
+ import BeeEditor from '../../v2Containers/BeeEditor';
49
+ import CustomPopOver from '../CustomPopOver';
50
+ import messages from './messages';
51
+ import { makeSelectMetaEntities, selectCurrentOrgDetails, selectLiquidStateDetails, selectMetaDataStatus } from "../../v2Containers/Cap/selectors";
52
+ import * as actions from "../../v2Containers/Cap/actions";
53
+ import './_formBuilder.scss';
54
+ import {updateCharCount, checkUnicode} from "../../utils/smsCharCountV2";
55
+ import { preprocessHtml, validateTagsCore, hasUnsubscribeTag } from '../../utils/tagValidations';
56
+ import { containsBase64Images } from '../../utils/content';
57
+ import { SMS, MOBILE_PUSH, LINE, ENABLE_AI_SUGGESTIONS, EMAIL, LIQUID_SUPPORTED_CHANNELS, INAPP } from '../../v2Containers/CreativesContainer/constants';
58
+ import globalMessages from '../../v2Containers/Cap/messages';
59
+ import { convert } from 'html-to-text';
60
+ import { OUTBOUND, ADD_LANGUAGE, UPLOAD, USE_EDITOR, COPY_PRIMARY_LANGUAGE, GLOBAL_CONVERT_OPTIONS } from './constants';
61
+ import { GET_TRANSLATION_MAPPED } from '../../constants/unified';
62
+ import moment from 'moment';
63
+ import { CUSTOMER_BARCODE_TAG , COPY_OF, ENTRY_TRIGGER_TAG_REGEX, SKIP_TAGS_REGEX_GROUPS} from '../../constants/unified';
64
+ import { REQUEST } from '../../v2Containers/Cap/constants'
65
+ import { isEmailUnsubscribeTagOptional, isAiContentBotDisabled } from '../../utils/common';
66
+ import { isUrl } from '../../v2Containers/Line/Container/Wrapper/utils';
67
+ import { bindActionCreators } from 'redux';
68
+ import { getChannelData, hasPersonalizationTags, validateLiquidTemplateContent, validateMobilePushContent } from '../../utils/commonUtils';
69
+ const {Column} = Table;
70
+ const {TextArea} = CapInput;
71
+ const {CapRadioGroup} = CapRadio;
72
+ // import styled from 'styled-components';
73
+
74
+ const tagsTypes = {
75
+ MISSING_TAGS: 'missingTags',
76
+ };
77
+ const errorMessageForTags = {
78
+ MISSING_TAG_ERROR: 'missingTagsError',
79
+ GENERIC_VALIDATION_ERROR: 'genericValidationError',
80
+ TAG_BRACKET_COUNT_MISMATCH_ERROR: 'tagBracketCountMismatchError'
81
+ };
82
+
83
+ // Isolated input for EMAIL template-name: only this tiny component re-renders on each keystroke.
84
+ // formData is updated only on blur (onCommit), eliminating all re-renders during typing.
85
+ class HighFreqInput extends React.Component {
86
+ constructor(props) {
87
+ super(props);
88
+ this.state = { localValue: props.value || '' };
89
+ }
90
+
91
+ componentDidUpdate(prevProps) {
92
+ if (prevProps.value !== this.props.value && this.state.localValue !== this.props.value) {
93
+ this.setState({ localValue: this.props.value || '' });
94
+ }
95
+ }
96
+
97
+ handleChange = (e) => {
98
+ this.setState({ localValue: e.target.value });
99
+ };
100
+
101
+ handleBlur = (e) => {
102
+ this.props.onCommit(this.state.localValue);
103
+ if (this.props.onBlur) this.props.onBlur(e);
104
+ };
105
+
106
+ render() {
107
+ const { value: _v, onCommit: _oc, onBlur: _ob, ...rest } = this.props;
108
+ return <CapInput {...rest} value={this.state.localValue} onChange={this.handleChange} onBlur={this.handleBlur} />;
109
+ }
110
+ }
111
+
112
+ // Isolated wrapper for EMAIL template-subject: blur-only commit, same as HighFreqInput.
113
+ class HighFreqTagInput extends React.Component {
114
+ constructor(props) {
115
+ super(props);
116
+ this.state = { localInputValue: props.inputValue || '' };
117
+ }
118
+
119
+ componentDidUpdate(prevProps) {
120
+ if (prevProps.inputValue !== this.props.inputValue && this.state.localInputValue !== this.props.inputValue) {
121
+ this.setState({ localInputValue: this.props.inputValue || '' });
122
+ }
123
+ }
124
+
125
+ handleInputChange = (e) => {
126
+ this.setState({ localInputValue: e.target.value });
127
+ };
128
+
129
+ handleBlur = () => {
130
+ this.props.onCommit(this.state.localInputValue);
131
+ };
132
+
133
+ render() {
134
+ const { inputValue: _iv, onCommit: _oc, inputOnChange: _ic, ...rest } = this.props;
135
+ return (
136
+ <CapTagListWithInput
137
+ {...rest}
138
+ inputValue={this.state.localInputValue}
139
+ inputOnChange={this.handleInputChange}
140
+ inputProps={{ ...(this.props.inputProps || {}), onBlur: this.handleBlur }}
141
+ />
142
+ );
143
+ }
144
+ }
145
+
146
+ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-stateless-function
147
+ constructor(props) {
148
+ super(props);
149
+ this.state = {
150
+ formData: {},
151
+ errorData: {},
152
+ currentTab: 1,
153
+ currentLangTab: 'en',
154
+ usingTabContainer: false,
155
+ tabCount: 1,
156
+ tabKey: '',
157
+ isFormValid: false,
158
+ checkValidation: false,
159
+ showModal: false,
160
+ modal: {},
161
+ currentEvent: {},
162
+ currentEventData: {},
163
+ popoverVisible: false,
164
+ customPopoverVisible: false,
165
+ isDrawerVisible: false,
166
+ translationLang: 'en',
167
+ liquidErrorMessage: {
168
+ STANDARD_ERROR_MSG: [],
169
+ LIQUID_ERROR_MSG: [],
170
+ },
171
+ };
172
+ this.renderForm = this.renderForm.bind(this);
173
+ this.updateFormData = this.updateFormData.bind(this);
174
+ this.callChildEvent = this.callChildEvent.bind(this);
175
+ this.renderColLabelSection = this.renderColLabelSection.bind(this);
176
+ this.renderMultiColSection = this.renderMultiColSection.bind(this);
177
+ this.renderSection = this.renderSection.bind(this);
178
+ this.renderContainer = this.renderContainer.bind(this);
179
+ this.renderParentSection = this.renderParentSection.bind(this);
180
+ this.initializeStandAloneSections = this.initializeStandAloneSections.bind(this);
181
+ this.initializeMultiColSection = this.initializeMultiColSection.bind(this);
182
+ this.initializeParentSection = this.initializeParentSection.bind(this);
183
+ this.initializeColLabelSection = this.initializeColLabelSection.bind(this);
184
+ this.initializeContainers = this.initializeContainers.bind(this);
185
+ this.cloneVersionData = this.cloneVersionData.bind(this);
186
+ this.markFinalTabVersion = this.markFinalTabVersion.bind(this);
187
+ this.duplicateVersion = this.duplicateVersion.bind(this);
188
+ this.deleteVersion = this.deleteVersion.bind(this);
189
+ this.renameVersion = this.renameVersion.bind(this);
190
+ this.resetState = this.resetState.bind(this);
191
+ this.onEdit = this.onEdit.bind(this);
192
+ this.changeVersionName = this.changeVersionName.bind(this);
193
+ this.hasClass = this.hasClass.bind(this);
194
+ this.validateForm = this.validateForm.bind(this);
195
+ this.indexOfEnd = this.indexOfEnd.bind(this);
196
+ this.saveForm = this.saveForm.bind(this);
197
+ this.initializeEditErrorData = this.initializeEditErrorData.bind(this);
198
+ this.checkIfSupportedTag = this.checkIfSupportedTag.bind(this);
199
+ this.transformInjectedTags = this.transformInjectedTags.bind(this);
200
+ this.handleSetRadioValue = this.handleSetRadioValue.bind(this);
201
+ this.formElements = [];
202
+ // Check if the liquid flow feature is supported and the channel is in the supported list.
203
+ this.isLiquidFlowSupportedByChannel = this.isLiquidFlowSupportedByChannel.bind(this);
204
+ this.onSubmitWrapper = this.onSubmitWrapper.bind(this);
205
+
206
+ // Performance optimization: Debounced functions for high-frequency updates
207
+ this.debouncedUpdateFormData = _.debounce((data, val, event, skipStateUpdate) => {
208
+ this.performFormDataUpdate(data, val, event, skipStateUpdate);
209
+ }, 300);
210
+ this.debouncedValidation = _.debounce(this.validateForm.bind(this), 500);
211
+
212
+ // Memoized validation cache
213
+ this.validationCache = new Map();
214
+
215
+ }
216
+
217
+ // Helper function to generate unique tab ID
218
+ generateUniqueTabId(formData, tabIndex) {
219
+ let id = _.uniqueId();
220
+ let validId = false;
221
+
222
+ while (!validId) {
223
+ validId = true;
224
+ for (let idx = 0; idx < formData[tabIndex].selectedLanguages.length; idx += 1) {
225
+ if (!formData[tabIndex]) {
226
+ continue;
227
+ }
228
+ if (id === formData[tabIndex][formData[tabIndex].selectedLanguages[idx]].tabKey) {
229
+ validId = false;
230
+ break;
231
+ }
232
+ }
233
+ if (!validId) {
234
+ id = _.uniqueId();
235
+ }
236
+ }
237
+
238
+ return id;
239
+ }
240
+
241
+ // Performance optimized form data update function
242
+ performFormDataUpdate(data, val, event, skipStateUpdate = false) {
243
+
244
+ // Use optimized state update instead of deep cloning
245
+ const formData = this.optimizedFormDataUpdate(data, val, event);
246
+
247
+ const tabIndex = this.state.currentTab - 1;
248
+ let currentTab = this.state.currentTab;
249
+
250
+ if (this.state.usingTabContainer && !val.standalone) {
251
+ const data1 = data;
252
+ if (event === ADD_LANGUAGE) {
253
+ const addLanguageType = this.props.addLanguageType;
254
+ if (addLanguageType === '') {
255
+ return;
256
+ }
257
+ const currentLang = formData[tabIndex].activeTab;
258
+ let baseTab;
259
+
260
+ switch (addLanguageType) {
261
+ case UPLOAD:
262
+ baseTab = _.cloneDeep(formData[tabIndex][currentLang]);
263
+
264
+ baseTab.iso_code = data.iso_code;
265
+ baseTab.lang_id = data.lang_id;
266
+ baseTab.language = data.language;
267
+ baseTab.tabKey = this.generateUniqueTabId(formData, tabIndex);
268
+
269
+ formData[tabIndex][data.iso_code] = baseTab;
270
+ formData[tabIndex].activeTab = data.iso_code;
271
+ formData[tabIndex].tabKey = baseTab.tabKey;
272
+ break;
273
+ case COPY_PRIMARY_LANGUAGE:
274
+ case USE_EDITOR:
275
+ baseTab = _.cloneDeep(formData[tabIndex][this.props.baseLanguage]);
276
+
277
+ baseTab.iso_code = data.iso_code;
278
+ baseTab.lang_id = data.lang_id;
279
+ baseTab.language = data.language;
280
+ baseTab.tabKey = this.generateUniqueTabId(formData, tabIndex);
281
+
282
+ formData[tabIndex].selectedLanguages.push(data.iso_code);
283
+ formData[tabIndex][data.iso_code] = baseTab;
284
+ formData[tabIndex].activeTab = data.iso_code;
285
+ formData[tabIndex].tabKey = baseTab.tabKey;
286
+ break;
287
+ case '':
288
+ return;
289
+ default:
290
+ break;
291
+ }
292
+ const that = this;
293
+ setTimeout(() => {
294
+ that.setState({tabKey: baseTab.tabKey});
295
+ }, 0);
296
+ }
297
+
298
+ if (!this.props.isNewVersionFlow) {
299
+ formData[tabIndex][val.id] = data1;
300
+ } else if (this.props.isNewVersionFlow && event !== ADD_LANGUAGE && event !== "onContentChange") {
301
+ formData[tabIndex][this.props.baseLanguage][val.id] = data1;
302
+ }
303
+
304
+ if (formData[tabIndex].base) {
305
+ if (!this.props.isNewVersionFlow) {
306
+ formData.base[val.id] = data1;
307
+ } else {
308
+ formData.base[data1.iso_code] = formData[tabIndex][data1.iso_code];
309
+ formData.base.tabKey = formData[tabIndex].tabKey;
310
+ formData.base.activeTab = formData[tabIndex].activeTab;
311
+ formData.base.selectedLanguages = formData[tabIndex].selectedLanguages;
312
+ }
313
+ }
314
+ } else {
315
+ formData[val.id] = data;
316
+ }
317
+
318
+ if (this.props.isNewVersionFlow) {
319
+ if (event === 'onSelect' && data === 'New Version') {
320
+ this.callChildEvent(data, val, 'addVersion', event);
321
+ } else if (event === 'onSelect' && data !== 'New Version') {
322
+ currentTab = _.findIndex(this.state.formData['template-version-options'], { key: data}) + 1;
323
+ this.setState({currentTab, tabKey: formData[`${currentTab - 1}`].tabKey}, () => {
324
+ val.injectedEvents[event].call(this, this.state.formData['template-version-options'][currentTab - 1].key, formData, val);
325
+ });
326
+ }
327
+
328
+ if (event === 'onContentChange') {
329
+ // Content change handling
330
+ }
331
+ }
332
+
333
+ // Only update state if not already updated (for immediate UI feedback)
334
+ if (!skipStateUpdate) {
335
+ this.setState({formData}, () => {
336
+ if (this.props.startValidation) {
337
+ this.debouncedValidation();
338
+ }
339
+ });
340
+ } else {
341
+ // Just trigger validation if state was already updated
342
+ if (this.props.startValidation) {
343
+ this.debouncedValidation();
344
+ }
345
+ }
346
+
347
+ if (event && val.injectedEvents[event]) {
348
+ if (event === "onRowClick") {
349
+ val.injectedEvents[event].call(this, data);
350
+ } else if (this.props.isNewVersionFlow && event !== 'onSelect') {
351
+ if (event === ADD_LANGUAGE) {
352
+ val.injectedEvents[event].call(this, data, formData, val);
353
+ this.setState({currentEventVal: {}, currentEvent: {}, currentEventData: {}});
354
+ } else {
355
+ val.injectedEvents[event].call(this, true, formData, val);
356
+ }
357
+ } else if (!this.props.isNewVersionFlow) {
358
+ val.injectedEvents[event].call(this, true, formData, val);
359
+ }
360
+ } else if (val.injectedEvents && val.injectedEvents.onChange) {
361
+ val.injectedEvents.onChange.call(this, true, formData, val);
362
+ }
363
+
364
+ if (!((event === 'onSelect' && data === 'New Version') || event === 'onContentChange')) {
365
+ this.props.onChange(formData, this.state.tabCount, currentTab, val);
366
+ }
367
+ }
368
+
369
+ // Optimized form data update - only clone what's necessary
370
+ optimizedFormDataUpdate(data, val, event) {
371
+ const currentFormData = this.state.formData;
372
+
373
+ // For simple field updates, use spread operator instead of deep clone
374
+ if (!this.state.usingTabContainer || val.standalone) {
375
+ return {
376
+ ...currentFormData,
377
+ [val.id]: data
378
+ };
379
+ }
380
+
381
+ // For tab container updates, only clone the affected tab
382
+ const tabIndex = this.state.currentTab - 1;
383
+ const updatedFormData = { ...currentFormData };
384
+
385
+ if (updatedFormData[tabIndex]) {
386
+ updatedFormData[tabIndex] = {
387
+ ...updatedFormData[tabIndex],
388
+ [val.id]: data
389
+ };
390
+ }
391
+
392
+ return updatedFormData;
393
+ }
394
+
395
+ isLiquidFlowSupportedByChannel = () => {
396
+ return Boolean(LIQUID_SUPPORTED_CHANNELS.includes(this.props?.schema?.channel?.toUpperCase()));
397
+ }
398
+
399
+ componentWillMount() {
400
+ this.setState({usingTabContainer: this.props.usingTabContainer ? this.props.usingTabContainer : false});
401
+ if (this.props.formData && (this.props.isEdit || (!this.props.isEdit && !_.isEmpty(this.props.formData)))) {
402
+ this.setState({formData: this.props.formData}, () => {
403
+ // if (!this.props.isNewVersionFlow) {
404
+ this.initialiseForm(this.props.schema, false, true);
405
+ // }
406
+ });
407
+ } else {
408
+ this.initialiseForm(this.props.schema, true);
409
+ }
410
+ }
411
+
412
+ componentWillReceiveProps(nextProps) {
413
+ const type = this.props.location.query.type;
414
+ const isLibraryModule = this.props.location.query.module === "library";
415
+ if (this.state.usingTabContainer && nextProps.tabCount !== this.props.tabCount) {
416
+ this.setState({tabCount: nextProps.tabCount});
417
+ }
418
+ if (nextProps.startValidation && nextProps.startValidation !== false && this.props.startValidation !== nextProps.startValidation) {
419
+ if (this.debouncedUpdateFormData) this.debouncedUpdateFormData.flush();
420
+ this.setState({checkValidation: true});
421
+ this.validateForm(null, null, true, true, () => {
422
+ //triggering the saveFormData or onSubmit when validation sets isFormValid to TRUE
423
+ if (this.state.isFormValid) {
424
+ this.saveForm(true);
425
+ } else {
426
+ this.props.stopValidation();
427
+ }
428
+ });
429
+ }
430
+ // if (nextProps.saveForm && this.props.saveForm !== nextProps.saveForm) {
431
+ // this.saveForm(saveForm);
432
+ // }
433
+ if (nextProps.isEdit) {
434
+ if (!_.isEqual(nextProps.formData, this.state.formData) && !_.isEmpty(nextProps.formData)) {
435
+ if (type === 'embedded' && !isLibraryModule) { //TODO: has to be checked and changed.
436
+ this.setState({checkValidation: true, formData: nextProps.formData}, () => {
437
+ this.validateForm();
438
+ });
439
+ // this.setState({formData: nextProps.formData}, () => {
440
+ // this.validateForm();
441
+ // });
442
+ }
443
+ //TODO change this structure
444
+ if (this.props.schema && this.props.schema.channel && this.props.schema.channel.toUpperCase() === 'WECHAT') {
445
+ this.setState({formData: nextProps.formData});
446
+ } else if ( !_.isEmpty(nextProps.formData) && !_.isEqual(this.state.formData, nextProps.formData) && this.props.schema && this.props.schema.channel && this.props.schema.channel.toUpperCase() === 'MOBILEPUSH') {
447
+ this.setState({formData: nextProps.formData, tabCount: nextProps.tabCount});
448
+ // this.resetTabKeys(nextProps.formData, nextProps.tabCount);
449
+ } else if (this.props.schema && this.props.schema.channel && this.props.schema.channel.toUpperCase() === 'EMAIL') {
450
+ this.setState({formData: nextProps.formData});
451
+ }
452
+
453
+ if (this.state.usingTabContainer && this.state.tabKey === '') {
454
+ //this.setState({formData: nextProps.formData});
455
+ if (nextProps.isNewVersionFlow && !_.isEqual(this.state.tabKey, nextProps.tabKey)) {
456
+ this.setState({tabKey: nextProps.tabKey});
457
+ } else if (!nextProps.isNewVersionFlow) {
458
+ this.resetTabKeys(nextProps.formData, nextProps.tabCount);
459
+ }
460
+ this.initializeEditErrorData(nextProps.formData, nextProps.tabCount);
461
+ }
462
+ }
463
+ if (_.isEmpty(this.props.formData) && !_.isEmpty(nextProps.formData)) {
464
+ if (this.state.usingTabContainer) {
465
+ this.resetTabKeys(nextProps.formData, nextProps.tabCount);
466
+ }
467
+ this.initializeEditErrorData(nextProps.formData, nextProps.tabCount);
468
+ }
469
+ if (!_.isEmpty(this.props.formData) && this.props.formData !== this.state.formData && _.isEmpty(this.state.formData) && this.state.usingTabContainer) {
470
+ this.resetTabKeys(this.props.formData, nextProps.tabCount);
471
+ }
472
+ if ( this.state.usingTabContainer && nextProps.formData && !_.isEmpty(nextProps.formData) && _.isEmpty(this.props.formData) && nextProps.tabCount && this.state.tabCount !== nextProps.tabCount) {
473
+ this.setState({ formData: nextProps.formData, tabCount: nextProps.tabCount }, () => {
474
+ this.initializeEditErrorData(nextProps.formData, nextProps.tabCount);
475
+ if (this.state.usingTabContainer) {
476
+ this.resetTabKeys(nextProps.formData, nextProps.tabCount);
477
+ }
478
+ });
479
+ if (nextProps.tabCount > 1) {
480
+ this.setState({usingTabContainer: true});
481
+ }
482
+ }
483
+ //In Context of MPUSH currentTab represents whether its Android or IOS tab , 1 for Android and 2 for IOS
484
+ if (nextProps.currentTab && _.get(this.props, 'schema.channel') === MOBILE_PUSH) {
485
+ this.setState({currentTab: nextProps.currentTab});
486
+ }
487
+ } else if (!_.isEmpty(nextProps.formData) &&
488
+ ( !this.state.usingTabContainer || (this.state.usingTabContainer && nextProps.tabKey !== ''))
489
+ && !_.isEqual(nextProps.formData, this.state.formData) &&
490
+ !_.isEqual(nextProps.formData, this.props.formData)) {
491
+ // Don't run validation if we're in Test & Preview mode
492
+ if (!nextProps.isTestAndPreviewMode) {
493
+ this.setState({formData: nextProps.formData, tabKey: nextProps.tabKey}, () => {
494
+ this.validateForm();
495
+ });
496
+ } else {
497
+ // Just update formData without validation
498
+ this.setState({formData: nextProps.formData, tabKey: nextProps.tabKey});
499
+ }
500
+ //this.resetTabKeys(nextProps.formData, nextProps.tabCount);
501
+ } else if ((_.isEmpty(this.props.formData) || !this.props.formData) && _.isEmpty(this.state.formData)) {
502
+ this.initialiseForm(nextProps.schema, true);
503
+ } else if (_.isEmpty(this.props.formData) && !_.isEmpty(nextProps.formData)) {
504
+ this.setState({formData: nextProps.formData});
505
+ }
506
+
507
+ let currentLangTab = _.cloneDeep(this.state.currentLangTab);
508
+ if (nextProps.isNewVersionFlow && nextProps.formData[`${this.state.currentTab - 1}`] && nextProps.formData[`${this.state.currentTab - 1}`].activeTab) {
509
+ currentLangTab = nextProps.formData[`${this.state.currentTab - 1}`].activeTab;
510
+ }
511
+
512
+ if (nextProps.isNewVersionFlow && nextProps.currentTab && nextProps.currentTab !== this.props.currentTab) {
513
+ this.setState({currentTab: nextProps.currentTab});
514
+ }
515
+
516
+ if (!_.isEmpty(nextProps.formData) && !_.isEqual(this.state.formData, nextProps.formData)) {
517
+ if (nextProps.isNewVersionFlow) {
518
+ const tabKey = (this.state.tabKey !== nextProps.formData[nextProps.currentTab - 1].tabKey) ? nextProps.formData[nextProps.currentTab - 1].tabKey : this.state.tabKey;
519
+
520
+ this.setState({tabKey});
521
+ }
522
+ this.setState({formData: nextProps.formData, currentLangTab}, () => {
523
+ if (nextProps?.isNewVersionFlow && !this.state?.formData[this.state?.currentTab - 1][this.state?.formData[this.state?.currentTab - 1]?.activeTab]?.tabKey) {
524
+ this.resetTabKeys(nextProps.formData, nextProps.tabCount, false, true);
525
+ }
526
+ if (type === 'embedded' || ( this.props.schema.channel && this.props.schema.channel.toUpperCase() === 'EMAIL')) {
527
+ // Don't run validation if we're in Test & Preview mode
528
+ if (!nextProps.isTestAndPreviewMode) {
529
+ this.validateForm();
530
+ }
531
+ }
532
+ if ((this.props.schema && this.props.schema.channel && this.props.schema.channel.toUpperCase() === 'MOBILEPUSH')) {
533
+ // Don't run validation if we're in Test & Preview mode
534
+ if (!nextProps.isTestAndPreviewMode) {
535
+ this.validateForm();
536
+ }
537
+ }
538
+ });
539
+ }
540
+
541
+
542
+ if (!_.isEqual(nextProps.schema, this.props.schema)) {
543
+ this.setState({ isFormValid: (this.props.isEdit ? this.props.isEdit : true) }, () => {
544
+ if (!this.props.isEdit || (this.props.isEdit && !_.isEmpty(nextProps.formData))) {
545
+ let resetTabKeys = (this.state.formData.tabKey === undefined);
546
+ if ((this.props.schema && this.props.schema.channel && this.props.schema.channel.toUpperCase() === 'MOBILEPUSH')) {
547
+ resetTabKeys = false;
548
+ }
549
+ if (this.props.isNewVersionFlow) {
550
+ resetTabKeys = false;
551
+ }
552
+
553
+ this.initialiseForm(nextProps.schema, false, resetTabKeys);
554
+ // Don't run validation if we're in Test & Preview mode
555
+ if (!nextProps.isTestAndPreviewMode) {
556
+ this.validateForm();
557
+ }
558
+ }
559
+ });
560
+ }
561
+ if (!_.isEqual(nextProps.checkValidation, this.props.checkValidation)) {
562
+ this.setState({ checkValidation: nextProps.checkValidation });
563
+ }
564
+
565
+ if (this.props.currentTab !== nextProps.currentTab && nextProps.currentTab) {
566
+ this.setState({currentTab: nextProps.currentTab});
567
+ }
568
+
569
+ if (!_.isEmpty(nextProps.injectedTags) && !_.isEqual(nextProps.injectedTags, this.props.injectedTags)) {
570
+ // Don't run validation if we're in Test & Preview mode
571
+ if (!nextProps.isTestAndPreviewMode) {
572
+ this.validateForm(nextProps.tags, nextProps.injectedTags);
573
+ }
574
+ }
575
+ if (!_.isEmpty(nextProps.tags) && !_.isEqual(nextProps.tags, this.props.tags)) {
576
+ // Don't run validation if we're in Test & Preview mode
577
+ if (!nextProps.isTestAndPreviewMode) {
578
+ this.validateForm(nextProps.tags, nextProps.injectedTags);
579
+ }
580
+ }
581
+ if (!_.isEqual(nextProps.showModal, this.props.showModal)) {
582
+ this.setState({showModal: nextProps.showModal});
583
+ }
584
+ if (nextProps.modal && (!_.isEqual(nextProps.modal, this.props.modal) || _.isEmpty(this.state.modal))) {
585
+ this.setState({modal: nextProps.modal});
586
+ }
587
+ if (nextProps.isDrawerRequired !== this.props.isDrawerRequired) {
588
+ this.setState({
589
+ isDrawerVisible: nextProps.isDrawerRequired,
590
+ });
591
+ }
592
+ }
593
+
594
+ onEdit() {
595
+
596
+ }
597
+ getModal = () => {
598
+ let modal;
599
+ if (this.state.modal) {
600
+ if (this.state.modal.type === 'confirm') {
601
+ modal = (<Modal
602
+ open={this.state.showModal}
603
+ title={this.state.modal.title}
604
+ onOk={this.handleOk}
605
+ onCancel={this.handleCancel}
606
+ footer={[
607
+ <CapButton key="back" onClick={this.handleCancel}><FormattedMessage {...messages.cancel} /></CapButton>,
608
+ <CapButton key="submit" type="primary" id={this.state.modal.id} onClick={this.handleOk}>
609
+ <FormattedMessage {...messages.yes} />
610
+ </CapButton>,
611
+ ]}
612
+ >
613
+ {this.state.modal.body}
614
+ </Modal>);
615
+ } else if (this.state.modal.type === "info") {
616
+ modal = (<Modal
617
+ open={this.state.showModal}
618
+ title={this.state.modal.title}
619
+ onOk={this.handleOk}
620
+ onCancel={this.handleCancel}
621
+ footer={[
622
+ <CapButton key="back" onClick={this.handleCancel}><FormattedMessage {...messages.ok} /></CapButton>,
623
+ ]}
624
+ >
625
+ {this.state.modal.body}
626
+ </Modal>);
627
+ } else if (this.state.modal.type === "action") {
628
+ modal = (<Modal
629
+ open={this.state.showModal}
630
+ title={this.state.modal.title}
631
+ onOk={this.handleOk}
632
+ onCancel={this.handleCancel}
633
+ footer={[
634
+ ]}
635
+ afterClose={this.handleCloseModal}
636
+ >
637
+ {this.state.modal.jsx}
638
+ </Modal>);
639
+ }
640
+ }
641
+ return modal;
642
+ };
643
+
644
+ openNotificationWithIcon = (type, message, key) => {
645
+ CapNotification.error({
646
+ key: key || type + message,
647
+ message: (`${type.toUpperCase()} ! ! ! `),
648
+ description: message, //'This is the content of the notification. This is the content of the notification. This is the content of the notification.',
649
+ });
650
+ }
651
+
652
+ handleCloseModal = () => {
653
+ const event = this.state.currentEvent;
654
+ if (!_.isEmpty(this.state.currentEvent)) {
655
+ switch (event) {
656
+ case ADD_LANGUAGE:
657
+ this.updateFormData(this.state.currentEventData, this.state.currentEventVal, this.state.currentEvent);
658
+ break;
659
+ default:
660
+ break;
661
+ }
662
+ }
663
+ }
664
+
665
+ handleVisibleChange = (popoverVisible) => {
666
+ this.setState({ popoverVisible });
667
+ };
668
+
669
+ handleCustomPopoverVisibleChange = (customPopoverVisible) => {
670
+ this.setState({ customPopoverVisible });
671
+ };
672
+
673
+ resetTabKeys(form, count, changeDefaultTab, setFormTabKey) {
674
+ const tabCount = count || this.state.tabCount;
675
+
676
+
677
+ const formData = _.cloneDeep(form);
678
+ let baseIndex = 0;
679
+ for (let idx = 0; idx < tabCount; idx += 1) {
680
+ if (this.props.isNewVersionFlow && formData[idx].selectedLanguages.length > 0) {
681
+ for (let langIndex = 0; langIndex < formData[idx].selectedLanguages.length; langIndex += 1 ) {
682
+ const id = _.uniqueId();
683
+ if (formData[idx][formData[idx].selectedLanguages[langIndex]]) {
684
+ if (!formData[idx][formData[idx].selectedLanguages[langIndex]].tabKey) {
685
+ formData[idx][formData[idx].selectedLanguages[langIndex]].tabKey = id;
686
+ }
687
+ if (formData[idx].base) {
688
+ formData.base[formData.base.selectedLanguages[langIndex]].tabKey = id;
689
+ }
690
+ }
691
+ }
692
+ if (setFormTabKey) {
693
+ if (!formData[idx].activeTab) {
694
+ const baseLanguage = this.props.baseLanguage;
695
+
696
+ formData[idx].tabKey = formData[idx][baseLanguage].tabKey;
697
+ } else {
698
+ formData[idx].tabKey = formData[idx][formData[idx].activeTab].tabKey;
699
+ }
700
+ if (formData[idx].base) {
701
+ if (!formData.base.activeTab) {
702
+ const baseLanguage = this.props.baseLanguage;
703
+ formData.base.tabKey = formData.base[baseLanguage].tabKey;
704
+ } else {
705
+ formData.base.tabKey = formData.base[formData[idx].activeTab].tabKey;
706
+ }
707
+ }
708
+ }
709
+
710
+ if (formData[idx].base && formData.base.activeTab) {
711
+ // if (!formData.base.tabKey) {
712
+
713
+ formData.base.tabKey = formData[idx][formData[idx].activeTab].tabKey;
714
+ //}
715
+ baseIndex = idx;
716
+ }
717
+ } else if (formData[idx]) {
718
+ if (!formData[idx].tabKey) {
719
+ formData[idx].tabKey = _.uniqueId();
720
+ }
721
+ if (formData[idx].base) {
722
+ formData.base = formData[idx];
723
+ baseIndex = idx;
724
+ }
725
+ }
726
+ }
727
+
728
+ if ((formData.base && formData.base.tabKey && formData.base.tabKey !== '' && !changeDefaultTab) || (formData.base && this.state.tabKey !== formData.base.tabKey)) {
729
+ if (this.state.currentTab === 1 && !this.props.isNewVersionFlow) {
730
+ this.setState({tabKey: formData.base.tabKey});
731
+ } else if (this.props.isNewVersionFlow && this.state.tabKey !== formData[this.state.currentTab - 1].tabKey) {
732
+ this.setState({tabKey: formData[this.state.currentTab - 1].tabKey});
733
+ }
734
+ }
735
+ if (setFormTabKey) {
736
+ this.setState({tabKey: formData.base.tabKey});
737
+ }
738
+
739
+
740
+ if (changeDefaultTab) {
741
+ this.setState({formData}, () => {
742
+ this.props.onChange(formData, this.state.tabCount);
743
+ this.validateForm();
744
+ });
745
+ } else {
746
+ this.setState({formData, currentTab: baseIndex + 1}, () => {
747
+ this.props.onChange(formData, this.state.tabCount);
748
+ this.validateForm();
749
+ });
750
+ }
751
+ }
752
+ isValidExternalLink = ({obj, key}) => {
753
+ if (key in obj) {
754
+ const url = _.get(obj, key);
755
+ return (url && url !== "" && isUrl(url));
756
+ }
757
+ return true;
758
+ }
759
+ errorFieldsPresent(errorData, channel) {
760
+ const formData = _.cloneDeep(this.state.formData);
761
+ let isPresent = true;
762
+ if (channel === SMS) {
763
+ const formDataKeys = Object.keys(formData[0]);
764
+ const errorDataKeys = Object.keys(errorData[0]);
765
+ isPresent = _.isEqualWith(formDataKeys, errorDataKeys, (formDataKey, errorDataKey) => formDataKeys.includes(errorDataKey));
766
+ }
767
+ return isPresent;
768
+ }
769
+ /* eslint-disable */
770
+ validateForm(tags, injectedTags, isSave, showMessages, onValidationComplete) {
771
+ const channel = _.get(this, "props.schema.channel");
772
+ let isValid = true;
773
+ let isLiquidValid = true;
774
+ const type = (this.props.location && this.props.location.query.type) ? this.props.location.query.type : 'full';
775
+ const currentModule = (this.props.location && this.props.location.query.module) ? this.props.location.query.module : 'default';
776
+ let errorData = _.cloneDeep(this.state.errorData);
777
+ if (channel && channel.toUpperCase() === SMS) {
778
+ for (let count = 0; count < this.state.tabCount; count += 1) {
779
+ if (_.isEmpty(errorData[count])) {
780
+ // Do not return early. An empty tab object can appear transiently and returning here
781
+ // prevents onFormValidityChange from firing, which makes Done appear unresponsive.
782
+ errorData[count] = {};
783
+ }
784
+ const index = count + 1;
785
+ if (!this.state.formData[count]) {
786
+ continue;
787
+ }
788
+ const content = this.state.formData[count][`sms-editor${index > 1 ? index : ''}`];
789
+ const unicodeCheck = this.state.formData[count][`unicode-validity${index > 1 ? index : ''}`];
790
+ const ifUnicode = content && content !== '' ? checkUnicode(content) : false;
791
+
792
+ let tagValidationResponse = false;
793
+ if (content) {
794
+ tagValidationResponse = this.validateTags(content, tags, false, this.props?.isFullMode);
795
+ }
796
+
797
+ const tagResult = tagValidationResponse && typeof tagValidationResponse === 'object'
798
+ ? tagValidationResponse
799
+ : { valid: false, missingTags: [], isBraceError: false };
800
+ if (tagResult.valid) {
801
+ errorData[count][`sms-editor${index > 1 ? index : ''}`] = false;
802
+ } else {
803
+ const { MISSING_TAG_ERROR, GENERIC_VALIDATION_ERROR, TAG_BRACKET_COUNT_MISMATCH_ERROR } = errorMessageForTags || {};
804
+ const { missingTags, isBraceError } = tagResult;
805
+ errorData[count][`sms-editor${index > 1 ? index : ''}`] = missingTags && missingTags.length ? MISSING_TAG_ERROR : (isBraceError ? TAG_BRACKET_COUNT_MISMATCH_ERROR : GENERIC_VALIDATION_ERROR);
806
+ errorData[count][`bracket-error`] = isBraceError && TAG_BRACKET_COUNT_MISMATCH_ERROR;
807
+ isValid = false;
808
+ }
809
+ if(content !== '' && (ifUnicode && !unicodeCheck)) {
810
+ errorData[count][`unicode-validity${index > 1 ? index : ''}`] = true;
811
+ isValid = false;
812
+ } else if (content === '') {
813
+ errorData[count][`sms-editor${index > 1 ? index : ''}`] = errorMessageForTags.GENERIC_VALIDATION_ERROR;
814
+ isValid = false;
815
+ } else {
816
+ errorData[count][`unicode-validity${index > 1 ? index : ''}`] = false;
817
+ }
818
+ }
819
+ if(type !== 'embedded' && (!this.state.formData['template-name'] || this.state.formData['template-name'] === '')) {
820
+ errorData['template-name'] = true;
821
+ isValid = false;
822
+ } else {
823
+ errorData['template-name'] = false;
824
+ }
825
+ isLiquidValid = isValid;
826
+ }
827
+
828
+ //validation for LINE
829
+ if (channel === LINE) {
830
+
831
+ if (this.state.formData['message-editor'] !== undefined ) {
832
+ const content = this.state.formData['0']['message-editor'] || '';
833
+
834
+ const tagValidationResponse = this.validateTags((content), tags, false, this.props?.isFullMode);
835
+
836
+ if (tagValidationResponse.valid) {
837
+ errorData = {
838
+ ...errorData,
839
+ 0: {
840
+ ...errorData[0],
841
+ 'message-editor': false,
842
+ }
843
+ };
844
+ } else {
845
+ errorData = {
846
+ ...errorData,
847
+ 0: {
848
+ ...errorData[0],
849
+ 'message-editor': true,
850
+ }
851
+ };
852
+ isValid = false;
853
+ }
854
+ if (content.trim() === '') {
855
+ errorData = {
856
+ ...errorData,
857
+ 0: {
858
+ ...errorData[0],
859
+ 'message-editor': true,
860
+ }
861
+ };
862
+ isValid = false;
863
+ }
864
+ }
865
+
866
+ if (this.state.formData['image-upload-line'] !== undefined) {
867
+ const {
868
+ formData: {
869
+ 0: {
870
+ image,
871
+ imagePreview,
872
+ }
873
+ }
874
+ } = this.state;
875
+ const imgContentLength = image ? image.length : 0;
876
+ const imgContentLengthPreview = imagePreview ? imagePreview.length : 0;
877
+ if ( (imgContentLength < 1) && (imgContentLengthPreview < 1) ) {
878
+
879
+ errorData = {
880
+ ...errorData,
881
+ 0: {
882
+ ...errorData[0],
883
+ image: true,
884
+ imagePreview: true,
885
+ }
886
+ };
887
+ isValid = false;
888
+ }else{
889
+ errorData = {
890
+ ...errorData,
891
+ 0: {
892
+ ...errorData[0],
893
+ image: false,
894
+ imagePreview: false,
895
+ }
896
+ };
897
+ }
898
+ }
899
+ if(type !== 'embedded' && (!this.state.formData['template-name'] || this.state.formData['template-name'] === '')) {
900
+ errorData['template-name'] = true;
901
+ isValid = false;
902
+ } else {
903
+ errorData['template-name'] = false;
904
+ }
905
+ }
906
+
907
+ if (this.props.schema && this.props.schema.channel && this.props.schema.channel.toUpperCase() === 'WECHAT') {
908
+
909
+ _.forEach(this.state.formData, (val, index) => {
910
+
911
+ if (index.indexOf('template') > -1 && this.state.formData[index] !== undefined) {
912
+ const content = this.state.formData[index];
913
+ if (content.trim() === '') {
914
+ errorData[index] = true;
915
+ isValid = false;
916
+ } else {
917
+ const tagValidationResponse = this.validateTags(content, tags, false, this.props?.isFullMode);
918
+
919
+ if (tagValidationResponse.valid) {
920
+ errorData[index] = false;
921
+ } else {
922
+ errorData[index] = true;
923
+ isValid = false;
924
+ }
925
+ }
926
+ } else if (index === 'wechat-template') {
927
+ const content = this.state.formData[index];
928
+ if (content.trim() === '') {
929
+ errorData[index] = true;
930
+ isValid = false;
931
+ }
932
+ }
933
+ });
934
+
935
+ }
936
+ if(this.props.schema && this.props.schema.channel && this.props.schema.channel.toUpperCase() === "MOBILEPUSH"){
937
+ let androidValid = true, iosValid = true;
938
+ let isOnlyAndroid = false, isOnlyIos = false;
939
+ const { TAG_BRACKET_COUNT_MISMATCH_ERROR } = errorMessageForTags;
940
+ const androidData = {};
941
+ if (this.state.formData && this.state.formData[0]) {
942
+ for(let data in this.state.formData[0]){
943
+ if(!!this.state.formData[0][data] && data !== "tabKey" && data !== "base"){
944
+ androidData[data] = this.state.formData[0][data];
945
+ }
946
+ }
947
+ if(_.isEmpty(androidData)){
948
+ isOnlyIos = true;
949
+ }
950
+ const iosData = {};
951
+ for(let data in this.state.formData[1]){
952
+ if(!!this.state.formData[1][data] && data !== "tabKey" && data !== "base"){
953
+ iosData[data] = this.state.formData[1][data];
954
+ }
955
+ }
956
+ if(_.isEmpty(iosData)){
957
+ isOnlyAndroid = true;
958
+ }
959
+ _.forEach(this.state.formData, (tab, index) => {
960
+ let isCurrentTabValid = true;
961
+ let skipValidation = false;
962
+ if (type.toLowerCase() === 'embedded' && ((isOnlyAndroid && parseInt(index) === 1) || (isOnlyIos && parseInt(index) === 0)) && isOnlyAndroid !== isOnlyIos) {
963
+ // skip validation of the tab which is not open in embedded mode
964
+ skipValidation = true;
965
+ }
966
+ if (!isNaN(index) && tab && !skipValidation) {
967
+ const selector = index > 0 ? `${Number(index) + 1}` : '';
968
+ let message = tab[`message-editor${selector}`];
969
+
970
+ if (errorData[parseInt(index)]) {
971
+ if (message) {
972
+ message = message.trim();
973
+ if (message === "") {
974
+ errorData[parseInt(index)][`message-editor${selector}`] = true;
975
+ isValid = false;
976
+ isCurrentTabValid = false;
977
+ } else if (this.props.restrictPersonalization && hasPersonalizationTags(message)) {
978
+ errorData[parseInt(index)][`message-editor${selector}`] = true;
979
+ isValid = false;
980
+ isCurrentTabValid = false;
981
+ } else {
982
+ errorData[parseInt(index)][`message-editor${selector}`] = false;
983
+ const tagValidationResponse = this.validateTags(message, tags, false, this.props?.isFullMode);
984
+
985
+ if (tagValidationResponse.valid) {
986
+ errorData[parseInt(index)][`message-editor${selector}`] = false;
987
+ } else {
988
+ const { isBraceError } = tagValidationResponse;
989
+ errorData[parseInt(index)][`message-editor${selector}`] = isBraceError ? TAG_BRACKET_COUNT_MISMATCH_ERROR : true;
990
+ errorData[parseInt(index)][`bracket-error`] = isBraceError && TAG_BRACKET_COUNT_MISMATCH_ERROR;
991
+ isValid = false;
992
+ }
993
+ }
994
+
995
+ } else {
996
+ errorData[parseInt(index)][`message-editor${selector}`] = true;
997
+ isValid = false;
998
+ }
999
+
1000
+
1001
+
1002
+ let title = tab[`message-title${selector}`]
1003
+ if(title){
1004
+ title = title.trim();
1005
+ if (title === "") {
1006
+ errorData[parseInt(index)][`message-title${selector}`] = true;
1007
+ isValid = false;
1008
+ isCurrentTabValid = false;
1009
+ } else if (this.props.restrictPersonalization && hasPersonalizationTags(title)) {
1010
+ errorData[parseInt(index)][`message-title${selector}`] = true;
1011
+ isValid = false;
1012
+ isCurrentTabValid = false;
1013
+ } else {
1014
+ errorData[parseInt(index)][`message-title${selector}`] = false;
1015
+ const tagValidationResponse = this.validateTags(title, tags, false, this.props?.isFullMode);
1016
+
1017
+ if (tagValidationResponse.valid) {
1018
+ errorData[parseInt(index)][`message-title${selector}`] = false;
1019
+ } else {
1020
+ const {isBraceError} = tagValidationResponse;
1021
+ errorData[parseInt(index)][`message-title${selector}`] = isBraceError ? TAG_BRACKET_COUNT_MISMATCH_ERROR : true;
1022
+ errorData[parseInt(index)][`bracket-error`] = isBraceError && TAG_BRACKET_COUNT_MISMATCH_ERROR;
1023
+ isValid = false;
1024
+ }
1025
+ }
1026
+ } else {
1027
+ errorData[parseInt(index)][`message-title${selector}`] = true;
1028
+ isValid = false;
1029
+ isCurrentTabValid = false;
1030
+ }
1031
+ // if(tab['primary-cta-link'].trim() === ""){
1032
+ // errorData[parseInt(index)][`primary-cta-link`] = true;
1033
+ // isValid = false;
1034
+ // }else {
1035
+ // errorData[parseInt(index)]['primary-cta-link'] = false;
1036
+ // }
1037
+ if(tab['cta-deeplink-select'] === ""){
1038
+ errorData[parseInt(index)][`cta-deeplink-select`] = true;
1039
+ isValid = false;
1040
+ isCurrentTabValid = false;
1041
+ }else {
1042
+ errorData[parseInt(index)]['cta-deeplink-select'] = false;
1043
+ }
1044
+ if(tab['cta-deeplink2-select'] === ""){
1045
+ errorData[parseInt(index)][`cta-deeplink2-select`] = true;
1046
+ isValid = false;
1047
+ isCurrentTabValid = false;
1048
+ }else {
1049
+ errorData[parseInt(index)]['cta-deeplink2-select'] = false;
1050
+ }
1051
+ // }
1052
+ if(!this.isValidExternalLink({obj: tab, key: 'cta-deeplink-text'})){
1053
+ errorData[parseInt(index)][`cta-deeplink-text`] = true;
1054
+ isValid = false;
1055
+ isCurrentTabValid = false;
1056
+ }else {
1057
+ errorData[parseInt(index)]['cta-deeplink-text'] = false;
1058
+ }
1059
+
1060
+ if(!this.isValidExternalLink({obj: tab, key: 'cta-deeplink2-text'})){
1061
+ errorData[parseInt(index)][`cta-deeplink2-text`] = true;
1062
+ isValid = false;
1063
+ isCurrentTabValid = false;
1064
+ }else {
1065
+ errorData[parseInt(index)]['cta-deeplink2-text'] = false;
1066
+ }
1067
+
1068
+
1069
+ if(tab['secondary-cta-0-link'] === ""){
1070
+ errorData[parseInt(index)][`secondary-cta-0-link`] = true;
1071
+ isValid = false;
1072
+ isCurrentTabValid = false;
1073
+ }else {
1074
+ errorData[parseInt(index)]['secondary-cta-0-link'] = false;
1075
+ }
1076
+ if(tab['secondary-cta-0-label'] === ""){
1077
+ errorData[parseInt(index)][`secondary-cta-0-label`] = true;
1078
+ isValid = false;
1079
+ isCurrentTabValid = false;
1080
+ }else {
1081
+ errorData[parseInt(index)]['secondary-cta-0-label'] = false;
1082
+ }
1083
+
1084
+
1085
+ if(!this.isValidExternalLink({obj: tab, key: 'cta-deeplink-secondary-cta-0-text'})){
1086
+ errorData[parseInt(index)][`cta-deeplink-secondary-cta-0-text`] = true;
1087
+ isValid = false;
1088
+ isCurrentTabValid = false;
1089
+ }else {
1090
+ errorData[parseInt(index)]['cta-deeplink-secondary-cta-0-text'] = false;
1091
+ }
1092
+ if(tab['cta-deeplink-secondary-cta-0-select'] === ""){
1093
+ errorData[parseInt(index)][`cta-deeplink-secondary-cta-0-select`] = true;
1094
+ isValid = false;
1095
+ isCurrentTabValid = false;
1096
+ }else {
1097
+ errorData[parseInt(index)]['cta-deeplink-secondary-cta-0-select'] = false;
1098
+ }
1099
+ if(tab['secondary-cta-1-label'] === ""){
1100
+ errorData[parseInt(index)][`secondary-cta-1-label`] = true;
1101
+ isValid = false;
1102
+ isCurrentTabValid = false;
1103
+ }else {
1104
+ errorData[parseInt(index)]['secondary-cta-1-label'] = false;
1105
+ }
1106
+
1107
+ if(!this.isValidExternalLink({obj: tab, key: 'cta-deeplink-secondary-cta-1-text'})){
1108
+ errorData[parseInt(index)][`cta-deeplink-secondary-cta-1-text`] = true;
1109
+ isValid = false;
1110
+ isCurrentTabValid = false;
1111
+ }else {
1112
+ errorData[parseInt(index)]['cta-deeplink-secondary-cta-1-text'] = false;
1113
+ }
1114
+ if(tab['cta-deeplink-secondary-cta-1-select'] === ""){
1115
+ errorData[parseInt(index)][`cta-deeplink-secondary-cta-1-select`] = true;
1116
+ isValid = false;
1117
+ isCurrentTabValid = false;
1118
+ }else {
1119
+ errorData[parseInt(index)]['cta-deeplink-secondary-cta-1-select'] = false;
1120
+ }
1121
+
1122
+ if(!this.isValidExternalLink({obj: tab, key: 'cta-deeplink-secondary-cta-1-text2'})){
1123
+ errorData[parseInt(index)][`cta-deeplink-secondary-cta-1-text2`] = true;
1124
+ isValid = false;
1125
+ isCurrentTabValid = false;
1126
+ }else {
1127
+ errorData[parseInt(index)]['cta-deeplink-secondary-cta-1-text2'] = false;
1128
+ }
1129
+ if(tab['cta-deeplink-secondary-cta-1-select2'] === ""){
1130
+ errorData[parseInt(index)][`cta-deeplink-secondary-cta-1-select2`] = true;
1131
+ isValid = false;
1132
+ isCurrentTabValid = false;
1133
+ }else {
1134
+ errorData[parseInt(index)]['cta-deeplink-secondary-cta-1-select2'] = false;
1135
+ }
1136
+
1137
+ if(typeof tab['image'] !== 'undefined' && tab['image'] === ""){
1138
+ errorData[parseInt(index)]['image'] = true;
1139
+ isValid = false;
1140
+ isCurrentTabValid = false;
1141
+ }else{
1142
+ errorData[parseInt(index)]['image'] = false;
1143
+ }
1144
+ _.forEach(Object.keys(tab), (key) => {
1145
+ if (key.indexOf('custom-value') > -1) {
1146
+ if (tab[key] === "") {
1147
+ errorData[parseInt(index)][key] = true;
1148
+ isValid = false;
1149
+ isCurrentTabValid = false;
1150
+ }else {
1151
+ errorData[parseInt(index)][key] = false;
1152
+ }
1153
+ }
1154
+ });
1155
+ }
1156
+ }
1157
+ if(index == 0){
1158
+ androidValid = isCurrentTabValid;
1159
+ } else if(index == 1){
1160
+ iosValid = isCurrentTabValid;
1161
+ }
1162
+ });
1163
+ if(type !== 'embedded' && (!this.state.formData['template-name'] || this.state.formData['template-name'] === '')) {
1164
+ errorData['template-name'] = true;
1165
+ isValid = false;
1166
+ androidValid = false;
1167
+ iosValid = false;
1168
+ } else {
1169
+ errorData['template-name'] = false;
1170
+ }
1171
+ if(isSave && (androidValid || iosValid)){
1172
+ const modal = this.state.modal;
1173
+ const androidData = {};
1174
+
1175
+ for(let data in this.state.formData[0]){
1176
+ if(!!this.state.formData[0][data] && data !== "tabKey" && data !== "base"){
1177
+ androidData[data] = this.state.formData[0][data];
1178
+ }
1179
+ }
1180
+ if(_.isEmpty(androidData) && this.state.currentTab == 2){
1181
+ this.setState({androidValid, iosValid, errorData}, () => {
1182
+ this.props.setModalContent('ios');
1183
+ });
1184
+ // modal.body = "Android template is not configured. Save without Android template";
1185
+ // modal.id = "ios";
1186
+ // modal.type = 'confirm';
1187
+ return; //returning from here will not call the liquid middleware
1188
+ }
1189
+ const iosData = {};
1190
+ for(let data in this.state.formData[1]){
1191
+ if(!!this.state.formData[1][data] && data !== "tabKey"){
1192
+ iosData[data] = this.state.formData[1][data];
1193
+ }
1194
+ }
1195
+ if(_.isEmpty(iosData) && this.state.currentTab == 1){
1196
+ this.setState({androidValid, iosValid, errorData}, () => {
1197
+ this.props.setModalContent('android');
1198
+ });
1199
+ // modal.body = "IOS template is not configured, Save without IOS template";
1200
+ // modal.id = 'android';
1201
+ // modal.type = 'confirm';
1202
+ // this.setState({modal, showModal: true, androidValid, iosValid});
1203
+ return; //returning from here will not call the liquid middleware
1204
+ }
1205
+ }
1206
+ if (!androidValid || !iosValid) {
1207
+ this.setState({androidValid, iosValid});
1208
+ }
1209
+ if (isOnlyAndroid && androidValid) {
1210
+ isValid = isValid && true;
1211
+ } else if (isOnlyIos && iosValid) {
1212
+ isValid = isValid && true;
1213
+ } else if (!isOnlyIos && !isOnlyAndroid && androidValid && iosValid) {
1214
+ isValid = isValid && true;
1215
+ }
1216
+ isLiquidValid = isValid;
1217
+ }
1218
+ }
1219
+
1220
+ // Form Data Validation for Email Channel
1221
+ if (this.props?.schema?.channel?.toUpperCase() === EMAIL) {
1222
+ // Validating Email By iterating all elements of FormBuilder
1223
+ const isEmail = true;
1224
+ _.forEach(this.state.formData,(data, index) => {
1225
+
1226
+ if (!this.state.formData["template-subject"] && type.toLowerCase() === 'embedded' && (currentModule.toLowerCase() === 'loyalty' || currentModule.toLowerCase() === 'dvs' || currentModule.toLowerCase() === 'timeline' || currentModule.toLowerCase() === 'library')) {
1227
+ errorData["template-subject"] = true;
1228
+ isValid = false;
1229
+ }
1230
+ // Check if template subject present in form data from Loyalty, DVS, Timeline
1231
+ if (index === 'template-subject' && type.toLowerCase() === 'embedded' && (currentModule.toLowerCase() === 'loyalty' || currentModule.toLowerCase() === 'dvs' || currentModule.toLowerCase() === 'timeline' || currentModule.toLowerCase() === 'library')) {
1232
+ //checking email subject name validation
1233
+ if (!data?.trim()) {
1234
+ errorData[index] = true;
1235
+ isValid = false;
1236
+ } else {
1237
+ errorData[index] = false;
1238
+ }
1239
+ }
1240
+
1241
+ // Check if Template name present in full mode
1242
+ if(type !== 'embedded' && index === 'template-name' && data === '') {
1243
+ errorData[index] = true;
1244
+ isValid = false;
1245
+ } else if(type !== 'embedded' && index === 'template-name' && data !== '') {
1246
+ errorData[index] = false;
1247
+ }
1248
+
1249
+ // Check formData if selected version present or not
1250
+ if(index === 'template-version' && data === '') {
1251
+ errorData[index] = true;
1252
+ isValid = false;
1253
+ } else if(index === 'template-version' && data !== '') {
1254
+ errorData[index] = false;
1255
+ }
1256
+ isLiquidValid = isValid; // Existing validations support for liquid enabled orgs
1257
+ // Check error for the versions
1258
+ if (!isNaN(index) || index === 'base') {
1259
+ if (errorData[index] === undefined) {
1260
+ errorData[index] = {};
1261
+ }
1262
+ let errorString = "";
1263
+ for (var i = 0; i < data.selectedLanguages.length; i+= 1) {
1264
+ const currentLang = data.selectedLanguages[i];
1265
+ const content = (data[currentLang] || {})['template-content'];
1266
+ if (!content) {
1267
+ return false;
1268
+ }
1269
+ const tagValidationResponse = this.validateTags(content, tags, isEmail, this.props?.isFullMode);
1270
+ // Check for base64 images in email content
1271
+ isEmail && containsBase64Images({content, callback:()=>{
1272
+ tagValidationResponse.valid = false;
1273
+ tagValidationResponse.hasBase64Images = true;
1274
+ }})
1275
+
1276
+ if (errorData[index][currentLang] === undefined) {
1277
+ errorData[index][currentLang] = {};
1278
+ }
1279
+ if (tagValidationResponse?.valid) {
1280
+ errorData[index][currentLang]['template-content'] = false;
1281
+ } else {
1282
+ errorData[index][currentLang]['template-content'] = true;
1283
+ isValid = false;
1284
+ isLiquidValid = false;
1285
+ if ((showMessages && !isNaN(index)) || this.isLiquidFlowSupportedByChannel()) {
1286
+ if (tagValidationResponse?.missingTags?.length > 0) {
1287
+ errorString += `${this.props.intl.formatMessage(messages.contentNotValidLanguage)} ${currentLang}\n`;
1288
+ }
1289
+ if (tagValidationResponse?.missingTags?.length > 0) {
1290
+ errorString += `${this.props.intl.formatMessage(messages.missingTags)} ${tagValidationResponse.missingTags.toString()}\n`;
1291
+ }
1292
+ if (tagValidationResponse?.isBraceError){
1293
+ errorString += this.props.intl.formatMessage(globalMessages.unbalanacedCurlyBraces);
1294
+ }
1295
+ if (tagValidationResponse?.isContentEmpty) {
1296
+ errorString += this.props.intl.formatMessage(messages.emailBodyEmptyError);
1297
+ // Adds a bypass for cases where content is initially empty in the creation flow.
1298
+ if(this.isLiquidFlowSupportedByChannel()){
1299
+ errorString = "";
1300
+ isLiquidValid = true;
1301
+ }
1302
+ }
1303
+ if (tagValidationResponse?.hasBase64Images) {
1304
+ errorString += this.props.intl.formatMessage(messages.base64ImageError);
1305
+ }
1306
+ }
1307
+ }
1308
+ }
1309
+ if (errorString) {
1310
+ if (this.isLiquidFlowSupportedByChannel()) {
1311
+ this.setState(
1312
+ (prevState) => ({
1313
+ liquidErrorMessage: {
1314
+ ...prevState.liquidErrorMessage,
1315
+ STANDARD_ERROR_MSG: [errorString],
1316
+ },
1317
+ }),
1318
+ () => {
1319
+ this.props.showLiquidErrorInFooter(this.state.liquidErrorMessage, this.props.channel === SMS? null: this.state.currentTab);
1320
+ }
1321
+ );
1322
+ // Footer shows the error; skip notification for Email CK/BEE (non-HTML) flow to avoid duplicate feedback
1323
+ const isEmailChannel = this.props?.schema?.channel?.toUpperCase() === EMAIL;
1324
+ if (!isEmailChannel) {
1325
+ this.openNotificationWithIcon('error', errorString, 'email-validation-error');
1326
+ }
1327
+ } else {
1328
+ this.openNotificationWithIcon('error', errorString, "email-validation-error");
1329
+ }
1330
+ }
1331
+ _.forEach(errorData[index], (versionData, key) => {
1332
+ if (data.selectedLanguages.indexOf(key) === -1) {
1333
+ delete errorData[index][key];
1334
+ }
1335
+ });
1336
+ }
1337
+ });
1338
+ }
1339
+
1340
+ const isTemplateValid = this.isLiquidFlowSupportedByChannel() ? isLiquidValid : isValid;
1341
+ //Updating the state with the error data
1342
+ this.setState((prevState) => ({
1343
+ isFormValid: isTemplateValid,
1344
+ liquidErrorMessage: {
1345
+ //Do not update the LIQUID_ERROR_MSG validation error message
1346
+ ...prevState.liquidErrorMessage,
1347
+ //update Standard error message based on the updated content
1348
+ STANDARD_ERROR_MSG: isTemplateValid ? [] : prevState.liquidErrorMessage?.STANDARD_ERROR_MSG,
1349
+ },
1350
+ errorData,
1351
+ }), () => {
1352
+ this.props.showLiquidErrorInFooter(this.state.liquidErrorMessage, this.props.channel === SMS? null: this.state.currentTab);
1353
+
1354
+ if (onValidationComplete) {
1355
+
1356
+ onValidationComplete();
1357
+ }
1358
+ });
1359
+
1360
+
1361
+ this.props.onFormValidityChange(isTemplateValid, errorData);
1362
+ return isTemplateValid;
1363
+ }
1364
+
1365
+ indexOfEnd(targetString, string) {
1366
+ var io = targetString.indexOf(string);
1367
+ return io == -1 ? -1 : io + string.length;
1368
+ }
1369
+
1370
+ handleLiquidTemplateSubmit = (templateContent) => {
1371
+ const channel = this.props.channel || this.props?.schema?.channel?.toUpperCase();
1372
+
1373
+ if (templateContent && channel === EMAIL) {
1374
+ this.setState((prevState) => {
1375
+ return {
1376
+ formData: {
1377
+ ...prevState?.formData,
1378
+ base: {
1379
+ ...prevState?.formData?.base,
1380
+ [this.props.baseLanguage]: {
1381
+ ...prevState?.formData?.base?.[this.props.baseLanguage],
1382
+ "template-content": preprocessHtml(templateContent)
1383
+ }
1384
+ }
1385
+ }
1386
+ };
1387
+ }, () => {
1388
+ if (this.props.onSubmit) {
1389
+ this.props.onSubmit(this.state.formData);
1390
+ }
1391
+ });
1392
+ } else {
1393
+ // For all other channels
1394
+ this.props.onSubmit(this.state.formData);
1395
+ }
1396
+ }
1397
+ onSubmitWrapper = (args) => {
1398
+ const {singleTab = null} = args || {};
1399
+ // Liquid validation (extractTags + Aira) only in library mode
1400
+ const runLiquidValidation = this.isLiquidFlowSupportedByChannel() && !this.props.isFullMode;
1401
+ if (runLiquidValidation) {
1402
+ // For MPUSH, we need to validate both Android and iOS content separately
1403
+ if (this.props.channel === MOBILE_PUSH || this.props?.schema?.channel?.toUpperCase() === MOBILE_PUSH) {
1404
+ this.validateFormBuilderMPush(this.state.formData, singleTab);
1405
+ return;
1406
+ }
1407
+
1408
+ // For other channels (EMAIL, SMS, INAPP): only call extractTags if there are no brace/empty errors already.
1409
+ // Run sync validation first; if it fails, block and show errors without calling the API.
1410
+ this.validateForm(null, null, true, false, () => {
1411
+ if (!this.state.isFormValid) {
1412
+ this.props.stopValidation();
1413
+ return;
1414
+ }
1415
+ const content = getChannelData(this.props.schema.channel || this.props.channel, this.state.formData, this.props.baseLanguage);
1416
+
1417
+ const onError = ({ standardErrors, liquidErrors }) => {
1418
+ this.setState(
1419
+ (prevState) => ({
1420
+ liquidErrorMessage: {
1421
+ ...prevState.liquidErrorMessage,
1422
+ STANDARD_ERROR_MSG: standardErrors,
1423
+ LIQUID_ERROR_MSG: liquidErrors,
1424
+ },
1425
+ }),
1426
+ () => {
1427
+ this.props.showLiquidErrorInFooter(this.state.liquidErrorMessage);
1428
+ this.props.stopValidation();
1429
+ this.props.onFormValidityChange(false, this.state.errorData);
1430
+ }
1431
+ );
1432
+ };
1433
+
1434
+ const onSuccess = (contentToSubmit) => {
1435
+ const channel = this.props.channel || this.props?.schema?.channel?.toUpperCase();
1436
+ if(channel === EMAIL) {
1437
+ const content = this.state.formData?.base?.[this.props.baseLanguage]?.["template-content"] || "";
1438
+ this.handleLiquidTemplateSubmit(content);
1439
+ } else {
1440
+ this.handleLiquidTemplateSubmit(contentToSubmit);
1441
+ }
1442
+ };
1443
+
1444
+ validateLiquidTemplateContent(content, {
1445
+ getLiquidTags: this.props.actions.getLiquidTags,
1446
+ formatMessage: this.props.intl.formatMessage,
1447
+ messages,
1448
+ onError,
1449
+ onSuccess,
1450
+ skipTags: this.skipTags.bind(this)
1451
+ });
1452
+ });
1453
+ } else {
1454
+ this.props.onSubmit(this.state.formData);
1455
+ }
1456
+ }
1457
+
1458
+ saveForm(saveForm) {
1459
+ if (this.props.isNewVersionFlow && !saveForm) {
1460
+ this.props.getValidationData();
1461
+ return;
1462
+ }
1463
+ if ( this.state.isFormValid ) {
1464
+ this.onSubmitWrapper();
1465
+ } else {
1466
+ this.setState({checkValidation: true});
1467
+ this.validateForm(null, null, true);
1468
+ }
1469
+ }
1470
+
1471
+
1472
+ // New function to handle MPUSH content validation for both Android and iOS
1473
+ validateFormBuilderMPush = (formData,singleTab) => {
1474
+ const currentTab = this.state.currentTab;
1475
+
1476
+ // Set up callbacks for error and success handling
1477
+ const onLiquidError = ({ standardErrors, liquidErrors }) => {
1478
+ this.setState(
1479
+ (prevState) => ({
1480
+ liquidErrorMessage: {
1481
+ ...prevState.liquidErrorMessage,
1482
+ STANDARD_ERROR_MSG: standardErrors,
1483
+ LIQUID_ERROR_MSG: liquidErrors,
1484
+ },
1485
+ }),
1486
+ () => {
1487
+ this.props.showLiquidErrorInFooter(this.state.liquidErrorMessage, this.state.currentTab);
1488
+ this.props.stopValidation();
1489
+ // Block save: tell parent form is invalid so Done/submit is blocked
1490
+ this.props.onFormValidityChange(false, this.state.errorData);
1491
+ }
1492
+ );
1493
+ };
1494
+
1495
+ const onSuccess = (contentToSubmit) => {
1496
+ // Clear any previous errors
1497
+ this.setState(
1498
+ (prevState) => ({
1499
+ liquidErrorMessage: {
1500
+ ...prevState.liquidErrorMessage,
1501
+ STANDARD_ERROR_MSG: [],
1502
+ LIQUID_ERROR_MSG: [],
1503
+ },
1504
+ }),
1505
+ () => {
1506
+ // Format depends on the tabType
1507
+ this.handleLiquidTemplateSubmit(contentToSubmit);
1508
+ }
1509
+ );
1510
+ };
1511
+
1512
+ // Call the utility function with our callbacks
1513
+ validateMobilePushContent(formData, {
1514
+ currentTab,
1515
+ onError: onLiquidError,
1516
+ onSuccess,
1517
+ getLiquidTags: this.props.actions.getLiquidTags,
1518
+ formatMessage: this.props.intl.formatMessage,
1519
+ messages: messages,
1520
+ singleTab: singleTab?.toUpperCase(),
1521
+ });
1522
+ }
1523
+
1524
+ transformInjectedTags(tags) {
1525
+ _.forEach(tags, (tag) => {
1526
+ const temp = tag;
1527
+ let subKey = '';
1528
+ Object.keys(tag).map( (k) => {
1529
+ if (k.indexOf("subtags") !== -1) {
1530
+ subKey = k;
1531
+ }
1532
+ return true;
1533
+ });
1534
+ if (subKey !== '') {
1535
+ temp['tag-header'] = true;
1536
+ if (subKey !== 'subtags') {
1537
+ temp.subtags = _.cloneDeep(temp[subKey]);
1538
+ delete temp[subKey];
1539
+ }
1540
+ temp.subtags = this.transformInjectedTags(temp.subtags);
1541
+ }
1542
+ });
1543
+ return tags;
1544
+ }
1545
+
1546
+ checkIfSupportedTag(checkingTag, injectedTags) {
1547
+ let result = false;
1548
+ _.forEach(injectedTags, (tag, key) => {
1549
+ if (tag.subtags) {
1550
+ if (this.checkIfSupportedTag(checkingTag, tag.subtags)) {
1551
+ result = true;
1552
+ }
1553
+ } else if (checkingTag === key) {
1554
+ result = true;
1555
+ }
1556
+ });
1557
+
1558
+ return result;
1559
+ };
1560
+
1561
+ skipTags(tag) {
1562
+ // If the tag contains the word "entryTrigger.", then it's an event context tag and it should not be skipped.
1563
+ if (tag?.match(ENTRY_TRIGGER_TAG_REGEX)) {
1564
+ return false;
1565
+ }
1566
+ // Use some() to check if any pattern matches (stops on first match)
1567
+ return SKIP_TAGS_REGEX_GROUPS.some((group) => {
1568
+ // Create a new RegExp for each test to avoid state issues with global flag
1569
+ const groupRegex = new RegExp(group);
1570
+ return groupRegex.test(tag);
1571
+ });
1572
+ }
1573
+
1574
+ validateTags(content, tagsParam, isEmail = false, isFullMode = this.props?.isFullMode) {
1575
+ let currentModule = this.props.location.query.module ? this.props.location.query.module : 'default';
1576
+ if (this.props.tagModule) {
1577
+ currentModule = this.props.tagModule;
1578
+ }
1579
+ const tags = tagsParam ? tagsParam : this.props.tags;
1580
+ const contentForValidation = isEmail ? convert(content, GLOBAL_CONVERT_OPTIONS) : content;
1581
+ const isOutboundModule = (currentModule || '').toUpperCase() === OUTBOUND;
1582
+
1583
+ const initialMissingTags = (tags && tags.length && !isFullMode && isEmail && isOutboundModule && !isEmailUnsubscribeTagOptional() && !hasUnsubscribeTag(content))
1584
+ ? ['unsubscribe']
1585
+ : [];
1586
+
1587
+ const response = validateTagsCore({
1588
+ contentForBraceCheck: contentForValidation,
1589
+ contentForUnsubscribeScan: content,
1590
+ tags,
1591
+ currentModule,
1592
+ isFullMode,
1593
+ initialMissingTags, // [] or ['unsubscribe']; core uses this instead of definition-based when provided
1594
+ skipTagsFn: this.skipTags.bind(this),
1595
+ includeIsContentEmpty: true,
1596
+ });
1597
+
1598
+ // When unsubscribe tag is optional (isEmailUnsubscribeTagOptional): do not enforce unsubscribe (defensive splice); set isContentEmpty only when content is empty. Do not override response.valid so brace/format errors are preserved.
1599
+ const validString = /\S/.test(contentForValidation);
1600
+ if (isEmailUnsubscribeTagOptional() && isEmail && isOutboundModule) {
1601
+ const missingTagIndex = response.missingTags.indexOf('unsubscribe');
1602
+ if (missingTagIndex !== -1) {
1603
+ response.missingTags.splice(missingTagIndex, 1);
1604
+ }
1605
+ if (!validString) {
1606
+ response.isContentEmpty = true;
1607
+ }
1608
+ }
1609
+ return response;
1610
+ }
1611
+ /* eslint-enable */
1612
+ initializeParentSection(section, formDataObj, isContainer, index, ifError) {
1613
+ let formData = formDataObj;
1614
+ _.forEach(section.childSections, (childSection) => {
1615
+ if (childSection.type === 'col-label') {
1616
+ formData = this.initializeColLabelSection(childSection, formData, isContainer, index, ifError);
1617
+ } else if (childSection.type === 'multicols') {
1618
+ formData = this.initializeMultiColSection(childSection, formData, isContainer, index, ifError);
1619
+ } else {
1620
+ formData = this.initializeParentSection(childSection, formData, isContainer, index, ifError);
1621
+ }
1622
+ });
1623
+ return formData;
1624
+ }
1625
+
1626
+ // eslint-disable-next-line consistent-return
1627
+ initializeEditErrorData(formData, tabCount, isReturnErrorData) {
1628
+ const errorData = {};
1629
+ for (let idx = 0; idx < tabCount; idx += 1 ) {
1630
+ _.forEach(formData[0], (val, key) => {
1631
+ if (!errorData[idx]) {
1632
+ errorData[idx] = {};
1633
+ }
1634
+ if (key === 'tabKey' || key === 'base' || key === 'selectedLanguages') {
1635
+ return true;
1636
+ }
1637
+ if (this.props.isNewVersionFlow && formData[0].selectedLanguages.indexOf(key) > -1) {
1638
+ _.forEach(formData[0].selectedLanguages, (lang) => {
1639
+ if (errorData[idx] === undefined) {
1640
+ errorData[idx] = {};
1641
+ }
1642
+ if (errorData[idx][lang] === undefined) {
1643
+ errorData[idx][lang] = {};
1644
+ }
1645
+ errorData[idx][lang]['template-content'] = false;
1646
+ });
1647
+ } else {
1648
+ errorData[idx][key] = false;
1649
+ }
1650
+ return true;
1651
+ });
1652
+ }
1653
+ if (isReturnErrorData) {
1654
+ return errorData;
1655
+ }
1656
+ this.setState({errorData});
1657
+ }
1658
+
1659
+ initializeMultiColSection(section, formDataObj, isContainer, index, ifError) {
1660
+ const formData = _.cloneDeep(formDataObj);
1661
+ const newFormData = {};
1662
+ newFormData[index] = {};
1663
+ _.forEach(section.inputFields, (inputField) => {
1664
+ _.forEach(inputField.cols, (col) => {
1665
+ if (isContainer && !formData[index]) {
1666
+ formData[index] = {};
1667
+ }
1668
+ if (isContainer && this.props.isNewVersionFlow) {
1669
+ // if (formData[index] && formData[index].selectedLanguages && formData[index].selectedLanguages.length > 0) {
1670
+ // for (let i = 0; i < formData[index].selectedLanguages.length; i += 1 ) {
1671
+ // formData[index][formData[index].selectedLanguages[i]] = {};
1672
+ // }
1673
+ // } else {
1674
+ // formData[index][`${this.props.baseLanguage ? this.props.baseLanguage : 'en'}`] = {};
1675
+ // }
1676
+ _.forEach(this.props.supportedLanguages, (language) => {
1677
+ if (!formData[index][language.iso_code] && this.props.baseLanguage === language.iso_code) {
1678
+ formData[index][language.iso_code] = {};
1679
+ }
1680
+ });
1681
+ }
1682
+ // if (!ifError && (col.id.indexOf('template-label-') === -1 && col.id.indexOf('tagList-') === -1)) {
1683
+ // this.formElements.push(col.id);
1684
+ // }
1685
+ if (!col.onlyDisplay && !ifError) {
1686
+ if (col.id && col.id.indexOf('tagList-') === -1) {
1687
+ this.formElements.push(col.id);
1688
+ }
1689
+
1690
+ if (!this.props.isNewVersionFlow) {
1691
+ if (isContainer && formData[index][col.id] === undefined) {
1692
+ if (ifError) {
1693
+ formData[index][col.id] = false;
1694
+ } else if (isContainer && formData[index][col.id] === undefined && col.value) {
1695
+ formData[index][col.id] = col.value;
1696
+ } else {
1697
+ formData[index][col.id] = '';
1698
+ }
1699
+ } else if (!isContainer && ifError && formData[col.id] === undefined) {
1700
+ formData[col.id] = false;
1701
+ } else if (!isContainer && !ifError && formData[col.id] === undefined) {
1702
+ formData[col.id] = '';
1703
+ }
1704
+ } else if (!(col.id.indexOf('template-content') > -1 && col.id !== 'template-content')) {
1705
+ if (isContainer && formData[index] && formData[index][this.props.baseLanguage] && formData[index][this.props.baseLanguage][col.id] === undefined) {
1706
+ if (ifError) {
1707
+ formData[index][this.props.baseLanguage][col.id] = false;
1708
+ } else if (isContainer && formData[index][this.props.baseLanguage][col.id] === undefined && col.value) {
1709
+ formData[index][this.props.baseLanguage][col.id] = col.value;
1710
+ } else {
1711
+ formData[index][this.props.baseLanguage][col.id] = '';
1712
+ }
1713
+ } else if (!isContainer && ifError && formData[col.id] === undefined) {
1714
+ formData[col.id] = false;
1715
+ } else if (!isContainer && !ifError && formData[col.id] === undefined) {
1716
+ formData[col.id] = '';
1717
+ }
1718
+ }
1719
+ }
1720
+ if (col.type === 'checkbox') {
1721
+ if (isContainer && !formData[index][col.id]) {
1722
+ formData[index][col.id] = false;
1723
+ } else if (!isContainer && formData[col.id] === undefined) {
1724
+ formData[col.id] = false;
1725
+ }
1726
+ }
1727
+ });
1728
+ });
1729
+ return formData;
1730
+ }
1731
+
1732
+ initializeColLabelSection(section, formDataObj, isContainer, index, ifError) {
1733
+ const formData = formDataObj;
1734
+ _.forEach(section.inputFields, (inputField) => {
1735
+ if (isContainer && !formData[index]) {
1736
+ formData[index] = {};
1737
+ }
1738
+ if (!ifError && (inputField.id.indexOf('template-label-') === -1 && inputField.id.indexOf('tagList-') === -1)) {
1739
+ this.formElements.push(inputField.id);
1740
+ }
1741
+ if (!inputField.onlyDisplay) {
1742
+ if (isContainer && !this.props.isNewVersionFlow && formData[index][inputField.id] === undefined) {
1743
+ if (ifError) {
1744
+ formData[index][inputField.id] = false;
1745
+ } else {
1746
+ formData[index][inputField.id] = '';
1747
+ }
1748
+ } else if (isContainer && this.props.isNewVersionFlow && formData[index][this.props.baseLanguage][inputField.id] === undefined) {
1749
+ if (ifError) {
1750
+ formData[index][this.props.baseLanguage][inputField.id] = false;
1751
+ } else {
1752
+ formData[index][this.props.baseLanguage][inputField.id] = '';
1753
+ }
1754
+ } else if (!isContainer && ifError && this.state.formData[inputField.id] === undefined) {
1755
+ formData[inputField.id] = false;
1756
+ } else if (!isContainer && !ifError && this.state.formData[inputField.id] === undefined) {
1757
+ formData[inputField.id] = '';
1758
+ }
1759
+ }
1760
+ if (inputField.type === 'checkbox') {
1761
+ if (isContainer) {
1762
+ formData[index][inputField.id] = false;
1763
+ } else {
1764
+ formData[inputField.id] = false;
1765
+ }
1766
+ }
1767
+ });
1768
+ return formData;
1769
+ }
1770
+
1771
+ initializeStandAloneSections(schema, formDataObj, ifError) {
1772
+ let formData = _.cloneDeep(formDataObj);
1773
+ const channel = _.get(this, "props.schema.channel");
1774
+ let isContainer = false;
1775
+ let index = -1;
1776
+ if (channel && channel.toUpperCase() === SMS) {
1777
+ // sms v2 scema does not have any containers section the input ant preview fields are in stand alon section and v1 FormBuilder did not have valiation support for stand alone input fields
1778
+ isContainer = true;
1779
+ index = 0;
1780
+ }
1781
+ if (schema.standalone) {
1782
+ _.forEach(schema.standalone.sections, (section) => {
1783
+ if (section) {
1784
+ if (section.type === 'multicols') {
1785
+ formData = this.initializeMultiColSection(section, formData, isContainer, index, ifError);
1786
+ } else if (section.type === 'col-label') {
1787
+ formData = this.initializeColLabelSection(section, formData, isContainer, index, ifError);
1788
+ } else if (section.type === 'parent') {
1789
+ formData = this.initializeParentSection(section, formData, isContainer, index, ifError);
1790
+ }
1791
+ }
1792
+ });
1793
+ }
1794
+ return formData;
1795
+ }
1796
+
1797
+ initializeContainers(schema, formDataObj, ifError) {
1798
+ let formData = _.cloneDeep(formDataObj);
1799
+ if (this.props.isNewVersionFlow) {
1800
+ _.forEach(schema.containers, (val, index) => {
1801
+ if (val.type === 'tabs') {
1802
+ this.setState({usingTabContainer: true});
1803
+ _.forEach(val.panes, (pane, paneIndex) => {
1804
+ _.forEach(pane.sections, (section) => {
1805
+ if (section.type === 'multicols') {
1806
+ formData = this.initializeMultiColSection(section, formData, true, index, ifError, paneIndex);
1807
+ } else if (section.type === 'col-label') {
1808
+ formData = this.initializeColLabelSection(section, formData, true, index, ifError);
1809
+ } else if (section.type === 'parent') {
1810
+ formData = this.initializeParentSection(section, formData, true, index, ifError);
1811
+ }
1812
+ });
1813
+ });
1814
+ if (ifError) {
1815
+ formData[val.id] = false;
1816
+ }
1817
+ formData[val.id] = '';
1818
+ }
1819
+ if (val.type === 'checkbox') {
1820
+ formData[val.id] = false;
1821
+ }
1822
+ });
1823
+ } else {
1824
+ _.forEach(schema.containers, (val) => {
1825
+ if (val.type === 'tabs') {
1826
+ this.setState({usingTabContainer: true});
1827
+ _.forEach(val.panes, (pane, index) => {
1828
+ _.forEach(pane.sections, (section) => {
1829
+ if (section.type === 'multicols') {
1830
+ formData = this.initializeMultiColSection(section, formData, true, index, ifError);
1831
+ } else if (section.type === 'col-label') {
1832
+ formData = this.initializeColLabelSection(section, formData, true, index, ifError);
1833
+ } else if (section.type === 'parent') {
1834
+ formData = this.initializeParentSection(section, formData, true, index, ifError);
1835
+ }
1836
+ });
1837
+ });
1838
+ if (ifError) {
1839
+ formData[val.id] = false;
1840
+ }
1841
+ formData[val.id] = '';
1842
+ }
1843
+ if (val.type === 'checkbox') {
1844
+ formData[val.id] = false;
1845
+ }
1846
+ });
1847
+ }
1848
+ return formData;
1849
+ }
1850
+
1851
+ initialiseForm(schema, makeFormEmpty, resetTabKeys) {
1852
+ let formData = makeFormEmpty ? {} : _.cloneDeep(this.state.formData);
1853
+ let errorData = {};
1854
+ this.formElements = [];
1855
+ if (_.isEmpty(schema)) {
1856
+ return;
1857
+ }
1858
+ errorData = this.initializeStandAloneSections(schema, errorData, true);
1859
+ formData = this.initializeStandAloneSections(schema, formData);
1860
+ formData = this.initializeContainers(schema, formData);
1861
+ errorData = this.initializeContainers(schema, errorData, true);
1862
+
1863
+
1864
+ if (!this.props.isEdit) {
1865
+ if (!formData[0]) {
1866
+ formData[0] = {};
1867
+ }
1868
+ if (!errorData[0]) {
1869
+ errorData[0] = {};
1870
+ }
1871
+ formData[0].base = true;
1872
+ formData[0].tabKey = formData[0].tabKey ? formData[0].tabKey : _.uniqueId();
1873
+ errorData.base = errorData[0];
1874
+ formData.base = formData[0];
1875
+ if (this.state.currentTab === 1) {
1876
+ this.setState({tabKey: formData.base.tabKey});
1877
+ }
1878
+ }
1879
+ const tempFormData = _.cloneDeep(formData);
1880
+ // Store the value of 'template-name' before the deletion process
1881
+ const templateNameValue = tempFormData['template-name'];
1882
+ if (!this.props.isNewVersionFlow) {
1883
+ _.forEach(formData, (element, key) => {
1884
+ // This code block is used to clean up the tempFormData object.
1885
+ // If the element is not an object and the key of the element is not found in the formElements array,
1886
+ // then the element is removed from the tempFormData object.
1887
+ if (typeof element !== 'object' && this.formElements.indexOf(key) === -1) {
1888
+ delete tempFormData[key];
1889
+ } else if (typeof element === "object") {
1890
+ // If the property key (innerKey) is not "tabKey", "base", "name", "imagePreview", "image"
1891
+ // and is not found in the formElements array, then the property is removed from the object in tempFormData.
1892
+ _.forEach(element, (innerElement, innerKey) => {
1893
+ if (innerKey !== "tabKey" && innerKey !== "base" && innerKey !== 'name' && innerKey !== 'imagePreview' && innerKey !== 'image' && this.formElements.indexOf(innerKey) === -1) {
1894
+ delete tempFormData[key][innerKey];
1895
+ }
1896
+ });
1897
+ }
1898
+ });
1899
+ }
1900
+ const channel = this.props.schema?.channel || "";
1901
+ // If 'template-name' was deleted, re-add it to tempFormData for mobilepush channel
1902
+ if (!('template-name' in tempFormData) && channel.toUpperCase() === MOBILE_PUSH) {
1903
+ tempFormData['template-name'] = templateNameValue;
1904
+ }
1905
+ formData = _.cloneDeep(tempFormData);
1906
+ this.setState({formData, errorData}, () => {
1907
+ this.validateForm();
1908
+ if (this.props.isSchemaChanged || resetTabKeys) {
1909
+ this.resetTabKeys(formData, this.props.tabCount, true);
1910
+ }
1911
+ });
1912
+ }
1913
+
1914
+ cloneVersionData(ifError) {
1915
+ if (this.state.tabCount === 0) {
1916
+ this.initialiseForm(this.props.schema, true);
1917
+ return;
1918
+ }
1919
+ let formData = {};
1920
+ if (ifError) {
1921
+ formData = Object.assign({}, this.state.errorData);
1922
+ } else {
1923
+ formData = Object.assign({}, this.state.formData);
1924
+ }
1925
+ let newTabIndex = 0;
1926
+ if (ifError) {
1927
+ newTabIndex = this.state.tabCount - 1;
1928
+ } else {
1929
+ newTabIndex = this.state.tabCount;
1930
+ }
1931
+ formData[newTabIndex] = {};
1932
+
1933
+ const newTabCount = newTabIndex + 1;
1934
+ _.forEach(formData[0], (val, key) => {
1935
+ if (key === 'base') {
1936
+ return true;
1937
+ }
1938
+ if (key === 'tabKey') {
1939
+ return true;
1940
+ }
1941
+ if (typeof val === 'string') {
1942
+ if (ifError) {
1943
+ formData[newTabIndex][`${key}${newTabCount}`] = false;
1944
+ } else if (this.props.isNewVersionFlow) {
1945
+ if (key.indexOf('activeTab') > -1) {
1946
+ formData[newTabIndex][`${key}`] = this.props.baseLanguage;
1947
+ } else {
1948
+ formData[newTabIndex][`${key}${newTabCount}`] = '';
1949
+ }
1950
+ } else {
1951
+ formData[newTabIndex][`${key}${newTabCount}`] = '';
1952
+ }
1953
+ } else if (typeof val === 'boolean') {
1954
+ formData[newTabIndex][`${key}${newTabCount}`] = false;
1955
+ } else if (typeof val === 'object') {
1956
+ if (this.props.isNewVersionFlow) {
1957
+ if (key.indexOf('selectedLanguages') > -1) {
1958
+ formData[newTabIndex][key] = [this.props.baseLanguage];
1959
+ } else if (key === this.props.baseLanguage) {
1960
+ formData[newTabIndex][key] = {};
1961
+ _.forEach(val, (subVal, index) => {
1962
+ if (index === 'tabKey') {
1963
+ formData[newTabIndex][key][index] = _.uniqueId();
1964
+ } else if (index === 'template-content') {
1965
+ formData[newTabIndex][key][index] = "";
1966
+ } else {
1967
+ formData[newTabIndex][key][index] = _.cloneDeep(formData[0][key][index]);
1968
+ }
1969
+ // }
1970
+ });
1971
+ }
1972
+ } else {
1973
+ formData[newTabIndex][key] = {};
1974
+ _.forEach(val, (subVal, index) => {
1975
+ formData[newTabIndex][key][index] = '';
1976
+ });
1977
+ }
1978
+ }
1979
+ return true;
1980
+ });
1981
+ let id = (this.props.isNewVersionFlow && formData[newTabIndex][this.props.baseLanguage].tabKey) ? formData[newTabIndex][this.props.baseLanguage].tabKey : _.uniqueId();
1982
+
1983
+ if (!ifError) {
1984
+ let validId = false;
1985
+ while (!validId) {
1986
+ validId = true;
1987
+ for (let idx = 0; idx < this.state.tabCount; idx += 1) {
1988
+ if (id === formData[idx].tabKey) {
1989
+ validId = false;
1990
+ }
1991
+ }
1992
+ if (validId) {
1993
+ break;
1994
+ }
1995
+ id = _.uniqueId();
1996
+ }
1997
+ formData[newTabIndex].tabKey = id;
1998
+ }
1999
+ // if (!this.props.isEdit) {
2000
+ if (ifError) {
2001
+ this.setState({errorData: formData});
2002
+ } else {
2003
+ this.props.onChange(formData, newTabCount, newTabIndex + 1);
2004
+
2005
+ this.setState({tabCount: newTabCount, formData, currentTab: newTabIndex + 1, tabKey: id}, () => {
2006
+ this.cloneVersionData(true);
2007
+ });
2008
+ }
2009
+ }
2010
+
2011
+ changeVersionName(data) {
2012
+ const formData = _.cloneDeep(this.state.formData);
2013
+ if (formData[this.state.currentTab - 1]) {
2014
+ formData[this.state.currentTab - 1].name = data;
2015
+ this.setState({formData});
2016
+ }
2017
+ }
2018
+
2019
+ markFinalTabVersion() {
2020
+ const formData = _.cloneDeep(this.state.formData);
2021
+ if (this.state.usingTabContainer) {
2022
+ for (let count = 0; count < this.state.tabCount; count += 1) {
2023
+ if (parseInt(this.state.currentTab - 1, 10) !== count) {
2024
+ formData[count].base = false;
2025
+ } else {
2026
+ formData[count].base = true;
2027
+ formData.base = formData[count];
2028
+ }
2029
+ }
2030
+ this.setState({formData});
2031
+
2032
+ this.props.onChange(formData, this.state.tabCount);
2033
+ }
2034
+ }
2035
+
2036
+ resetState(isEdit, usingTabContainer) {
2037
+ this.setState({
2038
+ formData: {},
2039
+ currentTab: 1,
2040
+ usingTabContainer: usingTabContainer || false,
2041
+ tabCount: 1,
2042
+ errorData: {},
2043
+ tabKey: '',
2044
+ }, () => {
2045
+ if (!isEdit) {
2046
+ this.initialiseForm(this.props.schema, true);
2047
+ }
2048
+ });
2049
+ }
2050
+
2051
+ duplicateVersion(ifError, currentTab) {
2052
+ let formData = {};
2053
+ let tabCount = 0;
2054
+ if (ifError) {
2055
+ formData = _.cloneDeep(this.state.errorData);
2056
+ tabCount = this.state.tabCount - 1;
2057
+ } else {
2058
+ formData = _.cloneDeep(this.state.formData);
2059
+ tabCount = this.state.tabCount;
2060
+ }
2061
+ const newTabCount = tabCount + 1;
2062
+ _.forEach(formData[0], (val, key) => {
2063
+ if (!formData[tabCount]) {
2064
+ formData[tabCount] = {};
2065
+ }
2066
+ if (key === 'base') {
2067
+ formData[tabCount].base = false;
2068
+ return true;
2069
+ }
2070
+ if (key === 'tabKey') {
2071
+ let validId = false;
2072
+ let id = _.uniqueId();
2073
+ while (!validId) {
2074
+ validId = true;
2075
+ for (let idx = 0; idx < this.state.tabCount; idx += 1) {
2076
+ if (id === formData[idx].tabKey) {
2077
+ validId = false;
2078
+ }
2079
+ }
2080
+ if (validId) {
2081
+ break;
2082
+ }
2083
+ id = _.uniqueId();
2084
+ }
2085
+ formData[tabCount].tabKey = id;
2086
+ return true;
2087
+ }
2088
+ const tempTab = ifError ? currentTab : this.state.currentTab;
2089
+ if (typeof val === 'string') {
2090
+ if (this.props.isNewVersionFlow) {
2091
+ formData[tabCount][`${key}`] = formData[tempTab - 1][`${key}`];
2092
+ } else {
2093
+ formData[tabCount][`${key}${newTabCount}`] = formData[tempTab - 1][`${key}${parseInt(tempTab, 10) === 1 ? '' : tempTab}`];
2094
+ }
2095
+ } else if (typeof val === 'boolean') {
2096
+ if (this.props.isNewVersionFlow) {
2097
+ formData[tabCount][`${key}`] = formData[tempTab - 1][`${key}`];
2098
+ } else {
2099
+ formData[tabCount][`${key}${newTabCount}`] = formData[tempTab - 1][`${key}${parseInt(tempTab, 10) === 1 ? '' : tempTab}`];
2100
+ }
2101
+ }
2102
+ return true;
2103
+ });
2104
+ if (ifError) {
2105
+ this.setState({errorData: formData});
2106
+ } else {
2107
+ const tempTab = ifError ? currentTab : this.state.currentTab;
2108
+ const version = `Version ${this.state.currentTab}`;
2109
+ formData[tabCount].name = `${COPY_OF} ${formData[tempTab - 1].name ? formData[tempTab - 1].name : version}`;
2110
+ formData[tabCount].base = false;
2111
+ const initialTab = this.state.currentTab;
2112
+ this.setState({formData, tabCount: tabCount + 1, currentTab: tabCount + 1, tabKey: formData[tabCount].tabKey}, () => {
2113
+ this.duplicateVersion(true, initialTab);
2114
+ });
2115
+
2116
+ this.props.onChange(formData, tabCount + 1, tabCount + 1);
2117
+ }
2118
+ }
2119
+
2120
+ deleteVersion(ifError, currentTab) {
2121
+ if (!ifError && this.state.tabCount === 1) {
2122
+ return;
2123
+ }
2124
+ let formData = {};
2125
+ let finalCurrentTab = 0;
2126
+ let tabCount = 0;
2127
+ if (ifError) {
2128
+ formData = _.cloneDeep(this.state.errorData);
2129
+ tabCount = this.state.tabCount + 1;
2130
+ finalCurrentTab = currentTab;
2131
+ } else {
2132
+ formData = _.cloneDeep(this.state.formData);
2133
+ tabCount = this.state.tabCount;
2134
+ finalCurrentTab = this.state.currentTab;
2135
+ }
2136
+ // delete formData[this.state.currentTab - 1];
2137
+ let finalActiveTabKey = '';
2138
+ if (tabCount > 1 && currentTab !== tabCount) {
2139
+ finalActiveTabKey = formData[currentTab].tabKey;
2140
+ }
2141
+
2142
+ if (tabCount > 1 && currentTab === tabCount) {
2143
+ finalActiveTabKey = formData[currentTab - 2].tabKey;
2144
+ finalCurrentTab = currentTab - 1;
2145
+ }
2146
+ // if (this.state.tabCount > 1 && this.state.currentTab === 1) {
2147
+ // finalActiveTabKey = this.state.formData[1].tabKey;
2148
+ // }
2149
+ if (tabCount === 1) {
2150
+ finalActiveTabKey = '';
2151
+ }
2152
+ const initialFormData = _.cloneDeep(formData[0]);
2153
+ for (let count = currentTab - 1; count < tabCount - 1; count += 1) {
2154
+ delete formData[count];
2155
+ _.forEach(initialFormData, (val, key) => {
2156
+ if (!formData[count]) {
2157
+ formData[count] = {};
2158
+ }
2159
+ if (key === 'base') {
2160
+ formData[count].base = formData[count + 1].base ? formData[count + 1].base : false;
2161
+ return true;
2162
+ }
2163
+ if (key === 'tabKey') {
2164
+ formData[count].tabKey = formData[count + 1].tabKey;
2165
+ return true;
2166
+ }
2167
+ formData[count][`${key}${count === 0 ? '' : count + 1}`] = formData[count + 1][`${key}${count + 2}`];
2168
+ return true;
2169
+ });
2170
+ }
2171
+ delete formData[tabCount - 1];
2172
+ if (ifError) {
2173
+ this.setState({errorData: formData});
2174
+ } else {
2175
+ const initialTab = currentTab;
2176
+ if (this.props.isNewVersionFlow) {
2177
+ formData['template-version-options'].splice(`${currentTab - 1}`, 1);
2178
+ formData['template-version'] = formData['template-version-options'][0].value;
2179
+ }
2180
+ this.setState({formData, tabCount: tabCount - 1, tabKey: finalActiveTabKey, currentTab: finalCurrentTab}, () => {
2181
+ this.deleteVersion(true, initialTab);
2182
+ });
2183
+
2184
+ this.props.onChange(formData, tabCount - 1);
2185
+ }
2186
+ }
2187
+
2188
+ deleteLanguage = () => {
2189
+ // if (this.state.formData[`${this.state.currentTab - 1}`].activeTab === this.props.baseLanguage) {
2190
+
2191
+ // } else {
2192
+ if (this.state.formData[`${this.state.currentTab - 1}`].activeTab !== this.props.baseLanguage) {
2193
+ const formData = _.cloneDeep(this.state.formData);
2194
+ const currentTab = `${this.state.currentTab - 1}`;
2195
+ const currentLang = formData[`${this.state.currentTab - 1}`].activeTab;
2196
+ const deleteLanguageIndex = formData[currentTab].selectedLanguages.indexOf(currentLang);
2197
+ const baseLangTabKey = formData[currentTab][this.props.baseLanguage].tabKey;
2198
+
2199
+ delete formData[currentTab][currentLang];
2200
+ formData[currentTab].activeTab = this.props.baseLanguage;
2201
+ formData[currentTab].selectedLanguages.splice(deleteLanguageIndex, 1);
2202
+ formData[currentTab].tabKey = baseLangTabKey;
2203
+ if (formData[currentTab].base) {
2204
+ delete formData.base[currentLang];
2205
+ delete formData.base.selectedLanguages.splice(deleteLanguageIndex, 1);
2206
+ formData.base.tabKey = baseLangTabKey;
2207
+ formData.base.activeTab = this.props.baseLanguage;
2208
+ }
2209
+
2210
+ this.setState({formData, currentLangTab: this.props.baseLanguage, tabKey: baseLangTabKey}, () => {
2211
+ this.state.currentEvent.call(this.props.parent, formData, deleteLanguageIndex);
2212
+ this.setState({currentEvent: {}, currentEventData: {}});
2213
+ });
2214
+ }
2215
+ };
2216
+
2217
+ renameVersion(currentTab) {
2218
+ document.getElementById(`tab-header${currentTab > 1 ? currentTab : ''}`).contentEditable = "true";
2219
+ document.getElementById(`tab-header${currentTab > 1 ? currentTab : ''}`).focus();
2220
+ }
2221
+
2222
+ updateFormData(data, val, event) {
2223
+
2224
+ // Check if this is a high-frequency input field that should be optimized
2225
+ const isHighFrequencyField = val && (
2226
+ val.id === 'template-name' ||
2227
+ val.id === 'template-subject'
2228
+ );
2229
+
2230
+ if (isHighFrequencyField && !event) {
2231
+ // For high-frequency fields: immediate UI update + debounced expensive operations
2232
+ this.updateFormDataOptimized(data, val, event);
2233
+ return;
2234
+ }
2235
+
2236
+ // For non-high-frequency fields or special events, use immediate update
2237
+ this.performFormDataUpdate(data, val, event);
2238
+ }
2239
+
2240
+ // Optimized update for high-frequency fields
2241
+ updateFormDataOptimized(data, val, event) {
2242
+ // 1. Immediate UI update - update the field value instantly
2243
+ this.updateFieldValueImmediately(data, val);
2244
+
2245
+ // 2. Debounce expensive operations (validation, parent updates) - skip state update since we already did it
2246
+ this.debouncedUpdateFormData(data, val, event, true);
2247
+ }
2248
+
2249
+ // Update field value immediately for UI feedback
2250
+ updateFieldValueImmediately(data, val) {
2251
+ const currentFormData = this.state.formData;
2252
+ let updatedFormData;
2253
+
2254
+ if (!this.state.usingTabContainer || val.standalone) {
2255
+ // Simple field update
2256
+ updatedFormData = {
2257
+ ...currentFormData,
2258
+ [val.id]: data
2259
+ };
2260
+ } else {
2261
+ // Tab container update
2262
+ const tabIndex = this.state.currentTab - 1;
2263
+ updatedFormData = { ...currentFormData };
2264
+
2265
+ if (updatedFormData[tabIndex]) {
2266
+ updatedFormData[tabIndex] = {
2267
+ ...updatedFormData[tabIndex],
2268
+ [val.id]: data
2269
+ };
2270
+ }
2271
+ }
2272
+
2273
+ // Update state immediately for UI feedback
2274
+ this.setState({ formData: updatedFormData });
2275
+ }
2276
+
2277
+ // Legacy updateFormData function - kept for backward compatibility
2278
+ updateFormDataLegacy(data, val, event) {
2279
+ const formData = _.cloneDeep(this.state.formData);
2280
+
2281
+ const tabIndex = this.state.currentTab - 1;
2282
+ let currentTab = _.cloneDeep(this.state.currentTab);
2283
+
2284
+ if (this.state.usingTabContainer && !val.standalone) {
2285
+ const data1 = data;
2286
+ if (event === ADD_LANGUAGE) {
2287
+ const addLanguageType = this.props.addLanguageType;
2288
+ if (addLanguageType === '') {
2289
+ return;
2290
+ }
2291
+ const currentLang = formData[tabIndex].activeTab;
2292
+ let baseTab = _.cloneDeep(formData[tabIndex][currentLang]);
2293
+ let id = _.uniqueId();
2294
+ let validId = false;
2295
+ switch (addLanguageType) {
2296
+ case "upload":
2297
+ baseTab = _.cloneDeep(formData[tabIndex][currentLang]);
2298
+
2299
+ baseTab.iso_code = data.iso_code;
2300
+ baseTab.lang_id = data.lang_id;
2301
+ baseTab.language = data.language;
2302
+ while (!validId) {
2303
+ validId = true;
2304
+ for (let idx = 0; idx < formData[tabIndex].selectedLanguages.length; idx += 1) {
2305
+ if (!formData[tabIndex]) {
2306
+ continue;
2307
+ }
2308
+
2309
+ if (id === formData[tabIndex][formData[tabIndex].selectedLanguages[idx]].tabKey) {
2310
+ validId = false;
2311
+ }
2312
+ }
2313
+ if (validId) {
2314
+ break;
2315
+ }
2316
+ id = _.uniqueId();
2317
+ }
2318
+ baseTab.tabKey = id;
2319
+ //formData[tabIndex].selectedLanguages.push(data.iso_code);
2320
+ formData[tabIndex][data.iso_code] = baseTab;
2321
+
2322
+ formData[tabIndex].activeTab = data.iso_code;
2323
+ formData[tabIndex].tabKey = baseTab.tabKey;
2324
+ break;
2325
+ case "copyPrimaryLanguage":
2326
+ case "useEditor":
2327
+ baseTab = _.cloneDeep(formData[tabIndex][this.props.baseLanguage]);
2328
+
2329
+ //baseTab['template-content'] = '';
2330
+ baseTab.iso_code = data.iso_code;
2331
+ baseTab.lang_id = data.lang_id;
2332
+ baseTab.language = data.language;
2333
+ validId = false;
2334
+ while (!validId) {
2335
+ validId = true;
2336
+ for (let idx = 0; idx < formData[tabIndex].selectedLanguages.length; idx += 1) {
2337
+ if (!formData[idx]) {
2338
+ continue;
2339
+ }
2340
+ if (id === formData[tabIndex][formData[tabIndex].selectedLanguages[idx]].tabKey) {
2341
+ validId = false;
2342
+ }
2343
+ }
2344
+ if (validId) {
2345
+ break;
2346
+ }
2347
+ id = _.uniqueId();
2348
+ }
2349
+ baseTab.tabKey = id;
2350
+ formData[tabIndex].selectedLanguages.push(data.iso_code);
2351
+ formData[tabIndex][data.iso_code] = baseTab;
2352
+
2353
+ formData[tabIndex].activeTab = data.iso_code;
2354
+ formData[tabIndex].tabKey = baseTab.tabKey;
2355
+ break;
2356
+ case '':
2357
+ return;
2358
+ default:
2359
+ break;
2360
+ }
2361
+ const that = this;
2362
+ setTimeout(() => {
2363
+ that.setState({tabKey: baseTab.tabKey});
2364
+ }, 0);
2365
+ }
2366
+
2367
+ if (!this.props.isNewVersionFlow) {
2368
+ formData[tabIndex][val.id] = data1;
2369
+ } else if (this.props.isNewVersionFlow && event !== ADD_LANGUAGE && event !== "onContentChange") {
2370
+ formData[tabIndex][this.props.baseLanguage][val.id] = data1;
2371
+ }
2372
+
2373
+ if (formData[tabIndex].base) {
2374
+ if (!this.props.isNewVersionFlow) {
2375
+ formData.base[val.id] = data1;
2376
+ } else {
2377
+ formData.base[data1.iso_code] = formData[tabIndex][data1.iso_code];
2378
+ formData.base.tabKey = formData[tabIndex].tabKey;
2379
+ formData.base.activeTab = formData[tabIndex].activeTab;
2380
+ formData.base.selectedLanguages = formData[tabIndex].selectedLanguages;
2381
+ }
2382
+ }
2383
+ } else {
2384
+ formData[val.id] = data;
2385
+ }
2386
+
2387
+ if (this.props.isNewVersionFlow) {
2388
+ if (event === 'onSelect' && data === 'New Version') {
2389
+ this.callChildEvent(data, val, 'addVersion', event);
2390
+ } else if (event === 'onSelect' && data !== 'New Version') {
2391
+ currentTab = _.findIndex(this.state.formData['template-version-options'], { key: data}) + 1;
2392
+ this.setState({currentTab, tabKey: formData[`${currentTab - 1}`].tabKey}, () => {
2393
+ val.injectedEvents[event].call(this, this.state.formData['template-version-options'][currentTab - 1].key, formData, val);
2394
+ });
2395
+ }
2396
+
2397
+ if (event === 'onContentChange') {
2398
+
2399
+ // formData[`${this.state.currentTab - 1}`][formData[`${this.state.currentTab - 1}`].activeTab]['template-content'] = data.editor.getData();
2400
+ // if (formData[`${this.state.currentTab - 1}`].tabKey === formData.base.tabKey) {
2401
+ // formData.base[formData[`${this.state.currentTab - 1}`].activeTab][`template-content`] = data.editor.getData();
2402
+ //}
2403
+ }
2404
+ }
2405
+
2406
+
2407
+ this.setState({formData}, () => {
2408
+ if (this.props.startValidation) {
2409
+ this.validateForm();
2410
+ }
2411
+ });
2412
+ if (event && val.injectedEvents[event]) {
2413
+ if (event === "onRowClick") {
2414
+ val.injectedEvents[event].call(this, data);
2415
+ } else if (this.props.isNewVersionFlow && event !== 'onSelect') {
2416
+ if (event === ADD_LANGUAGE) {
2417
+ val.injectedEvents[event].call(this, data, formData, val);
2418
+ this.setState({currentEventVal: {}, currentEvent: {}, currentEventData: {}});
2419
+ } else {
2420
+ val.injectedEvents[event].call(this, true, formData, val);
2421
+ }
2422
+ } else if (!this.props.isNewVersionFlow) {
2423
+ val.injectedEvents[event].call(this, true, formData, val);
2424
+ }
2425
+ } else if (val.injectedEvents && val.injectedEvents.onChange) {
2426
+ val.injectedEvents.onChange.call(this, true, formData, val);
2427
+ }
2428
+
2429
+ if (!((event === 'onSelect' && data === 'New Version') || event === 'onContentChange')) {
2430
+ this.props.onChange(formData, this.state.tabCount, currentTab, val);
2431
+ }
2432
+ }
2433
+
2434
+
2435
+ hasClass(element, className) {
2436
+ return (` ${element.className} `).indexOf(` ${className} `) > -1;
2437
+ }
2438
+
2439
+ // Handle field blur for validation
2440
+ handleFieldBlur = (e, val) => {
2441
+ // Trigger validation on blur for high-frequency fields
2442
+ if (val && (val.id === 'template-name' || val.id === 'template-subject')) {
2443
+ this.debouncedValidation();
2444
+ }
2445
+ }
2446
+
2447
+ // Memoized validation for specific fields
2448
+ getMemoizedValidation = (fieldId, fieldValue) => {
2449
+ const cacheKey = `${fieldId}_${fieldValue}`;
2450
+
2451
+ if (this.validationCache.has(cacheKey)) {
2452
+ return this.validationCache.get(cacheKey);
2453
+ }
2454
+
2455
+ // Perform validation logic here
2456
+ const isValid = this.performFieldValidation(fieldId, fieldValue);
2457
+ this.validationCache.set(cacheKey, isValid);
2458
+
2459
+ // Clear cache if it gets too large
2460
+ if (this.validationCache.size > 100) {
2461
+ this.validationCache.clear();
2462
+ }
2463
+
2464
+ return isValid;
2465
+ }
2466
+
2467
+ // Perform validation for a specific field
2468
+ performFieldValidation = (fieldId, fieldValue) => {
2469
+ // Basic validation logic for template-name and template-subject
2470
+ if (fieldId === 'template-name' || fieldId === 'template-subject') {
2471
+ return fieldValue && fieldValue.trim().length > 0;
2472
+ }
2473
+ return true;
2474
+ }
2475
+
2476
+ // Cleanup debounced functions on component unmount
2477
+ componentWillUnmount() {
2478
+ if (this.debouncedUpdateFormData) {
2479
+ this.debouncedUpdateFormData.cancel();
2480
+ }
2481
+ if (this.debouncedValidation) {
2482
+ this.debouncedValidation.cancel();
2483
+ }
2484
+ // Clear validation cache
2485
+ if (this.validationCache) {
2486
+ this.validationCache.clear();
2487
+ }
2488
+ }
2489
+ allowAddSecondaryCta = (val) => {
2490
+ if (val.fieldsCount > 0) {
2491
+ const errorData = _.cloneDeep(this.state.errorData);
2492
+ _.forEach(Object.keys(errorData), (key) => {
2493
+ if (typeof errorData[key] !== "object") {
2494
+ errorData[key] = false;
2495
+ } else {
2496
+ _.forEach(Object.keys(errorData[key]), (innerKey) => {
2497
+ errorData[key][innerKey] = false;
2498
+ });
2499
+ }
2500
+ });
2501
+ if (this.state.formData[this.state.currentTab - 1][`secondary-cta-${val.fieldsCount - 1}-label`] === "") {
2502
+ errorData[this.state.currentTab - 1][`secondary-cta-${val.fieldsCount - 1}-label`] = true;
2503
+ this.setState({errorData, checkValidation: true, isFormValid: false});
2504
+ return false;
2505
+ }
2506
+ if (this.state.formData[this.state.currentTab - 1][`cta-deeplink-secondary-cta-${val.fieldsCount - 1}`] === "") {
2507
+ errorData[this.state.currentTab - 1][`cta-deeplink-secondary-cta-${val.fieldsCount - 1}`] = true;
2508
+ this.setState({errorData, checkValidation: true, isFormValid: false});
2509
+ return false;
2510
+ }
2511
+ if (this.state.formData[this.state.currentTab - 1][`cta-deeplink-secondary-cta-${val.fieldsCount - 1}-select`] === "") {
2512
+ errorData[this.state.currentTab - 1][`cta-deeplink-secondary-cta--${val.fieldsCount - 1}-select`] = true;
2513
+ this.setState({errorData, checkValidation: true, isFormValid: false});
2514
+ return false;
2515
+ }
2516
+ }
2517
+ return true;
2518
+ };
2519
+ callChildEvent(data, val, event) {
2520
+ if (!event) {
2521
+ return;
2522
+ }
2523
+
2524
+ const tab = this.state.tabCount;
2525
+ // const parent = this._reactInternalInstance._currentElement._owner._instance;
2526
+ if (event === 'onTabChange') {
2527
+ const that = this;
2528
+ this.setState({tabKey: `${data}`});
2529
+ setTimeout(() => {
2530
+ let activeTabIndex = '1';
2531
+ let selector = ".ant-tabs-tab";
2532
+ if (this.props.schema.channel.toUpperCase() === "MOBILEPUSH") {
2533
+ selector = `.mobilepush-wrapper ${selector}`;
2534
+ }
2535
+ const elems = document.querySelectorAll(selector);
2536
+
2537
+ for (let i = 0; i < elems.length; i += 1) {
2538
+ if (elems.item(i).classList.contains("ant-tabs-tab-active")) {
2539
+ activeTabIndex = i + 1;
2540
+ }
2541
+ }
2542
+ if (this.props.isNewVersionFlow) {
2543
+ const formData = _.cloneDeep(this.state.formData);
2544
+ formData[(this.state.currentTab - 1)].activeTab = formData[(this.state.currentTab - 1)].selectedLanguages[activeTabIndex - 1];
2545
+ formData[(this.state.currentTab - 1)].tabKey = `${data}`;
2546
+ if (formData[(this.state.currentTab - 1)].base) {
2547
+ formData.base.activeTab = formData[(this.state.currentTab - 1)].selectedLanguages[activeTabIndex - 1];
2548
+ formData.base.tabKey = `${data}`;
2549
+ }
2550
+
2551
+ that.setState({formData, currentLangTab: formData[(this.state.currentTab - 1)].activeTab, tabKey: formData[(this.state.currentTab - 1)][formData[(this.state.currentTab - 1)].activeTab].tabKey}, () => {
2552
+ val.injectedEvents[event].call(that.props.parent, this.state.currentTab, formData);
2553
+ });
2554
+ } else {
2555
+ that.setState({currentTab: activeTabIndex});
2556
+ val.injectedEvents[event].call(that.props.parent, activeTabIndex);
2557
+ }
2558
+ }, 0);
2559
+ return;
2560
+ } else if (event === 'addVersion') {
2561
+ this.cloneVersionData();
2562
+ this.setState({ popoverVisible: false });
2563
+ const that = this;
2564
+ setTimeout(() => {
2565
+ val.injectedEvents[event].call(that.props.parent, tab);
2566
+ }, 0);
2567
+ return;
2568
+ } else if (event === 'onTagSelect') {
2569
+ val.injectedEvents[event].call(this.props.parent, data, this.state.currentTab, val);
2570
+ return;
2571
+ } else if (event === 'markFinalVersion' || event === 'duplicateVersion' || event === 'deleteVersion' || event === 'renameVersion') {
2572
+ this.setState({ popoverVisible: false });
2573
+ if ((event !== 'deleteVersion' && !this.props.isNewVersionFlow) || ((event === 'deleteVersion') && this.props.schema.channel && this.props.schema.channel.toUpperCase() === 'EMAIL')) {
2574
+ val.injectedEvents[event].call(this.props.parent, data, this.state.currentTab);
2575
+ } else {
2576
+ this.setState({currentEvent: val.injectedEvents[event], currentEventData: data});
2577
+ }
2578
+ if (event === 'markFinalVersion') {
2579
+ this.markFinalTabVersion();
2580
+ if (this.props.isNewVersionFlow) {
2581
+ val.injectedEvents[event].call(this.props.parent, data, this.state.currentTab);
2582
+ }
2583
+ } else if (event === 'duplicateVersion') {
2584
+ if (this.props.schema.channel.toUpperCase() === 'EMAIL') {
2585
+ const formData = _.cloneDeep(this.state.formData);
2586
+ formData[this.state.tabCount] = _.cloneDeep(formData[`${this.state.currentTab - 1}`]);
2587
+ _.forEach(formData[this.state.tabCount].selectedLanguages, (lang) => {
2588
+ const tmptabData = formData[this.state.tabCount][lang];
2589
+ _.forEach(formData[this.state.tabCount][lang], (tabData, index) => {
2590
+ if (index === 'tabKey') {
2591
+ tmptabData.tabKey = _.uniqueId();
2592
+ }
2593
+ });
2594
+ });
2595
+ formData[this.state.tabCount].tabKey = formData[this.state.tabCount][formData[this.state.tabCount].activeTab].tabKey;
2596
+
2597
+ this.setState({formData, tabKey: formData[this.state.tabCount].tabKey}, () => {
2598
+ val.injectedEvents[event].call(this.props.parent, formData, this.state.currentTab);
2599
+ });
2600
+ } else {
2601
+ this.duplicateVersion();
2602
+ }
2603
+ } else if (event === 'deleteVersion') {
2604
+ if (this.props.schema.channel.toUpperCase() === 'EMAIL') {
2605
+ // this.deleteVersion(false, this.state.currentTab);
2606
+ this.setState({currentEvent: val.injectedEvents[event], currentEventData: data }, () => {
2607
+ this.props.setModalContent('delete-version');
2608
+ });
2609
+ } else {
2610
+ this.handleConfirmations();
2611
+ }
2612
+ } else if (event === 'renameVersion') {
2613
+ this.renameVersion(this.state.currentTab);
2614
+ }
2615
+ return;
2616
+ } else if (event === 'discardValues') {
2617
+ val.injectedEvents[event].call(this.props.parent, data);
2618
+ if (this.props.isEdit) {
2619
+ this.resetState(true, this.state.usingTabContainer);
2620
+ } else {
2621
+ this.resetState(false, this.state.usingTabContainer);
2622
+ }
2623
+ return;
2624
+ } else if (event === 'onChange') {
2625
+ if (val.id === 'tab-header') {
2626
+ val.injectedEvents[event].call(this.props.parent, data, this.state.currentTab);
2627
+ this.changeVersionName(data);
2628
+ }
2629
+ return;
2630
+ } else if (event === "addSecondaryCta") {
2631
+ if (!this.allowAddSecondaryCta(val)) {
2632
+ return;
2633
+ }
2634
+ } else if (event === "onContentChange") {
2635
+ val.injectedEvents[event].call(this.props.parent, data, val.id);
2636
+ return;
2637
+ } else if (event === 'deleteLanguage') {
2638
+ this.setState({currentEvent: val.injectedEvents[event], currentEventData: data }, () => {
2639
+ this.props.setModalContent('delete-language');
2640
+ });
2641
+ // const modalContent = {
2642
+ // title: "Alert",
2643
+ // body: "Do you really want to delete this language?",
2644
+ // type: 'confirm',
2645
+ // id: 'email-language-delete-modal',
2646
+ // };
2647
+ // debugger;
2648
+ // if (this.state.formData[`${this.state.currentTab - 1}`].activeTab === this.props.baseLanguage) {
2649
+ //
2650
+ // } else {
2651
+ // const formData = _.cloneDeep(this.state.formData);
2652
+ // const currentTab = `${this.state.currentTab - 1}`;
2653
+ // const currentLang = formData[`${this.state.currentTab - 1}`].activeTab;
2654
+ // const deleteLanguageIndex = formData[currentTab].selectedLanguages.indexOf(currentLang);
2655
+ //
2656
+ // delete formData[currentTab][currentLang];
2657
+ // formData[currentTab].activeTab = this.props.baseLanguage;
2658
+ // formData[currentTab].selectedLanguages.splice(deleteLanguageIndex, 1);
2659
+ //
2660
+ // this.setState({formData, currentLangTab: this.props.baseLanguage}, () => {
2661
+ // val.injectedEvents[event].call(this.props.parent, formData, deleteLanguageIndex);
2662
+ // });
2663
+ // }
2664
+ return;
2665
+ }
2666
+
2667
+ // val.injectedEvents[event].call(this.props.parent, data);
2668
+ val.injectedEvents[event].call(this.props.parent, data, val.id);
2669
+ }
2670
+ openFileDialog = (e, parentID) => {
2671
+
2672
+ document.querySelector(`#${parentID} #fileName`).click();
2673
+ };
2674
+ handleOk = (ev) => {
2675
+ // antd v6 wraps button text in a <span>; use currentTarget so the button id resolves.
2676
+ const id = (ev.currentTarget && ev.currentTarget.id) || ev.target.id;
2677
+ this.setState({showModal: false});
2678
+ if (id === "android" && this.state.androidValid) {
2679
+ this.onSubmitWrapper({singleTab: id});
2680
+ } else if (id === "ios" && this.state.iosValid) {
2681
+ this.onSubmitWrapper({singleTab: id});
2682
+ } else if (id === "sms-version-modal") {
2683
+ this.state.currentEvent.call(this.props.parent, this.state.currentEventData, this.state.currentTab);
2684
+ this.setState({currentEvent: {}, currentEventData: {}});
2685
+ this.deleteVersion(false, this.state.currentTab);
2686
+ } else if (id === 'template-back-confirm-modal') {
2687
+ const type = this.props.location.query.type;
2688
+ const response = {
2689
+ action: 'moveToTemplates',
2690
+ value: 'ok',
2691
+ };
2692
+ this.props.iframeParent.postMessage(JSON.stringify(response), '*');
2693
+ const isLanguageSupport = this.props.location.query.isLanguageSupport || false;
2694
+ const isEdmSupport = (this.props.location.query.isEdmSupport !== "false") || false;
2695
+ const module = this.props.location.query.module ? this.props.location.query.module : 'default';
2696
+ this.props.router.push({
2697
+ pathname: `/${(this.props.schema.channel || '').toLowerCase()}/`,
2698
+ query: type === 'embedded' ? {type: 'embedded', module, isLanguageSupport, isEdmSupport} : {module, isLanguageSupport, isEdmSupport},
2699
+ });
2700
+ } else if (id === "email-language-delete-modal") {
2701
+ this.deleteLanguage();
2702
+ this.props.handleCancelModal();
2703
+ // this.setState({currentEvent: {}, currentEventData: {}});
2704
+ } else if (id === 'email-version-delete-modal') {
2705
+ this.deleteVersion(false, this.state.currentTab);
2706
+ } else if ( id === 'template-delete-confirm-modal') {
2707
+ this.props.handleDelete(this.props.modal);
2708
+ }
2709
+ };
2710
+ handleCancel = (ev) => {
2711
+ // antd v6 wraps button text in a <span>; use currentTarget so the button id resolves.
2712
+ const id = (ev.currentTarget && ev.currentTarget.id) || ev.target.id;
2713
+ this.setState({showModal: false});
2714
+ if (id === 'template-back-confirm-modal') {
2715
+ const response = {
2716
+ action: 'moveToTemplate',
2717
+ value: 'cancel',
2718
+ };
2719
+ this.props.iframeParent.postMessage(JSON.stringify(response), '*');
2720
+ }
2721
+ if (this.props.handleCancelModal) {
2722
+ this.props.handleCancelModal();
2723
+ }
2724
+ };
2725
+ handleConfirmations = () => {
2726
+ this.setState({showModal: true});
2727
+ };
2728
+
2729
+ handleSetRadioValue = (formData, currentTab, val) => {
2730
+ if (formData[currentTab - 1] && formData[currentTab - 1][val.id] && formData[currentTab - 1][val.id] !== "") {
2731
+ return formData[currentTab - 1][val.id];
2732
+ } else if (formData[val.id]) {
2733
+ return formData[val.id];
2734
+ }
2735
+ return val.value;
2736
+ }
2737
+ uploadImages = (e, {files}, val) => {
2738
+ if (e) {
2739
+ e.preventDefault();
2740
+ }
2741
+ const _URL = window.URL || window.webkitURL;
2742
+ const file = files[0];
2743
+ if (val.supportedExtensions) {
2744
+ const allowedExtensions = /(\.bmp|\.jpeg|\.png|\.gif|\.avif|\.jpg)$/i;
2745
+ if (!allowedExtensions.exec(file.name)) {
2746
+ this.callChildEvent({file, type: 'wrong file'}, val, val.submitAction);
2747
+ }
2748
+ }
2749
+ const img = new Image();
2750
+ img.src = _URL.createObjectURL(file);
2751
+ img.onload = () => {
2752
+ const fileParams = {
2753
+ width: this.width || img.width,
2754
+ height: this.height || img.height,
2755
+ error: file && (file.size / (1e+6) > 5), // Checking if file exists and its size is greater than 5MB (5 * 10^6 bytes)
2756
+ };
2757
+ this.callChildEvent({file, type: 'image', fileParams}, val, val.submitAction);
2758
+ };
2759
+ if (e) {
2760
+ const event = e;
2761
+ event.target.value = null;
2762
+ }
2763
+ };
2764
+
2765
+ capUploaderCustomRequest = (uploadData, val) => {
2766
+ this.uploadImages(undefined, {files: [uploadData.file]}, val);
2767
+ }
2768
+
2769
+ findBaseTab = () => {
2770
+ const activeTabKey = this.state.formData && this.state.formData.base ? this.state.formData.base.tabKey : -1;
2771
+ const formData = _.cloneDeep(this.state.formData);
2772
+ let baseTab = 0;
2773
+ for (let i = 0; i < this.state.tabCount; i += 1) {
2774
+ if (formData[i] && formData[i].tabKey === activeTabKey) {
2775
+ baseTab = i;
2776
+ }
2777
+ }
2778
+
2779
+ return baseTab;
2780
+ };
2781
+
2782
+ handleOnHoverItem = (isHovering, id) => {
2783
+ if (isHovering) {
2784
+ this.setState({hoveredItem: id});
2785
+ } else {
2786
+ this.setState({hoveredItem: ''});
2787
+ }
2788
+ }
2789
+
2790
+ handleOnItemClick = (isClicked, id) => {
2791
+ this.setState({clickedItem: id});
2792
+ }
2793
+
2794
+ populateTemplatesList(data) {
2795
+ if (!data) {
2796
+ return [];
2797
+ }
2798
+ const items = [];
2799
+ data.map( (template) => {
2800
+ //
2801
+ const temp = {};
2802
+ temp.id = template._id;
2803
+ if ((this.props.schema.channel || '').toLowerCase() === 'email') {
2804
+ //
2805
+ const imgSrc = template && template.versions && template.versions.base ? template.versions.base.preview_http_url : '';
2806
+ // const ifPreviewGenerated = template && template.versions && template.versions.base ? template.versions.base.isPreviewGenerated : 0;
2807
+ const content = <CapImage src={imgSrc || ''} alt="" />;
2808
+
2809
+ temp.content = (
2810
+ <div
2811
+ className="sms-template-content">
2812
+ <div className={`sms-text ${this.state.clickedItem === template._id ? 'sms-text-blurred' : ''}`}>
2813
+ {content}
2814
+ </div>
2815
+ {/*template._id === this.state.clickedItem ? <CapButton onClick={(e) => this.handleEditClick(e, template._id)} className="edit-button" type="secondary">{type === 'embedded' ? this.props.intl.formatMessage(messages.selectButton) : this.props.intl.formatMessage(messages.editButton)}</CapButton> : ''*/}
2816
+ {/*template._id === this.state.clickedItem ? <CapButton onClick={() => this.handlePreviewClick(template)} className="preview-button" type="cancel">{this.props.intl.formatMessage(messages.previewButton)}</CapButton> : ''*/}
2817
+ </div>);
2818
+ }
2819
+ // temp.footer = (
2820
+ // <div className="footer-container">
2821
+ // <div className="card-title">
2822
+ // <span className="template-name" style={{ fontWeight: `${this.props.schema.channel.toLowerCase() === 'wechat' ? '400' : '600'}` }}>
2823
+ // { template && template.versions && template.versions.history && template.versions.history.length > 1 && this.props.schema.channel.toLowerCase() !== 'mobilepush' && <i style={{fontSize: '16px', margin: '0 8px 0 0', verticalAlign: 'middle'}} className="material-icons">filter_none</i>}
2824
+ // {template.name}
2825
+ // </span>
2826
+ // </div>
2827
+ // </div>
2828
+ // );
2829
+ items.push(temp);
2830
+ return true;
2831
+ });
2832
+ return items;
2833
+ }
2834
+
2835
+ handleAddLanguageFlow(data, val, event) {
2836
+ this.setState({currentEventData: data, currentEventVal: val, currentEvent: event, customPopoverVisible: false}, () => {
2837
+ this.props.setModalContent('add-language', data);
2838
+ });
2839
+ }
2840
+
2841
+
2842
+ getMissingOrUnsupportedTagsName = (content = '', type) => {
2843
+ const { MISSING_TAGS } = tagsTypes;
2844
+ const tagValidationResponse = this.validateTags(content);
2845
+ if (type && type === MISSING_TAGS) {
2846
+ return (tagValidationResponse[type] || []).join(', ').toString();
2847
+ }
2848
+ return null;
2849
+ };
2850
+
2851
+ renderTextAreaContent = (styling, columns, val, isVersionEnable, rows, cols, offset = 0) => {
2852
+ const { checkValidation, errorData, currentTab, formData } = this.state;
2853
+ const { MISSING_TAGS } = tagsTypes;
2854
+ const errorType = (isVersionEnable ? errorData[`${currentTab - 1}`][val.id] : errorData[val.id]);
2855
+ const ifError = checkValidation && errorType;
2856
+ const messageContent = isVersionEnable ? formData[`${currentTab - 1}`][val.id] : formData[val.id];
2857
+ const { MISSING_TAG_ERROR, TAG_BRACKET_COUNT_MISMATCH_ERROR } = errorMessageForTags;
2858
+ const { formatMessage } = this.props.intl;
2859
+
2860
+ const aiContentBotDisabled = isAiContentBotDisabled();
2861
+
2862
+ let errorMessageText = false;
2863
+ switch (errorType) {
2864
+ case MISSING_TAG_ERROR:
2865
+ errorMessageText = formatMessage(messages.missingTagsValidationError, {missingTags: this.getMissingOrUnsupportedTagsName(messageContent, MISSING_TAGS)});
2866
+ break;
2867
+ case TAG_BRACKET_COUNT_MISMATCH_ERROR:
2868
+ errorMessageText = formatMessage(globalMessages.unbalanacedCurlyBraces);
2869
+ break;
2870
+ case true:
2871
+ errorMessageText = formatMessage(messages.genericTagsValidationError);
2872
+ break;
2873
+ default:
2874
+ break;
2875
+ }
2876
+
2877
+ if (this.props.restrictPersonalization && hasPersonalizationTags(messageContent)) {
2878
+ errorMessageText = formatMessage(messages.personalizationTagsErrorMessage);
2879
+ }
2880
+ // Empty/required error: only show after user has triggered validation (ifError / "Done").
2881
+ // All other errors (brace, personalization, missing tags, generic): show in real time while typing.
2882
+ const isContentEmpty = !messageContent || !/\S/.test(String(messageContent).trim());
2883
+ const isEmptyError = errorType && isContentEmpty;
2884
+ const showError = errorType && (isEmptyError ? ifError : true);
2885
+ const prevErrorMessage = this.state.liquidErrorMessage?.STANDARD_ERROR_MSG?.[0];
2886
+ if (prevErrorMessage !== errorMessageText && errorMessageText && this.isLiquidFlowSupportedByChannel()) {
2887
+ this.setState((prevState) => ({
2888
+ liquidErrorMessage: {
2889
+ ...prevState.liquidErrorMessage,
2890
+ STANDARD_ERROR_MSG: [errorMessageText],
2891
+ }
2892
+ }), () => {
2893
+
2894
+ this.props.showLiquidErrorInFooter(this.state.liquidErrorMessage, this.props.channel === SMS? null: this.state.currentTab);
2895
+ });
2896
+ }
2897
+ // Always render the textarea regardless of the liquid error state update above.
2898
+ // Previously the textarea was inside the `else` branch, so it was not rendered during the
2899
+ // one render cycle when the liquid error message was first set – causing the input to lose
2900
+ // focus whenever a brace/tag error first appeared.
2901
+ if (styling === 'semantic') {
2902
+ columns.push(
2903
+ <CapColumn key="input" span={val.width} offset={offset}>
2904
+ <TextArea
2905
+ id={val.id}
2906
+ placeholder={val.placeholder ? val.placeholder : ''}
2907
+ className={`${showError ? 'error-form-builder' : ''}`}
2908
+ errorMessage={
2909
+ showError && errorMessageText && (
2910
+ !this.isLiquidFlowSupportedByChannel() ||
2911
+ [MOBILE_PUSH, INAPP].includes(this.props.schema?.channel?.toUpperCase())
2912
+ )
2913
+ ? errorMessageText
2914
+ : ''
2915
+ }
2916
+ label={val.label}
2917
+ autosize={val.autosize ? val.autosizeParams : false}
2918
+ onChange={(e) => this.updateFormData(e.target.value, val)}
2919
+ onBlur={(e) => this.handleFieldBlur(e, val)}
2920
+ style={val.style ? val.style : {}}
2921
+ defaultValue={messageContent || ''}
2922
+ value={messageContent || ""}
2923
+ rows={rows}
2924
+ disabled={val.disabled}
2925
+ cols={cols}
2926
+ />
2927
+ {[SMS, MOBILE_PUSH].includes(this.props.schema?.channel)
2928
+ && !aiContentBotDisabled
2929
+ && (
2930
+ <CapAskAira.ContentGenerationBot
2931
+ text={messageContent || ""}
2932
+ setText={(x) => {
2933
+ this.updateFormData(x, val);
2934
+ }}
2935
+ iconPlacement="float-br"
2936
+ rootStyle={{
2937
+ // 1rem is the margin-bottom of textarea
2938
+ bottom: 'calc(1rem + 0.2rem)',
2939
+ right: '0.2rem',
2940
+ }}
2941
+ />
2942
+ )}
2943
+ </CapColumn>
2944
+ );
2945
+ }
2946
+ };
2947
+
2948
+
2949
+ renderColLabelSection(section, childIndex) {
2950
+ // const schema = this.props.schema
2951
+ const fields = [];
2952
+ _.forEach(section.inputFields, (val) => {
2953
+ const row = [];
2954
+ const columns = [];
2955
+ const type = val.type;
2956
+ const styling = val.styling;
2957
+ if (!this.props.isNewVersionFlow && !val.standalone && !val.onlyDisplay && this.props.usingTabContainer && !this.state.formData[`${this.state.currentTab - 1}`]) {
2958
+ return true;
2959
+ }
2960
+ if (!val.onlyDisplay && !val.standalone && (!this.state.formData[`${this.state.currentTab - 1}`] || (!this.state.formData[`${this.state.currentTab - 1}`][val.id] && this.state.formData[`${this.state.currentTab - 1}`][val.id] === null))) {
2961
+ return true;
2962
+ }
2963
+ if (!val.onlyDisplay && !val.standalone && (!this.state.errorData[`${this.state.currentTab - 1}`] || (!this.state.errorData[`${this.state.currentTab - 1}`][val.id] && this.state.errorData[`${this.state.currentTab - 1}`][val.id] === null))) {
2964
+ return true;
2965
+ }
2966
+
2967
+ let ifError = false;
2968
+ const isVersionEnable = (this.state.usingTabContainer && !val.standalone);
2969
+ if (val.primitive) {
2970
+ if (val.id === "mark-final-version-label") {
2971
+ const baseTab = this.findBaseTab();
2972
+ if ((baseTab + 1) === childIndex) {
2973
+ return true;
2974
+ }
2975
+ }
2976
+ columns.push(
2977
+ <CapColumn key={val.id} span={val.width}>
2978
+ {this.renderPrimitiveElement(val, childIndex)}
2979
+ </CapColumn>
2980
+ );
2981
+ return true;
2982
+ }
2983
+ let content = "";
2984
+ let charList = {};
2985
+ switch (type) {
2986
+ case "input":
2987
+ if (styling === 'semantic') {
2988
+ ifError = this.state.checkValidation && (isVersionEnable ? this.state.errorData[`${this.state.currentTab - 1}`][val.id] : this.state.errorData[val.id]);
2989
+ const value = isVersionEnable ? this.state.formData[`${this.state.currentTab - 1}`][val.id] : this.state.formData[val.id];
2990
+ columns.push(
2991
+ <CapColumn key={val.id} span={val.width} offset={val.offset} style={val.style || {}}>
2992
+ <CapInput
2993
+ id={val.id}
2994
+ errorMessage={val.errorMessage && ifError ? val.errorMessage : ''}
2995
+ className={`input-primary chart-name-input${ifError ? ' error' : ''}`}
2996
+ // fluid={val.fluid}
2997
+ style={val.style ? val.style : {}}
2998
+ placeholder={val.placeholder}
2999
+ onChange={(e) => this.updateFormData(e.target.value, val)}
3000
+ onBlur={(e) => this.handleFieldBlur(e, val)}
3001
+ defaultValue={isVersionEnable ? this.state.formData[`${this.state.currentTab - 1}`][val.id] : this.state.formData[val.id]}
3002
+ value={value || ""}
3003
+ disabled={val.disabled}
3004
+ size="default"
3005
+ />
3006
+ </CapColumn>
3007
+ );
3008
+ }
3009
+ break;
3010
+ case "textarea":
3011
+ const rows = 20;
3012
+ const cols = 20;
3013
+ this.renderTextAreaContent(styling, columns, val, isVersionEnable, rows, cols, val.offset);
3014
+ break;
3015
+ case "contentPreview":
3016
+ const { formData , currentTab } = this.state || {};
3017
+ const tab = formData && formData[currentTab - 1];
3018
+ content = isVersionEnable ? tab && tab['sms-editor'] : formData && formData['sms-editor'];
3019
+ charList = updateCharCount(content).char_list;
3020
+ columns.push(<CapColumn id={val.id} className={"preview-chars"} span={val.width}>
3021
+ {charList.map((char) => char)}
3022
+ </CapColumn>);
3023
+ break;
3024
+ case "checkbox":
3025
+ ifError = this.state.checkValidation && (isVersionEnable ? this.state.errorData[`${this.state.currentTab - 1}`][val.id] : this.state.errorData[val.id]);
3026
+ if (styling === 'semantic') {
3027
+ columns.push(
3028
+ <CapColumn key="input" span={val.width} offset={val.offset}>
3029
+ <CapCheckbox
3030
+ className={`${ifError ? 'error' : ''}`}
3031
+ errorMessage={val.errorMessage && ifError ? val.errorMessage : ''}
3032
+ style={val.style ? val.style : {}}
3033
+ onChange={(e) => this.updateFormData(e, val)}
3034
+ checked={isVersionEnable ? this.state.formData[`${this.state.currentTab - 1}`][val.id] : this.state.formData[val.id]}
3035
+ defaultChecked={isVersionEnable ? this.state.formData[`${this.state.currentTab - 1}`][val.id] : this.state.formData[val.id]}
3036
+ disabled={val.disabled}
3037
+ />
3038
+ </CapColumn>
3039
+ );
3040
+ }
3041
+ break;
3042
+ case "tag-list":
3043
+ columns.push(
3044
+ <CapColumn key={`input-${val.id}`} span={10} offset={val.offset}>
3045
+ <TagList
3046
+ key={`input-${val.id}`}
3047
+ moduleFilterEnabled={this.props.location && this.props.location.query && this.props.location.query.type !== 'embedded'}
3048
+ label={val.label ? val.label : ''}
3049
+ onTagSelect={(data) => this.callChildEvent(data, val, 'onTagSelect')}
3050
+ onContextChange={this.props.onContextChange}
3051
+ location={this.props.location}
3052
+ tags={this.props.tags ? this.props.tags : []}
3053
+ injectedTags={this.props.injectedTags ? this.props.injectedTags : {}}
3054
+ className={val.className ? val.className : ''}
3055
+ id={val.id}
3056
+ userLocale={this.props.userLocale}
3057
+ selectedOfferDetails={this.props.selectedOfferDetails}
3058
+ eventContextTags={this.props?.eventContextTags}
3059
+ restrictPersonalization={this.props.restrictPersonalization}
3060
+ waitEventContextTags={this.props?.waitEventContextTags}
3061
+ />
3062
+ </CapColumn>
3063
+ );
3064
+ break;
3065
+ case "cap-tag-list-with-input":
3066
+ ifError = this.state.checkValidation && this.state.errorData[val.id];
3067
+ columns.push(
3068
+ <CapColumn key={`input-${val.id}`} span={val.width || 10} offset={val.offset}>
3069
+ <CapTagListWithInput
3070
+ key={`input-${val.id}`}
3071
+ inputId={val.id}
3072
+ inputValue={this.state.formData[val.id] || ''}
3073
+ inputOnChange={(e) => this.updateFormData(e.target.value, val)}
3074
+ inputPlaceholder={val.placeholder || ''}
3075
+ inputErrorMessage={val.errorMessage && ifError ? val.errorMessage : ''}
3076
+ inputRequired={val.required || false}
3077
+ inputDisabled={val.disabled || false}
3078
+ headingText={val.label || ''}
3079
+ headingStyle={val.headingStyle || { marginTop: '3%', marginRight: '79%' }}
3080
+ headingType="h4"
3081
+ onTagSelect={(data) => this.callChildEvent(data, val, 'onTagSelect')}
3082
+ onContextChange={this.props.onContextChange}
3083
+ location={this.props.location}
3084
+ tags={this.props.tags ? this.props.tags : []}
3085
+ injectedTags={this.props.injectedTags ? this.props.injectedTags : {}}
3086
+ className={val.className ? val.className : ''}
3087
+ userLocale={this.props.userLocale}
3088
+ selectedOfferDetails={this.props.selectedOfferDetails}
3089
+ eventContextTags={this.props?.eventContextTags}
3090
+ waitEventContextTags={this.props?.waitEventContextTags}
3091
+ moduleFilterEnabled={this.props.location && this.props.location.query && this.props.location.query.type !== 'embedded'}
3092
+ containerStyle={val.style || {}}
3093
+ inputProps={val.inputProps || {}}
3094
+ showInput={val.showInput !== false}
3095
+ showTagList={val.showTagList !== false}
3096
+ restrictPersonalization={this.props.restrictPersonalization}
3097
+ />
3098
+ </CapColumn>
3099
+ );
3100
+ break;
3101
+ case "tabs":
3102
+ columns.push(
3103
+ <CapColumn key="input" span={10}>
3104
+ <TagList
3105
+ onTagSelect={(data) => this.callChildEvent(data, val, 'onTagSelect')}
3106
+ />
3107
+ </CapColumn>
3108
+ );
3109
+ break;
3110
+ case "sms-preview": {
3111
+ if (this.state.usingTabContainer && !this.state.formData[`${this.state.currentTab - 1}`]) {
3112
+ return true;
3113
+ }
3114
+ content = isVersionEnable ? this.state.formData[`${this.state.currentTab - 1}`][`sms-editor${this.state.currentTab > 1 ? this.state.currentTab : ''}`] : this.state.formData[val.id];
3115
+ const unicodeEnabled = isVersionEnable ? this.state.formData[`${this.state.currentTab - 1}`][`unicode-validity${this.state.currentTab > 1 ? this.state.currentTab : ''}`] : this.state.formData['unicode-validity'];
3116
+ columns.push(
3117
+ <CapColumn key="input" span={10}>
3118
+ <UnifiedPreview
3119
+ channel={val.channel ? val.channel : 'SMS'}
3120
+ content={content}
3121
+ device={ANDROID}
3122
+ showDeviceToggle={false}
3123
+ showHeader={false}
3124
+ formatMessage={this.props.intl.formatMessage}
3125
+ senderId={unicodeEnabled ? 'Unicode' : 'ASCII'}
3126
+ />
3127
+ </CapColumn>
3128
+ );
3129
+ break;
3130
+ }
3131
+ case "popover":
3132
+ columns.push(
3133
+ <CapPopover
3134
+ trigger={val.trigger}
3135
+ placement={val.placement}
3136
+ visible={this.state.popoverVisible && (this.props.isNewVersionFlow || this.state.currentTab === childIndex || !childIndex)}
3137
+ onVisibleChange={this.handleVisibleChange}
3138
+ content={
3139
+ _.forEach(val.content.sections, (contentSection) => {
3140
+ this.renderSection(contentSection);
3141
+ })
3142
+ }
3143
+ >
3144
+ {
3145
+ _.forEach(val.value.sections, (popoverSection) => {
3146
+ this.renderSection(popoverSection);
3147
+ })
3148
+ }
3149
+ </CapPopover>
3150
+ );
3151
+ break;
3152
+ case "select": {
3153
+ const options = (this.state.formData[`${val.id}-options`]) ? (this.state.formData[`${val.id}-options`]) : val.options;
3154
+ columns.push(
3155
+ <CapColumn style={val.colStyle ? val.colStyle : {}} span={val.width}>
3156
+ <CapSelect
3157
+ id={val.id}
3158
+ placeholder={val.placeholder ? val.placeholder : ''}
3159
+ options={options}
3160
+ style={val.style ? val.style : {}}
3161
+ onSelect={(data) => this.updateFormData(data, val, 'onSelect')}
3162
+ value={(this.state.formData[val.id] && this.state.formData[val.id] !== '' ? this.state.formData[val.id] : undefined)}
3163
+ disabled={val.disabled}
3164
+ label={val.label}
3165
+ />
3166
+ </CapColumn>
3167
+ );
3168
+ }
3169
+ break;
3170
+ case "ckeditor":
3171
+
3172
+ columns.push(
3173
+ <CapColumn style={val.colStyle ? val.colStyle : {}} span={val.width}>
3174
+ <CKEditor
3175
+ id={val.id}
3176
+ content={(this.state.formData[`${this.state.currentTab - 1}`][val.id] ? this.state.formData[`${this.state.currentTab - 1}`][val.id] : '')}
3177
+ events={val.events}
3178
+ getEditorInstanse={val.getEditorInstanse}
3179
+ // this.callChildEvent(data, val, 'addVersion', event);
3180
+ handleContentChange={(event) => this.callChildEvent(event, val, 'onContentChange')}
3181
+ currentOrgDetails={this.props.currentOrgDetails}
3182
+ />
3183
+ </CapColumn>
3184
+ );
3185
+ break;
3186
+ case "edmeditor": {
3187
+ // if (!this.state.formData[`${this.state.currentTab - 1}`]) {
3188
+ // return true;
3189
+ // }
3190
+ let langTab = 1;
3191
+ if (val.id.match(/edmeditor/g)) {
3192
+ if (val.length > 9 && val.id.charAt(9)) {
3193
+ langTab = val.id.charAt(9);
3194
+ }
3195
+ }
3196
+ const edmSrc = this.state.formData[`${this.state.currentTab - 1}`][this.state.currentLangTab][`edmeditor${langTab > 1 ? langTab : ''}src`];
3197
+
3198
+ if (!edmSrc) {
3199
+ return false;
3200
+ }
3201
+
3202
+ columns.push(
3203
+ <CapColumn style={val.colStyle ? val.colStyle : {}} span={val.width}>
3204
+ <EDMEditor
3205
+ id={val.id}
3206
+ getEditorInstanse={this.getEditorInstanse}
3207
+ edmSrc={edmSrc}
3208
+ />
3209
+ </CapColumn>
3210
+ );
3211
+ }
3212
+ break;
3213
+ case "label": {
3214
+ columns.push(
3215
+ <CapHeading
3216
+ type={val.headingType || "h4"}
3217
+ style={val.style || {}}
3218
+ >
3219
+ {val.value}
3220
+ </CapHeading>
3221
+ );
3222
+ break;
3223
+ }
3224
+ default:
3225
+ break;
3226
+ }
3227
+ row.push(
3228
+ <CapRow useLegacy>
3229
+ {
3230
+ _.forEach(columns, (value) => {
3231
+ if (value) {
3232
+ return value;
3233
+ }
3234
+ return value;
3235
+ })
3236
+ }
3237
+ </CapRow>
3238
+ );
3239
+
3240
+ fields.push(row);
3241
+ return true;
3242
+ });
3243
+ _.forEach(section.actionFields, (val) => {
3244
+ const row = [];
3245
+ const columns = [];
3246
+ const type = val.type;
3247
+ const styling = val.styling;
3248
+ switch (type) {
3249
+ case "button":
3250
+ if (styling === 'semantic' && val.metaType === 'submit-button') {
3251
+ columns.push(
3252
+ <CapColumn>
3253
+ <CapButton
3254
+ onClick={(data) => this.callChildEvent(data, val, val.submitAction)}>
3255
+ {val.value}
3256
+ </CapButton>
3257
+ </CapColumn>
3258
+ );
3259
+ }
3260
+ break;
3261
+ default:
3262
+ break;
3263
+ }
3264
+ row.push(columns);
3265
+ fields.push(row);
3266
+ });
3267
+ return fields;
3268
+ }
3269
+
3270
+ renderParentSection(section, childIndex) {
3271
+ const cols = [];
3272
+ section.childSections.map( (childSection) => {
3273
+ const renderedChildSection = this.renderSection(childSection, childIndex);
3274
+ cols.push(<CapColumn style={childSection.colStyle ? childSection.colStyle : {}}offset={childSection.offset} span={childSection.width}>{renderedChildSection}</CapColumn>);
3275
+ return true;
3276
+ });
3277
+ const row = (<CapRow useLegacy style={section.rowStyle ? section.rowStyle : {}}>
3278
+ {
3279
+ _.forEach(cols, (value) => {
3280
+ if (value) {
3281
+ return value;
3282
+ }
3283
+ return value;
3284
+ })
3285
+ }
3286
+ </CapRow>);
3287
+ return row;
3288
+ }
3289
+
3290
+ renderSection(section, childIndex) {
3291
+ switch (section.type) {
3292
+ case "col-label":
3293
+ return this.renderColLabelSection(section, childIndex);
3294
+ case "multicols":
3295
+ return this.renderMultiColSection(section, childIndex);
3296
+ case "parent":
3297
+ return this.renderParentSection(section, childIndex);
3298
+ default:
3299
+ break;
3300
+ }
3301
+ return null;
3302
+ }
3303
+
3304
+ renderPrimitiveElement(val, childIndex) {
3305
+ const type = val.type;
3306
+ const tabName = this.state.formData[childIndex - 1] && this.state.formData[childIndex - 1].name ? this.state.formData[childIndex - 1].name : `${val.value} ${childIndex}`;
3307
+ const value = val.dynamicTab ? tabName : val.value;
3308
+ switch (type) {
3309
+ case "span":
3310
+ return <span id={`${val.id}${childIndex > 1 ? childIndex : ''}`}>{val.dynamicTab ? tabName : val.value}</span>;
3311
+ case "div":
3312
+ let children = _.get(this.state, `formData[${this.state.currentTab - 1}][${val.id}]`) || value;
3313
+ if (_.isEmpty(value) && !_.isEmpty(val.value)) {
3314
+ children = val.value;
3315
+ }
3316
+ const ref = _.get(this, `props.refs[${val.id}]`);
3317
+ return (<div
3318
+ ref={ref}
3319
+ className={val.className ? val.className : ''}
3320
+ id={`${val.id}${childIndex > 1 ? childIndex : ''}`}
3321
+ onClick={(data) => this.callChildEvent(data, val, val.submitAction)}
3322
+ style={val.style ? val.style : {}}
3323
+ onInput={(e) => { e.stopPropagation(); this.callChildEvent(e.target.textContent, val, 'onChange'); }}
3324
+ >{children}</div>);
3325
+ default:
3326
+ return '';
3327
+ }
3328
+ }
3329
+
3330
+ onGalleryClick = (event) => {
3331
+ event.stopPropagation();
3332
+ this.props.setDrawerVisibility(true);
3333
+ }
3334
+
3335
+ onReUpload = () => {
3336
+ const { formData } = this.state;
3337
+ this.props.onChange({
3338
+ ...formData,
3339
+ 0: {
3340
+ ...formData[0],
3341
+ image: undefined,
3342
+ imagePreview: undefined,
3343
+ },
3344
+ base: {
3345
+ ...formData.base,
3346
+ image: undefined,
3347
+ imagePreview: undefined,
3348
+ },
3349
+ reUpload: true,
3350
+ });
3351
+ }
3352
+
3353
+ getUploadVariants = (isImage, val) => {
3354
+ if (this.props.schema && this.props.schema.channel === 'LINE') {
3355
+ if (!isImage) {
3356
+ return (
3357
+ <>
3358
+ <CapUploader.CapDragger
3359
+ customRequest={(data) => this.capUploaderCustomRequest(data, val)}
3360
+ {...val.componentProps}
3361
+ className="form-builder-dragger"
3362
+ >
3363
+ <CapHeading className="dragger-title" type="h7">
3364
+ <FormattedMessage {...messages.dragAndDrop} />
3365
+ </CapHeading>
3366
+ <CapHeading className="dragger-or" type="label6">
3367
+ <FormattedMessage {...messages.or} />
3368
+ </CapHeading>
3369
+ <CapButton className="dragger-button" type="secondary" style={{marginRight: CAP_SPACE_08}}>
3370
+ <FormattedMessage {...messages.uploadComputer} />
3371
+ </CapButton>
3372
+ <CapButton
3373
+ className="dragger-button"
3374
+ type="secondary"
3375
+ style={{marginLeft: CAP_SPACE_08}}
3376
+ onClick={this.onGalleryClick}
3377
+ >
3378
+ <FormattedMessage {...messages.uploadGallery} />
3379
+ </CapButton>
3380
+ </CapUploader.CapDragger>
3381
+ <CapHeading type="h6" style={{marginTop: CAP_SPACE_12 }}>
3382
+ <FormattedMessage {...messages.imageDimenstionDescription} />
3383
+ </CapHeading>
3384
+ </>
3385
+ );
3386
+ }
3387
+ return (
3388
+ <CapButton
3389
+ className="dragger-button"
3390
+ type="link"
3391
+ style={{top: 0, position: 'absolute', right: 0, color: FONT_COLOR_05 }}
3392
+ onClick={this.onReUpload}
3393
+ >
3394
+ <FormattedMessage {...messages.imageReUpload} />
3395
+ </CapButton>
3396
+ );
3397
+ }
3398
+ return (
3399
+ <CapButton disabled={val.disabled ? val.disabled : false} type="link" prefix={<CapIcon size="s" type="add-photo" />} onClick={(e) => this.openFileDialog(e, val.id)} style={{float: 'right', color: `${val.disabled ? FONT_COLOR_04 : FONT_COLOR_05}`}}>
3400
+ {val.label}
3401
+ </CapButton>
3402
+ );
3403
+ }
3404
+
3405
+ checkBeeEditorAllowedForLibrary = () => {
3406
+ const { isFullMode = false, editor } = this.props || {};
3407
+ if ((editor === "BEE" && !isFullMode) || isFullMode) {
3408
+ return true;
3409
+ }
3410
+ return false;
3411
+ }
3412
+
3413
+ handleSetText = (val, newText) => {
3414
+ this.updateFormData(newText, val);
3415
+ };
3416
+
3417
+ renderMultiColSection(section, childIndex) {
3418
+ const fields = [];
3419
+ let contentSections = [];
3420
+ let popoverSections = [];
3421
+ const self = this;
3422
+ _.forEach(section.inputFields, (field) => {
3423
+ const columns = [];
3424
+ _.forEach(field.cols, (val) => {
3425
+ const type = val.type;
3426
+ const styling = val.styling;
3427
+ const isVersionEnable = (this.state.usingTabContainer && !val.standalone);
3428
+
3429
+
3430
+ if (!this.props.isNewVersionFlow && !val.standalone && !val.onlyDisplay && isVersionEnable && !this.state.formData[`${this.state.currentTab - 1}`]) {
3431
+ return true;
3432
+ }
3433
+ if (!val.standalone && !val.onlyDisplay &&
3434
+ (!this.state.formData[`${this.state.currentTab - 1}`] ||
3435
+ (!this.props.isNewVersionFlow && !this.state.formData[`${this.state.currentTab - 1}`] &&
3436
+ this.state.errorData[`${this.state.currentTab - 1}`][val.id] === null)) &&
3437
+ (this.props.isNewVersionFlow && !this.state.formData[`${this.state.currentTab - 1}`] &&
3438
+ this.state.errorData[`${this.state.currentTab - 1}`] && this.state.errorData[`${this.state.currentTab - 1}`][val.id] === null)) {
3439
+ return true;
3440
+ }
3441
+ if (!val.onlyDisplay && !val.standalone &&
3442
+ (!this.props.isNewVersionFlow && (!this.state.errorData[`${this.state.currentTab - 1}`] ||
3443
+ (!this.state.errorData[`${this.state.currentTab - 1}`] &&
3444
+ this.state.errorData[`${this.state.currentTab - 1}`][val.id] === null)))) {
3445
+ return true;
3446
+ }
3447
+
3448
+ let ifError = false;
3449
+ if (val.primitive) {
3450
+ if (val.id === "mark-final-version-label") {
3451
+ const baseTab = this.findBaseTab();
3452
+ if ((baseTab + 1) === childIndex) {
3453
+ return true;
3454
+ }
3455
+ }
3456
+ columns.push(
3457
+ <CapColumn style={val.colStyle ? val.colStyle : {}} offset={val.offset} key={val.id} span={val.width}>
3458
+ {this.renderPrimitiveElement(val, childIndex)}
3459
+ </CapColumn>
3460
+ );
3461
+ return true;
3462
+ }
3463
+ switch (type) {
3464
+ case "input":
3465
+ const errorType = isVersionEnable ? this.state.errorData[`${this.state.currentTab - 1}`][val.id] : this.state.errorData[val.id];
3466
+ const aiContentBotDisabled = isAiContentBotDisabled();
3467
+ ifError = this.state.checkValidation && (isVersionEnable ? this.state.errorData[`${this.state.currentTab - 1}`][val.id] : this.state.errorData[val.id]);
3468
+ const { TAG_BRACKET_COUNT_MISMATCH_ERROR } = errorMessageForTags;
3469
+ const { formatMessage } = this.props.intl;
3470
+ const value = isVersionEnable ? this.state.formData[`${this.state.currentTab - 1}`][val.id] : this.state.formData[val.id];
3471
+ // Show personalization error for title field inline (same as message textarea) when restrictPersonalization is true
3472
+ const hasPersonalizationError = this.props.restrictPersonalization && hasPersonalizationTags(value);
3473
+ if (hasPersonalizationError) {
3474
+ ifError = true;
3475
+ }
3476
+ let errorMessageText = hasPersonalizationError
3477
+ ? formatMessage(messages.personalizationTagsErrorMessage)
3478
+ : (errorType === TAG_BRACKET_COUNT_MISMATCH_ERROR ? formatMessage(globalMessages.unbalanacedCurlyBraces) : (val.errorMessage && ifError ? val.errorMessage : ''));
3479
+ if (styling === 'semantic') {
3480
+ const isEmailStandaloneHighFreq = val.standalone && this.props.schema?.channel?.toUpperCase() === EMAIL;
3481
+ if (isEmailStandaloneHighFreq) {
3482
+ columns.push(
3483
+ <CapColumn key={val.id} span={val.width} offset={val.offset} style={val.style || {}}>
3484
+ <HighFreqInput
3485
+ id={val.id}
3486
+ errorMessage={errorMessageText}
3487
+ label={val.label}
3488
+ inductiveText={val.inductiveText}
3489
+ className={`input-primary chart-name-input${ifError ? ' error' : ''}`}
3490
+ style={val.style ? val.style : {}}
3491
+ placeholder={val.placeholder}
3492
+ onCommit={(newValue) => this.performFormDataUpdate(newValue, val)}
3493
+ onBlur={(e) => this.handleFieldBlur(e, val)}
3494
+ value={value || ""}
3495
+ disabled={val.disabled}
3496
+ size={val.size || "default"}
3497
+ />
3498
+ {!aiContentBotDisabled && (
3499
+ <CapAskAira.ContentGenerationBot
3500
+ text={value || ""}
3501
+ setText={this.handleSetText.bind(this, val)}
3502
+ iconPlacement="float-br"
3503
+ iconSize="1.6rem"
3504
+ rootStyle={{
3505
+ bottom: "0.2rem",
3506
+ right: "0.2rem",
3507
+ left: "auto",
3508
+ }}
3509
+ />
3510
+ )}
3511
+ </CapColumn>
3512
+ );
3513
+ } else {
3514
+ columns.push(
3515
+ <CapColumn key={val.id} span={val.width} offset={val.offset} style={val.style || {}}>
3516
+ <CapInput
3517
+ id={val.id}
3518
+ errorMessage={errorMessageText}
3519
+ label={val.label}
3520
+ inductiveText={val.inductiveText}
3521
+ className={`input-primary chart-name-input${ifError ? ' error' : ''}`}
3522
+ // fluid={val.fluid}
3523
+ style={val.style ? val.style : {}}
3524
+ placeholder={val.placeholder}
3525
+ onChange={(e) => this.updateFormData(e.target.value, val)}
3526
+ onBlur={(e) => this.handleFieldBlur(e, val)}
3527
+ value={value || ""}
3528
+ defaultValue={isVersionEnable ? this.state.formData[`${this.state.currentTab - 1}`][val.id] : this.state.formData[val.id]}
3529
+ disabled={val.disabled}
3530
+ size={val.size || "default"}
3531
+ />
3532
+ {this.props.schema?.channel === EMAIL &&
3533
+ !aiContentBotDisabled && (
3534
+ <CapAskAira.ContentGenerationBot
3535
+ text={value || ""}
3536
+ setText={this.handleSetText.bind(this, val)}
3537
+ iconPlacement="float-br"
3538
+ iconSize="1.6rem"
3539
+ rootStyle={{
3540
+ bottom: "0.2rem",
3541
+ right: "0.2rem",
3542
+ left: "auto",
3543
+ }}
3544
+ />
3545
+ )}
3546
+ </CapColumn>
3547
+ );
3548
+ }
3549
+ }
3550
+ break;
3551
+
3552
+ case "textarea":
3553
+ const rows = 10;
3554
+ const cols = 2;
3555
+ this.renderTextAreaContent(styling, columns, val, isVersionEnable, rows, cols, val.offset);
3556
+ break;
3557
+ case "checkbox":
3558
+ ifError = this.state.checkValidation && (isVersionEnable ? this.state.errorData[`${this.state.currentTab - 1}`][val.id] : this.state.errorData[val.id]);
3559
+ const { disabled , hoverText } = val || {};
3560
+ if (styling === 'semantic') {
3561
+ columns.push(
3562
+ <CapColumn key="input" span={val.width} offset={val.offset}>
3563
+ <CapTooltip
3564
+ title={ disabled && hoverText ? hoverText : '' }
3565
+ placement="topLeft"
3566
+ >
3567
+ <CapCheckbox
3568
+ key={val.id}
3569
+ className={`${ifError ? 'error' : ''}`}
3570
+ errorMessage={val.errorMessage && ifError ? val.errorMessage : ''}
3571
+ onChange={(e) => this.updateFormData(e.target.checked, val, val.submitAction)}
3572
+ checked={isVersionEnable ? this.state.formData[`${this.state.currentTab - 1}`][val.id] : this.state.formData[val.id]}
3573
+ defaultChecked={isVersionEnable ? this.state.formData[`${this.state.currentTab - 1}`][val.id] : this.state.formData[val.id]}
3574
+ style={val.style ? val.style : {}}
3575
+ disabled={val.disabled}
3576
+ inductiveText={val.inductiveText}
3577
+ >
3578
+ {val.label}
3579
+ </CapCheckbox>
3580
+ </CapTooltip>
3581
+ </CapColumn>
3582
+ );
3583
+ }
3584
+ break;
3585
+ case "radio":
3586
+ ifError = this.state.checkValidation && (isVersionEnable ? this.state.errorData[`${this.state.currentTab - 1}`][val.id] : this.state.errorData[val.id]);
3587
+ if (styling === 'semantic') {
3588
+ columns.push(
3589
+ <CapColumn key={`input-${val.id}`} span={val.width} offset={val.offset}>
3590
+ <CapRadio
3591
+ className={`${ifError ? 'error' : ''}`}
3592
+ errorMessage={val.errorMessage && ifError ? val.errorMessage : ''}
3593
+ onChange={(e) => this.updateFormData(e.target.checked, val)}
3594
+ checked={isVersionEnable ? this.state.formData[`${this.state.currentTab - 1}`][val.id] : this.state.formData[val.id]}
3595
+ defaultChecked={isVersionEnable ? this.state.formData[`${this.state.currentTab - 1}`][val.id] : this.state.formData[val.id]}
3596
+ style={val.style ? val.style : {}}
3597
+ >
3598
+ {val.label}
3599
+ </CapRadio>
3600
+ </CapColumn>
3601
+ );
3602
+ }
3603
+ break;
3604
+ case 'radioGroup':
3605
+ ifError = this.state.checkValidation && (isVersionEnable ? this.state.errorData[`${this.state.currentTab - 1}`][val.id] : this.state.errorData[val.id]);
3606
+ if (styling === 'semantic') {
3607
+ columns.push(
3608
+ <CapColumn key={`input-${val.id}`} span={val.width} offset={val.offset}>
3609
+ <CapRadioGroup
3610
+ key={`${val.id}-radio-group`}
3611
+ className={`form-builder-radio-group ${ifError ? 'error' : ''}`}
3612
+ errorMessage={val.errorMessage && ifError ? val.errorMessage : ''}
3613
+ onChange={(e) => this.updateFormData(e.target.value, val)}
3614
+ style={val.style ? val.style : {}}
3615
+ value={this.handleSetRadioValue(this.state.formData, self.state.currentTab, val)}
3616
+ name={val.name ? val.name : `${val.id}-name`}
3617
+ disabled={val.disabled}
3618
+ >
3619
+ {val.options.map((option, index) => {
3620
+ const isMappedTemplateWechat = val.id === "template-redirect-options-radio";
3621
+ return (
3622
+ <CapRadio
3623
+ value={ isMappedTemplateWechat ? option.value : option }
3624
+ inductiveText={val.inductiveText && val.inductiveText[index]}>{ isMappedTemplateWechat ? option.label : option }
3625
+ </CapRadio>
3626
+ )
3627
+ }
3628
+ )}
3629
+
3630
+ </CapRadioGroup>
3631
+ {/*{ifError ?*/}
3632
+ {/*<div className="error"> {val.errorMessage}</div>*/}
3633
+ {/*:*/}
3634
+ {/*""*/}
3635
+ {/*}*/}
3636
+ </CapColumn>
3637
+ );
3638
+ }
3639
+ break;
3640
+ case 'image':
3641
+ ifError = this.state.checkValidation && (isVersionEnable ? this.state.errorData[`${this.state.currentTab - 1}`][val.id] : this.state.errorData[val.id]);
3642
+ columns.push(
3643
+ <CapColumn key={`input-${val.id}${self.state.currentTab - 1}`} style={val.style} span={val.width} offset={val.offset}>
3644
+ <div>
3645
+ <div className={`image-container ${ifError ? 'error' : ''}`}>
3646
+ {self.state && !!self.state.formData[`${self.state.currentTab - 1}`].image ?
3647
+ <CapImage src={self.state.formData[`${self.state.currentTab - 1}`].image} alt={val.alt} style={val.style}/>
3648
+ :
3649
+ <div style={{width: "100%", textAlign: "center"}}><span className="image-placeholder">{val.placeholder}</span></div>
3650
+ }
3651
+ </div>
3652
+ {ifError && <span className="error">{val.errorMessage}</span>}
3653
+ </div>
3654
+ </CapColumn>
3655
+ );
3656
+ break;
3657
+ case UPLOAD:
3658
+ const {
3659
+ formData: {
3660
+ [`${self.state.currentTab - 1}`]: {
3661
+ image,
3662
+ } = {},
3663
+ } = {},
3664
+ } = self.state;
3665
+ const isImage = !!image;
3666
+ ifError = (this.state.checkValidation || this.props.startValidation) && (isVersionEnable ? this.state.errorData[`${this.state.currentTab - 1}`][val.id] : this.state.errorData[val.id]);
3667
+ const ImageComponent = (props) => (
3668
+ <div key={`${val.id}-${this.state.currentTab}`}>
3669
+ <div className={`image-container ${props.ifError ? 'error' : ''}`}>
3670
+
3671
+ {isImage ?
3672
+ <CapImage src={self.state.formData[`${self.state.currentTab - 1}`].image} alt={props.alt} style={props.style}/>
3673
+ :
3674
+ <div style={{width: "100%", textAlign: "center"}}><span className="image-placeholder">{props.placeholder}</span></div>
3675
+ }
3676
+ </div>
3677
+ </div>
3678
+ );
3679
+ const WithLabel = LabelHOC(ImageComponent);
3680
+ const {errorMessage, ...rest} = val.previewProps || {};
3681
+ columns.push(
3682
+ <CapColumn span={val.width} offset={val.offset} style={val.style}>
3683
+ {val.showPreview && <WithLabel key={`${val.id}-with-label`} {...rest} errorMessage={ifError && errorMessage} ifError={ifError}/>}
3684
+ <form encType="multipart/form-data" id={val.id}>
3685
+ <input key={val.id} style={{ display: 'none'}} id="fileName" type="file" onChange={(e) => this.uploadImages(e, {files: e.target.files}, val)} accept={val.supportedExtensions ? val.supportedExtensions : "image/*"} />
3686
+ {this.getUploadVariants(isImage, val)}
3687
+ </form>
3688
+ </CapColumn>
3689
+ );
3690
+ break;
3691
+ case 'capUpload':
3692
+ columns.push(
3693
+ <CapUploader
3694
+ customRequest={(data) => this.capUploaderCustomRequest(data, val)}
3695
+ >
3696
+ <CapButton>
3697
+ <FormattedMessage {...messages.upload} />
3698
+ </CapButton>
3699
+ </CapUploader>
3700
+ );
3701
+ break;
3702
+
3703
+ case 'capDragger':
3704
+ columns.push(
3705
+ <CapUploader.CapDragger
3706
+ customRequest={(data) => this.capUploaderCustomRequest(data, val)}
3707
+ {...val.componentProps}
3708
+ className="form-builder-dragger"
3709
+ >
3710
+ <CapHeading className="dragger-title" type="h7">
3711
+ <FormattedMessage {...messages.dragAndDrop} />
3712
+ </CapHeading>
3713
+ <CapHeading className="dragger-or" type="label6">
3714
+ <FormattedMessage {...messages.or} />
3715
+ </CapHeading>
3716
+ <CapButton className="dragger-button" type="secondary">
3717
+ <FormattedMessage {...messages.selectAFile} />
3718
+ </CapButton>
3719
+ </CapUploader.CapDragger>
3720
+ );
3721
+ break;
3722
+
3723
+ case "table":
3724
+ columns.push(
3725
+ <CapTable dataSource={self.props.iosCtasData} loading={self.props.iosCtasData ? !self.props.iosCtasData.length : true} onRowClick={(row) => this.updateFormData(row, val, "onRowClick" )} pagination={val.pagination}>
3726
+ {_.map(val.columns, (col) => <Column {...col}/>)}
3727
+ </CapTable>
3728
+ );
3729
+ break;
3730
+ case "tag-list":
3731
+ let moduleFilterEnabled = this.props.location && this.props.location.query && this.props.location.query.type !== 'embedded';
3732
+ const channel = _.get(this.props, 'schema.channel', "");
3733
+ if (channel === 'EMAIL') {
3734
+ moduleFilterEnabled = this.props.isFullMode;
3735
+ }
3736
+ const langIndex = 0;
3737
+ 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;
3738
+ const isBEEAppEnable = this.checkBeeEditorAllowedForLibrary();
3739
+
3740
+ // Always render Subject TagList (title-tagList) even when Bee editor is active for EMAIL channel
3741
+ if (
3742
+ val.id === 'title-tagList' ||
3743
+ !(_.get(this.state, `formData[${this.state.currentTab - 1}][${currentLang}].is_drag_drop`, false)) ||
3744
+ isBEEAppEnable === false ||
3745
+ channel !== 'EMAIL'
3746
+ ) {
3747
+ columns.push(
3748
+ <CapColumn key={`input-${val.id}`} offset={val.offset} span={val.width ? val.width : ''} style={val.style ? val.style : {marginBottom: '16px'}}>
3749
+ <TagList
3750
+ key={`input-${val.id}`}
3751
+ moduleFilterEnabled={moduleFilterEnabled}
3752
+ label={val.label ? val.label : ''}
3753
+ onTagSelect={(data) => this.callChildEvent(data, val, 'onTagSelect')}
3754
+ onContextChange={this.props.onContextChange}
3755
+ location={this.props.location}
3756
+ tags={this.props.tags ? this.props.tags : []}
3757
+ injectedTags={this.props.injectedTags ? this.props.injectedTags : {}}
3758
+ className={val.className ? val.className : ''}
3759
+ id={val.id}
3760
+ userLocale={this.state.translationLang}
3761
+ selectedOfferDetails={this.props.selectedOfferDetails}
3762
+ channel={channel}
3763
+ eventContextTags={this.props?.eventContextTags}
3764
+ restrictPersonalization={this.props.restrictPersonalization}
3765
+ waitEventContextTags={this.props?.waitEventContextTags}
3766
+ />
3767
+ </CapColumn>
3768
+ );
3769
+ }
3770
+ break;
3771
+ case "cap-tag-list-with-input":
3772
+ let moduleFilterEnabledForCapTagList = this.props.location && this.props.location.query && this.props.location.query.type !== 'embedded';
3773
+ const channelForCapTagList = _.get(this.props, 'schema.channel', "");
3774
+ if (channelForCapTagList === 'EMAIL') {
3775
+ moduleFilterEnabledForCapTagList = this.props.isFullMode;
3776
+ }
3777
+ const langIndexForCapTagList = 0;
3778
+ 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;
3779
+ const isBEEAppEnableForCapTagList = this.checkBeeEditorAllowedForLibrary();
3780
+ ifError = this.state.checkValidation && (isVersionEnable ? this.state.errorData[`${this.state.currentTab - 1}`][val.id] : this.state.errorData[val.id]);
3781
+
3782
+ // Always render Subject CapTagListWithInput even when Bee editor is active for EMAIL channel
3783
+ if (
3784
+ val.id === 'template-subject' ||
3785
+ !(_.get(this.state, `formData[${this.state.currentTab - 1}][${currentLangForCapTagList}].is_drag_drop`, false)) ||
3786
+ isBEEAppEnableForCapTagList === false ||
3787
+ channelForCapTagList !== 'EMAIL'
3788
+ ) {
3789
+ const isEmailStandaloneSubject = val.standalone && channelForCapTagList === EMAIL && val.id === 'template-subject';
3790
+ const tagListProps = {
3791
+ key: `input-${val.id}`,
3792
+ inputId: val.id,
3793
+ inputValue: this.state.formData[val.id] || '',
3794
+ inputPlaceholder: val.placeholder || '',
3795
+ inputErrorMessage: val.errorMessage && ifError ? val.errorMessage : '',
3796
+ inputRequired: val.required || false,
3797
+ inputDisabled: val.disabled || false,
3798
+ headingText: val.label || '',
3799
+ headingStyle: val.headingStyle || { marginTop: '3%', marginRight: '79%' },
3800
+ headingType: "h4",
3801
+ onTagSelect: (data) => this.callChildEvent(data, val, 'onTagSelect'),
3802
+ onContextChange: this.props.onContextChange,
3803
+ location: this.props.location,
3804
+ tags: this.props.tags ? this.props.tags : [],
3805
+ injectedTags: this.props.injectedTags ? this.props.injectedTags : {},
3806
+ className: val.className ? val.className : '',
3807
+ userLocale: this.state.translationLang,
3808
+ selectedOfferDetails: this.props.selectedOfferDetails,
3809
+ eventContextTags: this.props?.eventContextTags,
3810
+ waitEventContextTags: this.props?.waitEventContextTags,
3811
+ moduleFilterEnabled: moduleFilterEnabledForCapTagList,
3812
+ containerStyle: val.style || {},
3813
+ inputProps: val.inputProps || {},
3814
+ showInput: val.showInput !== false,
3815
+ showTagList: val.showTagList !== false,
3816
+ restrictPersonalization: this.props.restrictPersonalization,
3817
+ };
3818
+ columns.push(
3819
+ <CapColumn key={`input-${val.id}`} offset={val.offset} span={val.width ? val.width : ''} style={val.style ? val.style : {marginBottom: '16px'}}>
3820
+ {isEmailStandaloneSubject ? (
3821
+ <HighFreqTagInput
3822
+ {...tagListProps}
3823
+ onCommit={(newValue) => this.performFormDataUpdate(newValue, val)}
3824
+ />
3825
+ ) : (
3826
+ <CapTagListWithInput
3827
+ {...tagListProps}
3828
+ inputOnChange={(e) => this.updateFormData(e.target.value, val)}
3829
+ />
3830
+ )}
3831
+ </CapColumn>
3832
+ );
3833
+ }
3834
+ break;
3835
+ case "button":
3836
+ if (styling === 'semantic' && val.metaType === 'submit-button') {
3837
+ columns.push(
3838
+ <CapColumn style={val.colStyle ? val.colStyle : {}} span={val.width} offset={val.offset}>
3839
+ <CapButton
3840
+ type={val.buttonType}
3841
+ icon={val.icon}
3842
+ disabled={val.disabled ? val.disabled : false}
3843
+ onClick={(data) => this.callChildEvent(data, val, val.submitAction)}>
3844
+ {val.value}
3845
+ </CapButton>
3846
+ </CapColumn>
3847
+ );
3848
+ }
3849
+ break;
3850
+
3851
+ case "sms-preview": {
3852
+ let content = "";
3853
+ if (this.state.usingTabContainer && !this.state.formData[`${this.state.currentTab - 1}`]) {
3854
+ return true;
3855
+ }
3856
+ content = isVersionEnable ? this.state.formData[`${this.state.currentTab - 1}`][`sms-editor${this.state.currentTab > 1 ? this.state.currentTab : ''}`] : this.state.formData[val.id];
3857
+ const unicodeEnabled = isVersionEnable ? this.state.formData[`${this.state.currentTab - 1}`][`unicode-validity${this.state.currentTab > 1 ? this.state.currentTab : ''}`] : this.state.formData['unicode-validity'];
3858
+ columns.push(
3859
+ <CapColumn key="input" span={23} offset={1}>
3860
+ <UnifiedPreview
3861
+ key={val.id}
3862
+ style={val.customStyling ? val.customStyling : {}}
3863
+ channel={val.channel ? val.channel : 'SMS'}
3864
+ content={content}
3865
+ device={ANDROID}
3866
+ showDeviceToggle={false}
3867
+ showHeader={false}
3868
+ formatMessage={this.props.intl.formatMessage}
3869
+ senderId={unicodeEnabled ? 'Unicode' : 'ASCII'}
3870
+ />
3871
+ </CapColumn>
3872
+ );
3873
+ }
3874
+ break;
3875
+ case "line-preview": {
3876
+ const content = {
3877
+ bodyText: isVersionEnable ? this.state.formData[`${this.state.currentTab - 1}`][`message-editor${this.state.currentTab > 1 ? this.state.currentTab : ''}`] : this.state.formData[val.id],
3878
+ bodyImage: this.state.formData[`${this.state.currentTab - 1}`].imagePreview,
3879
+ };
3880
+ if (!this.state.formData[`${this.state.currentTab - 1}`]) {
3881
+ return true;
3882
+ }
3883
+
3884
+ columns.push(
3885
+ <CapColumn key="input" span={23} offset={1}>
3886
+ <TemplatePreview
3887
+ key={val.id}
3888
+ style={val.customStyling ? val.customStyling : {}}
3889
+ channel={val.channel}
3890
+ content={content}
3891
+ >
3892
+ </TemplatePreview>
3893
+ </CapColumn>
3894
+ );
3895
+ }
3896
+ break;
3897
+ case "mobile-push-preview":
3898
+ if (!this.state.formData[`${this.state.currentTab - 1}`]) {
3899
+ return true;
3900
+ }
3901
+ // Transform FormBuilder content format to UnifiedPreview format
3902
+ // MobilePushPreviewContent expects: { androidContent: { header, bodyText, bodyImage, actions }, iosContent: {...} }
3903
+ const headerValue = this.state.formData[`${this.state.currentTab - 1}`][val.content.title] || '';
3904
+ const bodyTextValue = this.state.formData[`${this.state.currentTab - 1}`][val.content.message] || '';
3905
+ const bodyImageValue = this.state.formData[`${this.state.currentTab - 1}`].image || '';
3906
+ const actionsArray = [];
3907
+ if (this.state.formData[`${this.state.currentTab - 1}`][val.content.secondaryCta1]) {
3908
+ actionsArray.push({label: this.state.formData[`${this.state.currentTab - 1}`][val.content.secondaryCta1]});
3909
+ }
3910
+ if (this.state.formData[`${this.state.currentTab - 1}`][val.content.secondaryCta2]) {
3911
+ actionsArray.push({label: this.state.formData[`${this.state.currentTab - 1}`][val.content.secondaryCta2]});
3912
+ }
3913
+ const mobilePushContent = {
3914
+ androidContent: {
3915
+ header: headerValue,
3916
+ bodyText: bodyTextValue,
3917
+ bodyImage: bodyImageValue,
3918
+ actions: actionsArray,
3919
+ appName: val.content.appName || '',
3920
+ },
3921
+ iosContent: {
3922
+ header: headerValue,
3923
+ bodyText: bodyTextValue,
3924
+ bodyImage: bodyImageValue,
3925
+ actions: actionsArray,
3926
+ appName: val.content.appName || '',
3927
+ },
3928
+ };
3929
+ columns.push(
3930
+ <CapColumn key="input" span={23} offset={1}>
3931
+ <UnifiedPreview
3932
+ key={val.id}
3933
+ style={val.customStyling ? val.customStyling : {}}
3934
+ channel={val.channel || 'MOBILEPUSH'}
3935
+ content={mobilePushContent}
3936
+ device={ANDROID}
3937
+ showDeviceToggle={false}
3938
+ showHeader={false}
3939
+ formatMessage={this.props.intl.formatMessage}
3940
+ />
3941
+ </CapColumn>
3942
+ );
3943
+ break;
3944
+ case "popover":
3945
+ contentSections = val.content.sections.map( (contentSection) => {
3946
+ const result = this.renderSection(contentSection, childIndex);
3947
+ return result;
3948
+ });
3949
+ popoverSections = val.value.sections.map( (popoverSection) => {
3950
+ const result = this.renderSection(popoverSection, childIndex);
3951
+ return result;
3952
+ });
3953
+ columns.push(
3954
+ <CapColumn style={val.colStyle ? val.colStyle : {}} span={val.width} offset={val.offset}>
3955
+ <CapPopover
3956
+ trigger={val.trigger}
3957
+ placement={val.placement}
3958
+ visible={this.state.popoverVisible && (this.props.isNewVersionFlow || this.state.currentTab === childIndex || !childIndex)}
3959
+ onVisibleChange={this.handleVisibleChange}
3960
+ content={
3961
+ _.forEach(contentSections, (contentSection) => {
3962
+ if (contentSection) {
3963
+ return contentSection;
3964
+ }
3965
+ return contentSection;
3966
+ })
3967
+ }
3968
+ >
3969
+ {
3970
+ _.forEach(popoverSections, (popoverSection) => {
3971
+ if (popoverSection) {
3972
+ return popoverSection;
3973
+ }
3974
+ return popoverSection;
3975
+ })
3976
+ }
3977
+ </CapPopover>
3978
+ </CapColumn>
3979
+ );
3980
+ break;
3981
+
3982
+ case "icon":
3983
+ if (val.id === "base-version-icon") {
3984
+ const baseTab = this.findBaseTab();
3985
+ if ((baseTab + 1) !== childIndex) {
3986
+ return true;
3987
+ }
3988
+ }
3989
+ columns.push(
3990
+ <CapColumn
3991
+ key={`icon-${val.id}`}
3992
+ id={val.id}
3993
+ offset={val.offset}
3994
+ width={val.width ? val.width : 1}
3995
+ onClick={(data) => this.callChildEvent(data, val, val.submitAction)}
3996
+ className={val.className ? val.className : ''}
3997
+ style={val.colStyle ? val.colStyle : {}}
3998
+ >
3999
+ <i style={val.style ? val.style : {}} className="material-icons">{val.value ? val.value : '&#xE313;'}</i>
4000
+ </CapColumn>
4001
+ );
4002
+ break;
4003
+ case "select": {
4004
+ ifError = this.state.checkValidation && (isVersionEnable ? this.state.errorData[`${this.state.currentTab - 1}`][val.id] : this.state.errorData[val.id]);
4005
+ const options = (this.state.formData[`${val.id}-options`]) ? (this.state.formData[`${val.id}-options`]) : val.options;
4006
+ columns.push(
4007
+ <CapColumn style={val.colStyle ? val.colStyle : {}} span={val.width} offset={val.offset}>
4008
+ <CapSelect
4009
+ id={val.id}
4010
+ options={options}
4011
+ placeholder={val.placeholder ? val.placeholder : ''}
4012
+ style={val.style ? val.style : {}}
4013
+ onSelect={(data) => this.updateFormData(data, val, 'onSelect')}
4014
+ value={isVersionEnable ? this.state.formData[this.state.currentTab - 1][val.id] : this.state.formData[val.id]}
4015
+ disabled={val.disabled}
4016
+ label={val.label}
4017
+ />
4018
+ {ifError && val.errorMessage &&
4019
+ <span className={'error'}>{val.errorMessage}</span>
4020
+ }
4021
+ </CapColumn>
4022
+ );
4023
+ }
4024
+ break;
4025
+ case "ckeditor": {
4026
+ if (this.props.schema && (this.props.schema.channel || '').toLowerCase() === 'wechat' ) {
4027
+ const content = !_.isEmpty(this.state.formData) && this.state.formData['content-value'] ? this.state.formData['content-value'] : '';
4028
+ columns.push(
4029
+ <CKEditor
4030
+ id={val.id}
4031
+ content={content}
4032
+ events={val.events}
4033
+ getEditorInstanse={(evt, id) => val.injectedEvents.getEditorInstanse.call(this, evt, id)}
4034
+ handleContentChange={(event) => this.callChildEvent(event, val, 'onContentChange')}
4035
+ channel="wechat"
4036
+ onFocusOut={(event) => this.callChildEvent(event, val, 'onFocusOut')}
4037
+ onFocusIn={(event) => this.callChildEvent(event, val, 'onFocusIn')}
4038
+ currentOrgDetails={this.props.currentOrgDetails}
4039
+ /> );
4040
+ } else {
4041
+ if (_.isEmpty(this.state.formData[`${this.state.currentTab - 1}`])) {
4042
+ return true;
4043
+ }
4044
+ let langIndex = 0;
4045
+ if (val.id.indexOf('template-content') > -1 && val.id !== 'template-content') {
4046
+ langIndex = parseInt(val.id.replace('template-content', ''), 10) - 1;
4047
+ }
4048
+
4049
+ 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) || 'en';
4050
+ const content = (!_.isEmpty(this.state.formData) && this.state.formData[`${this.state.currentTab - 1}`] && this.state.formData[`${this.state.currentTab - 1}`][currentLang] && this.state.formData[`${this.state.currentTab - 1}`][currentLang]['template-content'] || '');
4051
+ columns.push(
4052
+ <CapColumn
4053
+ style={val.colStyle ? val.colStyle : {border : ""}}
4054
+ span={val.width}
4055
+ className={(this.state.liquidErrorMessage?.LIQUID_ERROR_MSG?.length || this.state.liquidErrorMessage?.STANDARD_ERROR_MSG?.length) && this.isLiquidFlowSupportedByChannel() ? "error-boundary" : ""}
4056
+ >
4057
+ <CKEditor
4058
+ id={val.id}
4059
+ content={content}
4060
+ events={val.events}
4061
+ getEditorInstanse={(evt, id) => val.injectedEvents.getEditorInstanse.call(this, evt, id)}
4062
+ handleContentChange={(event) => this.callChildEvent(event, val, 'onContentChange')}
4063
+ currentOrgDetails={this.props.currentOrgDetails}
4064
+ />
4065
+ </CapColumn>
4066
+ );
4067
+ }
4068
+ }
4069
+ break;
4070
+ case "BEEeditor": {
4071
+ let langTab = 1;
4072
+ if (val.id.match(/BEEeditor/g)) {
4073
+ if (val.id.length > 9 && val.id.charAt(9)) {
4074
+ langTab = val.id.charAt(9);
4075
+ }
4076
+ }
4077
+ const { currentTab, formData } = this.state;
4078
+ const supportedLanguages = (formData[currentTab - 1] || {}).selectedLanguages || {};
4079
+ const currentLang = supportedLanguages[langTab - 1];
4080
+
4081
+ let beeJson = '',
4082
+ beeToken = '',
4083
+ uuid = '';
4084
+ const data = formData[`${currentTab - 1}`][currentLang];
4085
+ if (data) {
4086
+ beeJson = data[`BEEeditor${currentTab > 1 ? currentTab : ''}json`];
4087
+ beeToken = data[`BEEeditor${currentTab > 1 ? currentTab : ''}token`];
4088
+ uuid = this.props.uuid;
4089
+ }
4090
+ if (!beeJson || !beeToken) {
4091
+ return false;
4092
+ }
4093
+ let isModuleFilterEnabled = _.get(this.props.location, 'query.type', '') !== 'embedded';
4094
+ const schemaChannel = _.get(this.props.schema, 'channel', "");
4095
+ if (schemaChannel === 'EMAIL') {
4096
+ isModuleFilterEnabled = this.props.isFullMode;
4097
+ }
4098
+ columns.push(
4099
+ <CapColumn style={val.colStyle ? val.colStyle : {}} span={val.width} className={(this.state.liquidErrorMessage?.LIQUID_ERROR_MSG?.length || this.state.liquidErrorMessage?.STANDARD_ERROR_MSG?.length) && this.isLiquidFlowSupportedByChannel() ? "error-boundary" : ""}>
4100
+ <BeeEditor
4101
+ uid={uuid}
4102
+ tokenData={beeToken}
4103
+ id={val.id}
4104
+ beeJson={beeJson}
4105
+ showTagsPopover={this.showTagsPopover}
4106
+ location={this.props.location}
4107
+ label={val.label || ''}
4108
+ className={val.className || ''}
4109
+ userLocale={this.props.userLocale}
4110
+ selectedOfferDetails={this.props.selectedOfferDetails}
4111
+ tags={this.props.tags || []}
4112
+ injectedTags={this.props.injectedTags ? this.props.injectedTags : {}}
4113
+ saveBeeInstance={this.props.saveBeeInstance}
4114
+ saveBeeData={this.props.saveBeeData}
4115
+ onContextChange={this.props.onContextChange}
4116
+ moduleFilterEnabled={isModuleFilterEnabled}
4117
+ eventContextTags={this.props?.eventContextTags}
4118
+ waitEventContextTags={this.props?.waitEventContextTags}
4119
+ isGetBeeData={this.props?.isGetBeeData}
4120
+ getBEEData={this.props?.getBEEData}
4121
+ />
4122
+ </CapColumn>
4123
+ );
4124
+ }
4125
+ break;
4126
+ case "customPopover": {
4127
+ if (!this.state.formData[`${this.state.currentTab - 1}`]) {
4128
+ return true;
4129
+ }
4130
+ columns.push(
4131
+ <CapColumn style={val.colStyle ? val.colStyle : {}} span={val.width}>
4132
+ <CustomPopOver
4133
+ id={val.id}
4134
+ popOverList={this.props.supportedLanguages}
4135
+ excludeList={this.state.formData[`${this.state.currentTab - 1}`].selectedLanguages}
4136
+ handlePopOverClick={(data) => this.handleAddLanguageFlow(data, val, ADD_LANGUAGE)}
4137
+ visible={this.state.customPopoverVisible}
4138
+ onVisibleChange={this.handleCustomPopoverVisibleChange}
4139
+ />
4140
+ </CapColumn>
4141
+ );
4142
+ }
4143
+ break;
4144
+ default:
4145
+ break;
4146
+ }
4147
+ return true;
4148
+ });
4149
+ const row = (
4150
+ <CapRow useLegacy style={field.rowStyle ? field.rowStyle : {}} className={field.rowClassName ? field.rowClassName : ''}>
4151
+ {
4152
+ _.forEach(columns, (value) => {
4153
+ if (value) {
4154
+ return value;
4155
+ }
4156
+ return value;
4157
+ })
4158
+ }
4159
+ </CapRow>
4160
+ );
4161
+ if (columns.length) {
4162
+ fields.push(row);
4163
+ }
4164
+ });
4165
+
4166
+ _.forEach(section.actionFields, (field) => {
4167
+ const columns = [];
4168
+ _.forEach(field.cols, (val) => {
4169
+ const type = val.type;
4170
+ const styling = val.styling;
4171
+ switch (type) {
4172
+ case "button":
4173
+ if (styling === 'semantic' && val.metaType === 'submit-button') {
4174
+ columns.push(
4175
+ <CapColumn span={3}>
4176
+ <CapButton
4177
+ type={val.buttonType}
4178
+ icon={val.icon}
4179
+ disabled={val.disabled ? val.disabled : false}
4180
+ onClick={(data) => val.id === 'save-button' ? this.saveForm() : this.callChildEvent(data, val, val.submitAction)}>
4181
+ {val.value}
4182
+ </CapButton>
4183
+ </CapColumn>
4184
+ );
4185
+ }
4186
+ break;
4187
+ default:
4188
+ break;
4189
+ }
4190
+ });
4191
+ const row = (
4192
+ <CapRow useLegacy>
4193
+ {
4194
+ _.forEach(columns, (value) => {
4195
+ if (value) {
4196
+ return value;
4197
+ }
4198
+ return value;
4199
+ })
4200
+ }
4201
+ </CapRow>
4202
+ );
4203
+ if (columns.length) {
4204
+ fields.push(row);
4205
+ }
4206
+ });
4207
+ if (!this.props.isEmailLoading && this.props.schema.channel.toUpperCase() === EMAIL && !fields.length) {
4208
+ return (
4209
+ <div style={{ textAlign: 'center' }}>
4210
+ <CapSpin spinning />
4211
+ </div>
4212
+ );
4213
+ }
4214
+ return fields;
4215
+ }
4216
+
4217
+ //To Find from which locale user is loggedin
4218
+ getTranslationMappedLocale(locale) {
4219
+ return GET_TRANSLATION_MAPPED?.[locale];
4220
+ }
4221
+ componentDidMount() {
4222
+ const user = localStorage.getItem('user');
4223
+ let locale = 'en';
4224
+ if (user) {
4225
+ locale = JSON.parse(user).lang || locale;
4226
+ }
4227
+ locale = this.getTranslationMappedLocale(locale);
4228
+ moment.locale(locale);
4229
+ this.setState({
4230
+ translationLang: locale,
4231
+ });
4232
+ }
4233
+
4234
+ renderContainer(container) {
4235
+ const containerType = container.type;
4236
+ const renderedPanes = [];
4237
+ let renderedSections = [];
4238
+ let renderedContentSections = [];
4239
+ let paneIndex = 1;
4240
+ let panes = [];
4241
+ let result = [];
4242
+ let extraActionSections = [];
4243
+ let activeTabKey;
4244
+ let paneTabKey;
4245
+ let stateTabKey = '';
4246
+ switch (containerType) {
4247
+ case "tabs":
4248
+ panes = container.panes;
4249
+ _.forEach(panes, (pane, index) => {
4250
+ renderedSections = pane.sections.map((section) => {
4251
+ result = this.renderSection(section, paneIndex);
4252
+ return result;
4253
+ });
4254
+
4255
+ const sectionHeadeers = pane.sectionsHeaders ? pane.sectionsHeaders : container.tabContent && container.tabContent.sections;
4256
+ renderedContentSections = sectionHeadeers.map( (tabContentSection) => {
4257
+ result = this.renderSection(tabContentSection, paneIndex);
4258
+ return result;
4259
+ });
4260
+ // let newTabKey = `${_.uniqueId()}`;
4261
+ if (!this.state.formData[this.state.currentTab - 1]) {
4262
+ return;
4263
+ }
4264
+ activeTabKey = this.state.formData.tabKey;
4265
+ if (this.props.isNewVersionFlow) {
4266
+ activeTabKey = this.state.tabKey;
4267
+ }
4268
+ paneTabKey = this.state.formData[paneIndex - 1] && this.state.formData[paneIndex - 1].tabKey;
4269
+ if (this.props.isNewVersionFlow) {
4270
+ const currentLang = this.state.formData[this.state.currentTab - 1].selectedLanguages[index];
4271
+ paneTabKey = this.state.formData[this.state.currentTab - 1] && this.state.formData[this.state.currentTab - 1][currentLang] && this.state.formData[this.state.currentTab - 1][currentLang].tabKey;
4272
+ }
4273
+ const isPaneSupported = typeof pane.isSupported === "boolean" ? pane.isSupported : true; // pane.isSupported field exists only in mpush schema, in other channels it is not
4274
+ if (isPaneSupported) {
4275
+ renderedPanes.push({
4276
+ forceRender: true,
4277
+ tab: (
4278
+ <div className="form-tab-header" onInput={(e) => e.stopPropagation()}>
4279
+ { this.state.translationLang !== 'ja-JP' && (
4280
+ _.forEach(renderedContentSections, (value) => {
4281
+ if (value) {
4282
+ return value;
4283
+ }
4284
+ return value;
4285
+ }))
4286
+ }
4287
+ </div>
4288
+ ),
4289
+ key: paneTabKey && paneTabKey !== '' ? `${paneTabKey}` : index,
4290
+ content: (
4291
+ _.forEach(renderedSections, (value) => {
4292
+ if (value) {
4293
+ return value;
4294
+ }
4295
+ return value;
4296
+ })
4297
+ ),
4298
+ });
4299
+ paneIndex += 1;
4300
+ }
4301
+ });
4302
+ extraActionSections = container.tabBarExtraContent && container.tabBarExtraContent.sections ? container.tabBarExtraContent.sections.map((section) => {
4303
+ result = this.renderSection(section);
4304
+ return result;
4305
+ }) : {};
4306
+ stateTabKey = this.state.tabKey;
4307
+ if (stateTabKey && stateTabKey !== '') {
4308
+ activeTabKey = stateTabKey;
4309
+ }
4310
+ return (
4311
+ <CapTab
4312
+ className={`cap-tabs-${container.id}`}
4313
+ activeKey={activeTabKey && activeTabKey !== '' ? `${activeTabKey}` : '1'}
4314
+ defaultActiveKey={activeTabKey && activeTabKey !== '' ? `${activeTabKey}` : '1'}
4315
+ onChange={(data, e) => { this.callChildEvent(data, container, 'onTabChange', e); }}
4316
+ onEdit={(data, e) => this.onEdit(data, e)}
4317
+ style={{width: '100%'}}
4318
+ tabBarExtraContent={
4319
+ !_.isEmpty(extraActionSections) && _.forEach(extraActionSections, (value) => {
4320
+ if (value) {
4321
+ return value;
4322
+ }
4323
+ return value;
4324
+ })
4325
+ }
4326
+ panes={renderedPanes}
4327
+ />
4328
+ );
4329
+
4330
+ default:
4331
+ break;
4332
+ }
4333
+ return null;
4334
+ }
4335
+
4336
+ renderForm() {
4337
+ const schema = this.props.schema;
4338
+ const allElements = [];
4339
+ let renderedStandAloneSections;
4340
+ let renderedContainers;
4341
+ if (schema.standalone) {
4342
+ const sections = schema.standalone.sections;
4343
+ renderedStandAloneSections = sections.map((section) => {
4344
+ const result = this.renderSection(section);
4345
+ return result;
4346
+ });
4347
+ allElements.push(renderedStandAloneSections);
4348
+ }
4349
+ if (schema.containers) {
4350
+ renderedContainers = schema.containers.map((container) => {
4351
+ if (container.isActive === undefined || (container.isActive)) {
4352
+ const result = this.renderContainer(container);
4353
+ return result;
4354
+ }
4355
+ return null;
4356
+ });
4357
+
4358
+ allElements.push(renderedContainers);
4359
+ }
4360
+ const modal = this.getModal();
4361
+ allElements.push(modal);
4362
+ return allElements;
4363
+ }
4364
+
4365
+ render() {
4366
+ const cmsTemplateSelectionContent = (
4367
+ <CardGrid
4368
+ className={""}
4369
+ listItem={this.populateTemplatesList(this.props.cmsTemplates)}
4370
+ // filterContent={filterContent}
4371
+ onHoverItem={this.handleOnHoverItem}
4372
+ onItemClick={this.props.handleEdmDefaultTemplateSelection}
4373
+ colNumber={2}
4374
+ >
4375
+ </CardGrid>
4376
+ );
4377
+
4378
+
4379
+ return (
4380
+ <CapSpin spinning={Boolean((this.isLiquidFlowSupportedByChannel() && this.props.liquidExtractionInProgress) || this.props.metaDataStatus === REQUEST)} tip={this.props.intl.formatMessage(messages.liquidSpinText)} >
4381
+ <CapRow useLegacy>
4382
+ {this.props.schema && this.renderForm()}
4383
+ <SlideBox
4384
+ header={
4385
+ <h3>{this.props.intl.formatMessage(messages.layoutSelection)}</h3>
4386
+ }
4387
+ width={60}
4388
+ content={cmsTemplateSelectionContent}
4389
+ show={this.props.showEdmEmailTemplates}
4390
+ handleClose={this.props.toggleEdmEmailTemplateSelection}
4391
+ loadingText={this.props.intl.formatMessage(
4392
+ messages.loadingEDMTemplates
4393
+ )}
4394
+ isLoading={this.props.getCmsTemplatesInProgress}
4395
+ />
4396
+ {this.props.capDrawerContent && (
4397
+ <CapDrawer
4398
+ content={this.props.capDrawerContent}
4399
+ visible={this.state.isDrawerVisible}
4400
+ width={380}
4401
+ onClose={() => this.props.setDrawerVisibility(false)}
4402
+ />
4403
+ )}
4404
+ </CapRow>
4405
+ </CapSpin>
4406
+ );
4407
+ }
4408
+ }
4409
+
4410
+ FormBuilder.defaultProps = {
4411
+ isNewVersionFlow: false,
4412
+ userLocale: localStorage.getItem('jlocale') || 'en',
4413
+ showLiquidErrorInFooter: () => {},
4414
+ metaDataStatus: "",
4415
+ waitEventContextTags: {},
4416
+ isTestAndPreviewMode: false, // Default to false to maintain existing behavior
4417
+ };
4418
+
4419
+ FormBuilder.propTypes = {
4420
+ schema: PropTypes.object.isRequired,
4421
+ onSubmit: PropTypes.func.isRequired,
4422
+ onChange: PropTypes.func.isRequired,
4423
+ currentTab: PropTypes.number.isRequired,
4424
+ parent: PropTypes.object.isRequired,
4425
+ formData: PropTypes.object.isRequired,
4426
+ location: PropTypes.object.isRequired,
4427
+ tabKey: PropTypes.string.isRequired,
4428
+ tags: PropTypes.array.isRequired,
4429
+ tagModule: PropTypes.string.isRequired,
4430
+ injectedTags: PropTypes.object.isRequired,
4431
+ onFormValidityChange: PropTypes.func.isRequired,
4432
+ handleCancelModal: PropTypes.func.isRequired,
4433
+ usingTabContainer: PropTypes.bool.isRequired,
4434
+ checkValidation: PropTypes.bool.isRequired,
4435
+ onContextChange: PropTypes.func.isRequired,
4436
+ tabCount: PropTypes.number.isRequired,
4437
+ isNewVersionFlow: PropTypes.bool.isRequired,
4438
+ modal: PropTypes.object.isRequired,
4439
+ showModal: PropTypes.bool.isRequired,
4440
+ isEdit: PropTypes.bool.isRequired,
4441
+ iframeParent: PropTypes.object.isRequired,
4442
+ router: PropTypes.object.isRequired,
4443
+ baseLanguage: PropTypes.string.isRequired,
4444
+ supportedLanguages: PropTypes.array.isRequired,
4445
+ isSchemaChanged: PropTypes.bool.isRequired,
4446
+ cmsTemplates: PropTypes.array.isRequired,
4447
+ getCmsTemplatesInProgress: PropTypes.bool.isRequired,
4448
+ showEdmEmailTemplates: PropTypes.bool.isRequired,
4449
+ toggleEdmEmailTemplateSelection: PropTypes.func.isRequired,
4450
+ handleEdmDefaultTemplateSelection: PropTypes.func.isRequired,
4451
+ setModalContent: PropTypes.func.isRequired,
4452
+ addLanguageType: PropTypes.string.isRequired,
4453
+ startValidation: PropTypes.bool.isRequired,
4454
+ getValidationData: PropTypes.func.isRequired,
4455
+ saveForm: PropTypes.bool.isRequired,
4456
+ stopValidation: PropTypes.func.isRequired,
4457
+ selectedOfferDetails: PropTypes.object.isRequired,
4458
+ saveBeeInstance: PropTypes.func.isRequired,
4459
+ saveBeeData: PropTypes.func.isRequired,
4460
+ uuid: PropTypes.string.isRequired,
4461
+ type: PropTypes.string.isRequired,
4462
+ isEmailLoading: PropTypes.bool.isRequired,
4463
+ moduleType: PropTypes.string.isRequired,
4464
+ showLiquidErrorInFooter: PropTypes.bool.isRequired,
4465
+ eventContextTags: PropTypes.array.isRequired,
4466
+ waitEventContextTags: PropTypes.object,
4467
+ forwardedTags: PropTypes.object.isRequired,
4468
+ isLoyaltyModule: PropTypes.bool.isRequired,
4469
+ isTestAndPreviewMode: PropTypes.bool, // Add new prop type
4470
+ restrictPersonalization: PropTypes.bool,
4471
+ };
4472
+
4473
+ const mapStateToProps = createStructuredSelector({
4474
+ currentOrgDetails: selectCurrentOrgDetails(),
4475
+ liquidExtractionInProgress: selectLiquidStateDetails(),
4476
+ metaDataStatus: selectMetaDataStatus(),
4477
+ metaEntities: makeSelectMetaEntities(),
4478
+ });
4479
+
4480
+ function mapDispatchToProps(dispatch) {
4481
+ return {
4482
+ actions: bindActionCreators(actions, dispatch),
4483
+ };
4484
+ }
4485
+
4486
+ export default connect(mapStateToProps,mapDispatchToProps)(injectIntl(FormBuilder));
4487
+