@capillarytech/creatives-library 9.0.15 → 9.0.16-beta.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/AppRoot.js ADDED
@@ -0,0 +1,80 @@
1
+ import React, { useMemo, useEffect, useRef } from 'react';
2
+ import { Provider } from 'react-redux';
3
+ import { configureStore } from '@capillarytech/vulcan-react-sdk/utils';
4
+ import { createBrowserHistory } from 'history';
5
+ import ConfigProvider from 'antd/lib/config-provider';
6
+ import { getCapThemeConfig, loadCapUI } from '@capillarytech/cap-ui-library/utils';
7
+
8
+ import LanguageProvider from 'v2Containers/LanguageProvider';
9
+ import App from './containers/App';
10
+ import './styles/main.scss';
11
+ import { initialReducer } from './initialReducer';
12
+ import { translationMessages } from './i18n';
13
+ import initialState from './initialState';
14
+ import pathConfig from './config/path';
15
+ import { isMFEMode } from './utils/mfeDetect';
16
+ import { MFEEventBus } from '@capillarytech/cap-ui-utils';
17
+ import appConfig from '../app-config';
18
+
19
+ // Module-scope SDK init — MUST run before configureStore.
20
+ loadCapUI();
21
+
22
+ const AppRoot = ({ basename: _basename = '/creatives/ui', history: hostHistory }) => {
23
+ const { store, history } = useMemo(() => {
24
+ const hist =
25
+ hostHistory ||
26
+ createBrowserHistory({ basename: pathConfig.publicPath });
27
+ const st = configureStore(initialState, initialReducer, hist);
28
+ return { store: st, history: hist };
29
+ }, [hostHistory]);
30
+
31
+ const fcpReported = useRef(false);
32
+ useEffect(() => {
33
+ if (isMFEMode() && !fcpReported.current) {
34
+ fcpReported.current = true;
35
+ requestAnimationFrame(() => {
36
+ MFEEventBus.emit('mfe:segment', {
37
+ action: 'stop',
38
+ appId: appConfig.appName,
39
+ type: 'fcp',
40
+ timestamp: performance.now(),
41
+ });
42
+ });
43
+ }
44
+ }, []);
45
+
46
+ useEffect(() => {
47
+ if (!isMFEMode()) {
48
+ const { startStandalone } = require('locize');
49
+ startStandalone();
50
+ }
51
+ }, []);
52
+
53
+ useEffect(() => {
54
+ if (isMFEMode()) {
55
+ MFEEventBus.emit('gtm:init', {
56
+ appId: appConfig.appName,
57
+ containerId: appConfig.gtm.trackingId,
58
+ });
59
+ }
60
+ }, []);
61
+
62
+ useEffect(() => {
63
+ const unsub = MFEEventBus.on('creatives:navigate', ({ path }) => {
64
+ if (path) history.push(path);
65
+ });
66
+ return unsub;
67
+ }, [history]);
68
+
69
+ return (
70
+ <Provider store={store}>
71
+ <LanguageProvider messages={translationMessages}>
72
+ <ConfigProvider theme={getCapThemeConfig()}>
73
+ <App history={history} />
74
+ </ConfigProvider>
75
+ </LanguageProvider>
76
+ </Provider>
77
+ );
78
+ };
79
+
80
+ export default AppRoot;
package/app.js CHANGED
@@ -33,9 +33,11 @@ import { getCapThemeConfig, loadCapUI } from '@capillarytech/cap-ui-library/util
33
33
  loadCapUI();
34
34
 
35
35
  try {
36
- const { setupNewrelicWebVitals } = require('./newRelic/utils');
37
- // Setup New Relic WebVitals tracking
38
- setupNewrelicWebVitals();
36
+ if (!window.MFE_HOST) {
37
+ const { setupNewrelicWebVitals } = require('./newRelic/utils');
38
+ // Setup New Relic WebVitals tracking
39
+ setupNewrelicWebVitals();
40
+ }
39
41
  } catch (error) {
40
42
  Bugsnag.notify(error);
41
43
  }
package/entry.js CHANGED
@@ -1,2 +1,68 @@
1
1
  import { publicPath } from './config/path';
