@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.
- package/constants/unified.js +32 -0
- package/global-styles.js +1 -1
- package/package.json +1 -1
- package/services/tests/api.test.js +35 -20
- package/utils/common.js +8 -0
- package/utils/commonUtils.js +19 -1
- package/utils/rcsPayloadUtils.js +92 -0
- package/utils/templateVarUtils.js +201 -0
- package/utils/tests/rcsPayloadUtils.test.js +226 -0
- package/utils/tests/templateVarUtils.test.js +204 -0
- package/v2Components/CapActionButton/constants.js +7 -0
- package/v2Components/CapActionButton/index.js +168 -109
- package/v2Components/CapActionButton/index.scss +156 -5
- package/v2Components/CapActionButton/messages.js +19 -3
- package/v2Components/CapActionButton/tests/index.test.js +42 -18
- package/v2Components/CapImageUpload/index.js +2 -2
- package/v2Components/CapTagList/index.js +10 -0
- package/v2Components/CommonTestAndPreview/CustomValuesEditor.js +70 -48
- package/v2Components/CommonTestAndPreview/DeliverySettings/DeliverySettings.scss +8 -2
- package/v2Components/CommonTestAndPreview/DeliverySettings/ModifyDeliverySettings.js +214 -21
- package/v2Components/CommonTestAndPreview/DeliverySettings/constants.js +16 -0
- package/v2Components/CommonTestAndPreview/DeliverySettings/index.js +83 -9
- package/v2Components/CommonTestAndPreview/DeliverySettings/messages.js +30 -0
- package/v2Components/CommonTestAndPreview/DeliverySettings/utils/parseSenderDetailsResponse.js +60 -11
- package/v2Components/CommonTestAndPreview/SendTestMessage.js +10 -5
- package/v2Components/CommonTestAndPreview/UnifiedPreview/RcsPreviewContent.js +201 -23
- package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +355 -76
- package/v2Components/CommonTestAndPreview/UnifiedPreview/index.js +155 -8
- package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +11 -0
- package/v2Components/CommonTestAndPreview/constants.js +38 -2
- package/v2Components/CommonTestAndPreview/index.js +761 -222
- package/v2Components/CommonTestAndPreview/messages.js +45 -3
- package/v2Components/CommonTestAndPreview/previewApiUtils.js +59 -0
- package/v2Components/CommonTestAndPreview/sagas.js +25 -6
- package/v2Components/CommonTestAndPreview/tests/CommonTestAndPreview.addTestCustomer.test.js +1 -0
- package/v2Components/CommonTestAndPreview/tests/CustomValuesEditor.test.js +284 -267
- package/v2Components/CommonTestAndPreview/tests/DeliverySettings/ModifyDeliverySettings.test.js +231 -65
- package/v2Components/CommonTestAndPreview/tests/DeliverySettings/index.test.js +118 -5
- package/v2Components/CommonTestAndPreview/tests/DeliverySettings/utils/parseSenderDetailsResponse.test.js +341 -0
- package/v2Components/CommonTestAndPreview/tests/PreviewSection.test.js +8 -1
- package/v2Components/CommonTestAndPreview/tests/SendTestMessage.test.js +34 -13
- package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/RcsPreviewContent.test.js +281 -283
- package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/index.test.js +199 -1
- package/v2Components/CommonTestAndPreview/tests/index.test.js +133 -4
- package/v2Components/CommonTestAndPreview/tests/previewApiUtils.test.js +67 -0
- package/v2Components/CommonTestAndPreview/tests/sagas.test.js +31 -24
- package/v2Components/CommonTestAndPreview/utils.js +84 -0
- package/v2Components/FormBuilder/Classic.js +4487 -0
- package/v2Components/FormBuilder/Functional/FormBuilderShell.js +366 -0
- package/v2Components/FormBuilder/Functional/channels/registry.js +17 -0
- package/v2Components/FormBuilder/Functional/channels/sms/buildSubmitPayload.js +9 -0
- package/v2Components/FormBuilder/Functional/channels/sms/config.js +30 -0
- package/v2Components/FormBuilder/Functional/channels/sms/getEditorErrorDescriptor.js +46 -0
- package/v2Components/FormBuilder/Functional/channels/sms/getLiquidContent.js +13 -0
- package/v2Components/FormBuilder/Functional/channels/sms/index.js +22 -0
- package/v2Components/FormBuilder/Functional/channels/sms/tests/getEditorErrorDescriptor.test.js +52 -0
- package/v2Components/FormBuilder/Functional/channels/sms/tests/getLiquidContent.test.js +25 -0
- package/v2Components/FormBuilder/Functional/channels/sms/tests/validate.test.js +87 -0
- package/v2Components/FormBuilder/Functional/channels/sms/validate.js +89 -0
- package/v2Components/FormBuilder/Functional/constants.js +42 -0
- package/v2Components/FormBuilder/Functional/core/schema/fieldRegistry.js +38 -0
- package/v2Components/FormBuilder/Functional/core/schema/initializeFormState.js +85 -0
- package/v2Components/FormBuilder/Functional/core/store/formReducer.js +81 -0
- package/v2Components/FormBuilder/Functional/core/store/selectors.js +30 -0
- package/v2Components/FormBuilder/Functional/core/store/toLegacyFormData.js +91 -0
- package/v2Components/FormBuilder/Functional/index.js +26 -0
- package/v2Components/FormBuilder/Functional/layout/FieldSlot.js +59 -0
- package/v2Components/FormBuilder/Functional/layout/SchemaForm.js +31 -0
- package/v2Components/FormBuilder/Functional/layout/Section.js +116 -0
- package/v2Components/FormBuilder/Functional/renderers/smsRenderers.js +258 -0
- package/v2Components/FormBuilder/Functional/tests/channelRegistry.test.js +21 -0
- package/v2Components/FormBuilder/Functional/tests/fieldRegistry.test.js +65 -0
- package/v2Components/FormBuilder/Functional/tests/fieldSlot.test.js +97 -0
- package/v2Components/FormBuilder/Functional/tests/fixtures/smsParityCases.js +192 -0
- package/v2Components/FormBuilder/Functional/tests/formReducer.test.js +129 -0
- package/v2Components/FormBuilder/Functional/tests/initializeFormState.test.js +132 -0
- package/v2Components/FormBuilder/Functional/tests/schemaForm.test.js +40 -0
- package/v2Components/FormBuilder/Functional/tests/section.test.js +99 -0
- package/v2Components/FormBuilder/Functional/tests/selectors.test.js +67 -0
- package/v2Components/FormBuilder/Functional/tests/sms.crossFlowParity.test.js +155 -0
- package/v2Components/FormBuilder/Functional/tests/sms.liquid.test.js +172 -0
- package/v2Components/FormBuilder/Functional/tests/sms.rollout.test.js +122 -0
- package/v2Components/FormBuilder/Functional/tests/sms.shell.parity.test.js +329 -0
- package/v2Components/FormBuilder/Functional/tests/smsRenderers.test.js +160 -0
- package/v2Components/FormBuilder/Functional/tests/toLegacyFormData.test.js +95 -0
- package/v2Components/FormBuilder/_formBuilder.scss +5 -0
- package/v2Components/FormBuilder/index.js +41 -4479
- package/v2Components/FormBuilder/tests/__snapshots__/sms.characterization.test.js.snap +114 -0
- package/v2Components/FormBuilder/tests/entryGate.test.js +106 -0
- package/v2Components/FormBuilder/tests/sms.characterization.test.js +336 -0
- package/v2Components/SmsFallback/SmsFallbackLocalSelector.js +91 -0
- package/v2Components/SmsFallback/constants.js +94 -0
- package/v2Components/SmsFallback/index.js +958 -0
- package/v2Components/SmsFallback/index.scss +266 -0
- package/v2Components/SmsFallback/messages.js +78 -0
- package/v2Components/SmsFallback/smsFallbackUtils.js +120 -0
- package/v2Components/SmsFallback/tests/SmsFallbackLocalSelector.test.js +50 -0
- package/v2Components/SmsFallback/tests/rcsSmsFallback.acceptance.test.js +147 -0
- package/v2Components/SmsFallback/tests/smsFallbackHandlers.test.js +304 -0
- package/v2Components/SmsFallback/tests/smsFallbackUi.test.js +208 -0
- package/v2Components/SmsFallback/tests/smsFallbackUtils.test.js +309 -0
- package/v2Components/SmsFallback/tests/useLocalTemplateList.test.js +422 -0
- package/v2Components/SmsFallback/useLocalTemplateList.js +92 -0
- package/v2Components/TemplatePreview/_templatePreview.scss +41 -22
- package/v2Components/TemplatePreview/constants.js +2 -0
- package/v2Components/TemplatePreview/index.js +143 -31
- package/v2Components/TemplatePreview/tests/index.test.js +142 -0
- package/v2Components/TestAndPreviewSlidebox/CustomValuesEditor.js +6 -6
- package/v2Components/TestAndPreviewSlidebox/index.js +13 -1
- package/v2Components/TestAndPreviewSlidebox/sagas.js +11 -4
- package/v2Components/TestAndPreviewSlidebox/tests/saga.test.js +3 -1
- package/v2Components/VarSegmentMessageEditor/constants.js +2 -0
- package/v2Components/VarSegmentMessageEditor/index.js +125 -0
- package/v2Components/VarSegmentMessageEditor/index.scss +46 -0
- package/v2Containers/CommunicationFlow/Tests/CommunicationFlow.test.js +4 -1
- package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/DeliverySettingsSection.test.js +4 -0
- package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/SenderDetails.test.js +20 -20
- package/v2Containers/CreativesContainer/CreativesSlideBoxWrapper.js +17 -0
- package/v2Containers/CreativesContainer/SlideBoxContent.js +36 -4
- package/v2Containers/CreativesContainer/SlideBoxFooter.js +14 -5
- package/v2Containers/CreativesContainer/SlideBoxHeader.js +36 -5
- package/v2Containers/CreativesContainer/constants.js +11 -0
- package/v2Containers/CreativesContainer/embeddedSlideboxUtils.js +79 -0
- package/v2Containers/CreativesContainer/index.js +323 -104
- package/v2Containers/CreativesContainer/index.scss +83 -1
- package/v2Containers/CreativesContainer/tests/SlideBoxContent.localTemplates.test.js +90 -0
- package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +79 -34
- package/v2Containers/CreativesContainer/tests/SlideBoxHeader.test.js +79 -16
- package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +8 -0
- package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxHeader.test.js.snap +333 -90
- package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +20 -15
- package/v2Containers/CreativesContainer/tests/embeddedSlideboxUtils.test.js +258 -0
- package/v2Containers/CreativesContainer/tests/index.test.js +71 -9
- package/v2Containers/CreativesContainer/tests/useLocalTemplatesProp.test.js +125 -0
- package/v2Containers/MobilePush/Create/test/saga.test.js +2 -2
- package/v2Containers/Rcs/constants.js +131 -11
- package/v2Containers/Rcs/index.js +2602 -807
- package/v2Containers/Rcs/index.scss +289 -8
- package/v2Containers/Rcs/messages.js +34 -3
- package/v2Containers/Rcs/rcsLibraryHydrationUtils.js +227 -0
- package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +73535 -38537
- package/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap +0 -8
- package/v2Containers/Rcs/tests/index.test.js +147 -36
- package/v2Containers/Rcs/tests/mockData.js +38 -0
- package/v2Containers/Rcs/tests/rcsLibraryHydrationUtils.test.js +318 -0
- package/v2Containers/Rcs/tests/utils.test.js +646 -30
- package/v2Containers/Rcs/utils.js +478 -11
- package/v2Containers/Sms/Create/index.js +115 -48
- package/v2Containers/Sms/smsFormDataHelpers.js +67 -0
- package/v2Containers/Sms/tests/smsFormDataHelpers.test.js +253 -0
- package/v2Containers/SmsTrai/Create/index.js +9 -4
- package/v2Containers/SmsTrai/Edit/constants.js +2 -0
- package/v2Containers/SmsTrai/Edit/index.js +678 -169
- package/v2Containers/SmsTrai/Edit/index.scss +126 -0
- package/v2Containers/SmsTrai/Edit/messages.js +14 -4
- package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +5615 -3014
- package/v2Containers/SmsWrapper/index.js +37 -8
- package/v2Containers/TagList/index.js +6 -0
- package/v2Containers/Templates/TemplatesActionBar.js +101 -0
- package/v2Containers/Templates/_templates.scss +171 -12
- package/v2Containers/Templates/actions.js +11 -0
- package/v2Containers/Templates/constants.js +2 -0
- package/v2Containers/Templates/index.js +125 -55
- package/v2Containers/Templates/sagas.js +57 -13
- package/v2Containers/Templates/tests/TemplatesActionBar.test.js +120 -0
- package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +1060 -1015
- package/v2Containers/Templates/tests/sagas.test.js +199 -16
- package/v2Containers/Templates/tests/smsTemplatesListApi.test.js +180 -0
- package/v2Containers/Templates/utils/smsTemplatesListApi.js +79 -0
- package/v2Containers/TemplatesV2/TemplatesV2.style.js +72 -1
- package/v2Containers/TemplatesV2/index.js +86 -23
- package/v2Containers/TemplatesV2/tests/TemplatesV2.localTemplates.test.js +131 -0
- package/v2Containers/WeChat/MapTemplates/test/saga.test.js +9 -9
- package/v2Containers/Whatsapp/index.js +3 -20
- package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +578 -34
|
@@ -24,6 +24,7 @@ import {getMessageObject} from '../../../utils/messageUtils';
|
|
|
24
24
|
import { gtmPush } from '../../../utils/gtmTrackers';
|
|
25
25
|
import * as creativesContainerActions from '../../CreativesContainer/actions';
|
|
26
26
|
import {SMS} from '../../CreativesContainer/constants';
|
|
27
|
+
import { EMBEDDED, FULL } from '../../SmsTrai/Edit/constants';
|
|
27
28
|
// import callNativeEvent from '../../../legacy/callNativeEvent';
|
|
28
29
|
import {showError} from '../commonMethods';
|
|
29
30
|
import withCreatives from '../../../hoc/withCreatives';
|
|
@@ -33,6 +34,10 @@ import injectReducer from '../../../utils/injectReducer';
|
|
|
33
34
|
import v2SmsCreateReducer from './reducer';
|
|
34
35
|
import * as globalActions from '../../Cap/actions';
|
|
35
36
|
import TestAndPreviewSlidebox from '../../../v2Components/TestAndPreviewSlidebox';
|
|
37
|
+
import {
|
|
38
|
+
getSmsEmbeddedFooterValidity,
|
|
39
|
+
getSmsMessageFromFormData,
|
|
40
|
+
} from '../smsFormDataHelpers';
|
|
36
41
|
|
|
37
42
|
export class Create extends React.Component { // eslint-disable-line react/prefer-stateless-function
|
|
38
43
|
constructor(props) {
|
|
@@ -53,6 +58,11 @@ export class Create extends React.Component { // eslint-disable-line react/prefe
|
|
|
53
58
|
isTestAndPreviewMode: false,
|
|
54
59
|
pendingGetFormData: false,
|
|
55
60
|
};
|
|
61
|
+
// Tracks the last validity value reported to SmsFallback so componentDidUpdate
|
|
62
|
+
// does not dispatch on every render and create an infinite update loop.
|
|
63
|
+
// Intentionally undefined (not true) so the first render always reports the
|
|
64
|
+
// real validity rather than assuming the form starts invalid.
|
|
65
|
+
this._lastReportedSmsFooterInvalid = undefined;
|
|
56
66
|
this.saveFormData = this.saveFormData.bind(this);
|
|
57
67
|
this.onFormDataChange = this.onFormDataChange.bind(this);
|
|
58
68
|
this.onTemplateNameChange = this.onTemplateNameChange.bind(this);
|
|
@@ -117,7 +127,7 @@ export class Create extends React.Component { // eslint-disable-line react/prefe
|
|
|
117
127
|
// const module = this.props.location.query.module;
|
|
118
128
|
const name = this.props.route.name;
|
|
119
129
|
window.addEventListener("message", this.handleFrameTasks);
|
|
120
|
-
if (type ===
|
|
130
|
+
if (type === EMBEDDED) {
|
|
121
131
|
const response = {
|
|
122
132
|
action: 'startTemplateCreation',
|
|
123
133
|
window: name === 'view' ? 'view' : 'create',
|
|
@@ -158,20 +168,44 @@ export class Create extends React.Component { // eslint-disable-line react/prefe
|
|
|
158
168
|
const query = {
|
|
159
169
|
layout: 'SMS',
|
|
160
170
|
type: 'TAG',
|
|
161
|
-
context: this.props.location.query.type ===
|
|
162
|
-
embedded: this.props.
|
|
171
|
+
context: this.props.location.query.type === EMBEDDED ? this.props.location.query.module : 'default',
|
|
172
|
+
embedded: this.props.forceFullTagContext
|
|
173
|
+
? FULL
|
|
174
|
+
: (this.props.location.query.type === EMBEDDED ? this.props.location.query.type : FULL),
|
|
163
175
|
};
|
|
164
176
|
if (this.props.getDefaultTags) {
|
|
165
177
|
query.context = this.props.getDefaultTags;
|
|
166
178
|
}
|
|
167
179
|
this.props.globalActions.fetchSchemaForEntity(query);
|
|
168
|
-
if (this.props.location.query.type ===
|
|
180
|
+
if (this.props.location.query.type === EMBEDDED) {
|
|
169
181
|
this.showNext();
|
|
170
182
|
}
|
|
171
183
|
}
|
|
172
184
|
}
|
|
173
185
|
}
|
|
174
186
|
|
|
187
|
+
componentDidUpdate() {
|
|
188
|
+
if (!this.props.embeddedSmsFallback || typeof this.props.onEmbeddedSmsFooterValidity !== 'function') {
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
const validity = getSmsEmbeddedFooterValidity(this.state.formData, this.state.tabCount);
|
|
192
|
+
const isTemplateNameEmpty = !!validity.isTemplateNameEmpty;
|
|
193
|
+
const isMessageEmpty = !!validity.isMessageEmpty;
|
|
194
|
+
// Only dispatch when the validity tuple changes. Dispatching unconditionally caused
|
|
195
|
+
// an infinite loop: componentDidUpdate → dispatch → SmsFallback re-render →
|
|
196
|
+
// SmsCreate re-render → componentDidUpdate → ... even when state was unchanged.
|
|
197
|
+
// Tracking both fields individually (not just the combined isInvalid flag) ensures
|
|
198
|
+
// the parent receives an updated validity object when individual fields flip even
|
|
199
|
+
// while the combined invalid state stays the same (e.g. true→true with different fields).
|
|
200
|
+
if (
|
|
201
|
+
this._lastReportedSmsFooterTemplateNameEmpty === isTemplateNameEmpty &&
|
|
202
|
+
this._lastReportedSmsFooterMessageEmpty === isMessageEmpty
|
|
203
|
+
) return;
|
|
204
|
+
this._lastReportedSmsFooterTemplateNameEmpty = isTemplateNameEmpty;
|
|
205
|
+
this._lastReportedSmsFooterMessageEmpty = isMessageEmpty;
|
|
206
|
+
this.props.onEmbeddedSmsFooterValidity(validity);
|
|
207
|
+
}
|
|
208
|
+
|
|
175
209
|
componentWillUnmount() {
|
|
176
210
|
if (this.pendingGetFormDataTimeout) {
|
|
177
211
|
clearTimeout(this.pendingGetFormDataTimeout);
|
|
@@ -253,7 +287,7 @@ export class Create extends React.Component { // eslint-disable-line react/prefe
|
|
|
253
287
|
} else {
|
|
254
288
|
this.props.router.push({
|
|
255
289
|
pathname: `/sms/`,
|
|
256
|
-
query: type ===
|
|
290
|
+
query: type === EMBEDDED ? {type: EMBEDDED, module} : {module},
|
|
257
291
|
});
|
|
258
292
|
}
|
|
259
293
|
}
|
|
@@ -275,6 +309,10 @@ export class Create extends React.Component { // eslint-disable-line react/prefe
|
|
|
275
309
|
}
|
|
276
310
|
const result = {};
|
|
277
311
|
result.base = baseData;
|
|
312
|
+
/* Root field used by FormBuilder; embedded getFormSubscriptionData reads value.base */
|
|
313
|
+
if (formData['template-name'] !== undefined) {
|
|
314
|
+
result.base['template-name'] = formData['template-name'];
|
|
315
|
+
}
|
|
278
316
|
if (this.state.isValid) {
|
|
279
317
|
const msgObj = charCount.updateCharCount(baseData['sms-editor']);
|
|
280
318
|
result.base.msg_count = msgObj.msgCount;
|
|
@@ -417,7 +455,7 @@ export class Create extends React.Component { // eslint-disable-line react/prefe
|
|
|
417
455
|
// const module = this.props.location.query.module ? this.props.location.query.module : 'default';
|
|
418
456
|
// this.props.router.push({
|
|
419
457
|
// pathname: `/sms/`,
|
|
420
|
-
// query: type ===
|
|
458
|
+
// query: type === EMBEDDED ? {type: EMBEDDED, module} : {module},
|
|
421
459
|
// });
|
|
422
460
|
}
|
|
423
461
|
|
|
@@ -426,7 +464,7 @@ export class Create extends React.Component { // eslint-disable-line react/prefe
|
|
|
426
464
|
const module = this.props.location.query.module ? this.props.location.query.module : 'default';
|
|
427
465
|
this.props.router.push({
|
|
428
466
|
pathname: `/sms/`,
|
|
429
|
-
query: type ===
|
|
467
|
+
query: type === EMBEDDED ? {type: EMBEDDED, module} : {module},
|
|
430
468
|
});
|
|
431
469
|
}
|
|
432
470
|
|
|
@@ -441,7 +479,7 @@ export class Create extends React.Component { // eslint-disable-line react/prefe
|
|
|
441
479
|
|
|
442
480
|
this.setState({schema, loadingStatus: this.state.loadingStatus + 1}, () => {
|
|
443
481
|
const type = this.props.location.query.type;
|
|
444
|
-
if (type ===
|
|
482
|
+
if (type === EMBEDDED) {
|
|
445
483
|
this.removeStandAlone();
|
|
446
484
|
}
|
|
447
485
|
});
|
|
@@ -884,7 +922,9 @@ export class Create extends React.Component { // eslint-disable-line react/prefe
|
|
|
884
922
|
layout: 'SMS',
|
|
885
923
|
type: 'TAG',
|
|
886
924
|
context: (data && data.toLowerCase() === 'all') ? 'default' : (data && data.toLowerCase()),
|
|
887
|
-
embedded: this.props.
|
|
925
|
+
embedded: this.props.forceFullTagContext
|
|
926
|
+
? FULL
|
|
927
|
+
: (this.props.location.query.type === EMBEDDED ? this.props.location.query.type : FULL),
|
|
888
928
|
};
|
|
889
929
|
this.props.globalActions.fetchSchemaForEntity(query);
|
|
890
930
|
}
|
|
@@ -892,11 +932,29 @@ export class Create extends React.Component { // eslint-disable-line react/prefe
|
|
|
892
932
|
removeStandAlone() {
|
|
893
933
|
const schema = _.cloneDeep(this.state.schema);
|
|
894
934
|
const childSections = _.get(schema, 'standalone.sections[0].childSections');
|
|
895
|
-
childSections
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
935
|
+
if (childSections) {
|
|
936
|
+
/* In-form Save / Test row (index 2) only exists when the schema has > 2 sections. */
|
|
937
|
+
if (childSections.length > 2) {
|
|
938
|
+
childSections.splice(2, 1);
|
|
939
|
+
}
|
|
940
|
+
/*
|
|
941
|
+
* Creatives library drops the standalone template-name block because `SlideBoxHeader`
|
|
942
|
+
* shows the name. This is independent of the section count — guard it separately so
|
|
943
|
+
* it still runs even when childSections.length <= 2 (e.g. schema arrives pre-trimmed).
|
|
944
|
+
* RCS SMS fallback uses the same slidebox footer but keeps the name in the form.
|
|
945
|
+
*/
|
|
946
|
+
if (!this.props.embeddedSmsFallback) {
|
|
947
|
+
const fields = _.get(childSections, '[1].childSections[0].childSections');
|
|
948
|
+
if (fields && fields.length > 0) {
|
|
949
|
+
fields.splice(0, 1);
|
|
950
|
+
_.set(childSections, '[1].childSections[0].childSections', fields);
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
_.set(schema, 'standalone.sections[0].childSections', childSections);
|
|
954
|
+
}
|
|
955
|
+
// Always increment loadingStatus — isSmsLoading() requires >= 2 in library mode
|
|
956
|
+
// (isFullMode=false). The early return previously skipped this, leaving the
|
|
957
|
+
// spinner stuck forever when the schema had <= 2 childSections.
|
|
900
958
|
this.setState({ schema, loadingStatus: this.state.loadingStatus + 1 });
|
|
901
959
|
}
|
|
902
960
|
|
|
@@ -941,37 +999,8 @@ export class Create extends React.Component { // eslint-disable-line react/prefe
|
|
|
941
999
|
this.setState({startValidation: false});
|
|
942
1000
|
}
|
|
943
1001
|
|
|
944
|
-
getTemplateContent = () =>
|
|
945
|
-
|
|
946
|
-
if (!this.state.formData || !Array.isArray(this.state.formData) || this.state.formData.length === 0) {
|
|
947
|
-
return '';
|
|
948
|
-
}
|
|
949
|
-
const currentTabData = this.state.formData[this.state.currentTab - 1];
|
|
950
|
-
if (!currentTabData) {
|
|
951
|
-
return '';
|
|
952
|
-
}
|
|
953
|
-
|
|
954
|
-
// PRIORITY 1: Check direct path first (most common for SMS)
|
|
955
|
-
// This handles: formData[0]['sms-editor']
|
|
956
|
-
if (currentTabData['sms-editor']) {
|
|
957
|
-
return currentTabData['sms-editor'];
|
|
958
|
-
}
|
|
959
|
-
|
|
960
|
-
// PRIORITY 2: Check activeTab structure (for versioned templates)
|
|
961
|
-
// This handles: formData[0][activeTab]['sms-editor']
|
|
962
|
-
const activeTab = currentTabData?.activeTab || 'base';
|
|
963
|
-
if (currentTabData[activeTab]?.['sms-editor']) {
|
|
964
|
-
return currentTabData[activeTab]['sms-editor'];
|
|
965
|
-
}
|
|
966
|
-
|
|
967
|
-
// PRIORITY 3: Check base explicitly (fallback)
|
|
968
|
-
// This handles: formData[0]['base']['sms-editor']
|
|
969
|
-
if (currentTabData['base']?.['sms-editor']) {
|
|
970
|
-
return currentTabData['base']['sms-editor'];
|
|
971
|
-
}
|
|
972
|
-
|
|
973
|
-
return '';
|
|
974
|
-
}
|
|
1002
|
+
getTemplateContent = () =>
|
|
1003
|
+
getSmsMessageFromFormData(this.state.formData, this.state.currentTab);
|
|
975
1004
|
|
|
976
1005
|
handleTestAndPreview = () => {
|
|
977
1006
|
// If parent is managing state (props exist), call parent handler
|
|
@@ -1000,6 +1029,35 @@ export class Create extends React.Component { // eslint-disable-line react/prefe
|
|
|
1000
1029
|
}
|
|
1001
1030
|
|
|
1002
1031
|
saveFormData() {
|
|
1032
|
+
/*
|
|
1033
|
+
* RCS SMS fallback slidebox (embeddedSmsFallback): parent passes isFullMode from RCS, but we must not
|
|
1034
|
+
* call createTemplate — that spins CapSpin on createTemplateInProgress and is not the embedded contract.
|
|
1035
|
+
* Same as library: hand off form payload via getFormSubscriptionData.
|
|
1036
|
+
*/
|
|
1037
|
+
if (this.props.embeddedSmsFallback && this.props.getFormSubscriptionData) {
|
|
1038
|
+
const { isTemplateNameEmpty, isMessageEmpty } = getSmsEmbeddedFooterValidity(
|
|
1039
|
+
this.state.formData,
|
|
1040
|
+
this.state.tabCount,
|
|
1041
|
+
);
|
|
1042
|
+
if (isTemplateNameEmpty || isMessageEmpty) {
|
|
1043
|
+
this.setState({ startValidation: true, pendingGetFormData: false });
|
|
1044
|
+
if (this.props.onValidationFail) {
|
|
1045
|
+
this.props.onValidationFail();
|
|
1046
|
+
}
|
|
1047
|
+
return;
|
|
1048
|
+
}
|
|
1049
|
+
const payload = this.getFormData();
|
|
1050
|
+
if (!payload.validity) {
|
|
1051
|
+
if (this.props.onValidationFail) {
|
|
1052
|
+
this.props.onValidationFail();
|
|
1053
|
+
}
|
|
1054
|
+
this.setState({ pendingGetFormData: false, startValidation: false });
|
|
1055
|
+
return;
|
|
1056
|
+
}
|
|
1057
|
+
this.props.getFormSubscriptionData(payload);
|
|
1058
|
+
this.setState({ pendingGetFormData: false, startValidation: false });
|
|
1059
|
+
return;
|
|
1060
|
+
}
|
|
1003
1061
|
// In library mode: FormBuilder calls onSubmit only after liquid validation succeeds.
|
|
1004
1062
|
// Submit to parent here so the slidebox can close with valid data.
|
|
1005
1063
|
if (!this.props.isFullMode) {
|
|
@@ -1052,7 +1110,7 @@ export class Create extends React.Component { // eslint-disable-line react/prefe
|
|
|
1052
1110
|
const spinning = this.isSmsLoading();
|
|
1053
1111
|
let tags = this.props.metaEntities && this.props.metaEntities.tags && this.props.metaEntities.tags.standard ? this.props.metaEntities.tags.standard : [];
|
|
1054
1112
|
|
|
1055
|
-
if (this.props.location.query.type ===
|
|
1113
|
+
if (this.props.location.query.type === EMBEDDED && this.props.location.query.module === 'library' && !this.props.getDefaultTags) {
|
|
1056
1114
|
tags = this.props.supportedTags; // if get default tags is not present and supported tags is present use that inlbrary mode
|
|
1057
1115
|
}
|
|
1058
1116
|
return (
|
|
@@ -1099,7 +1157,9 @@ export class Create extends React.Component { // eslint-disable-line react/prefe
|
|
|
1099
1157
|
onTestContentClicked={this.props.onTestContentClicked}
|
|
1100
1158
|
onPreviewContentClicked={this.props.onPreviewContentClicked}
|
|
1101
1159
|
eventContextTags={this.props?.eventContextTags}
|
|
1102
|
-
|
|
1160
|
+
tagListGetPopupContainer={this.props.tagListGetPopupContainer}
|
|
1161
|
+
tagListPopoverOverlayStyle={this.props.tagListPopoverOverlayStyle}
|
|
1162
|
+
tagListPopoverOverlayClassName={this.props.tagListPopoverOverlayClassName}
|
|
1103
1163
|
/>
|
|
1104
1164
|
</CapColumn>
|
|
1105
1165
|
</CapRow>
|
|
@@ -1112,6 +1172,7 @@ export class Create extends React.Component { // eslint-disable-line react/prefe
|
|
|
1112
1172
|
formData={this.state.formData}
|
|
1113
1173
|
content={this.getTemplateContent()}
|
|
1114
1174
|
currentChannel={SMS}
|
|
1175
|
+
smsRegister={this.props.smsRegister}
|
|
1115
1176
|
/>
|
|
1116
1177
|
</div>
|
|
1117
1178
|
);
|
|
@@ -1136,11 +1197,17 @@ Create.propTypes = {
|
|
|
1136
1197
|
isLoadingMetaEntities: PropTypes.bool,
|
|
1137
1198
|
selectedOfferDetails: PropTypes.array,
|
|
1138
1199
|
eventContextTags: PropTypes.array,
|
|
1139
|
-
waitEventContextTags: PropTypes.object,
|
|
1140
1200
|
showTestAndPreviewSlidebox: PropTypes.bool,
|
|
1141
1201
|
handleTestAndPreview: PropTypes.func,
|
|
1142
1202
|
handleCloseTestAndPreview: PropTypes.func,
|
|
1143
1203
|
isTestAndPreviewMode: PropTypes.bool,
|
|
1204
|
+
smsRegister: PropTypes.any,
|
|
1205
|
+
forceFullTagContext: PropTypes.bool,
|
|
1206
|
+
embeddedSmsFallback: PropTypes.bool,
|
|
1207
|
+
onEmbeddedSmsFooterValidity: PropTypes.func,
|
|
1208
|
+
tagListGetPopupContainer: PropTypes.func,
|
|
1209
|
+
tagListPopoverOverlayStyle: PropTypes.object,
|
|
1210
|
+
tagListPopoverOverlayClassName: PropTypes.string,
|
|
1144
1211
|
};
|
|
1145
1212
|
|
|
1146
1213
|
const mapStateToProps = createStructuredSelector({
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared SMS FormBuilder formData helpers for Sms/Create (and any embedded host).
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @param {object} formData FormBuilder state (same shape as Sms/Create `this.state.formData`)
|
|
7
|
+
* @param {number} currentTab 1-based tab index
|
|
8
|
+
* @returns {string} Raw message body or ''
|
|
9
|
+
*/
|
|
10
|
+
export function getSmsMessageFromFormData(formData, currentTab) {
|
|
11
|
+
if (formData == null || typeof formData !== 'object') {
|
|
12
|
+
return '';
|
|
13
|
+
}
|
|
14
|
+
const tab = currentTab != null && currentTab > 0 ? currentTab : 1;
|
|
15
|
+
const currentTabData = formData[tab - 1];
|
|
16
|
+
if (currentTabData && typeof currentTabData === 'object') {
|
|
17
|
+
const versionedKey = tab > 1 ? `sms-editor${tab}` : 'sms-editor';
|
|
18
|
+
if (Object.prototype.hasOwnProperty.call(currentTabData, versionedKey)) {
|
|
19
|
+
const versionedContent = currentTabData[versionedKey];
|
|
20
|
+
// Key exists — commit to this version's value rather than falling through to base.
|
|
21
|
+
// Treat null/undefined as empty so a cleared version correctly reports as empty.
|
|
22
|
+
return (versionedContent != null && versionedContent !== '') ? String(versionedContent) : '';
|
|
23
|
+
}
|
|
24
|
+
if (currentTabData['sms-editor'] != null) {
|
|
25
|
+
return String(currentTabData['sms-editor']);
|
|
26
|
+
}
|
|
27
|
+
const activeTab = currentTabData.activeTab || 'base';
|
|
28
|
+
if (currentTabData[activeTab]?.['sms-editor'] != null) {
|
|
29
|
+
return String(currentTabData[activeTab]['sms-editor']);
|
|
30
|
+
}
|
|
31
|
+
if (currentTabData.base?.['sms-editor'] != null) {
|
|
32
|
+
return String(currentTabData.base['sms-editor']);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
const rootBase = formData.base;
|
|
36
|
+
if (rootBase && typeof rootBase === 'object' && rootBase['sms-editor'] != null) {
|
|
37
|
+
return String(rootBase['sms-editor']);
|
|
38
|
+
}
|
|
39
|
+
return '';
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* @param {number} [tabCount] Total number of versions/tabs. When >1 all versions are checked.
|
|
44
|
+
* @returns {{ isTemplateNameEmpty: boolean, isMessageEmpty: boolean }}
|
|
45
|
+
*/
|
|
46
|
+
export function getSmsEmbeddedFooterValidity(formData, tabCount) {
|
|
47
|
+
const rawName = formData?.['template-name'];
|
|
48
|
+
const name = rawName != null && rawName !== '' ? String(rawName).trim() : '';
|
|
49
|
+
|
|
50
|
+
// Check ALL versions: if any version's message is empty, Done should be disabled.
|
|
51
|
+
// With a single version this is equivalent to the previous single-tab check.
|
|
52
|
+
const count = tabCount != null && tabCount > 1 ? tabCount : 1;
|
|
53
|
+
let isMessageEmpty = false;
|
|
54
|
+
for (let i = 1; i <= count; i++) {
|
|
55
|
+
const content = getSmsMessageFromFormData(formData, i);
|
|
56
|
+
const msg = content != null && content !== '' ? String(content).trim() : '';
|
|
57
|
+
if (!msg) {
|
|
58
|
+
isMessageEmpty = true;
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
isTemplateNameEmpty: !name,
|
|
65
|
+
isMessageEmpty,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getSmsMessageFromFormData,
|
|
3
|
+
getSmsEmbeddedFooterValidity,
|
|
4
|
+
} from '../smsFormDataHelpers';
|
|
5
|
+
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
// getSmsMessageFromFormData
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
|
|
10
|
+
describe('getSmsMessageFromFormData', () => {
|
|
11
|
+
describe('null / invalid formData guard', () => {
|
|
12
|
+
it('returns empty string when formData is null', () => {
|
|
13
|
+
expect(getSmsMessageFromFormData(null, 1)).toBe('');
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('returns empty string when formData is undefined', () => {
|
|
17
|
+
expect(getSmsMessageFromFormData(undefined, 1)).toBe('');
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('returns empty string when formData is a string (not an object)', () => {
|
|
21
|
+
expect(getSmsMessageFromFormData('bad', 1)).toBe('');
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('returns empty string when formData is a number', () => {
|
|
25
|
+
expect(getSmsMessageFromFormData(42, 1)).toBe('');
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
describe('currentTab normalisation', () => {
|
|
30
|
+
it('defaults to tab 1 when currentTab is null', () => {
|
|
31
|
+
const formData = { 0: { 'sms-editor': 'hello' } };
|
|
32
|
+
expect(getSmsMessageFromFormData(formData, null)).toBe('hello');
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('defaults to tab 1 when currentTab is undefined', () => {
|
|
36
|
+
const formData = { 0: { 'sms-editor': 'hello' } };
|
|
37
|
+
expect(getSmsMessageFromFormData(formData, undefined)).toBe('hello');
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('defaults to tab 1 when currentTab is 0', () => {
|
|
41
|
+
const formData = { 0: { 'sms-editor': 'hello' } };
|
|
42
|
+
expect(getSmsMessageFromFormData(formData, 0)).toBe('hello');
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('defaults to tab 1 when currentTab is negative', () => {
|
|
46
|
+
const formData = { 0: { 'sms-editor': 'hello' } };
|
|
47
|
+
expect(getSmsMessageFromFormData(formData, -5)).toBe('hello');
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
describe('versioned key lookup', () => {
|
|
52
|
+
it('uses "sms-editor" key for tab 1', () => {
|
|
53
|
+
const formData = { 0: { 'sms-editor': 'tab1 msg' } };
|
|
54
|
+
expect(getSmsMessageFromFormData(formData, 1)).toBe('tab1 msg');
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('uses "sms-editor2" versioned key for tab 2', () => {
|
|
58
|
+
const formData = { 1: { 'sms-editor2': 'tab2 msg', 'sms-editor': 'fallback' } };
|
|
59
|
+
expect(getSmsMessageFromFormData(formData, 2)).toBe('tab2 msg');
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('uses "sms-editor3" versioned key for tab 3', () => {
|
|
63
|
+
const formData = { 2: { 'sms-editor3': 'tab3 msg' } };
|
|
64
|
+
expect(getSmsMessageFromFormData(formData, 3)).toBe('tab3 msg');
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('returns empty string when versioned key exists but is null (cleared version)', () => {
|
|
68
|
+
const formData = { 1: { 'sms-editor2': null } };
|
|
69
|
+
expect(getSmsMessageFromFormData(formData, 2)).toBe('');
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('returns empty string when versioned key exists but is empty string', () => {
|
|
73
|
+
const formData = { 1: { 'sms-editor2': '' } };
|
|
74
|
+
expect(getSmsMessageFromFormData(formData, 2)).toBe('');
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('coerces a numeric value in versioned key to string', () => {
|
|
78
|
+
const formData = { 1: { 'sms-editor2': 12345 } };
|
|
79
|
+
expect(getSmsMessageFromFormData(formData, 2)).toBe('12345');
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
describe('"sms-editor" flat key fallback (versioned key absent)', () => {
|
|
84
|
+
it('falls back to "sms-editor" when versioned key is absent for tab 2', () => {
|
|
85
|
+
const formData = { 1: { 'sms-editor': 'flat fallback' } };
|
|
86
|
+
expect(getSmsMessageFromFormData(formData, 2)).toBe('flat fallback');
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
describe('activeTab nested fallback', () => {
|
|
91
|
+
it('falls back to activeTab["sms-editor"] when neither versioned key nor flat "sms-editor" is present', () => {
|
|
92
|
+
// The versioned key must be absent entirely for the activeTab fallback to be reached.
|
|
93
|
+
// (If 'sms-editor' exists but is null, the function commits to '' without falling through.)
|
|
94
|
+
const formData = {
|
|
95
|
+
0: {
|
|
96
|
+
activeTab: 'variant1',
|
|
97
|
+
variant1: { 'sms-editor': 'variant msg' },
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
expect(getSmsMessageFromFormData(formData, 1)).toBe('variant msg');
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('falls back to base["sms-editor"] when activeTab entry lacks "sms-editor" and flat key is absent', () => {
|
|
104
|
+
const formData = {
|
|
105
|
+
0: {
|
|
106
|
+
activeTab: 'variant1',
|
|
107
|
+
variant1: {},
|
|
108
|
+
base: { 'sms-editor': 'base msg' },
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
expect(getSmsMessageFromFormData(formData, 1)).toBe('base msg');
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('defaults activeTab to "base" when activeTab prop is not set and flat key is absent', () => {
|
|
115
|
+
const formData = {
|
|
116
|
+
0: {
|
|
117
|
+
base: { 'sms-editor': 'default base msg' },
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
expect(getSmsMessageFromFormData(formData, 1)).toBe('default base msg');
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
describe('root formData.base fallback', () => {
|
|
125
|
+
it('falls back to formData.base["sms-editor"] when tab slot is missing', () => {
|
|
126
|
+
const formData = {
|
|
127
|
+
base: { 'sms-editor': 'root base msg' },
|
|
128
|
+
};
|
|
129
|
+
expect(getSmsMessageFromFormData(formData, 1)).toBe('root base msg');
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('returns empty string when even root base has no "sms-editor"', () => {
|
|
133
|
+
const formData = { base: {} };
|
|
134
|
+
expect(getSmsMessageFromFormData(formData, 1)).toBe('');
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it('returns empty string when formData has no relevant keys at all', () => {
|
|
138
|
+
expect(getSmsMessageFromFormData({}, 1)).toBe('');
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
// ---------------------------------------------------------------------------
|
|
144
|
+
// getSmsEmbeddedFooterValidity
|
|
145
|
+
// ---------------------------------------------------------------------------
|
|
146
|
+
|
|
147
|
+
describe('getSmsEmbeddedFooterValidity', () => {
|
|
148
|
+
describe('isTemplateNameEmpty', () => {
|
|
149
|
+
it('is true when template-name is absent', () => {
|
|
150
|
+
const formData = { 0: { 'sms-editor': 'msg' } };
|
|
151
|
+
const { isTemplateNameEmpty } = getSmsEmbeddedFooterValidity(formData, 1);
|
|
152
|
+
expect(isTemplateNameEmpty).toBe(true);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it('is true when template-name is empty string', () => {
|
|
156
|
+
const formData = { 'template-name': '', 0: { 'sms-editor': 'msg' } };
|
|
157
|
+
const { isTemplateNameEmpty } = getSmsEmbeddedFooterValidity(formData, 1);
|
|
158
|
+
expect(isTemplateNameEmpty).toBe(true);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it('is true when template-name is whitespace only', () => {
|
|
162
|
+
const formData = { 'template-name': ' ', 0: { 'sms-editor': 'msg' } };
|
|
163
|
+
const { isTemplateNameEmpty } = getSmsEmbeddedFooterValidity(formData, 1);
|
|
164
|
+
expect(isTemplateNameEmpty).toBe(true);
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it('is false when template-name is a non-empty string', () => {
|
|
168
|
+
const formData = { 'template-name': 'My Template', 0: { 'sms-editor': 'msg' } };
|
|
169
|
+
const { isTemplateNameEmpty } = getSmsEmbeddedFooterValidity(formData, 1);
|
|
170
|
+
expect(isTemplateNameEmpty).toBe(false);
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
describe('isMessageEmpty — single tab', () => {
|
|
175
|
+
it('is true when message is empty string', () => {
|
|
176
|
+
const formData = { 'template-name': 'T', 0: { 'sms-editor': '' } };
|
|
177
|
+
const { isMessageEmpty } = getSmsEmbeddedFooterValidity(formData, 1);
|
|
178
|
+
expect(isMessageEmpty).toBe(true);
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it('is true when message is whitespace only', () => {
|
|
182
|
+
const formData = { 'template-name': 'T', 0: { 'sms-editor': ' ' } };
|
|
183
|
+
const { isMessageEmpty } = getSmsEmbeddedFooterValidity(formData, 1);
|
|
184
|
+
expect(isMessageEmpty).toBe(true);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
it('is true when no sms-editor key at all', () => {
|
|
188
|
+
const formData = { 'template-name': 'T', 0: {} };
|
|
189
|
+
const { isMessageEmpty } = getSmsEmbeddedFooterValidity(formData, 1);
|
|
190
|
+
expect(isMessageEmpty).toBe(true);
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it('is false when message is non-empty', () => {
|
|
194
|
+
const formData = { 'template-name': 'T', 0: { 'sms-editor': 'Hello world' } };
|
|
195
|
+
const { isMessageEmpty } = getSmsEmbeddedFooterValidity(formData, 1);
|
|
196
|
+
expect(isMessageEmpty).toBe(false);
|
|
197
|
+
});
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
describe('isMessageEmpty — multiple tabs', () => {
|
|
201
|
+
it('is false when all tabs have non-empty messages', () => {
|
|
202
|
+
const formData = {
|
|
203
|
+
'template-name': 'T',
|
|
204
|
+
0: { 'sms-editor': 'Tab 1 message' },
|
|
205
|
+
1: { 'sms-editor2': 'Tab 2 message' },
|
|
206
|
+
2: { 'sms-editor3': 'Tab 3 message' },
|
|
207
|
+
};
|
|
208
|
+
const { isMessageEmpty } = getSmsEmbeddedFooterValidity(formData, 3);
|
|
209
|
+
expect(isMessageEmpty).toBe(false);
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
it('is true when the second tab has an empty message', () => {
|
|
213
|
+
const formData = {
|
|
214
|
+
'template-name': 'T',
|
|
215
|
+
0: { 'sms-editor': 'Tab 1 message' },
|
|
216
|
+
1: { 'sms-editor2': '' },
|
|
217
|
+
};
|
|
218
|
+
const { isMessageEmpty } = getSmsEmbeddedFooterValidity(formData, 2);
|
|
219
|
+
expect(isMessageEmpty).toBe(true);
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
it('is true when the third tab has no message', () => {
|
|
223
|
+
const formData = {
|
|
224
|
+
'template-name': 'T',
|
|
225
|
+
0: { 'sms-editor': 'msg1' },
|
|
226
|
+
1: { 'sms-editor2': 'msg2' },
|
|
227
|
+
2: {},
|
|
228
|
+
};
|
|
229
|
+
const { isMessageEmpty } = getSmsEmbeddedFooterValidity(formData, 3);
|
|
230
|
+
expect(isMessageEmpty).toBe(true);
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
it('treats tabCount=1 as single-tab check even when not explicitly provided', () => {
|
|
234
|
+
const formData = { 'template-name': 'T', 0: { 'sms-editor': 'msg' } };
|
|
235
|
+
const { isMessageEmpty } = getSmsEmbeddedFooterValidity(formData);
|
|
236
|
+
expect(isMessageEmpty).toBe(false);
|
|
237
|
+
});
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
describe('both fields together', () => {
|
|
241
|
+
it('returns both empty when template name absent and message empty', () => {
|
|
242
|
+
const formData = { 0: { 'sms-editor': '' } };
|
|
243
|
+
const result = getSmsEmbeddedFooterValidity(formData, 1);
|
|
244
|
+
expect(result).toEqual({ isTemplateNameEmpty: true, isMessageEmpty: true });
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
it('returns both non-empty when template name and message are present', () => {
|
|
248
|
+
const formData = { 'template-name': 'My Template', 0: { 'sms-editor': 'Hello!' } };
|
|
249
|
+
const result = getSmsEmbeddedFooterValidity(formData, 1);
|
|
250
|
+
expect(result).toEqual({ isTemplateNameEmpty: false, isMessageEmpty: false });
|
|
251
|
+
});
|
|
252
|
+
});
|
|
253
|
+
});
|
|
@@ -70,6 +70,7 @@ export const SmsTraiCreate = (props) => {
|
|
|
70
70
|
onCreateComplete,
|
|
71
71
|
isFullMode,
|
|
72
72
|
onShowTemplates,
|
|
73
|
+
embeddedSmsFallback,
|
|
73
74
|
traiSms: {
|
|
74
75
|
duplicateDetails = {},
|
|
75
76
|
duplicateDetailsError = '',
|
|
@@ -659,9 +660,11 @@ export const SmsTraiCreate = (props) => {
|
|
|
659
660
|
|
|
660
661
|
const createCallback = ({ errorMessage }) => {
|
|
661
662
|
if (!errorMessage) {
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
663
|
+
if (!embeddedSmsFallback) {
|
|
664
|
+
CapNotification.success({
|
|
665
|
+
message: formatMessage(messages.smsCreateNotification),
|
|
666
|
+
});
|
|
667
|
+
}
|
|
665
668
|
actions.clearCreateResponse();
|
|
666
669
|
} else {
|
|
667
670
|
CapNotification.error({
|
|
@@ -741,7 +744,9 @@ export const SmsTraiCreate = (props) => {
|
|
|
741
744
|
{ templates: savedData },
|
|
742
745
|
(resp, errorMessage) => {
|
|
743
746
|
createCallback({ errorMessage });
|
|
744
|
-
if (
|
|
747
|
+
if (embeddedSmsFallback) {
|
|
748
|
+
if (!errorMessage) onCreateComplete(savedData);
|
|
749
|
+
} else if (isFullMode) {
|
|
745
750
|
onCreateComplete();
|
|
746
751
|
} else {
|
|
747
752
|
onShowTemplates();
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
export const CHARLIMIT = 40;
|
|
8
|
+
/** Display / soft cap label for DLT SMS body length (matches product UI). */
|
|
9
|
+
export const SMS_TRAI_CONTENT_MAX_LENGTH = 1024;
|
|
8
10
|
export const SMS = 'SMS';
|
|
9
11
|
export const SMS_TRAI_VAR = '{#var#}';
|
|
10
12
|
export const TAG = 'TAG';
|