add ad jobs/overview/dfa/trained models/settings/supplied configs to management

This commit is contained in:
Melissa 2024-12-10 09:49:33 -07:00
parent 817063d1aa
commit b75d960d19
35 changed files with 601 additions and 920 deletions

View file

@ -43,6 +43,7 @@ interface AppProps {
mlFeatures: MlFeatures;
experimentalFeatures: ExperimentalFeatures;
nlpSettings: NLPSettings;
entryPoint?: string; // This will need to be defined as finite set of possible ids - maybe the id used to register the app in the management section
}
const localStorage = new Storage(window.localStorage);
@ -53,7 +54,7 @@ export interface MlServicesContext {
export type MlGlobalServices = ReturnType<typeof getMlGlobalServices>;
const App: FC<AppProps> = ({
export const App: FC<AppProps> = ({
coreStart,
deps,
appMountParams,
@ -61,6 +62,7 @@ const App: FC<AppProps> = ({
mlFeatures,
experimentalFeatures,
nlpSettings,
entryPoint, // TODO: might need to update this naming
}) => {
const pageDeps: PageDependencies = {
history: appMountParams.history,
@ -145,7 +147,7 @@ const App: FC<AppProps> = ({
experimentalFeatures={experimentalFeatures}
>
<MlServerInfoContextProvider nlpSettings={nlpSettings}>
<MlRouter pageDeps={pageDeps} />
<MlRouter pageDeps={pageDeps} entryPoint={entryPoint} />
</MlServerInfoContextProvider>
</EnabledFeaturesContextProvider>
</DatePickerContextProvider>

View file

@ -10,16 +10,22 @@ import PropTypes from 'prop-types';
import { EuiIcon, EuiFlexItem } from '@elastic/eui';
import { LinkCard } from '../link_card';
import { useMlKibana } from '../../contexts/kibana';
import { useMlManagementLocator } from '../../contexts/kibana';
import { ML_PAGES } from '../../../../common/constants/locator';
export const RecognizedResult = ({ config, indexPattern, savedSearch }) => {
const {
services: {
http: { basePath },
},
} = useMlKibana();
const id = savedSearch === null ? `index=${indexPattern.id}` : `savedSearchId=${savedSearch.id}`;
const href = `${basePath.get()}/app/ml/jobs/new_job/recognize?id=${config.id}&${id}`;
const mlManagementLocator = useMlManagementLocator();
const navigateToCreateJobRecognizerPath = async () => {
if (!mlManagementLocator) return;
await mlManagementLocator.navigate({
sectionId: 'ml',
appId: `anomaly_detection/${ML_PAGES.ANOMALY_DETECTION_CREATE_JOB_RECOGNIZER}?id=${config.id}&${id}`,
});
};
let logo = null;
// if a logo is available, use that, otherwise display the id
@ -36,7 +42,7 @@ export const RecognizedResult = ({ config, indexPattern, savedSearch }) => {
<EuiFlexItem>
<LinkCard
data-test-subj={`mlRecognizerCard ${config.id}`}
href={href}
onClick={navigateToCreateJobRecognizerPath}
title={config.title}
description={config.description}
icon={logo}

View file

@ -11,7 +11,7 @@ import { createHtmlPortalNode, type HtmlPortalNode } from 'react-reverse-portal'
import { Redirect } from 'react-router-dom';
import { Routes, Route } from '@kbn/shared-ux-router';
import { Subscription } from 'rxjs';
import { EuiPageSection } from '@elastic/eui';
import { EuiPageSection, EuiPageHeader } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { type AppMountParameters } from '@kbn/core/public';
@ -20,6 +20,12 @@ import { RedirectAppLinks } from '@kbn/shared-ux-link-redirect-app';
import { DatePickerWrapper } from '@kbn/ml-date-picker';
import * as routes from '../../routing/routes';
import * as overviewRoutes from '../../routing/routes/overview';
import * as anomalyDetectionRoutes from '../../routing/routes/anomaly_detection_management';
import * as dfaRoutes from '../../routing/routes/data_frame_analytics';
import * as suppliedConfigsRoutes from '../../routing/routes/supplied_configurations';
import * as settingsRoutes from '../../routing/routes/settings';
import * as trainedModelsRoutes from '../../routing/routes/trained_models';
import { MlPageWrapper } from '../../routing/ml_page_wrapper';
import { useMlKibana, useNavigateToPath } from '../../contexts/kibana';
import type { MlRoute, PageDependencies } from '../../routing/router';
@ -49,152 +55,198 @@ export const MlPageControlsContext = createContext<{
* Main page component of the ML App
* @constructor
*/
export const MlPage: FC<{ pageDeps: PageDependencies }> = React.memo(({ pageDeps }) => {
const navigateToPath = useNavigateToPath();
const {
services: {
http: { basePath },
mlServices: { httpService },
},
} = useMlKibana();
const { showMLNavMenu } = useEnabledFeatures();
export const MlPage: FC<{ pageDeps: PageDependencies; entryPoint?: string }> = React.memo(
({ pageDeps, entryPoint }) => {
const navigateToPath = useNavigateToPath();
const {
services: {
http: { basePath },
mlServices: { httpService },
},
} = useMlKibana();
const { showMLNavMenu } = useEnabledFeatures();
const headerPortalNode = useMemo(() => createHtmlPortalNode(), []);
const [isHeaderMounted, setIsHeaderMounted] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const headerPortalNode = useMemo(() => createHtmlPortalNode(), []);
const [isHeaderMounted, setIsHeaderMounted] = useState(false);
const [isLoading, setIsLoading] = useState(false);
useEffect(() => {
const subscriptions = new Subscription();
useEffect(() => {
const subscriptions = new Subscription();
subscriptions.add(
httpService.getLoadingCount$.subscribe((v) => {
setIsLoading(v !== 0);
})
);
return function cleanup() {
subscriptions.unsubscribe();
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
subscriptions.add(
httpService.getLoadingCount$.subscribe((v) => {
setIsLoading(v !== 0);
})
);
return function cleanup() {
subscriptions.unsubscribe();
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const routeList = useMemo(
() =>
Object.values(routes)
.map((routeFactory) => routeFactory(navigateToPath, basePath.get()))
.filter((d) => !d.disabled),
// eslint-disable-next-line react-hooks/exhaustive-deps
[]
);
const routeList = useMemo(
() => {
let currentRoutes;
const activeRoute = useActiveRoute(routeList);
const rightSideItems = useMemo(() => {
return [
...(activeRoute.enableDatePicker
? [<DatePickerWrapper isLoading={isLoading} width="full" />]
: []),
];
}, [activeRoute.enableDatePicker, isLoading]);
useDocTitle(activeRoute);
// The deprecated `KibanaPageTemplate` from`'@kbn/kibana-react-plugin/public'`
// had a `pageBodyProps` prop where we could pass in the `data-test-subj` for
// the `main` element. This is no longer available in the update template
// imported from `'@kbn/shared-ux-page-kibana-template'`. The following is a
// workaround to add the `data-test-subj` on the `main` element again.
useEffect(() => {
const mlApp = document.querySelector(ML_APP_SELECTOR) as HTMLElement;
if (mlApp && typeof activeRoute?.['data-test-subj'] === 'string') {
const mlAppMain = mlApp.querySelector('main') as HTMLElement;
if (mlAppMain) {
mlAppMain.setAttribute('data-test-subj', activeRoute?.['data-test-subj']);
}
}
}, [activeRoute]);
const sideNavItems = useSideNavItems(activeRoute);
return (
<MlPageControlsContext.Provider
value={{
setHeaderActionMenu: pageDeps.setHeaderActionMenu,
headerPortal: headerPortalNode,
setIsHeaderMounted,
isHeaderMounted,
}}
>
<KibanaPageTemplate
className={'ml-app'}
data-test-subj={'mlApp'}
restrictWidth={false}
panelled
solutionNav={
showMLNavMenu
? {
name: i18n.translate('xpack.ml.plugin.title', {
defaultMessage: 'Machine Learning',
}),
icon: 'machineLearningApp',
items: sideNavItems,
}
: undefined
switch (entryPoint) {
case 'overview':
currentRoutes = overviewRoutes;
break;
case 'anomaly_detection':
currentRoutes = anomalyDetectionRoutes;
break;
case 'analytics':
currentRoutes = dfaRoutes;
break;
case 'trained_models':
currentRoutes = trainedModelsRoutes;
break;
case 'supplied_configurations':
currentRoutes = suppliedConfigsRoutes;
break;
case 'settings':
currentRoutes = settingsRoutes;
break;
default:
currentRoutes = routes;
}
pageHeader={{
pageTitle: <MlPageHeaderRenderer />,
rightSideItems,
restrictWidth: false,
return Object.values(currentRoutes)
.map((routeFactory) => routeFactory(navigateToPath, basePath.get()))
.filter((d) => !d.disabled);
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[entryPoint]
);
const activeRoute = useActiveRoute(routeList);
const rightSideItems = useMemo(() => {
return [
...(activeRoute.enableDatePicker
? [<DatePickerWrapper isLoading={isLoading} width="full" />]
: []),
];
}, [activeRoute.enableDatePicker, isLoading]);
useDocTitle(activeRoute);
// The deprecated `KibanaPageTemplate` from`'@kbn/kibana-react-plugin/public'`
// had a `pageBodyProps` prop where we could pass in the `data-test-subj` for
// the `main` element. This is no longer available in the update template
// imported from `'@kbn/shared-ux-page-kibana-template'`. The following is a
// workaround to add the `data-test-subj` on the `main` element again.
useEffect(() => {
const mlApp = document.querySelector(ML_APP_SELECTOR) as HTMLElement;
if (mlApp && typeof activeRoute?.['data-test-subj'] === 'string') {
const mlAppMain = mlApp.querySelector('main') as HTMLElement;
if (mlAppMain) {
mlAppMain.setAttribute('data-test-subj', activeRoute?.['data-test-subj']);
}
}
}, [activeRoute]);
const sideNavItems = useSideNavItems(activeRoute);
return (
<MlPageControlsContext.Provider
value={{
setHeaderActionMenu: pageDeps.setHeaderActionMenu,
headerPortal: headerPortalNode,
setIsHeaderMounted,
isHeaderMounted,
}}
>
<CommonPageWrapper
headerPortal={headerPortalNode}
setIsHeaderMounted={setIsHeaderMounted}
pageDeps={pageDeps}
routeList={routeList}
/>
</KibanaPageTemplate>
</MlPageControlsContext.Provider>
);
});
{entryPoint === undefined ? (
<KibanaPageTemplate
className={'ml-app'}
data-test-subj={'mlApp'}
restrictWidth={false}
panelled
solutionNav={
showMLNavMenu
? {
name: i18n.translate('xpack.ml.plugin.title', {
defaultMessage: 'Machine Learning',
}),
icon: 'machineLearningApp',
items: sideNavItems,
}
: undefined
}
pageHeader={{
pageTitle: <MlPageHeaderRenderer />,
rightSideItems,
restrictWidth: false,
}}
>
<CommonPageWrapper
headerPortal={headerPortalNode}
setIsHeaderMounted={setIsHeaderMounted}
pageDeps={pageDeps}
routeList={routeList}
/>
</KibanaPageTemplate>
) : (
<>
<EuiPageHeader pageTitle={<MlPageHeaderRenderer />} rightSideItems={rightSideItems} />
<CommonPageWrapper
headerPortal={headerPortalNode}
setIsHeaderMounted={setIsHeaderMounted}
pageDeps={pageDeps}
routeList={routeList}
entryPoint={entryPoint}
/>
</>
)}
</MlPageControlsContext.Provider>
);
}
);
interface CommonPageWrapperProps {
setIsHeaderMounted: (v: boolean) => void;
pageDeps: PageDependencies;
routeList: MlRoute[];
headerPortal: HtmlPortalNode;
entryPoint?: string;
}
const CommonPageWrapper: FC<CommonPageWrapperProps> = React.memo(({ pageDeps, routeList }) => {
const {
services: { application },
} = useMlKibana();
const CommonPageWrapper: FC<CommonPageWrapperProps> = React.memo(
({ pageDeps, routeList, entryPoint }) => {
const {
services: { application },
} = useMlKibana();
return (
/** RedirectAppLinks intercepts all <a> tags to use navigateToUrl
* avoiding full page reload **/
<RedirectAppLinks coreStart={{ application }}>
<EuiPageSection restrictWidth={false}>
<Routes>
{routeList.map((route) => {
return (
<Route
key={route.path}
path={route.path}
exact
render={(props) => {
window.setTimeout(() => {
pageDeps.setBreadcrumbs(route.breadcrumbs);
});
return (
<MlPageWrapper path={route.path}>{route.render(props, pageDeps)}</MlPageWrapper>
);
}}
/>
);
})}
<Redirect to="/overview" />
</Routes>
</EuiPageSection>
</RedirectAppLinks>
);
});
return (
/** RedirectAppLinks intercepts all <a> tags to use navigateToUrl
* avoiding full page reload **/
<RedirectAppLinks coreStart={{ application }}>
<EuiPageSection restrictWidth={false}>
<Routes>
{routeList.map((route) => {
return (
<Route
key={route.path}
path={route.path}
exact
render={(props) => {
window.setTimeout(() => {
pageDeps.setBreadcrumbs(route.breadcrumbs);
});
return (
<MlPageWrapper path={route.path}>
{route.render(props, pageDeps)}
</MlPageWrapper>
);
}}
/>
);
})}
<Redirect to="/" />
</Routes>
</EuiPageSection>
</RedirectAppLinks>
);
}
);

View file

@ -124,15 +124,6 @@ export function useSideNavItems(activeRoute: MlRoute | undefined) {
}),
disabled: disableLinks,
items: [
{
id: 'anomaly_detection',
name: i18n.translate('xpack.ml.navMenu.anomalyDetection.jobsManagementText', {
defaultMessage: 'Jobs',
}),
disabled: disableLinks,
pathId: ML_PAGES.ANOMALY_DETECTION_JOBS_MANAGE,
testSubj: 'mlMainTab anomalyDetection',
},
{
id: 'anomaly_explorer',
name: i18n.translate('xpack.ml.navMenu.anomalyDetection.anomalyExplorerText', {
@ -151,28 +142,6 @@ export function useSideNavItems(activeRoute: MlRoute | undefined) {
disabled: disableLinks,
testSubj: 'mlMainTab singleMetricViewer',
},
{
id: 'settings',
pathId: ML_PAGES.SETTINGS,
name: i18n.translate('xpack.ml.navMenu.settingsTabLinkText', {
defaultMessage: 'Settings',
}),
disabled: disableLinks,
testSubj: 'mlMainTab settings',
highlightNestedRoutes: true,
},
{
id: 'supplied_cofigurations',
name: i18n.translate(
'xpack.ml.navMenu.anomalyDetection.suppliedConfigurationsLinkText',
{
defaultMessage: 'Supplied Configurations',
}
),
disabled: disableLinks,
pathId: ML_PAGES.SUPPLIED_CONFIGURATIONS,
testSubj: 'mlMainTab suppliedConfigurations',
},
],
},
{
@ -182,15 +151,6 @@ export function useSideNavItems(activeRoute: MlRoute | undefined) {
}),
disabled: disableLinks,
items: [
{
id: 'data_frame_analytics_jobs',
pathId: ML_PAGES.DATA_FRAME_ANALYTICS_JOBS_MANAGE,
name: i18n.translate('xpack.ml.navMenu.dataFrameAnalytics.jobsManagementText', {
defaultMessage: 'Jobs',
}),
disabled: disableLinks,
testSubj: 'mlMainTab dataFrameAnalytics',
},
{
id: 'data_frame_analytics_results_explorer',
pathId: ML_PAGES.DATA_FRAME_ANALYTICS_EXPLORATION,
@ -211,24 +171,6 @@ export function useSideNavItems(activeRoute: MlRoute | undefined) {
},
],
},
{
id: 'model_management',
name: i18n.translate('xpack.ml.navMenu.modelManagementText', {
defaultMessage: 'Model Management',
}),
disabled: disableLinks,
items: [
{
id: 'trained_models',
pathId: ML_PAGES.TRAINED_MODELS_MANAGE,
name: i18n.translate('xpack.ml.navMenu.trainedModelsText', {
defaultMessage: 'Trained Models',
}),
disabled: disableLinks,
testSubj: 'mlMainTab trainedModels',
},
],
},
];
mlTabs.push({

View file

@ -11,7 +11,7 @@ export type { NavigateToPath } from './use_navigate_to_path';
export { useNavigateToPath } from './use_navigate_to_path';
export { useUiSettings } from './use_ui_settings_context';
export { useNotifications } from './use_notifications_context';
export { useMlLocator, useMlLink } from './use_create_url';
export { useMlLocator, useMlLink, useMlManagementLocator } from './use_create_url';
export { useMlApi } from './use_ml_api_context';
export { useFieldFormatter } from './use_field_formatter';
export { useMlLicenseInfo } from './use_ml_license';

View file

@ -8,10 +8,19 @@
import { useCallback, useEffect, useState } from 'react';
import type { LocatorGetUrlParams } from '@kbn/share-plugin/common/url_service';
import { useUrlState } from '@kbn/ml-url-state';
import { MANAGEMENT_APP_LOCATOR } from '@kbn/deeplinks-management/constants';
import { useMlKibana } from './kibana_context';
import { ML_APP_LOCATOR } from '../../../../common/constants/locator';
import type { MlLocatorParams } from '../../../../common/types/locator';
export const useMlManagementLocator = () => {
const {
services: { share },
} = useMlKibana();
return share.url.locators.get(MANAGEMENT_APP_LOCATOR);
};
export const useMlLocator = () => {
const {
services: { share },

View file

@ -12,7 +12,7 @@ import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';
import dfaImage from './data_frame_analytics_kibana.png';
import { mlNodesAvailable } from '../../../../../ml_nodes_check';
import { useMlKibana, useNavigateToPath } from '../../../../../contexts/kibana';
import { useMlKibana, useMlManagementLocator } from '../../../../../contexts/kibana';
import { ML_PAGES } from '../../../../../../../common/constants/locator';
import { usePermissionCheck } from '../../../../../capabilities/check_capabilities';
@ -21,6 +21,8 @@ export const AnalyticsEmptyPrompt: FC = () => {
services: { docLinks },
} = useMlKibana();
const mlLocator = useMlManagementLocator();
const [canCreateDataFrameAnalytics, canStartStopDataFrameAnalytics] = usePermissionCheck([
'canCreateDataFrameAnalytics',
'canStartStopDataFrameAnalytics',
@ -29,10 +31,13 @@ export const AnalyticsEmptyPrompt: FC = () => {
const disabled =
!mlNodesAvailable() || !canCreateDataFrameAnalytics || !canStartStopDataFrameAnalytics;
const navigateToPath = useNavigateToPath();
const navigateToSourceSelection = async () => {
await navigateToPath(ML_PAGES.DATA_FRAME_ANALYTICS_SOURCE_SELECTION);
if (!mlLocator) return;
await mlLocator.navigate({
sectionId: 'ml',
appId: `analytics/${ML_PAGES.DATA_FRAME_ANALYTICS_SOURCE_SELECTION}`,
});
};
return (

View file

@ -16,7 +16,7 @@ import { useRefreshInterval } from './components/analytics_list/use_refresh_inte
import { NodeAvailableWarning } from '../../../components/node_available_warning';
import { SavedObjectsWarning } from '../../../components/saved_objects_warning';
import { UpgradeWarning } from '../../../components/upgrade';
import { JobMap } from '../job_map';
// import { JobMap } from '../job_map';
import { DataFrameAnalyticsListColumn } from './components/analytics_list/common';
import { ML_PAGES } from '../../../../../common/constants/locator';
import { HelpMenu } from '../../../components/help_menu';
@ -71,16 +71,16 @@ export const Page: FC = () => {
<SavedObjectsWarning onCloseFlyout={refresh} forceRefresh={isLoading} />
<UpgradeWarning />
{selectedTabId === 'map' && (mapJobId || mapModelId) && (
{/* {selectedTabId === 'map' && (mapJobId || mapModelId) && (
<JobMap analyticsId={mapJobId} modelId={mapModelId} />
)}
{selectedTabId === 'data_frame_analytics' && (
<DataFrameAnalyticsList
blockRefresh={blockRefresh}
pageState={dfaPageState}
updatePageState={setDfaPageState}
/>
)}
)} */}
{/* {selectedTabId === 'data_frame_analytics' && ( */}
<DataFrameAnalyticsList
blockRefresh={blockRefresh}
pageState={dfaPageState}
updatePageState={setDfaPageState}
/>
{/* )} */}
<HelpMenu docLink={helpLink} />
</>
);

View file

@ -11,7 +11,7 @@ import { FormattedMessage } from '@kbn/i18n-react';
import { EuiButton, EuiEmptyPrompt, EuiImage, EuiLink } from '@elastic/eui';
import adImage from './anomaly_detection_kibana.png';
import { ML_PAGES } from '../../../../../../common/constants/locator';
import { useMlKibana, useMlLocator, useNavigateToPath } from '../../../../contexts/kibana';
import { useMlKibana, useMlManagementLocator } from '../../../../contexts/kibana';
import { usePermissionCheck } from '../../../../capabilities/check_capabilities';
import { mlNodesAvailable } from '../../../../ml_nodes_check';
@ -23,15 +23,15 @@ export const AnomalyDetectionEmptyState: FC = () => {
services: { docLinks },
} = useMlKibana();
const mlLocator = useMlLocator();
const navigateToPath = useNavigateToPath();
const mlLocator = useMlManagementLocator();
const redirectToCreateJobSelectIndexPage = async () => {
if (!mlLocator) return;
const path = await mlLocator.getUrl({
page: ML_PAGES.ANOMALY_DETECTION_CREATE_JOB_SELECT_INDEX,
await mlLocator.navigate({
sectionId: 'ml',
appId: `anomaly_detection/${ML_PAGES.ANOMALY_DETECTION_CREATE_JOB_SELECT_INDEX}`,
});
await navigateToPath(path, true);
};
return (

View file

@ -15,6 +15,7 @@ import { JobsListView } from './components/jobs_list_view';
import { ML_PAGES } from '../../../../common/constants/locator';
import { HelpMenu } from '../../components/help_menu';
import { useMlKibana } from '../../contexts/kibana';
import { MlPageHeader } from '../../components/page_header';
import { HeaderMenuPortal } from '../../components/header_menu_portal';
import { JobsActionMenu } from '../components/jobs_action_menu';
import { useEnabledFeatures } from '../../contexts/ml';
@ -55,6 +56,9 @@ export const JobsPage: FC<JobsPageProps> = ({ isMlEnabledInSpace, lastRefresh })
const helpLink = docLinks.links.ml.anomalyDetection;
return (
<>
<MlPageHeader>
<FormattedMessage id="xpack.ml.jobsList.title" defaultMessage="Anomaly Detection Jobs" />
</MlPageHeader>
<HeaderMenuPortal>
<JobsActionMenu />
</HeaderMenuPortal>

View file

@ -6,14 +6,14 @@
*/
import type { FC } from 'react';
import React, { useCallback } from 'react';
import React from 'react';
import { EuiFlexGroup, EuiPageBody, EuiPanel } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';
import { SavedObjectFinder } from '@kbn/saved-objects-finder-plugin/public';
import type { FinderAttributes, SavedObjectCommon } from '@kbn/saved-objects-finder-plugin/common';
import { CreateDataViewButton } from '../../../../components/create_data_view_button';
import { useMlKibana, useNavigateToPath } from '../../../../contexts/kibana';
import { useMlKibana, useMlManagementLocator } from '../../../../contexts/kibana';
import { MlPageHeader } from '../../../../components/page_header';
export interface PageProps {
@ -32,18 +32,16 @@ export const Page: FC<PageProps> = ({
extraButtons?: React.ReactNode;
}) => {
const { contentManagement, uiSettings } = useMlKibana().services;
const navigateToPath = useNavigateToPath();
const mlLocator = useMlManagementLocator();
const onObjectSelection = useCallback(
(id: string, type: string, name?: string) => {
navigateToPath(
`${nextStepPath}?${
type === 'index-pattern' ? 'index' : 'savedSearchId'
}=${encodeURIComponent(id)}`
);
},
[navigateToPath, nextStepPath]
);
const onObjectSelection = async (id: string, type: string, name?: string) => {
await mlLocator?.navigate({
sectionId: 'ml',
appId: `anomaly_detection/${nextStepPath}?${
type === 'index-pattern' ? 'index' : 'savedSearchId'
}=${encodeURIComponent(id)}`,
});
};
return (
<div data-test-subj="mlPageSourceSelection">

View file

@ -26,7 +26,7 @@ export async function preConfiguredJobRedirect(
try {
const redirectUrl = await getWizardUrlFromCloningJob(createdBy, dataViewId);
await navigateToUrl(`${basePath}/app/ml/${redirectUrl}`);
await navigateToUrl(`${basePath}/app/management/ml/${redirectUrl}`);
return Promise.reject();
} catch (error) {
return Promise.resolve();

View file

@ -19,7 +19,11 @@ import {
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n-react';
import { ES_FIELD_TYPES } from '@kbn/field-types';
import { useMlKibana, useNavigateToPath } from '../../../../contexts/kibana';
import {
useMlKibana,
useMlManagementLocator,
useNavigateToPath,
} from '../../../../contexts/kibana';
import { useDataSource } from '../../../../contexts/ml';
import { DataRecognizer } from '../../../../components/data_recognizer';
@ -53,6 +57,17 @@ export const Page: FC = () => {
const isTimeBasedIndex: boolean = selectedDataView.isTimeBased();
const mlManagementLocator = useMlManagementLocator();
const navigateToManagementPath = async (path: string) => {
if (!mlManagementLocator) return;
await mlManagementLocator.navigate({
sectionId: 'ml',
appId: `anomaly_detection${path}`,
});
};
useEffect(() => {
if (!isTimeBasedIndex) {
toasts.addWarning({
@ -140,12 +155,12 @@ export const Page: FC = () => {
dataVisualizerLink,
recentlyAccessed
);
navigateToPath(`/jobs/new_job/datavisualizer${getUrlParams()}`);
navigateToPath(`/jobs/new_job/datavisualizer${getUrlParams()}`); // TODO: does this one need to be updated?
};
const jobTypes = [
{
onClick: () => navigateToPath(`/jobs/new_job/single_metric${getUrlParams()}`),
onClick: () => navigateToManagementPath(`/jobs/new_job/single_metric${getUrlParams()}`),
icon: {
type: 'createSingleMetricJob',
ariaLabel: i18n.translate('xpack.ml.newJob.wizard.jobType.singleMetricAriaLabel', {
@ -161,7 +176,7 @@ export const Page: FC = () => {
id: 'mlJobTypeLinkSingleMetricJob',
},
{
onClick: () => navigateToPath(`/jobs/new_job/multi_metric${getUrlParams()}`),
onClick: () => navigateToManagementPath(`/jobs/new_job/multi_metric${getUrlParams()}`),
icon: {
type: 'createMultiMetricJob',
ariaLabel: i18n.translate('xpack.ml.newJob.wizard.jobType.multiMetricAriaLabel', {
@ -178,7 +193,7 @@ export const Page: FC = () => {
id: 'mlJobTypeLinkMultiMetricJob',
},
{
onClick: () => navigateToPath(`/jobs/new_job/population${getUrlParams()}`),
onClick: () => navigateToManagementPath(`/jobs/new_job/population${getUrlParams()}`),
icon: {
type: 'createPopulationJob',
ariaLabel: i18n.translate('xpack.ml.newJob.wizard.jobType.populationAriaLabel', {
@ -195,7 +210,7 @@ export const Page: FC = () => {
id: 'mlJobTypeLinkPopulationJob',
},
{
onClick: () => navigateToPath(`/jobs/new_job/advanced${getUrlParams()}`),
onClick: () => navigateToManagementPath(`/jobs/new_job/advanced${getUrlParams()}`),
icon: {
type: 'createAdvancedJob',
ariaLabel: i18n.translate('xpack.ml.newJob.wizard.jobType.advancedAriaLabel', {
@ -212,7 +227,7 @@ export const Page: FC = () => {
id: 'mlJobTypeLinkAdvancedJob',
},
{
onClick: () => navigateToPath(`/jobs/new_job/categorization${getUrlParams()}`),
onClick: () => navigateToManagementPath(`/jobs/new_job/categorization${getUrlParams()}`),
icon: {
type: CategorizationIcon,
ariaLabel: i18n.translate('xpack.ml.newJob.wizard.jobType.categorizationAriaLabel', {
@ -228,7 +243,7 @@ export const Page: FC = () => {
id: 'mlJobTypeLinkCategorizationJob',
},
{
onClick: () => navigateToPath(`/jobs/new_job/rare${getUrlParams()}`),
onClick: () => navigateToManagementPath(`/jobs/new_job/rare${getUrlParams()}`),
icon: {
type: RareIcon,
ariaLabel: i18n.translate('xpack.ml.newJob.wizard.jobType.rareAriaLabel', {
@ -247,7 +262,7 @@ export const Page: FC = () => {
if (hasGeoFields) {
jobTypes.push({
onClick: () => navigateToPath(`/jobs/new_job/geo${getUrlParams()}`),
onClick: () => navigateToManagementPath(`/jobs/new_job/geo${getUrlParams()}`),
icon: {
type: GeoIcon,
ariaLabel: i18n.translate('xpack.ml.newJob.wizard.jobType.geoAriaLabel', {

View file

@ -11,67 +11,98 @@ import type { CoreSetup } from '@kbn/core/public';
import type { ManagementSetup } from '@kbn/management-plugin/public';
import type { UsageCollectionSetup } from '@kbn/usage-collection-plugin/public';
import type { ManagementAppMountParams } from '@kbn/management-plugin/public';
import type { MlFeatures } from '../../../common/constants/app';
import type { MlFeatures, NLPSettings, ExperimentalFeatures } from '../../../common/constants/app';
import type { MlStartDependencies } from '../../plugin';
const managementSectionIds = {
overview: i18n.translate('xpack.ml.management.overviewTitle', {
defaultMessage: 'Overview',
}),
anomaly_detection: i18n.translate('xpack.ml.management.anomalyDetectionJobsTitle', {
defaultMessage: 'Anomaly Detection Jobs',
}),
analytics: i18n.translate('xpack.ml.management.dataFrameAnalyticsJobsTitle', {
defaultMessage: 'Data Frame Analytics Jobs',
}),
trained_models: i18n.translate('xpack.ml.management.trainedModelsTitle', {
defaultMessage: 'Trained Models',
}),
supplied_configurations: i18n.translate('xpack.ml.management.suppliedConfigurationsTitle', {
defaultMessage: 'Supplied Configurations',
}),
settings: i18n.translate('xpack.ml.management.settingsTitle', {
defaultMessage: 'Settings',
}),
};
export function registerManagementSections(
management: ManagementSetup,
core: CoreSetup<MlStartDependencies>,
deps: { usageCollection?: UsageCollectionSetup },
deps: { usageCollection?: UsageCollectionSetup }, // TODO: update type
isServerless: boolean,
mlFeatures: MlFeatures
mlFeatures: MlFeatures,
nlpSettings: NLPSettings,
experimentalFeatures: ExperimentalFeatures
) {
const overviewTitle = i18n.translate('xpack.ml.management.overviewTitle', {
defaultMessage: 'Overview',
Object.keys(managementSectionIds).forEach((sectionId) => {
const sectionTitle = managementSectionIds[sectionId];
management.sections.section.machineLearning
.registerApp({
id: sectionId,
title: sectionTitle,
order: 1,
async mount(params: ManagementAppMountParams) {
const [coreStart, pluginsStart] = await core.getStartServices();
const {
chrome: { docTitle },
} = coreStart;
docTitle.change(sectionTitle);
const mlDeps = {
cases: pluginsStart.cases,
charts: pluginsStart.charts,
contentManagement: pluginsStart.contentManagement,
dashboard: pluginsStart.dashboard,
data: pluginsStart.data,
dataViewEditor: pluginsStart.dataViewEditor,
dataVisualizer: pluginsStart.dataVisualizer,
// embeddable: { ...pluginsSetup.embeddable, ...pluginsStart.embeddable },
fieldFormats: pluginsStart.fieldFormats,
// kibanaVersion: this.initializerContext.env.packageInfo.version,
lens: pluginsStart.lens,
licensing: pluginsStart.licensing,
maps: pluginsStart.maps,
observabilityAIAssistant: pluginsStart.observabilityAIAssistant,
presentationUtil: pluginsStart.presentationUtil,
savedObjectsManagement: pluginsStart.savedObjectsManagement,
savedSearch: pluginsStart.savedSearch,
security: pluginsStart.security,
share: pluginsStart.share,
triggersActionsUi: pluginsStart.triggersActionsUi,
uiActions: pluginsStart.uiActions,
unifiedSearch: pluginsStart.unifiedSearch,
...deps,
};
const { mountApp } = await import('./mount_management_app');
const unmountAppCallback = await mountApp(
core,
params,
mlDeps,
isServerless,
mlFeatures,
experimentalFeatures,
nlpSettings,
sectionId
);
return () => {
docTitle.reset();
unmountAppCallback();
};
},
})
.enable();
});
const anomalyDetectionJobsTitle = i18n.translate(
'xpack.ml.management.anomalyDetectionJobsTitle',
{
defaultMessage: 'Anomaly Detection Jobs',
}
);
management.sections.section.machineLearning
.registerApp({
id: 'jobsListLink', // TODO: will need to update this
title: overviewTitle,
order: 1,
async mount(params: ManagementAppMountParams) {
const [{ chrome }] = await core.getStartServices();
const { docTitle } = chrome;
docTitle.change(overviewTitle);
const { mountApp } = await import('./overview');
const unmountAppCallback = await mountApp(core, params, deps, isServerless, mlFeatures);
return () => {
docTitle.reset();
unmountAppCallback();
};
},
})
.enable();
management.sections.section.machineLearning
.registerApp({
id: 'anomalyDetectionJobsLink',
title: anomalyDetectionJobsTitle,
order: 2,
async mount(params: ManagementAppMountParams) {
const [{ chrome }] = await core.getStartServices();
const { docTitle } = chrome;
docTitle.change(overviewTitle);
const { mountApp } = await import('./anomaly_detection_jobs');
const unmountAppCallback = await mountApp(core, params, deps, isServerless, mlFeatures);
return () => {
docTitle.reset();
unmountAppCallback();
};
},
})
.enable();
}

View file

@ -8,12 +8,10 @@
import type { FC } from 'react';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { Router } from '@kbn/shared-ux-router';
import { FormattedMessage, I18nProvider } from '@kbn/i18n-react';
import { I18nProvider } from '@kbn/i18n-react';
import type { CoreStart } from '@kbn/core/public';
import { pick } from 'lodash';
import { EuiPageTemplate, EuiSpacer } from '@elastic/eui';
import type { UsageCollectionSetup } from '@kbn/usage-collection-plugin/public';
import { DatePickerContextProvider, type DatePickerDependencies } from '@kbn/ml-date-picker';
import { UI_SETTINGS } from '@kbn/data-plugin/common';
@ -26,17 +24,15 @@ import type { SharePluginStart } from '@kbn/share-plugin/public';
import type { SpacesContextProps, SpacesPluginStart } from '@kbn/spaces-plugin/public';
import type { ChartsPluginStart } from '@kbn/charts-plugin/public';
import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public';
import { UpgradeWarning } from '../../../components/upgrade/upgrade_warning';
import { getMlGlobalServices } from '../../../util/get_services';
import { EnabledFeaturesContextProvider } from '../../../contexts/ml';
import { type MlFeatures, PLUGIN_ID } from '../../../../../common/constants/app';
import { UpgradeWarning } from '../components/upgrade/upgrade_warning';
import { getMlGlobalServices } from '../util/get_services';
import { EnabledFeaturesContextProvider } from '../contexts/ml';
import { type MlFeatures, PLUGIN_ID } from '../../../common/constants/app';
import { checkGetManagementMlJobsResolver } from '../../../capabilities/check_capabilities';
import { checkGetManagementMlJobsResolver } from '../capabilities/check_capabilities';
import { AccessDeniedPage } from '../../jobs_list/components/access_denied_page';
import { InsufficientLicensePage } from '../../jobs_list/components/insufficient_license_page';
// import { DocsLink } from '../../jobs_list/components/jobs_list_page/docs_link';
import { JobsPage } from '../../../jobs/jobs_list';
import { AccessDeniedPage } from './jobs_list/components/access_denied_page';
import { InsufficientLicensePage } from './jobs_list/components/insufficient_license_page';
const getEmptyFunctionComponent: React.FC<SpacesContextProps> = ({ children }) => <>{children}</>;
@ -51,9 +47,10 @@ interface Props {
fieldFormats: FieldFormatsStart;
isServerless: boolean;
mlFeatures: MlFeatures;
children: React.ReactNode;
}
export const AnomalyDetectionJobsPage: FC<Props> = ({
export const ManagementSectionWrapper: FC<Props> = ({
coreStart,
share,
history,
@ -64,6 +61,7 @@ export const AnomalyDetectionJobsPage: FC<Props> = ({
fieldFormats,
isServerless,
mlFeatures,
children,
}) => {
const [initialized, setInitialized] = useState(false);
const [accessDenied, setAccessDenied] = useState(false);
@ -177,35 +175,7 @@ export const AnomalyDetectionJobsPage: FC<Props> = ({
<DatePickerContextProvider {...datePickerDeps}>
<ContextWrapper feature={PLUGIN_ID}>
<EnabledFeaturesContextProvider isServerless={isServerless} mlFeatures={mlFeatures}>
<Router history={history}>
<EuiPageTemplate.Header
pageTitle={
<FormattedMessage
id="xpack.ml.management.overview.anomalyDetectionJobsPageTitle"
defaultMessage="Anomaly Detection Jobs"
/>
}
description={
<FormattedMessage
id="xpack.ml.management.jobsList.jobsListTagline"
defaultMessage="Identify, analyze, and process your data using advanced analysis techniques."
/>
}
// rightSideItems={[<DocsLink currentTabId={currentTabId} />]}
bottomBorder
paddingSize={'none'}
/>
<EuiSpacer size="l" />
<EuiPageTemplate.Section
paddingSize={'none'}
id="kibanaManagementMLSection"
data-test-subj="mlPageStackManagementJobsList"
>
<JobsPage />
</EuiPageTemplate.Section>
</Router>
<Router history={history}>{children}</Router>
</EnabledFeaturesContextProvider>
</ContextWrapper>
</DatePickerContextProvider>

View file

@ -0,0 +1,71 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import ReactDOM, { unmountComponentAtNode } from 'react-dom';
import React from 'react';
import type { CoreSetup } from '@kbn/core/public';
import type { UsageCollectionSetup } from '@kbn/usage-collection-plugin/public';
import type { ManagementAppMountParams } from '@kbn/management-plugin/public';
import type { MlFeatures, NLPSettings, ExperimentalFeatures } from '../../../common/constants/app';
import type { MlStartDependencies } from '../../plugin';
import { getJobsListBreadcrumbs } from './breadcrumbs';
import { App } from '../app';
const renderApp = (
coreStart: CoreSetup<MlStartDependencies>,
params: ManagementAppMountParams,
deps: any,
isServerless: boolean,
mlFeatures: MlFeatures,
experimentalFeatures: ExperimentalFeatures,
nlpSettings: NLPSettings,
entryPoint: string
) => {
ReactDOM.render(
React.createElement(App, {
coreStart,
deps,
appMountParams: params,
isServerless,
mlFeatures,
experimentalFeatures,
nlpSettings,
entryPoint,
}),
params.element
);
return () => {
unmountComponentAtNode(params.element);
deps.data.search.session.clear();
};
};
export async function mountApp(
core: CoreSetup<MlStartDependencies>,
params: ManagementAppMountParams,
deps: { usageCollection?: UsageCollectionSetup },
isServerless: boolean,
mlFeatures: MlFeatures,
experimentalFeatures: ExperimentalFeatures,
nlpSettings: NLPSettings,
entryPoint: string
) {
const [coreStart] = await core.getStartServices();
params.setBreadcrumbs(getJobsListBreadcrumbs()); // TODO: update breadcrumbs depending on section
return renderApp(
coreStart,
params,
deps,
isServerless,
mlFeatures,
experimentalFeatures,
nlpSettings,
entryPoint
);
}

View file

@ -27,6 +27,8 @@ import { UpgradeWarning } from '../components/upgrade';
import { HelpMenu } from '../components/help_menu';
import { useMlKibana, useMlLink } from '../contexts/kibana';
import { NodesList } from '../memory_usage/nodes_overview';
import { MlPageHeader } from '../components/page_header';
import { PageTitle } from '../components/page_title';
import { getMlNodesCount } from '../ml_nodes_check/check_ml_nodes';
export const overviewPanelDefaultState = Object.freeze({
@ -60,6 +62,13 @@ export const OverviewPage: FC = () => {
return (
<div>
<MlPageHeader>
<PageTitle
title={i18n.translate('xpack.ml.overview.overviewLabel', {
defaultMessage: 'Overview',
})}
/>
</MlPageHeader>
<NodeAvailableWarning />
<JobsAwaitingNodeWarning jobCount={adLazyJobCount + dfaLazyJobCount} />
<SavedObjectsWarning
@ -73,6 +82,7 @@ export const OverviewPage: FC = () => {
}}
/>
<UpgradeWarning />
{canViewMlNodes ? (
<>
<CollapsiblePanel
@ -111,6 +121,7 @@ export const OverviewPage: FC = () => {
<EuiSpacer size="m" />
</>
) : null}
<OverviewContent
createAnomalyDetectionJobDisabled={disableCreateAnomalyDetectionJob}
setAdLazyJobCount={setAdLazyJobCount}

View file

@ -85,11 +85,12 @@ export const PageLoader: FC<PropsWithChildren<{ context: RouteResolverContext }>
*/
export const MlRouter: FC<{
pageDeps: PageDependencies;
}> = ({ pageDeps }) => (
entryPoint?: string; // update this to finite set of possible ids - now 'jobs' - maybe make part of the context to avoid prop drilling?
}> = ({ pageDeps, entryPoint }) => (
<Router history={pageDeps.history}>
<UrlStateProvider>
<MlNotificationsContextProvider>
<MlPage pageDeps={pageDeps} />
<MlPage pageDeps={pageDeps} entryPoint={entryPoint} />
</MlNotificationsContextProvider>
</UrlStateProvider>
</Router>

View file

@ -5,4 +5,6 @@
* 2.0.
*/
export { OverviewPage } from './overview_page';
export * from './new_job';
export * from './datavisualizer';
export * from './jobs_list';

View file

@ -9,10 +9,9 @@ import type { FC } from 'react';
import React from 'react';
import { i18n } from '@kbn/i18n';
import { dynamic } from '@kbn/shared-ux-utility';
import { ML_PAGES } from '../../../../locator';
import type { NavigateToPath } from '../../../contexts/kibana';
import type { MlRoute } from '../../router';
import { createPath, PageLoader } from '../../router';
import { PageLoader } from '../../router';
import { useRouteResolver } from '../../use_resolver';
import { basicResolvers } from '../../resolvers';
import { getBreadcrumbWithUrlForApp } from '../../breadcrumbs';
@ -26,7 +25,7 @@ export const analyticsJobsListRouteFactory = (
basePath: string
): MlRoute => ({
id: 'data_frame_analytics',
path: createPath(ML_PAGES.DATA_FRAME_ANALYTICS_JOBS_MANAGE),
path: '',
title: i18n.translate('xpack.ml.dataFrameAnalytics.jobs.docTitle', {
defaultMessage: 'Data Frame Analytics Jobs',
}),

View file

@ -6,15 +6,16 @@
*/
export * from './overview';
export * from './jobs_list';
export * from './new_job';
export * from './datavisualizer';
export * from './settings';
export * from './data_frame_analytics';
// -- overview section will be replaced
// export * from './new_job';
// export * from './datavisualizer';
// export * from './jobs_list';
// export * from './settings';
// export * from './data_frame_analytics';
export * from './aiops';
export { timeSeriesExplorerRouteFactory } from './timeseriesexplorer';
export * from './explorer';
export * from './trained_models';
// export * from './trained_models';
export * from './notifications';
export * from './memory_usage';
export * from './supplied_configurations';
// export * from './supplied_configurations';

View file

@ -15,11 +15,10 @@ import {
useTimefilter,
} from '@kbn/ml-date-picker';
import { dynamic } from '@kbn/shared-ux-utility';
import { ML_PAGES } from '../../../locator';
import type { NavigateToPath } from '../../contexts/kibana';
import { DEFAULT_REFRESH_INTERVAL_MS } from '../../../../common/constants/jobs_list';
import type { MlRoute } from '../router';
import { createPath, PageLoader } from '../router';
import { PageLoader } from '../router';
import { useRouteResolver } from '../use_resolver';
import { getBreadcrumbWithUrlForApp } from '../breadcrumbs';
import { AnnotationUpdatesService } from '../../services/annotations_service';
@ -35,7 +34,7 @@ export const jobListRouteFactory = (navigateToPath: NavigateToPath, basePath: st
title: i18n.translate('xpack.ml.anomalyDetection.jobs.docTitle', {
defaultMessage: 'Anomaly Detection Jobs',
}),
path: createPath(ML_PAGES.ANOMALY_DETECTION_JOBS_MANAGE),
path: '',
render: () => <PageWrapper />,
breadcrumbs: [
getBreadcrumbWithUrlForApp('ML_BREADCRUMB', navigateToPath, basePath),

View file

@ -106,109 +106,109 @@ export const indexOrSearchRouteFactory = (
breadcrumbs: getBreadcrumbs(navigateToPath, basePath),
});
export const dataVizIndexOrSearchRouteFactory = (
navigateToPath: NavigateToPath,
basePath: string
): MlRoute => ({
id: 'data_view_datavisualizer',
path: createPath(ML_PAGES.DATA_VISUALIZER_INDEX_SELECT),
title: i18n.translate('xpack.ml.selectDataViewLabel', {
defaultMessage: 'Select Data View',
}),
render: (props, deps) => {
const button = (
<NavigateToPageButton
nextStepPath={createPath(ML_PAGES.DATA_VISUALIZER_ESQL)}
title={
<FormattedMessage
id="xpack.ml.datavisualizer.selector.useESQLButtonLabel"
defaultMessage="Use ES|QL"
/>
}
/>
);
return (
<PageWrapper
{...props}
nextStepPath={createPath(ML_PAGES.DATA_VISUALIZER_INDEX_VIEWER)}
deps={deps}
mode={MODE.DATAVISUALIZER}
extraButtons={button}
/>
);
},
breadcrumbs: getDataVisBreadcrumbs(navigateToPath, basePath),
});
// export const dataVizIndexOrSearchRouteFactory = (
// navigateToPath: NavigateToPath,
// basePath: string
// ): MlRoute => ({
// id: 'data_view_datavisualizer',
// path: createPath(ML_PAGES.DATA_VISUALIZER_INDEX_SELECT),
// title: i18n.translate('xpack.ml.selectDataViewLabel', {
// defaultMessage: 'Select Data View',
// }),
// render: (props, deps) => {
// const button = (
// <NavigateToPageButton
// nextStepPath={createPath(ML_PAGES.DATA_VISUALIZER_ESQL)}
// title={
// <FormattedMessage
// id="xpack.ml.datavisualizer.selector.useESQLButtonLabel"
// defaultMessage="Use ES|QL"
// />
// }
// />
// );
// return (
// <PageWrapper
// {...props}
// nextStepPath={createPath(ML_PAGES.DATA_VISUALIZER_INDEX_VIEWER)}
// deps={deps}
// mode={MODE.DATAVISUALIZER}
// extraButtons={button}
// />
// );
// },
// breadcrumbs: getDataVisBreadcrumbs(navigateToPath, basePath),
// });
export const logRateAnalysisIndexOrSearchRouteFactory = (
navigateToPath: NavigateToPath,
basePath: string
): MlRoute => ({
id: 'data_view_log_rate_analysis',
path: createPath(ML_PAGES.AIOPS_LOG_RATE_ANALYSIS_INDEX_SELECT),
title: i18n.translate('xpack.ml.selectDataViewLabel', {
defaultMessage: 'Select Data View',
}),
render: (props, deps) => (
<PageWrapper
{...props}
nextStepPath={createPath(ML_PAGES.AIOPS_LOG_RATE_ANALYSIS)}
deps={deps}
mode={MODE.DATAVISUALIZER}
/>
),
breadcrumbs: getLogRateAnalysisBreadcrumbs(navigateToPath, basePath),
});
// export const logRateAnalysisIndexOrSearchRouteFactory = (
// navigateToPath: NavigateToPath,
// basePath: string
// ): MlRoute => ({
// id: 'data_view_log_rate_analysis',
// path: createPath(ML_PAGES.AIOPS_LOG_RATE_ANALYSIS_INDEX_SELECT),
// title: i18n.translate('xpack.ml.selectDataViewLabel', {
// defaultMessage: 'Select Data View',
// }),
// render: (props, deps) => (
// <PageWrapper
// {...props}
// nextStepPath={createPath(ML_PAGES.AIOPS_LOG_RATE_ANALYSIS)}
// deps={deps}
// mode={MODE.DATAVISUALIZER}
// />
// ),
// breadcrumbs: getLogRateAnalysisBreadcrumbs(navigateToPath, basePath),
// });
/**
* @deprecated since 8.10, kept here to redirect old bookmarks.
*/
export const explainLogRateSpikesIndexOrSearchRouteFactory = (): MlRoute => ({
path: createPath(ML_PAGES.AIOPS_EXPLAIN_LOG_RATE_SPIKES_INDEX_SELECT),
render: () => <Redirect to={createPath(ML_PAGES.AIOPS_LOG_RATE_ANALYSIS_INDEX_SELECT)} />,
// no breadcrumbs since it's just a redirect
breadcrumbs: [],
});
// export const explainLogRateSpikesIndexOrSearchRouteFactory = (): MlRoute => ({
// path: createPath(ML_PAGES.AIOPS_EXPLAIN_LOG_RATE_SPIKES_INDEX_SELECT),
// render: () => <Redirect to={createPath(ML_PAGES.AIOPS_LOG_RATE_ANALYSIS_INDEX_SELECT)} />,
// // no breadcrumbs since it's just a redirect
// breadcrumbs: [],
// });
export const logCategorizationIndexOrSearchRouteFactory = (
navigateToPath: NavigateToPath,
basePath: string
): MlRoute => ({
id: 'data_view_log_categorization',
path: createPath(ML_PAGES.AIOPS_LOG_CATEGORIZATION_INDEX_SELECT),
title: i18n.translate('xpack.ml.selectDataViewLabel', {
defaultMessage: 'Select Data View',
}),
render: (props, deps) => (
<PageWrapper
{...props}
nextStepPath={createPath(ML_PAGES.AIOPS_LOG_CATEGORIZATION)}
deps={deps}
mode={MODE.DATAVISUALIZER}
/>
),
breadcrumbs: getLogCategorizationBreadcrumbs(navigateToPath, basePath),
});
// export const logCategorizationIndexOrSearchRouteFactory = (
// navigateToPath: NavigateToPath,
// basePath: string
// ): MlRoute => ({
// id: 'data_view_log_categorization',
// path: createPath(ML_PAGES.AIOPS_LOG_CATEGORIZATION_INDEX_SELECT),
// title: i18n.translate('xpack.ml.selectDataViewLabel', {
// defaultMessage: 'Select Data View',
// }),
// render: (props, deps) => (
// <PageWrapper
// {...props}
// nextStepPath={createPath(ML_PAGES.AIOPS_LOG_CATEGORIZATION)}
// deps={deps}
// mode={MODE.DATAVISUALIZER}
// />
// ),
// breadcrumbs: getLogCategorizationBreadcrumbs(navigateToPath, basePath),
// });
export const changePointDetectionIndexOrSearchRouteFactory = (
navigateToPath: NavigateToPath,
basePath: string
): MlRoute => ({
id: 'data_view_change_point_detection',
path: createPath(ML_PAGES.AIOPS_CHANGE_POINT_DETECTION_INDEX_SELECT),
title: i18n.translate('xpack.ml.selectDataViewLabel', {
defaultMessage: 'Select Data View',
}),
render: (props, deps) => (
<PageWrapper
{...props}
nextStepPath={createPath(ML_PAGES.AIOPS_CHANGE_POINT_DETECTION)}
deps={deps}
mode={MODE.DATAVISUALIZER}
/>
),
breadcrumbs: getChangePointDetectionBreadcrumbs(navigateToPath, basePath),
});
// export const changePointDetectionIndexOrSearchRouteFactory = (
// navigateToPath: NavigateToPath,
// basePath: string
// ): MlRoute => ({
// id: 'data_view_change_point_detection',
// path: createPath(ML_PAGES.AIOPS_CHANGE_POINT_DETECTION_INDEX_SELECT),
// title: i18n.translate('xpack.ml.selectDataViewLabel', {
// defaultMessage: 'Select Data View',
// }),
// render: (props, deps) => (
// <PageWrapper
// {...props}
// nextStepPath={createPath(ML_PAGES.AIOPS_CHANGE_POINT_DETECTION)}
// deps={deps}
// mode={MODE.DATAVISUALIZER}
// />
// ),
// breadcrumbs: getChangePointDetectionBreadcrumbs(navigateToPath, basePath),
// });
const PageWrapper: FC<IndexOrSearchPageProps> = ({ nextStepPath, mode, extraButtons }) => {
const {

View file

@ -10,14 +10,11 @@ import React from 'react';
import { i18n } from '@kbn/i18n';
import { useTimefilter } from '@kbn/ml-date-picker';
import { dynamic } from '@kbn/shared-ux-utility';
import { ML_PAGES } from '../../../../locator';
import type { NavigateToPath } from '../../../contexts/kibana';
import type { MlRoute } from '../../router';
import { createPath, PageLoader } from '../../router';
import { PageLoader } from '../../router';
import { useRouteResolver } from '../../use_resolver';
import { usePermissionCheck } from '../../../capabilities/check_capabilities';
import { getMlNodeCount } from '../../../ml_nodes_check/check_ml_nodes';
import { AnomalyDetectionSettingsContext } from '../../../settings';
import { getBreadcrumbWithUrlForApp } from '../../breadcrumbs';
const Settings = dynamic(async () => ({
@ -29,7 +26,7 @@ export const settingsRouteFactory = (
basePath: string
): MlRoute => ({
id: 'settings',
path: createPath(ML_PAGES.SETTINGS),
path: '',
title: i18n.translate('xpack.ml.settings.docTitle', {
defaultMessage: 'Settings',
}),
@ -48,20 +45,9 @@ const PageWrapper: FC = () => {
useTimefilter({ timeRangeSelector: false, autoRefreshSelector: false });
const [canGetFilters, canCreateFilter, canGetCalendars, canCreateCalendar] = usePermissionCheck([
'canGetFilters',
'canCreateFilter',
'canGetCalendars',
'canCreateCalendar',
]);
return (
<PageLoader context={context}>
<AnomalyDetectionSettingsContext.Provider
value={{ canGetFilters, canCreateFilter, canGetCalendars, canCreateCalendar }}
>
<Settings />
</AnomalyDetectionSettingsContext.Provider>
<Settings />
</PageLoader>
);
};

View file

@ -11,10 +11,9 @@ import { FormattedMessage } from '@kbn/i18n-react';
import { i18n } from '@kbn/i18n';
import { EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui';
import { dynamic } from '@kbn/shared-ux-utility';
import { ML_PAGES } from '../../../../locator';
import type { NavigateToPath } from '../../../contexts/kibana';
import type { MlRoute } from '../../router';
import { createPath, PageLoader } from '../../router';
import { PageLoader } from '../../router';
import { useRouteResolver } from '../../use_resolver';
import { basicResolvers } from '../../resolvers';
import { getBreadcrumbWithUrlForApp } from '../../breadcrumbs';
@ -30,7 +29,7 @@ export const suppliedConfigurationsRouteFactory = (
basePath: string
): MlRoute => ({
id: 'supplied_configurations',
path: createPath(ML_PAGES.SUPPLIED_CONFIGURATIONS),
path: '',
title: i18n.translate('xpack.ml.suppliedConfigurations.suppliedConfigurations.docTitle', {
defaultMessage: 'Supplied configurations',
}),

View file

@ -11,10 +11,9 @@ import { FormattedMessage } from '@kbn/i18n-react';
import { i18n } from '@kbn/i18n';
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { dynamic } from '@kbn/shared-ux-utility';
import { ML_PAGES } from '../../../../locator';
import type { NavigateToPath } from '../../../contexts/kibana';
import type { MlRoute } from '../../router';
import { createPath, PageLoader } from '../../router';
import { PageLoader } from '../../router';
import { useRouteResolver } from '../../use_resolver';
import { basicResolvers } from '../../resolvers';
import { getBreadcrumbWithUrlForApp } from '../../breadcrumbs';
@ -30,7 +29,7 @@ export const modelsListRouteFactory = (
basePath: string
): MlRoute => ({
id: 'trained_models',
path: createPath(ML_PAGES.TRAINED_MODELS_MANAGE),
path: '',
title: i18n.translate('xpack.ml.modelManagement.trainedModels.docTitle', {
defaultMessage: 'Trained Models',
}),

View file

@ -6,7 +6,7 @@
*/
import type { FC } from 'react';
import React, { Fragment, useContext, useEffect, useState } from 'react';
import React, { Fragment, useEffect, useState } from 'react';
import {
EuiBadge,
@ -23,7 +23,7 @@ import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';
import { useMlApi } from '../contexts/kibana';
import { AnomalyDetectionSettingsContext } from './anomaly_detection_settings_context';
import { usePermissionCheck } from '../capabilities/check_capabilities';
import { useToastNotificationService } from '../services/toast_notification_service';
import { ML_PAGES } from '../../../common/constants/locator';
import { useCreateAndNavigateToMlLink } from '../contexts/kibana/use_create_url';
@ -36,9 +36,12 @@ export const AnomalyDetectionSettings: FC = () => {
const [calendarsDstCount, setCalendarsDstCount] = useState(0);
const [filterListsCount, setFilterListsCount] = useState(0);
const { canGetFilters, canCreateFilter, canGetCalendars, canCreateCalendar } = useContext(
AnomalyDetectionSettingsContext
);
const [canGetFilters, canCreateFilter, canGetCalendars, canCreateCalendar] = usePermissionCheck([
'canGetFilters',
'canCreateFilter',
'canGetCalendars',
'canCreateCalendar',
]);
const { displayErrorToast } = useToastNotificationService();
const redirectToCalendarList = useCreateAndNavigateToMlLink(ML_PAGES.CALENDARS_MANAGE);

View file

@ -1,22 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { createContext } from 'react';
export interface AnomalyDetectionSettingsContextValue {
canGetFilters: boolean;
canCreateFilter: boolean;
canGetCalendars: boolean;
canCreateCalendar: boolean;
}
export const AnomalyDetectionSettingsContext = createContext<AnomalyDetectionSettingsContextValue>({
canGetFilters: false,
canCreateFilter: false,
canGetCalendars: false,
canCreateCalendar: false,
});

View file

@ -5,5 +5,4 @@
* 2.0.
*/
export { AnomalyDetectionSettingsContext } from './anomaly_detection_settings_context';
export { Settings } from './settings';

View file

@ -8,7 +8,6 @@
import { mountWithIntl } from '@kbn/test-jest-helpers';
import React from 'react';
import { AnomalyDetectionSettingsContext } from './anomaly_detection_settings_context';
import { Settings } from './settings';
jest.mock('../components/help_menu', () => ({
@ -37,22 +36,12 @@ jest.mock('../contexts/kibana/use_create_url', () => ({
describe('Settings', () => {
function runCheckButtonsDisabledTest(
canGetFilters: boolean,
canCreateFilter: boolean,
canGetCalendars: boolean,
canCreateCalendar: boolean,
isFilterListsMngDisabled: boolean,
isFilterListCreateDisabled: boolean,
isCalendarsMngDisabled: boolean,
isCalendarCreateDisabled: boolean
) {
const wrapper = mountWithIntl(
<AnomalyDetectionSettingsContext.Provider
value={{ canGetFilters, canCreateFilter, canGetCalendars, canCreateCalendar }}
>
<Settings />
</AnomalyDetectionSettingsContext.Provider>
);
const wrapper = mountWithIntl(<Settings />);
const filterMngButton = wrapper
.find('[data-test-subj="mlFilterListsMngButton"]')
@ -74,16 +63,4 @@ describe('Settings', () => {
.find('EuiButtonEmpty');
expect(calendarCreateButton.prop('isDisabled')).toBe(isCalendarCreateDisabled);
}
test('should render settings page with all buttons enabled when full user capabilities', () => {
runCheckButtonsDisabledTest(true, true, true, true, false, false, false, false);
});
test('should disable Filter Lists buttons if filters capabilities are false', () => {
runCheckButtonsDisabledTest(false, false, true, true, true, true, false, false);
});
test('should disable Calendars buttons if calendars capabilities are false', () => {
runCheckButtonsDisabledTest(true, true, false, false, false, false, true, true);
});
});

View file

@ -169,6 +169,14 @@ export class MlPlugin implements Plugin<MlPluginSetup, MlPluginStart> {
pluginsSetup: MlSetupDependencies
): { locator?: LocatorPublic<MlLocatorParams>; elasticModels?: ElasticModels } {
this.sharedMlServices = getMlSharedServices(core.http);
const deps = {
// embeddable: pluginsSetup.embeddable,
// embeddable: { ...pluginsSetup.embeddable, ...pluginsStart.embeddable },
home: pluginsSetup.home,
licenseManagement: pluginsSetup.licenseManagement,
management: pluginsSetup.management,
usageCollection: pluginsSetup.usageCollection,
};
core.application.register({
id: PLUGIN_ID,
@ -195,12 +203,9 @@ export class MlPlugin implements Plugin<MlPluginSetup, MlPluginStart> {
dataVisualizer: pluginsStart.dataVisualizer,
embeddable: { ...pluginsSetup.embeddable, ...pluginsStart.embeddable },
fieldFormats: pluginsStart.fieldFormats,
home: pluginsSetup.home,
kibanaVersion: this.initializerContext.env.packageInfo.version,
lens: pluginsStart.lens,
licenseManagement: pluginsSetup.licenseManagement,
licensing: pluginsStart.licensing,
management: pluginsSetup.management,
maps: pluginsStart.maps,
observabilityAIAssistant: pluginsStart.observabilityAIAssistant,
presentationUtil: pluginsStart.presentationUtil,
@ -211,7 +216,7 @@ export class MlPlugin implements Plugin<MlPluginSetup, MlPluginStart> {
triggersActionsUi: pluginsStart.triggersActionsUi,
uiActions: pluginsStart.uiActions,
unifiedSearch: pluginsStart.unifiedSearch,
usageCollection: pluginsSetup.usageCollection,
...deps,
},
params,
this.isServerless,
@ -230,11 +235,11 @@ export class MlPlugin implements Plugin<MlPluginSetup, MlPluginStart> {
registerManagementSections(
pluginsSetup.management,
core,
{
usageCollection: pluginsSetup.usageCollection,
},
deps,
this.isServerless,
this.enabledFeatures
this.enabledFeatures,
this.nlpSettings,
this.experimentalFeatures
);
}

View file

@ -1,8 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
export { AnomalyDetectionJobsPage } from './anomaly_detection_jobs';

View file

@ -1,79 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import ReactDOM, { unmountComponentAtNode } from 'react-dom';
import React from 'react';
import type { CoreSetup, CoreStart } from '@kbn/core/public';
import type { DataPublicPluginStart } from '@kbn/data-plugin/public';
import type { UsageCollectionSetup } from '@kbn/usage-collection-plugin/public';
import type { ManagementAppMountParams } from '@kbn/management-plugin/public';
import type { SharePluginStart } from '@kbn/share-plugin/public';
import type { SpacesPluginStart } from '@kbn/spaces-plugin/public';
import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public';
import type { ChartsPluginStart } from '@kbn/charts-plugin/public';
import type { MlFeatures } from '../../../../common/constants/app';
import type { MlStartDependencies } from '../../../plugin';
import { AnomalyDetectionJobsPage } from './components';
import { getJobsListBreadcrumbs } from '../breadcrumbs';
const renderApp = (
element: HTMLElement,
history: ManagementAppMountParams['history'],
coreStart: CoreStart,
share: SharePluginStart,
data: DataPublicPluginStart,
fieldFormats: FieldFormatsStart,
charts: ChartsPluginStart,
isServerless: boolean,
mlFeatures: MlFeatures,
spacesApi?: SpacesPluginStart,
usageCollection?: UsageCollectionSetup
) => {
ReactDOM.render(
React.createElement(AnomalyDetectionJobsPage, {
coreStart,
history,
share,
data,
charts,
spacesApi,
usageCollection,
fieldFormats,
isServerless,
mlFeatures,
}),
element
);
return () => {
unmountComponentAtNode(element);
};
};
export async function mountApp(
core: CoreSetup<MlStartDependencies>,
params: ManagementAppMountParams,
deps: { usageCollection?: UsageCollectionSetup },
isServerless: boolean,
mlFeatures: MlFeatures
) {
const [coreStart, pluginsStart] = await core.getStartServices();
params.setBreadcrumbs(getJobsListBreadcrumbs()); // TODO: update this
return renderApp(
params.element,
params.history,
coreStart,
pluginsStart.share,
pluginsStart.data,
pluginsStart.fieldFormats,
pluginsStart.charts,
isServerless,
mlFeatures,
pluginsStart.spaces,
deps.usageCollection
);
}

View file

@ -1,217 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import type { FC } from 'react';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { Router } from '@kbn/shared-ux-router';
import { FormattedMessage, I18nProvider } from '@kbn/i18n-react';
import type { CoreStart } from '@kbn/core/public';
import { pick } from 'lodash';
import { EuiPageTemplate, EuiSpacer } from '@elastic/eui';
import type { UsageCollectionSetup } from '@kbn/usage-collection-plugin/public';
import { DatePickerContextProvider, type DatePickerDependencies } from '@kbn/ml-date-picker';
import { UI_SETTINGS } from '@kbn/data-plugin/common';
import type { DataPublicPluginStart } from '@kbn/data-plugin/public';
import type { ManagementAppMountParams } from '@kbn/management-plugin/public';
import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public';
import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render';
import { RedirectAppLinks } from '@kbn/shared-ux-link-redirect-app';
import type { SharePluginStart } from '@kbn/share-plugin/public';
import type { SpacesContextProps, SpacesPluginStart } from '@kbn/spaces-plugin/public';
import type { ChartsPluginStart } from '@kbn/charts-plugin/public';
import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public';
import { UpgradeWarning } from '../../../components/upgrade/upgrade_warning';
import { getMlGlobalServices } from '../../../util/get_services';
import { EnabledFeaturesContextProvider } from '../../../contexts/ml';
import { type MlFeatures, PLUGIN_ID } from '../../../../../common/constants/app';
import { checkGetManagementMlJobsResolver } from '../../../capabilities/check_capabilities';
import { AccessDeniedPage } from '../../jobs_list/components/access_denied_page';
import { InsufficientLicensePage } from '../../jobs_list/components/insufficient_license_page';
// import { DocsLink } from '../../jobs_list/components/jobs_list_page/docs_link';
import { OverviewPage as OverviewPageContent } from '../../../overview/overview_page';
const getEmptyFunctionComponent: React.FC<SpacesContextProps> = ({ children }) => <>{children}</>;
interface Props {
coreStart: CoreStart;
share: SharePluginStart;
history: ManagementAppMountParams['history'];
spacesApi?: SpacesPluginStart;
data: DataPublicPluginStart;
charts: ChartsPluginStart;
usageCollection?: UsageCollectionSetup;
fieldFormats: FieldFormatsStart;
isServerless: boolean;
mlFeatures: MlFeatures;
}
export const OverviewPage: FC<Props> = ({
coreStart,
share,
history,
spacesApi,
data,
charts,
usageCollection,
fieldFormats,
isServerless,
mlFeatures,
}) => {
const [initialized, setInitialized] = useState(false);
const [accessDenied, setAccessDenied] = useState(false);
const [isUpgradeInProgress, setIsUpgradeInProgress] = useState(false);
const [isPlatinumOrTrialLicense, setIsPlatinumOrTrialLicense] = useState(true);
const mlServices = useMemo(
() => getMlGlobalServices(coreStart, data.dataViews, usageCollection),
[coreStart, data.dataViews, usageCollection]
);
const datePickerDeps: DatePickerDependencies = {
...pick(coreStart, ['http', 'notifications', 'theme', 'uiSettings', 'i18n']),
data,
uiSettingsKeys: UI_SETTINGS,
showFrozenDataTierChoice: false,
};
const check = async () => {
try {
await checkGetManagementMlJobsResolver(mlServices);
} catch (e) {
if (e.mlFeatureEnabledInSpace && e.isPlatinumOrTrialLicense === false) {
setIsPlatinumOrTrialLicense(false);
} else if (e.isUpgradeInProgress) {
setIsUpgradeInProgress(true);
} else {
setAccessDenied(true);
}
}
setInitialized(true);
};
useEffect(() => {
check();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
// eslint-disable-next-line react-hooks/exhaustive-deps
const ContextWrapper = useCallback(
spacesApi ? spacesApi.ui.components.getSpacesContextProvider : getEmptyFunctionComponent,
[spacesApi]
);
if (initialized === false) {
return null;
}
if (isUpgradeInProgress) {
return (
<I18nProvider>
<KibanaRenderContextProvider {...coreStart}>
<KibanaContextProvider
services={{
...coreStart,
share,
data,
usageCollection,
fieldFormats,
spacesApi,
mlServices,
}}
>
<UpgradeWarning />
</KibanaContextProvider>
</KibanaRenderContextProvider>
</I18nProvider>
);
}
if (accessDenied) {
return (
<I18nProvider>
<KibanaRenderContextProvider {...coreStart}>
<AccessDeniedPage />
</KibanaRenderContextProvider>
</I18nProvider>
);
}
if (isPlatinumOrTrialLicense === false) {
return (
<I18nProvider>
<KibanaRenderContextProvider {...coreStart}>
<InsufficientLicensePage basePath={coreStart.http.basePath} />
</KibanaRenderContextProvider>
</I18nProvider>
);
}
return (
<I18nProvider>
<KibanaRenderContextProvider {...coreStart}>
<RedirectAppLinks
coreStart={{
application: coreStart.application,
}}
>
<KibanaContextProvider
services={{
...coreStart,
share,
data,
charts,
usageCollection,
fieldFormats,
spacesApi,
mlServices,
}}
>
<DatePickerContextProvider {...datePickerDeps}>
<ContextWrapper feature={PLUGIN_ID}>
<EnabledFeaturesContextProvider isServerless={isServerless} mlFeatures={mlFeatures}>
<Router history={history}>
<EuiPageTemplate.Header
pageTitle={
<FormattedMessage
id="xpack.ml.management.overview.overviewPageTitle"
defaultMessage="Overview"
/>
}
description={
<FormattedMessage
id="xpack.ml.management.jobsList.jobsListTagline"
defaultMessage="Identify, analyze, and process your data using advanced analysis techniques."
/>
}
// rightSideItems={[<DocsLink currentTabId={currentTabId} />]}
bottomBorder
paddingSize={'none'}
/>
<EuiSpacer size="l" />
<EuiPageTemplate.Section
paddingSize={'none'}
id="kibanaManagementMLSection"
data-test-subj="mlPageStackManagementJobsList"
>
<OverviewPageContent />
</EuiPageTemplate.Section>
</Router>
</EnabledFeaturesContextProvider>
</ContextWrapper>
</DatePickerContextProvider>
</KibanaContextProvider>
</RedirectAppLinks>
</KibanaRenderContextProvider>
</I18nProvider>
);
};

View file

@ -1,79 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import ReactDOM, { unmountComponentAtNode } from 'react-dom';
import React from 'react';
import type { CoreSetup, CoreStart } from '@kbn/core/public';
import type { DataPublicPluginStart } from '@kbn/data-plugin/public';
import type { UsageCollectionSetup } from '@kbn/usage-collection-plugin/public';
import type { ManagementAppMountParams } from '@kbn/management-plugin/public';
import type { SharePluginStart } from '@kbn/share-plugin/public';
import type { SpacesPluginStart } from '@kbn/spaces-plugin/public';
import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public';
import type { ChartsPluginStart } from '@kbn/charts-plugin/public';
import type { MlFeatures } from '../../../../common/constants/app';
import type { MlStartDependencies } from '../../../plugin';
import { OverviewPage } from './components';
import { getJobsListBreadcrumbs } from '../breadcrumbs';
const renderApp = (
element: HTMLElement,
history: ManagementAppMountParams['history'],
coreStart: CoreStart,
share: SharePluginStart,
data: DataPublicPluginStart,
fieldFormats: FieldFormatsStart,
charts: ChartsPluginStart,
isServerless: boolean,
mlFeatures: MlFeatures,
spacesApi?: SpacesPluginStart,
usageCollection?: UsageCollectionSetup
) => {
ReactDOM.render(
React.createElement(OverviewPage, {
coreStart,
history,
share,
data,
charts,
spacesApi,
usageCollection,
fieldFormats,
isServerless,
mlFeatures,
}),
element
);
return () => {
unmountComponentAtNode(element);
};
};
export async function mountApp(
core: CoreSetup<MlStartDependencies>,
params: ManagementAppMountParams,
deps: { usageCollection?: UsageCollectionSetup },
isServerless: boolean,
mlFeatures: MlFeatures
) {
const [coreStart, pluginsStart] = await core.getStartServices();
params.setBreadcrumbs(getJobsListBreadcrumbs()); // TODO: update this
return renderApp(
params.element,
params.history,
coreStart,
pluginsStart.share,
pluginsStart.data,
pluginsStart.fieldFormats,
pluginsStart.charts,
isServerless,
mlFeatures,
pluginsStart.spaces,
deps.usageCollection
);
}