2
- __webpack_public_path__ = ((new URL(((document || {}).currentScript || {}).src || window.location)).origin) + `${publicPath}/`;
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,7 +2,5 @@
2
2
  // Contents of this file are used in webpack config's ModuleFederationPlugin.
3
3
  //
4
4
  module.exports = {
5
- // which exposes
6
- // ADD MFE COMPONENT YOU WANT TO EXPORT example:
7
- // './Home': './app/components/pages/Home',
8
- };
5
+ './AppRoot': './app/AppRoot',
6
+ };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@capillarytech/creatives-library",
3
3
  "author": "meharaj",
4
- "version": "9.0.15",
4
+ "version": "9.0.16-beta.0",
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.4",
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
@@ -679,7 +679,7 @@ export const getSupportVideosConfig = () => {
679
679
  };
680
680
 
681
681
  export const getNavigationConfigApi = async () => {
682
- const url = `${ARYA_ENDPOINT}/navigations`;
682
+ const url = `${ARYA_ENDPOINT}/mfe_navigations`;
683
683
  return request(url, getAPICallObject('GET'));
684
684
  };
685
685
 
@@ -79,7 +79,6 @@
79
79
  flex: 1;
80
80
  height: calc(100vh - 50px);
81
81
  overflow: auto;
82
- padding: 8px 16px 0;
83
82
  color: #333333;
84
83
  }
85
84
 
@@ -154,16 +153,19 @@
154
153
  }
155
154
 
156
155
  .cap-loader-box {
157
- position: fixed;
156
+ position: absolute;
158
157
  top: 0;
158
+ left: 0;
159
159
  width: 100%;
160
- height: 100%;
161
- background: rgb(255,255,255);
162
- text-align: center;
160
+ min-height: calc(100vh - 48px);
161
+ background: #ffffff;
162
+ display: flex;
163
+ align-items: center;
164
+ justify-content: center;
163
165
 
164
166
  .loader-image {
165
167
  width: 80px;
166
- margin-top: 20%;
168
+ height: auto;
167
169
  }
168
170
  }
169
171
 
@@ -0,0 +1,15 @@
1
+ import { isMFEMode } from './mfeDetect';
2
+ import appConfig from '../../app-config';
3
+
4
+ const getDataLayerName = () => {
5
+ if (isMFEMode()) {
6
+ return `dataLayer_${appConfig.appName.replace(/-/g, '_')}`;
7
+ }
8
+ return 'dataLayer';
9
+ };
10
+
11
+ export const getDataLayer = () => {
12
+ const name = getDataLayerName();
13
+ window[name] = window[name] || [];
14
+ return window[name];
15
+ };
@@ -1,4 +1,5 @@
1
1
  import { CREATIVES } from "../../../v2Containers/App/constants";
2
+ import { getDataLayer } from '../../getDataLayer';
2
3
 
