2018-05-18 17:31:08 +00:00
|
|
|
/**
|
|
|
|
* External dependencies
|
|
|
|
*/
|
2021-04-16 13:29:54 +00:00
|
|
|
import { SlotFillProvider } from '@wordpress/components';
|
2020-04-27 14:41:26 +00:00
|
|
|
import { compose } from '@wordpress/compose';
|
|
|
|
import { withSelect } from '@wordpress/data';
|
2023-05-18 08:25:36 +00:00
|
|
|
import { Component, lazy, Suspense, useEffect } from '@wordpress/element';
|
2022-05-30 06:51:33 +00:00
|
|
|
import {
|
|
|
|
unstable_HistoryRouter as HistoryRouter,
|
|
|
|
Route,
|
|
|
|
Routes,
|
|
|
|
useLocation,
|
|
|
|
useMatch,
|
|
|
|
useParams,
|
|
|
|
} from 'react-router-dom';
|
|
|
|
import { Children, cloneElement } from 'react';
|
2018-06-26 14:49:42 +00:00
|
|
|
import PropTypes from 'prop-types';
|
2023-05-18 08:25:36 +00:00
|
|
|
import { isFunction, identity } from 'lodash';
|
2023-03-21 15:58:35 +00:00
|
|
|
import {
|
|
|
|
CustomerEffortScoreModalContainer,
|
|
|
|
triggerExitPageCesSurvey,
|
|
|
|
} from '@woocommerce/customer-effort-score';
|
2020-11-26 00:27:37 +00:00
|
|
|
import { getHistory, getQuery } from '@woocommerce/navigation';
|
2020-06-10 23:49:27 +00:00
|
|
|
import {
|
|
|
|
PLUGINS_STORE_NAME,
|
2021-02-19 13:57:17 +00:00
|
|
|
useUser,
|
2020-06-10 23:49:27 +00:00
|
|
|
withPluginsHydration,
|
|
|
|
withOptionsHydration,
|
|
|
|
} from '@woocommerce/data';
|
2020-08-20 04:59:52 +00:00
|
|
|
import { recordPageView } from '@woocommerce/tracks';
|
2022-06-03 23:05:26 +00:00
|
|
|
import '@woocommerce/notices';
|
2021-04-23 03:44:54 +00:00
|
|
|
import { PluginArea } from '@wordpress/plugins';
|
2023-04-25 22:17:33 +00:00
|
|
|
import {
|
|
|
|
LayoutContextProvider,
|
|
|
|
getLayoutContextValue,
|
|
|
|
} from '@woocommerce/admin-layout';
|
2018-11-05 21:02:04 +00:00
|
|
|
|
2018-05-18 17:31:08 +00:00
|
|
|
/**
|
|
|
|
* Internal dependencies
|
|
|
|
*/
|
|
|
|
import './style.scss';
|
2020-08-20 23:14:55 +00:00
|
|
|
import { Controller, getPages } from './controller';
|
2020-08-23 22:46:18 +00:00
|
|
|
import { Header } from '../header';
|
2022-12-02 09:35:47 +00:00
|
|
|
import { Footer } from './footer';
|
2018-05-18 17:31:08 +00:00
|
|
|
import Notices from './notices';
|
2019-02-13 11:44:58 +00:00
|
|
|
import TransientNotices from './transient-notices';
|
2022-01-06 12:53:30 +00:00
|
|
|
import { getAdminSetting } from '~/utils/admin-settings';
|
2023-05-18 08:25:36 +00:00
|
|
|
import { usePageClasses } from './hooks/use-page-classes';
|
2021-12-14 16:56:42 +00:00
|
|
|
import '~/activity-panel';
|
|
|
|
import '~/mobile-banner';
|
2020-10-22 03:01:25 +00:00
|
|
|
import './navigation';
|
2020-08-13 02:05:22 +00:00
|
|
|
|
2020-04-29 18:01:27 +00:00
|
|
|
const StoreAlerts = lazy( () =>
|
|
|
|
import( /* webpackChunkName: "store-alerts" */ './store-alerts' )
|
|
|
|
);
|
2018-05-18 17:31:08 +00:00
|
|
|
|
2020-11-26 00:27:37 +00:00
|
|
|
const WCPayUsageModal = lazy( () =>
|
|
|
|
import(
|
2023-05-17 01:54:31 +00:00
|
|
|
/* webpackChunkName: "wcpay-usage-modal" */ '../task-lists/fills/PaymentGatewaySuggestions/components/WCPay/UsageModal'
|
2020-11-26 00:27:37 +00:00
|
|
|
)
|
|
|
|
);
|
|
|
|
|
2019-05-22 17:53:30 +00:00
|
|
|
export class PrimaryLayout extends Component {
|
|
|
|
render() {
|
|
|
|
const { children } = this.props;
|
|
|
|
return (
|
2020-02-14 02:23:21 +00:00
|
|
|
<div
|
|
|
|
className="woocommerce-layout__primary"
|
|
|
|
id="woocommerce-layout__primary"
|
|
|
|
>
|
2020-04-29 18:01:27 +00:00
|
|
|
{ window.wcAdminFeatures[ 'store-alerts' ] && (
|
2021-11-03 04:17:26 +00:00
|
|
|
<Suspense fallback={ null }>
|
2020-04-29 18:01:27 +00:00
|
|
|
<StoreAlerts />
|
|
|
|
</Suspense>
|
|
|
|
) }
|
2019-05-22 17:53:30 +00:00
|
|
|
<Notices />
|
|
|
|
{ children }
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-30 06:51:33 +00:00
|
|
|
/**
|
|
|
|
* Exists for the sole purpose of passing on react-router hooks into
|
|
|
|
* the class based Layout component. Feel free to refactor it by turning _Layout
|
|
|
|
* into a functional component and moving the hooks inside
|
|
|
|
*
|
|
|
|
* @param {Object} root0 root0 React component props
|
|
|
|
* @param {Object} root0.children Children componeents
|
|
|
|
*/
|
|
|
|
const WithReactRouterProps = ( { children } ) => {
|
|
|
|
const location = useLocation();
|
|
|
|
const match = useMatch( location.pathname );
|
|
|
|
const params = useParams();
|
|
|
|
const matchProp = { params, url: match.pathname };
|
|
|
|
return Children.toArray( children ).map( ( child ) => {
|
|
|
|
return cloneElement( child, {
|
|
|
|
...child.props,
|
|
|
|
location,
|
|
|
|
match: matchProp,
|
|
|
|
} );
|
|
|
|
} );
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Wraps _Layout with WithReactRouterProps for non-embedded page renders
|
|
|
|
* We need this because the hooks fail for embedded page renders as there is no Router context above it.
|
|
|
|
*
|
|
|
|
* @param {Object} props React component props
|
|
|
|
*/
|
|
|
|
const LayoutSwitchWrapper = ( props ) => {
|
|
|
|
if ( props.isEmbedded ) {
|
|
|
|
return <_Layout { ...props } />;
|
|
|
|
}
|
|
|
|
return (
|
|
|
|
<WithReactRouterProps>
|
|
|
|
<_Layout { ...props } />
|
|
|
|
</WithReactRouterProps>
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
2023-05-18 08:25:36 +00:00
|
|
|
function _Layout( {
|
|
|
|
activePlugins,
|
|
|
|
installedPlugins,
|
|
|
|
isEmbedded,
|
|
|
|
isJetpackConnected,
|
|
|
|
location,
|
|
|
|
match,
|
|
|
|
page,
|
|
|
|
} ) {
|
|
|
|
usePageClasses( page );
|
|
|
|
|
|
|
|
function recordPageViewTrack() {
|
2020-10-22 22:13:57 +00:00
|
|
|
const navigationFlag = {
|
|
|
|
has_navigation: !! window.wcNavigation,
|
|
|
|
};
|
|
|
|
|
2019-11-15 13:32:02 +00:00
|
|
|
if ( isEmbedded ) {
|
|
|
|
const path = document.location.pathname + document.location.search;
|
2020-10-22 22:13:57 +00:00
|
|
|
recordPageView( path, {
|
|
|
|
is_embedded: true,
|
|
|
|
...navigationFlag,
|
|
|
|
} );
|
2019-11-15 13:32:02 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-05-18 08:25:36 +00:00
|
|
|
const { pathname } = location;
|
2018-09-19 18:23:05 +00:00
|
|
|
if ( ! pathname ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remove leading slash, and camel case remaining pathname
|
|
|
|
let path = pathname.substring( 1 ).replace( /\//g, '_' );
|
|
|
|
|
2020-09-14 23:44:46 +00:00
|
|
|
// When pathname is `/` we are on the home screen.
|
2018-09-19 18:23:05 +00:00
|
|
|
if ( path.length === 0 ) {
|
2020-09-14 23:44:46 +00:00
|
|
|
path = 'home_screen';
|
2018-09-19 18:23:05 +00:00
|
|
|
}
|
|
|
|
|
2020-04-27 14:41:26 +00:00
|
|
|
recordPageView( path, {
|
|
|
|
jetpack_installed: installedPlugins.includes( 'jetpack' ),
|
|
|
|
jetpack_active: activePlugins.includes( 'jetpack' ),
|
|
|
|
jetpack_connected: isJetpackConnected,
|
2020-10-22 22:13:57 +00:00
|
|
|
...navigationFlag,
|
2020-04-27 14:41:26 +00:00
|
|
|
} );
|
2018-09-19 18:23:05 +00:00
|
|
|
}
|
|
|
|
|
2023-05-18 08:25:36 +00:00
|
|
|
useEffect( () => {
|
|
|
|
triggerExitPageCesSurvey();
|
|
|
|
}, [] );
|
|
|
|
|
|
|
|
useEffect( () => {
|
|
|
|
recordPageViewTrack();
|
|
|
|
setTimeout( () => {
|
|
|
|
triggerExitPageCesSurvey();
|
|
|
|
}, 0 );
|
|
|
|
}, [ location?.pathname ] );
|
|
|
|
|
|
|
|
function isWCPaySettingsPage() {
|
|
|
|
const { page: queryPage, section, tab } = getQuery();
|
2020-11-26 00:27:37 +00:00
|
|
|
return (
|
2023-05-18 08:25:36 +00:00
|
|
|
queryPage === 'wc-settings' &&
|
2020-11-26 00:27:37 +00:00
|
|
|
tab === 'checkout' &&
|
|
|
|
section === 'woocommerce_payments'
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2023-05-18 16:00:42 +00:00
|
|
|
const { breadcrumbs, layout = { header: true, footer: true } } = page;
|
|
|
|
const { header: showHeader = true, footer: showFooter = true } = layout;
|
2020-03-13 04:34:53 +00:00
|
|
|
|
2023-05-18 08:25:36 +00:00
|
|
|
const query = Object.fromEntries(
|
|
|
|
new URLSearchParams( location && location.search )
|
|
|
|
);
|
2021-04-16 13:29:54 +00:00
|
|
|
|
2023-05-18 08:25:36 +00:00
|
|
|
return (
|
|
|
|
<LayoutContextProvider
|
|
|
|
value={ getLayoutContextValue( [
|
|
|
|
page?.navArgs?.id?.toLowerCase() || 'page',
|
|
|
|
] ) }
|
|
|
|
>
|
|
|
|
<SlotFillProvider>
|
|
|
|
<div className="woocommerce-layout">
|
2023-05-18 16:00:42 +00:00
|
|
|
{ showHeader && (
|
|
|
|
<Header
|
|
|
|
sections={
|
|
|
|
isFunction( breadcrumbs )
|
|
|
|
? breadcrumbs( { match } )
|
|
|
|
: breadcrumbs
|
|
|
|
}
|
|
|
|
isEmbedded={ isEmbedded }
|
|
|
|
query={ query }
|
|
|
|
/>
|
|
|
|
) }
|
2023-05-18 08:25:36 +00:00
|
|
|
<TransientNotices />
|
|
|
|
{ ! isEmbedded && (
|
|
|
|
<PrimaryLayout>
|
|
|
|
<div className="woocommerce-layout__main">
|
|
|
|
<Controller
|
|
|
|
page={ page }
|
|
|
|
match={ match }
|
|
|
|
query={ query }
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
</PrimaryLayout>
|
2021-04-16 13:29:54 +00:00
|
|
|
) }
|
2023-05-18 08:25:36 +00:00
|
|
|
|
|
|
|
{ isEmbedded && isWCPaySettingsPage() && (
|
|
|
|
<Suspense fallback={ null }>
|
|
|
|
<WCPayUsageModal />
|
|
|
|
</Suspense>
|
|
|
|
) }
|
2023-05-18 16:00:42 +00:00
|
|
|
{ showFooter && <Footer /> }
|
2023-05-18 08:25:36 +00:00
|
|
|
<CustomerEffortScoreModalContainer />
|
|
|
|
</div>
|
|
|
|
<PluginArea scope="woocommerce-admin" />
|
|
|
|
{ window.wcAdminFeatures.navigation && (
|
|
|
|
<PluginArea scope="woocommerce-navigation" />
|
|
|
|
) }
|
|
|
|
<PluginArea scope="woocommerce-tasks" />
|
|
|
|
</SlotFillProvider>
|
|
|
|
</LayoutContextProvider>
|
|
|
|
);
|
2018-05-18 17:31:08 +00:00
|
|
|
}
|
|
|
|
|
2020-04-27 14:41:26 +00:00
|
|
|
_Layout.propTypes = {
|
2019-05-22 17:53:30 +00:00
|
|
|
isEmbedded: PropTypes.bool,
|
2019-07-05 08:15:49 +00:00
|
|
|
page: PropTypes.shape( {
|
2020-04-29 18:01:27 +00:00
|
|
|
container: PropTypes.oneOfType( [
|
|
|
|
PropTypes.func,
|
|
|
|
PropTypes.object, // Support React.lazy
|
|
|
|
] ),
|
2019-07-08 00:54:52 +00:00
|
|
|
path: PropTypes.string,
|
2019-07-05 08:15:49 +00:00
|
|
|
breadcrumbs: PropTypes.oneOfType( [
|
|
|
|
PropTypes.func,
|
|
|
|
PropTypes.arrayOf(
|
2020-02-14 02:23:21 +00:00
|
|
|
PropTypes.oneOfType( [
|
|
|
|
PropTypes.arrayOf( PropTypes.string ),
|
|
|
|
PropTypes.string,
|
|
|
|
] )
|
2019-07-05 08:15:49 +00:00
|
|
|
),
|
|
|
|
] ).isRequired,
|
2019-07-08 00:54:52 +00:00
|
|
|
wpOpenMenu: PropTypes.string,
|
2019-07-05 08:15:49 +00:00
|
|
|
} ).isRequired,
|
2018-06-26 14:49:42 +00:00
|
|
|
};
|
|
|
|
|
2022-01-06 12:53:30 +00:00
|
|
|
const dataEndpoints = getAdminSetting( 'dataEndpoints' );
|
2020-04-27 14:41:26 +00:00
|
|
|
const Layout = compose(
|
|
|
|
withPluginsHydration( {
|
2022-01-06 12:53:30 +00:00
|
|
|
...getAdminSetting( 'plugins', {} ),
|
2020-04-27 14:41:26 +00:00
|
|
|
jetpackStatus:
|
2021-08-24 11:39:48 +00:00
|
|
|
( dataEndpoints && dataEndpoints.jetpackStatus ) || false,
|
2020-04-27 14:41:26 +00:00
|
|
|
} ),
|
|
|
|
withSelect( ( select, { isEmbedded } ) => {
|
|
|
|
// Embedded pages don't send plugin info to Tracks.
|
|
|
|
if ( isEmbedded ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-06-21 08:37:34 +00:00
|
|
|
const { getActivePlugins, getInstalledPlugins, isJetpackConnected } =
|
|
|
|
select( PLUGINS_STORE_NAME );
|
2020-04-27 14:41:26 +00:00
|
|
|
|
|
|
|
return {
|
|
|
|
activePlugins: getActivePlugins(),
|
|
|
|
isJetpackConnected: isJetpackConnected(),
|
|
|
|
installedPlugins: getInstalledPlugins(),
|
|
|
|
};
|
|
|
|
} )
|
2022-05-30 06:51:33 +00:00
|
|
|
)( LayoutSwitchWrapper );
|
2020-04-27 14:41:26 +00:00
|
|
|
|
2021-02-19 13:57:17 +00:00
|
|
|
const _PageLayout = () => {
|
|
|
|
const { currentUserCan } = useUser();
|
|
|
|
|
2022-05-30 06:51:33 +00:00
|
|
|
// get the basename, usually 'wp-admin/' but can be something else if the site installation changed it
|
|
|
|
const path = document.location.pathname;
|
|
|
|
const basename = path.substring( 0, path.lastIndexOf( '/' ) );
|
|
|
|
|
2021-02-19 13:57:17 +00:00
|
|
|
return (
|
2022-05-30 06:51:33 +00:00
|
|
|
<HistoryRouter history={ getHistory() }>
|
|
|
|
<Routes basename={ basename }>
|
2021-02-19 13:57:17 +00:00
|
|
|
{ getPages()
|
|
|
|
.filter(
|
|
|
|
( page ) =>
|
|
|
|
! page.capability ||
|
|
|
|
currentUserCan( page.capability )
|
|
|
|
)
|
|
|
|
.map( ( page ) => {
|
2019-06-13 19:58:21 +00:00
|
|
|
return (
|
|
|
|
<Route
|
|
|
|
key={ page.path }
|
|
|
|
path={ page.path }
|
|
|
|
exact
|
2022-05-30 06:51:33 +00:00
|
|
|
element={ <Layout page={ page } /> }
|
2019-06-13 19:58:21 +00:00
|
|
|
/>
|
|
|
|
);
|
2018-05-18 17:31:08 +00:00
|
|
|
} ) }
|
2022-05-30 06:51:33 +00:00
|
|
|
</Routes>
|
|
|
|
</HistoryRouter>
|
2021-02-19 13:57:17 +00:00
|
|
|
);
|
|
|
|
};
|
2020-06-03 20:42:30 +00:00
|
|
|
|
|
|
|
export const PageLayout = compose(
|
2021-08-24 11:39:48 +00:00
|
|
|
window.wcSettings.admin
|
2020-06-10 23:49:27 +00:00
|
|
|
? withOptionsHydration( {
|
2022-01-06 12:53:30 +00:00
|
|
|
...getAdminSetting( 'preloadOptions', {} ),
|
2020-06-10 23:49:27 +00:00
|
|
|
} )
|
2020-06-11 00:26:20 +00:00
|
|
|
: identity
|
2020-06-03 20:42:30 +00:00
|
|
|
)( _PageLayout );
|
2018-06-26 14:49:42 +00:00
|
|
|
|
2020-10-13 00:05:06 +00:00
|
|
|
const _EmbedLayout = () => (
|
|
|
|
<Layout
|
|
|
|
page={ {
|
2022-01-06 12:53:30 +00:00
|
|
|
breadcrumbs: getAdminSetting( 'embedBreadcrumbs', [] ),
|
2020-10-13 00:05:06 +00:00
|
|
|
} }
|
|
|
|
isEmbedded
|
|
|
|
/>
|
|
|
|
);
|
|
|
|
|
|
|
|
export const EmbedLayout = compose(
|
2022-01-06 12:53:30 +00:00
|
|
|
getAdminSetting( 'preloadOptions' )
|
2020-10-13 00:05:06 +00:00
|
|
|
? withOptionsHydration( {
|
2022-01-06 12:53:30 +00:00
|
|
|
...getAdminSetting( 'preloadOptions' ),
|
2020-10-13 00:05:06 +00:00
|
|
|
} )
|
|
|
|
: identity
|
|
|
|
)( _EmbedLayout );
|