@capillarytech/creatives-library 8.0.358 → 8.0.359-alpha.0
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/index.html +0 -1
- package/package.json +1 -1
- package/utils/cdnTransformation.js +3 -75
- package/utils/tests/cdnTransformation.test.js +0 -127
- package/v2Components/CommonTestAndPreview/UnifiedPreview/PreviewHeader.js +0 -16
- package/v2Components/CommonTestAndPreview/UnifiedPreview/ViberPreviewContent.js +132 -14
- package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +163 -54
- package/v2Components/CommonTestAndPreview/UnifiedPreview/index.js +6 -52
- package/v2Components/CommonTestAndPreview/constants.js +0 -2
- package/v2Components/CommonTestAndPreview/index.js +231 -77
- package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/PreviewHeader.test.js +0 -163
- package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/ViberPreviewContent.test.js +364 -0
- package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/index.test.js +0 -255
- package/v2Components/CommonTestAndPreview/tests/constants.test.js +1 -2
- package/v2Components/CommonTestAndPreview/tests/index.test.js +0 -194
- package/v2Components/FormBuilder/index.js +52 -162
- package/v2Components/TestAndPreviewSlidebox/index.js +2 -2
- package/v2Containers/App/constants.js +0 -3
- package/v2Containers/CreativesContainer/index.js +24 -60
- package/v2Containers/Templates/_templates.scss +77 -0
- package/v2Containers/Templates/index.js +92 -82
- package/v2Containers/Templates/sagas.js +1 -6
- package/v2Containers/Templates/tests/sagas.test.js +6 -23
- package/v2Containers/Viber/constants.js +19 -0
- package/v2Containers/Viber/index.js +714 -47
- package/v2Containers/Viber/index.scss +148 -0
- package/v2Containers/Viber/messages.js +116 -0
- package/v2Containers/Viber/tests/index.test.js +80 -0
- package/v2Containers/WebPush/Create/index.js +8 -91
- package/v2Containers/WebPush/Create/index.scss +0 -7
- package/v2Components/CommonTestAndPreview/UnifiedPreview/WebPushPreviewContent.js +0 -169
- package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/WebPushPreviewContent.test.js +0 -522
- package/v2Containers/App/tests/constants.test.js +0 -61
- package/v2Containers/Templates/tests/webpush.test.js +0 -375
- package/v2Containers/WebPush/Create/tests/getTemplateContent.test.js +0 -348
- package/v2Containers/WebPush/Create/tests/testAndPreviewIntegration.test.js +0 -325
|
@@ -1174,50 +1174,6 @@ describe('CommonTestAndPreview', () => {
|
|
|
1174
1174
|
expect(screen.getByTestId('preview-section')).toBeTruthy();
|
|
1175
1175
|
});
|
|
1176
1176
|
});
|
|
1177
|
-
|
|
1178
|
-
it('should handle WEBPUSH channel', async () => {
|
|
1179
|
-
const webpushContent = {
|
|
1180
|
-
content: { title: 'Web Push', message: 'Hello from web push' },
|
|
1181
|
-
accountId: 'acc-001',
|
|
1182
|
-
};
|
|
1183
|
-
const props = {
|
|
1184
|
-
...defaultProps,
|
|
1185
|
-
channel: CHANNELS.WEBPUSH,
|
|
1186
|
-
content: webpushContent,
|
|
1187
|
-
};
|
|
1188
|
-
|
|
1189
|
-
render(
|
|
1190
|
-
<TestWrapper>
|
|
1191
|
-
<CommonTestAndPreview {...props} />
|
|
1192
|
-
</TestWrapper>
|
|
1193
|
-
);
|
|
1194
|
-
|
|
1195
|
-
await waitFor(() => {
|
|
1196
|
-
expect(screen.getByTestId('left-panel')).toBeTruthy();
|
|
1197
|
-
expect(screen.getByTestId('preview-section')).toBeTruthy();
|
|
1198
|
-
});
|
|
1199
|
-
});
|
|
1200
|
-
|
|
1201
|
-
it('should treat WEBPUSH content as object (same branch as WHATSAPP)', async () => {
|
|
1202
|
-
// The WEBPUSH channel uses the same contentObj branch as WHATSAPP (JSON string or object)
|
|
1203
|
-
const props = {
|
|
1204
|
-
...defaultProps,
|
|
1205
|
-
channel: CHANNELS.WEBPUSH,
|
|
1206
|
-
content: JSON.stringify({
|
|
1207
|
-
content: { title: 'Stringified', message: 'Content' },
|
|
1208
|
-
}),
|
|
1209
|
-
};
|
|
1210
|
-
|
|
1211
|
-
render(
|
|
1212
|
-
<TestWrapper>
|
|
1213
|
-
<CommonTestAndPreview {...props} />
|
|
1214
|
-
</TestWrapper>
|
|
1215
|
-
);
|
|
1216
|
-
|
|
1217
|
-
await waitFor(() => {
|
|
1218
|
-
expect(screen.getByTestId('preview-section')).toBeTruthy();
|
|
1219
|
-
});
|
|
1220
|
-
});
|
|
1221
1177
|
});
|
|
1222
1178
|
|
|
1223
1179
|
describe('Device Initialization', () => {
|
|
@@ -2673,156 +2629,6 @@ describe('CommonTestAndPreview', () => {
|
|
|
2673
2629
|
});
|
|
2674
2630
|
});
|
|
2675
2631
|
});
|
|
2676
|
-
|
|
2677
|
-
describe('preparePreviewPayload - WEBPUSH', () => {
|
|
2678
|
-
it('should build WEBPUSH payload with title and message', async () => {
|
|
2679
|
-
const webpushContent = {
|
|
2680
|
-
content: {
|
|
2681
|
-
title: 'Push Title',
|
|
2682
|
-
message: 'Push message body',
|
|
2683
|
-
},
|
|
2684
|
-
accountId: 'acc-123',
|
|
2685
|
-
messageSubject: 'Push Title',
|
|
2686
|
-
};
|
|
2687
|
-
const props = {
|
|
2688
|
-
...defaultProps,
|
|
2689
|
-
channel: CHANNELS.WEBPUSH,
|
|
2690
|
-
content: JSON.stringify(webpushContent),
|
|
2691
|
-
};
|
|
2692
|
-
|
|
2693
|
-
render(
|
|
2694
|
-
<TestWrapper>
|
|
2695
|
-
<CommonTestAndPreview {...props} />
|
|
2696
|
-
</TestWrapper>
|
|
2697
|
-
);
|
|
2698
|
-
|
|
2699
|
-
await waitFor(() => {
|
|
2700
|
-
expect(screen.getByTestId('left-panel')).toBeTruthy();
|
|
2701
|
-
expect(screen.getByTestId('preview-section')).toBeTruthy();
|
|
2702
|
-
});
|
|
2703
|
-
});
|
|
2704
|
-
|
|
2705
|
-
it('should handle WEBPUSH content with iconImageUrl', async () => {
|
|
2706
|
-
const webpushContent = {
|
|
2707
|
-
content: {
|
|
2708
|
-
title: 'Hello',
|
|
2709
|
-
message: 'World',
|
|
2710
|
-
iconImageUrl: 'https://example.com/icon.png',
|
|
2711
|
-
},
|
|
2712
|
-
accountId: 'acc-123',
|
|
2713
|
-
};
|
|
2714
|
-
const props = {
|
|
2715
|
-
...defaultProps,
|
|
2716
|
-
channel: CHANNELS.WEBPUSH,
|
|
2717
|
-
content: webpushContent,
|
|
2718
|
-
};
|
|
2719
|
-
|
|
2720
|
-
render(
|
|
2721
|
-
<TestWrapper>
|
|
2722
|
-
<CommonTestAndPreview {...props} />
|
|
2723
|
-
</TestWrapper>
|
|
2724
|
-
);
|
|
2725
|
-
|
|
2726
|
-
await waitFor(() => {
|
|
2727
|
-
expect(screen.getByTestId('preview-section')).toBeTruthy();
|
|
2728
|
-
});
|
|
2729
|
-
});
|
|
2730
|
-
|
|
2731
|
-
it('should handle WEBPUSH content with cta', async () => {
|
|
2732
|
-
const webpushContent = {
|
|
2733
|
-
content: {
|
|
2734
|
-
title: 'Hello',
|
|
2735
|
-
message: 'World',
|
|
2736
|
-
cta: { type: 'EXTERNAL_URL', actionLink: 'https://example.com' },
|
|
2737
|
-
},
|
|
2738
|
-
accountId: 'acc-123',
|
|
2739
|
-
};
|
|
2740
|
-
const props = {
|
|
2741
|
-
...defaultProps,
|
|
2742
|
-
channel: CHANNELS.WEBPUSH,
|
|
2743
|
-
content: webpushContent,
|
|
2744
|
-
};
|
|
2745
|
-
|
|
2746
|
-
render(
|
|
2747
|
-
<TestWrapper>
|
|
2748
|
-
<CommonTestAndPreview {...props} />
|
|
2749
|
-
</TestWrapper>
|
|
2750
|
-
);
|
|
2751
|
-
|
|
2752
|
-
await waitFor(() => {
|
|
2753
|
-
expect(screen.getByTestId('preview-section')).toBeTruthy();
|
|
2754
|
-
});
|
|
2755
|
-
});
|
|
2756
|
-
|
|
2757
|
-
it('should handle WEBPUSH content with expandableDetails', async () => {
|
|
2758
|
-
const webpushContent = {
|
|
2759
|
-
content: {
|
|
2760
|
-
title: 'Hello',
|
|
2761
|
-
message: 'World',
|
|
2762
|
-
expandableDetails: {
|
|
2763
|
-
media: [{ url: 'https://example.com/image.jpg', type: 'IMAGE' }],
|
|
2764
|
-
ctas: [{ title: 'Click', actionLink: 'https://example.com', type: 'EXTERNAL_URL' }],
|
|
2765
|
-
},
|
|
2766
|
-
},
|
|
2767
|
-
accountId: 'acc-123',
|
|
2768
|
-
};
|
|
2769
|
-
const props = {
|
|
2770
|
-
...defaultProps,
|
|
2771
|
-
channel: CHANNELS.WEBPUSH,
|
|
2772
|
-
content: webpushContent,
|
|
2773
|
-
};
|
|
2774
|
-
|
|
2775
|
-
render(
|
|
2776
|
-
<TestWrapper>
|
|
2777
|
-
<CommonTestAndPreview {...props} />
|
|
2778
|
-
</TestWrapper>
|
|
2779
|
-
);
|
|
2780
|
-
|
|
2781
|
-
await waitFor(() => {
|
|
2782
|
-
expect(screen.getByTestId('preview-section')).toBeTruthy();
|
|
2783
|
-
});
|
|
2784
|
-
});
|
|
2785
|
-
|
|
2786
|
-
it('should handle WEBPUSH with null formData gracefully', async () => {
|
|
2787
|
-
const props = {
|
|
2788
|
-
...defaultProps,
|
|
2789
|
-
channel: CHANNELS.WEBPUSH,
|
|
2790
|
-
formData: null,
|
|
2791
|
-
content: null,
|
|
2792
|
-
};
|
|
2793
|
-
|
|
2794
|
-
render(
|
|
2795
|
-
<TestWrapper>
|
|
2796
|
-
<CommonTestAndPreview {...props} />
|
|
2797
|
-
</TestWrapper>
|
|
2798
|
-
);
|
|
2799
|
-
|
|
2800
|
-
await waitFor(() => {
|
|
2801
|
-
expect(screen.getByTestId('preview-section')).toBeTruthy();
|
|
2802
|
-
});
|
|
2803
|
-
});
|
|
2804
|
-
|
|
2805
|
-
it('should handle WEBPUSH channel with object content (non-string)', async () => {
|
|
2806
|
-
const props = {
|
|
2807
|
-
...defaultProps,
|
|
2808
|
-
channel: CHANNELS.WEBPUSH,
|
|
2809
|
-
content: {
|
|
2810
|
-
content: { title: 'ObjTitle', message: 'ObjMessage' },
|
|
2811
|
-
accountId: 'acc-999',
|
|
2812
|
-
},
|
|
2813
|
-
};
|
|
2814
|
-
|
|
2815
|
-
render(
|
|
2816
|
-
<TestWrapper>
|
|
2817
|
-
<CommonTestAndPreview {...props} />
|
|
2818
|
-
</TestWrapper>
|
|
2819
|
-
);
|
|
2820
|
-
|
|
2821
|
-
await waitFor(() => {
|
|
2822
|
-
expect(screen.getByTestId('preview-section')).toBeTruthy();
|
|
2823
|
-
});
|
|
2824
|
-
});
|
|
2825
|
-
});
|
|
2826
2632
|
});
|
|
2827
2633
|
|
|
2828
2634
|
describe('Tag Extraction', () => {
|
|
@@ -79,69 +79,6 @@ const errorMessageForTags = {
|
|
|
79
79
|
TAG_BRACKET_COUNT_MISMATCH_ERROR: 'tagBracketCountMismatchError'
|
|
80
80
|
};
|
|
81
81
|
|
|
82
|
-
// Isolated input for EMAIL template-name: only this tiny component re-renders on each keystroke.
|
|
83
|
-
// formData is updated only on blur (onCommit), eliminating all re-renders during typing.
|
|
84
|
-
class HighFreqInput extends React.Component {
|
|
85
|
-
constructor(props) {
|
|
86
|
-
super(props);
|
|
87
|
-
this.state = { localValue: props.value || '' };
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
componentDidUpdate(prevProps) {
|
|
91
|
-
if (prevProps.value !== this.props.value && this.state.localValue !== this.props.value) {
|
|
92
|
-
this.setState({ localValue: this.props.value || '' });
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
handleChange = (e) => {
|
|
97
|
-
this.setState({ localValue: e.target.value });
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
handleBlur = (e) => {
|
|
101
|
-
this.props.onCommit(this.state.localValue);
|
|
102
|
-
if (this.props.onBlur) this.props.onBlur(e);
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
render() {
|
|
106
|
-
const { value: _v, onCommit: _oc, onBlur: _ob, ...rest } = this.props;
|
|
107
|
-
return <CapInput {...rest} value={this.state.localValue} onChange={this.handleChange} onBlur={this.handleBlur} />;
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// Isolated wrapper for EMAIL template-subject: blur-only commit, same as HighFreqInput.
|
|
112
|
-
class HighFreqTagInput extends React.Component {
|
|
113
|
-
constructor(props) {
|
|
114
|
-
super(props);
|
|
115
|
-
this.state = { localInputValue: props.inputValue || '' };
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
componentDidUpdate(prevProps) {
|
|
119
|
-
if (prevProps.inputValue !== this.props.inputValue && this.state.localInputValue !== this.props.inputValue) {
|
|
120
|
-
this.setState({ localInputValue: this.props.inputValue || '' });
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
handleInputChange = (e) => {
|
|
125
|
-
this.setState({ localInputValue: e.target.value });
|
|
126
|
-
};
|
|
127
|
-
|
|
128
|
-
handleBlur = () => {
|
|
129
|
-
this.props.onCommit(this.state.localInputValue);
|
|
130
|
-
};
|
|
131
|
-
|
|
132
|
-
render() {
|
|
133
|
-
const { inputValue: _iv, onCommit: _oc, inputOnChange: _ic, ...rest } = this.props;
|
|
134
|
-
return (
|
|
135
|
-
<CapTagListWithInput
|
|
136
|
-
{...rest}
|
|
137
|
-
inputValue={this.state.localInputValue}
|
|
138
|
-
inputOnChange={this.handleInputChange}
|
|
139
|
-
inputProps={{ ...(this.props.inputProps || {}), onBlur: this.handleBlur }}
|
|
140
|
-
/>
|
|
141
|
-
);
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
82
|
class FormBuilder extends React.Component { // eslint-disable-line react/prefer-stateless-function
|
|
146
83
|
constructor(props) {
|
|
147
84
|
super(props);
|
|
@@ -415,7 +352,6 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
415
352
|
this.setState({tabCount: nextProps.tabCount});
|
|
416
353
|
}
|
|
417
354
|
if (nextProps.startValidation && nextProps.startValidation !== false && this.props.startValidation !== nextProps.startValidation) {
|
|
418
|
-
if (this.debouncedUpdateFormData) this.debouncedUpdateFormData.flush();
|
|
419
355
|
this.setState({checkValidation: true});
|
|
420
356
|
this.validateForm(null, null, true, true, () => {
|
|
421
357
|
//triggering the saveFormData or onSubmit when validation sets isFormValid to TRUE
|
|
@@ -3474,25 +3410,26 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
3474
3410
|
? formatMessage(messages.personalizationTagsErrorMessage)
|
|
3475
3411
|
: (errorType === TAG_BRACKET_COUNT_MISMATCH_ERROR ? formatMessage(globalMessages.unbalanacedCurlyBraces) : (val.errorMessage && ifError ? val.errorMessage : ''));
|
|
3476
3412
|
if (styling === 'semantic') {
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
|
|
3493
|
-
|
|
3494
|
-
|
|
3495
|
-
|
|
3413
|
+
columns.push(
|
|
3414
|
+
<CapColumn key={val.id} span={val.width} offset={val.offset} style={val.style || {}}>
|
|
3415
|
+
<CapInput
|
|
3416
|
+
id={val.id}
|
|
3417
|
+
errorMessage={errorMessageText}
|
|
3418
|
+
label={val.label}
|
|
3419
|
+
inductiveText={val.inductiveText}
|
|
3420
|
+
className={`input-primary chart-name-input${ifError ? ' error' : ''}`}
|
|
3421
|
+
// fluid={val.fluid}
|
|
3422
|
+
style={val.style ? val.style : {}}
|
|
3423
|
+
placeholder={val.placeholder}
|
|
3424
|
+
onChange={(e) => this.updateFormData(e.target.value, val)}
|
|
3425
|
+
onBlur={(e) => this.handleFieldBlur(e, val)}
|
|
3426
|
+
value={value || ""}
|
|
3427
|
+
defaultValue={isVersionEnable ? this.state.formData[`${this.state.currentTab - 1}`][val.id] : this.state.formData[val.id]}
|
|
3428
|
+
disabled={val.disabled}
|
|
3429
|
+
size={val.size || "default"}
|
|
3430
|
+
/>
|
|
3431
|
+
{this.props.schema?.channel === EMAIL &&
|
|
3432
|
+
!aiContentBotDisabled && (
|
|
3496
3433
|
<CapAskAira.ContentGenerationBot
|
|
3497
3434
|
text={value || ""}
|
|
3498
3435
|
setText={this.handleSetText.bind(this, val)}
|
|
@@ -3501,48 +3438,12 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
3501
3438
|
rootStyle={{
|
|
3502
3439
|
bottom: "0.2rem",
|
|
3503
3440
|
right: "0.2rem",
|
|
3504
|
-
left: "auto",
|
|
3441
|
+
left: "auto",
|
|
3505
3442
|
}}
|
|
3506
3443
|
/>
|
|
3507
3444
|
)}
|
|
3508
|
-
|
|
3509
|
-
|
|
3510
|
-
} else {
|
|
3511
|
-
columns.push(
|
|
3512
|
-
<CapColumn key={val.id} span={val.width} offset={val.offset} style={val.style || {}}>
|
|
3513
|
-
<CapInput
|
|
3514
|
-
id={val.id}
|
|
3515
|
-
errorMessage={errorMessageText}
|
|
3516
|
-
label={val.label}
|
|
3517
|
-
inductiveText={val.inductiveText}
|
|
3518
|
-
className={`input-primary chart-name-input${ifError ? ' error' : ''}`}
|
|
3519
|
-
// fluid={val.fluid}
|
|
3520
|
-
style={val.style ? val.style : {}}
|
|
3521
|
-
placeholder={val.placeholder}
|
|
3522
|
-
onChange={(e) => this.updateFormData(e.target.value, val)}
|
|
3523
|
-
onBlur={(e) => this.handleFieldBlur(e, val)}
|
|
3524
|
-
value={value || ""}
|
|
3525
|
-
defaultValue={isVersionEnable ? this.state.formData[`${this.state.currentTab - 1}`][val.id] : this.state.formData[val.id]}
|
|
3526
|
-
disabled={val.disabled}
|
|
3527
|
-
size={val.size || "default"}
|
|
3528
|
-
/>
|
|
3529
|
-
{this.props.schema?.channel === EMAIL &&
|
|
3530
|
-
!aiContentBotDisabled && (
|
|
3531
|
-
<CapAskAira.ContentGenerationBot
|
|
3532
|
-
text={value || ""}
|
|
3533
|
-
setText={this.handleSetText.bind(this, val)}
|
|
3534
|
-
iconPlacement="float-br"
|
|
3535
|
-
iconSize="1.6rem"
|
|
3536
|
-
rootStyle={{
|
|
3537
|
-
bottom: "0.2rem",
|
|
3538
|
-
right: "0.2rem",
|
|
3539
|
-
left: "auto",
|
|
3540
|
-
}}
|
|
3541
|
-
/>
|
|
3542
|
-
)}
|
|
3543
|
-
</CapColumn>
|
|
3544
|
-
);
|
|
3545
|
-
}
|
|
3445
|
+
</CapColumn>
|
|
3446
|
+
);
|
|
3546
3447
|
}
|
|
3547
3448
|
break;
|
|
3548
3449
|
|
|
@@ -3783,48 +3684,37 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
3783
3684
|
isBEEAppEnableForCapTagList === false ||
|
|
3784
3685
|
channelForCapTagList !== 'EMAIL'
|
|
3785
3686
|
) {
|
|
3786
|
-
const isEmailStandaloneSubject = val.standalone && channelForCapTagList === EMAIL && val.id === 'template-subject';
|
|
3787
|
-
const tagListProps = {
|
|
3788
|
-
key: `input-${val.id}`,
|
|
3789
|
-
inputId: val.id,
|
|
3790
|
-
inputValue: this.state.formData[val.id] || '',
|
|
3791
|
-
inputPlaceholder: val.placeholder || '',
|
|
3792
|
-
inputErrorMessage: val.errorMessage && ifError ? val.errorMessage : '',
|
|
3793
|
-
inputRequired: val.required || false,
|
|
3794
|
-
inputDisabled: val.disabled || false,
|
|
3795
|
-
headingText: val.label || '',
|
|
3796
|
-
headingStyle: val.headingStyle || { marginTop: '3%', marginRight: '79%' },
|
|
3797
|
-
headingType: "h4",
|
|
3798
|
-
onTagSelect: (data) => this.callChildEvent(data, val, 'onTagSelect'),
|
|
3799
|
-
onContextChange: this.props.onContextChange,
|
|
3800
|
-
location: this.props.location,
|
|
3801
|
-
tags: this.props.tags ? this.props.tags : [],
|
|
3802
|
-
injectedTags: this.props.injectedTags ? this.props.injectedTags : {},
|
|
3803
|
-
className: val.className ? val.className : '',
|
|
3804
|
-
userLocale: this.state.translationLang,
|
|
3805
|
-
selectedOfferDetails: this.props.selectedOfferDetails,
|
|
3806
|
-
eventContextTags: this.props?.eventContextTags,
|
|
3807
|
-
waitEventContextTags: this.props?.waitEventContextTags,
|
|
3808
|
-
moduleFilterEnabled: moduleFilterEnabledForCapTagList,
|
|
3809
|
-
containerStyle: val.style || {},
|
|
3810
|
-
inputProps: val.inputProps || {},
|
|
3811
|
-
showInput: val.showInput !== false,
|
|
3812
|
-
showTagList: val.showTagList !== false,
|
|
3813
|
-
restrictPersonalization: this.props.restrictPersonalization,
|
|
3814
|
-
};
|
|
3815
3687
|
columns.push(
|
|
3816
3688
|
<CapColumn key={`input-${val.id}`} offset={val.offset} span={val.width ? val.width : ''} style={val.style ? val.style : {marginBottom: '16px'}}>
|
|
3817
|
-
|
|
3818
|
-
|
|
3819
|
-
|
|
3820
|
-
|
|
3821
|
-
|
|
3822
|
-
|
|
3823
|
-
|
|
3824
|
-
|
|
3825
|
-
|
|
3826
|
-
|
|
3827
|
-
|
|
3689
|
+
<CapTagListWithInput
|
|
3690
|
+
key={`input-${val.id}`}
|
|
3691
|
+
inputId={val.id}
|
|
3692
|
+
inputValue={this.state.formData[val.id] || ''}
|
|
3693
|
+
inputOnChange={(e) => this.updateFormData(e.target.value, val)}
|
|
3694
|
+
inputPlaceholder={val.placeholder || ''}
|
|
3695
|
+
inputErrorMessage={val.errorMessage && ifError ? val.errorMessage : ''}
|
|
3696
|
+
inputRequired={val.required || false}
|
|
3697
|
+
inputDisabled={val.disabled || false}
|
|
3698
|
+
headingText={val.label || ''}
|
|
3699
|
+
headingStyle={val.headingStyle || { marginTop: '3%', marginRight: '79%' }}
|
|
3700
|
+
headingType="h4"
|
|
3701
|
+
onTagSelect={(data) => this.callChildEvent(data, val, 'onTagSelect')}
|
|
3702
|
+
onContextChange={this.props.onContextChange}
|
|
3703
|
+
location={this.props.location}
|
|
3704
|
+
tags={this.props.tags ? this.props.tags : []}
|
|
3705
|
+
injectedTags={this.props.injectedTags ? this.props.injectedTags : {}}
|
|
3706
|
+
className={val.className ? val.className : ''}
|
|
3707
|
+
userLocale={this.state.translationLang}
|
|
3708
|
+
selectedOfferDetails={this.props.selectedOfferDetails}
|
|
3709
|
+
eventContextTags={this.props?.eventContextTags}
|
|
3710
|
+
waitEventContextTags={this.props?.waitEventContextTags}
|
|
3711
|
+
moduleFilterEnabled={moduleFilterEnabledForCapTagList}
|
|
3712
|
+
containerStyle={val.style || {}}
|
|
3713
|
+
inputProps={val.inputProps || {}}
|
|
3714
|
+
showInput={val.showInput !== false}
|
|
3715
|
+
showTagList={val.showTagList !== false}
|
|
3716
|
+
restrictPersonalization={this.props.restrictPersonalization}
|
|
3717
|
+
/>
|
|
3828
3718
|
</CapColumn>
|
|
3829
3719
|
);
|
|
3830
3720
|
}
|
|
@@ -69,8 +69,8 @@ const TestAndPreviewSlidebox = (props) => {
|
|
|
69
69
|
|
|
70
70
|
TestAndPreviewSlidebox.propTypes = {
|
|
71
71
|
// Channel prop - supports all channels
|
|
72
|
-
channel: PropTypes.oneOf([CHANNELS.EMAIL, CHANNELS.SMS, CHANNELS.RCS, CHANNELS.WHATSAPP, CHANNELS.INAPP, CHANNELS.MOBILEPUSH
|
|
73
|
-
currentChannel: PropTypes.oneOf([CHANNELS.EMAIL, CHANNELS.SMS, CHANNELS.RCS, CHANNELS.WHATSAPP, CHANNELS.INAPP, CHANNELS.MOBILEPUSH
|
|
72
|
+
channel: PropTypes.oneOf([CHANNELS.EMAIL, CHANNELS.SMS, CHANNELS.RCS, CHANNELS.WHATSAPP, CHANNELS.INAPP, CHANNELS.MOBILEPUSH]),
|
|
73
|
+
currentChannel: PropTypes.oneOf([CHANNELS.EMAIL, CHANNELS.SMS, CHANNELS.RCS, CHANNELS.WHATSAPP, CHANNELS.INAPP, CHANNELS.MOBILEPUSH]), // Alternative prop name for backward compatibility
|
|
74
74
|
// All original props are passed through
|
|
75
75
|
show: PropTypes.bool.isRequired,
|
|
76
76
|
onClose: PropTypes.func.isRequired,
|
|
@@ -124,6 +124,3 @@ export const LOYALTY = 'loyalty';
|
|
|
124
124
|
export const FAILURE = 'FAILURE';
|
|
125
125
|
export const DATE_DISPLAY_FORMAT = 'D MMM YYYY';
|
|
126
126
|
export const TIME_DISPLAY_FORMAT = 'hh:mm A';
|
|
127
|
-
export const EXTERNAL_URL = 'EXTERNAL_URL';
|
|
128
|
-
export const URL = 'URL';
|
|
129
|
-
export const SITE_URL = 'SITE_URL';
|
|
@@ -1,48 +1,4 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
|
|
3
|
-
// Isolated input for the email template name field.
|
|
4
|
-
// Manages its own value in local state so keystrokes only re-render this
|
|
5
|
-
// small component, not the entire CreativesContainer → Email → FormBuilder tree.
|
|
6
|
-
class TemplateNameInputField extends React.Component {
|
|
7
|
-
constructor(props) {
|
|
8
|
-
super(props);
|
|
9
|
-
this.state = { localValue: props.initialValue || '' };
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
componentDidUpdate(prevProps) {
|
|
13
|
-
// Sync from props only when the external value changed AND the user hasn't
|
|
14
|
-
// diverged from the previous prop value. This handles async data-load in edit
|
|
15
|
-
// mode without overwriting what the user is actively typing.
|
|
16
|
-
if (
|
|
17
|
-
prevProps.initialValue !== this.props.initialValue &&
|
|
18
|
-
this.state.localValue === (prevProps.initialValue || '')
|
|
19
|
-
) {
|
|
20
|
-
this.setState({ localValue: this.props.initialValue || '' });
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
handleChange = (ev) => {
|
|
25
|
-
const { value } = ev.currentTarget;
|
|
26
|
-
this.setState({ localValue: value });
|
|
27
|
-
if (this.props.onChange) this.props.onChange(value);
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
handleBlur = () => {
|
|
31
|
-
if (this.props.onBlur) this.props.onBlur(this.state.localValue);
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
render() {
|
|
35
|
-
const { onChange: _onChange, initialValue: _initialValue, onBlur: _ob, ...rest } = this.props;
|
|
36
|
-
return (
|
|
37
|
-
<CapInput
|
|
38
|
-
{...rest}
|
|
39
|
-
value={this.state.localValue}
|
|
40
|
-
onChange={this.handleChange}
|
|
41
|
-
onBlur={this.handleBlur}
|
|
42
|
-
/>
|
|
43
|
-
);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
2
|
import PropTypes from 'prop-types';
|
|
47
3
|
import {
|
|
48
4
|
CAP_SPACE_16, CAP_SPACE_32, CAP_SPACE_56, CAP_SPACE_64,
|
|
@@ -1797,22 +1753,30 @@ export class Creatives extends React.Component {
|
|
|
1797
1753
|
} />
|
|
1798
1754
|
)
|
|
1799
1755
|
|
|
1800
|
-
templateNameComponentInput = ({ formData, onFormDataChange, name }) =>
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1756
|
+
templateNameComponentInput = ({ formData, onFormDataChange, name }) => {
|
|
1757
|
+
// Use local state for immediate UI feedback, fallback to prop value
|
|
1758
|
+
const displayValue = this.state.localTemplateName !== '' ? this.state.localTemplateName : name;
|
|
1759
|
+
|
|
1760
|
+
return (
|
|
1761
|
+
<CapInput
|
|
1762
|
+
value={displayValue}
|
|
1763
|
+
suffix={<span />}
|
|
1764
|
+
onBlur={() => {
|
|
1765
|
+
this.setState({
|
|
1766
|
+
isEditName: false,
|
|
1767
|
+
localTemplateName: '', // Clear local state on blur
|
|
1768
|
+
}, () => {
|
|
1769
|
+
this.showTemplateName({ formData, onFormDataChange });
|
|
1770
|
+
});
|
|
1771
|
+
}}
|
|
1772
|
+
onChange={(ev) => {
|
|
1773
|
+
const { value } = ev.currentTarget;
|
|
1774
|
+
// Use optimized update for better performance
|
|
1775
|
+
this.updateTemplateNameImmediately(value, formData, onFormDataChange);
|
|
1776
|
+
}}
|
|
1777
|
+
/>
|
|
1778
|
+
);
|
|
1779
|
+
}
|
|
1816
1780
|
|
|
1817
1781
|
showTemplateName = ({ formData, onFormDataChange }) => { //gets called from email/index after template data is fetched
|
|
1818
1782
|
const {
|
|
@@ -153,6 +153,83 @@
|
|
|
153
153
|
line-clamp: 3;
|
|
154
154
|
}
|
|
155
155
|
}
|
|
156
|
+
.viber-carousel-static-content {
|
|
157
|
+
width: 100%;
|
|
158
|
+
overflow: hidden;
|
|
159
|
+
.viber-carousel-static-message-box {
|
|
160
|
+
width: 100%;
|
|
161
|
+
height: 2.25rem;
|
|
162
|
+
border-radius: $CAP_SPACE_04;
|
|
163
|
+
background: $CAP_WHITE;
|
|
164
|
+
margin-bottom: $CAP_SPACE_08;
|
|
165
|
+
padding: 0 $CAP_SPACE_08;
|
|
166
|
+
display: flex;
|
|
167
|
+
align-items: center;
|
|
168
|
+
}
|
|
169
|
+
.viber-carousel-static-message {
|
|
170
|
+
display: block;
|
|
171
|
+
width: 100%;
|
|
172
|
+
color: $CAP_G01;
|
|
173
|
+
overflow: hidden;
|
|
174
|
+
text-overflow: ellipsis;
|
|
175
|
+
white-space: nowrap;
|
|
176
|
+
}
|
|
177
|
+
.viber-carousel-static-cards {
|
|
178
|
+
display: flex;
|
|
179
|
+
gap: $CAP_SPACE_08;
|
|
180
|
+
overflow: hidden;
|
|
181
|
+
}
|
|
182
|
+
.viber-carousel-static-card {
|
|
183
|
+
flex: 1;
|
|
184
|
+
min-width: 0;
|
|
185
|
+
background: $CAP_WHITE;
|
|
186
|
+
border-radius: $CAP_SPACE_04;
|
|
187
|
+
padding: $CAP_SPACE_06;
|
|
188
|
+
overflow: hidden;
|
|
189
|
+
}
|
|
190
|
+
.viber-carousel-static-image,
|
|
191
|
+
.viber-carousel-static-image-placeholder {
|
|
192
|
+
width: 100%;
|
|
193
|
+
height: 5.357rem;
|
|
194
|
+
border-radius: $CAP_SPACE_04;
|
|
195
|
+
}
|
|
196
|
+
.viber-carousel-static-image {
|
|
197
|
+
object-fit: cover;
|
|
198
|
+
}
|
|
199
|
+
.viber-carousel-static-image-placeholder {
|
|
200
|
+
background: $CAP_G07;
|
|
201
|
+
}
|
|
202
|
+
.viber-carousel-static-text {
|
|
203
|
+
display: block;
|
|
204
|
+
color: $CAP_G01;
|
|
205
|
+
margin: $CAP_SPACE_04 0;
|
|
206
|
+
line-height: 1.3;
|
|
207
|
+
overflow: hidden;
|
|
208
|
+
text-overflow: ellipsis;
|
|
209
|
+
white-space: normal;
|
|
210
|
+
}
|
|
211
|
+
.viber-carousel-static-buttons {
|
|
212
|
+
display: flex;
|
|
213
|
+
flex-direction: column;
|
|
214
|
+
gap: $CAP_SPACE_04;
|
|
215
|
+
}
|
|
216
|
+
.viber-carousel-static-button {
|
|
217
|
+
display: block;
|
|
218
|
+
min-height: 1.5rem;
|
|
219
|
+
border-radius: $CAP_SPACE_12;
|
|
220
|
+
background: $CAP_PURPLE01;
|
|
221
|
+
color: $CAP_WHITE;
|
|
222
|
+
text-align: center;
|
|
223
|
+
padding: 0.179rem $CAP_SPACE_08;
|
|
224
|
+
white-space: normal;
|
|
225
|
+
}
|
|
226
|
+
.viber-carousel-static-button-secondary {
|
|
227
|
+
color: $CAP_PURPLE01;
|
|
228
|
+
background: transparent;
|
|
229
|
+
border: none;
|
|
230
|
+
box-shadow: none;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
156
233
|
|
|
157
234
|
}
|
|
158
235
|
|