3
4
  const creativeDetails = ({
4
5
  name,
@@ -23,7 +24,7 @@ const creativeDetails = ({
23
24
  videoAdded,
24
25
  stickersAdded
25
26
  };
26
- window.dataLayer.push({
27
+ getDataLayer().push({
27
28
  creativeDetails: parsedObj,
28
29
  event: 'creativeDetails',
29
30
  });
@@ -0,0 +1 @@
1
+ export const isMFEMode = () => Boolean(window.MFE_HOST);
@@ -12,8 +12,10 @@ import { loadItem } from 'services/localStorageApi';
12
12
  import { intlShape, injectIntl } from 'react-intl';
13
13
  import { withRouter } from 'react-router-dom';
14
14
  import { get } from 'lodash';
15
- import CapNavigation from '@capillarytech/cap-ui-library/CapNavigation';
15
+ import CapNavigation from '@capillarytech/cap-ui-library/CapNavigationSPA';
16
16
  import { CAP_WHITE } from '@capillarytech/cap-ui-library/styled/variables';
17
+ import { MFEModuleHeader } from '@capillarytech/cap-ui-utils';
18
+ import { MFE_MODULE_HEADER } from './mfeModuleHeader.config';
17
19
  import injectSaga from '../../utils/injectSaga';
18
20
  import sagas from './saga';
19
21
  import messages from './messages';
@@ -30,12 +32,11 @@ import { CapLeftNavigatioOpenCss, CapLeftNavigationCss } from './style';
30
32
  import { EMBEDDED } from './constants';
31
33
 
32
34
  const CapWrapper = styled.div`
33
- position: absolute;
34
35
  padding: 0;
35
36
  background-color: ${CAP_WHITE};
36
37
  width: 100%;
37
38
  display: flex;
38
- margin-top: ${(props) => props.isEmbedded ? '0px' : '64px'};
39
+
39
40
 
40
41
  .sidebar-container {
41
42
  margin-right: 10px;
@@ -46,10 +47,8 @@ const CapWrapper = styled.div`
46
47
  `;
47
48
 
48
49
  const ComponentWrapper = styled.div`
49
- max-width: 1140px;
50
- margin: 0 auto;
51
50
  width: 100%;
52
- padding: 24px 0;
51
+ padding: 0;
53
52
  `;
54
53
 
55
54
  export class NavigationBar extends React.Component {
@@ -58,6 +57,7 @@ export class NavigationBar extends React.Component {
58
57
  this.state = this.initializeSelectedProduct();
59
58
  }
60
59
 
60
+
61
61
  initializeSelectedProduct = () => {
62
62
  const { location, intl: { formatMessage } } = this.props;
63
63
  const { pathname } = location;
@@ -179,6 +179,7 @@ export class NavigationBar extends React.Component {
179
179
  const topbarIcons = this.getTopbarIcons(showDocumentationBot);
180
180
  const headerOverideCss = leftNavbarExpandedProp ? CapLeftNavigatioOpenCss : CapLeftNavigationCss;
181
181
  return (
182
+ <>
182
183
  <CapNavigation
183
184
  className="creatives-main-container"
184
185
  showContent
@@ -212,7 +213,8 @@ export class NavigationBar extends React.Component {
212
213
  </CapWrapper>
213
214
  </div>
214
215
  </CapNavigation>
215
-
216
+ <MFEModuleHeader history={this.props.history} {...MFE_MODULE_HEADER} />
217
+ </>
216
218
  );
217
219
  }
218
220
  }
@@ -0,0 +1,16 @@
1
+ // Static, module-specific config for the shared MFE module-header wrapper
2
+ // (MFEModuleHeader). Runtime values (history) are supplied in NavigationBar.
3
+ export const MFE_MODULE_HEADER = {
4
+ header: {
5
+ name: 'Creatives',
6
+ description: 'Store your creative templates for all your channels',
7
+ showBorder: false,
8
+ showSettings: false,
9
+ },
10
+ landingRoutes: [
11
+ '/creatives/ui/v2/',
12
+ '/creatives/ui/v2',
13
+ '/v2/',
14
+ '/v2'
15
+ ],
16
+ };
@@ -2,6 +2,8 @@
2
2
  height: calc(100vh - 20rem);
3
3
  overflow-y: auto;
4
4
  overflow-x: hidden;
5
+ margin-right: -24px;
6
+ padding-right: 24px;
5
7
  }
6
8
 
7
9
  .v2-pagination-container.full-height {
@@ -56,6 +56,7 @@ export const SUCCESS = 'SUCCESS';
56
56
  export const FAILURE = 'FAILURE';
57
57
 
58
58
  export const ENABLE_PRODUCT_SUPPORT_VIDEOS = 'ENABLE_PRODUCT_SUPPORT_VIDEOS';
59
+ export const ENABLE_NEW_LEFT_NAVIGATION = 'ENABLE_NEW_LEFT_NAVIGATION';
59
60
  export const DEFAULT = 'default';
60
61
  export const DEFAULT_MODULE = 'creatives';
61
62
 
@@ -30,6 +30,7 @@ import {
30
30
  REQUEST,
31
31
  DEFAULT,
32
32
  ENABLE_PRODUCT_SUPPORT_VIDEOS,
33
+ ENABLE_NEW_LEFT_NAVIGATION,
33
34
  CAMPAIGN_SETTINGS_URL,
34
35
  } from './constants';
35
36
  import './_cap.scss';
@@ -53,7 +54,8 @@ import { v2ViberSagas } from '../Viber/sagas';
53
54
  import { v2FacebookSagas } from '../Facebook/sagas';
54
55
  import createReducer from '../Line/Container/reducer';
55
56
  import { DAEMON } from '@capillarytech/vulcan-react-sdk/utils/sagaInjectorTypes';
56
- const gtm = window.dataLayer || [];
57
+ import { getDataLayer } from '../../utils/getDataLayer';
58
+ const gtm = getDataLayer();
57
59
  const {
58
60
  logNewTab,
59
61
  utilsGetOrgNameFromId,
@@ -72,18 +74,19 @@ const CapWrapper = styled.div`
72
74
  min-height: 100%;
73
75
  padding: 0;
74
76
  flex-direction: column;
77
+ position: relative;
75
78
  `;
76
79
  GA.initialize({ accessKey: GTM_TRACKING_ID, trackUIError: true, trackLoadPerformance: 'creatives' });
77
80
 
78
81
  const MainWrapper = styled.div`
79
82
  position: relative;
80
- min-height: calc(100vh - 5.29rem);
81
- top: 4.72rem;
83
+ min-height: 100%;
84
+ top: 0;
82
85
  `;
83
86
 
84
87
  const ContentWrapper = styled.div`
85
88
  && {
86
- margin-left: ${(props) => (props.leftNavbarExpanded ? '17rem' : '4.475rem')};
89
+ margin-left: 0;
87
90
  }
88
91
  `;
89
92
 
@@ -500,6 +503,8 @@ export class Cap extends React.Component { // eslint-disable-line react/prefer-s
500
503
  const type = this.props.location.query.type;
501
504
  const toastMessages = this.props.Global.messages;
502
505
  const { isCreativesAccessible, showOrgChangeModal, showRefreshModal } = this.state;
506
+ const { accessibleFeatures = [] } = currentOrgDetails;
507
+ const isLatestLeftNavigationEnabled = accessibleFeatures.includes(ENABLE_NEW_LEFT_NAVIGATION);
503
508
  return (
504
509
  <CapWrapper>
505
510
  { this.props.loader.localeLoading || this.props.Global.fetching_userdata ?
@@ -537,26 +542,47 @@ export class Cap extends React.Component { // eslint-disable-line react/prefer-s
537
542
  campaignOrgV2Status={currentOrgDetails.org_campaign_v2_status}
538
543
  handleLeftNavBarExpanded={this.handleLeftNavBarExpanded}
539
544
  leftNavbarExpandedProp={this.state.leftNavbarExpanded}
540
- />) : ''}
541
- <MainWrapper className="main">
542
545
 
543
- <ContentWrapper
544
- className={`main-content ${this.state.leftNavbarExpanded ? "contet-width-collapse" : "content-width-expanded"}`}
545
- leftNavbarExpanded={this.state.leftNavbarExpanded}
546
546
  >
547
- <Switch>
548
- {componentRoutes.map(routeProps => {
549
- return (
550
- <RenderRoute
551
- {...routeProps}
552
- key={routeProps.path}
553
- isLoggedIn={isLoggedIn}
554
- />
547
+ <MainWrapper isLatestLeftNavigationEnabled={isLatestLeftNavigationEnabled} className="main">
548
+ <ContentWrapper
549
+ className={`main-content ${this.state.leftNavbarExpanded ? "contet-width-collapse" : "content-width-expanded"}`}
550
+ isLatestLeftNavigationEnabled={isLatestLeftNavigationEnabled}
551
+ leftNavbarExpanded={this.state.leftNavbarExpanded}
552
+ >
553
+ <Switch>
554
+ {componentRoutes.map(routeProps => {
555
+ return (
556
+ <RenderRoute
557
+ {...routeProps}
558
+ key={routeProps.path}
559
+ isLoggedIn={isLoggedIn}
560
+ />
561
+ )}
562
+ )}
563
+ </Switch>
564
+ </ContentWrapper>
565
+ </MainWrapper>
566
+ </NavigationBar>) : (
567
+ <MainWrapper isLatestLeftNavigationEnabled={isLatestLeftNavigationEnabled} className="main">
568
+ <ContentWrapper
569
+ className={`main-content ${this.state.leftNavbarExpanded ? "contet-width-collapse" : "content-width-expanded"}`}
570
+ isLatestLeftNavigationEnabled={isLatestLeftNavigationEnabled}
571
+ leftNavbarExpanded={this.state.leftNavbarExpanded}
572
+ >
573
+ <Switch>
574
+ {componentRoutes.map(routeProps => {
575
+ return (
576
+ <RenderRoute
577
+ {...routeProps}
578
+ key={routeProps.path}
579
+ isLoggedIn={isLoggedIn}
580
+ />
581
+ )}
555
582
  )}
556
- )}
557
- </Switch>
558
- </ContentWrapper>
559
- </MainWrapper>
583
+ </Switch>
584
+ </ContentWrapper>
585
+ </MainWrapper>)}
560
586
  </div>
561
587
  {(toastMessages && toastMessages.length > 0) &&
562
588
  toastMessages.map((message) => {
@@ -7,6 +7,7 @@ import {
7
7
  // import { normalize } from 'normalizr';
8
8
  import * as Api from '../../services/api';
9
9
  import * as LocalStorage from '../../services/localStorageApi';
10
+ import { isMFEMode } from '../../utils/mfeDetect';
10
11
  import * as types from './constants';
11
12
  import config from '../../config/app';
12
13
  //import pathConfig from '../../config/path';
@@ -82,6 +83,24 @@ export function* logoutFlow() {
82
83
 
83
84
  export function* fetchUserInfo(option) {
84
85
  try {
86
+ if (isMFEMode()) {
87
+ const userData = LocalStorage.loadItem('user');
88
+ const currentOrgId = LocalStorage.loadItem('orgID');
89
+ const storedOrgDetails = LocalStorage.loadItem('currentOrgDetails');
90
+ const currentOrgDetails = {
91
+ ...(storedOrgDetails || {}),
92
+ accessibleFeatures: window.capAuth?.accessibleFeatures || [],
93
+ };
94
+ yield put({
95
+ type: types.GET_USER_DATA_SUCCESS,
96
+ userData: userData || {},
97
+ currentOrgId,
98
+ currentOrgDetails,
99
+ });
100
+ if (option.callback) option.callback(userData, currentOrgDetails);
101
+ return;
102
+ }
103
+
85
104
  const result = yield call(Api.getUserData);
86
105
  if (result?.isError || result?.status === 401) {
87
106
  yield call(LocalStorage.clearItem, 'user');
@@ -1,7 +1,7 @@
1
1
  @import '~@capillarytech/cap-ui-library/styles/_variables.scss';
2
2
 
3
3
  .ant-tabs-content{
4
- margin-top: $CAP_SPACE_16;
4
+ margin-top: $CAP_SPACE_08;
5
5
  .ant-tabs-tabpane-active{
6
6
  padding: unset;
7
7
  }
@@ -1174,7 +1174,7 @@
1174
1174
  display: flex;
1175
1175
  align-items: baseline;
1176
1176
  justify-content: space-between;
1177
- width: 102%;
1177
+ width: 100%;
1178
1178
  }
1179
1179
 
1180
1180
  .filter-row-content {
@@ -1243,7 +1243,6 @@
1243
1243
  justify-content: space-between;
1244
1244
  align-items: center;
1245
1245
  gap: $CAP_SPACE_08;
1246
- margin-right: $CAP_SPACE_32;
1247
1246
  }
1248
1247
 
1249
1248
  .template-listing-more-btn {
@@ -4561,6 +4561,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
4561
4561
  <CapButton
4562
4562
  className={`create-new-${channelLowerCase} margin-l-8 margin-b-12`}
4563
4563
  type={"primary"}
4564
+ isAddBtn
4564
4565
  disabled={this.isCreateDisabled()}
4565
4566
  onClick={this.createTemplate}
4566
4567
  >
@@ -10,10 +10,10 @@ export default css`
10
10
 
11
11
  .component-wrapper {
12
12
  ${(props) => props.isFullMode ? `
13
- max-width: 1140px;
14
- margin: 0 auto;
13
+ max-width: 100%;
14
+ margin: 0;
15
15
  width: 100%;
16
- padding: 0.714rem 0;
16
+ padding: 0;
17
17
  /* Only main channel tabs content, not HTML Editor validation panel tabs */
18
18
  > .cap-tab-v2 > .ant-tabs-content-holder > .ant-tabs-content,
19
19
  > .cap-tab-v2 > .ant-tabs-content {
@@ -26,6 +26,12 @@ export default css`
26
26
  `;
27
27
 
28
28
  export const CapTabStyle = css`
29
- ${(props) => props.isFullMode ? `margin-top: ${CAP_SPACE_24}` : ``
29
+ ${(props) => props.isFullMode ? `margin-top: ${CAP_SPACE_24};` : ``
30
+ }
31
+ .ant-tabs-nav-list {
32
+ gap: 2rem;
33
+ }
34
+ .ant-tabs-tab + .ant-tabs-tab {
35
+ margin: 0 !important;
30
36
  }
31
37
  `;
@@ -46,6 +46,9 @@ import {
46
46
  NORMALIZED_CHANNEL_ALIASES,
47
47
  SMS,
48
48
  } from "../CreativesContainer/constants";
49
+ import { MFEEventBus } from '@capillarytech/cap-ui-utils';
50
+ import { isMFEMode } from '../../utils/mfeDetect';
51
+ import appConfig from '../../../app-config';
49
52
 
50
53
  const {CapCustomCardList} = CapCustomCard;
51
54
 
@@ -53,6 +56,7 @@ const StyledCapTab = withStyles(CapTab, CapTabStyle);
53
56
  export class TemplatesV2 extends React.Component { // eslint-disable-line react/prefer-stateless-function
54
57
  constructor(props) {
55
58
  super(props);
59
+ this.lcpReported = false;
56
60
  let defaultChannel = get(this, 'props.channel') || get(this, 'props.params.channel') || 'sms';
57
61
  if (defaultChannel === LOYALTY) {
58
62
  defaultChannel = 'sms';
@@ -221,6 +225,23 @@ export class TemplatesV2 extends React.Component { // eslint-disable-line react/
221
225
  }
222
226
  }
223
227
 
228
+ componentDidUpdate(prevProps) {
229
+ if (
230
+ isMFEMode() &&
231
+ !this.lcpReported &&
232
+ !this.props.Templates.getAllTemplatesInProgress &&
233
+ prevProps.Templates.getAllTemplatesInProgress
234
+ ) {
235
+ this.lcpReported = true;
236
+ MFEEventBus.emit('mfe:segment', {
237
+ action: 'stop',
238
+ appId: appConfig.appName,
239
+ type: 'lcp',
240
+ timestamp: performance.now(),
241
+ });
242
+ }
243
+ }
244
+
224
245
  componentWillReceiveProps(nextProps) {
225
246
  if (this.props.channel !== nextProps.channel) {
226
247
  const panes = this.setChannelContent(nextProps.channel, this.state.panes);
@@ -381,7 +402,6 @@ export class TemplatesV2 extends React.Component { // eslint-disable-line react/
381
402
  ]}
382
403
  />}
383
404
  <div className="component-wrapper">
384
- {isFullMode && <CapHeader title={<FormattedMessage {...messages.creatives}/>} description={<FormattedMessage {...messages.creativesDesc}/>}/>}
385
405
  <StyledCapTab
386
406
  panes={this.state.panes}
387
407
  onChange={this.channelChange}
@@ -744,7 +744,11 @@ export const Whatsapp = (props) => {
744
744
  carouselIndex,
745
745
  } = tagSelectData;
746
746
  if (mapData && updatedData) {
747
- const numId = Number(areaId?.slice(areaId?.indexOf("_") + 1));
747
+ const underscoreIdx = areaId ? areaId.indexOf("_") : -1;
748
+ if (underscoreIdx < 0) {
749
+ return;
750
+ }
751
+ const numId = Number(areaId.slice(underscoreIdx + 1));
748
752
  if (!isNaN(numId)) {
749
753
  const arr = [...updatedData];
750
754
  //when trying to insert tag in empty textarea,{#var#} is replaced with "" and then tag is added