@capillarytech/creatives-library 9.0.56-alpha.6 → 9.0.56-alpha.8
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/package.json +1 -1
- package/services/api.js +1 -1
- package/utils/templateVarUtils.js +1 -17
- package/v2Components/CapActionButton/index.js +73 -60
- package/v2Components/CapActionButton/index.scss +44 -28
- package/v2Components/CapActionButton/messages.js +7 -3
- package/v2Components/CapActionButton/tests/index.test.js +17 -1
- package/v2Components/CapWhatsappCTA/messages.js +4 -0
- package/v2Components/CapWhatsappCarouselButton/index.js +42 -33
- package/v2Components/CapWhatsappCarouselButton/index.scss +44 -2
- package/v2Components/CommonTestAndPreview/UnifiedPreview/index.js +1 -2
- package/v2Components/CommonTestAndPreview/index.js +3 -6
- package/v2Containers/CommunicationFlow/CommunicationFlow.js +12 -0
- package/v2Containers/CommunicationFlow/CommunicationFlow.scss +2 -14
- package/v2Containers/CommunicationFlow/CommunicationFlowCard.js +10 -2
- package/v2Containers/CommunicationFlow/Tests/CommunicationFlow.test.js +35 -3
- package/v2Containers/CommunicationFlow/Tests/CommunicationFlowCard.test.js +58 -0
- package/v2Containers/CommunicationFlow/constants.js +1 -3
- package/v2Containers/CommunicationFlow/messages.js +4 -0
- package/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/ChannelSelectionStep.js +5 -1
- package/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/Tests/ChannelSelectionStep.test.js +64 -0
- package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/DeliverySettingsSection.js +19 -4
- package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/SenderDetails.js +8 -4
- package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/DeliverySettingsSection.test.js +11 -0
- package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/SenderDetails.test.js +26 -0
- package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/deliverySettingsConfig.js +3 -3
- package/v2Containers/CreativesContainer/SlideBoxContent.js +15 -0
- package/v2Containers/CreativesContainer/SlideBoxFooter.js +6 -2
- package/v2Containers/CreativesContainer/index.js +27 -0
- package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +29 -0
- package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +13 -0
- package/v2Containers/MobilePush/Create/index.js +18 -1
- package/v2Containers/MobilePush/Create/test/contentValidity.test.js +96 -0
- package/v2Containers/MobilePush/Edit/index.js +18 -1
- package/v2Containers/MobilePush/Edit/test/contentValidity.test.js +116 -0
- package/v2Containers/MobilePush/commonMethods.js +49 -1
- package/v2Containers/MobilePushNew/index.js +29 -4
- package/v2Containers/MobilePushNew/tests/index.test.js +119 -0
- package/v2Containers/MobilePushNew/tests/utils.test.js +82 -0
- package/v2Containers/MobilePushNew/utils.js +34 -1
- package/v2Containers/MobilepushWrapper/index.js +3 -1
- package/v2Containers/Rcs/index.js +34 -0
- package/v2Containers/Rcs/index.scss +15 -0
- package/v2Containers/Rcs/tests/index.test.js +67 -0
- package/v2Containers/Sms/Create/index.js +3 -1
- package/v2Containers/Sms/Edit/index.js +25 -0
- package/v2Containers/Sms/Edit/tests/index.test.js +85 -0
- package/v2Containers/Viber/index.js +24 -1
- package/v2Containers/Viber/tests/index.test.js +103 -0
- package/v2Containers/WebPush/Create/index.js +24 -0
- package/v2Containers/WebPush/Create/tests/contentValidity.test.js +294 -0
- package/v2Containers/Whatsapp/index.js +20 -4
- package/v2Containers/Whatsapp/tests/index.test.js +115 -0
- package/v2Containers/Zalo/index.js +28 -1
- package/v2Containers/Zalo/tests/index.test.js +99 -0
package/package.json
CHANGED
package/services/api.js
CHANGED
|
@@ -701,7 +701,7 @@ export const createCentralCommsMetaId = (payload, metaType = TRANSACTION) => {
|
|
|
701
701
|
return request(url, getAPICallObject('POST', payload, false, false, false, true));
|
|
702
702
|
};
|
|
703
703
|
|
|
704
|
-
// CCS CommDefinition API
|
|
704
|
+
// Use the CCS CommDefinition API for CommunicationFlow Save; keep legacy messageMeta APIs for CreativesContainer.
|
|
705
705
|
const COMM_DEFINITIONS_PATH = `${API_ENDPOINT}/comm-definitions`;
|
|
706
706
|
|
|
707
707
|
export const createCommDefinition = (payload) => {
|
|
@@ -115,23 +115,7 @@ export const extractTemplateVariables = (templateStr = '', captureRegex) => {
|
|
|
115
115
|
return variables;
|
|
116
116
|
};
|
|
117
117
|
|
|
118
|
-
|
|
119
|
-
* Reconciles a var-value map from an external source (e.g. a saved CommDefinition) into this
|
|
120
|
-
* UI's own `${token}_${segmentIndex}` slot-key format used by the WhatsApp/RCS editors' own
|
|
121
|
-
* text-merge logic.
|
|
122
|
-
*
|
|
123
|
-
* Two shapes are recognized:
|
|
124
|
-
* - Slot format: keys already look like `${token}_${index}` (contain an underscore) — used as-is.
|
|
125
|
-
* - CCS format: keys are plain sequential occurrence indices ("0", "1", ...), one per variable in
|
|
126
|
-
* template order — unrelated to this UI's own array-position indexing (which depends on how many
|
|
127
|
-
* plain-text segments fall between variables) — so it's remapped by walking `segments` in order
|
|
128
|
-
* and assigning the Nth variable occurrence to `rawVarMap[N]`.
|
|
129
|
-
*
|
|
130
|
-
* @param {Object} rawVarMap
|
|
131
|
-
* @param {string[]} segments - text+var segments, e.g. from `splitContentByOrderedVarTokens`
|
|
132
|
-
* @param {RegExp} regex - matches a variable token
|
|
133
|
-
* @returns {Object} `${token}_${segmentIndex}` -> value
|
|
134
|
-
*/
|
|
118
|
+
// Reconcile slot-format (`${token}_${index}`) and CCS-format (`"0"`, `"1"`, ...) var maps into the editor's `${token}_${segmentIndex}` slot-key format.
|
|
135
119
|
export const reconcileVarMapToSlotFormat = (rawVarMap = {}, segments = [], regex) => {
|
|
136
120
|
if (Object.keys(rawVarMap ?? {}).length === 0) return {};
|
|
137
121
|
const isSlotFormat = Object.keys(rawVarMap).some((key) => key.includes('_'));
|
|
@@ -101,10 +101,12 @@ export const CapActionButton = (props) => {
|
|
|
101
101
|
const onButtonTextChange = ({target}) => {
|
|
102
102
|
const { value, id } = target;
|
|
103
103
|
let errorMessage = '';
|
|
104
|
-
if (value.
|
|
104
|
+
if (!value.trim()) {
|
|
105
|
+
errorMessage = formatMessage(messages.ctaButtonTextEmptyMessage);
|
|
106
|
+
} else if (!isValidText(value)) {
|
|
107
|
+
errorMessage = formatMessage(messages.ctaButtonTextErrorMessage);
|
|
108
|
+
} else if (value.length > BTN_MAX_LENGTH) {
|
|
105
109
|
errorMessage = formatMessage(messages.ctaButtonTextLengthError, { maxLength: BTN_MAX_LENGTH });
|
|
106
|
-
} if (!isValidText(value)) {
|
|
107
|
-
errorMessage = formatMessage(messages.ctaButtonErrorMessage);
|
|
108
110
|
}
|
|
109
111
|
setButtonError(errorMessage);
|
|
110
112
|
updateDisplayAndPostback(value, id);
|
|
@@ -446,12 +448,11 @@ export const CapActionButton = (props) => {
|
|
|
446
448
|
gutter={9}
|
|
447
449
|
>
|
|
448
450
|
<CapColumn
|
|
449
|
-
|
|
450
|
-
className={`${ctaIsPhone ? 'whatsapp-saved-cta-phone-icon' : ''}`}
|
|
451
|
+
className={`rcs-saved-cta-cell ${ctaIsPhone ? 'whatsapp-saved-cta-phone-icon' : ''}`}
|
|
451
452
|
>
|
|
452
453
|
<CapIcon size="s" type={ctaIsPhone ? 'call' : (ctaIsReply ? 'small-link' : 'open-in-new')} />
|
|
453
454
|
</CapColumn>
|
|
454
|
-
<CapColumn
|
|
455
|
+
<CapColumn className="rcs-saved-cta-cell rcs-saved-cta-text-cell">
|
|
455
456
|
<CapLabel
|
|
456
457
|
type="label2"
|
|
457
458
|
className="rcs-saved-cta-button-text"
|
|
@@ -459,64 +460,76 @@ export const CapActionButton = (props) => {
|
|
|
459
460
|
{text}
|
|
460
461
|
</CapLabel>
|
|
461
462
|
</CapColumn>
|
|
462
|
-
<CapColumn
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
463
|
+
<CapColumn className="rcs-saved-cta-cell rcs-saved-cta-type-cell">
|
|
464
|
+
{!ctaIsPhone && !ctaIsReply && (
|
|
465
|
+
<CapLabel className="url-type" type="label2">
|
|
466
|
+
{(cta.urlType || RCS_CTA_URL_TYPE.STATIC) === RCS_CTA_URL_TYPE.DYNAMIC
|
|
467
|
+
? formatMessage(messages.ctaWebsiteTypeDynamic)
|
|
468
|
+
: formatMessage(messages.ctaWebsiteTypeStatic)}
|
|
469
|
+
</CapLabel>
|
|
470
|
+
)}
|
|
471
|
+
</CapColumn>
|
|
472
|
+
<CapColumn className="rcs-saved-cta-cell rcs-saved-cta-url-cell">
|
|
473
|
+
{ctaIsPhone && (
|
|
474
|
+
<CapLabel className="phone">+{phoneNumber}</CapLabel>
|
|
475
|
+
)}
|
|
476
|
+
{!ctaIsPhone && !ctaIsReply && (
|
|
477
|
+
<CapTooltip
|
|
478
|
+
title={url}
|
|
479
|
+
placement="top"
|
|
480
|
+
wrapperClassName="rcs-cta-url-tooltip-wrapper"
|
|
481
|
+
triggerClassName="rcs-cta-url-tooltip-trigger"
|
|
482
|
+
>
|
|
483
|
+
<CapLabel className="url">{url}</CapLabel>
|
|
484
|
+
</CapTooltip>
|
|
485
|
+
)}
|
|
486
|
+
</CapColumn>
|
|
487
|
+
<CapColumn className="rcs-saved-cta-cell rcs-saved-cta-actions-cell">
|
|
488
|
+
{isDynamicUrlSubtype && (url || '').includes(DYNAMIC_URL_SUFFIX) && (
|
|
489
|
+
<TagList
|
|
490
|
+
className="rcs-cta-taglist"
|
|
491
|
+
label={formatMessage(messages.ctaTagListLabel)}
|
|
492
|
+
onTagSelect={(data) => onTagSelect(data, index, url)}
|
|
493
|
+
location={location}
|
|
494
|
+
tags={tags}
|
|
495
|
+
injectedTags={injectedTags}
|
|
496
|
+
selectedOfferDetails={selectedOfferDetails}
|
|
497
|
+
onContextChange={onContextChange}
|
|
498
|
+
/>
|
|
499
|
+
)}
|
|
500
|
+
{isDynamicUrlSubtype && !(url || '').includes(DYNAMIC_URL_SUFFIX) && (
|
|
501
|
+
<CapTooltip title={formatMessage(messages.ctaTagListRevert)} placement="top">
|
|
502
|
+
<CapIcon
|
|
503
|
+
size="s"
|
|
504
|
+
type="return"
|
|
505
|
+
className="rcs-cta-tag-revert"
|
|
506
|
+
onClick={() => revertTagSelect(index, url)}
|
|
489
507
|
/>
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
508
|
+
</CapTooltip>
|
|
509
|
+
)}
|
|
510
|
+
{(isFullMode && !isEditFlow) && (
|
|
511
|
+
<CapRow
|
|
512
|
+
className="rcs-saved-cta-action-icons"
|
|
513
|
+
type="flex"
|
|
514
|
+
align="middle"
|
|
515
|
+
noWrap
|
|
516
|
+
>
|
|
517
|
+
<CapIcon
|
|
518
|
+
size="s"
|
|
519
|
+
type="edit"
|
|
520
|
+
className="rcs-saved-cta-edit-icon"
|
|
521
|
+
onClick={() => editCta(index)}
|
|
522
|
+
/>
|
|
523
|
+
{!(isHostIcs && index === 0) && !hideDeleteForSuggestionIndex(index) && !cannotDeleteSavedMandatory(index) && (
|
|
503
524
|
<CapIcon
|
|
504
525
|
size="s"
|
|
505
|
-
type="
|
|
506
|
-
className="rcs-saved-cta-
|
|
507
|
-
onClick={() =>
|
|
526
|
+
type="delete"
|
|
527
|
+
className="rcs-saved-cta-delete-icon"
|
|
528
|
+
onClick={() => deleteButtonHandler(index)}
|
|
508
529
|
/>
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
type="delete"
|
|
513
|
-
className="rcs-saved-cta-delete-icon"
|
|
514
|
-
onClick={() => deleteButtonHandler(index)}
|
|
515
|
-
/>
|
|
516
|
-
)}
|
|
517
|
-
</div>
|
|
518
|
-
)}
|
|
519
|
-
</div>
|
|
530
|
+
)}
|
|
531
|
+
</CapRow>
|
|
532
|
+
)}
|
|
520
533
|
</CapColumn>
|
|
521
534
|
</CapRow>
|
|
522
535
|
);
|
|
@@ -25,37 +25,51 @@
|
|
|
25
25
|
margin-bottom: $CAP_SPACE_12;
|
|
26
26
|
flex-wrap: nowrap;
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
// fixed span, the gutter/margin between columns had nowhere to come from
|
|
32
|
-
// and pushed this column's content past the card's right border.
|
|
33
|
-
.rcs-saved-cta-value-column {
|
|
28
|
+
.rcs-saved-cta-cell {
|
|
29
|
+
display: flex;
|
|
30
|
+
align-items: center;
|
|
34
31
|
min-width: 0;
|
|
32
|
+
|
|
33
|
+
// Cell children are flex items too — without this their own min-content
|
|
34
|
+
// (the full text/URL) makes the cell unshrinkable and pushes the row.
|
|
35
|
+
> * {
|
|
36
|
+
min-width: 0;
|
|
37
|
+
}
|
|
35
38
|
}
|
|
36
39
|
|
|
37
|
-
//
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
40
|
+
// Fixed basis for the 20-char button text cap.
|
|
41
|
+
.rcs-saved-cta-text-cell {
|
|
42
|
+
flex: 0 1 10rem;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Fixed, not auto: "Static" is narrower than "Dynamic", which left the URL
|
|
46
|
+
// starting at a different x per row.
|
|
47
|
+
.rcs-saved-cta-type-cell {
|
|
48
|
+
flex: 0 1 4rem;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.rcs-saved-cta-url-cell {
|
|
52
|
+
flex: 1 1 0;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.rcs-saved-cta-actions-cell {
|
|
56
|
+
flex: 0 0 auto;
|
|
44
57
|
justify-content: flex-end;
|
|
45
58
|
gap: $CAP_SPACE_08;
|
|
46
|
-
|
|
47
|
-
min-width: 0;
|
|
59
|
+
}
|
|
48
60
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
61
|
+
// CapTooltip nests `.url` two levels down, so these — not `.url` — are the
|
|
62
|
+
// boxes that must be allowed to shrink for the ellipsis to apply.
|
|
63
|
+
.rcs-cta-url-tooltip-wrapper {
|
|
64
|
+
flex: 1 1 0;
|
|
65
|
+
min-width: 0;
|
|
66
|
+
}
|
|
52
67
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
}
|
|
68
|
+
.rcs-cta-url-tooltip-trigger {
|
|
69
|
+
min-width: 0;
|
|
56
70
|
}
|
|
57
71
|
|
|
58
|
-
.url {
|
|
72
|
+
.url, .rcs-saved-cta-button-text, .url-type {
|
|
59
73
|
white-space: nowrap;
|
|
60
74
|
overflow: hidden;
|
|
61
75
|
text-overflow: ellipsis;
|
|
@@ -69,10 +83,6 @@
|
|
|
69
83
|
word-break: break-word;
|
|
70
84
|
}
|
|
71
85
|
|
|
72
|
-
.saved-button-text {
|
|
73
|
-
font-weight: 500;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
86
|
.rcs-saved-cta-action-icons {
|
|
77
87
|
display: flex;
|
|
78
88
|
align-items: center;
|
|
@@ -85,8 +95,6 @@
|
|
|
85
95
|
cursor: pointer;
|
|
86
96
|
}
|
|
87
97
|
|
|
88
|
-
// Dynamic-URL tag controls: keep them from stretching the flex row's
|
|
89
|
-
// height/width — same treatment as the edit/delete icons above.
|
|
90
98
|
.rcs-cta-tag-revert {
|
|
91
99
|
display: flex;
|
|
92
100
|
align-items: center;
|
|
@@ -95,7 +103,15 @@
|
|
|
95
103
|
}
|
|
96
104
|
|
|
97
105
|
.rcs-cta-taglist {
|
|
106
|
+
display: flex;
|
|
107
|
+
align-items: center;
|
|
98
108
|
flex-shrink: 0;
|
|
109
|
+
|
|
110
|
+
// CapTagList floats this trigger right, taking it out of flow, which
|
|
111
|
+
// let it paint over the edit icon instead of occupying real width.
|
|
112
|
+
.tooltip-add-label-container {
|
|
113
|
+
float: none;
|
|
114
|
+
}
|
|
99
115
|
}
|
|
100
116
|
}
|
|
101
117
|
|
|
@@ -83,9 +83,13 @@ export default defineMessages({
|
|
|
83
83
|
id: `${prefix}.comingSoonTooltip`,
|
|
84
84
|
defaultMessage: 'Right now only one Call to action button is enabled, in future you would be able to add upto 3 buttons.',
|
|
85
85
|
},
|
|
86
|
-
|
|
87
|
-
id: `${prefix}.
|
|
88
|
-
defaultMessage: 'Button text cannot be empty',
|
|
86
|
+
ctaButtonTextEmptyMessage: {
|
|
87
|
+
id: `${prefix}.ctaButtonTextEmptyMessage`,
|
|
88
|
+
defaultMessage: 'Button text cannot be empty or contain only whitespace',
|
|
89
|
+
},
|
|
90
|
+
ctaButtonTextErrorMessage: {
|
|
91
|
+
id: `${prefix}.ctaButtonTextErrorMessage`,
|
|
92
|
+
defaultMessage: 'Newlines, Emojis, or Special characters are not allowed',
|
|
89
93
|
},
|
|
90
94
|
ctaButtonTextLengthError: {
|
|
91
95
|
id: `${prefix}.ctaButtonTextLengthError`,
|
|
@@ -521,7 +521,23 @@ describe('CapActionButton', () => {
|
|
|
521
521
|
initializeComponent([initial]);
|
|
522
522
|
const buttontextInput = screen.getByPlaceholderText('Enter button text');
|
|
523
523
|
fireEvent.change(buttontextInput, { target: { value: '&*^', id: 0 } });
|
|
524
|
-
expect(screen.getByText(/
|
|
524
|
+
expect(screen.getByText(/special characters are not allowed/i)).toBeInTheDocument();
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
it('should show empty error for whitespace-only button text (onButtonTextChange)', () => {
|
|
528
|
+
const initial = {
|
|
529
|
+
index: 0,
|
|
530
|
+
type: RCS_BUTTON_TYPES.QUICK_REPLY,
|
|
531
|
+
text: 'Shop',
|
|
532
|
+
phoneNumber: '',
|
|
533
|
+
url: '',
|
|
534
|
+
postback: 'Shop',
|
|
535
|
+
isSaved: false,
|
|
536
|
+
};
|
|
537
|
+
initializeComponent([initial]);
|
|
538
|
+
const buttontextInput = screen.getByPlaceholderText('Enter button text');
|
|
539
|
+
fireEvent.change(buttontextInput, { target: { value: ' ', id: 0 } });
|
|
540
|
+
expect(screen.getByText(/cannot be empty or contain only whitespace/i)).toBeInTheDocument();
|
|
525
541
|
});
|
|
526
542
|
|
|
527
543
|
it('should show error for invalid url (onUrlChange)', () => {
|
|
@@ -57,6 +57,10 @@ export default defineMessages({
|
|
|
57
57
|
id: `${prefix}.ctaButtonErrorMessage`,
|
|
58
58
|
defaultMessage: "Newlines, Emojis, or Special characters are not allowed",
|
|
59
59
|
},
|
|
60
|
+
ctaButtonTextEmptyMessage: {
|
|
61
|
+
id: `${prefix}.ctaButtonTextEmptyMessage`,
|
|
62
|
+
defaultMessage: 'Button text cannot be empty or contain only whitespace',
|
|
63
|
+
},
|
|
60
64
|
addButton: {
|
|
61
65
|
id: `${prefix}.addButton`,
|
|
62
66
|
defaultMessage: 'Add button',
|
|
@@ -126,7 +126,9 @@ export const CapWhatsappCarouselButton = (props) => {
|
|
|
126
126
|
|
|
127
127
|
const onButtonTextChange = (value, buttonIndex) => {
|
|
128
128
|
let errorMessage = '';
|
|
129
|
-
if (!
|
|
129
|
+
if (!value.trim()) {
|
|
130
|
+
errorMessage = formatMessage(capWhatsappCTAMsg.ctaButtonTextEmptyMessage);
|
|
131
|
+
} else if (!isValidWhatsappCtaText(value)) {
|
|
130
132
|
errorMessage = formatMessage(capWhatsappCTAMsg.ctaButtonErrorMessage);
|
|
131
133
|
}
|
|
132
134
|
onValueChange(buttonIndex, [{fieldName: 'text', value}, {fieldName: 'textError', value: errorMessage}]);
|
|
@@ -333,12 +335,11 @@ export const CapWhatsappCarouselButton = (props) => {
|
|
|
333
335
|
noWrap
|
|
334
336
|
>
|
|
335
337
|
<CapColumn
|
|
336
|
-
|
|
337
|
-
className={`${isPhoneNumberType ? 'whatsapp-saved-cta-phone-icon' : ''}`}
|
|
338
|
+
className={`whatsapp-saved-cta-cell ${isPhoneNumberType ? 'whatsapp-saved-cta-phone-icon' : ''}`}
|
|
338
339
|
>
|
|
339
340
|
<CapIcon size="s" type={isPhoneNumberType ? 'call' : isURLtype ? 'launch' : 'six-dots'} />
|
|
340
341
|
</CapColumn>
|
|
341
|
-
<CapColumn
|
|
342
|
+
<CapColumn className="whatsapp-saved-cta-cell whatsapp-saved-cta-text-cell">
|
|
342
343
|
<CapLabel
|
|
343
344
|
type="label2"
|
|
344
345
|
className="whatsapp-saved-cta-button-text"
|
|
@@ -346,39 +347,47 @@ export const CapWhatsappCarouselButton = (props) => {
|
|
|
346
347
|
{button?.text}
|
|
347
348
|
</CapLabel>
|
|
348
349
|
</CapColumn>
|
|
349
|
-
|
|
350
|
-
|
|
350
|
+
<CapColumn className="whatsapp-saved-cta-cell whatsapp-saved-cta-type-cell">
|
|
351
|
+
{isURLtype && (
|
|
352
|
+
<CapLabel className="url-type" type="label2">
|
|
353
|
+
{button?.urlType === STATIC_URL
|
|
354
|
+
? formatMessage(capWhatsappCTAMsg.ctaWebsiteTypeStatic)
|
|
355
|
+
: formatMessage(capWhatsappCTAMsg.ctaWebsiteTypeDynamic)}
|
|
356
|
+
</CapLabel>
|
|
357
|
+
)}
|
|
358
|
+
</CapColumn>
|
|
359
|
+
<CapColumn className="whatsapp-saved-cta-cell whatsapp-saved-cta-url-cell whatsapp-url-preview-wrapper">
|
|
360
|
+
{isPhoneNumberType && (
|
|
351
361
|
<CapLabel className="phone">
|
|
352
362
|
{button?.phone_number}
|
|
353
363
|
</CapLabel>
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
364
|
+
)}
|
|
365
|
+
{isURLtype && (
|
|
366
|
+
<CapTooltip
|
|
367
|
+
title={button?.url}
|
|
368
|
+
placement="top"
|
|
369
|
+
wrapperClassName="whatsapp-cta-url-tooltip-wrapper"
|
|
370
|
+
triggerClassName="whatsapp-cta-url-tooltip-trigger"
|
|
371
|
+
>
|
|
372
|
+
<CapLabel className="url">{button?.url}</CapLabel>
|
|
373
|
+
</CapTooltip>
|
|
374
|
+
)}
|
|
375
|
+
</CapColumn>
|
|
376
|
+
<CapColumn className="whatsapp-saved-cta-cell whatsapp-saved-cta-actions-cell">
|
|
377
|
+
{!isEditFlow && (
|
|
378
|
+
<CapRow
|
|
379
|
+
className="whatsapp-saved-cta-action-icons"
|
|
380
|
+
type="flex"
|
|
381
|
+
align="middle"
|
|
382
|
+
noWrap
|
|
383
|
+
>
|
|
384
|
+
<CapIcon size="s" type="edit" onClick={() => handleEditButton(index)}/>
|
|
385
|
+
{parseInt(carouselIndex, 10) === 0 && (
|
|
386
|
+
<CapIcon size="s" type="delete" onClick={() => handleDeleteButton(index)} disabled={buttonData?.length === 1}/>
|
|
387
|
+
)}
|
|
371
388
|
</CapRow>
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
{!isEditFlow && (
|
|
375
|
-
<CapRow type='flex' align='middle' justify="end" gap={12} fillSpace>
|
|
376
|
-
<CapIcon size="s" type="edit" onClick={() => handleEditButton(index)}/>
|
|
377
|
-
{parseInt(carouselIndex, 10) === 0 && (
|
|
378
|
-
<CapIcon size="s" type="delete" onClick={() => handleDeleteButton(index)} disabled={buttonData?.length === 1}/>
|
|
379
|
-
)}
|
|
380
|
-
</CapRow>
|
|
381
|
-
)}
|
|
389
|
+
)}
|
|
390
|
+
</CapColumn>
|
|
382
391
|
{isEditFlow && button?.urlType === DYNAMIC_URL && button?.url.includes('{{1}}') && (
|
|
383
392
|
<CapColumn>
|
|
384
393
|
<TagList
|
|
@@ -37,7 +37,49 @@
|
|
|
37
37
|
height: auto;
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
.whatsapp-
|
|
41
|
-
|
|
40
|
+
.whatsapp-saved-cta-cell {
|
|
41
|
+
display: flex;
|
|
42
|
+
align-items: center;
|
|
43
|
+
min-width: 0;
|
|
44
|
+
|
|
45
|
+
> * {
|
|
46
|
+
min-width: 0;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.whatsapp-saved-cta-text-cell {
|
|
51
|
+
flex: 0 1 10rem;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.whatsapp-saved-cta-type-cell {
|
|
55
|
+
flex: 0 1 5.5rem;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.whatsapp-saved-cta-url-cell {
|
|
59
|
+
flex: 1 1 0;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.whatsapp-saved-cta-actions-cell {
|
|
63
|
+
flex: 0 0 4rem;
|
|
64
|
+
justify-content: flex-end;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.whatsapp-saved-cta-action-icons {
|
|
68
|
+
display: flex;
|
|
69
|
+
align-items: center;
|
|
70
|
+
flex-shrink: 0;
|
|
71
|
+
gap: $CAP_SPACE_12;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// CapTooltip nests `.url` two levels down, so these — not `.url` — are the
|
|
75
|
+
// boxes that must be allowed to shrink for the ellipsis to apply.
|
|
76
|
+
.whatsapp-cta-url-tooltip-wrapper {
|
|
77
|
+
flex: 1 1 0;
|
|
78
|
+
min-width: 0;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.whatsapp-cta-url-tooltip-trigger {
|
|
82
|
+
width: 100%;
|
|
83
|
+
min-width: 0;
|
|
42
84
|
}
|
|
43
85
|
}
|
|
@@ -140,8 +140,7 @@ const UnifiedPreview = ({
|
|
|
140
140
|
}
|
|
141
141
|
|
|
142
142
|
case CHANNELS.LINE:
|
|
143
|
-
// LINE
|
|
144
|
-
// preview. Rich types (image/video/sticker/imageMap/carousel/flex) aren't rendered yet.
|
|
143
|
+
// LINE currently supports plain text only, so reuse the SMS bubble preview; rich message types are not rendered yet.
|
|
145
144
|
return (
|
|
146
145
|
<SmsPreviewContent
|
|
147
146
|
content={typeof content === 'string' ? content : (content?.resolvedBody || '')}
|
|
@@ -209,7 +209,7 @@ const CommonTestAndPreview = (props) => {
|
|
|
209
209
|
wecrmAccounts = [],
|
|
210
210
|
isLoadingSenderDetails = false,
|
|
211
211
|
orgUnitId = -1,
|
|
212
|
-
// notify/ui only:
|
|
212
|
+
// notify/ui only:{commDefinitionId, referenceId} routes "Send test" to CCS's send/transaction endpoint.
|
|
213
213
|
ccsSendTransaction,
|
|
214
214
|
// Email-specific props
|
|
215
215
|
beeInstance,
|
|
@@ -3624,14 +3624,11 @@ const CommonTestAndPreview = (props) => {
|
|
|
3624
3624
|
setIsCustomerDataLoading(false);
|
|
3625
3625
|
}
|
|
3626
3626
|
};
|
|
3627
|
-
|
|
3627
|
+
|
|
3628
3628
|
/**
|
|
3629
3629
|
* Handle send test message
|
|
3630
3630
|
*/
|
|
3631
|
-
// notify/ui
|
|
3632
|
-
// real CommDefinition, instead of the legacy createMessageMeta/sendTestMessage flow below.
|
|
3633
|
-
// Dispatched via the same action/saga pattern as every other API call in this component,
|
|
3634
|
-
// rather than calling Api.sendTransactionalComm directly.
|
|
3631
|
+
// notify/ui sends tests via CCS's CommDefinition send/transaction endpoint using the standard action/saga flow, not the legacy messageMeta flow.
|
|
3635
3632
|
const handleCcsSendTestMessage = () => {
|
|
3636
3633
|
const allUserIds = [];
|
|
3637
3634
|
selectedTestEntities.forEach((entityId) => {
|
|
@@ -221,6 +221,10 @@ const CommunicationFlow = ({
|
|
|
221
221
|
singleChannelStrategy,
|
|
222
222
|
})
|
|
223
223
|
: await createCommDefinition(payload);
|
|
224
|
+
if (res?.isError) {
|
|
225
|
+
setSaveError(formatMessage(messages.genericSaveError));
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
224
228
|
if (isDuplicateReferenceIdError(res)) {
|
|
225
229
|
setSaveError(formatMessage(messages.duplicateReferenceIdError));
|
|
226
230
|
return;
|
|
@@ -234,6 +238,9 @@ const CommunicationFlow = ({
|
|
|
234
238
|
version: data.version?.version ?? data.version ?? 1,
|
|
235
239
|
status: data.status,
|
|
236
240
|
};
|
|
241
|
+
} else {
|
|
242
|
+
setSaveError(formatMessage(messages.genericSaveError));
|
|
243
|
+
return;
|
|
237
244
|
}
|
|
238
245
|
} else if (data?.id) {
|
|
239
246
|
ccsCommDefinition = {
|
|
@@ -242,9 +249,14 @@ const CommunicationFlow = ({
|
|
|
242
249
|
version: data.version?.version ?? 1,
|
|
243
250
|
status: data.status,
|
|
244
251
|
};
|
|
252
|
+
} else {
|
|
253
|
+
setSaveError(formatMessage(messages.genericSaveError));
|
|
254
|
+
return;
|
|
245
255
|
}
|
|
246
256
|
} catch (error) {
|
|
247
257
|
console.error('[CommunicationFlow] CCS createCommDefinition error:', error);
|
|
258
|
+
setSaveError(formatMessage(messages.genericSaveError));
|
|
259
|
+
return;
|
|
248
260
|
}
|
|
249
261
|
} else if (!name) {
|
|
250
262
|
console.warn('[CommunicationFlow] Skipping CCS createCommDefinition — config.context.name is required');
|
|
@@ -128,16 +128,7 @@
|
|
|
128
128
|
}
|
|
129
129
|
|
|
130
130
|
.communication-flow-container {
|
|
131
|
-
//
|
|
132
|
-
// area regardless of how much step content is above it. <CapRow useLegacy>
|
|
133
|
-
// carries cap-ui-library's `.ant-row-legacy` rule (`display: block !important`),
|
|
134
|
-
// which silently defeats a plain `display:flex`/`min-height` here unless the
|
|
135
|
-
// selector is compounded with `.ant-row-legacy` and marked `!important` too.
|
|
136
|
-
// Deliberately position-based, not flex + margin-top: auto — a flex version
|
|
137
|
-
// makes this element (and its .ant-row-legacy children) a flex container, and
|
|
138
|
-
// flexbox's default align-items: stretch leaks into deeply nested descendants
|
|
139
|
-
// (e.g. ChannelSelectionStep's fixed-width empty-state card), stretching them
|
|
140
|
-
// to fill the container instead of keeping their own size.
|
|
131
|
+
// Anchor the Save footer to the bottom without flex, avoiding .ant-row-legacy overrides and flexbox stretching nested fixed-size content.
|
|
141
132
|
&.ant-row-legacy {
|
|
142
133
|
position: relative !important;
|
|
143
134
|
min-height: calc(100vh - 6.571rem) !important;
|
|
@@ -148,10 +139,7 @@
|
|
|
148
139
|
margin: $CAP_SPACE_32 0;
|
|
149
140
|
}
|
|
150
141
|
|
|
151
|
-
//
|
|
152
|
-
// exists (no dangling line above an empty "Add creative" state), but the gap
|
|
153
|
-
// it would have provided (.step-divider's margin) must stay — otherwise the
|
|
154
|
-
// section sits flush against the Channel card in the empty state.
|
|
142
|
+
// Hide the divider when empty but preserve its margin gap so the section doesn’t sit flush against the Channel card.
|
|
155
143
|
.communication-strategy-row--no-divider {
|
|
156
144
|
margin-bottom: $CAP_SPACE_32;
|
|
157
145
|
}
|