@capillarytech/creatives-library 9.0.36-alpha.0 → 9.0.36
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/app.js +175 -10
- package/entry.js +1 -67
- package/mfe-exposed-components.js +4 -2
- package/package.json +2 -2
- package/services/api.js +1 -9
- package/styles/containers/layout/_layoutPage.scss +6 -8
- package/utils/gtmTrackers/gtmEvents/creativeDetails.js +1 -2
- package/utils/rcsPayloadUtils.js +2 -5
- package/utils/tests/rcsPayloadUtils.test.js +2 -54
- package/v2Components/CapActionButton/constants.js +0 -1
- package/v2Components/CapActionButton/index.js +9 -77
- package/v2Components/CapActionButton/index.scss +0 -13
- package/v2Components/CapActionButton/messages.js +0 -13
- package/v2Components/CapActionButton/tests/index.test.js +1 -32
- package/v2Components/CommonTestAndPreview/UnifiedPreview/ViberPreviewContent.js +15 -108
- package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +1 -139
- package/v2Components/CommonTestAndPreview/index.js +41 -284
- package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/ViberPreviewContent.test.js +0 -364
- package/v2Components/CommonTestAndPreview/tests/index.test.js +0 -78
- package/v2Components/CommonTestAndPreview/utils.js +0 -34
- package/v2Components/NavigationBar/index.js +7 -9
- package/v2Components/NavigationBar/tests/index.test.js +19 -39
- package/v2Components/SmsFallback/index.js +0 -6
- package/v2Containers/Cap/constants.js +0 -1
- package/v2Containers/Cap/index.js +27 -57
- package/v2Containers/CreativesContainer/index.js +8 -11
- package/v2Containers/CreativesContainer/tests/index.test.js +0 -53
- package/v2Containers/Rcs/carouselUtils.js +73 -46
- package/v2Containers/Rcs/components/CarouselCard.js +9 -11
- package/v2Containers/Rcs/components/CarouselCardButtons.js +0 -20
- package/v2Containers/Rcs/constants.js +7 -2
- package/v2Containers/Rcs/index.js +205 -226
- package/v2Containers/Rcs/rcsLibraryHydrationUtils.js +47 -60
- package/v2Containers/Rcs/tests/CarouselCard.test.js +2 -1
- package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +0 -286
- package/v2Containers/Rcs/tests/carouselUtils.test.js +32 -28
- package/v2Containers/Rcs/tests/index.test.js +238 -151
- package/v2Containers/Rcs/tests/rcsLibraryHydrationUtils.test.js +0 -110
- package/v2Containers/Rcs/tests/utils.test.js +17 -36
- package/v2Containers/Rcs/utils.js +55 -28
- package/v2Containers/SmsTrai/Edit/index.js +24 -35
- package/v2Containers/Templates/_templates.scss +1 -115
- package/v2Containers/Templates/index.js +10 -91
- package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +0 -5
- package/v2Containers/TemplatesV2/TemplatesV2.style.js +3 -8
- package/v2Containers/TemplatesV2/index.js +9 -22
- package/v2Containers/Viber/constants.js +0 -21
- package/v2Containers/Viber/index.js +49 -719
- package/v2Containers/Viber/index.scss +0 -175
- package/v2Containers/Viber/messages.js +0 -121
- package/v2Containers/Viber/tests/index.test.js +0 -80
- package/AppRoot.js +0 -124
- package/app-config.js +0 -61
- package/bootstrap.js +0 -185
- package/utils/getDataLayer.js +0 -15
- package/utils/mfeDetect.js +0 -1
- package/utils/mfeFirstPaintReady.js +0 -29
- package/utils/mfeHistory.js +0 -62
- package/v2Components/CommonTestAndPreview/UnifiedPreview/ViberCarouselPreviewCards.js +0 -132
- package/v2Components/CommonTestAndPreview/UnifiedPreview/_viberCarouselPreviewCards.scss +0 -131
- package/v2Components/NavigationBar/mfeModuleHeader.config.js +0 -16
package/app.js
CHANGED
|
@@ -1,10 +1,175 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
import
|
|
1
|
+
import 'babel-polyfill';
|
|
2
|
+
import 'whatwg-fetch';
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { createRoot } from 'react-dom/client';
|
|
5
|
+
import { Provider } from 'react-redux';
|
|
6
|
+
// import createHistory from 'history/lib/createBrowserHistory';
|
|
7
|
+
// import { applyRouterMiddleware, Router, useRouterHistory } from 'react-router';
|
|
8
|
+
// import { useScroll } from 'react-router-scroll';
|
|
9
|
+
import history from 'utils/history';
|
|
10
|
+
import CapSomethingWentWrong from '@capillarytech/cap-ui-library/CapSomethingWentWrong';
|
|
11
|
+
import { addLocizeSavedHandler, startStandalone } from 'locize';
|
|
12
|
+
import Bugsnag from '@bugsnag/js';
|
|
13
|
+
import BugsnagPluginReact from '@bugsnag/plugin-react';
|
|
14
|
+
// import { makeSelectLocationState } from 'containers/Cap/selectors';
|
|
15
|
+
import LanguageProvider from 'v2Containers/LanguageProvider';
|
|
16
|
+
import FontFaceObserver from 'fontfaceobserver';
|
|
17
|
+
/* eslint-disable import/no-unresolved, import/extensions */
|
|
18
|
+
import '!file-loader?name=[name].[ext]!./favicon.ico';
|
|
19
|
+
/* eslint-enable import/no-unresolved, import/extensions */
|
|
20
|
+
import { configureStore } from '@capillarytech/vulcan-react-sdk/utils';
|
|
21
|
+
import { initialReducer } from './initialReducer';
|
|
22
|
+
import { translationMessages } from './i18n';
|
|
23
|
+
import './global-styles';
|
|
24
|
+
import initialState from './initialState';
|
|
25
|
+
import './styles/vendor/semantic/dist/semantic.min.css';
|
|
26
|
+
import './styles/main.scss';
|
|
27
|
+
import pathConfig from './config/path';
|
|
28
|
+
import ignoredErrorMessages from './utils/ignoredErrorMessages';
|
|
29
|
+
import App from './containers/App';
|
|
30
|
+
import ConfigProvider from 'antd/lib/config-provider';
|
|
31
|
+
import { getCapThemeConfig, loadCapUI } from '@capillarytech/cap-ui-library/utils';
|
|
32
|
+
|
|
33
|
+
loadCapUI();
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
const { setupNewrelicWebVitals } = require('./newRelic/utils');
|
|
37
|
+
// Setup New Relic WebVitals tracking
|
|
38
|
+
setupNewrelicWebVitals();
|
|
39
|
+
} catch (error) {
|
|
40
|
+
Bugsnag.notify(error);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// const browserHistory = useRouterHistory(createHistory)({
|
|
44
|
+
// basename: pathConfig.publicPath,
|
|
45
|
+
// });
|
|
46
|
+
|
|
47
|
+
const store = configureStore(initialState, initialReducer, history);
|
|
48
|
+
const MOUNT_NODE = document.getElementById('app');
|
|
49
|
+
|
|
50
|
+
// const history = syncHistoryWithStore(browserHistory, store, {
|
|
51
|
+
// selectLocationState: makeSelectLocationState(),
|
|
52
|
+
// });
|
|
53
|
+
const BUGSNAG_APP_VERSION = `creatives-ui__${new Date().getTime()}`;
|
|
54
|
+
const BUGSNAG_API_KEY = 'c4d96446438ff7dc7c08da08ef94c5b2';
|
|
55
|
+
|
|
56
|
+
const bugSnagErrorCallback = (event) => {
|
|
57
|
+
const { app: { releaseStage } = {}, originalError } = event || {};
|
|
58
|
+
const errorMessage = originalError && originalError.message;
|
|
59
|
+
if (
|
|
60
|
+
(releaseStage || '').includes('nightly') ||
|
|
61
|
+
process.env.NODE_ENV !== 'production' ||
|
|
62
|
+
ignoredErrorMessages.includes(errorMessage)
|
|
63
|
+
) {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
let userId;
|
|
67
|
+
let userName;
|
|
68
|
+
let userEmail;
|
|
69
|
+
let orgId;
|
|
70
|
+
let orgName;
|
|
71
|
+
try {
|
|
72
|
+
const {
|
|
73
|
+
id,
|
|
74
|
+
firstName,
|
|
75
|
+
lastName,
|
|
76
|
+
loginName,
|
|
77
|
+
proxyOrgList,
|
|
78
|
+
} = JSON.parse(window.localStorage.getItem('user'));
|
|
79
|
+
orgId = window.localStorage.getItem('orgID');
|
|
80
|
+
userId = id;
|
|
81
|
+
userName = `${firstName} ${lastName}`;
|
|
82
|
+
userEmail = loginName;
|
|
83
|
+
orgName = proxyOrgList.find(({ orgID }) => orgID === Number(orgId)).orgName;
|
|
84
|
+
} catch (error) {
|
|
85
|
+
userId = '';
|
|
86
|
+
userName = '';
|
|
87
|
+
userEmail = '';
|
|
88
|
+
orgId = '';
|
|
89
|
+
orgName = '';
|
|
90
|
+
}
|
|
91
|
+
event.addMetadata('org', {
|
|
92
|
+
name: orgName,
|
|
93
|
+
id: orgId,
|
|
94
|
+
});
|
|
95
|
+
event.setUser(userId, userEmail, userName);
|
|
96
|
+
if (window.FS && window.FS.getCurrentSessionURL) {
|
|
97
|
+
event.addMetadata('fullstory', {
|
|
98
|
+
urlAtTime: window.FS.getCurrentSessionURL(true),
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
Bugsnag.start({
|
|
104
|
+
appVersion: BUGSNAG_APP_VERSION,
|
|
105
|
+
apiKey: BUGSNAG_API_KEY,
|
|
106
|
+
releaseStage: window.location.hostname,
|
|
107
|
+
plugins: [new BugsnagPluginReact()],
|
|
108
|
+
onError: bugSnagErrorCallback,
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
const ErrorBoundary = Bugsnag.getPlugin('react').createErrorBoundary(React);
|
|
112
|
+
|
|
113
|
+
export const BugsnagClient = Bugsnag;
|
|
114
|
+
|
|
115
|
+
const ErrorScreen = () => <CapSomethingWentWrong url={`${pathConfig.publicPath}/v2`} />
|
|
116
|
+
const root = createRoot(MOUNT_NODE);
|
|
117
|
+
const render = (messages) => {
|
|
118
|
+
root.render(
|
|
119
|
+
<Provider store={store}>
|
|
120
|
+
<LanguageProvider messages={messages}>
|
|
121
|
+
<ErrorBoundary FallbackComponent={ErrorScreen}>
|
|
122
|
+
<ConfigProvider theme={getCapThemeConfig()}>
|
|
123
|
+
<App />
|
|
124
|
+
</ConfigProvider>
|
|
125
|
+
</ErrorBoundary>
|
|
126
|
+
</LanguageProvider>
|
|
127
|
+
</Provider>,
|
|
128
|
+
);
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
// Hot reloadable translation json files
|
|
132
|
+
if (module.hot) {
|
|
133
|
+
// modules.hot.accept does not accept dynamic dependencies,
|
|
134
|
+
// have to be constants at compile-time
|
|
135
|
+
module.hot.accept('./i18n', () => {
|
|
136
|
+
render(translationMessages);
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// for auto render UI on locize saved, since we cache the translations in redis it can't be update on real time as of now
|
|
141
|
+
// need to comment as of now
|
|
142
|
+
// addLocizeSavedHandler(res => {
|
|
143
|
+
// res.updated.forEach(item => {
|
|
144
|
+
// const { lng, ns, key, data } = item
|
|
145
|
+
// // load the translations somewhere...
|
|
146
|
+
// // and maybe rerender your UI
|
|
147
|
+
// })
|
|
148
|
+
// })
|
|
149
|
+
|
|
150
|
+
// start
|
|
151
|
+
startStandalone();
|
|
152
|
+
|
|
153
|
+
const openSansObserver = new FontFaceObserver('Roboto', {});
|
|
154
|
+
// When Open Sans is loaded, add a font-family using Open Sans to the body
|
|
155
|
+
openSansObserver.load().then(() => {
|
|
156
|
+
document.body.classList.add('fontLoaded');
|
|
157
|
+
}, () => {
|
|
158
|
+
document.body.classList.remove('fontLoaded');
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
// Chunked polyfill for browsers without Intl support
|
|
162
|
+
if (!window.Intl) {
|
|
163
|
+
(new Promise((resolve) => {
|
|
164
|
+
resolve(import('intl'));
|
|
165
|
+
}))
|
|
166
|
+
.then(() => Promise.all([
|
|
167
|
+
import('intl/locale-data/jsonp/en.js'),
|
|
168
|
+
]))
|
|
169
|
+
.then(() => render(translationMessages))
|
|
170
|
+
.catch((err) => {
|
|
171
|
+
throw err;
|
|
172
|
+
});
|
|
173
|
+
} else {
|
|
174
|
+
render(translationMessages);
|
|
175
|
+
}
|
package/entry.js
CHANGED
|
@@ -1,68 +1,2 @@
|
|
|
1
1
|
import { publicPath } from './config/path';
|
|
2
|
-
|
|
3
|
-
function normalizeTrailingSlash(url) {
|
|
4
|
-
if (!url) return url;
|
|
5
|
-
return url.endsWith('/') ? url : `${url}/`;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
function getCreativesRemoteEntryScript() {
|
|
9
|
-
if (typeof document === 'undefined') return null;
|
|
10
|
-
const all = Array.from(
|
|
11
|
-
document.querySelectorAll('script[src*="remoteEntry"]'),
|
|
12
|
-
);
|
|
13
|
-
const match =
|
|
14
|
-
all.find(s => /cap-creatives-ui/i.test(s.src)) || all[all.length - 1];
|
|
15
|
-
return match;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function getRemoteEntryOrigin() {
|
|
19
|
-
const el = getCreativesRemoteEntryScript();
|
|
20
|
-
if (!el || !el.src) return '';
|
|
21
|
-
try {
|
|
22
|
-
return new URL(el.src, window.location.href).origin;
|
|
23
|
-
} catch (e) {
|
|
24
|
-
return '';
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function getRemoteEntryBaseUrl() {
|
|
29
|
-
const el = getCreativesRemoteEntryScript();
|
|
30
|
-
if (!el || !el.src) return '';
|
|
31
|
-
try {
|
|
32
|
-
const u = new URL(el.src, window.location.href);
|
|
33
|
-
const dir = u.pathname.replace(/\/[^/]+\.js(\?.*)?$/, '/');
|
|
34
|
-
return normalizeTrailingSlash(`${u.origin}${dir}`);
|
|
35
|
-
} catch (e) {
|
|
36
|
-
return '';
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function resolveWebpackPublicPath() {
|
|
41
|
-
const envPath = process.env.MFE_PUBLIC_PATH;
|
|
42
|
-
|
|
43
|
-
if (envPath && envPath.length > 0) {
|
|
44
|
-
if (/^https?:\/\//i.test(envPath)) {
|
|
45
|
-
return normalizeTrailingSlash(envPath);
|
|
46
|
-
}
|
|
47
|
-
const origin = getRemoteEntryOrigin();
|
|
48
|
-
if (origin) {
|
|
49
|
-
const p = envPath.startsWith('/') ? envPath : `/${envPath}`;
|
|
50
|
-
return normalizeTrailingSlash(new URL(p, origin).href);
|
|
51
|
-
}
|
|
52
|
-
return normalizeTrailingSlash(
|
|
53
|
-
`${window.location.origin}${envPath.startsWith('/') ? '' : '/'}${envPath}`,
|
|
54
|
-
);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const fromRemote = getRemoteEntryBaseUrl();
|
|
58
|
-
if (fromRemote) {
|
|
59
|
-
return fromRemote;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const origin = new URL(
|
|
63
|
-
((document || {}).currentScript || {}).src || window.location,
|
|
64
|
-
).origin;
|
|
65
|
-
return `${origin}${publicPath}/`;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
__webpack_public_path__ = resolveWebpackPublicPath();
|
|
2
|
+
__webpack_public_path__ = ((new URL(((document || {}).currentScript || {}).src || window.location)).origin) + `${publicPath}/`;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@capillarytech/creatives-library",
|
|
3
3
|
"author": "meharaj",
|
|
4
|
-
"version": "9.0.36
|
|
4
|
+
"version": "9.0.36",
|
|
5
5
|
"description": "Capillary creatives ui",
|
|
6
6
|
"main": "./index.js",
|
|
7
7
|
"module": "./index.es.js",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"@babel/polyfill": "7.4.3",
|
|
17
17
|
"@bugsnag/js": "^7.2.1",
|
|
18
18
|
"@bugsnag/plugin-react": "7.2.1",
|
|
19
|
-
"@capillarytech/cap-ui-utils": "3.0.
|
|
19
|
+
"@capillarytech/cap-ui-utils": "3.0.4",
|
|
20
20
|
"@capillarytech/vulcan-react-sdk": "3.0.1",
|
|
21
21
|
"@codemirror/autocomplete": "^6.19.0",
|
|
22
22
|
"@codemirror/lang-css": "^6.3.1",
|
package/services/api.js
CHANGED
|
@@ -5,7 +5,6 @@ import 'whatwg-fetch';
|
|
|
5
5
|
import { GA, decompressJsonObject } from '@capillarytech/cap-ui-utils';
|
|
6
6
|
import get from 'lodash/get';
|
|
7
7
|
import { loadItem, clearItem } from './localStorageApi';
|
|
8
|
-
import { isMFEMode } from '../utils/mfeDetect';
|
|
9
8
|
import config from '../config/app';
|
|
10
9
|
import pathConfig from '../config/path';
|
|
11
10
|
import getSchema from './getSchema';
|
|
@@ -259,13 +258,6 @@ export const getSidebar = () => {
|
|
|
259
258
|
|
|
260
259
|
// if 403, show SWR page
|
|
261
260
|
export const getUserData = () => {
|
|
262
|
-
// In MFE mode the host (cap-intouch-ui) owns the single auth call and exposes
|
|
263
|
-
// the full response via window.getCapAuthData(); consume it instead of hitting
|
|
264
|
-
// the auth endpoint again. The promise waits until the host call completes,
|
|
265
|
-
// then resolves instantly for later callers.
|
|
266
|
-
if (isMFEMode() && typeof window.getCapAuthData === 'function') {
|
|
267
|
-
return window.getCapAuthData();
|
|
268
|
-
}
|
|
269
261
|
const url = `${API_AUTH_ENDPOINT}/user?include_features=1`;
|
|
270
262
|
return request(url, getAPICallObject('GET'), true);
|
|
271
263
|
};
|
|
@@ -687,7 +679,7 @@ export const getSupportVideosConfig = () => {
|
|
|
687
679
|
};
|
|
688
680
|
|
|
689
681
|
export const getNavigationConfigApi = async () => {
|
|
690
|
-
const url = `${ARYA_ENDPOINT}/
|
|
682
|
+
const url = `${ARYA_ENDPOINT}/navigations`;
|
|
691
683
|
return request(url, getAPICallObject('GET'));
|
|
692
684
|
};
|
|
693
685
|
|
|
@@ -79,6 +79,7 @@
|
|
|
79
79
|
flex: 1;
|
|
80
80
|
height: calc(100vh - 50px);
|
|
81
81
|
overflow: auto;
|
|
82
|
+
padding: 8px 16px 0;
|
|
82
83
|
color: #333333;
|
|
83
84
|
}
|
|
84
85
|
|
|
@@ -153,19 +154,16 @@
|
|
|
153
154
|
}
|
|
154
155
|
|
|
155
156
|
.cap-loader-box {
|
|
156
|
-
position:
|
|
157
|
+
position: fixed;
|
|
157
158
|
top: 0;
|
|
158
|
-
left: 0;
|
|
159
159
|
width: 100%;
|
|
160
|
-
|
|
161
|
-
background:
|
|
162
|
-
|
|
163
|
-
align-items: center;
|
|
164
|
-
justify-content: center;
|
|
160
|
+
height: 100%;
|
|
161
|
+
background: rgb(255,255,255);
|
|
162
|
+
text-align: center;
|
|
165
163
|
|
|
166
164
|
.loader-image {
|
|
167
165
|
width: 80px;
|
|
168
|
-
|
|
166
|
+
margin-top: 20%;
|
|
169
167
|
}
|
|
170
168
|
}
|
|
171
169
|
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { CREATIVES } from "../../../v2Containers/App/constants";
|
|
2
|
-
import { getDataLayer } from '../../getDataLayer';
|
|
3
2
|
|
|
4
3
|
const creativeDetails = ({
|
|
5
4
|
name,
|
|
@@ -24,7 +23,7 @@ const creativeDetails = ({
|
|
|
24
23
|
videoAdded,
|
|
25
24
|
stickersAdded
|
|
26
25
|
};
|
|
27
|
-
|
|
26
|
+
window.dataLayer.push({
|
|
28
27
|
creativeDetails: parsedObj,
|
|
29
28
|
event: 'creativeDetails',
|
|
30
29
|
});
|
package/utils/rcsPayloadUtils.js
CHANGED
|
@@ -59,12 +59,9 @@ export const normalizeRcsMessageContentForApi = messageContentItem => {
|
|
|
59
59
|
rcsCardPayloadOnly.cardContent.forEach(card => {
|
|
60
60
|
if (Array.isArray(card?.suggestions)) {
|
|
61
61
|
card.suggestions.forEach(suggestion => {
|
|
62
|
-
// isSaved
|
|
63
|
-
//
|
|
64
|
-
// (if any) already lives inline in `url` (e.g. "https://x.com{{first_name}}") — no
|
|
65
|
-
// separate dynamicUrlPayload field is sent for RCS.
|
|
62
|
+
// isSaved is UI-only editor state: campaigns' send-for-approval API rejects it, and it
|
|
63
|
+
// isn't part of the persisted payload shape.
|
|
66
64
|
delete suggestion?.isSaved;
|
|
67
|
-
delete suggestion?.urlType;
|
|
68
65
|
});
|
|
69
66
|
}
|
|
70
67
|
});
|
|
@@ -209,7 +209,7 @@ describe('normalizeRcsMessageContentForApi', () => {
|
|
|
209
209
|
expect(item.rcsContent.cardContent).toEqual([{ title: 'Card' }]);
|
|
210
210
|
});
|
|
211
211
|
|
|
212
|
-
it('strips isSaved
|
|
212
|
+
it('strips isSaved from each suggestion in cardContent', () => {
|
|
213
213
|
const item = {
|
|
214
214
|
rcsContent: {
|
|
215
215
|
cardContent: [
|
|
@@ -217,9 +217,7 @@ describe('normalizeRcsMessageContentForApi', () => {
|
|
|
217
217
|
title: 'Card 1',
|
|
218
218
|
suggestions: [
|
|
219
219
|
{ type: 'QUICK_REPLY', text: 'Yes', isSaved: true },
|
|
220
|
-
{
|
|
221
|
-
type: 'CTA', text: 'Go', url: 'https://example.com', urlType: 'STATIC', isSaved: false,
|
|
222
|
-
},
|
|
220
|
+
{ type: 'CTA', text: 'Go', url: 'https://example.com', isSaved: false },
|
|
223
221
|
],
|
|
224
222
|
},
|
|
225
223
|
],
|
|
@@ -232,56 +230,6 @@ describe('normalizeRcsMessageContentForApi', () => {
|
|
|
232
230
|
]);
|
|
233
231
|
});
|
|
234
232
|
|
|
235
|
-
it('keeps type "CTA" and the inline tag in url, but drops urlType, for a dynamic-URL suggestion', () => {
|
|
236
|
-
const item = {
|
|
237
|
-
rcsContent: {
|
|
238
|
-
cardContent: [
|
|
239
|
-
{
|
|
240
|
-
title: 'Card 1',
|
|
241
|
-
suggestions: [
|
|
242
|
-
{
|
|
243
|
-
type: 'CTA',
|
|
244
|
-
text: 'Visit here',
|
|
245
|
-
url: 'https://example.com/{{first_name}}',
|
|
246
|
-
urlType: 'DYNAMIC',
|
|
247
|
-
isSaved: true,
|
|
248
|
-
},
|
|
249
|
-
],
|
|
250
|
-
},
|
|
251
|
-
],
|
|
252
|
-
},
|
|
253
|
-
};
|
|
254
|
-
normalizeRcsMessageContentForApi(item);
|
|
255
|
-
expect(item.rcsContent.cardContent[0].suggestions).toEqual([
|
|
256
|
-
{
|
|
257
|
-
type: 'CTA',
|
|
258
|
-
text: 'Visit here',
|
|
259
|
-
url: 'https://example.com/{{first_name}}',
|
|
260
|
-
},
|
|
261
|
-
]);
|
|
262
|
-
});
|
|
263
|
-
|
|
264
|
-
it('leaves phone number and quick reply suggestions untouched by the urlType stripping', () => {
|
|
265
|
-
const item = {
|
|
266
|
-
rcsContent: {
|
|
267
|
-
cardContent: [
|
|
268
|
-
{
|
|
269
|
-
title: 'Card 1',
|
|
270
|
-
suggestions: [
|
|
271
|
-
{
|
|
272
|
-
type: 'PHONE_NUMBER', text: 'Call', phoneNumber: '918987678', isSaved: true,
|
|
273
|
-
},
|
|
274
|
-
],
|
|
275
|
-
},
|
|
276
|
-
],
|
|
277
|
-
},
|
|
278
|
-
};
|
|
279
|
-
normalizeRcsMessageContentForApi(item);
|
|
280
|
-
expect(item.rcsContent.cardContent[0].suggestions).toEqual([
|
|
281
|
-
{ type: 'PHONE_NUMBER', text: 'Call', phoneNumber: '918987678' },
|
|
282
|
-
]);
|
|
283
|
-
});
|
|
284
|
-
|
|
285
233
|
it('handles cardContent with no suggestions gracefully', () => {
|
|
286
234
|
const item = {
|
|
287
235
|
rcsContent: {
|
|
@@ -43,7 +43,6 @@ export const RCS_CTA_URL_TYPE = {
|
|
|
43
43
|
export const BTN_MAX_LENGTH = 20;
|
|
44
44
|
export const PHONE_NUMBER_MAX_LENGTH = 15;
|
|
45
45
|
export const URL_MAX_LENGTH = 2000;
|
|
46
|
-
export const DYNAMIC_URL_SUFFIX = '{{1}}';
|
|
47
46
|
|
|
48
47
|
export const CTA_TYPE_RADIO_OPTIONS = [
|
|
49
48
|
{ label: <FormattedMessage {...messages.ctaPhoneNo} />, value: RCS_BUTTON_TYPES.PHONE_NUMBER },
|
|
@@ -17,8 +17,7 @@ import CapButton from '@capillarytech/cap-ui-library/CapButton';
|
|
|
17
17
|
import CapTooltip from '@capillarytech/cap-ui-library/CapTooltip';
|
|
18
18
|
import CapTooltipWithInfo from '@capillarytech/cap-ui-library/CapTooltipWithInfo';
|
|
19
19
|
import { CAP_SPACE_04 } from '@capillarytech/cap-ui-library/styled/variables';
|
|
20
|
-
import globalMessages from '../../v2Containers/Cap/messages';
|
|
21
|
-
import TagList from '../../v2Containers/TagList';
|
|
20
|
+
import globalMessages from '../../v2Containers/Cap/messages';import CapTagListWithInput from '../CapTagListWithInput';
|
|
22
21
|
|
|
23
22
|
import { isUrl, isValidText } from '../../v2Containers/Line/Container/Wrapper/utils';
|
|
24
23
|
import messages from './messages';
|
|
@@ -31,7 +30,6 @@ import {
|
|
|
31
30
|
RCS_CTA_URL_TYPE,
|
|
32
31
|
CTA_TYPE_RADIO_OPTIONS,
|
|
33
32
|
CTA_URL_TYPE_SELECT_OPTIONS,
|
|
34
|
-
DYNAMIC_URL_SUFFIX,
|
|
35
33
|
} from './constants';
|
|
36
34
|
import './index.scss';
|
|
37
35
|
import { INITIAL_SUGGESTIONS, RCS_BUTTON_TYPES, HOST_ICS} from '../../v2Containers/Rcs/constants';
|
|
@@ -58,7 +56,6 @@ export const CapActionButton = (props) => {
|
|
|
58
56
|
minSavedSuggestions = 0,
|
|
59
57
|
hideDeleteSuggestionIndexes = [],
|
|
60
58
|
} = props;
|
|
61
|
-
const { formatMessage } = intl;
|
|
62
59
|
const isHostIcs = host === HOST_ICS;
|
|
63
60
|
const [urlError, setUrlError] = useState(false);
|
|
64
61
|
const [buttonError, setButtonError] = useState(false);
|
|
@@ -125,8 +122,7 @@ export const CapActionButton = (props) => {
|
|
|
125
122
|
return formatMessage(messages.ctaWebsiteUrlErrorMessage);
|
|
126
123
|
}
|
|
127
124
|
if (urlSubtype === RCS_CTA_URL_TYPE.DYNAMIC) {
|
|
128
|
-
|
|
129
|
-
return isUrl(baseUrl) ? false : formatMessage(messages.ctaWebsiteUrlErrorMessage);
|
|
125
|
+
return false;
|
|
130
126
|
}
|
|
131
127
|
if (!isUrl(trimmedUrl)) {
|
|
132
128
|
return formatMessage(messages.ctaWebsiteUrlErrorMessage);
|
|
@@ -138,12 +134,9 @@ export const CapActionButton = (props) => {
|
|
|
138
134
|
};
|
|
139
135
|
|
|
140
136
|
const onUrlChange = ({ target }) => {
|
|
141
|
-
const { id } = target;
|
|
137
|
+
const { value, id } = target;
|
|
142
138
|
const row = suggestions[id] || {};
|
|
143
139
|
const subtype = row.urlType || RCS_CTA_URL_TYPE.STATIC;
|
|
144
|
-
const value = subtype === RCS_CTA_URL_TYPE.DYNAMIC
|
|
145
|
-
? target.value.replace(invalidVarRegex, '')
|
|
146
|
-
: target.value;
|
|
147
140
|
setUrlError(validateCtaUrlValue(value, subtype));
|
|
148
141
|
updateHandler(HANDLERS.URL, value, id);
|
|
149
142
|
};
|
|
@@ -156,14 +149,6 @@ export const CapActionButton = (props) => {
|
|
|
156
149
|
updateButtonChange(cloned, index);
|
|
157
150
|
};
|
|
158
151
|
|
|
159
|
-
const onTagSelect = (data, index, url) => {
|
|
160
|
-
updateHandler(HANDLERS.URL, (url || '').replace(DYNAMIC_URL_SUFFIX, `{{${data}}}`), index);
|
|
161
|
-
};
|
|
162
|
-
|
|
163
|
-
const revertTagSelect = (index, url) => {
|
|
164
|
-
updateHandler(HANDLERS.URL, (url || '').replace(invalidVarRegex, DYNAMIC_URL_SUFFIX), index);
|
|
165
|
-
};
|
|
166
|
-
|
|
167
152
|
const onPhoneNoChange = (value, index) => {
|
|
168
153
|
updateHandler(HANDLERS.PHONE_NUMBER, value, index);
|
|
169
154
|
};
|
|
@@ -176,25 +161,12 @@ export const CapActionButton = (props) => {
|
|
|
176
161
|
return true;
|
|
177
162
|
} if (type === RCS_BUTTON_TYPES.CTA) {
|
|
178
163
|
const subtype = urlType || RCS_CTA_URL_TYPE.STATIC;
|
|
179
|
-
|
|
180
|
-
return true;
|
|
181
|
-
}
|
|
182
|
-
// A dynamic URL still holding the unresolved {{1}} placeholder has no real
|
|
183
|
-
// personalization tag assigned yet — block save until one is picked via "Add URL label".
|
|
184
|
-
return subtype === RCS_CTA_URL_TYPE.DYNAMIC && (url || '').includes(DYNAMIC_URL_SUFFIX);
|
|
164
|
+
return !!validateCtaUrlValue(url, subtype);
|
|
185
165
|
}
|
|
186
166
|
return false;
|
|
187
167
|
};
|
|
188
168
|
|
|
189
169
|
const saveCta = (index) => {
|
|
190
|
-
const cta = suggestions[index];
|
|
191
|
-
if (
|
|
192
|
-
cta?.type === RCS_BUTTON_TYPES.CTA
|
|
193
|
-
&& cta?.urlType === RCS_CTA_URL_TYPE.DYNAMIC
|
|
194
|
-
&& !(cta?.url || '').includes(DYNAMIC_URL_SUFFIX)
|
|
195
|
-
) {
|
|
196
|
-
cta.url = `${cta.url || ''}${DYNAMIC_URL_SUFFIX}`;
|
|
197
|
-
}
|
|
198
170
|
updateHandler(HANDLERS.IS_SAVED, true, index);
|
|
199
171
|
};
|
|
200
172
|
|
|
@@ -208,6 +180,8 @@ export const CapActionButton = (props) => {
|
|
|
208
180
|
updateButtonChange(newSuggestion, suggestions?.length);
|
|
209
181
|
};
|
|
210
182
|
|
|
183
|
+
const { formatMessage } = intl;
|
|
184
|
+
|
|
211
185
|
const hideDeleteForSuggestionIndex = (idx) =>
|
|
212
186
|
Array.isArray(hideDeleteSuggestionIndexes) && hideDeleteSuggestionIndexes.includes(idx);
|
|
213
187
|
|
|
@@ -230,7 +204,6 @@ export const CapActionButton = (props) => {
|
|
|
230
204
|
const urlSubtype = type === RCS_BUTTON_TYPES.CTA
|
|
231
205
|
? (cta.urlType || RCS_CTA_URL_TYPE.STATIC)
|
|
232
206
|
: RCS_CTA_URL_TYPE.STATIC;
|
|
233
|
-
const isDynamicUrlSubtype = urlSubtype === RCS_CTA_URL_TYPE.DYNAMIC;
|
|
234
207
|
const phoneNumber = type !== RCS_BUTTON_TYPES.PHONE_NUMBER ? null : cta.phoneNumber;
|
|
235
208
|
if (isFullMode && !isEditFlow && !isSaved) {
|
|
236
209
|
renderArray.push(
|
|
@@ -342,32 +315,13 @@ export const CapActionButton = (props) => {
|
|
|
342
315
|
</CapColumn>
|
|
343
316
|
<CapColumn span={18} className="rcs-cta-url-value-col">
|
|
344
317
|
<CapHeading type="h4" className="cta-label">
|
|
345
|
-
{formatMessage(messages.
|
|
346
|
-
{isDynamicUrlSubtype && (
|
|
347
|
-
<CapTooltipWithInfo
|
|
348
|
-
infoIconProps={{
|
|
349
|
-
style: { marginLeft: CAP_SPACE_04 },
|
|
350
|
-
}}
|
|
351
|
-
autoAdjustOverflow
|
|
352
|
-
placement="right"
|
|
353
|
-
title={formatMessage(messages.ctaDynamicUrlTooltip, { one: DYNAMIC_URL_SUFFIX })}
|
|
354
|
-
/>
|
|
355
|
-
)}
|
|
318
|
+
{formatMessage(messages.ctaUrlField)}
|
|
356
319
|
</CapHeading>
|
|
357
320
|
<CapInput
|
|
358
321
|
id={index}
|
|
359
|
-
addonAfter={
|
|
360
|
-
isDynamicUrlSubtype
|
|
361
|
-
&& !(url || '').includes(DYNAMIC_URL_SUFFIX)
|
|
362
|
-
&& DYNAMIC_URL_SUFFIX
|
|
363
|
-
}
|
|
364
322
|
className="rcs-cta-url"
|
|
365
323
|
onChange={onUrlChange}
|
|
366
|
-
placeholder={
|
|
367
|
-
isDynamicUrlSubtype
|
|
368
|
-
? formatMessage(messages.ctaDynamicPlaceholder)
|
|
369
|
-
: formatMessage(messages.ctaEnterUrlPlaceholder)
|
|
370
|
-
}
|
|
324
|
+
placeholder={formatMessage(messages.ctaEnterUrlPlaceholder)}
|
|
371
325
|
value={url || ''}
|
|
372
326
|
size="large"
|
|
373
327
|
maxLength={URL_MAX_LENGTH}
|
|
@@ -449,7 +403,7 @@ export const CapActionButton = (props) => {
|
|
|
449
403
|
span={1}
|
|
450
404
|
className={`${ctaIsPhone ? 'whatsapp-saved-cta-phone-icon' : ''}`}
|
|
451
405
|
>
|
|
452
|
-
<CapIcon size="s" type={ctaIsPhone ? 'call' : (ctaIsReply ? 'small-link' : '
|
|
406
|
+
<CapIcon size="s" type={ctaIsPhone ? 'call' : (ctaIsReply ? 'small-link' : 'launch')} />
|
|
453
407
|
</CapColumn>
|
|
454
408
|
<CapColumn span={6}>
|
|
455
409
|
<CapLabel
|
|
@@ -476,28 +430,6 @@ export const CapActionButton = (props) => {
|
|
|
476
430
|
</CapTooltip>
|
|
477
431
|
</>
|
|
478
432
|
)}
|
|
479
|
-
{isDynamicUrlSubtype && (url || '').includes(DYNAMIC_URL_SUFFIX) && (
|
|
480
|
-
<TagList
|
|
481
|
-
className="rcs-cta-taglist"
|
|
482
|
-
label={formatMessage(messages.ctaTagListLabel)}
|
|
483
|
-
onTagSelect={(data) => onTagSelect(data, index, url)}
|
|
484
|
-
location={location}
|
|
485
|
-
tags={tags}
|
|
486
|
-
injectedTags={injectedTags}
|
|
487
|
-
selectedOfferDetails={selectedOfferDetails}
|
|
488
|
-
onContextChange={onContextChange}
|
|
489
|
-
/>
|
|
490
|
-
)}
|
|
491
|
-
{isDynamicUrlSubtype && !(url || '').includes(DYNAMIC_URL_SUFFIX) && (
|
|
492
|
-
<CapTooltip title={formatMessage(messages.ctaTagListRevert)} placement="top">
|
|
493
|
-
<CapIcon
|
|
494
|
-
size="s"
|
|
495
|
-
type="return"
|
|
496
|
-
className="rcs-cta-tag-revert"
|
|
497
|
-
onClick={() => revertTagSelect(index, url)}
|
|
498
|
-
/>
|
|
499
|
-
</CapTooltip>
|
|
500
|
-
)}
|
|
501
433
|
{(isFullMode && !isEditFlow) && (
|
|
502
434
|
<div className="rcs-saved-cta-action-icons">
|
|
503
435
|
<CapIcon
|
|
@@ -84,19 +84,6 @@
|
|
|
84
84
|
.rcs-saved-cta-delete-icon {
|
|
85
85
|
cursor: pointer;
|
|
86
86
|
}
|
|
87
|
-
|
|
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
|
-
.rcs-cta-tag-revert {
|
|
91
|
-
display: flex;
|
|
92
|
-
align-items: center;
|
|
93
|
-
flex-shrink: 0;
|
|
94
|
-
cursor: pointer;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
.rcs-cta-taglist {
|
|
98
|
-
flex-shrink: 0;
|
|
99
|
-
}
|
|
100
87
|
}
|
|
101
88
|
|
|
102
89
|
// Button text / URL: count via CapInput `suffix` (antd affix, right-aligned inside field).
|