@capillarytech/creatives-library 8.0.353-alpha.5 → 8.0.353-alpha.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (136) hide show
  1. package/constants/unified.js +29 -0
  2. package/package.json +1 -1
  3. package/services/tests/api.test.js +35 -20
  4. package/utils/commonUtils.js +19 -1
  5. package/utils/rcsPayloadUtils.js +92 -0
  6. package/utils/templateVarUtils.js +201 -0
  7. package/utils/tests/rcsPayloadUtils.test.js +226 -0
  8. package/utils/tests/templateVarUtils.test.js +204 -0
  9. package/v2Components/CapActionButton/constants.js +7 -0
  10. package/v2Components/CapActionButton/index.js +166 -108
  11. package/v2Components/CapActionButton/index.scss +157 -6
  12. package/v2Components/CapActionButton/messages.js +19 -3
  13. package/v2Components/CapActionButton/tests/index.test.js +41 -17
  14. package/v2Components/CapTagList/index.js +10 -0
  15. package/v2Components/CommonTestAndPreview/CustomValuesEditor.js +72 -49
  16. package/v2Components/CommonTestAndPreview/DeliverySettings/DeliverySettings.scss +8 -2
  17. package/v2Components/CommonTestAndPreview/DeliverySettings/ModifyDeliverySettings.js +213 -21
  18. package/v2Components/CommonTestAndPreview/DeliverySettings/constants.js +16 -0
  19. package/v2Components/CommonTestAndPreview/DeliverySettings/index.js +85 -10
  20. package/v2Components/CommonTestAndPreview/DeliverySettings/messages.js +30 -0
  21. package/v2Components/CommonTestAndPreview/DeliverySettings/utils/parseSenderDetailsResponse.js +79 -11
  22. package/v2Components/CommonTestAndPreview/SendTestMessage.js +10 -5
  23. package/v2Components/CommonTestAndPreview/UnifiedPreview/PreviewHeader.js +0 -17
  24. package/v2Components/CommonTestAndPreview/UnifiedPreview/RcsPreviewContent.js +157 -15
  25. package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +346 -146
  26. package/v2Components/CommonTestAndPreview/UnifiedPreview/index.js +138 -48
  27. package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +11 -0
  28. package/v2Components/CommonTestAndPreview/constants.js +38 -4
  29. package/v2Components/CommonTestAndPreview/index.js +691 -235
  30. package/v2Components/CommonTestAndPreview/messages.js +45 -3
  31. package/v2Components/CommonTestAndPreview/previewApiUtils.js +59 -0
  32. package/v2Components/CommonTestAndPreview/sagas.js +25 -6
  33. package/v2Components/CommonTestAndPreview/tests/CustomValuesEditor.test.js +308 -284
  34. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/ModifyDeliverySettings.test.js +231 -65
  35. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/index.test.js +118 -5
  36. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/utils/parseSenderDetailsResponse.test.js +341 -0
  37. package/v2Components/CommonTestAndPreview/tests/PreviewSection.test.js +8 -1
  38. package/v2Components/CommonTestAndPreview/tests/SendTestMessage.test.js +34 -13
  39. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/PreviewHeader.test.js +0 -159
  40. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/RcsPreviewContent.test.js +281 -283
  41. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/index.test.js +199 -256
  42. package/v2Components/CommonTestAndPreview/tests/constants.test.js +1 -2
  43. package/v2Components/CommonTestAndPreview/tests/index.test.js +132 -198
  44. package/v2Components/CommonTestAndPreview/tests/previewApiUtils.test.js +67 -0
  45. package/v2Components/CommonTestAndPreview/tests/sagas.test.js +36 -26
  46. package/v2Components/FormBuilder/index.js +11 -6
  47. package/v2Components/SmsFallback/SmsFallbackLocalSelector.js +91 -0
  48. package/v2Components/SmsFallback/constants.js +73 -0
  49. package/v2Components/SmsFallback/index.js +956 -0
  50. package/v2Components/SmsFallback/index.scss +265 -0
  51. package/v2Components/SmsFallback/messages.js +78 -0
  52. package/v2Components/SmsFallback/smsFallbackUtils.js +119 -0
  53. package/v2Components/SmsFallback/tests/SmsFallbackLocalSelector.test.js +50 -0
  54. package/v2Components/SmsFallback/tests/rcsSmsFallback.acceptance.test.js +147 -0
  55. package/v2Components/SmsFallback/tests/smsFallbackHandlers.test.js +304 -0
  56. package/v2Components/SmsFallback/tests/smsFallbackUi.test.js +223 -0
  57. package/v2Components/SmsFallback/tests/smsFallbackUtils.test.js +309 -0
  58. package/v2Components/SmsFallback/tests/useLocalTemplateList.test.js +422 -0
  59. package/v2Components/SmsFallback/useLocalTemplateList.js +92 -0
  60. package/v2Components/TemplatePreview/_templatePreview.scss +38 -23
  61. package/v2Components/TemplatePreview/constants.js +2 -0
  62. package/v2Components/TemplatePreview/index.js +143 -31
  63. package/v2Components/TemplatePreview/tests/index.test.js +142 -0
  64. package/v2Components/TestAndPreviewSlidebox/index.js +15 -3
  65. package/v2Components/TestAndPreviewSlidebox/sagas.js +11 -4
  66. package/v2Components/TestAndPreviewSlidebox/tests/saga.test.js +3 -1
  67. package/v2Components/VarSegmentMessageEditor/constants.js +2 -0
  68. package/v2Components/VarSegmentMessageEditor/index.js +125 -0
  69. package/v2Components/VarSegmentMessageEditor/index.scss +46 -0
  70. package/v2Containers/App/constants.js +0 -3
  71. package/v2Containers/CreativesContainer/CreativesSlideBoxWrapper.js +43 -0
  72. package/v2Containers/CreativesContainer/SlideBoxContent.js +36 -4
  73. package/v2Containers/CreativesContainer/SlideBoxFooter.js +10 -1
  74. package/v2Containers/CreativesContainer/SlideBoxHeader.js +29 -4
  75. package/v2Containers/CreativesContainer/constants.js +9 -0
  76. package/v2Containers/CreativesContainer/embeddedSlideboxUtils.js +79 -0
  77. package/v2Containers/CreativesContainer/index.js +322 -103
  78. package/v2Containers/CreativesContainer/index.scss +51 -1
  79. package/v2Containers/CreativesContainer/tests/SlideBoxContent.localTemplates.test.js +90 -0
  80. package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +78 -34
  81. package/v2Containers/CreativesContainer/tests/SlideBoxHeader.test.js +79 -16
  82. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +8 -0
  83. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxHeader.test.js.snap +357 -98
  84. package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +20 -15
  85. package/v2Containers/CreativesContainer/tests/embeddedSlideboxUtils.test.js +258 -0
  86. package/v2Containers/CreativesContainer/tests/index.test.js +71 -9
  87. package/v2Containers/CreativesContainer/tests/useLocalTemplatesProp.test.js +125 -0
  88. package/v2Containers/MobilePush/Create/test/saga.test.js +2 -2
  89. package/v2Containers/Rcs/constants.js +119 -10
  90. package/v2Containers/Rcs/index.js +2445 -813
  91. package/v2Containers/Rcs/index.scss +280 -8
  92. package/v2Containers/Rcs/messages.js +34 -3
  93. package/v2Containers/Rcs/rcsLibraryHydrationUtils.js +225 -0
  94. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +98018 -70073
  95. package/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap +0 -5
  96. package/v2Containers/Rcs/tests/index.test.js +152 -121
  97. package/v2Containers/Rcs/tests/mockData.js +38 -0
  98. package/v2Containers/Rcs/tests/rcsLibraryHydrationUtils.test.js +318 -0
  99. package/v2Containers/Rcs/tests/utils.test.js +646 -30
  100. package/v2Containers/Rcs/utils.js +478 -11
  101. package/v2Containers/Sms/Create/index.js +106 -40
  102. package/v2Containers/Sms/smsFormDataHelpers.js +67 -0
  103. package/v2Containers/Sms/tests/smsFormDataHelpers.test.js +253 -0
  104. package/v2Containers/SmsTrai/Create/index.js +9 -4
  105. package/v2Containers/SmsTrai/Edit/constants.js +2 -0
  106. package/v2Containers/SmsTrai/Edit/index.js +640 -130
  107. package/v2Containers/SmsTrai/Edit/index.scss +121 -0
  108. package/v2Containers/SmsTrai/Edit/messages.js +14 -4
  109. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +4328 -2375
  110. package/v2Containers/SmsWrapper/index.js +37 -8
  111. package/v2Containers/TagList/index.js +6 -0
  112. package/v2Containers/Templates/TemplatesActionBar.js +101 -0
  113. package/v2Containers/Templates/_templates.scss +166 -9
  114. package/v2Containers/Templates/actions.js +11 -0
  115. package/v2Containers/Templates/constants.js +2 -0
  116. package/v2Containers/Templates/index.js +122 -120
  117. package/v2Containers/Templates/sagas.js +56 -12
  118. package/v2Containers/Templates/tests/TemplatesActionBar.test.js +120 -0
  119. package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +1062 -1017
  120. package/v2Containers/Templates/tests/sagas.test.js +199 -16
  121. package/v2Containers/Templates/tests/smsTemplatesListApi.test.js +180 -0
  122. package/v2Containers/Templates/utils/smsTemplatesListApi.js +79 -0
  123. package/v2Containers/TemplatesV2/TemplatesV2.style.js +72 -1
  124. package/v2Containers/TemplatesV2/index.js +86 -23
  125. package/v2Containers/TemplatesV2/tests/TemplatesV2.localTemplates.test.js +131 -0
  126. package/v2Containers/WeChat/MapTemplates/test/saga.test.js +9 -9
  127. package/v2Containers/WebPush/Create/index.js +8 -91
  128. package/v2Containers/WebPush/Create/index.scss +0 -7
  129. package/v2Containers/Whatsapp/index.js +3 -20
  130. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +578 -34
  131. package/v2Components/CommonTestAndPreview/UnifiedPreview/WebPushPreviewContent.js +0 -169
  132. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/WebPushPreviewContent.test.js +0 -522
  133. package/v2Containers/App/tests/constants.test.js +0 -61
  134. package/v2Containers/Templates/tests/webpush.test.js +0 -375
  135. package/v2Containers/WebPush/Create/tests/getTemplateContent.test.js +0 -338
  136. package/v2Containers/WebPush/Create/tests/testAndPreviewIntegration.test.js +0 -325
