@capillarytech/creatives-library 9.0.31 → 9.0.33
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/AppRoot.js +124 -0
- package/app-config.js +61 -0
- package/app.js +10 -175
- package/bootstrap.js +185 -0
- package/entry.js +67 -1
- package/mfe-exposed-components.js +2 -4
- package/package.json +2 -2
- package/services/api.js +9 -1
- package/styles/containers/layout/_layoutPage.scss +8 -6
- package/utils/getDataLayer.js +15 -0
- package/utils/gtmTrackers/gtmEvents/creativeDetails.js +2 -1
- package/utils/mfeDetect.js +1 -0
- package/utils/mfeFirstPaintReady.js +29 -0
- package/utils/mfeHistory.js +62 -0
- package/v2Components/CommonTestAndPreview/index.js +25 -6
- package/v2Components/NavigationBar/index.js +9 -7
- package/v2Components/NavigationBar/mfeModuleHeader.config.js +16 -0
- package/v2Components/NavigationBar/tests/index.test.js +39 -19
- package/v2Containers/Cap/constants.js +1 -0
- package/v2Containers/Cap/index.js +57 -27
- package/v2Containers/Templates/_templates.scss +1 -1
- package/v2Containers/Templates/index.js +1 -0
- package/v2Containers/TemplatesV2/TemplatesV2.style.js +8 -2
- package/v2Containers/TemplatesV2/index.js +21 -0
package/AppRoot.js
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import React, { useMemo, useEffect, useRef } from 'react';
|
|
2
|
+
import { StyleSheetManager } from 'styled-components';
|
|
3
|
+
import { Provider } from 'react-redux';
|
|
4
|
+
import { configureStore } from '@capillarytech/vulcan-react-sdk/utils';
|
|
5
|
+
import { createBrowserHistory } from 'history';
|
|
6
|
+
import ConfigProvider from 'antd/lib/config-provider';
|
|
7
|
+
import { getCapThemeConfig, loadCapUI } from '@capillarytech/cap-ui-library/utils';
|
|
8
|
+
|
|
9
|
+
import LanguageProvider from 'v2Containers/LanguageProvider';
|
|
10
|
+
import App from './containers/App';
|
|
11
|
+
import './styles/main.scss';
|
|
12
|
+
import { initialReducer } from './initialReducer';
|
|
13
|
+
import { translationMessages } from './i18n';
|
|
14
|
+
import initialState from './initialState';
|
|
15
|
+
import pathConfig from './config/path';
|
|
16
|
+
import { isMFEMode } from './utils/mfeDetect';
|
|
17
|
+
import { emitFirstPaintReadyIfNotDataLanding } from './utils/mfeFirstPaintReady';
|
|
18
|
+
import rebaseHistory from './utils/mfeHistory';
|
|
19
|
+
import { MFEEventBus } from '@capillarytech/cap-ui-utils';
|
|
20
|
+
import appConfig from './app-config';
|
|
21
|
+
|
|
22
|
+
// Module-scope SDK init — MUST run before configureStore.
|
|
23
|
+
loadCapUI();
|
|
24
|
+
|
|
25
|
+
const AppRoot = ({ basename: _basename = '/creatives/ui', history: hostHistory }) => {
|
|
26
|
+
const { store, history } = useMemo(() => {
|
|
27
|
+
const hist = hostHistory
|
|
28
|
+
? rebaseHistory(hostHistory, pathConfig.publicPath)
|
|
29
|
+
: createBrowserHistory({ basename: pathConfig.publicPath });
|
|
30
|
+
const st = configureStore(initialState, initialReducer, hist);
|
|
31
|
+
return { store: st, history: hist };
|
|
32
|
+
}, [hostHistory]);
|
|
33
|
+
|
|
34
|
+
// MFE style isolation: route THIS remote's styled-components output (including any
|
|
35
|
+
// createGlobalStyle) into a tagged <div data-mfe-app> in <head>, instead of the shared
|
|
36
|
+
// default styled-components <style>. On unmount the container is removed, so all of this
|
|
37
|
+
// remote's styled-components CSS goes with it — no global-style leak into other remotes.
|
|
38
|
+
// Standalone (non-MFE) is untouched: styles inject into <head> as usual.
|
|
39
|
+
const styleTarget = useMemo(() => {
|
|
40
|
+
if (!isMFEMode() || typeof document === 'undefined') return null;
|
|
41
|
+
const el = document.createElement('div');
|
|
42
|
+
el.setAttribute('data-mfe-app', 'creatives/ui');
|
|
43
|
+
document.head.appendChild(el);
|
|
44
|
+
return el;
|
|
45
|
+
}, []);
|
|
46
|
+
|
|
47
|
+
useEffect(() => () => {
|
|
48
|
+
if (styleTarget) styleTarget.remove();
|
|
49
|
+
}, [styleTarget]);
|
|
50
|
+
|
|
51
|
+
const fcpReported = useRef(false);
|
|
52
|
+
useEffect(() => {
|
|
53
|
+
if (isMFEMode() && !fcpReported.current) {
|
|
54
|
+
fcpReported.current = true;
|
|
55
|
+
requestAnimationFrame(() => {
|
|
56
|
+
MFEEventBus.emit('mfe:segment', {
|
|
57
|
+
action: 'stop',
|
|
58
|
+
appId: appConfig.appName,
|
|
59
|
+
type: 'fcp',
|
|
60
|
+
timestamp: performance.now(),
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
}, []);
|
|
65
|
+
|
|
66
|
+
// Non-landing routes (anything outside the TemplatesV2 listing pages) never emit
|
|
67
|
+
// the data-settled 'lcp'; emit it at first paint so the host drops the skeleton
|
|
68
|
+
// overlay immediately instead of waiting out the grace timer.
|
|
69
|
+
useEffect(() => {
|
|
70
|
+
emitFirstPaintReadyIfNotDataLanding();
|
|
71
|
+
}, []);
|
|
72
|
+
|
|
73
|
+
useEffect(() => {
|
|
74
|
+
if (!isMFEMode()) {
|
|
75
|
+
const { startStandalone } = require('locize');
|
|
76
|
+
startStandalone();
|
|
77
|
+
}
|
|
78
|
+
}, []);
|
|
79
|
+
|
|
80
|
+
useEffect(() => {
|
|
81
|
+
if (isMFEMode()) {
|
|
82
|
+
MFEEventBus.emit('gtm:init', {
|
|
83
|
+
appId: appConfig.appName,
|
|
84
|
+
containerId: appConfig.gtm.trackingId,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
}, []);
|
|
88
|
+
|
|
89
|
+
// In MFE mode the HOST's index.html is served, not creatives' — so BeePlugin.js
|
|
90
|
+
// (declared in creatives/index.html) is never loaded. Inject it here so BeeEditor works.
|
|
91
|
+
useEffect(() => {
|
|
92
|
+
if (isMFEMode() && !window.BeePlugin && !document.querySelector('script[src*="BeePlugin.js"]')) {
|
|
93
|
+
const script = document.createElement('script');
|
|
94
|
+
script.src = 'https://app-rsrc.getbee.io/plugin/BeePlugin.js';
|
|
95
|
+
script.type = 'text/javascript';
|
|
96
|
+
document.head.appendChild(script);
|
|
97
|
+
}
|
|
98
|
+
}, []);
|
|
99
|
+
|
|
100
|
+
useEffect(() => {
|
|
101
|
+
const unsub = MFEEventBus.on('creatives:navigate', ({ path }) => {
|
|
102
|
+
if (path) history.push(path);
|
|
103
|
+
});
|
|
104
|
+
return unsub;
|
|
105
|
+
}, [history]);
|
|
106
|
+
|
|
107
|
+
const tree = (
|
|
108
|
+
<Provider store={store}>
|
|
109
|
+
<LanguageProvider messages={translationMessages}>
|
|
110
|
+
<ConfigProvider theme={getCapThemeConfig()}>
|
|
111
|
+
<App history={history} />
|
|
112
|
+
</ConfigProvider>
|
|
113
|
+
</LanguageProvider>
|
|
114
|
+
</Provider>
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
return styleTarget ? (
|
|
118
|
+
<StyleSheetManager target={styleTarget}>{tree}</StyleSheetManager>
|
|
119
|
+
) : (
|
|
120
|
+
tree
|
|
121
|
+
);
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
export default AppRoot;
|
package/app-config.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
appName: 'cap-creatives-ui',
|
|
3
|
+
intouchBaseUrl: 'nightly.intouch.capillarytech.com',
|
|
4
|
+
prefix: '/creatives/ui',
|
|
5
|
+
isHostedOnPlatform: false,
|
|
6
|
+
appType: 'native',
|
|
7
|
+
bugsnag: {
|
|
8
|
+
useBugsnag: false,
|
|
9
|
+
apiKey: '',
|
|
10
|
+
retainSourceMaps: false,
|
|
11
|
+
},
|
|
12
|
+
useSourceMaps: true,
|
|
13
|
+
i18n: {
|
|
14
|
+
useI18n: true,
|
|
15
|
+
customI18n: false,
|
|
16
|
+
localI18n: false,
|
|
17
|
+
appNames: ['cap_creatives_ui'],
|
|
18
|
+
locales: [],
|
|
19
|
+
defaultLocale: null,
|
|
20
|
+
},
|
|
21
|
+
gtm: {
|
|
22
|
+
useGTM: true,
|
|
23
|
+
trackingId: 'GTM-MC4TRPX',
|
|
24
|
+
projectId: 'GTM-MC4TRPX',
|
|
25
|
+
},
|
|
26
|
+
useNavigationComponent: true,
|
|
27
|
+
useTestSetup: true,
|
|
28
|
+
newrelic: {
|
|
29
|
+
enabled: true,
|
|
30
|
+
licenseKey: '082da40fff',
|
|
31
|
+
environments: {
|
|
32
|
+
nightly: {
|
|
33
|
+
appId: '718411553',
|
|
34
|
+
},
|
|
35
|
+
staging: {
|
|
36
|
+
appId: '718413027',
|
|
37
|
+
},
|
|
38
|
+
ushc: {
|
|
39
|
+
appId: '718413124',
|
|
40
|
+
},
|
|
41
|
+
apac: {
|
|
42
|
+
appId: '718413134',
|
|
43
|
+
},
|
|
44
|
+
'north-america': {
|
|
45
|
+
appId: '718413144',
|
|
46
|
+
},
|
|
47
|
+
apac2: {
|
|
48
|
+
appId: '718413155',
|
|
49
|
+
},
|
|
50
|
+
tata: {
|
|
51
|
+
appId: '718413165',
|
|
52
|
+
},
|
|
53
|
+
sea: {
|
|
54
|
+
appId: '718413175',
|
|
55
|
+
},
|
|
56
|
+
eu: {
|
|
57
|
+
appId: '718413185',
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
};
|
package/app.js
CHANGED
|
@@ -1,175 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
import
|
|
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
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* app.js
|
|
3
|
+
*
|
|
4
|
+
* Async boundary for Module Federation. Shared deps (react, react-dom, …) are
|
|
5
|
+
* configured with `eager: false` in the production build, so the entry module
|
|
6
|
+
* must not import them synchronously — webpack needs this dynamic import to
|
|
7
|
+
* load the shared-scope modules before any application code runs. All real
|
|
8
|
+
* bootstrap code lives in bootstrap.js.
|
|
9
|
+
*/
|
|
10
|
+
import('./bootstrap');
|
package/bootstrap.js
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* bootstrap.js
|
|
3
|
+
*
|
|
4
|
+
* Application setup and boilerplate. Loaded via the async boundary in app.js
|
|
5
|
+
* (`import('./bootstrap')`) so Module Federation shared deps (eager: false in
|
|
6
|
+
* prod) are resolved before this module executes.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import 'babel-polyfill';
|
|
10
|
+
import 'whatwg-fetch';
|
|
11
|
+
import React from 'react';
|
|
12
|
+
import { createRoot } from 'react-dom/client';
|
|
13
|
+
import { Provider } from 'react-redux';
|
|
14
|
+
// import createHistory from 'history/lib/createBrowserHistory';
|
|
15
|
+
// import { applyRouterMiddleware, Router, useRouterHistory } from 'react-router';
|
|
16
|
+
// import { useScroll } from 'react-router-scroll';
|
|
17
|
+
import history from 'utils/history';
|
|
18
|
+
import CapSomethingWentWrong from '@capillarytech/cap-ui-library/CapSomethingWentWrong';
|
|
19
|
+
import { addLocizeSavedHandler, startStandalone } from 'locize';
|
|
20
|
+
import Bugsnag from '@bugsnag/js';
|
|
21
|
+
import BugsnagPluginReact from '@bugsnag/plugin-react';
|
|
22
|
+
// import { makeSelectLocationState } from 'containers/Cap/selectors';
|
|
23
|
+
import LanguageProvider from 'v2Containers/LanguageProvider';
|
|
24
|
+
import FontFaceObserver from 'fontfaceobserver';
|
|
25
|
+
/* eslint-disable import/no-unresolved, import/extensions */
|
|
26
|
+
import '!file-loader?name=[name].[ext]!./favicon.ico';
|
|
27
|
+
/* eslint-enable import/no-unresolved, import/extensions */
|
|
28
|
+
import { configureStore } from '@capillarytech/vulcan-react-sdk/utils';
|
|
29
|
+
import { initialReducer } from './initialReducer';
|
|
30
|
+
import { translationMessages } from './i18n';
|
|
31
|
+
import './global-styles';
|
|
32
|
+
import initialState from './initialState';
|
|
33
|
+
import './styles/vendor/semantic/dist/semantic.min.css';
|
|
34
|
+
import './styles/main.scss';
|
|
35
|
+
import pathConfig from './config/path';
|
|
36
|
+
import ignoredErrorMessages from './utils/ignoredErrorMessages';
|
|
37
|
+
import App from './containers/App';
|
|
38
|
+
import ConfigProvider from 'antd/lib/config-provider';
|
|
39
|
+
import { getCapThemeConfig, loadCapUI } from '@capillarytech/cap-ui-library/utils';
|
|
40
|
+
|
|
41
|
+
loadCapUI();
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
if (!window.MFE_HOST) {
|
|
45
|
+
const { setupNewrelicWebVitals } = require('./newRelic/utils');
|
|
46
|
+
// Setup New Relic WebVitals tracking
|
|
47
|
+
setupNewrelicWebVitals();
|
|
48
|
+
}
|
|
49
|
+
} catch (error) {
|
|
50
|
+
Bugsnag.notify(error);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// const browserHistory = useRouterHistory(createHistory)({
|
|
54
|
+
// basename: pathConfig.publicPath,
|
|
55
|
+
// });
|
|
56
|
+
|
|
57
|
+
const store = configureStore(initialState, initialReducer, history);
|
|
58
|
+
const MOUNT_NODE = document.getElementById('app');
|
|
59
|
+
|
|
60
|
+
// const history = syncHistoryWithStore(browserHistory, store, {
|
|
61
|
+
// selectLocationState: makeSelectLocationState(),
|
|
62
|
+
// });
|
|
63
|
+
const BUGSNAG_APP_VERSION = `creatives-ui__${new Date().getTime()}`;
|
|
64
|
+
const BUGSNAG_API_KEY = 'c4d96446438ff7dc7c08da08ef94c5b2';
|
|
65
|
+
|
|
66
|
+
const bugSnagErrorCallback = (event) => {
|
|
67
|
+
const { app: { releaseStage } = {}, originalError } = event || {};
|
|
68
|
+
const errorMessage = originalError && originalError.message;
|
|
69
|
+
if (
|
|
70
|
+
(releaseStage || '').includes('nightly') ||
|
|
71
|
+
process.env.NODE_ENV !== 'production' ||
|
|
72
|
+
ignoredErrorMessages.includes(errorMessage)
|
|
73
|
+
) {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
let userId;
|
|
77
|
+
let userName;
|
|
78
|
+
let userEmail;
|
|
79
|
+
let orgId;
|
|
80
|
+
let orgName;
|
|
81
|
+
try {
|
|
82
|
+
const {
|
|
83
|
+
id,
|
|
84
|
+
firstName,
|
|
85
|
+
lastName,
|
|
86
|
+
loginName,
|
|
87
|
+
proxyOrgList,
|
|
88
|
+
} = JSON.parse(window.localStorage.getItem('user'));
|
|
89
|
+
orgId = window.localStorage.getItem('orgID');
|
|
90
|
+
userId = id;
|
|
91
|
+
userName = `${firstName} ${lastName}`;
|
|
92
|
+
userEmail = loginName;
|
|
93
|
+
orgName = proxyOrgList.find(({ orgID }) => orgID === Number(orgId)).orgName;
|
|
94
|
+
} catch (error) {
|
|
95
|
+
userId = '';
|
|
96
|
+
userName = '';
|
|
97
|
+
userEmail = '';
|
|
98
|
+
orgId = '';
|
|
99
|
+
orgName = '';
|
|
100
|
+
}
|
|
101
|
+
event.addMetadata('org', {
|
|
102
|
+
name: orgName,
|
|
103
|
+
id: orgId,
|
|
104
|
+
});
|
|
105
|
+
event.setUser(userId, userEmail, userName);
|
|
106
|
+
if (window.FS && window.FS.getCurrentSessionURL) {
|
|
107
|
+
event.addMetadata('fullstory', {
|
|
108
|
+
urlAtTime: window.FS.getCurrentSessionURL(true),
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
Bugsnag.start({
|
|
114
|
+
appVersion: BUGSNAG_APP_VERSION,
|
|
115
|
+
apiKey: BUGSNAG_API_KEY,
|
|
116
|
+
releaseStage: window.location.hostname,
|
|
117
|
+
plugins: [new BugsnagPluginReact()],
|
|
118
|
+
onError: bugSnagErrorCallback,
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
const ErrorBoundary = Bugsnag.getPlugin('react').createErrorBoundary(React);
|
|
122
|
+
|
|
123
|
+
export const BugsnagClient = Bugsnag;
|
|
124
|
+
|
|
125
|
+
const ErrorScreen = () => <CapSomethingWentWrong url={`${pathConfig.publicPath}/v2`} />
|
|
126
|
+
const root = createRoot(MOUNT_NODE);
|
|
127
|
+
const render = (messages) => {
|
|
128
|
+
root.render(
|
|
129
|
+
<Provider store={store}>
|
|
130
|
+
<LanguageProvider messages={messages}>
|
|
131
|
+
<ErrorBoundary FallbackComponent={ErrorScreen}>
|
|
132
|
+
<ConfigProvider theme={getCapThemeConfig()}>
|
|
133
|
+
<App />
|
|
134
|
+
</ConfigProvider>
|
|
135
|
+
</ErrorBoundary>
|
|
136
|
+
</LanguageProvider>
|
|
137
|
+
</Provider>,
|
|
138
|
+
);
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
// Hot reloadable translation json files
|
|
142
|
+
if (module.hot) {
|
|
143
|
+
// modules.hot.accept does not accept dynamic dependencies,
|
|
144
|
+
// have to be constants at compile-time
|
|
145
|
+
module.hot.accept('./i18n', () => {
|
|
146
|
+
render(translationMessages);
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// 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
|
|
151
|
+
// need to comment as of now
|
|
152
|
+
// addLocizeSavedHandler(res => {
|
|
153
|
+
// res.updated.forEach(item => {
|
|
154
|
+
// const { lng, ns, key, data } = item
|
|
155
|
+
// // load the translations somewhere...
|
|
156
|
+
// // and maybe rerender your UI
|
|
157
|
+
// })
|
|
158
|
+
// })
|
|
159
|
+
|
|
160
|
+
// start
|
|
161
|
+
startStandalone();
|
|
162
|
+
|
|
163
|
+
const openSansObserver = new FontFaceObserver('Roboto', {});
|
|
164
|
+
// When Open Sans is loaded, add a font-family using Open Sans to the body
|
|
165
|
+
openSansObserver.load().then(() => {
|
|
166
|
+
document.body.classList.add('fontLoaded');
|
|
167
|
+
}, () => {
|
|
168
|
+
document.body.classList.remove('fontLoaded');
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
// Chunked polyfill for browsers without Intl support
|
|
172
|
+
if (!window.Intl) {
|
|
173
|
+
(new Promise((resolve) => {
|
|
174
|
+
resolve(import('intl'));
|
|
175
|
+
}))
|
|
176
|
+
.then(() => Promise.all([
|
|
177
|
+
import('intl/locale-data/jsonp/en.js'),
|
|
178
|
+
]))
|
|
179
|
+
.then(() => render(translationMessages))
|
|
180
|
+
.catch((err) => {
|
|
181
|
+
throw err;
|
|
182
|
+
});
|
|
183
|
+
} else {
|
|
184
|
+
render(translationMessages);
|
|
185
|
+
}
|
package/entry.js
CHANGED
|
@@ -1,2 +1,68 @@
|
|
|
1
1
|
import { publicPath } from './config/path';
|
|
2
|
-
|
|
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();
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@capillarytech/creatives-library",
|
|
3
3
|
"author": "meharaj",
|
|
4
|
-
"version": "9.0.
|
|
4
|
+
"version": "9.0.33",
|
|
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.20",
|
|
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,6 +5,7 @@ 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';
|
|
8
9
|
import config from '../config/app';
|
|
9
10
|
import pathConfig from '../config/path';
|
|
10
11
|
import getSchema from './getSchema';
|
|
@@ -258,6 +259,13 @@ export const getSidebar = () => {
|
|
|
258
259
|
|
|
259
260
|
// if 403, show SWR page
|
|
260
261
|
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
|
+
}
|
|
261
269
|
const url = `${API_AUTH_ENDPOINT}/user?include_features=1`;
|
|
262
270
|
return request(url, getAPICallObject('GET'), true);
|
|
263
271
|
};
|
|
@@ -679,7 +687,7 @@ export const getSupportVideosConfig = () => {
|
|
|
679
687
|
};
|
|
680
688
|
|
|
681
689
|
export const getNavigationConfigApi = async () => {
|
|
682
|
-
const url = `${ARYA_ENDPOINT}/
|
|
690
|
+
const url = `${ARYA_ENDPOINT}/mfe_navigations`;
|
|
683
691
|
return request(url, getAPICallObject('GET'));
|
|
684
692
|
};
|
|
685
693
|
|