@capillarytech/creatives-library 7.10.9 → 7.10.11

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.
@@ -22,12 +22,14 @@ describe('Creatives SmsTraiCreate test/>', () => {
22
22
  const clearCreateResponse = jest.fn();
23
23
  const createSMSTRAITemplates = jest.fn();
24
24
  const onCreateComplete = jest.fn();
25
+ const onShowTemplates = jest.fn();
25
26
  const setAttribute = jest.fn();
26
27
  beforeEach(() => {
27
28
  renderedComponent = mountWithIntl(
28
29
  <SmsTraiCreate
29
30
  actions={{ clearCreateResponse, createSMSTRAITemplates }}
30
31
  onCreateComplete={onCreateComplete}
32
+ onShowTemplates={onShowTemplates}
31
33
  />,
32
34
  );
33
35
  });
@@ -60,6 +62,9 @@ describe('Creatives SmsTraiCreate test/>', () => {
60
62
 
61
63
  it('valid file drop', async () => {
62
64
  const event = { target: { setAttribute } };
65
+ createSMSTRAITemplates.mockImplementation((obj, callback) => {
66
+ callback();
67
+ })
63
68
  renderHelper(mockData.validFile);
64
69
  const dropZone = renderedComponent.find('Dropzone').props();
65
70
  await dropZone.onDrop([file]);
@@ -70,6 +75,11 @@ describe('Creatives SmsTraiCreate test/>', () => {
70
75
  );
71
76
  renderedComponent.find('a.download-issues-csv-link').props().onClick(event);
72
77
  expect(setAttribute).toHaveBeenCalledWith('download', DOWNLOAD_ISSUES_CSV);
78
+ renderedComponent.setProps({ isFullMode: true });
79
+ renderedComponent.find('button.done-button').props().onClick();
80
+ renderedComponent.update();
81
+ expect(renderedComponent).toMatchSnapshot();
82
+ renderedComponent.setProps({ isFullMode: false });
73
83
  renderedComponent.find('button.done-button').props().onClick();
74
84
  renderedComponent.update();
75
85
  expect(renderedComponent).toMatchSnapshot();
@@ -24,7 +24,7 @@ export const mockData = {
24
24
  'CONSENT TYPE',
25
25
  ],
26
26
  [
27
- "'1107160277332377515'",
27
+ "12345678912345678901",
28
28
  '--',
29
29
  'CAP71871_12',
30
30
  'Service-Explicit',
@@ -49,7 +49,7 @@ export const mockData = {
49
49
  '07/10/20 17:52',
50
50
  '13/10/20 21:55',
51
51
  '',
52
- 'Active',
52
+ '',
53
53
  'DUSSEHRA DEALS@VISHAL\n\nBUY1GET1 FREE\n{#var#}\n{#var#}\n{#var#}\n{#var#}\n+\n{#var#}\n{#var#}\n{#var#}\nTC',
54
54
  'Explicit',
55
55
  ],
@@ -13,6 +13,9 @@ import CapLabel from '@capillarytech/cap-ui-library/CapLabel';
13
13
  import CapHeading from '@capillarytech/cap-ui-library/CapHeading';
14
14
  import CapSpin from '@capillarytech/cap-ui-library/CapSpin';
15
15
  import CapNotification from '@capillarytech/cap-ui-library/CapNotification';
16
+ import CapError from '@capillarytech/cap-ui-library/CapError';
17
+ import CapCheckbox from '@capillarytech/cap-ui-library/CapCheckbox';
18
+ import CapInfoNote from '@capillarytech/cap-ui-library/CapInfoNote';
16
19
  import {
17
20
  CAP_SPACE_16,
18
21
  CAP_SPACE_12,
@@ -24,12 +27,12 @@ import {
24
27
  } from '@capillarytech/cap-ui-library/styled/variables';
25
28
  import { makeSelectTemplateDetailsResponse } from '../../Sms/Edit/selectors';
26
29
  import { makeSelectMetaEntities, setInjectedTags } from '../../Cap/selectors';
27
- import * as actions from '../../Sms/Edit/actions';
30
+ import * as smsEditActions from '../../Sms/Edit/actions';
28
31
  import messages from './messages';
29
32
  import TagList from '../../TagList';
30
33
  import TemplatePreview from '../../../v2Components/TemplatePreview';
31
34
  import withCreatives from '../../../hoc/withCreatives';
32
-
35
+ import { validateTags } from '../../../utils/tagValidations';
33
36
  import {
34
37
  CHARLIMIT,
35
38
  SMS,
@@ -41,9 +44,13 @@ import {
41
44
  ALL,
42
45
  LIBRARY,
43
46
  } from './constants';
47
+
44
48
  let varMap = {};
49
+ let traiData = {};
50
+ let tagValidationResponse = {};
45
51
  const { TextArea } = CapInput;
46
52
  const { CapLabelInline } = CapLabel;
53
+
47
54
  export const SmsTraiEdit = (props) => {
48
55
  const {
49
56
  intl,
@@ -58,6 +65,9 @@ export const SmsTraiEdit = (props) => {
58
65
  metaEntities,
59
66
  injectedTags,
60
67
  onCreateComplete,
68
+ isFullMode,
69
+ getFormSubscriptionData,
70
+ templateData = {},
61
71
  } = props || {};
62
72
  const { formatMessage } = intl;
63
73
  const [loading, updateLoading] = useState(true);
@@ -66,7 +76,10 @@ export const SmsTraiEdit = (props) => {
66
76
  const [tags, updateTags] = useState([]);
67
77
  const [textAreaId, updateTextAreaId] = useState();
68
78
  const [isValidationError, updateIsValidationError] = useState(false);
69
- let totalVarCount = 0;
79
+ const [isTagValidationError, updateIsTagValidationError] = useState(false);
80
+ const [totalMessageLength, setTotalMessageLength] = useState(0);
81
+ const [isUnicodeAllowed, updateIsUnicodeAllowed] = useState(true);
82
+ const [showMsgLengthNote, updateshowMsgLengthNote] = useState(false);
70
83
  const SMSTraiFooter = styled.div`
71
84
  background-color: ${CAP_WHITE};
72
85
  position: fixed;
@@ -85,32 +98,38 @@ export const SmsTraiEdit = (props) => {
85
98
  margin-right: ${CAP_SPACE_12};
86
99
  }
87
100
  `;
101
+
88
102
  useEffect(() => {
103
+ //fetching tags
104
+ const { type, module } = location.query || {};
105
+ const isEmbedded = type === EMBEDDED;
106
+ const query = {
107
+ layout: SMS,
108
+ type: TAG,
109
+ context: isEmbedded ? module : DEFAULT,
110
+ embedded: isEmbedded ? type : FULL,
111
+ };
112
+ if (getDefaultTags) {
113
+ query.context = getDefaultTags;
114
+ }
115
+ globalActions.fetchSchemaForEntity(query);
116
+ //fetching template data in fullmode
89
117
  const { id } = params || {};
90
118
  if (id) {
91
119
  actions.getTemplateDetails(id);
92
- //fetching tags
93
- const query = {
94
- layout: SMS,
95
- type: TAG,
96
- context:
97
- location.query.type === EMBEDDED ? location.query.module : DEFAULT,
98
- embedded: location.query.type === EMBEDDED ? location.query.type : FULL,
99
- };
100
- if (getDefaultTags) {
101
- query.context = getDefaultTags;
102
- }
103
- globalActions.fetchSchemaForEntity(query);
104
120
  }
121
+ //cleanup code
105
122
  return () => {
106
123
  actions.resetEditTemplate();
124
+ varMap = {};
107
125
  };
108
126
  }, []);
109
127
 
110
128
  //computing placeholder array for mapping values and rendering dynamic form
111
129
  useEffect(() => {
112
- if (templateDetails && !isEmpty(templateDetails)) {
113
- let msg = get(templateDetails, `versions.base.sms-editor`);
130
+ traiData = isFullMode ? templateDetails : templateData;
131
+ if (traiData && !isEmpty(traiData)) {
132
+ let msg = get(traiData, `versions.base.sms-editor`, '');
114
133
  const templateMessageArray = [];
115
134
  //converting sms-editor string to an array split at '{#var#}'
116
135
  //split and push string before '{#var#}[0 to index]', push '{#var#}',
@@ -126,44 +145,56 @@ export const SmsTraiEdit = (props) => {
126
145
  break;
127
146
  }
128
147
  }
129
-
130
- const filteredTemplateMessageArray = templateMessageArray.filter(
131
- (i) => i.trim() !== '',
132
- );
148
+ const filteredTemplateMessageArray = templateMessageArray.filter((i) => i === 0 || i);
133
149
  updateTempMsgArray(filteredTemplateMessageArray);
134
150
  //stop spinner
135
151
  updateLoading(false);
136
152
  }
137
- }, [templateDetails]);
153
+ }, [templateDetails || templateData]);
138
154
 
155
+ //compute/get varMapped and updated-sms-editor
139
156
  useEffect(() => {
140
157
  if (tempMsgArray.length !== 0) {
141
- //if varMapped and updated-sms-editor is already present on non first edits
142
- if (!isEmpty(get(templateDetails, `versions.base.var-mapped`))) {
143
- varMap = cloneDeep(get(templateDetails, `versions.base.var-mapped`));
144
- setUpdatedSmsEditor(
145
- get(templateDetails, `versions.base.updated-sms-editor`),
146
- );
158
+ const traiBase = traiData?.versions?.base || {};
159
+ const {
160
+ 'var-mapped': varMapped = {},
161
+ 'updated-sms-editor': traiSmsEditor = '',
162
+ } = traiBase;
163
+ //if varMap and updated-sms-editor is already present on non first edits,use those values
164
+ if (!isEmpty(varMapped)) {
165
+ varMap = cloneDeep(varMapped);
166
+ if (isFullMode) {
167
+ setUpdatedSmsEditor(traiSmsEditor);
168
+ } else {
169
+ computeUpdatedSmsEditor();
170
+ }
147
171
  } else {
148
- //computing varMap for first edit
149
- let counter = 1;
150
- for (let i = 0; i < tempMsgArray.length; i++) {
172
+ //computing and setting varMap for first edit
173
+ let varCount = 1;
174
+ let horizontalSpaceCount = 0;
175
+ for (let i = 0; i < tempMsgArray.length; i += 1) {
151
176
  if (tempMsgArray[i] === SMS_TRAI_VAR) {
152
177
  const nextElem =
153
- tempMsgArray[i === tempMsgArray.length - 1 ? 0 : i + 1];
154
- if (tempMsgArray[i] !== nextElem) {
155
- varMap[`${tempMsgArray[i]}_${i - counter + 1}`] = {
178
+ tempMsgArray[i === tempMsgArray.length - 1 ? -1 : i + 1];
179
+ if (nextElem?.replace(/[^\S\r\n]/gm, '') === '') {
180
+ horizontalSpaceCount += 1;
181
+ }
182
+ if (tempMsgArray[i] !== nextElem && nextElem?.replace(/[^\S\r\n]/gm, '') !== '') {
183
+ varMap[`${tempMsgArray[i]}_${(i - varCount - horizontalSpaceCount) + 1}`] = {
156
184
  data: '',
157
- count: counter,
185
+ count: varCount,
158
186
  };
159
- counter = 1;
187
+ varCount = 1;
188
+ horizontalSpaceCount = 0;
160
189
  } else {
161
- counter++;
190
+ varCount += 1;
162
191
  }
163
192
  }
164
193
  }
165
194
  setUpdatedSmsEditor(tempMsgArray);
166
195
  }
196
+ // calcaulate message length here
197
+ calculateTotalMessageLength();
167
198
  }
168
199
  }, [tempMsgArray]);
169
200
 
@@ -176,6 +207,44 @@ export const SmsTraiEdit = (props) => {
176
207
  }
177
208
  }, []);
178
209
 
210
+ //performs tag validation
211
+ useEffect(() => {
212
+ if (
213
+ !isFullMode &&
214
+ updatedSmsEditor?.length > 0 &&
215
+ !updatedSmsEditor.includes(SMS_TRAI_VAR)
216
+ ) {
217
+ tagValidationResponse =
218
+ validateTags({
219
+ content: updatedSmsEditor.join(''),
220
+ tagsParam: tags,
221
+ injectedTagsParams: injectedTags,
222
+ location,
223
+ tagModule: getDefaultTags,
224
+ }) || {};
225
+ updateIsTagValidationError(
226
+ tagValidationResponse.unsupportedTags.length > 0,
227
+ );
228
+ }
229
+ }, [updatedSmsEditor, tags]);
230
+
231
+ const computeUpdatedSmsEditor = () => {
232
+ const arr = [...tempMsgArray];
233
+ const varMapKeys = Object.keys(varMap)?.map((key) => Number(key.slice(8)))?.sort((a, b) => a - b) || [];
234
+ for (const key in varMap) {
235
+ const _id = Number(key.slice(8)); //Eg: -> extracting index 1 from keys like {#var# } _1
236
+ const loopIndex = varMapKeys[varMapKeys?.indexOf(_id) + 1] || arr.length;
237
+ for (let i = _id; i < loopIndex; i += 1) {
238
+ if (i === _id) {
239
+ arr[i] = varMap[key].data; //data for first #var# of the textbox
240
+ } else if (arr[i] === SMS_TRAI_VAR) {
241
+ arr[i] = '';//'' for remaining #var# of a textbox
242
+ }
243
+ }
244
+ }
245
+ setUpdatedSmsEditor(arr);
246
+ };
247
+
179
248
  //Saving on done start
180
249
  const onUpdateTemplateComplete = (editResponse, errorMsg) => {
181
250
  if (editResponse?.templateId) {
@@ -191,16 +260,30 @@ export const SmsTraiEdit = (props) => {
191
260
  }
192
261
  };
193
262
  const onDoneCallback = () => {
194
- if (updatedSmsEditor.includes(SMS_TRAI_VAR)) {
263
+ if (updatedSmsEditor.includes(SMS_TRAI_VAR) && !isFullMode) {
195
264
  //during save textbox should not be empty
196
265
  updateIsValidationError(true);
197
266
  } else {
198
267
  //start spinner
199
268
  updateLoading(true);
200
- templateDetails.versions.base['var-mapped'] = varMap;
201
- templateDetails.versions.base['updated-sms-editor'] = updatedSmsEditor;
202
- templateDetails.versions.history = [templateDetails.versions.base];
203
- actions.editTemplate(templateDetails, onUpdateTemplateComplete);
269
+ const traiVersions = traiData.versions || {};
270
+ traiVersions.base = {
271
+ ...traiVersions.base,
272
+ 'var-mapped': varMap,
273
+ 'updated-sms-editor': updatedSmsEditor,
274
+ 'unicode-validity': isUnicodeAllowed,
275
+ };
276
+ traiVersions.history = [traiVersions.base];
277
+ if (isFullMode) {
278
+ actions.editTemplate(traiData, onUpdateTemplateComplete);
279
+ } else {
280
+ getFormSubscriptionData({
281
+ value: traiData.versions,
282
+ _id: params && params.id,
283
+ validity: true,
284
+ type: SMS,
285
+ });
286
+ }
204
287
  }
205
288
  };
206
289
  //Saving on done end
@@ -209,17 +292,16 @@ export const SmsTraiEdit = (props) => {
209
292
  useEffect(() => {
210
293
  let tag =
211
294
  metaEntities && metaEntities.tags ? metaEntities.tags.standard : [];
212
- if (
213
- location.query.type === EMBEDDED &&
214
- location.query.module === LIBRARY &&
215
- !getDefaultTags
216
- ) {
295
+ const { type, module } = location.query || {};
296
+ if (type === EMBEDDED && module === LIBRARY && !getDefaultTags) {
217
297
  tag = supportedTags;
218
298
  }
219
299
  updateTags(tag);
220
300
  }, [metaEntities]);
221
301
 
222
302
  const handleOnTagsContextChange = (data) => {
303
+ const { type } = location.query || {};
304
+ const isEmbedded = type === EMBEDDED;
223
305
  const query = {
224
306
  layout: SMS,
225
307
  type: TAG,
@@ -227,28 +309,26 @@ export const SmsTraiEdit = (props) => {
227
309
  (data || '').toLowerCase() === ALL
228
310
  ? DEFAULT
229
311
  : (data || '').toLowerCase(),
230
- embedded: location.query.type === EMBEDDED ? location.query.type : FULL,
312
+ embedded: isEmbedded ? type : FULL,
231
313
  };
232
314
  globalActions.fetchSchemaForEntity(query);
233
315
  };
234
316
 
235
317
  const onTagSelect = (data) => {
236
318
  if (textAreaId && varMap && updatedSmsEditor) {
237
- const tempArr = [...updatedSmsEditor];
319
+ const arr = [...updatedSmsEditor];
320
+ const varMapKeys = Object.keys(varMap)?.map((key) => Number(key.slice(8)))?.sort((a, b) => a - b) || [];
321
+ const loopIndex = varMapKeys[varMapKeys?.indexOf(textAreaId) + 1] || arr.length;
238
322
  //when trying to insert tag in empty textarea,{#var#} is replaced with "" and then tag is added
239
- if (tempArr[textAreaId] === SMS_TRAI_VAR) {
240
- for (
241
- let i = 0;
242
- i < varMap[`${SMS_TRAI_VAR}_${textAreaId}`].count;
243
- i++
244
- ) {
245
- tempArr[textAreaId + i] = '';
323
+ for (let i = textAreaId; i < loopIndex; i += 1) {
324
+ if (arr[i] === SMS_TRAI_VAR) {
325
+ arr[i] = '';
246
326
  }
247
327
  }
248
- const messageData = `${tempArr[textAreaId]} {{${data}}}`;
249
- tempArr[textAreaId] = messageData;
328
+ const messageData = `${arr[textAreaId]}{{${data}}}`;
329
+ arr[textAreaId] = messageData;
250
330
  varMap[`${SMS_TRAI_VAR}_${textAreaId}`].data = messageData;
251
- setUpdatedSmsEditor(tempArr);
331
+ setUpdatedSmsEditor(arr);
252
332
  }
253
333
  };
254
334
  //setting the id of currently selected text area, is used onTagSelect
@@ -259,23 +339,34 @@ export const SmsTraiEdit = (props) => {
259
339
 
260
340
  // on change event of Text Area
261
341
  const textAreaValueChange = ({ target: { value, id } }) => {
262
- id = Number(id);
342
+ const _id = Number(id);
343
+ const _value = value.replace(/[\r\n]+/gm, "");//skip newlines eg: '/n'
263
344
  const arr = [...updatedSmsEditor];
264
- varMap[`${SMS_TRAI_VAR}_${id}`].data = value;
265
- if (value === '') {
266
- for (let i = 0; i < varMap[`${SMS_TRAI_VAR}_${id}`].count; i++) {
267
- arr[id + i] = SMS_TRAI_VAR;
345
+ const selectedVarCount = varMap[`${SMS_TRAI_VAR}_${_id}`].count;
346
+ const varMapKeys = Object.keys(varMap)?.map((key) => Number(key.slice(8)))?.sort((a, b) => a - b) || [];
347
+ const loopIndex = varMapKeys[varMapKeys?.indexOf(_id) + 1] || arr.length;
348
+
349
+ //assign entered value to varMap
350
+ varMap[`${SMS_TRAI_VAR}_${_id}`].data = _value;
351
+ //based on entered value update updatedSmsEditor
352
+ if (_value === '') {
353
+ for (let i = _id; i < loopIndex; i += 1) {
354
+ if (i === _id || arr[i] === '') {
355
+ arr[i] = SMS_TRAI_VAR;
356
+ }
268
357
  }
269
358
  } else {
270
- for (let i = 0; i < varMap[`${SMS_TRAI_VAR}_${id}`].count; i++) {
271
- if (i === 0) {
272
- arr[id] = varMap[`${SMS_TRAI_VAR}_${id}`].data;
273
- } else {
274
- arr[id + i] = '';
359
+ for (let i = _id; i < loopIndex; i += 1) {
360
+ if (i === _id) {
361
+ arr[i] = varMap[`${SMS_TRAI_VAR}_${_id}`].data;
362
+ } else if (arr[i] === SMS_TRAI_VAR) {
363
+ arr[i] = '';
275
364
  }
276
365
  }
277
366
  }
278
367
  setUpdatedSmsEditor(arr);
368
+ calculateTotalMessageLength();
369
+ updateshowMsgLengthNote(arr[_id]?.length > selectedVarCount * CHARLIMIT);
279
370
  };
280
371
 
281
372
  const textAreaValue = (idValue) => {
@@ -285,9 +376,8 @@ export const SmsTraiEdit = (props) => {
285
376
  updatedSmsEditor[idValue] !== SMS_TRAI_VAR
286
377
  ) {
287
378
  return updatedSmsEditor[idValue];
288
- } else {
289
- return '';
290
379
  }
380
+ return '';
291
381
  }
292
382
  return '';
293
383
  };
@@ -308,20 +398,20 @@ export const SmsTraiEdit = (props) => {
308
398
  </CapHeading>
309
399
  );
310
400
  }
401
+ return <></>;
311
402
  };
312
403
 
313
404
  const renderedContent = () => {
314
405
  const renderArray = [];
315
406
  if (tempMsgArray?.length !== 0) {
316
407
  let varCount = 0;
317
- tempMsgArray.map((elem, index) => {
408
+ tempMsgArray.forEach((elem, index) => {
318
409
  let prevElem = '';
410
+ prevElem =
411
+ tempMsgArray[[index === 0 ? -1 : index - 1]];
319
412
  if (elem.includes(SMS_TRAI_VAR)) {
320
- prevElem =
321
- tempMsgArray[[index === 0 ? tempMsgArray.length - 1 : index - 1]];
322
- varCount++;
323
- totalVarCount++;
324
- if (prevElem !== SMS_TRAI_VAR) {
413
+ varCount += 1;
414
+ if (prevElem !== SMS_TRAI_VAR && prevElem?.replace(/[^\S\r\n]/gm, '') !== '') {
325
415
  renderArray.push(
326
416
  <TextArea
327
417
  id={index}
@@ -345,18 +435,18 @@ export const SmsTraiEdit = (props) => {
345
435
  renderArray.push(capHeading(index, varCount));
346
436
  varCount = 0;
347
437
  }
348
- } else if (varCount > 0) {
438
+ } else if (varCount > 0 && elem.replace(/[^\S\r\n]/gm, '') !== '') {
349
439
  renderArray.push(
350
440
  capHeading(index, varCount),
351
441
  <CapHeading
352
442
  key={`${elem}${index}`}
353
- style={{ margin: `${CAP_SPACE_16} 0` }}
443
+ style={{ margin: `${CAP_SPACE_16} 0`, clear: 'both' }}
354
444
  >
355
445
  {elem}
356
446
  </CapHeading>,
357
447
  );
358
448
  varCount = 0;
359
- } else {
449
+ } else if (elem.replace(/[^\S\r\n]/gm, '') !== '') {
360
450
  renderArray.push(
361
451
  <CapHeading
362
452
  key={`${elem}${index}`}
@@ -371,42 +461,88 @@ export const SmsTraiEdit = (props) => {
371
461
  return renderArray;
372
462
  };
373
463
 
464
+ const smsLengthForVar = () => (
465
+ <CapHeading type="h5" style={{ marginTop: CAP_SPACE_04, float: 'right' }}>
466
+ {formatMessage(messages.totalCharacters, {
467
+ smsCount: Math.ceil(totalMessageLength / 160),
468
+ number: totalMessageLength,
469
+ })}
470
+ </CapHeading>
471
+ );
472
+
374
473
  // to compute the length of the message
375
- //30 characters is blocked per'{#var#}' and the remaining string length is added to it
376
- const smsLengthForVar = () => {
377
- const msgLenWithoutVar =
378
- tempMsgArray?.filter((i) => i !== SMS_TRAI_VAR)?.join('').length || 0;
379
- const totalMessageLength = msgLenWithoutVar + totalVarCount * CHARLIMIT;
380
- return (
381
- <CapHeading type="h5" style={{ marginTop: CAP_SPACE_04, float: 'right' }}>
382
- {formatMessage(messages.totalCharacters, {
383
- smsCount: Math.ceil(totalMessageLength / 160),
384
- number: totalMessageLength,
385
- })}
386
- </CapHeading>
387
- );
474
+ //30 characters is blocked per'{#var#}' if textbox is empty otherwise it will be textbox length
475
+ // and the remaining string length is added to it
476
+ const calculateTotalMessageLength = () => {
477
+ const msgLenWithoutVar = tempMsgArray
478
+ ?.filter((i) => i !== SMS_TRAI_VAR)
479
+ .join('');
480
+ const varLen = calculateLenForTextBox();
481
+ const totalMsgLen = msgLenWithoutVar?.length + varLen;
482
+ setTotalMessageLength(totalMsgLen);
483
+ };
484
+
485
+ const calculateLenForTextBox = () => {
486
+ let countVarChar = 0;
487
+ Object.keys(varMap).forEach((i) => {
488
+ if (varMap[i].data) {
489
+ countVarChar += varMap[i].data?.length;
490
+ } else {
491
+ countVarChar += varMap[i].count * CHARLIMIT;
492
+ }
493
+ });
494
+ return countVarChar;
495
+ };
496
+
497
+ const tagValidationErrorMessage = () => {
498
+ const { unsupportedTags = [] } = tagValidationResponse;
499
+ let tagError = '';
500
+ if (unsupportedTags.length > 0) {
501
+ tagError = formatMessage(messages.unsupportedTagsValidationError, {
502
+ unsupportedTags,
503
+ });
504
+ }
505
+ return <CapError>{tagError}</CapError>;
506
+ };
507
+
508
+ const disablehandler = () => {
509
+ if (traiData && !isEmpty(traiData)) {
510
+ const msg = get(traiData, `versions.base.sms-editor`, '');
511
+ const index = msg.search(SMS_TRAI_VAR);
512
+ if (index === -1) {
513
+ return true;
514
+ }
515
+ return false;
516
+ }
517
+ return true;
518
+ };
519
+
520
+ const unicodeHandler = ({ target: { checked } }) => {
521
+ updateIsUnicodeAllowed(checked);
388
522
  };
389
523
 
390
524
  return (
391
525
  <>
392
526
  <CapSpin spinning={loading}>
393
527
  <CapRow>
394
- {templateDetails && !isEmpty(templateDetails) && (
528
+ {traiData && !isEmpty(traiData) && (
395
529
  <TraiEditTemplateDetails>
396
530
  <CapLabelInline type="label1">
397
531
  {formatMessage(messages.templateLabel)}
398
532
  </CapLabelInline>
399
533
  <CapLabelInline type="label2">
400
- {templateDetails.versions.base.template_name}
534
+ {get(traiData, `versions.base.template_name`, '')}
401
535
  </CapLabelInline>
536
+
402
537
  <CapLabelInline type="label1">
403
538
  {formatMessage(messages.traiEditSeperator)}
404
539
  </CapLabelInline>
540
+
405
541
  <CapLabelInline type="label1">
406
542
  {formatMessage(messages.senderIdlabel)}
407
543
  </CapLabelInline>
408
544
  <CapLabelInline type="label2">
409
- {templateDetails.versions.base.header}
545
+ {[...get(traiData, `versions.base.header`, [])].join(', ')}
410
546
  </CapLabelInline>
411
547
  </TraiEditTemplateDetails>
412
548
  )}
@@ -423,6 +559,7 @@ export const SmsTraiEdit = (props) => {
423
559
  tags={tags || []}
424
560
  onContextChange={handleOnTagsContextChange}
425
561
  injectedTags={injectedTags || {}}
562
+ hidePopover={disablehandler()}
426
563
  />
427
564
  }
428
565
  />
@@ -437,17 +574,28 @@ export const SmsTraiEdit = (props) => {
437
574
  {renderedContent()}
438
575
  </CapRow>
439
576
  <CapRow>{smsLengthForVar()}</CapRow>
577
+ {isTagValidationError && tagValidationErrorMessage()}
578
+ <CapCheckbox onChange={unicodeHandler} checked={isUnicodeAllowed} disabled={disablehandler()}>
579
+ {formatMessage(messages.unicodeLabel)}
580
+ </CapCheckbox>
581
+ {showMsgLengthNote && <CapInfoNote message={<FormattedMessage {...messages.msgLengthNote} />} />}
440
582
  <div style={{ marginBottom: '100px' }} />
441
583
  </CapColumn>
442
584
  <CapColumn span={10}>
443
585
  <TemplatePreview
444
586
  channel={SMS}
445
- content={[updatedSmsEditor.join('')]}
587
+ content={updatedSmsEditor.join('')}
588
+ unicodeEnabled={isUnicodeAllowed}
589
+ charCounterEnabled
446
590
  />
447
591
  </CapColumn>
448
592
  </CapRow>
449
593
  <SMSTraiFooter>
450
- <CapButton onClick={onDoneCallback} className="create-msg">
594
+ <CapButton
595
+ onClick={onDoneCallback}
596
+ className="create-msg"
597
+ disabled={isTagValidationError}
598
+ >
451
599
  <FormattedMessage {...messages.saveButtonLabel} />
452
600
  </CapButton>
453
601
  <CapButton
@@ -469,11 +617,9 @@ const mapStateToProps = createStructuredSelector({
469
617
  injectedTags: setInjectedTags(),
470
618
  });
471
619
 
472
- const mapDispatchToProps = (dispatch) => {
473
- return {
474
- actions: bindActionCreators(actions, dispatch),
475
- };
476
- };
620
+ const mapDispatchToProps = (dispatch) => ({
621
+ actions: bindActionCreators(smsEditActions, dispatch),
622
+ });
477
623
 
478
624
  export default withCreatives({
479
625
  WrappedComponent: injectIntl(SmsTraiEdit),
@@ -29,7 +29,7 @@ export default defineMessages({
29
29
  },
30
30
  textAreaCounts: {
31
31
  id: `${prefix}.textAreaCounts`,
32
- defaultMessage: '{varCounts} Variables {var}: max. {charCounts} characters',
32
+ defaultMessage: '{varCounts} variable{varCounts, plural, one {} other {s}} {var}: max. {charCounts} characters',
33
33
  },
34
34
  totalCharacters: {
35
35
  id: `${prefix}.totalCharacters`,
@@ -55,4 +55,17 @@ export default defineMessages({
55
55
  id: `${prefix}.senderIdlabel`,
56
56
  defaultMessage: 'Sender ID',
57
57
  },
58
+ unsupportedTagsValidationError: {
59
+ id: `${prefix}.unsupportedTagsValidationError`,
60
+ defaultMessage:
61
+ 'Unsupported tags: {unsupportedTags}. Please remove them from this message.',
62
+ },
63
+ unicodeLabel: {
64
+ id: `${prefix}.unicodeLabel`,
65
+ defaultMessage: 'Allow unicode characters',
66
+ },
67
+ msgLengthNote: {
68
+ id: `${prefix}.msgLengthNote`,
69
+ defaultMessage: 'Only 30 characters are allowed per variable {#var#} field. In case the field resolves to more than 30 characters the message is bound to fail. Hence will not be sent by the Capillary system.',
70
+ },
58
71
  });