@@ -5,18 +5,56 @@
5
5
  position: fixed;
6
6
  bottom: 0;
7
7
  width: 100%;
8
- margin-left: -32px;
8
+ margin-left: -2rem;
9
9
  padding: $CAP_SPACE_32 $CAP_SPACE_24;
10
- margin-top: 40px;
10
+ margin-top: 2.5rem;
11
11
  z-index: 1;
12
12
  .ant-btn {
13
13
  margin-right: $CAP_SPACE_16;
14
14
  }
15
+
16
+ .rcs-test-preview-btn {
17
+ margin-left: $CAP_SPACE_08;
18
+ }
15
19
  }
16
20
 
17
21
  .cap-rcs-creatives {
22
+ .rcs-creative-name-readonly {
23
+ margin-bottom: $CAP_SPACE_24;
24
+ }
25
+
26
+ .rcs-creative-name-value {
27
+ margin-top: $CAP_SPACE_08;
28
+ font-weight: 400;
29
+ }
30
+
18
31
  .rcs-scroll-div {
19
- margin-bottom: 150px;
32
+ margin-bottom: 9.375rem;
33
+ }
34
+
35
+ &.rcs-edit-mode {
36
+ .rcs-scroll-div {
37
+ margin-bottom: 15.625rem;
38
+ }
39
+
40
+ .rcs-edit-template-message-input {
41
+ background-color: $CAP_G10;
42
+ padding: $CAP_SPACE_12 $CAP_SPACE_16;
43
+ }
44
+
45
+ .rcs-edit-template-message-split {
46
+ margin-bottom: $CAP_SPACE_08;
47
+ overflow: hidden;
48
+ text-overflow: ellipsis;
49
+ color: $FONT_COLOR_03;
50
+ font-weight: 500;
51
+ }
52
+ }
53
+
54
+ /* Keep message box text lighter across RCS flows */
55
+ .rcs-edit-template-message-split {
56
+ color: $FONT_COLOR_03;
57
+ font-weight: 500;
20
58
  }
21
59
  .rcs-preview-container {
22
60
  .cap-row-v2 {
@@ -24,18 +62,52 @@
24
62
  }
25
63
  }
26
64
  .rcs-optional-label {
27
- margin-left: 8px;
65
+ margin-left: $CAP_SPACE_08;
28
66
  color: $FONT_COLOR_03;
29
67
  font-weight: normal;
30
68
  }
31
69
  .rcs_text_area_wrapper {
32
70
  position: relative;
33
71
  }
72
+ .rcs-edit-template-message-input .ant-input,
73
+ .rcs-edit-template-message-input textarea.ant-input {
74
+ border-color: $CAP_G07;
75
+ box-shadow: none;
76
+ min-height: 2.5rem;
77
+ padding-top: $CAP_SPACE_08;
78
+ padding-bottom: $CAP_SPACE_08;
79
+ overflow: hidden;
80
+ }
81
+ .rcs-edit-template-message-input .ant-input:focus,
82
+ .rcs-edit-template-message-input .ant-input:active,
83
+ .rcs-edit-template-message-input textarea.ant-input:focus,
84
+ .rcs-edit-template-message-input textarea.ant-input:active {
85
+ border-color: $CAP_G07;
86
+ box-shadow: none;
87
+ outline: none;
88
+ }
34
89
  .rcs-button{
35
90
  display: inline-grid;
36
91
  }
37
- .rcs-button-label{
38
- margin-bottom: 17px;
92
+ .rcs-form-field-caption {
93
+ margin-bottom: $CAP_SPACE_16;
94
+ }
95
+
96
+ .rcs-form-section-heading {
97
+ display: flex;
98
+ margin-top: $CAP_SPACE_20;
99
+ }
100
+
101
+ .rcs-image-upload--top-spacing {
102
+ padding-top: $CAP_SPACE_20;
103
+ }
104
+
105
+ .rcs-dimension-select--bottom-spacing {
106
+ margin-bottom: $CAP_SPACE_20;
107
+ }
108
+
109
+ .rcs-fallback-section-divider {
110
+ margin: $CAP_SPACE_28 0;
39
111
  }
40
112
  .disabled-button{
41
113
  opacity: 0.5;
@@ -69,9 +141,11 @@
69
141
  overflow: hidden;
70
142
  &.title{
71
143
  -webkit-line-clamp: 1;
144
+ line-clamp: 1;
72
145
  }
73
146
  &.desc{
74
147
  -webkit-line-clamp: 5;
148
+ line-clamp: 5;
75
149
  }
76
150
  &.rcs-button-text{
77
151
  padding-top: 13px;
@@ -80,13 +154,71 @@
80
154
  }
81
155
  .rcs-listing-image {
82
156
  margin-bottom: 8px;
83
- width: 220px;
84
- height: 103px;
157
+ width: 100%;
158
+ max-height: 7.357rem;
159
+ object-fit: cover;
160
+ display: block;
161
+ }
162
+
163
+ // Templates listing preview (RCS): carousel should show a "peek" of next card like WhatsApp listing.
164
+ &.rcs-template-listing-preview {
165
+ &.is-carousel {
166
+ .rcs-listing-carousel-scroll {
167
+ overflow-x: auto;
168
+ display: flex;
169
+ padding-top: $CAP_SPACE_06;
170
+ padding-right: $CAP_SPACE_06;
171
+ white-space: nowrap;
172
+ scrollbar-width: none;
173
+ &::-webkit-scrollbar {
174
+ display: none;
175
+ }
176
+ // Listing should show a peek (no horizontal scroll interaction needed)
177
+ overflow: hidden;
178
+ }
179
+
180
+ .rcs-listing-carousel-card {
181
+ padding: $CAP_SPACE_04 0 $CAP_SPACE_08;
182
+ border-radius: $CAP_SPACE_06;
183
+ background-color: $CAP_WHITE;
184
+ width: 80%;
185
+ flex-shrink: 0;
186
+ margin-right: $CAP_SPACE_04;
187
+ white-space: normal;
188
+ text-align: left;
189
+ }
190
+
191
+ .rcs-listing-carousel-img {
192
+ width: 100%;
193
+ height: 6.4375rem; // match rcs-listing-image height (103px)
194
+ object-fit: cover;
195
+ display: block;
196
+ margin-bottom: $CAP_SPACE_08;
197
+ }
198
+
199
+ .rcs-listing-carousel-title,
200
+ .rcs-listing-carousel-desc {
201
+ display: block;
202
+ padding: 0 $CAP_SPACE_12;
203
+ }
204
+ .rcs-listing-carousel-desc {
205
+ margin-top: $CAP_SPACE_04;
206
+ }
207
+ }
85
208
  }
86
209
  .fallback-sms-length {
87
210
  margin-top: 13px;
88
211
  float: right;
89
212
  }
213
+ .rcs-character-count {
214
+ color: $FONT_COLOR_03;
215
+ }
216
+
217
+ .rcs-carousel-character-count-row {
218
+ display: flex;
219
+ justify-content: flex-end;
220
+ margin-top: $CAP_SPACE_08;
221
+ }
90
222
  .rcs-fallback-len-error{
91
223
  margin-top: 8px;
92
224
  }
@@ -120,6 +252,140 @@
120
252
  #rcs-dimension-select{
121
253
  width: 100%;
122
254
  }
255
+
256
+ .rcs-carousel-section {
257
+ margin-top: $CAP_SPACE_12;
258
+ }
259
+
260
+ .rcs-carousel-dimension-section {
261
+ margin-top: 1.25rem;
262
+ }
263
+
264
+ .rcs-carousel-selected-dimension {
265
+ display: block;
266
+ margin-top: 8px;
267
+ color: $FONT_COLOR_03;
268
+ }
269
+
270
+ .rcs-carousel-dimension-label {
271
+ display: inline-block;
272
+ margin-bottom: 6px;
273
+ }
274
+
275
+ #rcs-carousel-height-select,
276
+ #rcs-carousel-width-select {
277
+ width: 100%;
278
+ }
279
+
280
+ .rcs-carousel-tab {
281
+ margin-top: 12px;
282
+ }
283
+
284
+ .rcs-carousel-card {
285
+ margin-top: 12px;
286
+
287
+ // Match carousel card header: title left, icon-only delete right (Figma / product reference).
288
+ &.cap-card-v2.ant-card {
289
+ .ant-card-head {
290
+ min-height: 48px;
291
+ padding: 0 $CAP_SPACE_16;
292
+ border-bottom: 1px solid #e8e8e8;
293
+ }
294
+
295
+ .ant-card-head-title {
296
+ padding: $CAP_SPACE_12 0;
297
+ font-weight: 600;
298
+ color: $CAP_G01;
299
+ }
300
+
301
+ .ant-card-extra {
302
+ padding: $CAP_SPACE_08 0;
303
+ }
304
+ }
305
+
306
+ .rcs-carousel-card-delete.ant-btn.cap-button-v2 {
307
+ display: inline-flex;
308
+ align-items: center;
309
+ justify-content: center;
310
+ min-width: auto;
311
+ width: 2rem;
312
+ height: 2rem;
313
+ padding: 0;
314
+ border: none;
315
+ box-shadow: none;
316
+ color: $CAP_G01;
317
+
318
+ &:not(:disabled):hover {
319
+ // Match $CAP_SECONDARY base from cap-ui-library (avoid map-get — node-sass can mis-resolve imported maps).
320
+ color: #2466ea;
321
+ background-color: rgba(0, 0, 0, 0.04);
322
+ }
323
+
324
+ &:disabled {
325
+ color: rgba(0, 0, 0, 0.25);
326
+ background: transparent;
327
+ }
328
+ }
329
+
330
+ .rcs-carousel-delete-tooltip-wrap.button-disabled-tooltip-wrapper {
331
+ display: inline-flex;
332
+ align-items: center;
333
+ cursor: not-allowed;
334
+ }
335
+ }
336
+
337
+ .rcs-carousel-card-row {
338
+ margin-bottom: 12px;
339
+ }
340
+
341
+ .rcs-carousel-media-selection {
342
+ margin-top: 0.75rem;
343
+ margin-bottom: 0.75rem;
344
+ align-items: center;
345
+ }
346
+
347
+ // Carousel upload previews: keep media preview compact (CapImageUpload uses height=400 by default)
348
+ .rcs-carousel-media-upload {
349
+ padding-top: $CAP_SPACE_20;
350
+ .image-container {
351
+ background-image: none;
352
+ border: 0;
353
+ width: 100%;
354
+ max-width: 560px;
355
+ }
356
+
357
+ .image-container img {
358
+ width: 100%;
359
+ height: auto !important;
360
+ max-height: 240px;
361
+ object-fit: contain;
362
+ display: block;
363
+ }
364
+ }
365
+
366
+ .rcs-carousel-card-divider {
367
+ margin: $CAP_SPACE_24 0;
368
+ }
369
+
370
+ .rcs-content-section-divider {
371
+ margin: $CAP_SPACE_28 0;
372
+ }
373
+
374
+ .rcs-edit-template-message-static-textarea {
375
+ background: #fafafa;
376
+ color: #888;
377
+
378
+ // Ensure styles apply to underlying textarea/input for CapInput TextArea.
379
+ textarea,
380
+ .ant-input {
381
+ background: #fafafa;
382
+ color: #888;
383
+ }
384
+ }
385
+
386
+ .add-carousel-content-button {
387
+ padding: 0 $CAP_SPACE_08;
388
+ }
123
389
  }
