@capillarytech/creatives-library 9.0.47 → 9.0.48

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.
@@ -19,11 +19,14 @@ import CapNotification from '@capillarytech/cap-ui-library/CapNotification';
19
19
  import CapError from '@capillarytech/cap-ui-library/CapError';
20
20
  import CapCheckbox from '@capillarytech/cap-ui-library/CapCheckbox';
21
21
  import CapInfoNote from '@capillarytech/cap-ui-library/CapInfoNote';
22
+ import CapAlert from '@capillarytech/cap-ui-library/CapAlert';
23
+ import CapIcon from '@capillarytech/cap-ui-library/CapIcon';
22
24
  import injectSaga from '../../../utils/injectSaga';
23
25
  import injectReducer from '../../../utils/injectReducer';
24
26
  import {
25
27
  CAP_SPACE_16,
26
28
  CAP_SPACE_12,
29
+ CAP_SPACE_08,
27
30
  CAP_SPACE_24,
28
31
  CAP_SPACE_32,
29
32
  CAP_SPACE_04,
@@ -51,6 +54,7 @@ import {
51
54
  FULL,
52
55
  ALL,
53
56
  LIBRARY,
57
+ HORIZONTAL_WHITESPACE_ONLY_REGEX,
54
58
  } from './constants';
55
59
  import v2EditSmsReducer from '../../Sms/Edit/reducer';
56
60
  import { v2SmsEditSagas } from '../../Sms/Edit/sagas';
@@ -68,6 +72,15 @@ import {
68
72
  } from '../../../utils/templateVarUtils';
69
73
  import VarSegmentMessageEditor from '../../../v2Components/VarSegmentMessageEditor';
70
74
  import rcsMessages from '../../Rcs/messages';
75
+ import {
76
+ ANY_DLT_HASH_TOKEN_GLOBAL_REGEX,
77
+ ANY_DLT_HASH_TOKEN_PROBE_REGEX,
78
+ DLT_LEGACY_VAR_REGEX,
79
+ DLT_LEGACY_VAR_TOKEN,
80
+ isAnyDltVarToken,
81
+ getDltVarTypeMeta,
82
+ getDltVarCharLimit,
83
+ } from './dltVarTypes';
71
84
 
72
85
  import './index.scss';
73
86
 
@@ -257,8 +270,8 @@ export const SmsTraiEdit = (props) => {
257
270
  <span className="sms-trai-rcs-fallback-var-hint" style={dltVarSlotHintStyle}>
258
271
  {formatMessage(messages.textAreaCounts, {
259
272
  varCounts,
260
- var: SMS_TRAI_VAR,
261
- charCounts: varCounts * CHARLIMIT,
273
+ var: dltSegmentToken,
274
+ charCounts: varCounts * getDltVarCharLimit(dltSegmentToken, CHARLIMIT),
262
275
  })}
263
276
  </span>
264
277
  );
@@ -288,9 +301,13 @@ export const SmsTraiEdit = (props) => {
288
301
  }
289
302
  `;
290
303
  const TraiEditTemplateDetails = styled.div`
304
+ display: flex;
305
+ flex-wrap: wrap;
306
+ align-items: center;
307
+ gap: ${CAP_SPACE_08};
291
308
  margin-bottom: ${CAP_SPACE_16};
292
309
  .cap-label-span {
293
- margin-right: ${CAP_SPACE_12};
310
+ margin-right: 0;
294
311
  }
295
312
  `;
296
313
 
@@ -415,18 +432,21 @@ export const SmsTraiEdit = (props) => {
415
432
  if (isRcsSmsFallback) return;
416
433
  traiDataRef.current = isFullMode ? templateDetails : templateData;
417
434
  if (traiDataRef.current && !isEmpty(traiDataRef.current)) {
418
- let msg = get(traiDataRef.current, `versions.base.sms-editor`, '');
435
+ const msg = get(traiDataRef.current, `versions.base.sms-editor`, '') || '';
419
436
  const templateMessageArray = [];
420
- while (msg.length !== 0) {
421
- const index = msg.search(SMS_TRAI_VAR);
422
- if (index !== -1) {
423
- templateMessageArray.push(msg.substring(0, index));
424
- templateMessageArray.push(SMS_TRAI_VAR);
425
- msg = msg.substring(index + 7, msg.length);
426
- } else {
427
- templateMessageArray.push(msg);
428
- break;
437
+ let cursor = 0;
438
+ ANY_DLT_HASH_TOKEN_GLOBAL_REGEX.lastIndex = 0;
439
+ let match = ANY_DLT_HASH_TOKEN_GLOBAL_REGEX.exec(msg);
440
+ while (match) {
441
+ if (match.index > cursor) {
442
+ templateMessageArray.push(msg.substring(cursor, match.index));
429
443
  }
444
+ templateMessageArray.push(match[0]);
445
+ cursor = match.index + match[0].length;
446
+ match = ANY_DLT_HASH_TOKEN_GLOBAL_REGEX.exec(msg);
447
+ }
448
+ if (cursor < msg.length) {
449
+ templateMessageArray.push(msg.substring(cursor));
430
450
  }
431
451
  const filteredTemplateMessageArray = templateMessageArray.filter((i) => i === 0 || i);
432
452
  updateTempMsgArray(filteredTemplateMessageArray);
@@ -488,14 +508,14 @@ export const SmsTraiEdit = (props) => {
488
508
  let varCount = 1;
489
509
  let horizontalSpaceCount = 0;
490
510
  for (let i = 0; i < tempMsgArray.length; i += 1) {
491
- if (tempMsgArray[i] === SMS_TRAI_VAR) {
511
+ if (isAnyDltVarToken(tempMsgArray[i])) {
492
512
  const nextElem =
493
513
  tempMsgArray[i === tempMsgArray.length - 1 ? -1 : i + 1];
494
- if (nextElem?.replace(/[^\S\r\n]/gm, '') === '') {
514
+ if (nextElem?.replace(HORIZONTAL_WHITESPACE_ONLY_REGEX, '') === '') {
495
515
  horizontalSpaceCount += 1;
496
516
  }
497
- if (tempMsgArray[i] !== nextElem && nextElem?.replace(/[^\S\r\n]/gm, '') !== '') {
498
- varMapRef.current[`${tempMsgArray[i]}_${(i - varCount - horizontalSpaceCount) + 1}`] = {
517
+ if (tempMsgArray[i] !== nextElem && nextElem?.replace(HORIZONTAL_WHITESPACE_ONLY_REGEX, '') !== '') {
518
+ varMapRef.current[`${SMS_TRAI_VAR}_${(i - varCount - horizontalSpaceCount) + 1}`] = {
499
519
  data: '',
500
520
  count: varCount,
501
521
  };
@@ -567,7 +587,7 @@ export const SmsTraiEdit = (props) => {
567
587
  if (
568
588
  !isFullMode &&
569
589
  updatedSmsEditor?.length > 0 &&
570
- !updatedSmsEditor.includes(SMS_TRAI_VAR)
590
+ !updatedSmsEditor.some(isAnyDltVarToken)
571
591
  ) {
572
592
  tagValidationResponseRef.current = runValidateTags(updatedSmsEditor.join(''));
573
593
  const missing = (tagValidationResponseRef.current.missingTags || []).length > 0;
@@ -600,7 +620,7 @@ export const SmsTraiEdit = (props) => {
600
620
  for (let i = _id; i < loopIndex; i += 1) {
601
621
  if (i === _id) {
602
622
  arr[i] = varMapRef.current[key].data; //data for first #var# of the textbox
603
- } else if (arr[i] === SMS_TRAI_VAR) {
623
+ } else if (isAnyDltVarToken(arr[i])) {
604
624
  arr[i] = ''; //'' for remaining #var# of a textbox
605
625
  }
606
626
  }
@@ -698,7 +718,7 @@ export const SmsTraiEdit = (props) => {
698
718
  const editorJoined = Array.isArray(updatedSmsEditor)
699
719
  ? updatedSmsEditor.join('')
700
720
  : String(updatedSmsEditor || '');
701
- if (!isRcsSmsFallback && editorJoined.includes(SMS_TRAI_VAR) && !isFullMode) {
721
+ if (!isRcsSmsFallback && ANY_DLT_HASH_TOKEN_PROBE_REGEX.test(editorJoined) && !isFullMode) {
702
722
  updateIsValidationError(true);
703
723
  } else {
704
724
  // Only show full-screen spinner when waiting on full-mode SMS edit API — not for RCS SMS fallback
@@ -807,7 +827,7 @@ export const SmsTraiEdit = (props) => {
807
827
  const varMapKeys = Object.keys(varMapRef.current)?.map((key) => Number(key.slice(8)))?.sort((a, b) => a - b) || [];
808
828
  const loopIndex = varMapKeys[varMapKeys?.indexOf(textAreaId) + 1] || arr.length;
809
829
  for (let i = textAreaId; i < loopIndex; i += 1) {
810
- if (arr[i] === SMS_TRAI_VAR) {
830
+ if (isAnyDltVarToken(arr[i])) {
811
831
  arr[i] = '';
812
832
  }
813
833
  }
@@ -830,17 +850,18 @@ export const SmsTraiEdit = (props) => {
830
850
  const loopIndex = varMapKeys[varMapKeys?.indexOf(_id) + 1] || arr.length;
831
851
 
832
852
  varMapRef.current[`${SMS_TRAI_VAR}_${_id}`].data = value;
853
+ const slotToken = isAnyDltVarToken(tempMsgArray[_id]) ? tempMsgArray[_id] : SMS_TRAI_VAR;
833
854
  if (value === '') {
834
855
  for (let i = _id; i < loopIndex; i += 1) {
835
856
  if (i === _id || arr[i] === '') {
836
- arr[i] = SMS_TRAI_VAR;
857
+ arr[i] = slotToken;
837
858
  }
838
859
  }
839
860
  } else {
840
861
  for (let i = _id; i < loopIndex; i += 1) {
841
862
  if (i === _id) {
842
863
  arr[i] = varMapRef.current[`${SMS_TRAI_VAR}_${_id}`].data;
843
- } else if (arr[i] === SMS_TRAI_VAR) {
864
+ } else if (isAnyDltVarToken(arr[i])) {
844
865
  arr[i] = '';
845
866
  }
846
867
  }
@@ -853,7 +874,7 @@ export const SmsTraiEdit = (props) => {
853
874
  if (idValue >= 0 && updatedSmsEditor) {
854
875
  if (
855
876
  updatedSmsEditor[idValue] &&
856
- updatedSmsEditor[idValue] !== SMS_TRAI_VAR
877
+ !isAnyDltVarToken(updatedSmsEditor[idValue])
857
878
  ) {
858
879
  return updatedSmsEditor[idValue];
859
880
  }
@@ -862,62 +883,86 @@ export const SmsTraiEdit = (props) => {
862
883
  return '';
863
884
  };
864
885
 
865
- const capHeading = (index, varCount) => {
866
- if ((index, varCount)) {
867
- return (
868
- <CapHeading
869
- key={`label${index}`}
870
- type="h6"
871
- style={{ margin: `${CAP_SPACE_04} 0`, float: 'right' }}
872
- >
873
- {formatMessage(messages.textAreaCounts, {
874
- varCounts: varCount,
875
- var: SMS_TRAI_VAR,
876
- charCounts: varCount * CHARLIMIT,
877
- })}
878
- </CapHeading>
879
- );
886
+ const getSlotDisplayMeta = (token) => {
887
+ const meta = getDltVarTypeMeta(token);
888
+ if (!meta) {
889
+ return {
890
+ placeholder: props.intl.formatMessage(messages.inputplaceHolderText),
891
+ charLimit: CHARLIMIT,
892
+ };
880
893
  }
881
- return <></>;
894
+ return {
895
+ placeholder: formatMessage(messages[meta.placeholderKey]),
896
+ charLimit: meta.charLimit,
897
+ };
882
898
  };
883
899
 
900
+ const renderSlotCounter = (currentValue, charLimit) => (
901
+ <CapLabelInline type="label4" className="sms-trai-slot-counter">
902
+ {formatMessage(messages.slotCharCounter, {
903
+ current: (currentValue || '').length,
904
+ max: charLimit,
905
+ })}
906
+ </CapLabelInline>
907
+ );
908
+
884
909
  const renderedContent = () => {
885
910
  const renderArray = [];
886
911
  if (tempMsgArray?.length !== 0) {
887
912
  let varCount = 0;
913
+ let slotStartIndex = -1;
914
+ let slotToken = SMS_TRAI_VAR;
888
915
  tempMsgArray.forEach((elem, index) => {
889
916
  let prevElem = '';
890
917
  prevElem =
891
918
  tempMsgArray[[index === 0 ? -1 : index - 1]];
892
- if (elem.includes(SMS_TRAI_VAR)) {
919
+ if (isAnyDltVarToken(elem)) {
920
+ if (varCount === 0) {
921
+ slotStartIndex = index;
922
+ slotToken = elem;
923
+ }
893
924
  varCount += 1;
894
- if (prevElem !== SMS_TRAI_VAR && prevElem?.replace(/[^\S\r\n]/gm, '') !== '') {
925
+ if (!isAnyDltVarToken(prevElem) && prevElem?.replace(HORIZONTAL_WHITESPACE_ONLY_REGEX, '') !== '') {
926
+ const currentSlotToken = slotToken;
927
+ const currentSlotIndex = slotStartIndex;
928
+ const { placeholder, charLimit: baseLimit } = getSlotDisplayMeta(currentSlotToken);
929
+ const slotValue = textAreaValue(currentSlotIndex);
895
930
  renderArray.push(
896
- <TextArea
897
- id={index}
898
- key={`${elem}${index}`}
899
- placeholder={props.intl.formatMessage(
900
- messages.inputplaceHolderText,
901
- )}
902
- autosize={{ minRows: 1, maxRows: 3 }}
903
- onChange={textAreaValueChange}
904
- value={textAreaValue(index)}
905
- onFocus={setTextAreaId}
906
- errorMessage={
907
- isValidationError &&
908
- updatedSmsEditor[index] === SMS_TRAI_VAR &&
909
- formatMessage(messages.textAreaError)
910
- }
911
- />,
931
+ <CapRow
932
+ type="flex-column"
933
+ key={`slot_${currentSlotToken}_${index}`}
934
+ className="sms-trai-slot"
935
+ >
936
+ <CapRow type="flex" noWrap align="middle" className="sms-trai-slot__field">
937
+ <CapColumn className="sms-trai-slot__chip">
938
+ <CapLabel type="label3">{currentSlotToken}</CapLabel>
939
+ </CapColumn>
940
+ <CapColumn flex="auto" className="sms-trai-slot__input-wrap">
941
+ <TextArea
942
+ id={currentSlotIndex}
943
+ placeholder={placeholder}
944
+ autosize={{ minRows: 1, maxRows: 1 }}
945
+ onChange={textAreaValueChange}
946
+ value={slotValue}
947
+ onFocus={setTextAreaId}
948
+ errorMessage={
949
+ isValidationError &&
950
+ isAnyDltVarToken(updatedSmsEditor[currentSlotIndex]) &&
951
+ formatMessage(messages.textAreaError)
952
+ }
953
+ />
954
+ </CapColumn>
955
+ </CapRow>
956
+ {renderSlotCounter(slotValue, baseLimit * varCount)}
957
+ </CapRow>,
912
958
  );
913
959
  }
914
960
  if (index === tempMsgArray.length - 1) {
915
- renderArray.push(capHeading(index, varCount));
916
961
  varCount = 0;
962
+ slotStartIndex = -1;
917
963
  }
918
- } else if (varCount > 0 && elem.replace(/[^\S\r\n]/gm, '') !== '') {
964
+ } else if (varCount > 0 && elem?.replace(HORIZONTAL_WHITESPACE_ONLY_REGEX, '') !== '') {
919
965
  renderArray.push(
920
- capHeading(index, varCount),
921
966
  <CapHeading
922
967
  key={`${elem}${index}`}
923
968
  style={{ margin: `${CAP_SPACE_16} 0`, clear: 'both' }}
@@ -926,7 +971,8 @@ export const SmsTraiEdit = (props) => {
926
971
  </CapHeading>,
927
972
  );
928
973
  varCount = 0;
929
- } else if (elem.replace(/[^\S\r\n]/gm, '') !== '') {
974
+ slotStartIndex = -1;
975
+ } else if (elem?.replace(HORIZONTAL_WHITESPACE_ONLY_REGEX, '') !== '') {
930
976
  renderArray.push(
931
977
  <CapHeading
932
978
  key={`${elem}${index}`}
@@ -969,7 +1015,7 @@ export const SmsTraiEdit = (props) => {
969
1015
  return;
970
1016
  }
971
1017
  const msgLenWithoutVar = tempMsgArray
972
- ?.filter((i) => i !== SMS_TRAI_VAR)
1018
+ ?.filter((i) => !isAnyDltVarToken(i))
973
1019
  .join('');
974
1020
  const varLen = calculateLenForTextBox();
975
1021
  const totalMsgLen = msgLenWithoutVar?.length + varLen;
@@ -1014,12 +1060,8 @@ export const SmsTraiEdit = (props) => {
1014
1060
  const disablehandler = () => {
1015
1061
  if (isRcsSmsFallback) return false;
1016
1062
  if (traiDataRef.current && !isEmpty(traiDataRef.current)) {
1017
- const msg = get(traiDataRef.current, `versions.base.sms-editor`, '');
1018
- const index = msg.search(SMS_TRAI_VAR);
1019
- if (index === -1) {
1020
- return true;
1021
- }
1022
- return false;
1063
+ const msg = get(traiDataRef.current, `versions.base.sms-editor`, '') || '';
1064
+ return !ANY_DLT_HASH_TOKEN_PROBE_REGEX.test(msg);
1023
1065
  }
1024
1066
  return true;
1025
1067
  };
@@ -1069,7 +1111,7 @@ export const SmsTraiEdit = (props) => {
1069
1111
  const shouldShowPreview =
1070
1112
  !isRcsSmsFallback || (showPreviewInRcsFallback && !hidePreview);
1071
1113
  const smsSidePreviewColumn = (
1072
- <CapColumn span={8} offset={0}>
1114
+ <CapColumn span={6} offset={0}>
1073
1115
  <UnifiedPreview
1074
1116
  channel={SMS}
1075
1117
  content={updatedSmsEditor.join('')}
@@ -1097,25 +1139,35 @@ export const SmsTraiEdit = (props) => {
1097
1139
  <CapRow typeof='flex' align='top' noWrap fullWidth>
1098
1140
  <CapRow type='flex' vertical fullWidth>
1099
1141
  {traiDataRef.current && !isEmpty(traiDataRef.current) && !isRcsSmsFallback && (
1100
- <TraiEditTemplateDetails>
1101
- <CapLabelInline type="label1">
1102
- {formatMessage(messages.templateLabel)}
1103
- </CapLabelInline>
1104
- <CapLabelInline type="label2">
1105
- {get(traiDataRef.current, `versions.base.template_name`, '')}
1106
- </CapLabelInline>
1107
-
1108
- <CapLabelInline type="label1">
1109
- {formatMessage(messages.traiEditSeperator)}
1110
- </CapLabelInline>
1111
-
1112
- <CapLabelInline type="label1">
1113
- {formatMessage(messages.senderIdlabel)}
1114
- </CapLabelInline>
1115
- <CapLabelInline type="label2">
1116
- {[...get(traiDataRef.current, `versions.base.header`, [])].join(', ')}
1117
- </CapLabelInline>
1118
- </TraiEditTemplateDetails>
1142
+ <>
1143
+ <TraiEditTemplateDetails>
1144
+ <CapLabelInline type="label1">
1145
+ {formatMessage(messages.templateLabel)}
1146
+ </CapLabelInline>
1147
+ <CapLabelInline type="label2">
1148
+ {get(traiDataRef.current, 'versions.base.template_name', '')}
1149
+ </CapLabelInline>
1150
+ <CapLabelInline type="label1">
1151
+ {formatMessage(messages.traiEditSeperator)}
1152
+ </CapLabelInline>
1153
+ <CapLabelInline type="label1">
1154
+ {formatMessage(messages.senderIdlabel)}
1155
+ </CapLabelInline>
1156
+ <CapLabelInline className="sms-trai-sender-id-chip">
1157
+ <CapIcon
1158
+ type="closed-lock"
1159
+ size="s"
1160
+ className="sms-trai-sender-id-chip__lock"
1161
+ />
1162
+ <CapLabelInline type="label2">
1163
+ {[...get(traiDataRef.current, 'versions.base.header', [])].join(', ')}
1164
+ </CapLabelInline>
1165
+ </CapLabelInline>
1166
+ <CapLabelInline type="label1" className="sms-trai-sender-id-locked-note">
1167
+ {formatMessage(messages.senderIdLockedNote)}
1168
+ </CapLabelInline>
1169
+ </TraiEditTemplateDetails>
1170
+ </>
1119
1171
  )}
1120
1172
  {isRcsSmsFallback &&
1121
1173
  traiDataRef.current &&
@@ -1137,14 +1189,68 @@ export const SmsTraiEdit = (props) => {
1137
1189
  <CapLabelInline type="label1">
1138
1190
  {formatMessage(messages.senderIdlabel)}
1139
1191
  </CapLabelInline>
1140
- <CapLabelInline type="label2">
1141
- {[...get(traiDataRef.current, 'versions.base.header', [])].join(', ')}
1192
+ <CapLabelInline className="sms-trai-sender-id-chip">
1193
+ <CapIcon type="closed-lock" size="s" className="sms-trai-sender-id-chip__lock" />
1194
+ <CapLabelInline type="label2">
1195
+ {[...get(traiDataRef.current, 'versions.base.header', [])].join(', ')}
1196
+ </CapLabelInline>
1197
+ </CapLabelInline>
1198
+ <CapLabelInline type="label1" className="sms-trai-sender-id-locked-note">
1199
+ {formatMessage(messages.senderIdLockedNote)}
1142
1200
  </CapLabelInline>
1143
1201
  </>
1144
1202
  ) : null}
1145
1203
  </TraiEditTemplateDetails>
1146
1204
  )}
1147
- <CapColumn span={shouldShowPreview ? 16 : 24}>
1205
+ {isRcsSmsFallback
1206
+ && traiDltEnabled
1207
+ && !isEmpty(traiDataRef.current)
1208
+ && get(traiDataRef.current, 'versions.base.type', '')
1209
+ ?.toLowerCase()
1210
+ ?.includes('promo') && (
1211
+ <CapInfoNote
1212
+ type="clock-circle"
1213
+ className="sms-trai-promotional-window-banner"
1214
+ noteText={formatMessage(messages.promotionalWindowBanner)}
1215
+ />
1216
+ )}
1217
+ {isRcsSmsFallback
1218
+ && traiDltEnabled
1219
+ && DLT_LEGACY_VAR_REGEX.test(
1220
+ get(traiDataRef.current, 'versions.base.sms-editor', '') || '',
1221
+ ) && (
1222
+ <CapInfoNote
1223
+ type="warning"
1224
+ className="sms-trai-legacy-warning-banner"
1225
+ noteText={formatMessage(messages.smsLegacyFormatWarning, {
1226
+ legacyToken: DLT_LEGACY_VAR_TOKEN,
1227
+ })}
1228
+ />
1229
+ )}
1230
+ <CapColumn span={shouldShowPreview ? 18 : 24}>
1231
+ {!isRcsSmsFallback
1232
+ && !isEmpty(traiDataRef.current)
1233
+ && get(traiDataRef.current, 'versions.base.type', '')
1234
+ ?.toLowerCase()
1235
+ ?.includes('promo') && (
1236
+ <CapInfoNote
1237
+ type="clock-circle"
1238
+ className="sms-trai-promotional-window-banner"
1239
+ noteText={formatMessage(messages.promotionalWindowBanner)}
1240
+ />
1241
+ )}
1242
+ {!isRcsSmsFallback
1243
+ && DLT_LEGACY_VAR_REGEX.test(
1244
+ get(traiDataRef.current, 'versions.base.sms-editor', '') || '',
1245
+ ) && (
1246
+ <CapInfoNote
1247
+ type="warning"
1248
+ className="sms-trai-legacy-warning-banner"
1249
+ noteText={formatMessage(messages.smsLegacyFormatWarning, {
1250
+ legacyToken: DLT_LEGACY_VAR_TOKEN,
1251
+ })}
1252
+ />
1253
+ )}
1148
1254
  <CapRow useLegacy>
1149
1255
  <CapHeader
1150
1256
  title={formatMessage(messages.traiEditTitle)}
@@ -107,7 +107,9 @@
107
107
  /* Non–RCS fallback: segmented editor shell + character count row (was inline styles on CapRow) */
108
108
  .sms-trai-editor-segment-row {
109
109
  background-color: $CAP_G10;
110
- padding: $CAP_SPACE_16;
110
+ padding: $CAP_SPACE_24 $CAP_SPACE_24 $CAP_SPACE_16 $CAP_SPACE_24;
111
+ border-radius: 0.375rem;
112
+ margin-right: $CAP_SPACE_16;
111
113
  }
112
114
 
113
115
  .sms-trai-length-row {
@@ -124,3 +126,117 @@
124
126
  .sms-trai-edit-bottom-spacer {
125
127
  margin-bottom: 6.25rem;
126
128
  }
129
+
130
+ .sms-trai-slot {
131
+ display: flex;
132
+ flex-direction: column;
133
+ margin: 0 0 $CAP_SPACE_16 0;
134
+ }
135
+
136
+ .sms-trai-slot__field {
137
+ display: flex;
138
+ align-items: stretch;
139
+ background-color: $CAP_WHITE;
140
+ border: 1px solid $CAP_G07;
141
+ border-radius: 0.375rem;
142
+ overflow: hidden;
143
+ }
144
+
145
+ .sms-trai-slot__chip {
146
+ flex-shrink: 0;
147
+ display: flex;
148
+ align-items: center;
149
+ justify-content: center;
150
+ min-width: 4.5rem;
151
+ padding: 0 $CAP_SPACE_08;
152
+ background-color: $CAP_G10;
153
+ border-right: 1px solid $CAP_G07;
154
+ color: $FONT_COLOR_02;
155
+ font-family: Menlo, Monaco, 'Courier New', monospace;
156
+ font-size: 0.75rem;
157
+ line-height: 1.125rem;
158
+ white-space: nowrap;
159
+ }
160
+
161
+ .sms-trai-slot__input-wrap {
162
+ flex: 1;
163
+ min-width: 0;
164
+ }
165
+
166
+ .sms-trai-slot__input-wrap .ant-input,
167
+ .sms-trai-slot__input-wrap textarea.ant-input {
168
+ background-color: transparent;
169
+ border: none;
170
+ border-radius: 0;
171
+ padding: $CAP_SPACE_08 $CAP_SPACE_12;
172
+ min-height: 2.5rem;
173
+ box-shadow: none;
174
+ overflow-x: auto;
175
+ }
176
+
177
+ .sms-trai-slot__input-wrap .ant-input::placeholder,
178
+ .sms-trai-slot__input-wrap textarea.ant-input::placeholder {
179
+ white-space: nowrap;
180
+ overflow: hidden;
181
+ text-overflow: ellipsis;
182
+ }
183
+
184
+ .sms-trai-slot__input-wrap .ant-input:focus,
185
+ .sms-trai-slot__input-wrap .ant-input:active,
186
+ .sms-trai-slot__input-wrap textarea.ant-input:focus,
187
+ .sms-trai-slot__input-wrap textarea.ant-input:active {
188
+ border: none;
189
+ box-shadow: none;
190
+ outline: none;
191
+ }
192
+
193
+ .sms-trai-slot-counter {
194
+ align-self: flex-end;
195
+ margin-top: $CAP_SPACE_04;
196
+ color: $FONT_COLOR_02;
197
+ font-size: 0.75rem;
198
+ line-height: 1rem;
199
+ }
200
+
201
+ .sms-trai-sender-id-chip {
202
+ display: inline-flex;
203
+ align-items: center;
204
+ gap: $CAP_SPACE_04;
205
+ padding: $CAP_SPACE_02 $CAP_SPACE_08;
206
+ border: 1px solid $CAP_G07;
207
+ border-radius: 0.25rem;
208
+ background-color: $CAP_G10;
209
+ line-height: 1.25rem;
210
+ }
211
+
212
+ .sms-trai-sender-id-chip__lock {
213
+ display: inline-flex;
214
+ align-items: center;
215
+ font-size: 0.75rem;
216
+ line-height: 1;
217
+ color: $FONT_COLOR_02;
218
+ }
219
+ .sms-trai-sender-id-chip__lock svg {
220
+ width: 0.75rem;
221
+ height: 0.75rem;
222
+ }
223
+
224
+ .sms-trai-sender-id-locked-note {
225
+ margin-left: $CAP_SPACE_08;
226
+ color: $FONT_COLOR_02;
227
+ font-style: italic;
228
+ font-size: 0.75rem;
229
+ line-height: 1rem;
230
+ font-weight: 400;
231
+ }
232
+
233
+ .sms-trai-promotional-window-banner.ant-alert,
234
+ .sms-trai-legacy-warning-banner.ant-alert {
235
+ margin: 0 $CAP_SPACE_16 $CAP_SPACE_16 0;
236
+
237
+ .ant-alert-title,
238
+ .ant-alert-title span {
239
+ font-weight: $FONT_WEIGHT_REGULAR;
240
+ white-space: normal;
241
+ }
242
+ }
@@ -82,4 +82,44 @@ export default defineMessages({
82
82
  id: `${prefix}.incompleteOrInvalidTagsError`,
83
83
  defaultMessage: 'Incomplete or invalid tags. Please provide values for all variables (e.g. {{optout}}, {{fullname}}).',
84
84
  },
85
+ slotCharCounter: {
86
+ id: `${prefix}.slotCharCounter`,
87
+ defaultMessage: '{current}/{max}',
88
+ },
89
+ senderIdLockedNote: {
90
+ id: `${prefix}.senderIdLockedNote`,
91
+ defaultMessage: "— locked to this template, can't be overridden",
92
+ },
93
+ promotionalWindowBanner: {
94
+ id: `${prefix}.promotionalWindowBanner`,
95
+ defaultMessage: 'Promotional template — can only be delivered between 10:00 AM and 9:00 PM IST.',
96
+ },
97
+ smsLegacyFormatWarning: {
98
+ id: `${prefix}.smsLegacyFormatWarning`,
99
+ defaultMessage: "Uses old {legacyToken} placeholders — can't be used until re-registered with typed variables on the DLT portal.",
100
+ },
101
+ dltVarPlaceholderNumeric: {
102
+ id: `${prefix}.dltVarPlaceholderNumeric`,
103
+ defaultMessage: 'Digits 0–9 only. No letters, spaces, symbols or links.',
104
+ },
105
+ dltVarPlaceholderAlphanumeric: {
106
+ id: `${prefix}.dltVarPlaceholderAlphanumeric`,
107
+ defaultMessage: 'Letters & digits, common punctuation and spaces. No links, numbers as CTA, or email.',
108
+ },
109
+ dltVarPlaceholderUrl: {
110
+ id: `${prefix}.dltVarPlaceholderUrl`,
111
+ defaultMessage: 'A valid web link that matches a Call-To-Action (CTA) registered for this template.',
112
+ },
113
+ dltVarPlaceholderUrlott: {
114
+ id: `${prefix}.dltVarPlaceholderUrlott`,
115
+ defaultMessage: 'A valid app / OTT deep link that matches a registered CTA for this template.',
116
+ },
117
+ dltVarPlaceholderCbn: {
118
+ id: `${prefix}.dltVarPlaceholderCbn`,
119
+ defaultMessage: 'A valid phone number — digits with an optional +/country code. No free text.',
120
+ },
121
+ dltVarPlaceholderEmail: {
122
+ id: `${prefix}.dltVarPlaceholderEmail`,
123
+ defaultMessage: 'A valid email address in the form name@domain.tld.',
124
+ },
85
125
  });