124
390
  .rcs-fallback-preview > .cap-slide-box-v2-container.size-r{
125
391
  min-width: 430px;
@@ -127,6 +393,12 @@
127
393
  width: 430px;
128
394
  }
129
395
 
396
+ // SMS fallback slidebox / tag popover: see v2Components/SmsFallback/index.scss (loaded with SmsFallback)
397
+
398
+ .template-status-container {
399
+ margin-bottom: $CAP_SPACE_24;
400
+ }
401
+
130
402
  .rcs-button-cta{
131
403
  margin-top: 1.25rem;
132
404
  margin-bottom: 0.75rem;
@@ -6,6 +6,14 @@ export default defineMessages({
6
6
  id: `${prefix}.mediaLabel`,
7
7
  defaultMessage: 'Media',
8
8
  },
9
+ mediaTypeLabel: {
10
+ id: `${prefix}.mediaTypeLabel`,
11
+ defaultMessage: 'Media type',
12
+ },
13
+ carouselMediaVideoOption: {
14
+ id: `${prefix}.carouselMediaVideoOption`,
15
+ defaultMessage: 'Video',
16
+ },
9
17
  approvedStatusMsg: {
10
18
  id: `${prefix}.approvedStatusMsg`,
11
19
  defaultMessage: 'This template has been approved',
@@ -194,6 +202,10 @@ export default defineMessages({
194
202
  id: `${prefix}.disabledCarouselTooltip`,
195
203
  defaultMessage: 'Not yet enabled. Coming soon!',
196
204
  },
205
+ rcsCarouselMinCardDeleteTooltip: {
206
+ id: `${prefix}.rcsCarouselMinCardDeleteTooltip`,
207
+ defaultMessage: 'At least 1 card needs to be added in the carousel template',
208
+ },
197
209
  templateNamePlaceholder: {
198
210
  id: `${prefix}.templateNamePlaceholder`,
199
211
  defaultMessage: 'Enter template name',
@@ -262,6 +274,10 @@ export default defineMessages({
262
274
  id: `${prefix}.fallbackLabel`,
263
275
  defaultMessage: 'Fallback SMS message',
264
276
  },
277
+ smsFallbackOptional: {
278
+ id: `${prefix}.smsFallbackOptional`,
279
+ defaultMessage: 'Fallback SMS message (Optional)',
280
+ },
265
281
  fallbackDesc: {
266
282
  id: `${prefix}.fallbackDesc`,
267
283
  defaultMessage: 'We would suggest to add fallback SMS',
@@ -281,7 +297,12 @@ export default defineMessages({
281
297
  },
282
298
  fallbackMsgPlaceholder: {
283
299
  id: `${prefix}.fallbackMsgPlaceholder`,
284
- defaultMessage: 'Enter the text message content for fallback SMS',
300
+ defaultMessage: 'Add labels or text',
301
+ },
302
+ /** Shown only when a {{…}} slot has no value — not the saved text (that uses the input value). */
303
+ rcsVarSlotPlaceholder: {
304
+ id: `${prefix}.rcsVarSlotPlaceholder`,
305
+ defaultMessage: 'Add labels or text',
285
306
  },
286
307
  totalCharacters: {
287
308
  id: `${prefix}.totalCharacters`,
@@ -457,7 +478,8 @@ export default defineMessages({
457
478
  },
458
479
  unknownCharactersError: {
459
480
  id: `${prefix}.unknownCharactersError`,
460
- defaultMessage: 'Only alphanumeric characters and underscore are allowed in custom param. Spaces/other special characters are not allowed.',
481
+ defaultMessage:
482
+ 'Only letters, numbers, underscores, and dots are allowed in custom param (e.g. tag.FORMAT_1). Spaces and other special characters are not allowed.',
461
483
  },
462
484
  emptyVariableError: {
463
485
  id: `${prefix}.emptyVariableError`,
@@ -487,4 +509,13 @@ export default defineMessages({
487
509
  id: `${prefix}.rcsTestPreviewDisabledTooltip`,
488
510
  defaultMessage: 'Preview and test is only available after the template is approved.',
489
511
  },
490
- });
512
+ reUpload: {
513
+ id: `${prefix}.reUpload`,
514
+ defaultMessage: 'Reupload',
515
+ },
516
+ /** Carousel video card thumbnail size label (see RCS_CAROUSEL_VIDEO_THUMBNAIL_DIMENSIONS). */
517
+ rcsCarouselVideoThumbnailLabel: {
518
+ id: `${prefix}.rcsCarouselVideoThumbnailLabel`,
519
+ defaultMessage: 'Thumbnail ({width} × {height})',
520
+ },
521
+ });
@@ -0,0 +1,225 @@
1
+ import isEmpty from 'lodash/isEmpty';
2
+ import get from 'lodash/get';
3
+ import { RCS_SMS_FALLBACK_VAR_MAPPED_PROP } from '../../v2Components/CommonTestAndPreview/constants';
4
+ import {
5
+ RCS_NUMERIC_VAR_NAME_REGEX,
6
+ RCS_CARD_VAR_MAPPED_SEMANTIC_KEY_REGEX,
7
+ } from './constants';
8
+
9
+ /** RCS card `cardVarMapped` / `rcsCardVarMapped` only (SMS fallback slot keys stay on `smsFallBackContent`). */
10
+ export function pickRcsCardVarMappedEntries(record) {
11
+ if (record == null || typeof record !== 'object' || Array.isArray(record)) return {};
12
+ return Object.fromEntries(
13
+ Object.entries(record).filter(([k]) => {
14
+ const key = String(k);
15
+ return (
16
+ RCS_NUMERIC_VAR_NAME_REGEX.test(key)
17
+ || RCS_CARD_VAR_MAPPED_SEMANTIC_KEY_REGEX.test(key)
18
+ );
19
+ }),
20
+ );
21
+ }
22
+
23
+ /**
24
+ * Nested `versions…smsFallBackContent` and root `smsFallBackContent` (CreativesContainer mirror)
25
+ * can diverge. Merge explicitly (avoid `get(..., {}) || root` — `{}` is truthy and blocks fallback).
26
+ *
27
+ * When both exist, **nested wins on overlap**: the canonical RCS path is what `createPayload` / submit
28
+ * update; the root mirror can lag behind campaign parent state and would otherwise keep old
29
+ * `message` / `rcsSmsFallbackVarMapped` in hydration and approval payloads.
30
+ */
31
+ export function mergeRcsSmsFallBackContentFromDetails(details) {
32
+ if (!details || typeof details !== 'object') return {};
33
+ const nestedSmsFallback = details?.versions?.base?.content?.RCS?.smsFallBackContent;
34
+ const rootSmsFallback = details?.smsFallBackContent;
35
+ const nestedRecord =
36
+ nestedSmsFallback != null && typeof nestedSmsFallback === 'object' ? nestedSmsFallback : {};
37
+ const rootRecord =
38
+ rootSmsFallback != null && typeof rootSmsFallback === 'object' ? rootSmsFallback : {};
39
+ return { ...rootRecord, ...nestedRecord };
40
+ }
41
+
42
+ /**
43
+ * Merge SMS fallback slot maps from API/templateData (`apiShape`) and editor state (`localShape`).
44
+ * Spreading `{ ...api, ...local }` lets `rcsSmsFallbackVarMapped: {}` on local wipe a populated API map,
45
+ * which keeps Done disabled in DLT campaigns until fixed.
46
+ */
47
+ export function mergeRcsSmsFallbackVarMapLayers(apiShape = {}, localShape = {}) {
48
+ const readMap = (o) => {
49
+ if (!o || typeof o !== 'object') return {};
50
+ const camel = o.rcsSmsFallbackVarMapped;
51
+ const kebab = o['rcs-sms-fallback-var-mapped'];
52
+ const src =
53
+ camel && typeof camel === 'object'
54
+ ? camel
55
+ : kebab && typeof kebab === 'object'
56
+ ? kebab
57
+ : {};
58
+ return { ...src };
59
+ };
60
+ return { ...readMap(apiShape), ...readMap(localShape) };
61
+ }
62
+
63
+ /**
64
+ * First non-empty trimmed string for SMS fallback body used in DLT slot checks / payload `templateContent`.
65
+ * Prefer **raw** template fields before resolved `message` so `{#…#}` / `{{…}}` tokens are not lost
66
+ * when `message` is consumer-resolved text (campaigns).
67
+ */
68
+ /**
69
+ * Whether an SMS fallback object carries template body, name, or var-map data (same rule as Rcs `createPayload`).
70
+ */
71
+ export function hasMeaningfulSmsFallbackShape(s) {
72
+ return !!(
73
+ s
74
+ && (
75
+ String(
76
+ s.content
77
+ || s.templateContent
78
+ || s.message
79
+ || s.smsContent
80
+ || s.smsTemplateContent
81
+ || '',
82
+ ).trim() !== ''
83
+ || String(s.templateName || s.smsTemplateName || '').trim() !== ''
84
+ || (s.rcsSmsFallbackVarMapped && Object.keys(s.rcsSmsFallbackVarMapped).length > 0)
85
+ || (s[RCS_SMS_FALLBACK_VAR_MAPPED_PROP]
86
+ && Object.keys(s[RCS_SMS_FALLBACK_VAR_MAPPED_PROP]).length > 0)
87
+ )
88
+ );
89
+ }
90
+
91
+ /**
92
+ * Library `templateData`: merged root + nested `smsFallBackContent` (nested wins), matching `createPayload`.
93
+ */
94
+ export function getLibrarySmsFallbackApiBaselineFromTemplateData(templateData) {
95
+ const smsFromTemplateRoot = get(
96
+ templateData,
97
+ 'versions.base.content.RCS.smsFallBackContent',
98
+ );
99
+ return {
100
+ ...(templateData?.smsFallBackContent && typeof templateData.smsFallBackContent === 'object'
101
+ ? templateData.smsFallBackContent
102
+ : {}),
103
+ ...(smsFromTemplateRoot && typeof smsFromTemplateRoot === 'object'
104
+ ? smsFromTemplateRoot
105
+ : {}),
106
+ };
107
+ }
108
+
109
+ export { extractRegisteredSenderIdsFromSmsFallbackRecord } from '../../utils/commonUtils';
110
+
111
+ export function pickFirstSmsFallbackTemplateString(sms = {}) {
112
+ if (!sms || typeof sms !== 'object') return '';
113
+ const keys = [
114
+ 'templateContent',
115
+ 'smsTemplateContent',
116
+ 'content',
117
+ 'smsContent',
118
+ 'message',
119
+ ];
120
+ for (let i = 0; i < keys.length; i += 1) {
121
+ const v = sms[keys[i]];
122
+ if (v == null) continue;
123
+ const s = String(v).trim();
124
+ if (s) return s;
125
+ }
126
+ return '';
127
+ }
128
+
129
+ /**
130
+ * Campaign reopen: payloads often keep tag/label values on numeric keys (`"1"`, `"2"`, …) while
131
+ * semantic keys (`user_name`, …) stay `""`. Copy non-empty slot values onto empty semantic keys so
132
+ * VarSegment editors prepopulate after Done → Edit without changing global resolve behavior.
133
+ */
134
+ export function syncCardVarMappedSemanticsFromSlots(
135
+ cardVarMappedInput,
136
+ templateTitle,
137
+ templateDesc,
138
+ rcsVarRegex,
139
+ ) {
140
+ const cardVarMappedSynced =
141
+ cardVarMappedInput != null && typeof cardVarMappedInput === 'object'
142
+ ? { ...cardVarMappedInput }
143
+ : {};
144
+ const getVarNameFromToken = (token = '') => token.replace(/^\{\{|\}\}$/g, '');
145
+ const templateVarTokens = [
146
+ ...(templateTitle?.match(rcsVarRegex) ?? []),
147
+ ...(templateDesc?.match(rcsVarRegex) ?? []),
148
+ ];
149
+ templateVarTokens.forEach((token, slotIndexZeroBased) => {
150
+ const semanticVarName = getVarNameFromToken(token);
151
+ if (!semanticVarName) return;
152
+ const numericSlotKey = String(slotIndexZeroBased + 1);
153
+ const semanticValueTrimmed = String(cardVarMappedSynced[semanticVarName] ?? '').trim();
154
+ const numericSlotValueTrimmed = String(cardVarMappedSynced[numericSlotKey] ?? '').trim();
155
+ if (!semanticValueTrimmed && numericSlotValueTrimmed) {
156
+ cardVarMappedSynced[semanticVarName] = cardVarMappedSynced[numericSlotKey];
157
+ }
158
+ });
159
+ return cardVarMappedSynced;
160
+ }
161
+
162
+ /**
163
+ * Maps resolved campaign values back to `{{semanticKey}}` using `cardVarMapped` keys.
164
+ * Used when hydrating library/journey RCS edit from payloads without existing `{{…}}` tokens.
165
+ */
166
+ export function getUnmappedDesc(str, mapping) {
167
+ if (!str) return '';
168
+ if (!mapping || Object.keys(mapping).length === 0) return str;
169
+ let result = str;
170
+ const replacements = [];
171
+ Object.entries(mapping).forEach(([key, value]) => {
172
+ const raw = (value ?? '').toString();
173
+ if (!raw || raw?.trim?.() === '') return;
174
+ const braced = /^\{\{[\s\S]*\}\}$/.test(raw) ? raw : `{{${raw}}}`;
175
+ replacements.push({ key, needle: raw });
176
+ if (braced !== raw) replacements.push({ key, needle: braced });
177
+ });
178
+ const seen = new Set();
179
+ const uniq = replacements
180
+ .filter(({ key, needle }) => {
181
+ const id = `${key}::${needle}`;
182
+ if (seen.has(id)) return false;
183
+ seen.add(id);
184
+ return true;
185
+ })
186
+ .sort((a, b) => (b.needle.length - a.needle.length));
187
+
188
+ uniq.forEach(({ key, needle }) => {
189
+ if (!needle) return;
190
+ const escaped = needle.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
191
+ const regex = new RegExp(escaped, 'g');
192
+ result = result.replace(regex, `{{${key}}}`);
193
+ });
194
+ return result;
195
+ }
196
+
197
+ export function hasRcsVarTokens(s, rcsVarRegex) {
198
+ return (s?.match(rcsVarRegex) ?? []).length > 0;
199
+ }
200
+
201
+ /**
202
+ * Library / journey: only run `getUnmappedDesc` when the loaded copy has no `{{…}}` tokens
203
+ * (e.g. fully resolved campaign text). If the API already has numeric or named slots, keep as-is.
204
+ */
205
+ export function normalizeLibraryLoadedTitleDesc({
206
+ loadedTitle,
207
+ loadedDesc,
208
+ isFullMode,
209
+ cardVarMappedAfterHydration,
210
+ rcsVarRegex,
211
+ }) {
212
+ const normalizedTitle =
213
+ !isFullMode
214
+ && !isEmpty(cardVarMappedAfterHydration)
215
+ && !hasRcsVarTokens(loadedTitle, rcsVarRegex)
216
+ ? getUnmappedDesc(loadedTitle, cardVarMappedAfterHydration)
217
+ : loadedTitle;
218
+ const normalizedDesc =
219
+ !isFullMode
220
+ && !isEmpty(cardVarMappedAfterHydration)
221
+ && !hasRcsVarTokens(loadedDesc, rcsVarRegex)
222
+ ? getUnmappedDesc(loadedDesc, cardVarMappedAfterHydration)
223
+ : loadedDesc;
224
+ return { normalizedTitle, normalizedDesc };
225
+ }