mirror of
https://github.com/elastic/kibana.git
synced 2025-06-28 03:01:21 -04:00
[ML] Moves job and trained model management features into Stack Management (#204290)
## Summary Updates the navigation for Machine Learning pages, moving admin tasks for managing ML jobs and models to a single place inside Stack Management, and leaving exploratory tasks in a consolidated top-level Machine Learning menu. The available items vary by solution, so that the navigation for an Elasticsearch project, for example, contains a single item for managing trained models. #### Stack management menu for classic/observability/security nav <img width="275" alt="Screenshot 2025-04-04 at 16 10 04" src="https://github.com/user-attachments/assets/14b6e8d4-7111-4fbd-ae5d-9f389f83f23c" /> #### Stack management for search: <img width="271" alt="Screenshot 2025-04-07 at 14 38 45" src="https://github.com/user-attachments/assets/e104bf20-8a4d-4eed-9b5b-9c05944091ca" /> #### Machine Learning menu for Classic nav <img width="341" alt="Screenshot 2025-04-07 at 14 22 03" src="https://github.com/user-attachments/assets/610efd59-311f-410f-9881-548359ca7997" /> #### Machine Learning menu for Observability <img width="522" alt="Screenshot 2025-04-04 at 16 11 48" src="https://github.com/user-attachments/assets/ef16acf1-4d39-4494-a5d3-0fb078d74730" /> #### Machine Learning menu for Security <img width="528" alt="Screenshot 2025-04-04 at 17 46 43" src="https://github.com/user-attachments/assets/2df20c20-b894-4421-a732-9370bb5d6f2d" /> ### Checklist Check the PR satisfies following conditions. Reviewers should verify this PR satisfies this list as well. - [ ] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md) - [ ] [Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials - [ ] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios - [ ] If a plugin configuration key changed, check if it needs to be allowlisted in the cloud and added to the [docker list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker) - [ ] This was checked for breaking HTTP API changes, and any breaking changes have been approved by the breaking-change committee. The `release_note:breaking` label should be applied in these situations. - [ ] [Flaky Test Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was used on any tests changed - [ ] The PR description includes the appropriate Release Notes section, and the correct `release_note:*` label is applied per the [guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) ### Identify risks Does this PR introduce any risks? For example, consider risks like hard to test bugs, performance regression, potential of data loss. Describe the risk, its severity, and mitigation for each identified risk. Invite stakeholders and evaluate how to proceed before merging. - [ ] [See some risk examples](https://github.com/elastic/kibana/blob/main/RISK_MATRIX.mdx) - [ ] ... --------- Co-authored-by: Quynh Nguyen <quynh.nguyen@elastic.co> Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com> Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Timothy Sullivan <tsullivan@elastic.co>
This commit is contained in:
parent
b0c0917fa7
commit
f51ac13197
291 changed files with 5816 additions and 4309 deletions
|
@ -216,7 +216,7 @@ xpack.observabilityAIAssistant.scope: "observability"
|
|||
telemetry.labels.serverless: observability
|
||||
|
||||
xpack.ml.ad.enabled: true
|
||||
xpack.ml.dfa.enabled: false
|
||||
xpack.ml.dfa.enabled: true
|
||||
xpack.ml.nlp:
|
||||
enabled: true
|
||||
modelDeployment:
|
||||
|
|
|
@ -104,7 +104,7 @@ pageLoadAssetSize:
|
|||
maps: 46000
|
||||
mapsEms: 26072
|
||||
metricsDataAccess: 73287
|
||||
ml: 85000
|
||||
ml: 89000
|
||||
mockIdpPlugin: 30000
|
||||
monitoring: 80000
|
||||
navigation: 37269
|
||||
|
|
|
@ -32,12 +32,6 @@ export const defaultNavigation: MlNodeDefinition = {
|
|||
{
|
||||
link: 'ml:overview',
|
||||
},
|
||||
{
|
||||
link: 'ml:notifications',
|
||||
},
|
||||
{
|
||||
link: 'ml:memoryUsage',
|
||||
},
|
||||
{
|
||||
title: i18n.translate('defaultNavigation.ml.anomalyDetection', {
|
||||
defaultMessage: 'Anomaly Detection',
|
||||
|
|
|
@ -28,7 +28,10 @@ export type IntegrationsDeepLinkId = IntegrationsAppId | FleetAppId | OsQueryApp
|
|||
// Management
|
||||
export type ManagementAppId = typeof MANAGEMENT_APP_ID;
|
||||
export type ManagementId =
|
||||
| 'ad_settings'
|
||||
| 'aiAssistantManagementSelection'
|
||||
| 'analytics'
|
||||
| 'anomaly_detection'
|
||||
| 'securityAiAssistantManagement'
|
||||
| 'observabilityAiAssistantManagement'
|
||||
| 'api_keys'
|
||||
|
@ -46,6 +49,7 @@ export type ManagementId =
|
|||
| 'maintenanceWindows'
|
||||
| 'migrate_data'
|
||||
| 'objects'
|
||||
| 'overview'
|
||||
| 'pipelines'
|
||||
| 'remote_clusters'
|
||||
| 'reporting'
|
||||
|
@ -56,7 +60,9 @@ export type ManagementId =
|
|||
| 'settings'
|
||||
| 'snapshot_restore'
|
||||
| 'spaces'
|
||||
| 'supplied_configurations'
|
||||
| 'tags'
|
||||
| 'trained_models'
|
||||
| 'transform'
|
||||
| 'triggersActions'
|
||||
| 'triggersActionsConnectors'
|
||||
|
|
|
@ -34,6 +34,14 @@ const insightsAndAlertingTip = i18n.translate('management.sections.insightsAndAl
|
|||
defaultMessage: 'Manage how to detect changes in your data',
|
||||
});
|
||||
|
||||
const machineLearningTitle = i18n.translate('management.sections.machineLearningTitle', {
|
||||
defaultMessage: 'Machine Learning',
|
||||
});
|
||||
|
||||
const machineLearningTip = i18n.translate('management.sections.machineLearningTip', {
|
||||
defaultMessage: 'Manage your Machine Learning jobs and trained models',
|
||||
});
|
||||
|
||||
const sectionTitle = i18n.translate('management.sections.section.title', {
|
||||
defaultMessage: 'Security',
|
||||
});
|
||||
|
@ -79,6 +87,13 @@ export const InsightsAndAlertingSection = {
|
|||
order: 2,
|
||||
};
|
||||
|
||||
export const MachineLearningSection = {
|
||||
id: ManagementSectionId.MachineLearning,
|
||||
title: machineLearningTitle,
|
||||
tip: machineLearningTip,
|
||||
order: 4,
|
||||
};
|
||||
|
||||
export const SecuritySection = {
|
||||
id: 'security',
|
||||
title: sectionTitle,
|
||||
|
@ -104,6 +119,7 @@ export const managementSections = [
|
|||
IngestSection,
|
||||
DataSection,
|
||||
InsightsAndAlertingSection,
|
||||
MachineLearningSection,
|
||||
SecuritySection,
|
||||
KibanaSection,
|
||||
StackSection,
|
||||
|
|
|
@ -30,7 +30,7 @@ describe('ManagementService', () => {
|
|||
managementService.start({ capabilities });
|
||||
const start = getSectionsServiceStartPrivate();
|
||||
|
||||
expect(start.getSectionsEnabled().length).toEqual(6);
|
||||
expect(start.getSectionsEnabled().length).toEqual(7);
|
||||
});
|
||||
|
||||
test('Register section, enable and disable', () => {
|
||||
|
@ -44,11 +44,11 @@ describe('ManagementService', () => {
|
|||
managementService.start({ capabilities });
|
||||
const start = getSectionsServiceStartPrivate();
|
||||
|
||||
expect(start.getSectionsEnabled().length).toEqual(7);
|
||||
expect(start.getSectionsEnabled().length).toEqual(8);
|
||||
|
||||
testSection.disable();
|
||||
|
||||
expect(start.getSectionsEnabled().length).toEqual(6);
|
||||
expect(start.getSectionsEnabled().length).toEqual(7);
|
||||
});
|
||||
|
||||
test('Disables items that are not allowed by Capabilities', () => {
|
||||
|
|
|
@ -13,6 +13,7 @@ import {
|
|||
IngestSection,
|
||||
DataSection,
|
||||
InsightsAndAlertingSection,
|
||||
MachineLearningSection,
|
||||
SecuritySection,
|
||||
KibanaSection,
|
||||
StackSection,
|
||||
|
@ -41,6 +42,7 @@ export class ManagementSectionsService {
|
|||
ingest: this.registerSection(IngestSection),
|
||||
data: this.registerSection(DataSection),
|
||||
insightsAndAlerting: this.registerSection(InsightsAndAlertingSection),
|
||||
machineLearning: this.registerSection(MachineLearningSection),
|
||||
security: this.registerSection(SecuritySection),
|
||||
kibana: this.registerSection(KibanaSection),
|
||||
stack: this.registerSection(StackSection),
|
||||
|
|
|
@ -31,6 +31,7 @@ export interface DefinedSections {
|
|||
ingest: ManagementSection;
|
||||
data: ManagementSection;
|
||||
insightsAndAlerting: ManagementSection;
|
||||
machineLearning: ManagementSection;
|
||||
security: ManagementSection;
|
||||
kibana: ManagementSection;
|
||||
stack: ManagementSection;
|
||||
|
@ -65,6 +66,7 @@ export enum ManagementSectionId {
|
|||
Ingest = 'ingest',
|
||||
Data = 'data',
|
||||
InsightsAndAlerting = 'insightsAndAlerting',
|
||||
MachineLearning = 'ml',
|
||||
Security = 'security',
|
||||
Kibana = 'kibana',
|
||||
Stack = 'stack',
|
||||
|
|
|
@ -104,6 +104,10 @@ interface DatePickerWrapperProps {
|
|||
* Tooltip message for the update button
|
||||
*/
|
||||
tooltipMessage?: string;
|
||||
/**
|
||||
* Data test subject for the refresh button
|
||||
*/
|
||||
dataTestSubj?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -123,6 +127,7 @@ export const DatePickerWrapper: FC<DatePickerWrapperProps> = (props) => {
|
|||
needsUpdate,
|
||||
onRefresh,
|
||||
tooltipMessage,
|
||||
dataTestSubj = 'mlDatePickerRefreshPageButton',
|
||||
} = props;
|
||||
const {
|
||||
data,
|
||||
|
@ -337,7 +342,7 @@ export const DatePickerWrapper: FC<DatePickerWrapperProps> = (props) => {
|
|||
color={needsUpdate ? 'accentSecondary' : 'primary'}
|
||||
iconType={needsUpdate ? 'kqlFunction' : 'refresh'}
|
||||
onClick={handleRefresh}
|
||||
data-test-subj={`mlDatePickerRefreshPageButton${isLoading ? ' loading' : ' loaded'}`}
|
||||
data-test-subj={`${dataTestSubj}${isLoading ? ' loading' : ' loaded'}`}
|
||||
isLoading={isLoading}
|
||||
isDisabled={isDisabled}
|
||||
>
|
||||
|
|
|
@ -128,7 +128,7 @@ export function WelcomeMessageKnowledgeBaseSetupErrorPanel({
|
|||
<EuiLink
|
||||
data-test-subj="observabilityAiAssistantWelcomeMessageTrainedModelsLink"
|
||||
external
|
||||
href={http?.basePath.prepend('/app/ml/trained_models')}
|
||||
href={http?.basePath.prepend('/app/management/ml/trained_models')}
|
||||
target="_blank"
|
||||
>
|
||||
{i18n.translate('xpack.aiAssistant.welcomeMessage.trainedModelsLinkLabel', {
|
||||
|
|
|
@ -27163,7 +27163,6 @@
|
|||
"xpack.ml.anomalyDetectionAlert.name": "Détection des anomalies",
|
||||
"xpack.ml.anomalyDetectionAlert.topNBucketsDescription": "Nombre de groupes les plus récents à vérifier pour obtenir l'anomalie la plus élevée.",
|
||||
"xpack.ml.anomalyDetectionAlert.topNBucketsLabel": "Nombre de groupes les plus récents",
|
||||
"xpack.ml.anomalyDetectionBreadcrumbLabel": "Détection des anomalies",
|
||||
"xpack.ml.anomalyExplorerPageLabel": "Anomaly Explorer (Explorateur d'anomalies)",
|
||||
"xpack.ml.anomalyResultsViewSelector.anomalyExplorerLabel": "Voir les résultats dans Anomaly Explorer",
|
||||
"xpack.ml.anomalyResultsViewSelector.buttonGroupLegend": "Sélecteur de vue des résultats d'anomalie",
|
||||
|
@ -27301,7 +27300,6 @@
|
|||
"xpack.ml.controls.selectSeverity.minorLabel": "mineure",
|
||||
"xpack.ml.controls.selectSeverity.scoreDetailsDescription": "score {value} et supérieur",
|
||||
"xpack.ml.controls.selectSeverity.warningLabel": "avertissement",
|
||||
"xpack.ml.createJobsBreadcrumbLabel": "Créer une tâche",
|
||||
"xpack.ml.creationWizardUtils.destinationIndexInputAriaLabel": "Choisissez un nom d'index de destination unique.",
|
||||
"xpack.ml.creationWizardUtils.destinationIndexInvalidError": "Nom d'index de destination non valide.",
|
||||
"xpack.ml.creationWizardUtils.destinationIndexInvalidErrorLink": "Découvrez les limitations relatives aux noms d'index.",
|
||||
|
@ -27869,7 +27867,6 @@
|
|||
"xpack.ml.dataFrameAnalyticsBreadcrumbs.dataViewLabel": "Vue de données",
|
||||
"xpack.ml.dataFrameAnalyticsBreadcrumbs.esqlLabel": "Visualiseur de données pour les index (ES|QL)",
|
||||
"xpack.ml.dataFrameAnalyticsBreadcrumbs.jobsManagementLabel": "Tâches",
|
||||
"xpack.ml.dataFrameAnalyticsLabel": "Analyse du cadre de données",
|
||||
"xpack.ml.dataGrid.CcsWarningCalloutBody": "Un problème est survenu lors de la récupération des données pour la vue de données. L'aperçu de la source combiné avec la recherche inter-clusters est uniquement pris en charge pour les versions 7.10 et supérieures. Toutefois, vous pouvez toujours configurer et créer la transformation.",
|
||||
"xpack.ml.dataGrid.CcsWarningCalloutTitle": "La recherche inter-clusters n'a renvoyé aucune donnée de champ.",
|
||||
"xpack.ml.dataGrid.columnChart.ErrorMessageToast": "Une erreur s'est produite lors de la récupération des données de l'histogramme : {error}",
|
||||
|
@ -27904,8 +27901,6 @@
|
|||
"xpack.ml.dataVisualizer.pageHeader": "Data Visualizer (Visualiseur de données)",
|
||||
"xpack.ml.datavisualizer.selector.dataVisualizerDescription": "L'outil de Machine Learning Data Visualizer (Visualiseur de données) vous aide à comprendre vos données en analysant les indicateurs et les champs dans un fichier log ou un index Elasticsearch existant.",
|
||||
"xpack.ml.datavisualizer.selector.dataVisualizerTitle": "Data Visualizer (Visualiseur de données)",
|
||||
"xpack.ml.datavisualizer.selector.esqlTechnicalPreviewBadge.titleMsg": "Le visualiseur de données ES|QL est en version préliminaire technique.",
|
||||
"xpack.ml.datavisualizer.selector.importDataDescription": "Importez les données à partir d'un fichier log. Vous pouvez charger des fichiers d'une taille allant jusqu'à {maxFileSize}.",
|
||||
"xpack.ml.datavisualizer.selector.importDataTitle": "Visualiser les données à partir d'un fichier",
|
||||
"xpack.ml.datavisualizer.selector.selectDataViewButtonLabel": "Sélectionner la vue de données",
|
||||
"xpack.ml.datavisualizer.selector.selectDataViewTitle": "Visualiser les données à partir d'une vue de données",
|
||||
|
@ -27937,27 +27932,21 @@
|
|||
"xpack.ml.deepLink.analyticsMap": "Mapping d'analyse",
|
||||
"xpack.ml.deepLink.anomalyDetection": "Détection des anomalies",
|
||||
"xpack.ml.deepLink.anomalyExplorer": "Explorateur d'anomalies",
|
||||
"xpack.ml.deepLink.calendarSettings": "Calendriers",
|
||||
"xpack.ml.deepLink.changePointDetection": "Modifier la détection du point",
|
||||
"xpack.ml.deepLink.dataDrift": "Dérive de données",
|
||||
"xpack.ml.deepLink.dataFrameAnalytics": "Analyse du cadre de données",
|
||||
"xpack.ml.deepLink.dataVisualizer": "Data Visualizer (Visualiseur de données)",
|
||||
"xpack.ml.deepLink.esqlDataVisualizer": "Visualiseur de données ES|QL",
|
||||
"xpack.ml.deepLink.fileUpload": "Chargement du fichier",
|
||||
"xpack.ml.deepLink.filterListsSettings": "Listes de filtres",
|
||||
"xpack.ml.deepLink.indexDataVisualizer": "Index Data Visualizer (Visualiseur de données pour les index)",
|
||||
"xpack.ml.deepLink.logPatternAnalysis": "Analyse du modèle de log",
|
||||
"xpack.ml.deepLink.logRateAnalysis": "Analyse du Taux de Log",
|
||||
"xpack.ml.deepLink.memoryUsage": "Utilisation mémoire",
|
||||
"xpack.ml.deepLink.modelManagement": "Gestion des modèles",
|
||||
"xpack.ml.deepLink.nodes": "Nœuds",
|
||||
"xpack.ml.deepLink.notifications": "Notifications",
|
||||
"xpack.ml.deepLink.overview": "Aperçu",
|
||||
"xpack.ml.deepLink.resultExplorer": "Explorateur de résultats",
|
||||
"xpack.ml.deepLink.settings": "Paramètres",
|
||||
"xpack.ml.deepLink.singleMetricViewer": "Visionneuse d’indicateur unique",
|
||||
"xpack.ml.deepLink.suppliedConfigurations": "Configurations fournies",
|
||||
"xpack.ml.deepLink.trainedModels": "Modèles entraînés",
|
||||
"xpack.ml.deleteSpaceAwareItemCheckModal.buttonTextCanDelete.job": "Continuer pour supprimer {length, plural, one {# tâche} other {# tâches}}",
|
||||
"xpack.ml.deleteSpaceAwareItemCheckModal.buttonTextCanDelete.model": "Continuer pour supprimer {length, plural, one {# modèle} other {# modèles}}",
|
||||
"xpack.ml.deleteSpaceAwareItemCheckModal.buttonTextCanUnTagConfirm": "Retirer de l'espace en cours",
|
||||
|
@ -28179,11 +28168,6 @@
|
|||
"xpack.ml.importExport.exportFlyout.exportJobDependenciesWarningCallout.jobUsingCalendarsButton": "Tâches utilisant des calendriers",
|
||||
"xpack.ml.importExport.exportFlyout.exportJobDependenciesWarningCallout.jobUsingFiltersAria": "Tâches utilisant des listes de filtres",
|
||||
"xpack.ml.importExport.exportFlyout.exportJobDependenciesWarningCallout.jobUsingFiltersButton": "Tâches utilisant des listes de filtres",
|
||||
"xpack.ml.importExport.exportFlyout.flyoutHeader": "Exporter les tâches",
|
||||
"xpack.ml.importExport.exportFlyout.switchTabsConfirm.cancelButton": "Annuler",
|
||||
"xpack.ml.importExport.exportFlyout.switchTabsConfirm.confirmButton": "Confirmer",
|
||||
"xpack.ml.importExport.exportFlyout.switchTabsConfirm.text": "Le changement d'onglets effacera la sélection actuelle des tâches",
|
||||
"xpack.ml.importExport.exportFlyout.switchTabsConfirm.title": "Changer d'onglets ?",
|
||||
"xpack.ml.importExport.importButton": "Importer les tâches",
|
||||
"xpack.ml.importExport.importFlyout.cannotImportJobCallout.jobListAria": "afficher les tâches",
|
||||
"xpack.ml.importExport.importFlyout.cannotImportJobCallout.jobListButton": "Afficher les tâches",
|
||||
|
@ -28594,7 +28578,6 @@
|
|||
"xpack.ml.management.jobsList.noPermissionToAccessLabel": "Accès refusé",
|
||||
"xpack.ml.management.jobsList.syncFlyoutButton": "Synchroniser les objets enregistrés",
|
||||
"xpack.ml.management.jobsList.trainedModelsDocsLabel": "Documents relatifs aux modèles entraînés",
|
||||
"xpack.ml.management.jobsListTitle": "Machine Learning",
|
||||
"xpack.ml.management.jobsSpacesList.jobObjectNoun": "tâche",
|
||||
"xpack.ml.management.jobsSpacesList.modelObjectNoun": "modèle entraîné",
|
||||
"xpack.ml.management.jobsSpacesList.updateSpaces.error": "Erreur lors de la mise à jour de {id}",
|
||||
|
@ -28666,9 +28649,6 @@
|
|||
"xpack.ml.mapsAnomaliesLayerEmptyPrompt.createJobButtonText": "Créer une tâche",
|
||||
"xpack.ml.mapsAnomaliesLayerEmptyPrompt.createJobMessage": "Créer une tâche de détection des anomalies",
|
||||
"xpack.ml.mapsAnomaliesLayerEmptyPrompt.emptyPromptText": "La détection des anomalies permet d'identifier un comportement inhabituel dans des données géographiques. Créez une tâche utilisant la fonction lat_long, requise pour la couche d'anomalies de mapping.",
|
||||
"xpack.ml.memoryUsage.memoryTab": "Utilisation mémoire",
|
||||
"xpack.ml.memoryUsage.memoryUsageHeader": "Utilisation mémoire",
|
||||
"xpack.ml.memoryUsage.nodesTab": "Nœuds",
|
||||
"xpack.ml.memoryUsage.treeMap.adLabel": "Tâches de détection des anomalies",
|
||||
"xpack.ml.memoryUsage.treeMap.dfaLabel": "Tâches d'analyse du cadre de données",
|
||||
"xpack.ml.memoryUsage.treeMap.emptyPrompt": "Aucune tâche ouverte ni aucun modèle entraîné ne correspond à la sélection actuelle.",
|
||||
|
@ -28679,10 +28659,8 @@
|
|||
"xpack.ml.mlEntitySelector.dfaOptionsLabel": "Analyse du cadre de données",
|
||||
"xpack.ml.mlEntitySelector.fetchError": "Impossible de récupérer les entités de ML",
|
||||
"xpack.ml.mlEntitySelector.trainedModelsLabel": "Modèles entraînés",
|
||||
"xpack.ml.modelManagement.memoryUsage.docTitle": "Utilisation mémoire",
|
||||
"xpack.ml.modelManagement.trainedModels.docTitle": "Modèles entraînés",
|
||||
"xpack.ml.modelManagement.trainedModelsHeader": "Modèles entraînés",
|
||||
"xpack.ml.modelManagementLabel": "Gestion des modèles",
|
||||
"xpack.ml.models.dfaValidation.messages.analysisFieldsEmptyWarningText": "Certains champs inclus pour l'analyse ont au moins {percentEmpty} % de valeurs vides et peuvent ne pas être adaptés à l'analyse.",
|
||||
"xpack.ml.models.dfaValidation.messages.analysisFieldsHeading": "Champs d'analyse",
|
||||
"xpack.ml.models.dfaValidation.messages.analysisFieldsHighWarningText": "Plus de {includedFieldsThreshold} champs sont sélectionnés pour l'analyse. Cela peut augmenter l'utilisation des ressources et allonger le temps d'exécution des tâches.",
|
||||
|
@ -28851,31 +28829,21 @@
|
|||
"xpack.ml.multiSelectPicker.NoFiltersFoundMessage": "Aucun filtre trouvé",
|
||||
"xpack.ml.navMenu.aiopsTabLinkText": "AIOps Labs",
|
||||
"xpack.ml.navMenu.anomalyDetection.anomalyExplorerText": "Anomaly Explorer (Explorateur d'anomalies)",
|
||||
"xpack.ml.navMenu.anomalyDetection.jobsManagementText": "Tâches",
|
||||
"xpack.ml.navMenu.anomalyDetection.singleMetricViewerText": "Single Metric Viewer (Visionneuse d'indicateur unique)",
|
||||
"xpack.ml.navMenu.anomalyDetection.suppliedConfigurationsLinkText": "Configurations fournies",
|
||||
"xpack.ml.navMenu.anomalyDetectionTabLinkText": "Détection des anomalies",
|
||||
"xpack.ml.navMenu.changePointDetectionLinkText": "Modifier la détection du point",
|
||||
"xpack.ml.navMenu.dataComparisonText": "Dérive de données",
|
||||
"xpack.ml.navMenu.dataFrameAnalytics.analyticsMapText": "Mapping d'analyse",
|
||||
"xpack.ml.navMenu.dataFrameAnalytics.jobsManagementText": "Tâches",
|
||||
"xpack.ml.navMenu.dataFrameAnalytics.resultsExplorerText": "Explorateur de résultats",
|
||||
"xpack.ml.navMenu.dataFrameAnalyticsTabLinkText": "Analyse du cadre de données",
|
||||
"xpack.ml.navMenu.dataViewDataVisualizerLinkText": "Vue de données",
|
||||
"xpack.ml.navMenu.dataVisualizerTabLinkText": "Data Visualizer (Visualiseur de données)",
|
||||
"xpack.ml.navMenu.esqlDataVisualizerLinkText": "ES|QL",
|
||||
"xpack.ml.navMenu.fileDataVisualizerLinkText": "Fichier",
|
||||
"xpack.ml.navMenu.logCategorizationLinkText": "Analyse du modèle de log",
|
||||
"xpack.ml.navMenu.logRateAnalysisLinkText": "Analyse du Taux de Log",
|
||||
"xpack.ml.navMenu.memoryUsageText": "Utilisation mémoire",
|
||||
"xpack.ml.navMenu.mlAppNameText": "Machine Learning et Analytique",
|
||||
"xpack.ml.navMenu.modelManagementText": "Gestion des modèles",
|
||||
"xpack.ml.navMenu.notificationsTabLinkText": "Notifications",
|
||||
"xpack.ml.navMenu.overviewTabLinkText": "Aperçu",
|
||||
"xpack.ml.navMenu.settingsTabLinkText": "Paramètres",
|
||||
"xpack.ml.navMenu.trainedModelsTabBetaLabel": "Version d’évaluation technique",
|
||||
"xpack.ml.navMenu.trainedModelsTabBetaTooltipContent": "Cette fonctionnalité est en version d'évaluation technique et pourra être modifiée ou retirée complètement dans une future version. Elastic s'efforcera de corriger tout problème, mais les fonctionnalités des versions d'évaluation technique ne sont pas soumises aux SLA de support des fonctionnalités officielles en disponibilité générale.",
|
||||
"xpack.ml.navMenu.trainedModelsText": "Modèles entraînés",
|
||||
"xpack.ml.newJob.fromGeo.createJob.error.noTimeRange": "Plage temporelle non spécifiée.",
|
||||
"xpack.ml.newJob.fromLens.createJob.error.colsNoSourceField": "Certaines colonnes ne contiennent pas de champ source.",
|
||||
"xpack.ml.newJob.fromLens.createJob.error.colsUsingFilterTimeSift": "Les colonnes contenant des paramètres incompatibles avec les détecteurs de ML, le décalage temporel et la fonction Filtrer par ne sont pas prises en charge.",
|
||||
|
@ -29380,7 +29348,6 @@
|
|||
"xpack.ml.overview.nodesPanel.header": "Nœuds",
|
||||
"xpack.ml.overview.nodesPanel.totalNodesLabel": "Total",
|
||||
"xpack.ml.overview.nodesPanel.viewNodeLink": "Afficher les nœuds",
|
||||
"xpack.ml.overview.notificationsLabel": "Notifications",
|
||||
"xpack.ml.overview.overviewLabel": "Aperçu",
|
||||
"xpack.ml.overview.statsBar.failedAnalyticsLabel": "Échoué",
|
||||
"xpack.ml.overview.statsBar.runningAnalyticsLabel": "En cours d'exécution",
|
||||
|
@ -29509,7 +29476,6 @@
|
|||
"xpack.ml.sampleDataLinkLabel": "Tâches de ML",
|
||||
"xpack.ml.savedObjectFinder.createADataView": "Créer une vue de données",
|
||||
"xpack.ml.selectDataViewLabel": "Sélectionner la vue de données",
|
||||
"xpack.ml.settings.anomalyDetection.anomalyDetectionTitle": "Détection des anomalies",
|
||||
"xpack.ml.settings.anomalyDetection.calendarsDstSummaryCount": "Vous avez {calendarsCountBadge} {calendarsDstCount, plural, one {calendrier} other {calendriers}}",
|
||||
"xpack.ml.settings.anomalyDetection.calendarsDstText": "Les calendriers DST contiennent une liste d'événements programmés pour lesquels vous ne souhaitez pas générer d'anomalies, en tenant compte des décalages de l'heure d'été qui peuvent entraîner l'apparition d'événements une heure plus tôt ou plus tard.",
|
||||
"xpack.ml.settings.anomalyDetection.calendarsDstTitle": "Calendriers DST",
|
||||
|
@ -29529,11 +29495,9 @@
|
|||
"xpack.ml.settings.anomalyDetection.manageFilterListsLink": "Gérer",
|
||||
"xpack.ml.settings.breadcrumbs.calendarManagement.createLabel": "Créer",
|
||||
"xpack.ml.settings.breadcrumbs.calendarManagement.editLabel": "Modifier",
|
||||
"xpack.ml.settings.breadcrumbs.calendarManagementLabel": "Gestion du calendrier DST",
|
||||
"xpack.ml.settings.breadcrumbs.dataComparisonLabel": "Dérive de données",
|
||||
"xpack.ml.settings.breadcrumbs.filterLists.createLabel": "Créer",
|
||||
"xpack.ml.settings.breadcrumbs.filterLists.editLabel": "Modifier",
|
||||
"xpack.ml.settings.breadcrumbs.filterListsLabel": "Liste de filtres",
|
||||
"xpack.ml.settings.calendarList.docTitle": "Calendriers",
|
||||
"xpack.ml.settings.calendars.listHeader.calendarsDescription": "Les calendriers contiennent une liste d'événements programmés pour lesquels vous ne souhaitez pas générer d'anomalies, tels que des pannes système planifiées ou les jours fériés. Un même calendrier peut être affecté à plusieurs tâches.{br}{learnMoreLink}",
|
||||
"xpack.ml.settings.calendars.listHeader.calendarsDescription.learnMoreLinkText": "En savoir plus",
|
||||
|
@ -29593,7 +29557,6 @@
|
|||
"xpack.ml.settings.filterLists.table.noFiltersCreatedTitle": "Aucun filtre n'a été créé",
|
||||
"xpack.ml.settings.filterLists.table.notInUseAriaLabel": "Pas en cours d'utilisation",
|
||||
"xpack.ml.settings.filterLists.toolbar.deleteItemButtonLabel": "Supprimer un élément",
|
||||
"xpack.ml.settings.title": "Paramètres",
|
||||
"xpack.ml.settingsBreadcrumbLabel": "Paramètres",
|
||||
"xpack.ml.severitySelector.formControlAriaLabel": "Sélectionner le seuil de sévérité",
|
||||
"xpack.ml.severitySelector.formControlLabel": "Sévérité",
|
||||
|
@ -29628,8 +29591,6 @@
|
|||
"xpack.ml.suppliedConfigurations.preconfigurecJobsHeader": "Configurations fournies",
|
||||
"xpack.ml.suppliedConfigurations.preconfigurecJobsHeaderDescription": "Cette page répertorie les configurations de tâches de détection d'anomalies prédéfinies avec les ressources Kibana associées.",
|
||||
"xpack.ml.suppliedConfigurations.suppliedConfigurations.docTitle": "Configurations fournies",
|
||||
"xpack.ml.suppliedConfigurationsBreadcrumbs.suppliedConfigurationsLabel": "Configurations fournies",
|
||||
"xpack.ml.suppliedConfigurationsLabel": "Configurations fournies",
|
||||
"xpack.ml.swimlaneEmbeddable.errorMessage": "Impossible de charger les données du couloir de ML",
|
||||
"xpack.ml.swimlaneEmbeddable.noDataFound": "Aucune anomalie n'a été trouvée",
|
||||
"xpack.ml.swimlaneEmbeddable.panelTitleLabel": "Titre du panneau",
|
||||
|
@ -30131,7 +30092,6 @@
|
|||
"xpack.ml.trainedModels.testModelsFlyout.zeroShotClassification.inputText": "Entrer une expression à tester",
|
||||
"xpack.ml.trainedModels.testModelsFlyout.zeroShotClassification.label": "Classification Zero-Shot",
|
||||
"xpack.ml.trainedModelsBreadcrumbs.dataDriftLabel": "Dérive de données",
|
||||
"xpack.ml.trainedModelsBreadcrumbs.nodeOverviewLabel": "Utilisation mémoire",
|
||||
"xpack.ml.trainedModelsBreadcrumbs.trainedModelsLabel": "Modèles entraînés",
|
||||
"xpack.ml.upgrade.upgradeWarning.upgradeInProgressWarningDescription": "Les index associés au Machine Learning sont actuellement en cours de mise à niveau.",
|
||||
"xpack.ml.upgrade.upgradeWarning.upgradeInProgressWarningDescriptionExtra": "Certaines actions ne seront pas disponibles pendant cette opération.",
|
||||
|
@ -32425,23 +32385,13 @@
|
|||
"xpack.observability.obltNav.infrastructure.metricsExplorer": "Explorateur d'indicateurs",
|
||||
"xpack.observability.obltNav.infrastructure.universalProfiling": "Profilage universel",
|
||||
"xpack.observability.obltNav.machineLearning": "Machine Learning",
|
||||
"xpack.observability.obltNav.machineLearning.memoryUsage": "Utilisation mémoire",
|
||||
"xpack.observability.obltNav.management": "Gestion",
|
||||
"xpack.observability.obltNav.ml.aiops_labs": "Ateliers AIOps",
|
||||
"xpack.observability.obltNav.ml.aiops_labs.change_point_detection": "Modifier la détection du point",
|
||||
"xpack.observability.obltNav.ml.aiops_labs.log_pattern_analysis": "Analyse du modèle de log",
|
||||
"xpack.observability.obltNav.ml.aiops_labs.log_rate_analysis": "Analyse du taux de log",
|
||||
"xpack.observability.obltNav.ml.anomaly_detection": "Détection des anomalies",
|
||||
"xpack.observability.obltNav.ml.anomaly_detection.jobs": "Tâches",
|
||||
"xpack.observability.obltNav.ml.data_frame_analytics": "Analyse du cadre de données",
|
||||
"xpack.observability.obltNav.ml.data_frame_analytics.jobs": "Tâches",
|
||||
"xpack.observability.obltNav.ml.data_visualizer": "Data Visualizer (Visualiseur de données)",
|
||||
"xpack.observability.obltNav.ml.data_visualizer.data_drift": "Dérive de données",
|
||||
"xpack.observability.obltNav.ml.data_visualizer.data_view_data_visualizer": "Data Visualizer (Visualiseur de données)",
|
||||
"xpack.observability.obltNav.ml.data_visualizer.esql_data_visualizer": "Visualiseur de données ES|QL",
|
||||
"xpack.observability.obltNav.ml.data_visualizer.file_data_visualizer": "File Data Visualizer (Visualiseur de données pour les fichiers)",
|
||||
"xpack.observability.obltNav.ml.model_management": "Gestion des modèles",
|
||||
"xpack.observability.obltNav.ml.model_management.trainedModels": "Modèles entraînés",
|
||||
"xpack.observability.obltNav.otherTools": "Autres outils",
|
||||
"xpack.observability.obltNav.otherTools.logsAnomalies": "Anomalies des logs",
|
||||
"xpack.observability.obltNav.otherTools.logsCategories": "Bibliothèque Visualize",
|
||||
|
@ -32860,20 +32810,6 @@
|
|||
"xpack.observabilityShared.bottomBarActions.unsavedChanges": "{unsavedChangesCount, plural, =0{0 modification non enregistrée} one {1 modification non enregistrée} other {# modifications non enregistrées}}",
|
||||
"xpack.observabilityShared.breadcrumbs.observabilityLinkText": "Observabilité",
|
||||
"xpack.observabilityShared.common.constants.grouping": "Observabilité",
|
||||
"xpack.observabilityShared.experimentalOnboardingFlow.browseDocumentationFlexItemDescription": "Guides détaillés des fonctionnalités d'Elastic",
|
||||
"xpack.observabilityShared.experimentalOnboardingFlow.browseDocumentationFlexItemLabel": "Parcourir la documentation",
|
||||
"xpack.observabilityShared.experimentalOnboardingFlow.browseDocumentationFlexItemLinkARIALabel": "En savoir plus sur toutes les fonctionnalités d'Elastic",
|
||||
"xpack.observabilityShared.experimentalOnboardingFlow.browseDocumentationFlexItemLinkLabel": "En savoir plus",
|
||||
"xpack.observabilityShared.experimentalOnboardingFlow.demoEnvironmentFlexItemDescription": "Explorer notre environnement de démonstration en direct",
|
||||
"xpack.observabilityShared.experimentalOnboardingFlow.demoEnvironmentFlexItemLabel": "Environnement de démonstration",
|
||||
"xpack.observabilityShared.experimentalOnboardingFlow.demoEnvironmentFlexItemLinkLabel": "Explorer la démonstration",
|
||||
"xpack.observabilityShared.experimentalOnboardingFlow.exploreForumFlexItemDescription": "Échanger à propos d'Elastic",
|
||||
"xpack.observabilityShared.experimentalOnboardingFlow.exploreForumFlexItemLabel": "Explorer le forum",
|
||||
"xpack.observabilityShared.experimentalOnboardingFlow.exploreForumFlexItemLinkARIALabel": "Ouvrir le forum de discussion sur Elastic",
|
||||
"xpack.observabilityShared.experimentalOnboardingFlow.exploreForumFlexItemLinkLabel": "Forum de discussion",
|
||||
"xpack.observabilityShared.experimentalOnboardingFlow.supportHubFlexItemDescription": "Obtenez de l'aide dans l’ouverture d’un cas",
|
||||
"xpack.observabilityShared.experimentalOnboardingFlow.supportHubFlexItemLabel": "Hub de support technique",
|
||||
"xpack.observabilityShared.experimentalOnboardingFlow.supportHubFlexItemLinkLabel": "Ouvrir le Hub de support technique",
|
||||
"xpack.observabilityShared.featureFeedbackButton.tellUsWhatYouThinkLink": "Dites-nous ce que vous pensez !",
|
||||
"xpack.observabilityShared.fieldValueSelection.apply": "Appliquer",
|
||||
"xpack.observabilityShared.fieldValueSelection.apply.label": "Appliquer les filtres sélectionnés pour {label}",
|
||||
|
@ -41874,21 +41810,12 @@
|
|||
"xpack.serverlessObservability.nav.infrastructure": "Infrastructure",
|
||||
"xpack.serverlessObservability.nav.infrastructureInventory": "Inventaire de l'infrastructure",
|
||||
"xpack.serverlessObservability.nav.machineLearning": "Machine Learning",
|
||||
"xpack.serverlessObservability.nav.machineLearning.memoryUsage": "Utilisation mémoire",
|
||||
"xpack.serverlessObservability.nav.ml.aiops_labs": "Ateliers AIOps",
|
||||
"xpack.serverlessObservability.nav.ml.aiops_labs.change_point_detection": "Modifier la détection du point",
|
||||
"xpack.serverlessObservability.nav.ml.aiops_labs.log_pattern_analysis": "Analyse du modèle de log",
|
||||
"xpack.serverlessObservability.nav.ml.aiops_labs.log_rate_analysis": "Analyse du taux de log",
|
||||
"xpack.serverlessObservability.nav.ml.anomaly_detection": "Détection des anomalies",
|
||||
"xpack.serverlessObservability.nav.ml.anomaly_detection.jobs": "Tâches",
|
||||
"xpack.serverlessObservability.nav.ml.data_frame_analytics": "Analyse du cadre de données",
|
||||
"xpack.serverlessObservability.nav.ml.data_frame_analytics.jobs": "Tâches",
|
||||
"xpack.serverlessObservability.nav.ml.data_visualizer": "Data Visualizer (Visualiseur de données)",
|
||||
"xpack.serverlessObservability.nav.ml.data_visualizer.data_drift": "Dérive de données",
|
||||
"xpack.serverlessObservability.nav.ml.data_visualizer.data_view_data_visualizer": "Data Visualizer (Visualiseur de données)",
|
||||
"xpack.serverlessObservability.nav.ml.data_visualizer.file_data_visualizer": "File Data Visualizer (Visualiseur de données pour les fichiers)",
|
||||
"xpack.serverlessObservability.nav.ml.model_management": "Gestion des modèles",
|
||||
"xpack.serverlessObservability.nav.ml.model_management.trainedModels": "Modèles entraînés",
|
||||
"xpack.serverlessObservability.nav.mngt": "Gestion",
|
||||
"xpack.serverlessObservability.nav.mngt.access": "Accès",
|
||||
"xpack.serverlessObservability.nav.mngt.alertsAndInsights": "Alertes et informations exploitables",
|
||||
|
|
|
@ -27144,7 +27144,6 @@
|
|||
"xpack.ml.anomalyDetectionAlert.name": "異常検知",
|
||||
"xpack.ml.anomalyDetectionAlert.topNBucketsDescription": "最高の異常を取得するために確認する最新のバケット数。",
|
||||
"xpack.ml.anomalyDetectionAlert.topNBucketsLabel": "最新のバケット数",
|
||||
"xpack.ml.anomalyDetectionBreadcrumbLabel": "異常検知",
|
||||
"xpack.ml.anomalyExplorerPageLabel": "異常エクスプローラー",
|
||||
"xpack.ml.anomalyResultsViewSelector.anomalyExplorerLabel": "異常エクスプローラーで結果を表示",
|
||||
"xpack.ml.anomalyResultsViewSelector.buttonGroupLegend": "異常結果ビューセレクター",
|
||||
|
@ -27281,7 +27280,6 @@
|
|||
"xpack.ml.controls.selectSeverity.minorLabel": "マイナー",
|
||||
"xpack.ml.controls.selectSeverity.scoreDetailsDescription": "スコア{value}以上",
|
||||
"xpack.ml.controls.selectSeverity.warningLabel": "警告",
|
||||
"xpack.ml.createJobsBreadcrumbLabel": "ジョブを作成",
|
||||
"xpack.ml.creationWizardUtils.destinationIndexInputAriaLabel": "固有の宛先インデックス名を選択してください。",
|
||||
"xpack.ml.creationWizardUtils.destinationIndexInvalidError": "無効なデスティネーションインデックス名。",
|
||||
"xpack.ml.creationWizardUtils.destinationIndexInvalidErrorLink": "インデックス名の制限に関する詳細。",
|
||||
|
@ -27847,7 +27845,6 @@
|
|||
"xpack.ml.dataFrameAnalyticsBreadcrumbs.dataViewLabel": "データビュー",
|
||||
"xpack.ml.dataFrameAnalyticsBreadcrumbs.esqlLabel": "インデックスデータビジュアライザー(ES|QL)",
|
||||
"xpack.ml.dataFrameAnalyticsBreadcrumbs.jobsManagementLabel": "ジョブ",
|
||||
"xpack.ml.dataFrameAnalyticsLabel": "データフレーム分析",
|
||||
"xpack.ml.dataGrid.CcsWarningCalloutBody": "データビューのデータの取得中に問題が発生しました。ソースプレビューとクラスター横断検索を組み合わせることは、バージョン7.10以上ではサポートされていません。トランスフォームを構成して作成することはできます。",
|
||||
"xpack.ml.dataGrid.CcsWarningCalloutTitle": "クラスター横断検索でフィールドデータが返されませんでした。",
|
||||
"xpack.ml.dataGrid.columnChart.ErrorMessageToast": "ヒストグラムデータの取得でエラーが発生しました。{error}",
|
||||
|
@ -27883,7 +27880,6 @@
|
|||
"xpack.ml.datavisualizer.selector.dataVisualizerDescription": "機械学習データビジュアライザーツールは、ログファイルのメトリックとフィールド、または既存の Elasticsearch インデックスを分析し、データの理解を助けます。",
|
||||
"xpack.ml.datavisualizer.selector.dataVisualizerTitle": "データビジュアライザー",
|
||||
"xpack.ml.datavisualizer.selector.esqlTechnicalPreviewBadge.titleMsg": "ES|QLデータビジュアライザーはテクニカルプレビュー段階です。",
|
||||
"xpack.ml.datavisualizer.selector.importDataDescription": "ログファイルからデータをインポートします。最大{maxFileSize}のファイルをアップロードできます。",
|
||||
"xpack.ml.datavisualizer.selector.importDataTitle": "ファイルのデータを可視化",
|
||||
"xpack.ml.datavisualizer.selector.selectDataViewButtonLabel": "データビューを選択",
|
||||
"xpack.ml.datavisualizer.selector.selectDataViewTitle": "データビューのデータを可視化",
|
||||
|
@ -27915,27 +27911,21 @@
|
|||
"xpack.ml.deepLink.analyticsMap": "分析マップ",
|
||||
"xpack.ml.deepLink.anomalyDetection": "異常検知",
|
||||
"xpack.ml.deepLink.anomalyExplorer": "異常エクスプローラー",
|
||||
"xpack.ml.deepLink.calendarSettings": "カレンダー",
|
||||
"xpack.ml.deepLink.changePointDetection": "変化点検出",
|
||||
"xpack.ml.deepLink.dataDrift": "データドリフト",
|
||||
"xpack.ml.deepLink.dataFrameAnalytics": "データフレーム分析",
|
||||
"xpack.ml.deepLink.dataVisualizer": "データビジュアライザー",
|
||||
"xpack.ml.deepLink.esqlDataVisualizer": "ES|QLデータビジュアライザー",
|
||||
"xpack.ml.deepLink.fileUpload": "ファイルアップロード",
|
||||
"xpack.ml.deepLink.filterListsSettings": "フィルターリスト",
|
||||
"xpack.ml.deepLink.indexDataVisualizer": "インデックスデータビジュアライザー",
|
||||
"xpack.ml.deepLink.logPatternAnalysis": "ログパターン分析",
|
||||
"xpack.ml.deepLink.logRateAnalysis": "ログレート分析",
|
||||
"xpack.ml.deepLink.memoryUsage": "メモリー使用状況",
|
||||
"xpack.ml.deepLink.modelManagement": "モデル管理",
|
||||
"xpack.ml.deepLink.nodes": "ノード",
|
||||
"xpack.ml.deepLink.notifications": "通知",
|
||||
"xpack.ml.deepLink.overview": "概要",
|
||||
"xpack.ml.deepLink.resultExplorer": "結果エクスプローラー",
|
||||
"xpack.ml.deepLink.settings": "設定",
|
||||
"xpack.ml.deepLink.singleMetricViewer": "シングルメトリックビューアー",
|
||||
"xpack.ml.deepLink.suppliedConfigurations": "提供された構成",
|
||||
"xpack.ml.deepLink.trainedModels": "学習済みモデル",
|
||||
"xpack.ml.deleteSpaceAwareItemCheckModal.buttonTextCanDelete.job": "続行して、{length, plural, other {# 個のジョブ}}を削除します",
|
||||
"xpack.ml.deleteSpaceAwareItemCheckModal.buttonTextCanDelete.model": "続行して、{length, plural, other {# 個のモデル}}を削除します",
|
||||
"xpack.ml.deleteSpaceAwareItemCheckModal.buttonTextCanUnTagConfirm": "現在のスペースから削除",
|
||||
|
@ -28158,11 +28148,6 @@
|
|||
"xpack.ml.importExport.exportFlyout.exportJobDependenciesWarningCallout.jobUsingCalendarsButton": "カレンダーを使用したジョブ",
|
||||
"xpack.ml.importExport.exportFlyout.exportJobDependenciesWarningCallout.jobUsingFiltersAria": "フィルターリストを使用したジョブ",
|
||||
"xpack.ml.importExport.exportFlyout.exportJobDependenciesWarningCallout.jobUsingFiltersButton": "フィルターリストを使用したジョブ",
|
||||
"xpack.ml.importExport.exportFlyout.flyoutHeader": "ジョブのエクスポート",
|
||||
"xpack.ml.importExport.exportFlyout.switchTabsConfirm.cancelButton": "キャンセル",
|
||||
"xpack.ml.importExport.exportFlyout.switchTabsConfirm.confirmButton": "確認",
|
||||
"xpack.ml.importExport.exportFlyout.switchTabsConfirm.text": "タブを変更すると、現在選択しているジョブがクリアされます",
|
||||
"xpack.ml.importExport.exportFlyout.switchTabsConfirm.title": "タブを変更しますか?",
|
||||
"xpack.ml.importExport.importButton": "ジョブのインポート",
|
||||
"xpack.ml.importExport.importFlyout.cannotImportJobCallout.jobListAria": "ジョブを表示",
|
||||
"xpack.ml.importExport.importFlyout.cannotImportJobCallout.jobListButton": "ジョブを表示",
|
||||
|
@ -28575,7 +28560,6 @@
|
|||
"xpack.ml.management.jobsList.noPermissionToAccessLabel": "アクセスが拒否されました",
|
||||
"xpack.ml.management.jobsList.syncFlyoutButton": "保存されたオブジェクトを同期",
|
||||
"xpack.ml.management.jobsList.trainedModelsDocsLabel": "学習済みモデルドキュメント",
|
||||
"xpack.ml.management.jobsListTitle": "機械学習",
|
||||
"xpack.ml.management.jobsSpacesList.jobObjectNoun": "ジョブ",
|
||||
"xpack.ml.management.jobsSpacesList.modelObjectNoun": "学習済みモデル",
|
||||
"xpack.ml.management.jobsSpacesList.updateSpaces.error": "{id} の更新エラー",
|
||||
|
@ -28647,9 +28631,6 @@
|
|||
"xpack.ml.mapsAnomaliesLayerEmptyPrompt.createJobButtonText": "ジョブを作成",
|
||||
"xpack.ml.mapsAnomaliesLayerEmptyPrompt.createJobMessage": "異常検知ジョブを作成しますか?",
|
||||
"xpack.ml.mapsAnomaliesLayerEmptyPrompt.emptyPromptText": "異常検知により、地理データの異常な動作を検出できます。lat_long関数を使用するジョブを作成します。これはMaps異常レイヤーで必要です。",
|
||||
"xpack.ml.memoryUsage.memoryTab": "メモリー使用状況",
|
||||
"xpack.ml.memoryUsage.memoryUsageHeader": "メモリー使用状況",
|
||||
"xpack.ml.memoryUsage.nodesTab": "ノード",
|
||||
"xpack.ml.memoryUsage.treeMap.adLabel": "異常検知ジョブ",
|
||||
"xpack.ml.memoryUsage.treeMap.dfaLabel": "データフレーム分析ジョブ",
|
||||
"xpack.ml.memoryUsage.treeMap.emptyPrompt": "現在の選択と一致する開いているジョブまたは学習済みモデルがありません。",
|
||||
|
@ -28660,10 +28641,8 @@
|
|||
"xpack.ml.mlEntitySelector.dfaOptionsLabel": "データフレーム分析",
|
||||
"xpack.ml.mlEntitySelector.fetchError": "MLエンティティを取得できませんでした",
|
||||
"xpack.ml.mlEntitySelector.trainedModelsLabel": "学習済みモデル",
|
||||
"xpack.ml.modelManagement.memoryUsage.docTitle": "メモリー使用状況",
|
||||
"xpack.ml.modelManagement.trainedModels.docTitle": "学習済みモデル",
|
||||
"xpack.ml.modelManagement.trainedModelsHeader": "学習済みモデル",
|
||||
"xpack.ml.modelManagementLabel": "モデル管理",
|
||||
"xpack.ml.models.dfaValidation.messages.analysisFieldsEmptyWarningText": "分析に含まれる一部のフィールドには{percentEmpty}%以上の空の値があり、分析には適していない可能性があります。",
|
||||
"xpack.ml.models.dfaValidation.messages.analysisFieldsHeading": "分析フィールド",
|
||||
"xpack.ml.models.dfaValidation.messages.analysisFieldsHighWarningText": "{includedFieldsThreshold}を超えるフィールドが分析に選択されています。リソース使用量が増加し、ジョブの実行に時間がかかる場合があります。",
|
||||
|
@ -28832,31 +28811,21 @@
|
|||
"xpack.ml.multiSelectPicker.NoFiltersFoundMessage": "フィルターが見つかりません",
|
||||
"xpack.ml.navMenu.aiopsTabLinkText": "AIOps Labs",
|
||||
"xpack.ml.navMenu.anomalyDetection.anomalyExplorerText": "異常エクスプローラー",
|
||||
"xpack.ml.navMenu.anomalyDetection.jobsManagementText": "ジョブ",
|
||||
"xpack.ml.navMenu.anomalyDetection.singleMetricViewerText": "シングルメトリックビューアー",
|
||||
"xpack.ml.navMenu.anomalyDetection.suppliedConfigurationsLinkText": "提供された構成",
|
||||
"xpack.ml.navMenu.anomalyDetectionTabLinkText": "異常検知",
|
||||
"xpack.ml.navMenu.changePointDetectionLinkText": "変化点検出",
|
||||
"xpack.ml.navMenu.dataComparisonText": "データドリフト",
|
||||
"xpack.ml.navMenu.dataFrameAnalytics.analyticsMapText": "分析マップ",
|
||||
"xpack.ml.navMenu.dataFrameAnalytics.jobsManagementText": "ジョブ",
|
||||
"xpack.ml.navMenu.dataFrameAnalytics.resultsExplorerText": "結果エクスプローラー",
|
||||
"xpack.ml.navMenu.dataFrameAnalyticsTabLinkText": "データフレーム分析",
|
||||
"xpack.ml.navMenu.dataViewDataVisualizerLinkText": "データビュー",
|
||||
"xpack.ml.navMenu.dataVisualizerTabLinkText": "データビジュアライザー",
|
||||
"xpack.ml.navMenu.esqlDataVisualizerLinkText": "ES|QL",
|
||||
"xpack.ml.navMenu.fileDataVisualizerLinkText": "ファイル",
|
||||
"xpack.ml.navMenu.logCategorizationLinkText": "ログパターン分析",
|
||||
"xpack.ml.navMenu.logRateAnalysisLinkText": "ログレート分析",
|
||||
"xpack.ml.navMenu.memoryUsageText": "メモリー使用状況",
|
||||
"xpack.ml.navMenu.mlAppNameText": "機械学習と分析",
|
||||
"xpack.ml.navMenu.modelManagementText": "モデル管理",
|
||||
"xpack.ml.navMenu.notificationsTabLinkText": "通知",
|
||||
"xpack.ml.navMenu.overviewTabLinkText": "概要",
|
||||
"xpack.ml.navMenu.settingsTabLinkText": "設定",
|
||||
"xpack.ml.navMenu.trainedModelsTabBetaLabel": "テクニカルプレビュー",
|
||||
"xpack.ml.navMenu.trainedModelsTabBetaTooltipContent": "この機能はテクニカルプレビュー中であり、将来のリリースでは変更されたり完全に削除されたりする場合があります。Elasticはすべての問題の修正に努めますが、テクニカルプレビュー中の機能には正式なGA機能のサポートSLAが適用されません。",
|
||||
"xpack.ml.navMenu.trainedModelsText": "学習済みモデル",
|
||||
"xpack.ml.newJob.fromGeo.createJob.error.noTimeRange": "時間範囲が指定されていません。",
|
||||
"xpack.ml.newJob.fromLens.createJob.error.colsNoSourceField": "一部の列にはソースフィールドがありません。",
|
||||
"xpack.ml.newJob.fromLens.createJob.error.colsUsingFilterTimeSift": "ML検知器に対応していない設定が列に含まれています。時間シフトとフィルター条件はサポートされていません。",
|
||||
|
@ -29361,7 +29330,6 @@
|
|||
"xpack.ml.overview.nodesPanel.header": "ノード",
|
||||
"xpack.ml.overview.nodesPanel.totalNodesLabel": "合計",
|
||||
"xpack.ml.overview.nodesPanel.viewNodeLink": "ノードの表示",
|
||||
"xpack.ml.overview.notificationsLabel": "通知",
|
||||
"xpack.ml.overview.overviewLabel": "概要",
|
||||
"xpack.ml.overview.statsBar.failedAnalyticsLabel": "失敗",
|
||||
"xpack.ml.overview.statsBar.runningAnalyticsLabel": "実行中",
|
||||
|
@ -29490,7 +29458,6 @@
|
|||
"xpack.ml.sampleDataLinkLabel": "ML ジョブ",
|
||||
"xpack.ml.savedObjectFinder.createADataView": "データビューを作成",
|
||||
"xpack.ml.selectDataViewLabel": "データビューを選択",
|
||||
"xpack.ml.settings.anomalyDetection.anomalyDetectionTitle": "異常検知",
|
||||
"xpack.ml.settings.anomalyDetection.calendarsDstSummaryCount": "{calendarsCountBadge} {calendarsDstCount, plural, other {個のカレンダー}}があります",
|
||||
"xpack.ml.settings.anomalyDetection.calendarsDstText": "DSTカレンダーには、異常を生成すべきではないスケジュールされたイベントのリストが含まれています。夏時間の時差によりイベントが1時間早くなったり遅くなったりする可能性が考慮されています。",
|
||||
"xpack.ml.settings.anomalyDetection.calendarsDstTitle": "DSTカレンダー",
|
||||
|
@ -29510,11 +29477,9 @@
|
|||
"xpack.ml.settings.anomalyDetection.manageFilterListsLink": "管理",
|
||||
"xpack.ml.settings.breadcrumbs.calendarManagement.createLabel": "作成",
|
||||
"xpack.ml.settings.breadcrumbs.calendarManagement.editLabel": "編集",
|
||||
"xpack.ml.settings.breadcrumbs.calendarManagementLabel": "カレンダーDST管理",
|
||||
"xpack.ml.settings.breadcrumbs.dataComparisonLabel": "データドリフト",
|
||||
"xpack.ml.settings.breadcrumbs.filterLists.createLabel": "作成",
|
||||
"xpack.ml.settings.breadcrumbs.filterLists.editLabel": "編集",
|
||||
"xpack.ml.settings.breadcrumbs.filterListsLabel": "フィルターリスト",
|
||||
"xpack.ml.settings.calendarList.docTitle": "カレンダー",
|
||||
"xpack.ml.settings.calendars.listHeader.calendarsDescription": "システム停止日や祝日など、異常値を生成したくないイベントについては、カレンダーに予定されているイベントのリストを登録できます。カレンダーは複数のジョブに割り当てることができます。{br}{learnMoreLink}",
|
||||
"xpack.ml.settings.calendars.listHeader.calendarsDescription.learnMoreLinkText": "詳細",
|
||||
|
@ -29573,7 +29538,6 @@
|
|||
"xpack.ml.settings.filterLists.table.noFiltersCreatedTitle": "フィルターが 1 つも作成されていません",
|
||||
"xpack.ml.settings.filterLists.table.notInUseAriaLabel": "使用されていません",
|
||||
"xpack.ml.settings.filterLists.toolbar.deleteItemButtonLabel": "アイテムを削除",
|
||||
"xpack.ml.settings.title": "設定",
|
||||
"xpack.ml.settingsBreadcrumbLabel": "設定",
|
||||
"xpack.ml.severitySelector.formControlAriaLabel": "重要度のしきい値を選択",
|
||||
"xpack.ml.severitySelector.formControlLabel": "深刻度",
|
||||
|
@ -29607,8 +29571,6 @@
|
|||
"xpack.ml.suppliedConfigurations.preconfigurecJobsHeader": "提供された構成",
|
||||
"xpack.ml.suppliedConfigurations.preconfigurecJobsHeaderDescription": "このページでは、関連するKibanaアセットと、事前定義された異常検知ジョブ構成の一覧が表示されます。",
|
||||
"xpack.ml.suppliedConfigurations.suppliedConfigurations.docTitle": "提供された構成",
|
||||
"xpack.ml.suppliedConfigurationsBreadcrumbs.suppliedConfigurationsLabel": "提供された構成",
|
||||
"xpack.ml.suppliedConfigurationsLabel": "提供された構成",
|
||||
"xpack.ml.swimlaneEmbeddable.errorMessage": "スイムレーンのデータを読み込めません",
|
||||
"xpack.ml.swimlaneEmbeddable.noDataFound": "異常値が見つかりませんでした",
|
||||
"xpack.ml.swimlaneEmbeddable.panelTitleLabel": "パネルタイトル",
|
||||
|
@ -30111,7 +30073,6 @@
|
|||
"xpack.ml.trainedModels.testModelsFlyout.zeroShotClassification.inputText": "テストするフレーズを入力",
|
||||
"xpack.ml.trainedModels.testModelsFlyout.zeroShotClassification.label": "ゼロショット分類",
|
||||
"xpack.ml.trainedModelsBreadcrumbs.dataDriftLabel": "データドリフト",
|
||||
"xpack.ml.trainedModelsBreadcrumbs.nodeOverviewLabel": "メモリー使用状況",
|
||||
"xpack.ml.trainedModelsBreadcrumbs.trainedModelsLabel": "学習済みモデル",
|
||||
"xpack.ml.upgrade.upgradeWarning.upgradeInProgressWarningDescription": "機械学習に関連したインデックスは現在アップグレード中です。",
|
||||
"xpack.ml.upgrade.upgradeWarning.upgradeInProgressWarningDescriptionExtra": "現在いくつかのアクションが利用できません。",
|
||||
|
@ -32404,23 +32365,13 @@
|
|||
"xpack.observability.obltNav.infrastructure.metricsExplorer": "メトリックエクスプローラー",
|
||||
"xpack.observability.obltNav.infrastructure.universalProfiling": "ユニバーサルプロファイリング",
|
||||
"xpack.observability.obltNav.machineLearning": "機械学習",
|
||||
"xpack.observability.obltNav.machineLearning.memoryUsage": "メモリー使用状況",
|
||||
"xpack.observability.obltNav.management": "管理",
|
||||
"xpack.observability.obltNav.ml.aiops_labs": "Aiops labs",
|
||||
"xpack.observability.obltNav.ml.aiops_labs.change_point_detection": "変化点検出",
|
||||
"xpack.observability.obltNav.ml.aiops_labs.log_pattern_analysis": "ログパターン分析",
|
||||
"xpack.observability.obltNav.ml.aiops_labs.log_rate_analysis": "ログレート分析",
|
||||
"xpack.observability.obltNav.ml.anomaly_detection": "異常検知",
|
||||
"xpack.observability.obltNav.ml.anomaly_detection.jobs": "ジョブ",
|
||||
"xpack.observability.obltNav.ml.data_frame_analytics": "データフレーム分析",
|
||||
"xpack.observability.obltNav.ml.data_frame_analytics.jobs": "ジョブ",
|
||||
"xpack.observability.obltNav.ml.data_visualizer": "データビジュアライザー",
|
||||
"xpack.observability.obltNav.ml.data_visualizer.data_drift": "データドリフト",
|
||||
"xpack.observability.obltNav.ml.data_visualizer.data_view_data_visualizer": "データビューデータビジュアライザー",
|
||||
"xpack.observability.obltNav.ml.data_visualizer.esql_data_visualizer": "ES|QLデータビジュアライザー",
|
||||
"xpack.observability.obltNav.ml.data_visualizer.file_data_visualizer": "ファイルデータビジュアライザー",
|
||||
"xpack.observability.obltNav.ml.model_management": "モデル管理",
|
||||
"xpack.observability.obltNav.ml.model_management.trainedModels": "学習済みモデル",
|
||||
"xpack.observability.obltNav.otherTools": "その他のツール",
|
||||
"xpack.observability.obltNav.otherTools.logsAnomalies": "Logs異常",
|
||||
"xpack.observability.obltNav.otherTools.logsCategories": "Visualizeライブラリ",
|
||||
|
@ -32840,20 +32791,6 @@
|
|||
"xpack.observabilityShared.bottomBarActions.unsavedChanges": "{unsavedChangesCount, plural, other {# 未保存変更}}",
|
||||
"xpack.observabilityShared.breadcrumbs.observabilityLinkText": "Observability",
|
||||
"xpack.observabilityShared.common.constants.grouping": "Observability",
|
||||
"xpack.observabilityShared.experimentalOnboardingFlow.browseDocumentationFlexItemDescription": "すべてのElastic機能に関する詳細なガイド",
|
||||
"xpack.observabilityShared.experimentalOnboardingFlow.browseDocumentationFlexItemLabel": "ドキュメントを参照",
|
||||
"xpack.observabilityShared.experimentalOnboardingFlow.browseDocumentationFlexItemLinkARIALabel": "すべてのElastic機能の詳細",
|
||||
"xpack.observabilityShared.experimentalOnboardingFlow.browseDocumentationFlexItemLinkLabel": "詳細",
|
||||
"xpack.observabilityShared.experimentalOnboardingFlow.demoEnvironmentFlexItemDescription": "Elasticのライブデモを見る",
|
||||
"xpack.observabilityShared.experimentalOnboardingFlow.demoEnvironmentFlexItemLabel": "デモ環境",
|
||||
"xpack.observabilityShared.experimentalOnboardingFlow.demoEnvironmentFlexItemLinkLabel": "デモの探索",
|
||||
"xpack.observabilityShared.experimentalOnboardingFlow.exploreForumFlexItemDescription": "Elasticに関する意見を交換",
|
||||
"xpack.observabilityShared.experimentalOnboardingFlow.exploreForumFlexItemLabel": "フォーラムを探索",
|
||||
"xpack.observabilityShared.experimentalOnboardingFlow.exploreForumFlexItemLinkARIALabel": "Elasticディスカッションフォーラムを開く",
|
||||
"xpack.observabilityShared.experimentalOnboardingFlow.exploreForumFlexItemLinkLabel": "ディスカッションフォーラム",
|
||||
"xpack.observabilityShared.experimentalOnboardingFlow.supportHubFlexItemDescription": "ケースを作成してヘルプを依頼",
|
||||
"xpack.observabilityShared.experimentalOnboardingFlow.supportHubFlexItemLabel": "サポートハブ",
|
||||
"xpack.observabilityShared.experimentalOnboardingFlow.supportHubFlexItemLinkLabel": "サポートハブを開く",
|
||||
"xpack.observabilityShared.featureFeedbackButton.tellUsWhatYouThinkLink": "ご意見をお聞かせください。",
|
||||
"xpack.observabilityShared.fieldValueSelection.apply": "適用",
|
||||
"xpack.observabilityShared.fieldValueSelection.apply.label": "{label}に選択したフィルターを適用",
|
||||
|
@ -41847,21 +41784,12 @@
|
|||
"xpack.serverlessObservability.nav.infrastructure": "インフラストラクチャー",
|
||||
"xpack.serverlessObservability.nav.infrastructureInventory": "インフラインベントリ",
|
||||
"xpack.serverlessObservability.nav.machineLearning": "機械学習",
|
||||
"xpack.serverlessObservability.nav.machineLearning.memoryUsage": "メモリー使用状況",
|
||||
"xpack.serverlessObservability.nav.ml.aiops_labs": "Aiops labs",
|
||||
"xpack.serverlessObservability.nav.ml.aiops_labs.change_point_detection": "変化点検出",
|
||||
"xpack.serverlessObservability.nav.ml.aiops_labs.log_pattern_analysis": "ログパターン分析",
|
||||
"xpack.serverlessObservability.nav.ml.aiops_labs.log_rate_analysis": "ログレート分析",
|
||||
"xpack.serverlessObservability.nav.ml.anomaly_detection": "異常検知",
|
||||
"xpack.serverlessObservability.nav.ml.anomaly_detection.jobs": "ジョブ",
|
||||
"xpack.serverlessObservability.nav.ml.data_frame_analytics": "データフレーム分析",
|
||||
"xpack.serverlessObservability.nav.ml.data_frame_analytics.jobs": "ジョブ",
|
||||
"xpack.serverlessObservability.nav.ml.data_visualizer": "データビジュアライザー",
|
||||
"xpack.serverlessObservability.nav.ml.data_visualizer.data_drift": "データドリフト",
|
||||
"xpack.serverlessObservability.nav.ml.data_visualizer.data_view_data_visualizer": "データビューデータビジュアライザー",
|
||||
"xpack.serverlessObservability.nav.ml.data_visualizer.file_data_visualizer": "ファイルデータビジュアライザー",
|
||||
"xpack.serverlessObservability.nav.ml.model_management": "モデル管理",
|
||||
"xpack.serverlessObservability.nav.ml.model_management.trainedModels": "学習済みモデル",
|
||||
"xpack.serverlessObservability.nav.mngt": "管理",
|
||||
"xpack.serverlessObservability.nav.mngt.access": "アクセス",
|
||||
"xpack.serverlessObservability.nav.mngt.alertsAndInsights": "アラートとインサイト",
|
||||
|
|
|
@ -27192,7 +27192,6 @@
|
|||
"xpack.ml.anomalyDetectionAlert.name": "异常检测",
|
||||
"xpack.ml.anomalyDetectionAlert.topNBucketsDescription": "为获取最高异常而要检查的最新存储桶数目。",
|
||||
"xpack.ml.anomalyDetectionAlert.topNBucketsLabel": "最新存储桶数目",
|
||||
"xpack.ml.anomalyDetectionBreadcrumbLabel": "异常检测",
|
||||
"xpack.ml.anomalyExplorerPageLabel": "Anomaly Explorer",
|
||||
"xpack.ml.anomalyResultsViewSelector.anomalyExplorerLabel": "在 Anomaly Explorer 中查看结果",
|
||||
"xpack.ml.anomalyResultsViewSelector.buttonGroupLegend": "异常结果视图选择器",
|
||||
|
@ -27330,7 +27329,6 @@
|
|||
"xpack.ml.controls.selectSeverity.minorLabel": "轻微",
|
||||
"xpack.ml.controls.selectSeverity.scoreDetailsDescription": "{value} 及以上分数",
|
||||
"xpack.ml.controls.selectSeverity.warningLabel": "警告",
|
||||
"xpack.ml.createJobsBreadcrumbLabel": "创建作业",
|
||||
"xpack.ml.creationWizardUtils.destinationIndexInputAriaLabel": "选择唯一目标索引名称。",
|
||||
"xpack.ml.creationWizardUtils.destinationIndexInvalidError": "目标索引名称无效。",
|
||||
"xpack.ml.creationWizardUtils.destinationIndexInvalidErrorLink": "详细了解索引名称限制。",
|
||||
|
@ -27897,7 +27895,6 @@
|
|||
"xpack.ml.dataFrameAnalyticsBreadcrumbs.dataViewLabel": "数据视图",
|
||||
"xpack.ml.dataFrameAnalyticsBreadcrumbs.esqlLabel": "索引数据可视化工具 (ES|QL)",
|
||||
"xpack.ml.dataFrameAnalyticsBreadcrumbs.jobsManagementLabel": "作业",
|
||||
"xpack.ml.dataFrameAnalyticsLabel": "数据帧分析",
|
||||
"xpack.ml.dataGrid.CcsWarningCalloutBody": "检索数据视图的数据时出现问题。源预览和跨集群搜索仅在 7.10 及以上版本上受支持。可能需要配置和创建转换。",
|
||||
"xpack.ml.dataGrid.CcsWarningCalloutTitle": "跨集群搜索未返回字段数据。",
|
||||
"xpack.ml.dataGrid.columnChart.ErrorMessageToast": "提取直方图数据时发生错误:{error}",
|
||||
|
@ -27933,7 +27930,6 @@
|
|||
"xpack.ml.datavisualizer.selector.dataVisualizerDescription": "Machine Learning 数据可视化工具通过分析日志文件或现有 Elasticsearch 索引中的指标和字段,帮助您理解数据。",
|
||||
"xpack.ml.datavisualizer.selector.dataVisualizerTitle": "数据可视化工具",
|
||||
"xpack.ml.datavisualizer.selector.esqlTechnicalPreviewBadge.titleMsg": "ES|QL 数据可视化工具处于技术预览状态。",
|
||||
"xpack.ml.datavisualizer.selector.importDataDescription": "从日志文件导入数据。您可以上传不超过 {maxFileSize} 的文件。",
|
||||
"xpack.ml.datavisualizer.selector.importDataTitle": "可视化来自文件的数据",
|
||||
"xpack.ml.datavisualizer.selector.selectDataViewButtonLabel": "选择数据视图",
|
||||
"xpack.ml.datavisualizer.selector.selectDataViewTitle": "可视化来自数据视图的数据",
|
||||
|
@ -27965,27 +27961,21 @@
|
|||
"xpack.ml.deepLink.analyticsMap": "分析地图",
|
||||
"xpack.ml.deepLink.anomalyDetection": "异常检测",
|
||||
"xpack.ml.deepLink.anomalyExplorer": "Anomaly Explorer",
|
||||
"xpack.ml.deepLink.calendarSettings": "日历",
|
||||
"xpack.ml.deepLink.changePointDetection": "更改点检测",
|
||||
"xpack.ml.deepLink.dataDrift": "数据偏移",
|
||||
"xpack.ml.deepLink.dataFrameAnalytics": "数据帧分析",
|
||||
"xpack.ml.deepLink.dataVisualizer": "数据可视化工具",
|
||||
"xpack.ml.deepLink.esqlDataVisualizer": "ES|QL 数据可视化工具",
|
||||
"xpack.ml.deepLink.fileUpload": "文件上传",
|
||||
"xpack.ml.deepLink.filterListsSettings": "筛选列表",
|
||||
"xpack.ml.deepLink.indexDataVisualizer": "索引数据可视化工具",
|
||||
"xpack.ml.deepLink.logPatternAnalysis": "日志模式分析",
|
||||
"xpack.ml.deepLink.logRateAnalysis": "日志速率分析",
|
||||
"xpack.ml.deepLink.memoryUsage": "内存利用率",
|
||||
"xpack.ml.deepLink.modelManagement": "模型管理",
|
||||
"xpack.ml.deepLink.nodes": "节点",
|
||||
"xpack.ml.deepLink.notifications": "通知",
|
||||
"xpack.ml.deepLink.overview": "概览",
|
||||
"xpack.ml.deepLink.resultExplorer": "结果浏览器",
|
||||
"xpack.ml.deepLink.settings": "设置",
|
||||
"xpack.ml.deepLink.singleMetricViewer": "Single Metric Viewer",
|
||||
"xpack.ml.deepLink.suppliedConfigurations": "提供的配置",
|
||||
"xpack.ml.deepLink.trainedModels": "已训练模型",
|
||||
"xpack.ml.deleteSpaceAwareItemCheckModal.buttonTextCanDelete.job": "继续删除 {length, plural, other {# 个作业}}",
|
||||
"xpack.ml.deleteSpaceAwareItemCheckModal.buttonTextCanDelete.model": "继续删除 {length, plural, other {# 个模型}}",
|
||||
"xpack.ml.deleteSpaceAwareItemCheckModal.buttonTextCanUnTagConfirm": "从当前工作区中移除",
|
||||
|
@ -28208,11 +28198,6 @@
|
|||
"xpack.ml.importExport.exportFlyout.exportJobDependenciesWarningCallout.jobUsingCalendarsButton": "使用日历的作业",
|
||||
"xpack.ml.importExport.exportFlyout.exportJobDependenciesWarningCallout.jobUsingFiltersAria": "使用筛选列表的作业",
|
||||
"xpack.ml.importExport.exportFlyout.exportJobDependenciesWarningCallout.jobUsingFiltersButton": "使用筛选列表的作业",
|
||||
"xpack.ml.importExport.exportFlyout.flyoutHeader": "导出作业",
|
||||
"xpack.ml.importExport.exportFlyout.switchTabsConfirm.cancelButton": "取消",
|
||||
"xpack.ml.importExport.exportFlyout.switchTabsConfirm.confirmButton": "确认",
|
||||
"xpack.ml.importExport.exportFlyout.switchTabsConfirm.text": "更改选项卡将会清除当前选定的作业",
|
||||
"xpack.ml.importExport.exportFlyout.switchTabsConfirm.title": "更改选项卡?",
|
||||
"xpack.ml.importExport.importButton": "导入作业",
|
||||
"xpack.ml.importExport.importFlyout.cannotImportJobCallout.jobListAria": "查看作业",
|
||||
"xpack.ml.importExport.importFlyout.cannotImportJobCallout.jobListButton": "查看作业",
|
||||
|
@ -28626,7 +28611,6 @@
|
|||
"xpack.ml.management.jobsList.noPermissionToAccessLabel": "访问被拒绝",
|
||||
"xpack.ml.management.jobsList.syncFlyoutButton": "同步已保存对象",
|
||||
"xpack.ml.management.jobsList.trainedModelsDocsLabel": "已训练模型文档",
|
||||
"xpack.ml.management.jobsListTitle": "Machine Learning",
|
||||
"xpack.ml.management.jobsSpacesList.jobObjectNoun": "作业",
|
||||
"xpack.ml.management.jobsSpacesList.modelObjectNoun": "已训练模型",
|
||||
"xpack.ml.management.jobsSpacesList.updateSpaces.error": "更新 {id} 时出错",
|
||||
|
@ -28698,9 +28682,6 @@
|
|||
"xpack.ml.mapsAnomaliesLayerEmptyPrompt.createJobButtonText": "创建作业",
|
||||
"xpack.ml.mapsAnomaliesLayerEmptyPrompt.createJobMessage": "创建异常检测作业",
|
||||
"xpack.ml.mapsAnomaliesLayerEmptyPrompt.emptyPromptText": "通过异常检测,可发现地理数据中的异常行为。创建使用 lat_long 函数的作业,地图异常图层需要这样做。",
|
||||
"xpack.ml.memoryUsage.memoryTab": "内存使用",
|
||||
"xpack.ml.memoryUsage.memoryUsageHeader": "内存利用率",
|
||||
"xpack.ml.memoryUsage.nodesTab": "节点",
|
||||
"xpack.ml.memoryUsage.treeMap.adLabel": "异常检测作业",
|
||||
"xpack.ml.memoryUsage.treeMap.dfaLabel": "数据帧分析作业",
|
||||
"xpack.ml.memoryUsage.treeMap.emptyPrompt": "没有打开的作业或已训练模型匹配当前选择。",
|
||||
|
@ -28711,10 +28692,8 @@
|
|||
"xpack.ml.mlEntitySelector.dfaOptionsLabel": "数据帧分析",
|
||||
"xpack.ml.mlEntitySelector.fetchError": "无法提取 ML 实体",
|
||||
"xpack.ml.mlEntitySelector.trainedModelsLabel": "已训练模型",
|
||||
"xpack.ml.modelManagement.memoryUsage.docTitle": "内存利用率",
|
||||
"xpack.ml.modelManagement.trainedModels.docTitle": "已训练模型",
|
||||
"xpack.ml.modelManagement.trainedModelsHeader": "已训练模型",
|
||||
"xpack.ml.modelManagementLabel": "模型管理",
|
||||
"xpack.ml.models.dfaValidation.messages.analysisFieldsEmptyWarningText": "分析包括的一些字段至少有 {percentEmpty}% 的空值,可能不适合分析。",
|
||||
"xpack.ml.models.dfaValidation.messages.analysisFieldsHeading": "分析字段",
|
||||
"xpack.ml.models.dfaValidation.messages.analysisFieldsHighWarningText": "已选择 {includedFieldsThreshold} 以上字段进行分析。这可能导致资源使用率增加以及作业长时间运行。",
|
||||
|
@ -28883,31 +28862,21 @@
|
|||
"xpack.ml.multiSelectPicker.NoFiltersFoundMessage": "未找到任何筛选",
|
||||
"xpack.ml.navMenu.aiopsTabLinkText": "AIOps 实验室",
|
||||
"xpack.ml.navMenu.anomalyDetection.anomalyExplorerText": "Anomaly Explorer",
|
||||
"xpack.ml.navMenu.anomalyDetection.jobsManagementText": "作业",
|
||||
"xpack.ml.navMenu.anomalyDetection.singleMetricViewerText": "Single Metric Viewer",
|
||||
"xpack.ml.navMenu.anomalyDetection.suppliedConfigurationsLinkText": "提供的配置",
|
||||
"xpack.ml.navMenu.anomalyDetectionTabLinkText": "异常检测",
|
||||
"xpack.ml.navMenu.changePointDetectionLinkText": "更改点检测",
|
||||
"xpack.ml.navMenu.dataComparisonText": "数据偏移",
|
||||
"xpack.ml.navMenu.dataFrameAnalytics.analyticsMapText": "分析地图",
|
||||
"xpack.ml.navMenu.dataFrameAnalytics.jobsManagementText": "作业",
|
||||
"xpack.ml.navMenu.dataFrameAnalytics.resultsExplorerText": "结果浏览器",
|
||||
"xpack.ml.navMenu.dataFrameAnalyticsTabLinkText": "数据帧分析",
|
||||
"xpack.ml.navMenu.dataViewDataVisualizerLinkText": "数据视图",
|
||||
"xpack.ml.navMenu.dataVisualizerTabLinkText": "数据可视化工具",
|
||||
"xpack.ml.navMenu.esqlDataVisualizerLinkText": "ES|QL",
|
||||
"xpack.ml.navMenu.fileDataVisualizerLinkText": "文件",
|
||||
"xpack.ml.navMenu.logCategorizationLinkText": "日志模式分析",
|
||||
"xpack.ml.navMenu.logRateAnalysisLinkText": "日志速率分析",
|
||||
"xpack.ml.navMenu.memoryUsageText": "内存利用率",
|
||||
"xpack.ml.navMenu.mlAppNameText": "Machine Learning 和分析",
|
||||
"xpack.ml.navMenu.modelManagementText": "模型管理",
|
||||
"xpack.ml.navMenu.notificationsTabLinkText": "通知",
|
||||
"xpack.ml.navMenu.overviewTabLinkText": "概览",
|
||||
"xpack.ml.navMenu.settingsTabLinkText": "设置",
|
||||
"xpack.ml.navMenu.trainedModelsTabBetaLabel": "技术预览",
|
||||
"xpack.ml.navMenu.trainedModelsTabBetaTooltipContent": "此功能处于技术预览状态,在未来版本中可能会更改或完全移除。Elastic 将努力修复任何问题,但处于技术预览状态的功能不受正式 GA 功能支持 SLA 的约束。",
|
||||
"xpack.ml.navMenu.trainedModelsText": "已训练模型",
|
||||
"xpack.ml.newJob.fromGeo.createJob.error.noTimeRange": "未指定时间范围。",
|
||||
"xpack.ml.newJob.fromLens.createJob.error.colsNoSourceField": "某些列不包含源字段。",
|
||||
"xpack.ml.newJob.fromLens.createJob.error.colsUsingFilterTimeSift": "列包含与 ML 检测工具不兼容的设置,不支持时间偏移和筛选依据。",
|
||||
|
@ -29412,7 +29381,6 @@
|
|||
"xpack.ml.overview.nodesPanel.header": "节点",
|
||||
"xpack.ml.overview.nodesPanel.totalNodesLabel": "合计",
|
||||
"xpack.ml.overview.nodesPanel.viewNodeLink": "查看节点",
|
||||
"xpack.ml.overview.notificationsLabel": "通知",
|
||||
"xpack.ml.overview.overviewLabel": "概览",
|
||||
"xpack.ml.overview.statsBar.failedAnalyticsLabel": "失败",
|
||||
"xpack.ml.overview.statsBar.runningAnalyticsLabel": "正在运行",
|
||||
|
@ -29541,7 +29509,6 @@
|
|||
"xpack.ml.sampleDataLinkLabel": "ML 作业",
|
||||
"xpack.ml.savedObjectFinder.createADataView": "创建数据视图",
|
||||
"xpack.ml.selectDataViewLabel": "选择数据视图",
|
||||
"xpack.ml.settings.anomalyDetection.anomalyDetectionTitle": "异常检测",
|
||||
"xpack.ml.settings.anomalyDetection.calendarsDstSummaryCount": "您有 {calendarsCountBadge} 个{calendarsDstCount, plural, other {日历}}",
|
||||
"xpack.ml.settings.anomalyDetection.calendarsDstText": "DST 日历包含您不希望为其生成异常的已计划事件列表,考虑可能导致事件提前或延后一小时发生的夏令时转换。",
|
||||
"xpack.ml.settings.anomalyDetection.calendarsDstTitle": "DST 日历",
|
||||
|
@ -29561,11 +29528,9 @@
|
|||
"xpack.ml.settings.anomalyDetection.manageFilterListsLink": "管理",
|
||||
"xpack.ml.settings.breadcrumbs.calendarManagement.createLabel": "创建",
|
||||
"xpack.ml.settings.breadcrumbs.calendarManagement.editLabel": "编辑",
|
||||
"xpack.ml.settings.breadcrumbs.calendarManagementLabel": "日历 DST 管理",
|
||||
"xpack.ml.settings.breadcrumbs.dataComparisonLabel": "数据偏移",
|
||||
"xpack.ml.settings.breadcrumbs.filterLists.createLabel": "创建",
|
||||
"xpack.ml.settings.breadcrumbs.filterLists.editLabel": "编辑",
|
||||
"xpack.ml.settings.breadcrumbs.filterListsLabel": "筛选列表",
|
||||
"xpack.ml.settings.calendarList.docTitle": "日历",
|
||||
"xpack.ml.settings.calendars.listHeader.calendarsDescription": "日志包含不应生成异常的已计划事件列表,例如已计划系统中断或公共假期。同一日历可分配给多个作业。{br}{learnMoreLink}",
|
||||
"xpack.ml.settings.calendars.listHeader.calendarsDescription.learnMoreLinkText": "了解详情",
|
||||
|
@ -29625,7 +29590,6 @@
|
|||
"xpack.ml.settings.filterLists.table.noFiltersCreatedTitle": "未创建任何筛选",
|
||||
"xpack.ml.settings.filterLists.table.notInUseAriaLabel": "未在使用中",
|
||||
"xpack.ml.settings.filterLists.toolbar.deleteItemButtonLabel": "删除项",
|
||||
"xpack.ml.settings.title": "设置",
|
||||
"xpack.ml.settingsBreadcrumbLabel": "设置",
|
||||
"xpack.ml.severitySelector.formControlAriaLabel": "选择严重性阈值",
|
||||
"xpack.ml.severitySelector.formControlLabel": "严重性",
|
||||
|
@ -29660,8 +29624,6 @@
|
|||
"xpack.ml.suppliedConfigurations.preconfigurecJobsHeader": "提供的配置",
|
||||
"xpack.ml.suppliedConfigurations.preconfigurecJobsHeaderDescription": "此页列出了预定义的异常检测作业配置和相关 Kibana 资产。",
|
||||
"xpack.ml.suppliedConfigurations.suppliedConfigurations.docTitle": "提供的配置",
|
||||
"xpack.ml.suppliedConfigurationsBreadcrumbs.suppliedConfigurationsLabel": "提供的配置",
|
||||
"xpack.ml.suppliedConfigurationsLabel": "提供的配置",
|
||||
"xpack.ml.swimlaneEmbeddable.errorMessage": "无法为泳道加载数据",
|
||||
"xpack.ml.swimlaneEmbeddable.noDataFound": "找不到异常",
|
||||
"xpack.ml.swimlaneEmbeddable.panelTitleLabel": "面板标题",
|
||||
|
@ -30164,7 +30126,6 @@
|
|||
"xpack.ml.trainedModels.testModelsFlyout.zeroShotClassification.inputText": "输入短语以进行测试",
|
||||
"xpack.ml.trainedModels.testModelsFlyout.zeroShotClassification.label": "Zero shot 分类",
|
||||
"xpack.ml.trainedModelsBreadcrumbs.dataDriftLabel": "数据偏移",
|
||||
"xpack.ml.trainedModelsBreadcrumbs.nodeOverviewLabel": "内存利用率",
|
||||
"xpack.ml.trainedModelsBreadcrumbs.trainedModelsLabel": "已训练模型",
|
||||
"xpack.ml.upgrade.upgradeWarning.upgradeInProgressWarningDescription": "当前正在升级与 Machine Learning 相关的索引。",
|
||||
"xpack.ml.upgrade.upgradeWarning.upgradeInProgressWarningDescriptionExtra": "此次某些操作不可用。",
|
||||
|
@ -32459,23 +32420,13 @@
|
|||
"xpack.observability.obltNav.infrastructure.metricsExplorer": "指标浏览器",
|
||||
"xpack.observability.obltNav.infrastructure.universalProfiling": "Universal Profiling",
|
||||
"xpack.observability.obltNav.machineLearning": "Machine Learning",
|
||||
"xpack.observability.obltNav.machineLearning.memoryUsage": "内存使用",
|
||||
"xpack.observability.obltNav.management": "管理",
|
||||
"xpack.observability.obltNav.ml.aiops_labs": "Aiops 实验室",
|
||||
"xpack.observability.obltNav.ml.aiops_labs.change_point_detection": "更改点检测",
|
||||
"xpack.observability.obltNav.ml.aiops_labs.log_pattern_analysis": "日志模式分析",
|
||||
"xpack.observability.obltNav.ml.aiops_labs.log_rate_analysis": "日志速率分析",
|
||||
"xpack.observability.obltNav.ml.anomaly_detection": "异常检测",
|
||||
"xpack.observability.obltNav.ml.anomaly_detection.jobs": "作业",
|
||||
"xpack.observability.obltNav.ml.data_frame_analytics": "数据帧分析",
|
||||
"xpack.observability.obltNav.ml.data_frame_analytics.jobs": "作业",
|
||||
"xpack.observability.obltNav.ml.data_visualizer": "数据可视化工具",
|
||||
"xpack.observability.obltNav.ml.data_visualizer.data_drift": "数据偏移",
|
||||
"xpack.observability.obltNav.ml.data_visualizer.data_view_data_visualizer": "数据视图数据可视化工具",
|
||||
"xpack.observability.obltNav.ml.data_visualizer.esql_data_visualizer": "ES|QL 数据可视化工具",
|
||||
"xpack.observability.obltNav.ml.data_visualizer.file_data_visualizer": "文件数据可视化工具",
|
||||
"xpack.observability.obltNav.ml.model_management": "模型管理",
|
||||
"xpack.observability.obltNav.ml.model_management.trainedModels": "已训练模型",
|
||||
"xpack.observability.obltNav.otherTools": "其他工具",
|
||||
"xpack.observability.obltNav.otherTools.logsAnomalies": "日志异常",
|
||||
"xpack.observability.obltNav.otherTools.logsCategories": "Visualize 库",
|
||||
|
@ -32896,20 +32847,6 @@
|
|||
"xpack.observabilityShared.bottomBarActions.unsavedChanges": "{unsavedChangesCount, plural, =0{0 个未保存更改} one {1 个未保存更改} other {# 个未保存更改}}",
|
||||
"xpack.observabilityShared.breadcrumbs.observabilityLinkText": "Observability",
|
||||
"xpack.observabilityShared.common.constants.grouping": "Observability",
|
||||
"xpack.observabilityShared.experimentalOnboardingFlow.browseDocumentationFlexItemDescription": "有关所有 Elastic 功能的深入指南",
|
||||
"xpack.observabilityShared.experimentalOnboardingFlow.browseDocumentationFlexItemLabel": "浏览文档",
|
||||
"xpack.observabilityShared.experimentalOnboardingFlow.browseDocumentationFlexItemLinkARIALabel": "详细了解所有 Elastic 功能",
|
||||
"xpack.observabilityShared.experimentalOnboardingFlow.browseDocumentationFlexItemLinkLabel": "了解详情",
|
||||
"xpack.observabilityShared.experimentalOnboardingFlow.demoEnvironmentFlexItemDescription": "浏览我们的实时演示环境",
|
||||
"xpack.observabilityShared.experimentalOnboardingFlow.demoEnvironmentFlexItemLabel": "演示环境",
|
||||
"xpack.observabilityShared.experimentalOnboardingFlow.demoEnvironmentFlexItemLinkLabel": "浏览演示",
|
||||
"xpack.observabilityShared.experimentalOnboardingFlow.exploreForumFlexItemDescription": "交流有关 Elastic 的看法",
|
||||
"xpack.observabilityShared.experimentalOnboardingFlow.exploreForumFlexItemLabel": "浏览论坛",
|
||||
"xpack.observabilityShared.experimentalOnboardingFlow.exploreForumFlexItemLinkARIALabel": "打开 Elastic 讨论论坛",
|
||||
"xpack.observabilityShared.experimentalOnboardingFlow.exploreForumFlexItemLinkLabel": "讨论论坛",
|
||||
"xpack.observabilityShared.experimentalOnboardingFlow.supportHubFlexItemDescription": "通过创建案例获取帮助",
|
||||
"xpack.observabilityShared.experimentalOnboardingFlow.supportHubFlexItemLabel": "支持中心",
|
||||
"xpack.observabilityShared.experimentalOnboardingFlow.supportHubFlexItemLinkLabel": "打开支持中心",
|
||||
"xpack.observabilityShared.featureFeedbackButton.tellUsWhatYouThinkLink": "告诉我们您的看法!",
|
||||
"xpack.observabilityShared.fieldValueSelection.apply": "应用",
|
||||
"xpack.observabilityShared.fieldValueSelection.apply.label": "为 {label} 应用选定筛选",
|
||||
|
@ -41911,21 +41848,12 @@
|
|||
"xpack.serverlessObservability.nav.infrastructure": "基础设施",
|
||||
"xpack.serverlessObservability.nav.infrastructureInventory": "基础设施库存",
|
||||
"xpack.serverlessObservability.nav.machineLearning": "Machine Learning",
|
||||
"xpack.serverlessObservability.nav.machineLearning.memoryUsage": "内存使用",
|
||||
"xpack.serverlessObservability.nav.ml.aiops_labs": "Aiops 实验室",
|
||||
"xpack.serverlessObservability.nav.ml.aiops_labs.change_point_detection": "更改点检测",
|
||||
"xpack.serverlessObservability.nav.ml.aiops_labs.log_pattern_analysis": "日志模式分析",
|
||||
"xpack.serverlessObservability.nav.ml.aiops_labs.log_rate_analysis": "日志速率分析",
|
||||
"xpack.serverlessObservability.nav.ml.anomaly_detection": "异常检测",
|
||||
"xpack.serverlessObservability.nav.ml.anomaly_detection.jobs": "作业",
|
||||
"xpack.serverlessObservability.nav.ml.data_frame_analytics": "数据帧分析",
|
||||
"xpack.serverlessObservability.nav.ml.data_frame_analytics.jobs": "作业",
|
||||
"xpack.serverlessObservability.nav.ml.data_visualizer": "数据可视化工具",
|
||||
"xpack.serverlessObservability.nav.ml.data_visualizer.data_drift": "数据偏移",
|
||||
"xpack.serverlessObservability.nav.ml.data_visualizer.data_view_data_visualizer": "数据视图数据可视化工具",
|
||||
"xpack.serverlessObservability.nav.ml.data_visualizer.file_data_visualizer": "文件数据可视化工具",
|
||||
"xpack.serverlessObservability.nav.ml.model_management": "模型管理",
|
||||
"xpack.serverlessObservability.nav.ml.model_management.trainedModels": "已训练模型",
|
||||
"xpack.serverlessObservability.nav.mngt": "管理",
|
||||
"xpack.serverlessObservability.nav.mngt.access": "访问",
|
||||
"xpack.serverlessObservability.nav.mngt.alertsAndInsights": "告警和洞见",
|
||||
|
|
|
@ -35,7 +35,7 @@ const getKibanaLinkForESAsset = (type: ElasticsearchAssetType, id: string): stri
|
|||
case 'transform':
|
||||
return `/app/management/data/transform?_a=(transform:(queryText:${id}))`;
|
||||
case 'ml_model':
|
||||
return `/app/ml/trained_models?_a=(trained_models:(queryText:'model_id:(${id})'))`;
|
||||
return `/app/management/ml/trained_models?_a=(trained_models:(queryText:'model_id:(${id})'))`;
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ export const ML_APP_NAME = i18n.translate('xpack.ml.navMenu.mlAppNameText', {
|
|||
defaultMessage: 'Machine Learning and Analytics',
|
||||
});
|
||||
export const ML_APP_ROUTE = '/app/ml';
|
||||
export const ML_MANAGEMENT_APP_ROUTE = '/app/management/ml';
|
||||
export const ML_INTERNAL_BASE_PATH = '/internal/ml';
|
||||
export const ML_EXTERNAL_BASE_PATH = '/api/ml';
|
||||
|
||||
|
|
|
@ -7,11 +7,34 @@
|
|||
|
||||
export const ML_APP_LOCATOR = 'ML_APP_LOCATOR';
|
||||
|
||||
/**
|
||||
* @deprecated since 9.1, kept here to redirect old bookmarks
|
||||
*/
|
||||
export const DEPRECATED_ML_ROUTE_TO_NEW_ROUTE = {
|
||||
jobs: 'anomaly_detection',
|
||||
data_frame_analytics: 'analytics',
|
||||
trained_models: 'trained_models',
|
||||
notifications: 'overview?_g=(tab:notifications)&',
|
||||
memory_usage: 'overview',
|
||||
supplied_configurations: 'anomaly_detection/ad_supplied_configurations',
|
||||
settings: 'ad_settings',
|
||||
'settings/calendars_list': 'ad_settings/calendars_list',
|
||||
'settings/calendars_list/new_calendar': 'ad_settings/calendars_list/new_calendar',
|
||||
'settings/calendars_dst_list': 'ad_settings/calendars_dst_list',
|
||||
'settings/calendars_dst_list/new_calendar': 'ad_settings/calendars_dst_list/new_calendar',
|
||||
'settings/filter_lists': 'ad_settings/filter_lists',
|
||||
'settings/filter_lists/new_filter_list': 'ad_settings/filter_lists/new_filter_list',
|
||||
nodes: 'overview',
|
||||
'jobs/new_job/step/index_or_search': 'anomaly_detection/jobs/new_job/step/select_source',
|
||||
};
|
||||
|
||||
export const ML_PAGES = {
|
||||
ANOMALY_DETECTION_JOBS_MANAGE: 'jobs',
|
||||
ANOMALY_DETECTION_JOBS_MANAGE: '',
|
||||
ANOMALY_DETECTION_JOBS_MANAGE_FOR_URL: 'jobs',
|
||||
ANOMALY_EXPLORER: 'explorer',
|
||||
SINGLE_METRIC_VIEWER: 'timeseriesexplorer',
|
||||
DATA_FRAME_ANALYTICS_JOBS_MANAGE: 'data_frame_analytics',
|
||||
DATA_FRAME_ANALYTICS_JOBS_MANAGE: '',
|
||||
DATA_FRAME_ANALYTICS_JOBS_MANAGE_FOR_URL: 'data_frame_analytics',
|
||||
DATA_FRAME_ANALYTICS_SOURCE_SELECTION: 'data_frame_analytics/source_selection',
|
||||
DATA_FRAME_ANALYTICS_CREATE_JOB: 'data_frame_analytics/new_job',
|
||||
TRAINED_MODELS_MANAGE: 'trained_models',
|
||||
|
@ -22,7 +45,7 @@ export const ML_PAGES = {
|
|||
MEMORY_USAGE: 'memory_usage',
|
||||
DATA_FRAME_ANALYTICS_EXPLORATION: 'data_frame_analytics/exploration',
|
||||
DATA_FRAME_ANALYTICS_MAP: 'data_frame_analytics/map',
|
||||
SUPPLIED_CONFIGURATIONS: 'supplied_configurations',
|
||||
SUPPLIED_CONFIGURATIONS: 'ad_supplied_configurations',
|
||||
/**
|
||||
* Page: Data Visualizer
|
||||
*/
|
||||
|
@ -53,23 +76,23 @@ export const ML_PAGES = {
|
|||
ANOMALY_DETECTION_CREATE_JOB_CATEGORIZATION: 'jobs/new_job/categorization',
|
||||
ANOMALY_DETECTION_CREATE_JOB_RARE: 'jobs/new_job/rare',
|
||||
ANOMALY_DETECTION_CREATE_JOB_GEO: 'jobs/new_job/geo',
|
||||
ANOMALY_DETECTION_CREATE_JOB_CONVERT_TO_ADVANCED: 'jobs/new_job/convert_to_advanced',
|
||||
ANOMALY_DETECTION_CREATE_JOB_CONVERT_TO_ADVANCED: 'jobs/new_job/advanced',
|
||||
ANOMALY_DETECTION_CREATE_JOB_SELECT_TYPE: 'jobs/new_job/step/job_type',
|
||||
ANOMALY_DETECTION_CREATE_JOB_SELECT_INDEX: 'jobs/new_job/step/index_or_search',
|
||||
ANOMALY_DETECTION_CREATE_JOB_SELECT_INDEX: 'jobs/new_job/step/select_source',
|
||||
ANOMALY_DETECTION_CREATE_JOB_FROM_LENS: 'jobs/new_job/from_lens',
|
||||
ANOMALY_DETECTION_CREATE_JOB_FROM_PATTERN_ANALYSIS: 'jobs/new_job/from_pattern_analysis',
|
||||
ANOMALY_DETECTION_CREATE_JOB_FROM_MAP: 'jobs/new_job/from_map',
|
||||
ANOMALY_DETECTION_MODULES_VIEW_OR_CREATE: 'modules/check_view_or_create',
|
||||
SETTINGS: 'settings',
|
||||
CALENDARS_MANAGE: 'settings/calendars_list',
|
||||
CALENDARS_DST_MANAGE: 'settings/calendars_dst_list',
|
||||
CALENDARS_NEW: 'settings/calendars_list/new_calendar',
|
||||
CALENDARS_DST_NEW: 'settings/calendars_dst_list/new_calendar',
|
||||
CALENDARS_EDIT: 'settings/calendars_list/edit_calendar',
|
||||
CALENDARS_DST_EDIT: 'settings/calendars_dst_list/edit_calendar',
|
||||
FILTER_LISTS_MANAGE: 'settings/filter_lists',
|
||||
FILTER_LISTS_NEW: 'settings/filter_lists/new_filter_list',
|
||||
FILTER_LISTS_EDIT: 'settings/filter_lists/edit_filter_list',
|
||||
SETTINGS: '',
|
||||
CALENDARS_MANAGE: 'calendars_list',
|
||||
CALENDARS_DST_MANAGE: 'calendars_dst_list',
|
||||
CALENDARS_NEW: 'calendars_list/new_calendar',
|
||||
CALENDARS_DST_NEW: 'calendars_dst_list/new_calendar',
|
||||
CALENDARS_EDIT: 'calendars_list/edit_calendar',
|
||||
CALENDARS_DST_EDIT: 'calendars_dst_list/edit_calendar',
|
||||
FILTER_LISTS_MANAGE: 'filter_lists',
|
||||
FILTER_LISTS_NEW: 'filter_lists/new_filter_list',
|
||||
FILTER_LISTS_EDIT: 'filter_lists/edit_filter_list',
|
||||
OVERVIEW: 'overview',
|
||||
NOTIFICATIONS: 'notifications',
|
||||
AIOPS: 'aiops',
|
||||
|
|
|
@ -15,7 +15,7 @@ import type { ListingPageUrlState } from '@kbn/ml-url-state';
|
|||
import type { JobId } from './anomaly_detection_jobs/job';
|
||||
import type { ML_PAGES } from '../constants/locator';
|
||||
|
||||
type OptionalPageState = object | undefined;
|
||||
type OptionalPageState = (object & { globalState?: MlCommonGlobalState }) | undefined;
|
||||
|
||||
export type MLPageState<PageType, PageState> = PageState extends OptionalPageState
|
||||
? { page: PageType; pageState?: PageState }
|
||||
|
@ -43,8 +43,14 @@ export interface MlGenericUrlPageState extends MlIndexBasedSearchState {
|
|||
}
|
||||
|
||||
export type MlGenericUrlState = MLPageState<
|
||||
| typeof ML_PAGES.DATA_VISUALIZER_INDEX_VIEWER
|
||||
| typeof ML_PAGES.DATA_VISUALIZER_ESQL
|
||||
| typeof ML_PAGES.AIOPS
|
||||
| typeof ML_PAGES.AIOPS_LOG_CATEGORIZATION
|
||||
| typeof ML_PAGES.AIOPS_LOG_CATEGORIZATION_INDEX_SELECT
|
||||
| typeof ML_PAGES.AIOPS_LOG_RATE_ANALYSIS
|
||||
| typeof ML_PAGES.AIOPS_LOG_RATE_ANALYSIS_INDEX_SELECT
|
||||
| typeof ML_PAGES.AIOPS_CHANGE_POINT_DETECTION_INDEX_SELECT
|
||||
| typeof ML_PAGES.AIOPS_CHANGE_POINT_DETECTION
|
||||
| typeof ML_PAGES.ANOMALY_EXPLORER
|
||||
| typeof ML_PAGES.ANOMALY_DETECTION_CREATE_JOB
|
||||
| typeof ML_PAGES.ANOMALY_DETECTION_CREATE_JOB_RECOGNIZER
|
||||
| typeof ML_PAGES.ANOMALY_DETECTION_CREATE_JOB_ADVANCED
|
||||
|
@ -53,29 +59,26 @@ export type MlGenericUrlState = MLPageState<
|
|||
| typeof ML_PAGES.ANOMALY_DETECTION_CREATE_JOB_FROM_PATTERN_ANALYSIS
|
||||
| typeof ML_PAGES.ANOMALY_DETECTION_CREATE_JOB_SELECT_TYPE
|
||||
| typeof ML_PAGES.ANOMALY_DETECTION_CREATE_JOB_SELECT_INDEX
|
||||
| typeof ML_PAGES.DATA_FRAME_ANALYTICS_CREATE_JOB
|
||||
| typeof ML_PAGES.OVERVIEW
|
||||
| typeof ML_PAGES.CALENDARS_MANAGE
|
||||
| typeof ML_PAGES.CALENDARS_DST_MANAGE
|
||||
| typeof ML_PAGES.CALENDARS_NEW
|
||||
| typeof ML_PAGES.CALENDARS_DST_NEW
|
||||
| typeof ML_PAGES.FILTER_LISTS_MANAGE
|
||||
| typeof ML_PAGES.FILTER_LISTS_NEW
|
||||
| typeof ML_PAGES.SETTINGS
|
||||
| typeof ML_PAGES.CALENDARS_MANAGE
|
||||
| typeof ML_PAGES.CALENDARS_NEW
|
||||
| typeof ML_PAGES.DATA_DRIFT_CUSTOM
|
||||
| typeof ML_PAGES.DATA_DRIFT_INDEX_SELECT
|
||||
| typeof ML_PAGES.DATA_DRIFT
|
||||
| typeof ML_PAGES.DATA_FRAME_ANALYTICS_CREATE_JOB
|
||||
| typeof ML_PAGES.DATA_FRAME_ANALYTICS_SOURCE_SELECTION
|
||||
| typeof ML_PAGES.DATA_VISUALIZER
|
||||
| typeof ML_PAGES.DATA_VISUALIZER_FILE
|
||||
| typeof ML_PAGES.DATA_VISUALIZER_INDEX_SELECT
|
||||
| typeof ML_PAGES.AIOPS
|
||||
| typeof ML_PAGES.AIOPS_LOG_CATEGORIZATION
|
||||
| typeof ML_PAGES.AIOPS_LOG_CATEGORIZATION_INDEX_SELECT
|
||||
| typeof ML_PAGES.AIOPS_LOG_RATE_ANALYSIS
|
||||
| typeof ML_PAGES.AIOPS_LOG_RATE_ANALYSIS_INDEX_SELECT
|
||||
| typeof ML_PAGES.AIOPS_CHANGE_POINT_DETECTION_INDEX_SELECT
|
||||
| typeof ML_PAGES.AIOPS_CHANGE_POINT_DETECTION
|
||||
| typeof ML_PAGES.SUPPLIED_CONFIGURATIONS,
|
||||
| typeof ML_PAGES.DATA_VISUALIZER_INDEX_VIEWER
|
||||
| typeof ML_PAGES.DATA_VISUALIZER_ESQL
|
||||
| typeof ML_PAGES.FILTER_LISTS_MANAGE
|
||||
| typeof ML_PAGES.FILTER_LISTS_NEW
|
||||
| typeof ML_PAGES.SETTINGS
|
||||
| typeof ML_PAGES.SINGLE_METRIC_VIEWER
|
||||
| typeof ML_PAGES.SUPPLIED_CONFIGURATIONS
|
||||
| typeof ML_PAGES.OVERVIEW,
|
||||
MlGenericUrlPageState | undefined
|
||||
>;
|
||||
export interface AnomalyDetectionQueryState {
|
||||
|
|
|
@ -16,6 +16,7 @@ export const ML_FROZEN_TIER_PREFERENCE = 'ml.frozenDataTierPreference';
|
|||
export const ML_ANOMALY_EXPLORER_PANELS = 'ml.anomalyExplorerPanels';
|
||||
export const ML_NOTIFICATIONS_LAST_CHECKED_AT = 'ml.notificationsLastCheckedAt';
|
||||
export const ML_OVERVIEW_PANELS = 'ml.overviewPanels';
|
||||
export const ML_OVERVIEW_PANELS_EXTENDED = 'ml.overviewPanelsExtended';
|
||||
export const ML_ELSER_CALLOUT_DISMISSED = 'ml.elserUpdateCalloutDismissed';
|
||||
export const ML_SCHEDULED_MODEL_DEPLOYMENTS = 'ml.trainedModels.scheduledModelDeployments';
|
||||
|
||||
|
@ -63,6 +64,10 @@ export interface OverviewPanelsState {
|
|||
dfaJobs: boolean;
|
||||
}
|
||||
|
||||
export interface OverviewPanelsExtendedState {
|
||||
memoryUsage: boolean;
|
||||
}
|
||||
|
||||
export interface MlStorageRecord {
|
||||
[key: string]: unknown;
|
||||
[ML_ENTITY_FIELDS_CONFIG]: PartitionFieldsConfig;
|
||||
|
@ -72,6 +77,7 @@ export interface MlStorageRecord {
|
|||
[ML_ANOMALY_EXPLORER_PANELS]: AnomalyExplorerPanelsState | undefined;
|
||||
[ML_NOTIFICATIONS_LAST_CHECKED_AT]: number | undefined;
|
||||
[ML_OVERVIEW_PANELS]: OverviewPanelsState;
|
||||
[ML_OVERVIEW_PANELS_EXTENDED]: OverviewPanelsExtendedState;
|
||||
[ML_ELSER_CALLOUT_DISMISSED]: boolean | undefined;
|
||||
[ML_SCHEDULED_MODEL_DEPLOYMENTS]: StartAllocationParams[];
|
||||
}
|
||||
|
@ -94,6 +100,8 @@ export type TMlStorageMapped<T extends MlStorageKey> = T extends typeof ML_ENTIT
|
|||
? number | undefined
|
||||
: T extends typeof ML_OVERVIEW_PANELS
|
||||
? OverviewPanelsState | undefined
|
||||
: T extends typeof ML_OVERVIEW_PANELS_EXTENDED
|
||||
? OverviewPanelsExtendedState | undefined
|
||||
: T extends typeof ML_ELSER_CALLOUT_DISMISSED
|
||||
? boolean | undefined
|
||||
: T extends typeof ML_SCHEDULED_MODEL_DEPLOYMENTS
|
||||
|
@ -108,6 +116,7 @@ export const ML_STORAGE_KEYS = [
|
|||
ML_ANOMALY_EXPLORER_PANELS,
|
||||
ML_NOTIFICATIONS_LAST_CHECKED_AT,
|
||||
ML_OVERVIEW_PANELS,
|
||||
ML_OVERVIEW_PANELS_EXTENDED,
|
||||
ML_ELSER_CALLOUT_DISMISSED,
|
||||
ML_SCHEDULED_MODEL_DEPLOYMENTS,
|
||||
] as const;
|
||||
|
|
|
@ -64,9 +64,11 @@ const MlAnomalyAlertTrigger: FC<MlAnomalyAlertTriggerProps> = ({
|
|||
if (!mlCapabilities.canCreateJob) return;
|
||||
|
||||
getStartServices().then((startServices) => {
|
||||
const locator = startServices[2].locator;
|
||||
if (!locator) return;
|
||||
locator.getUrl({ page: ML_PAGES.ANOMALY_DETECTION_CREATE_JOB }).then((url) => {
|
||||
const { managementLocator } = startServices[2];
|
||||
if (!managementLocator) return;
|
||||
managementLocator
|
||||
.getUrl({ page: ML_PAGES.ANOMALY_DETECTION_CREATE_JOB }, 'anomaly_detection')
|
||||
.then(({ url }) => {
|
||||
if (mounted) {
|
||||
setNewJobUrl(url);
|
||||
}
|
||||
|
@ -76,7 +78,7 @@ const MlAnomalyAlertTrigger: FC<MlAnomalyAlertTriggerProps> = ({
|
|||
return () => {
|
||||
mounted = false;
|
||||
};
|
||||
}, [getStartServices, mlCapabilities]);
|
||||
}, [mlCapabilities, getStartServices]);
|
||||
|
||||
const mlHttpService = useMemo(() => new HttpService(http!), [http]);
|
||||
const adJobsApiService = useMemo(() => jobsApiProvider(mlHttpService), [mlHttpService]);
|
||||
|
|
|
@ -30,6 +30,7 @@ export const AccessDeniedCallout: FC<AccessDeniedCalloutProps> = ({ missingCapab
|
|||
return (
|
||||
<>
|
||||
<EuiPageTemplate.EmptyPrompt
|
||||
data-test-subj="mlAccessDenied"
|
||||
color={'danger'}
|
||||
alignment={'horizontalCenter'}
|
||||
iconType="warning"
|
||||
|
|
|
@ -18,6 +18,8 @@ import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render';
|
|||
import { StorageContextProvider } from '@kbn/ml-local-storage';
|
||||
import useLifecycles from 'react-use/lib/useLifecycles';
|
||||
import useObservable from 'react-use/lib/useObservable';
|
||||
import type { ManagementAppMountParams } from '@kbn/management-plugin/public';
|
||||
import { isPopulatedObject } from '@kbn/ml-is-populated-object';
|
||||
import type { ExperimentalFeatures, MlFeatures, NLPSettings } from '../../common/constants/app';
|
||||
import { ML_STORAGE_KEYS } from '../../common/types/storage';
|
||||
import type { MlSetupDependencies, MlStartDependencies } from '../plugin';
|
||||
|
@ -28,6 +30,7 @@ import { EnabledFeaturesContextProvider, MlServerInfoContextProvider } from './c
|
|||
import type { StartServices } from './contexts/kibana';
|
||||
import { getMlGlobalServices } from './util/get_services';
|
||||
import { MlTelemetryContextProvider } from './contexts/ml/ml_telemetry_context';
|
||||
import type { ManagementSectionId } from './management';
|
||||
|
||||
export type MlDependencies = Omit<
|
||||
MlSetupDependencies,
|
||||
|
@ -38,11 +41,12 @@ export type MlDependencies = Omit<
|
|||
interface AppProps {
|
||||
coreStart: CoreStart;
|
||||
deps: MlDependencies;
|
||||
appMountParams: AppMountParameters;
|
||||
appMountParams: ManagementAppMountParams | AppMountParameters;
|
||||
isServerless: boolean;
|
||||
mlFeatures: MlFeatures;
|
||||
experimentalFeatures: ExperimentalFeatures;
|
||||
nlpSettings: NLPSettings;
|
||||
entryPoint?: ManagementSectionId;
|
||||
}
|
||||
|
||||
const localStorage = new Storage(window.localStorage);
|
||||
|
@ -53,7 +57,7 @@ export interface MlServicesContext {
|
|||
|
||||
export type MlGlobalServices = ReturnType<typeof getMlGlobalServices>;
|
||||
|
||||
const App: FC<AppProps> = ({
|
||||
export const App: FC<AppProps> = ({
|
||||
coreStart,
|
||||
deps,
|
||||
appMountParams,
|
||||
|
@ -61,10 +65,13 @@ const App: FC<AppProps> = ({
|
|||
mlFeatures,
|
||||
experimentalFeatures,
|
||||
nlpSettings,
|
||||
entryPoint,
|
||||
}) => {
|
||||
const pageDeps: PageDependencies = {
|
||||
history: appMountParams.history,
|
||||
setHeaderActionMenu: appMountParams.setHeaderActionMenu,
|
||||
setHeaderActionMenu: isPopulatedObject(appMountParams, ['setHeaderActionMenu'])
|
||||
? appMountParams.setHeaderActionMenu
|
||||
: undefined,
|
||||
setBreadcrumbs: coreStart.chrome!.setBreadcrumbs,
|
||||
};
|
||||
|
||||
|
@ -156,7 +163,7 @@ const App: FC<AppProps> = ({
|
|||
>
|
||||
<MlServerInfoContextProvider nlpSettings={nlpSettings}>
|
||||
<MlTelemetryContextProvider telemetryClient={deps.telemetry}>
|
||||
<MlRouter pageDeps={pageDeps} />
|
||||
<MlRouter pageDeps={pageDeps} entryPoint={entryPoint} />
|
||||
</MlTelemetryContextProvider>
|
||||
</MlServerInfoContextProvider>
|
||||
</EnabledFeaturesContextProvider>
|
||||
|
|
|
@ -26,6 +26,7 @@ export interface CollapsiblePanelProps {
|
|||
header: React.ReactElement;
|
||||
headerItems?: React.ReactElement[];
|
||||
ariaLabel: string;
|
||||
dataTestSubj?: string;
|
||||
}
|
||||
|
||||
export const CollapsiblePanel: FC<PropsWithChildren<CollapsiblePanelProps>> = ({
|
||||
|
@ -35,11 +36,13 @@ export const CollapsiblePanel: FC<PropsWithChildren<CollapsiblePanelProps>> = ({
|
|||
header,
|
||||
headerItems,
|
||||
ariaLabel,
|
||||
dataTestSubj,
|
||||
}) => {
|
||||
const { euiTheme } = useEuiTheme();
|
||||
|
||||
return (
|
||||
<EuiSplitPanel.Outer
|
||||
data-test-subj={dataTestSubj}
|
||||
grow
|
||||
hasShadow={false}
|
||||
css={{
|
||||
|
@ -100,7 +103,7 @@ export const CollapsiblePanel: FC<PropsWithChildren<CollapsiblePanelProps>> = ({
|
|||
|
||||
export interface StatEntry {
|
||||
label: string;
|
||||
value: number;
|
||||
value: number | string;
|
||||
'data-test-subj'?: string;
|
||||
}
|
||||
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -37,7 +37,9 @@ export const HeaderMenuPortal: FC<PropsWithChildren<unknown>> = ({ children }) =
|
|||
|
||||
return () => {
|
||||
portalNode.unmount();
|
||||
if (setHeaderActionMenu) {
|
||||
setHeaderActionMenu(undefined);
|
||||
}
|
||||
};
|
||||
}, [portalNode, setHeaderActionMenu, services]);
|
||||
|
||||
|
|
|
@ -9,7 +9,6 @@ import {
|
|||
EuiButton,
|
||||
EuiButtonEmpty,
|
||||
EuiCheckbox,
|
||||
EuiConfirmModal,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiFlyout,
|
||||
|
@ -18,12 +17,10 @@ import {
|
|||
EuiFlyoutHeader,
|
||||
EuiLoadingSpinner,
|
||||
EuiSpacer,
|
||||
EuiTab,
|
||||
EuiTabs,
|
||||
EuiTitle,
|
||||
} from '@elastic/eui';
|
||||
import type { FC } from 'react';
|
||||
import { useEffect, useMemo, useCallback } from 'react';
|
||||
import { useEffect, useMemo } from 'react';
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
|
@ -47,39 +44,6 @@ const LoadingSpinner: FC = () => (
|
|||
</>
|
||||
);
|
||||
|
||||
const SwitchTabsConfirm: FC<{ onCancel: () => void; onConfirm: () => void }> = ({
|
||||
onCancel,
|
||||
onConfirm,
|
||||
}) => (
|
||||
<EuiConfirmModal
|
||||
title={i18n.translate('xpack.ml.importExport.exportFlyout.switchTabsConfirm.title', {
|
||||
defaultMessage: 'Change tabs?',
|
||||
})}
|
||||
onCancel={onCancel}
|
||||
onConfirm={onConfirm}
|
||||
cancelButtonText={i18n.translate(
|
||||
'xpack.ml.importExport.exportFlyout.switchTabsConfirm.cancelButton',
|
||||
{
|
||||
defaultMessage: 'Cancel',
|
||||
}
|
||||
)}
|
||||
confirmButtonText={i18n.translate(
|
||||
'xpack.ml.importExport.exportFlyout.switchTabsConfirm.confirmButton',
|
||||
{
|
||||
defaultMessage: 'Confirm',
|
||||
}
|
||||
)}
|
||||
defaultFocusedButton="confirm"
|
||||
>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.importExport.exportFlyout.switchTabsConfirm.text"
|
||||
defaultMessage="Changing tabs will clear currently selected jobs"
|
||||
/>
|
||||
</p>
|
||||
</EuiConfirmModal>
|
||||
);
|
||||
|
||||
export interface ExportJobsFlyoutContentProps {
|
||||
currentTab: JobType;
|
||||
isADEnabled: boolean;
|
||||
|
@ -119,8 +83,6 @@ export const ExportJobsFlyoutContent = ({
|
|||
const [loadingDFAJobs, setLoadingDFAJobs] = useState(true);
|
||||
|
||||
const [exporting, setExporting] = useState(false);
|
||||
const [switchTabConfirmVisible, setSwitchTabConfirmVisible] = useState(false);
|
||||
const [switchTabNextTab, setSwitchTabNextTab] = useState<JobType>(currentTab);
|
||||
const [selectedJobDependencies, setSelectedJobDependencies] = useState<JobDependencies>([]);
|
||||
|
||||
async function onExport() {
|
||||
|
@ -164,12 +126,6 @@ export const ExportJobsFlyoutContent = ({
|
|||
}
|
||||
}
|
||||
|
||||
function switchTab(jobType: JobType) {
|
||||
setSwitchTabConfirmVisible(false);
|
||||
setSelectedJobIds([]);
|
||||
setSelectedJobType(jobType);
|
||||
}
|
||||
|
||||
function onSelectAll() {
|
||||
const ids = selectedJobType === 'anomaly-detector' ? adJobIds : dfaJobIds;
|
||||
if (selectedJobIds.length === ids.length) {
|
||||
|
@ -179,25 +135,6 @@ export const ExportJobsFlyoutContent = ({
|
|||
}
|
||||
}
|
||||
|
||||
const attemptTabSwitch = useCallback(
|
||||
(jobType: JobType) => {
|
||||
if (jobType === selectedJobType) {
|
||||
return;
|
||||
}
|
||||
// if the user has already selected some jobs, open a confirm modal
|
||||
// rather than changing tabs
|
||||
if (selectedJobIds.length > 0) {
|
||||
setSwitchTabNextTab(jobType);
|
||||
setSwitchTabConfirmVisible(true);
|
||||
return;
|
||||
}
|
||||
|
||||
switchTab(jobType);
|
||||
},
|
||||
|
||||
[selectedJobIds, selectedJobType]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
setSelectedJobDependencies(
|
||||
jobDependencies.filter(({ jobId }) => selectedJobIds.includes(jobId))
|
||||
|
@ -209,7 +146,6 @@ export const ExportJobsFlyoutContent = ({
|
|||
setLoadingADJobs(true);
|
||||
setLoadingDFAJobs(true);
|
||||
setExporting(false);
|
||||
setSwitchTabConfirmVisible(false);
|
||||
setAdJobIds([]);
|
||||
setSelectedJobIds([]);
|
||||
setSelectedJobType(currentTab);
|
||||
|
@ -282,41 +218,27 @@ export const ExportJobsFlyoutContent = ({
|
|||
<h2>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.importExport.exportFlyout.flyoutHeader"
|
||||
defaultMessage="Export jobs"
|
||||
defaultMessage="Export {selectedMLType} jobs"
|
||||
values={{
|
||||
selectedMLType:
|
||||
currentTab === 'anomaly-detector' ? (
|
||||
<FormattedMessage
|
||||
id="xpack.ml.importExport.exportFlyout.adTab"
|
||||
defaultMessage="Anomaly detection"
|
||||
/>
|
||||
) : (
|
||||
<FormattedMessage
|
||||
id="xpack.ml.importExport.exportFlyout.dfaTab"
|
||||
defaultMessage="Analytics"
|
||||
/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</h2>
|
||||
</EuiTitle>
|
||||
</EuiFlyoutHeader>
|
||||
<EuiFlyoutBody>
|
||||
<ExportJobDependenciesWarningCallout jobs={selectedJobDependencies} />
|
||||
<EuiTabs size="s">
|
||||
{isADEnabled === true ? (
|
||||
<EuiTab
|
||||
isSelected={selectedJobType === 'anomaly-detector'}
|
||||
onClick={() => attemptTabSwitch('anomaly-detector')}
|
||||
disabled={exporting}
|
||||
data-test-subj="mlJobMgmtExportJobsADTab"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.importExport.exportFlyout.adTab"
|
||||
defaultMessage="Anomaly detection"
|
||||
/>
|
||||
</EuiTab>
|
||||
) : null}
|
||||
{isDFAEnabled === true ? (
|
||||
<EuiTab
|
||||
isSelected={selectedJobType === 'data-frame-analytics'}
|
||||
onClick={() => attemptTabSwitch('data-frame-analytics')}
|
||||
disabled={exporting}
|
||||
data-test-subj="mlJobMgmtExportJobsDFATab"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.importExport.exportFlyout.dfaTab"
|
||||
defaultMessage="Analytics"
|
||||
/>
|
||||
</EuiTab>
|
||||
) : null}
|
||||
</EuiTabs>
|
||||
<EuiSpacer size="s" />
|
||||
<>
|
||||
{isADEnabled === true && selectedJobType === 'anomaly-detector' && (
|
||||
|
@ -432,13 +354,6 @@ export const ExportJobsFlyoutContent = ({
|
|||
</EuiFlexGroup>
|
||||
</EuiFlyoutFooter>
|
||||
</EuiFlyout>
|
||||
|
||||
{switchTabConfirmVisible === true ? (
|
||||
<SwitchTabsConfirm
|
||||
onCancel={setSwitchTabConfirmVisible.bind(null, false)}
|
||||
onConfirm={() => switchTab(switchTabNextTab)}
|
||||
/>
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -43,15 +43,13 @@ export const AnomalyDetectionInfoButton: FC<Props> = ({
|
|||
prefix: 'adJobInfoContextMenu',
|
||||
suffix: jobId,
|
||||
});
|
||||
const onButtonClick = () => {
|
||||
setPopover(!isPopoverOpen);
|
||||
};
|
||||
const onButtonClick = () => setPopover((prev) => !prev);
|
||||
const closePopover = () => {
|
||||
setPopover(false);
|
||||
};
|
||||
|
||||
const { setActiveFlyout, setActiveJobId } = useJobInfoFlyouts();
|
||||
const panels = useMemo(
|
||||
const panels = useMemo<EuiContextMenuPanelDescriptor[]>(
|
||||
() => {
|
||||
return [
|
||||
{
|
||||
|
@ -71,7 +69,7 @@ export const AnomalyDetectionInfoButton: FC<Props> = ({
|
|||
share,
|
||||
}),
|
||||
},
|
||||
] as EuiContextMenuPanelDescriptor[];
|
||||
];
|
||||
},
|
||||
// globalState is an object with references change on every render, so we are stringifying it here
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
|
|
|
@ -10,6 +10,7 @@ import { render } from '@testing-library/react';
|
|||
import type { IdBadgesProps } from './id_badges';
|
||||
import { IdBadges } from './id_badges';
|
||||
import type { MlSummaryJob } from '../../../../../common/types/anomaly_detection_jobs';
|
||||
import { ML_PAGES } from '../../../../locator';
|
||||
|
||||
jest.mock('../../../contexts/kibana', () => ({
|
||||
useMlKibana: () => ({
|
||||
|
@ -21,7 +22,7 @@ jest.mock('../../../contexts/kibana', () => ({
|
|||
}));
|
||||
|
||||
const props: IdBadgesProps = {
|
||||
page: 'jobs',
|
||||
page: ML_PAGES.ANOMALY_DETECTION_JOBS_MANAGE,
|
||||
limit: 2,
|
||||
selectedGroups: [
|
||||
{
|
||||
|
|
|
@ -5,9 +5,10 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import React, { useState, useEffect, useMemo, useCallback } from 'react';
|
||||
import React, { useState, useEffect, useCallback, useMemo } from 'react';
|
||||
|
||||
import { EuiButtonEmpty, EuiFlexItem, EuiFlexGroup, EuiHorizontalRule } from '@elastic/eui';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
|
||||
|
@ -23,6 +24,8 @@ import type {
|
|||
import { FeedBackButton } from '../feedback_button';
|
||||
import { JobInfoFlyoutsProvider } from '../../jobs/components/job_details_flyout';
|
||||
import { JobInfoFlyoutsManager } from '../../jobs/components/job_details_flyout/job_details_context_manager';
|
||||
import { usePermissionCheck } from '../../capabilities/check_capabilities';
|
||||
import { useCreateAndNavigateToManagementMlLink } from '../../contexts/kibana/use_create_url';
|
||||
import { useJobSelectionFlyout } from '../../contexts/ml/use_job_selection_flyout';
|
||||
|
||||
export interface GroupObj {
|
||||
|
@ -142,6 +145,11 @@ export function JobSelector({
|
|||
setSelectedIds(newSelection);
|
||||
onSelectionChange?.({ jobIds: newSelection, time: undefined });
|
||||
};
|
||||
|
||||
const [canGetJobs, canCreateJob] = usePermissionCheck(['canGetJobs', 'canCreateJob']);
|
||||
|
||||
const redirectToADJobManagement = useCreateAndNavigateToManagementMlLink('', 'anomaly_detection');
|
||||
|
||||
function renderJobSelectionBar() {
|
||||
return (
|
||||
<>
|
||||
|
@ -193,6 +201,30 @@ export function JobSelector({
|
|||
<EuiFlexItem grow={false}>
|
||||
<FeedBackButton jobIds={selectedIds} page={page} />
|
||||
</EuiFlexItem>
|
||||
|
||||
{canGetJobs ? (
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonEmpty
|
||||
size="s"
|
||||
color="primary"
|
||||
onClick={redirectToADJobManagement}
|
||||
disabled={!canGetJobs}
|
||||
data-test-subj="mlJobSelectorManageJobsButton"
|
||||
>
|
||||
{canCreateJob ? (
|
||||
<FormattedMessage
|
||||
id="xpack.ml.jobSelector.manageJobsLinkLabel"
|
||||
defaultMessage="Manage jobs"
|
||||
/>
|
||||
) : (
|
||||
<FormattedMessage
|
||||
id="xpack.ml.jobSelector.viewJobsLinkLabel"
|
||||
defaultMessage="View jobs"
|
||||
/>
|
||||
)}
|
||||
</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
) : null}
|
||||
</EuiFlexGroup>
|
||||
<EuiHorizontalRule margin="s" />
|
||||
</>
|
||||
|
|
|
@ -0,0 +1,116 @@
|
|||
/*
|
||||
* 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, { useMemo, useState } from 'react';
|
||||
import type { EuiContextMenuPanelDescriptor } from '@elastic/eui';
|
||||
import { EuiButton, EuiContextMenu, EuiPopover, useGeneratedHtmlId } from '@elastic/eui';
|
||||
import { useUrlState } from '@kbn/ml-url-state';
|
||||
import { ML_PAGES, type MlPages } from '../../../../../common/constants/locator';
|
||||
import { useJobInfoFlyouts } from '../../../jobs/components/job_details_flyout';
|
||||
import { useMlKibana } from '../../../contexts/kibana';
|
||||
import { getOptionsForJobSelectorMenuItems } from '../group_or_job_selector_menu/get_options_for_job_selector_menu';
|
||||
|
||||
interface Props {
|
||||
jobId: string;
|
||||
page: MlPages;
|
||||
onRemoveJobId: (jobOrGroupId: string[]) => void;
|
||||
removeJobIdDisabled: boolean;
|
||||
isSingleMetricViewerDisabled: boolean;
|
||||
}
|
||||
|
||||
export const AnomalyDetectionInfoButton: FC<Props> = ({
|
||||
jobId,
|
||||
page,
|
||||
onRemoveJobId,
|
||||
removeJobIdDisabled,
|
||||
isSingleMetricViewerDisabled,
|
||||
}) => {
|
||||
const [isPopoverOpen, setPopover] = useState(false);
|
||||
const {
|
||||
services: {
|
||||
share,
|
||||
application: { navigateToUrl },
|
||||
},
|
||||
} = useMlKibana();
|
||||
const [globalState] = useUrlState('_g');
|
||||
|
||||
const popoverId = useGeneratedHtmlId({
|
||||
prefix: 'adJobInfoContextMenu',
|
||||
suffix: jobId,
|
||||
});
|
||||
const onButtonClick = () => {
|
||||
setPopover(!isPopoverOpen);
|
||||
};
|
||||
const closePopover = () => {
|
||||
setPopover(false);
|
||||
};
|
||||
|
||||
const { setActiveFlyout, setActiveJobId } = useJobInfoFlyouts();
|
||||
const panels = useMemo(
|
||||
() => {
|
||||
return [
|
||||
{
|
||||
id: 0,
|
||||
items: getOptionsForJobSelectorMenuItems({
|
||||
jobId,
|
||||
page,
|
||||
onRemoveJobId,
|
||||
removeJobIdDisabled,
|
||||
showRemoveJobId: page === ML_PAGES.ANOMALY_EXPLORER,
|
||||
isSingleMetricViewerDisabled,
|
||||
closePopover,
|
||||
globalState,
|
||||
setActiveFlyout,
|
||||
setActiveJobId,
|
||||
navigateToUrl,
|
||||
share,
|
||||
}),
|
||||
},
|
||||
] as EuiContextMenuPanelDescriptor[];
|
||||
},
|
||||
// globalState is an object with references change on every render, so we are stringifying it here
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[
|
||||
jobId,
|
||||
page,
|
||||
setActiveJobId,
|
||||
setActiveFlyout,
|
||||
navigateToUrl,
|
||||
share.url.locators,
|
||||
removeJobIdDisabled,
|
||||
onRemoveJobId,
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
JSON.stringify(globalState),
|
||||
]
|
||||
);
|
||||
|
||||
const button = (
|
||||
<EuiButton
|
||||
data-test-subj="mlJobSelectionBadge"
|
||||
iconType="boxesVertical"
|
||||
iconSide="right"
|
||||
onClick={onButtonClick}
|
||||
size="s"
|
||||
color="text"
|
||||
>
|
||||
{jobId}
|
||||
</EuiButton>
|
||||
);
|
||||
return (
|
||||
<EuiPopover
|
||||
id={popoverId}
|
||||
button={button}
|
||||
isOpen={isPopoverOpen}
|
||||
closePopover={closePopover}
|
||||
panelPaddingSize="none"
|
||||
anchorPosition="downLeft"
|
||||
>
|
||||
<EuiContextMenu initialPanelId={0} panels={panels} />
|
||||
</EuiPopover>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* 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 React, { useEffect, useState } from 'react';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { map, distinctUntilChanged } from 'rxjs';
|
||||
import { DatePickerWrapper } from '@kbn/ml-date-picker';
|
||||
import { useMlKibana } from '../../contexts/kibana';
|
||||
|
||||
export const DatePicker = () => {
|
||||
const {
|
||||
services: {
|
||||
mlServices: { httpService },
|
||||
},
|
||||
} = useMlKibana();
|
||||
|
||||
useEffect(() => {
|
||||
const subscriptions = new Subscription();
|
||||
|
||||
subscriptions.add(
|
||||
httpService.getLoadingCount$
|
||||
.pipe(
|
||||
map((v) => v !== 0),
|
||||
distinctUntilChanged()
|
||||
)
|
||||
.subscribe((loading) => {
|
||||
setIsLoading(loading);
|
||||
})
|
||||
);
|
||||
return function cleanup() {
|
||||
subscriptions.unsubscribe();
|
||||
};
|
||||
}, [httpService?.getLoadingCount$]);
|
||||
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
return (
|
||||
<DatePickerWrapper
|
||||
isLoading={isLoading}
|
||||
width="full"
|
||||
dataTestSubj="mlDatePickerRefreshPageButton"
|
||||
/>
|
||||
);
|
||||
};
|
|
@ -8,10 +8,11 @@
|
|||
import type { FC } from 'react';
|
||||
import React, { createContext, useEffect, useMemo, useState } from 'react';
|
||||
import { createHtmlPortalNode, type HtmlPortalNode } from 'react-reverse-portal';
|
||||
import { Redirect } from 'react-router-dom';
|
||||
import { Redirect, useLocation } from 'react-router-dom';
|
||||
import { Routes, Route } from '@kbn/shared-ux-router';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { EuiPageSection } from '@elastic/eui';
|
||||
import type { EuiPaddingSize } from '@elastic/eui';
|
||||
import { EuiPageSection, EuiPageHeader } from '@elastic/eui';
|
||||
import { map, distinctUntilChanged } from 'rxjs';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
@ -19,10 +20,16 @@ import { type AppMountParameters } from '@kbn/core/public';
|
|||
import { KibanaPageTemplate } from '@kbn/shared-ux-page-kibana-template';
|
||||
import { RedirectAppLinks } from '@kbn/shared-ux-link-redirect-app';
|
||||
import { DatePickerWrapper } from '@kbn/ml-date-picker';
|
||||
|
||||
import { DEPRECATED_ML_ROUTE_TO_NEW_ROUTE } from '../../../../common/constants/locator';
|
||||
import * as routes from '../../routing/routes';
|
||||
import * as overviewRoutes from '../../routing/routes/overview_management';
|
||||
import * as anomalyDetectionRoutes from '../../routing/routes/anomaly_detection_management';
|
||||
import * as dfaRoutes from '../../routing/routes/data_frame_analytics_management';
|
||||
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 { useMlKibana, useMlManagementLocator, useNavigateToPath } from '../../contexts/kibana';
|
||||
import type { NavigateToPath } from '../../contexts/kibana';
|
||||
import type { MlRoute, PageDependencies } from '../../routing/router';
|
||||
import { useActiveRoute } from '../../routing/use_active_route';
|
||||
import { useDocTitle } from '../../routing/use_doc_title';
|
||||
|
@ -31,6 +38,15 @@ import { MlPageHeaderRenderer } from '../page_header/page_header';
|
|||
|
||||
import { useSideNavItems } from './side_nav';
|
||||
import { useEnabledFeatures } from '../../contexts/ml';
|
||||
import { MANAGEMENT_SECTION_IDS } from '../../management';
|
||||
import type { NavigateToApp } from '../../routing/breadcrumbs';
|
||||
interface RouteToPath {
|
||||
[key: string]: (navigateToPath: NavigateToPath, basePath: string) => MlRoute;
|
||||
}
|
||||
interface RouteToApp {
|
||||
[key: string]: (navigateToApp: NavigateToApp, basePath: string) => MlRoute;
|
||||
}
|
||||
type RouteModules = RouteToPath | RouteToApp;
|
||||
|
||||
const ML_APP_SELECTOR = '[data-test-subj="mlApp"]';
|
||||
|
||||
|
@ -50,10 +66,42 @@ export const MlPageControlsContext = createContext<{
|
|||
* Main page component of the ML App
|
||||
* @constructor
|
||||
*/
|
||||
export const MlPage: FC<{ pageDeps: PageDependencies }> = React.memo(({ pageDeps }) => {
|
||||
export const MlPage: FC<{ pageDeps: PageDependencies; entryPoint?: string }> = React.memo(
|
||||
({ pageDeps, entryPoint }) => {
|
||||
const navigateToPath = useNavigateToPath();
|
||||
const { pathname, search } = useLocation();
|
||||
const mlManagementLocator = useMlManagementLocator();
|
||||
|
||||
useEffect(
|
||||
// Auto-redirect bookmarked jobs list and data frame analytics list
|
||||
// to the new management pages, and keep the search string as much
|
||||
function autoRedirectToManagementPages() {
|
||||
if (mlManagementLocator) {
|
||||
const searchString = decodeURIComponent(search);
|
||||
let decodedSearch = search;
|
||||
const oldPath = pathname.slice(1);
|
||||
const newPath =
|
||||
DEPRECATED_ML_ROUTE_TO_NEW_ROUTE[
|
||||
oldPath as keyof typeof DEPRECATED_ML_ROUTE_TO_NEW_ROUTE
|
||||
];
|
||||
if (oldPath && newPath) {
|
||||
if (oldPath !== newPath) {
|
||||
decodedSearch = searchString.replace(`=(${oldPath}:`, `=('':`);
|
||||
}
|
||||
mlManagementLocator.navigate({
|
||||
sectionId: 'ml',
|
||||
appId: `${newPath}${decodedSearch}`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
[pathname, navigateToPath, mlManagementLocator, search]
|
||||
);
|
||||
|
||||
const {
|
||||
services: {
|
||||
application: { navigateToApp },
|
||||
http: { basePath },
|
||||
mlServices: { httpService },
|
||||
},
|
||||
|
@ -83,12 +131,43 @@ export const MlPage: FC<{ pageDeps: PageDependencies }> = React.memo(({ pageDeps
|
|||
}, [httpService?.getLoadingCount$]);
|
||||
|
||||
const routeList = useMemo(
|
||||
() =>
|
||||
Object.values(routes)
|
||||
() => {
|
||||
let currentRoutes: RouteModules = routes as RouteToPath;
|
||||
|
||||
switch (entryPoint) {
|
||||
case MANAGEMENT_SECTION_IDS.OVERVIEW:
|
||||
currentRoutes = overviewRoutes as RouteToApp;
|
||||
break;
|
||||
case MANAGEMENT_SECTION_IDS.ANOMALY_DETECTION:
|
||||
currentRoutes = anomalyDetectionRoutes as RouteToApp;
|
||||
break;
|
||||
case MANAGEMENT_SECTION_IDS.ANALYTICS:
|
||||
currentRoutes = dfaRoutes as RouteToApp;
|
||||
break;
|
||||
case MANAGEMENT_SECTION_IDS.TRAINED_MODELS:
|
||||
currentRoutes = trainedModelsRoutes as RouteToApp;
|
||||
break;
|
||||
case MANAGEMENT_SECTION_IDS.AD_SETTINGS:
|
||||
currentRoutes = settingsRoutes as RouteToApp;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
const currentRoutesList = Object.values(currentRoutes);
|
||||
|
||||
if (entryPoint !== undefined) {
|
||||
return currentRoutesList
|
||||
.map((routeFactory) => routeFactory(navigateToApp))
|
||||
.filter((d) => !d.disabled);
|
||||
} else {
|
||||
return currentRoutesList
|
||||
.map((routeFactory) => routeFactory(navigateToPath, basePath.get()))
|
||||
.filter((d) => !d.disabled),
|
||||
.filter((d) => !d.disabled);
|
||||
}
|
||||
},
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[]
|
||||
[entryPoint]
|
||||
);
|
||||
|
||||
const activeRoute = useActiveRoute(routeList);
|
||||
|
@ -129,6 +208,7 @@ export const MlPage: FC<{ pageDeps: PageDependencies }> = React.memo(({ pageDeps
|
|||
isHeaderMounted,
|
||||
}}
|
||||
>
|
||||
{entryPoint === undefined ? (
|
||||
<KibanaPageTemplate
|
||||
className={'ml-app'}
|
||||
data-test-subj={'mlApp'}
|
||||
|
@ -158,18 +238,33 @@ export const MlPage: FC<{ pageDeps: PageDependencies }> = React.memo(({ pageDeps
|
|||
routeList={routeList}
|
||||
/>
|
||||
</KibanaPageTemplate>
|
||||
) : (
|
||||
<>
|
||||
<EuiPageHeader pageTitle={<MlPageHeaderRenderer />} rightSideItems={rightSideItems} />
|
||||
<CommonPageWrapper
|
||||
headerPortal={headerPortalNode}
|
||||
setIsHeaderMounted={setIsHeaderMounted}
|
||||
pageDeps={pageDeps}
|
||||
routeList={routeList}
|
||||
paddingSize="none"
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</MlPageControlsContext.Provider>
|
||||
);
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
interface CommonPageWrapperProps {
|
||||
setIsHeaderMounted: (v: boolean) => void;
|
||||
pageDeps: PageDependencies;
|
||||
routeList: MlRoute[];
|
||||
headerPortal: HtmlPortalNode;
|
||||
paddingSize?: EuiPaddingSize;
|
||||
}
|
||||
|
||||
const CommonPageWrapper: FC<CommonPageWrapperProps> = React.memo(({ pageDeps, routeList }) => {
|
||||
const CommonPageWrapper: FC<CommonPageWrapperProps> = React.memo(
|
||||
({ pageDeps, routeList, paddingSize }) => {
|
||||
const {
|
||||
services: { application },
|
||||
} = useMlKibana();
|
||||
|
@ -178,7 +273,7 @@ const CommonPageWrapper: FC<CommonPageWrapperProps> = React.memo(({ pageDeps, ro
|
|||
/** RedirectAppLinks intercepts all <a> tags to use navigateToUrl
|
||||
* avoiding full page reload **/
|
||||
<RedirectAppLinks coreStart={{ application }}>
|
||||
<EuiPageSection restrictWidth={false}>
|
||||
<EuiPageSection restrictWidth={false} paddingSize={paddingSize}>
|
||||
<Routes>
|
||||
{routeList.map((route) => {
|
||||
return (
|
||||
|
@ -191,7 +286,9 @@ const CommonPageWrapper: FC<CommonPageWrapperProps> = React.memo(({ pageDeps, ro
|
|||
pageDeps.setBreadcrumbs(route.breadcrumbs);
|
||||
});
|
||||
return (
|
||||
<MlPageWrapper path={route.path}>{route.render(props, pageDeps)}</MlPageWrapper>
|
||||
<MlPageWrapper path={route.path}>
|
||||
{route.render(props, pageDeps)}
|
||||
</MlPageWrapper>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
|
@ -202,4 +299,5 @@ const CommonPageWrapper: FC<CommonPageWrapperProps> = React.memo(({ pageDeps, ro
|
|||
</EuiPageSection>
|
||||
</RedirectAppLinks>
|
||||
);
|
||||
});
|
||||
}
|
||||
);
|
||||
|
|
|
@ -8,16 +8,15 @@
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import type { EuiSideNavItemType } from '@elastic/eui';
|
||||
import type { ReactNode } from 'react';
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { CHANGE_POINT_DETECTION_ENABLED } from '@kbn/aiops-change-point-detection/constants';
|
||||
import { useUrlState } from '@kbn/ml-url-state';
|
||||
import { NotificationsIndicator } from './notifications_indicator';
|
||||
import type { MlLocatorParams } from '../../../../common/types/locator';
|
||||
import { useMlLocator, useNavigateToPath } from '../../contexts/kibana';
|
||||
import { isFullLicense } from '../../license';
|
||||
import type { MlRoute } from '../../routing';
|
||||
import { ML_PAGES } from '../../../../common/constants/locator';
|
||||
import { usePermissionCheck } from '../../capabilities/check_capabilities';
|
||||
import { useEnabledFeatures } from '../../contexts/ml';
|
||||
|
||||
export interface Tab {
|
||||
id: string;
|
||||
|
@ -38,7 +37,7 @@ export function useSideNavItems(activeRoute: MlRoute | undefined) {
|
|||
const navigateToPath = useNavigateToPath();
|
||||
|
||||
const mlFeaturesDisabled = !isFullLicense();
|
||||
const canViewMlNodes = usePermissionCheck('canViewMlNodes');
|
||||
const { isADEnabled, isDFAEnabled } = useEnabledFeatures();
|
||||
|
||||
const [globalState] = useUrlState('_g');
|
||||
|
||||
|
@ -85,51 +84,31 @@ export function useSideNavItems(activeRoute: MlRoute | undefined) {
|
|||
testSubj: 'mlMainTab overview',
|
||||
},
|
||||
{
|
||||
id: 'notifications',
|
||||
pathId: ML_PAGES.NOTIFICATIONS,
|
||||
name: disableLinks ? (
|
||||
i18n.translate('xpack.ml.navMenu.notificationsTabLinkText', {
|
||||
defaultMessage: 'Notifications',
|
||||
})
|
||||
) : (
|
||||
<NotificationsIndicator />
|
||||
),
|
||||
disabled: disableLinks,
|
||||
testSubj: 'mlMainTab notifications',
|
||||
},
|
||||
{
|
||||
id: 'memory_usage',
|
||||
pathId: ML_PAGES.MEMORY_USAGE,
|
||||
name: i18n.translate('xpack.ml.navMenu.memoryUsageText', {
|
||||
defaultMessage: 'Memory Usage',
|
||||
id: 'datavisualizer',
|
||||
name: i18n.translate('xpack.ml.navMenu.dataVisualizerTabLinkText', {
|
||||
defaultMessage: 'Data Visualizer',
|
||||
}),
|
||||
disabled: disableLinks || !canViewMlNodes,
|
||||
testSubj: 'mlMainTab nodesOverview',
|
||||
disabled: false,
|
||||
pathId: ML_PAGES.DATA_VISUALIZER,
|
||||
testSubj: 'mlMainTab dataVisualizer',
|
||||
},
|
||||
],
|
||||
},
|
||||
...(isADEnabled
|
||||
? [
|
||||
{
|
||||
id: 'anomaly_detection_section',
|
||||
name: i18n.translate('xpack.ml.navMenu.anomalyDetectionTabLinkText', {
|
||||
defaultMessage: 'Anomaly Detection',
|
||||
}),
|
||||
disabled: disableLinks,
|
||||
disabled: disableLinks || !isADEnabled,
|
||||
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', {
|
||||
defaultMessage: 'Anomaly Explorer',
|
||||
}),
|
||||
disabled: disableLinks,
|
||||
disabled: disableLinks || !isADEnabled,
|
||||
pathId: ML_PAGES.ANOMALY_EXPLORER,
|
||||
testSubj: 'mlMainTab anomalyExplorer',
|
||||
},
|
||||
|
@ -139,56 +118,29 @@ export function useSideNavItems(activeRoute: MlRoute | undefined) {
|
|||
defaultMessage: 'Single Metric Viewer',
|
||||
}),
|
||||
pathId: ML_PAGES.SINGLE_METRIC_VIEWER,
|
||||
disabled: disableLinks,
|
||||
disabled: disableLinks || !isADEnabled,
|
||||
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',
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...(isDFAEnabled
|
||||
? [
|
||||
{
|
||||
id: 'data_frame_analytics_section',
|
||||
name: i18n.translate('xpack.ml.navMenu.dataFrameAnalyticsTabLinkText', {
|
||||
defaultMessage: 'Data Frame Analytics',
|
||||
}),
|
||||
disabled: disableLinks,
|
||||
disabled: disableLinks || !isDFAEnabled,
|
||||
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,
|
||||
name: i18n.translate('xpack.ml.navMenu.dataFrameAnalytics.resultsExplorerText', {
|
||||
defaultMessage: 'Results Explorer',
|
||||
}),
|
||||
disabled: disableLinks,
|
||||
disabled: disableLinks || !isDFAEnabled,
|
||||
testSubj: 'mlMainTab dataFrameAnalyticsResultsExplorer',
|
||||
},
|
||||
{
|
||||
|
@ -197,80 +149,13 @@ export function useSideNavItems(activeRoute: MlRoute | undefined) {
|
|||
name: i18n.translate('xpack.ml.navMenu.dataFrameAnalytics.analyticsMapText', {
|
||||
defaultMessage: 'Analytics Map',
|
||||
}),
|
||||
disabled: disableLinks,
|
||||
disabled: disableLinks || !isDFAEnabled,
|
||||
testSubj: 'mlMainTab dataFrameAnalyticsMap',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
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',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'datavisualizer',
|
||||
name: i18n.translate('xpack.ml.navMenu.dataVisualizerTabLinkText', {
|
||||
defaultMessage: 'Data Visualizer',
|
||||
}),
|
||||
disabled: false,
|
||||
pathId: ML_PAGES.DATA_VISUALIZER,
|
||||
testSubj: 'mlMainTab dataVisualizer',
|
||||
items: [
|
||||
{
|
||||
id: 'filedatavisualizer',
|
||||
pathId: ML_PAGES.DATA_VISUALIZER_FILE,
|
||||
name: i18n.translate('xpack.ml.navMenu.fileDataVisualizerLinkText', {
|
||||
defaultMessage: 'File',
|
||||
}),
|
||||
disabled: false,
|
||||
testSubj: 'mlMainTab fileDataVisualizer',
|
||||
},
|
||||
{
|
||||
id: 'data_view_datavisualizer',
|
||||
pathId: ML_PAGES.DATA_VISUALIZER_INDEX_SELECT,
|
||||
name: i18n.translate('xpack.ml.navMenu.dataViewDataVisualizerLinkText', {
|
||||
defaultMessage: 'Data View',
|
||||
}),
|
||||
disabled: false,
|
||||
testSubj: 'mlMainTab indexDataVisualizer',
|
||||
relatedRouteIds: ['data_view_datavisualizer'],
|
||||
},
|
||||
{
|
||||
id: 'esql_datavisualizer',
|
||||
pathId: ML_PAGES.DATA_VISUALIZER_ESQL,
|
||||
name: i18n.translate('xpack.ml.navMenu.esqlDataVisualizerLinkText', {
|
||||
defaultMessage: 'ES|QL',
|
||||
}),
|
||||
disabled: false,
|
||||
testSubj: 'mlMainTab esqlDataVisualizer',
|
||||
relatedRouteIds: ['data_view_datavisualizer_esql'],
|
||||
},
|
||||
|
||||
{
|
||||
id: 'data_drift',
|
||||
pathId: ML_PAGES.DATA_DRIFT_INDEX_SELECT,
|
||||
name: i18n.translate('xpack.ml.navMenu.dataComparisonText', {
|
||||
defaultMessage: 'Data Drift',
|
||||
}),
|
||||
disabled: disableLinks,
|
||||
testSubj: 'mlMainTab dataDrift',
|
||||
relatedRouteIds: ['data_drift'],
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
: []),
|
||||
];
|
||||
|
||||
mlTabs.push({
|
||||
|
@ -318,7 +203,7 @@ export function useSideNavItems(activeRoute: MlRoute | undefined) {
|
|||
});
|
||||
|
||||
return mlTabs;
|
||||
}, [mlFeaturesDisabled, canViewMlNodes]);
|
||||
}, [mlFeaturesDisabled, isADEnabled, isDFAEnabled]);
|
||||
|
||||
const getTabItem: (tab: Tab) => EuiSideNavItemType<unknown> = useCallback(
|
||||
(tab: Tab) => {
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* 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 React from 'react';
|
||||
import type { EuiEmptyPromptProps } from '@elastic/eui';
|
||||
import { EuiEmptyPrompt, EuiImage } from '@elastic/eui';
|
||||
|
||||
export const MLEmptyPromptCard = ({
|
||||
title,
|
||||
body,
|
||||
actions,
|
||||
iconSrc,
|
||||
iconAlt,
|
||||
'data-test-subj': dataTestSubj,
|
||||
}: Omit<EuiEmptyPromptProps, 'title'> & { title: string; iconSrc: string; iconAlt: string }) => (
|
||||
<EuiEmptyPrompt
|
||||
layout="horizontal"
|
||||
hasBorder={true}
|
||||
hasShadow={false}
|
||||
icon={<EuiImage size="fullWidth" src={iconSrc} alt={iconAlt} />}
|
||||
title={<h3>{title}</h3>}
|
||||
titleSize="s"
|
||||
body={body}
|
||||
actions={actions}
|
||||
data-test-subj={dataTestSubj}
|
||||
/>
|
||||
);
|
|
@ -19,17 +19,14 @@ import { checkPermission } from '../../capabilities/check_capabilities';
|
|||
import { getScopeFieldDefaults } from './utils';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { ML_PAGES } from '../../../../common/constants/locator';
|
||||
import { useMlLocator, useNavigateToPath } from '../../contexts/kibana';
|
||||
import { MANAGEMENT_SECTION_IDS } from '../../management';
|
||||
import { useCreateAndNavigateToManagementMlLink } from '../../contexts/kibana';
|
||||
|
||||
function NoFilterListsCallOut() {
|
||||
const mlLocator = useMlLocator();
|
||||
const navigateToPath = useNavigateToPath();
|
||||
const redirectToFilterManagementPage = async () => {
|
||||
const path = await mlLocator.getUrl({
|
||||
page: ML_PAGES.FILTER_LISTS_MANAGE,
|
||||
});
|
||||
await navigateToPath(path, true);
|
||||
};
|
||||
const redirectToFilterManagementPage = useCreateAndNavigateToManagementMlLink(
|
||||
ML_PAGES.FILTER_LISTS_MANAGE,
|
||||
MANAGEMENT_SECTION_IDS.AD_SETTINGS
|
||||
);
|
||||
|
||||
return (
|
||||
<EuiCallOut
|
||||
|
|
|
@ -11,6 +11,8 @@ import { BehaviorSubject } from 'rxjs';
|
|||
import { mlApiServicesMock } from '../../../services/__mocks__/ml_api_services';
|
||||
import { notificationServiceMock } from '@kbn/core-notifications-browser-mocks';
|
||||
import { LIGHT_THEME } from '@elastic/charts';
|
||||
import type { MlCapabilities } from '../../../../../common/types/capabilities';
|
||||
import { getDefaultCapabilities } from '../../../../../common/types/capabilities';
|
||||
|
||||
export const chartsServiceMock = {
|
||||
theme: {
|
||||
|
@ -25,10 +27,16 @@ export const chartsServiceMock = {
|
|||
},
|
||||
};
|
||||
|
||||
const defaultCapabilities = Object.keys(getDefaultCapabilities()).reduce((acc, key) => {
|
||||
acc[key] = true;
|
||||
return acc;
|
||||
}, {} as Record<string, boolean>);
|
||||
|
||||
export const kibanaContextMock = {
|
||||
services: {
|
||||
docLinks: { links: { ml: { guide: '' } } },
|
||||
uiSettings: { get: jest.fn() },
|
||||
chrome: { recentlyAccessed: { add: jest.fn() } },
|
||||
chrome: { recentlyAccessed: { add: jest.fn() }, setHelpExtension: jest.fn() },
|
||||
application: {
|
||||
navigateToApp: jest.fn(),
|
||||
navigateToUrl: jest.fn(),
|
||||
|
@ -63,6 +71,8 @@ export const kibanaContextMock = {
|
|||
mlServices: {
|
||||
mlApi: mlApiServicesMock,
|
||||
mlCapabilities: {
|
||||
capabilities$: new BehaviorSubject(defaultCapabilities),
|
||||
getCapabilities: jest.fn().mockResolvedValue(defaultCapabilities),
|
||||
refreshCapabilities: jest.fn(),
|
||||
},
|
||||
mlFieldFormatService: {
|
||||
|
@ -76,3 +86,20 @@ export const kibanaContextMock = {
|
|||
export const useMlKibana = jest.fn(() => {
|
||||
return kibanaContextMock;
|
||||
});
|
||||
|
||||
export const getMockedContextWithCapabilities = (capabilities: Partial<MlCapabilities>) => {
|
||||
return {
|
||||
...kibanaContextMock,
|
||||
services: {
|
||||
...kibanaContextMock.services,
|
||||
mlServices: {
|
||||
...kibanaContextMock.services.mlServices,
|
||||
mlCapabilities: {
|
||||
...kibanaContextMock.services.mlServices.mlCapabilities,
|
||||
getCapabilities: jest.fn().mockResolvedValue(capabilities),
|
||||
capabilities$: new BehaviorSubject(capabilities),
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
/*
|
||||
* 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 const useNotifications = () => {
|
||||
return {
|
||||
toasts: { addSuccess: jest.fn(), addDanger: jest.fn(), addError: jest.fn() },
|
||||
};
|
||||
};
|
|
@ -11,7 +11,12 @@ 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,
|
||||
useMlManagementLocatorInternal,
|
||||
} from './use_create_url';
|
||||
export { useMlApi } from './use_ml_api_context';
|
||||
export { useFieldFormatter } from './use_field_formatter';
|
||||
export { useMlLicenseInfo } from './use_ml_license';
|
||||
|
|
|
@ -8,9 +8,28 @@
|
|||
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';
|
||||
import { MlManagementLocatorInternal } from '../../../locator/ml_management_locator';
|
||||
import type { NavigateToMlManagementLink } from '../../jobs/new_job/common/job_creator/util/general';
|
||||
|
||||
export const useMlManagementLocator = () => {
|
||||
const {
|
||||
services: { share },
|
||||
} = useMlKibana();
|
||||
|
||||
return share.url.locators.get(MANAGEMENT_APP_LOCATOR);
|
||||
};
|
||||
|
||||
export const useMlManagementLocatorInternal = () => {
|
||||
const {
|
||||
services: { share },
|
||||
} = useMlKibana();
|
||||
|
||||
return new MlManagementLocatorInternal(share);
|
||||
};
|
||||
|
||||
export const useMlLocator = () => {
|
||||
const {
|
||||
|
@ -24,7 +43,8 @@ export const useMlLink = (params: MlLocatorParams, getUrlParams?: LocatorGetUrlP
|
|||
const [href, setHref] = useState<string>(params.page);
|
||||
const mlLocator = useMlLocator();
|
||||
|
||||
useEffect(() => {
|
||||
useEffect(
|
||||
function generateMlLink() {
|
||||
let isCancelled = false;
|
||||
const generateUrl = async (_params: MlLocatorParams) => {
|
||||
if (mlLocator) {
|
||||
|
@ -38,12 +58,48 @@ export const useMlLink = (params: MlLocatorParams, getUrlParams?: LocatorGetUrlP
|
|||
return () => {
|
||||
isCancelled = true;
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [params, getUrlParams]);
|
||||
},
|
||||
[params, getUrlParams, mlLocator]
|
||||
);
|
||||
|
||||
return href;
|
||||
};
|
||||
|
||||
export const useNavigateToManagementMlLink = (appId: string) => {
|
||||
const mlManagementLocatorInternal = useMlManagementLocatorInternal();
|
||||
const [globalState] = useUrlState('_g');
|
||||
|
||||
const redirectToMlPage: NavigateToMlManagementLink = useCallback(
|
||||
async (_page, pageState?) => {
|
||||
if (mlManagementLocatorInternal) {
|
||||
const modifiedPageState: MlLocatorParams['pageState'] = pageState ?? {};
|
||||
if (globalState?.refreshInterval !== undefined) {
|
||||
// @ts-expect-error globalState override
|
||||
modifiedPageState.globalState = {
|
||||
// @ts-expect-error globalState override
|
||||
...(modifiedPageState.globalState ?? {}),
|
||||
refreshInterval: globalState.refreshInterval,
|
||||
};
|
||||
}
|
||||
|
||||
const { path } = await mlManagementLocatorInternal.getUrl(
|
||||
// @ts-expect-error globalState modification
|
||||
{ page: _page, pageState: modifiedPageState },
|
||||
appId
|
||||
);
|
||||
await mlManagementLocatorInternal.navigate(path, appId);
|
||||
} else {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('mlManagementLocatorInternal is not defined');
|
||||
}
|
||||
},
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[MlManagementLocatorInternal, appId]
|
||||
);
|
||||
|
||||
return redirectToMlPage;
|
||||
};
|
||||
|
||||
export const useCreateAndNavigateToMlLink = (
|
||||
page: MlLocatorParams['page']
|
||||
): (() => Promise<void>) => {
|
||||
|
@ -58,6 +114,7 @@ export const useCreateAndNavigateToMlLink = (
|
|||
|
||||
const redirectToMlPage = useCallback(
|
||||
async (_page: MlLocatorParams['page']) => {
|
||||
if (mlLocator) {
|
||||
const pageState =
|
||||
globalState?.refreshInterval !== undefined
|
||||
? {
|
||||
|
@ -67,10 +124,13 @@ export const useCreateAndNavigateToMlLink = (
|
|||
}
|
||||
: undefined;
|
||||
|
||||
// TODO: fix ts only interpreting it as MlUrlGenericState if pageState is passed
|
||||
// @ts-ignore
|
||||
const url = await mlLocator.getUrl({ page: _page, pageState });
|
||||
|
||||
await navigateToUrl(url);
|
||||
} else {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('mlLocator is not defined');
|
||||
}
|
||||
},
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[mlLocator, navigateToUrl]
|
||||
|
@ -79,3 +139,105 @@ export const useCreateAndNavigateToMlLink = (
|
|||
// returns the onClick callback
|
||||
return useCallback(() => redirectToMlPage(page), [redirectToMlPage, page]);
|
||||
};
|
||||
|
||||
export const useCreateAndNavigateToManagementMlLink = (
|
||||
page: MlLocatorParams['page'],
|
||||
appId: string,
|
||||
pageState?: MlLocatorParams['pageState']
|
||||
): (() => Promise<void>) => {
|
||||
const mlManagementLocatorInternal = useMlManagementLocatorInternal();
|
||||
const [globalState] = useUrlState('_g');
|
||||
|
||||
const {
|
||||
services: {
|
||||
application: { navigateToUrl },
|
||||
},
|
||||
} = useMlKibana();
|
||||
|
||||
const redirectToMlPage = useCallback(
|
||||
async (_page: MlLocatorParams['page']) => {
|
||||
if (mlManagementLocatorInternal) {
|
||||
const modifiedPageState: MlLocatorParams['pageState'] = pageState ?? {};
|
||||
if (globalState?.refreshInterval !== undefined) {
|
||||
// @ts-expect-error globalState override
|
||||
modifiedPageState.globalState = {
|
||||
// @ts-expect-error globalState override
|
||||
...(modifiedPageState.globalState ?? {}),
|
||||
refreshInterval: globalState.refreshInterval,
|
||||
};
|
||||
}
|
||||
|
||||
const { path } = await mlManagementLocatorInternal.getUrl(
|
||||
// @ts-expect-error globalState modification
|
||||
{ page: _page, pageState: modifiedPageState },
|
||||
appId
|
||||
);
|
||||
await mlManagementLocatorInternal.navigate(path, appId);
|
||||
} else {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('mlManagementLocatorInternal is not defined');
|
||||
}
|
||||
},
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[MlManagementLocatorInternal, navigateToUrl, JSON.stringify(pageState)]
|
||||
);
|
||||
|
||||
// returns the onClick callback
|
||||
return useCallback(() => redirectToMlPage(page), [redirectToMlPage, page]);
|
||||
};
|
||||
|
||||
export const useMlManagementLink = (
|
||||
page: MlLocatorParams['page'],
|
||||
appId: string,
|
||||
pageState?: MlLocatorParams['pageState']
|
||||
): string => {
|
||||
const [href, setHref] = useState<string>('');
|
||||
const mlManagementLocatorInternal = useMlManagementLocatorInternal();
|
||||
const [globalState] = useUrlState('_g');
|
||||
|
||||
useEffect(
|
||||
function generateMlManagementLink() {
|
||||
let isCancelled = false;
|
||||
const generateUrl = async (_page: MlLocatorParams['page']) => {
|
||||
if (mlManagementLocatorInternal) {
|
||||
const modifiedPageState: MlLocatorParams['pageState'] = pageState ?? {};
|
||||
if (globalState?.refreshInterval !== undefined) {
|
||||
// @ts-expect-error globalState override
|
||||
modifiedPageState.globalState = {
|
||||
// @ts-expect-error globalState override
|
||||
...(modifiedPageState.globalState ?? {}),
|
||||
refreshInterval: globalState.refreshInterval,
|
||||
};
|
||||
}
|
||||
|
||||
const { url } = await mlManagementLocatorInternal.getUrl(
|
||||
// @ts-expect-error globalState modification
|
||||
{ page: _page, pageState: modifiedPageState },
|
||||
appId
|
||||
);
|
||||
if (!isCancelled && url) {
|
||||
setHref(url);
|
||||
}
|
||||
} else {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('mlManagementLocatorInternal is not defined');
|
||||
}
|
||||
};
|
||||
generateUrl(page);
|
||||
return () => {
|
||||
isCancelled = true;
|
||||
};
|
||||
},
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[
|
||||
mlManagementLocatorInternal,
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
JSON.stringify(pageState),
|
||||
globalState?.refreshInterval,
|
||||
appId,
|
||||
page,
|
||||
]
|
||||
);
|
||||
|
||||
return href;
|
||||
};
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
import { useCallback } from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { ML_APP_ROUTE, PLUGIN_ID } from '../../../../common/constants/app';
|
||||
import { ML_APP_ROUTE, ML_MANAGEMENT_APP_ROUTE, PLUGIN_ID } from '../../../../common/constants/app';
|
||||
|
||||
import { useMlKibana } from './kibana_context';
|
||||
|
||||
|
@ -29,7 +29,8 @@ export const useNavigateToPath = () => {
|
|||
/**
|
||||
* Handle urls generated by MlUrlGenerator where '/app/ml' is automatically prepended
|
||||
*/
|
||||
const url = modifiedPath.includes(ML_APP_ROUTE)
|
||||
const url =
|
||||
modifiedPath.includes(ML_APP_ROUTE) || modifiedPath.includes(ML_MANAGEMENT_APP_ROUTE)
|
||||
? modifiedPath
|
||||
: getUrlForApp(PLUGIN_ID, {
|
||||
path: modifiedPath,
|
||||
|
|
|
@ -9,13 +9,14 @@ import type { FC } from 'react';
|
|||
import React, { Fragment } from 'react';
|
||||
import { EuiCard, EuiIcon } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { useMlLink } from '../../../../../contexts/kibana';
|
||||
import { ML_PAGES } from '../../../../../../../common/constants/locator';
|
||||
import { useCreateAndNavigateToManagementMlLink } from '../../../../../contexts/kibana/use_create_url';
|
||||
|
||||
export const BackToListPanel: FC = () => {
|
||||
const analyticsManagementPageLink = useMlLink({
|
||||
page: ML_PAGES.DATA_FRAME_ANALYTICS_JOBS_MANAGE,
|
||||
});
|
||||
const redirectToAnalyticsList = useCreateAndNavigateToManagementMlLink(
|
||||
ML_PAGES.DATA_FRAME_ANALYTICS_JOBS_MANAGE,
|
||||
'analytics'
|
||||
);
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
|
@ -31,7 +32,7 @@ export const BackToListPanel: FC = () => {
|
|||
defaultMessage: 'Return to the analytics management page.',
|
||||
}
|
||||
)}
|
||||
href={analyticsManagementPageLink}
|
||||
onClick={redirectToAnalyticsList}
|
||||
data-test-subj="analyticsWizardCardManagement"
|
||||
/>
|
||||
</Fragment>
|
||||
|
|
|
@ -19,11 +19,11 @@ import {
|
|||
} from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { useUrlState } from '@kbn/ml-url-state';
|
||||
import { useCreateAndNavigateToManagementMlLink } from '../../../../../contexts/kibana/use_create_url';
|
||||
import { useJobInfoFlyouts } from '../../../../../jobs/components/job_details_flyout';
|
||||
import { useGetAnalytics } from '../../../analytics_management/services/analytics_service';
|
||||
import type { AnalyticStatsBarStats } from '../../../../../components/stats_bar';
|
||||
import type { DataFrameAnalyticsListRow } from '../../../analytics_management/components/analytics_list/common';
|
||||
import { useMlLocator, useNavigateToPath } from '../../../../../contexts/kibana';
|
||||
import { ML_PAGES } from '../../../../../../locator';
|
||||
import { ExpandedRow } from '../../../analytics_management/components/analytics_list/expanded_row';
|
||||
|
||||
|
@ -70,19 +70,14 @@ export const AnalyticsDetailFlyout = () => {
|
|||
[analytics, analyticsId]
|
||||
);
|
||||
|
||||
const locator = useMlLocator()!;
|
||||
const navigateToPath = useNavigateToPath();
|
||||
const [globalState] = useUrlState('_g');
|
||||
|
||||
const redirectToAnalyticsList = useCallback(async () => {
|
||||
const path = await locator.getUrl({
|
||||
page: ML_PAGES.DATA_FRAME_ANALYTICS_JOBS_MANAGE,
|
||||
pageState: {
|
||||
jobId: globalState?.ml?.jobId,
|
||||
},
|
||||
});
|
||||
await navigateToPath(path, false);
|
||||
}, [locator, navigateToPath, globalState?.ml?.jobId]);
|
||||
const pageState = useMemo(() => ({ jobId: globalState?.ml?.jobId }), [globalState?.ml?.jobId]);
|
||||
const redirectToAnalyticsList = useCreateAndNavigateToManagementMlLink(
|
||||
ML_PAGES.DATA_FRAME_ANALYTICS_JOBS_MANAGE,
|
||||
'analytics',
|
||||
pageState
|
||||
);
|
||||
|
||||
const flyoutTitleId = `mlAnalyticsDetailsFlyout-${analyticsId}`;
|
||||
return isDataFrameAnalyticsDetailsFlyoutOpen ? (
|
||||
|
|
|
@ -111,7 +111,7 @@ export const Page: FC<{
|
|||
}
|
||||
|
||||
if (jobsExist === false) {
|
||||
return <AnalyticsEmptyPrompt />;
|
||||
return <AnalyticsEmptyPrompt showDocsLink />;
|
||||
}
|
||||
return (
|
||||
<>
|
||||
|
|
|
@ -21,7 +21,7 @@ import {
|
|||
} from '@kbn/ml-data-frame-analytics-utils';
|
||||
import { toMountPoint } from '@kbn/react-kibana-mount';
|
||||
import type { DeepReadonly } from '../../../../../../../common/types/common';
|
||||
import { useMlKibana, useNavigateToPath } from '../../../../../contexts/kibana';
|
||||
import { useMlKibana, useMlManagementLocator } from '../../../../../contexts/kibana';
|
||||
import { DEFAULT_NUM_TOP_FEATURE_IMPORTANCE_VALUES } from '../../hooks/use_create_analytics_form';
|
||||
import type { State } from '../../hooks/use_create_analytics_form/state';
|
||||
import type { DataFrameAnalyticsListRow } from '../analytics_list/common';
|
||||
|
@ -417,7 +417,7 @@ export const useNavigateToWizardWithClonedJob = () => {
|
|||
...startServices
|
||||
},
|
||||
} = useMlKibana();
|
||||
const navigateToPath = useNavigateToPath();
|
||||
const mlLocator = useMlManagementLocator();
|
||||
const canCreateDataView =
|
||||
capabilities.savedObjectsManagement.edit === true || capabilities.indexPatterns.save === true;
|
||||
|
||||
|
@ -479,11 +479,12 @@ export const useNavigateToWizardWithClonedJob = () => {
|
|||
}
|
||||
|
||||
if (sourceIndexId) {
|
||||
await navigateToPath(
|
||||
`/data_frame_analytics/new_job?index=${encodeURIComponent(sourceIndexId)}&jobId=${
|
||||
item.config.id
|
||||
}`
|
||||
);
|
||||
await mlLocator?.navigate({
|
||||
sectionId: 'ml',
|
||||
appId: `analytics/data_frame_analytics/new_job?index=${encodeURIComponent(
|
||||
sourceIndexId
|
||||
)}&jobId=${item.config.id}`,
|
||||
});
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
|
@ -24,9 +24,6 @@ import {
|
|||
} from '@kbn/ml-data-frame-analytics-utils';
|
||||
import type { ListingPageUrlState } from '@kbn/ml-url-state';
|
||||
import { useRefreshAnalyticsList } from '../../../../common';
|
||||
import { usePermissionCheck } from '../../../../../capabilities/check_capabilities';
|
||||
import { useNavigateToPath } from '../../../../../contexts/kibana';
|
||||
import { ML_PAGES } from '../../../../../../../common/constants/locator';
|
||||
|
||||
import type { DataFrameAnalyticsListRow, ItemIdToExpandedRowMap } from './common';
|
||||
import { DataFrameAnalyticsListColumn } from './common';
|
||||
|
@ -35,13 +32,13 @@ import { getJobTypeBadge, getTaskStateBadge, useColumns } from './use_columns';
|
|||
import { ExpandedRow } from './expanded_row';
|
||||
import type { AnalyticStatsBarStats } from '../../../../../components/stats_bar';
|
||||
import { StatsBar } from '../../../../../components/stats_bar';
|
||||
import { CreateAnalyticsButton } from '../create_analytics_button';
|
||||
import { filterAnalytics } from '../../../../common/search_bar_filters';
|
||||
import { AnalyticsEmptyPrompt } from '../empty_prompt';
|
||||
import { useTableSettings } from './use_table_settings';
|
||||
import { JobsAwaitingNodeWarning } from '../../../../../components/jobs_awaiting_node_warning';
|
||||
import { useRefresh } from '../../../../../routing/use_refresh';
|
||||
import { SpaceManagementContextWrapper } from '../../../../../components/space_management_context_wrapper';
|
||||
import { DatePicker } from '../../../../../components/ml_page/date_picker';
|
||||
|
||||
const filters: EuiSearchBarProps['filters'] = [
|
||||
{
|
||||
|
@ -97,8 +94,6 @@ export const DataFrameAnalyticsList: FC<Props> = ({
|
|||
pageState,
|
||||
updatePageState,
|
||||
}) => {
|
||||
const navigateToPath = useNavigateToPath();
|
||||
|
||||
const searchQueryText = pageState.queryText ?? '';
|
||||
const setSearchQueryText = useCallback(
|
||||
(value: string) => {
|
||||
|
@ -121,13 +116,6 @@ export const DataFrameAnalyticsList: FC<Props> = ({
|
|||
|
||||
const refreshObs = useRefresh();
|
||||
|
||||
const [canCreateDataFrameAnalytics, canStartStopDataFrameAnalytics] = usePermissionCheck([
|
||||
'canCreateDataFrameAnalytics',
|
||||
'canStartStopDataFrameAnalytics',
|
||||
]);
|
||||
|
||||
const disabled = !canCreateDataFrameAnalytics || !canStartStopDataFrameAnalytics;
|
||||
|
||||
const getAnalytics = useGetAnalytics(
|
||||
setAnalytics,
|
||||
setAnalyticsStats,
|
||||
|
@ -198,11 +186,6 @@ export const DataFrameAnalyticsList: FC<Props> = ({
|
|||
updatePageState
|
||||
);
|
||||
|
||||
const navigateToSourceSelection = useCallback(async () => {
|
||||
await navigateToPath(ML_PAGES.DATA_FRAME_ANALYTICS_SOURCE_SELECTION);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const handleSearchOnChange: EuiSearchBarProps['onChange'] = (search) => {
|
||||
if (search.error !== null) {
|
||||
setSearchError(search.error.message);
|
||||
|
@ -237,7 +220,7 @@ export const DataFrameAnalyticsList: FC<Props> = ({
|
|||
return (
|
||||
<div data-test-subj="mlAnalyticsJobList">
|
||||
<EuiSpacer size="m" />
|
||||
<AnalyticsEmptyPrompt />
|
||||
<AnalyticsEmptyPrompt showDocsLink />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -261,6 +244,7 @@ export const DataFrameAnalyticsList: FC<Props> = ({
|
|||
|
||||
return (
|
||||
<SpaceManagementContextWrapper>
|
||||
<EuiSpacer size="m" />
|
||||
<div data-test-subj="mlAnalyticsJobList">
|
||||
{modals}
|
||||
<JobsAwaitingNodeWarning jobCount={jobsAwaitingNodeCount} />
|
||||
|
@ -269,10 +253,7 @@ export const DataFrameAnalyticsList: FC<Props> = ({
|
|||
<EuiFlexItem grow={false}>
|
||||
<EuiFlexGroup alignItems="center" gutterSize="s">
|
||||
<EuiFlexItem grow={false}>
|
||||
<CreateAnalyticsButton
|
||||
isDisabled={disabled}
|
||||
navigateToSourceSelection={navigateToSourceSelection}
|
||||
/>
|
||||
<DatePicker />
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
import type { FC } from 'react';
|
||||
import React from 'react';
|
||||
import type { EuiButtonProps } from '@elastic/eui';
|
||||
import { EuiButton, EuiToolTip } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { createPermissionFailureMessage } from '../../../../../capabilities/check_capabilities';
|
||||
|
@ -14,20 +15,21 @@ import { createPermissionFailureMessage } from '../../../../../capabilities/chec
|
|||
interface Props {
|
||||
isDisabled: boolean;
|
||||
navigateToSourceSelection: () => void;
|
||||
size?: EuiButtonProps['size'];
|
||||
}
|
||||
|
||||
export const CreateAnalyticsButton: FC<Props> = ({ isDisabled, navigateToSourceSelection }) => {
|
||||
const handleClick = () => {
|
||||
navigateToSourceSelection();
|
||||
};
|
||||
|
||||
export const CreateAnalyticsButton: FC<Props> = ({
|
||||
isDisabled,
|
||||
navigateToSourceSelection,
|
||||
size = 's',
|
||||
}) => {
|
||||
const button = (
|
||||
<EuiButton
|
||||
disabled={isDisabled}
|
||||
fill
|
||||
onClick={handleClick}
|
||||
onClick={navigateToSourceSelection}
|
||||
iconType="plusInCircle"
|
||||
size="s"
|
||||
size={size}
|
||||
data-test-subj="mlAnalyticsButtonCreate"
|
||||
>
|
||||
{i18n.translate('xpack.ml.dataframe.analyticsList.createDataFrameAnalyticsButton', {
|
||||
|
|
|
@ -7,20 +7,34 @@
|
|||
|
||||
import type { FC } from 'react';
|
||||
import React from 'react';
|
||||
import { EuiButton, EuiEmptyPrompt, EuiImage, EuiLink } from '@elastic/eui';
|
||||
import { EuiButton, EuiButtonEmpty, EuiTitle } from '@elastic/eui';
|
||||
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';
|
||||
import { MLEmptyPromptCard } from '../../../../../components/overview/ml_empty_prompt_card';
|
||||
|
||||
export const AnalyticsEmptyPrompt: FC = () => {
|
||||
export const TrainedAnalysisTitle = () => (
|
||||
<EuiTitle size="s">
|
||||
<h3>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.dataFrame.analyticsList.emptyPromptTitle"
|
||||
defaultMessage="Trained analysis of your data"
|
||||
/>
|
||||
</h3>
|
||||
</EuiTitle>
|
||||
);
|
||||
|
||||
export const AnalyticsEmptyPrompt: FC<{ showDocsLink?: boolean }> = ({ showDocsLink = false }) => {
|
||||
const {
|
||||
services: { docLinks },
|
||||
} = useMlKibana();
|
||||
|
||||
const mlLocator = useMlManagementLocator();
|
||||
|
||||
const [canCreateDataFrameAnalytics, canStartStopDataFrameAnalytics] = usePermissionCheck([
|
||||
'canCreateDataFrameAnalytics',
|
||||
'canStartStopDataFrameAnalytics',
|
||||
|
@ -29,61 +43,61 @@ 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 (
|
||||
<EuiEmptyPrompt
|
||||
layout="horizontal"
|
||||
hasBorder={false}
|
||||
hasShadow={false}
|
||||
icon={
|
||||
<EuiImage
|
||||
size="fullWidth"
|
||||
src={dfaImage}
|
||||
alt={i18n.translate('xpack.ml.dataFrame.analyticsList.emptyPromptTitle', {
|
||||
defaultMessage: 'Analyze your data with data frame analytics',
|
||||
<MLEmptyPromptCard
|
||||
iconSrc={dfaImage}
|
||||
iconAlt={i18n.translate('xpack.ml.dataFrame.analyticsList.emptyPromptTitle', {
|
||||
defaultMessage: 'Trained analysis of your data',
|
||||
})}
|
||||
title={i18n.translate('xpack.ml.dataFrame.analyticsList.emptyPromptTitle', {
|
||||
defaultMessage: 'Trained analysis of your data',
|
||||
})}
|
||||
/>
|
||||
}
|
||||
title={
|
||||
<h2>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.dataFrame.analyticsList.emptyPromptTitle"
|
||||
defaultMessage="Analyze your data with data frame analytics"
|
||||
/>
|
||||
</h2>
|
||||
}
|
||||
body={
|
||||
<>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.overview.analyticsList.emptyPromptText"
|
||||
defaultMessage="Train outlier detection, regression, or classification machine learning models using data frame analytics."
|
||||
/>
|
||||
</p>
|
||||
</>
|
||||
}
|
||||
actions={[
|
||||
...[
|
||||
<EuiButton
|
||||
onClick={navigateToSourceSelection}
|
||||
isDisabled={disabled}
|
||||
fill
|
||||
color="primary"
|
||||
data-test-subj="mlAnalyticsCreateFirstButton"
|
||||
>
|
||||
{i18n.translate('xpack.ml.dataFrame.analyticsList.emptyPromptButtonText', {
|
||||
defaultMessage: 'Create data frame analytics job',
|
||||
})}
|
||||
<FormattedMessage
|
||||
id="xpack.ml.dataFrame.analyticsList.emptyPromptButtonText"
|
||||
defaultMessage="Create Data Frame Analytics job"
|
||||
/>
|
||||
</EuiButton>,
|
||||
<EuiLink href={docLinks.links.ml.dataFrameAnalytics} target="_blank" external>
|
||||
],
|
||||
...(showDocsLink
|
||||
? [
|
||||
<EuiButtonEmpty
|
||||
target="_blank"
|
||||
href={docLinks.links.ml.dataFrameAnalytics}
|
||||
data-test-subj="mlAnalyticsReadDocumentationButton"
|
||||
iconType="popout"
|
||||
iconSide="left"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.common.readDocumentationLink"
|
||||
defaultMessage="Read documentation"
|
||||
/>
|
||||
</EuiLink>,
|
||||
</EuiButtonEmpty>,
|
||||
]
|
||||
: []),
|
||||
]}
|
||||
data-test-subj="mlNoDataFrameAnalyticsFound"
|
||||
/>
|
||||
|
|
|
@ -71,6 +71,7 @@ jest.mock('@kbn/saved-objects-finder-plugin/public', () => {
|
|||
});
|
||||
|
||||
const mockNavigateToPath = jest.fn();
|
||||
const mockLocatorNavigate = jest.fn();
|
||||
jest.mock('../../../../../contexts/kibana', () => ({
|
||||
useMlKibana: () => ({
|
||||
services: {
|
||||
|
@ -88,6 +89,9 @@ jest.mock('../../../../../contexts/kibana', () => ({
|
|||
toasts: { addSuccess: jest.fn(), addDanger: jest.fn(), addError: jest.fn() },
|
||||
};
|
||||
},
|
||||
useMlManagementLocator: () => ({
|
||||
navigate: mockLocatorNavigate,
|
||||
}),
|
||||
}));
|
||||
|
||||
jest.mock('../../../../../util/index_utils', () => {
|
||||
|
@ -168,9 +172,10 @@ describe('Data Frame Analytics: <SourceSelection />', () => {
|
|||
expect(
|
||||
screen.queryByText('Data views using cross-cluster search are not supported.')
|
||||
).not.toBeInTheDocument();
|
||||
expect(mockNavigateToPath).toHaveBeenCalledWith(
|
||||
'/data_frame_analytics/new_job?index=the-plain-index-pattern-id'
|
||||
);
|
||||
expect(mockLocatorNavigate).toHaveBeenCalledWith({
|
||||
appId: 'analytics/data_frame_analytics/new_job?index=the-plain-index-pattern-id',
|
||||
sectionId: 'ml',
|
||||
});
|
||||
expect(mockGetDataViewAndSavedSearchCallback).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
});
|
||||
|
@ -225,9 +230,10 @@ describe('Data Frame Analytics: <SourceSelection />', () => {
|
|||
expect(
|
||||
screen.queryByText('Data views using cross-cluster search are not supported.')
|
||||
).not.toBeInTheDocument();
|
||||
expect(mockNavigateToPath).toHaveBeenCalledWith(
|
||||
'/data_frame_analytics/new_job?savedSearchId=the-plain-saved-search-id'
|
||||
);
|
||||
expect(mockLocatorNavigate).toHaveBeenCalledWith({
|
||||
appId: 'analytics/data_frame_analytics/new_job?index=the-plain-index-pattern-id',
|
||||
sectionId: 'ml',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -13,8 +13,9 @@ import { getNestedProperty } from '@kbn/ml-nested-property';
|
|||
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 { useToastNotificationService } from '../../../../../services/toast_notification_service';
|
||||
import { ML_PAGES } from '../../../../../../../common/constants/locator';
|
||||
import {
|
||||
getDataViewAndSavedSearchCallback,
|
||||
isCcsIndexPattern,
|
||||
|
@ -33,7 +34,7 @@ export const SourceSelection: FC = () => {
|
|||
uiSettings,
|
||||
},
|
||||
} = useMlKibana();
|
||||
const navigateToPath = useNavigateToPath();
|
||||
const mlManagementLocator = useMlManagementLocator();
|
||||
|
||||
const [isCcsCallOut, setIsCcsCallOut] = useState(false);
|
||||
const [ccsCallOutBodyText, setCcsCallOutBodyText] = useState<string>();
|
||||
|
@ -96,11 +97,12 @@ export const SourceSelection: FC = () => {
|
|||
return;
|
||||
}
|
||||
|
||||
await navigateToPath(
|
||||
`/data_frame_analytics/new_job?${
|
||||
await mlManagementLocator?.navigate({
|
||||
sectionId: 'ml',
|
||||
appId: `analytics/${ML_PAGES.DATA_FRAME_ANALYTICS_CREATE_JOB}?${
|
||||
type === 'index-pattern' ? 'index' : 'savedSearchId'
|
||||
}=${encodeURIComponent(id)}`
|
||||
);
|
||||
}=${encodeURIComponent(id)}`,
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
|
|
|
@ -6,26 +6,29 @@
|
|||
*/
|
||||
|
||||
import type { FC } from 'react';
|
||||
import React, { useMemo, useState } from 'react';
|
||||
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import React, { useState, useCallback } from 'react';
|
||||
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { useUrlState, usePageUrlState, type ListingPageUrlState } from '@kbn/ml-url-state';
|
||||
import { usePageUrlState, type ListingPageUrlState } from '@kbn/ml-url-state';
|
||||
import { css } from '@emotion/react';
|
||||
import { DataFrameAnalyticsList } from './components/analytics_list';
|
||||
import { useRefreshInterval } from './components/analytics_list/use_refresh_interval';
|
||||
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 { DataFrameAnalyticsListColumn } from './components/analytics_list/common';
|
||||
import { ML_PAGES } from '../../../../../common/constants/locator';
|
||||
import { HelpMenu } from '../../../components/help_menu';
|
||||
import { useMlKibana } from '../../../contexts/kibana';
|
||||
import { useMlKibana, useMlManagementLocator } from '../../../contexts/kibana';
|
||||
import { useRefreshAnalyticsList } from '../../common';
|
||||
import { MlPageHeader } from '../../../components/page_header';
|
||||
import { CreateAnalyticsButton } from './components/create_analytics_button/create_analytics_button';
|
||||
import { usePermissionCheck } from '../../../capabilities/check_capabilities';
|
||||
import { ExportJobsFlyout, ImportJobsFlyout } from '../../../components/import_export_jobs';
|
||||
import { SynchronizeSavedObjectsButton } from '../../../jobs/jobs_list/components/top_level_actions/synchronize_saved_objects_button';
|
||||
|
||||
interface PageUrlState {
|
||||
pageKey: typeof ML_PAGES.DATA_FRAME_ANALYTICS_JOBS_MANAGE;
|
||||
pageKey: typeof ML_PAGES.DATA_FRAME_ANALYTICS_JOBS_MANAGE_FOR_URL;
|
||||
pageUrlState: ListingPageUrlState;
|
||||
}
|
||||
|
||||
|
@ -38,10 +41,9 @@ export const getDefaultDFAListState = (): ListingPageUrlState => ({
|
|||
|
||||
export const Page: FC = () => {
|
||||
const [blockRefresh, setBlockRefresh] = useState(false);
|
||||
const [globalState] = useUrlState('_g');
|
||||
|
||||
const [dfaPageState, setDfaPageState] = usePageUrlState<PageUrlState>(
|
||||
ML_PAGES.DATA_FRAME_ANALYTICS_JOBS_MANAGE,
|
||||
ML_PAGES.DATA_FRAME_ANALYTICS_JOBS_MANAGE_FOR_URL,
|
||||
getDefaultDFAListState()
|
||||
);
|
||||
|
||||
|
@ -49,21 +51,53 @@ export const Page: FC = () => {
|
|||
const [isLoading, setIsLoading] = useState(false);
|
||||
const { refresh } = useRefreshAnalyticsList({ isLoading: setIsLoading });
|
||||
|
||||
const location = useLocation();
|
||||
const selectedTabId = useMemo(() => location.pathname.split('/').pop(), [location]);
|
||||
const mapJobId = globalState?.ml?.jobId;
|
||||
const mapModelId = globalState?.ml?.modelId;
|
||||
const {
|
||||
services: { docLinks },
|
||||
} = useMlKibana();
|
||||
const helpLink = docLinks.links.ml.dataFrameAnalytics;
|
||||
const mlManagementLocator = useMlManagementLocator();
|
||||
|
||||
const navigateToSourceSelection = useCallback(async () => {
|
||||
await mlManagementLocator?.navigate({
|
||||
sectionId: 'ml',
|
||||
appId: `analytics/${ML_PAGES.DATA_FRAME_ANALYTICS_SOURCE_SELECTION}`,
|
||||
});
|
||||
}, [mlManagementLocator]);
|
||||
|
||||
const canCreateAnalytics = usePermissionCheck('canCreateDataFrameAnalytics');
|
||||
return (
|
||||
<>
|
||||
<MlPageHeader>
|
||||
<EuiFlexGroup direction="row" gutterSize="s" wrap={true}>
|
||||
<EuiFlexItem grow={true} css={css({ minWidth: '400px' })}>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.dataframe.analyticsList.title"
|
||||
defaultMessage="Data Frame Analytics Jobs"
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={true} />
|
||||
<EuiFlexItem grow={false} justifyContent="flexEnd">
|
||||
<EuiFlexGroup direction="row" gutterSize="s">
|
||||
<EuiFlexItem grow={false}>
|
||||
<SynchronizeSavedObjectsButton refreshJobs={refresh} />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<ExportJobsFlyout
|
||||
isDisabled={!canCreateAnalytics}
|
||||
currentTab={'data-frame-analytics'}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<ImportJobsFlyout isDisabled={!canCreateAnalytics} onImportComplete={refresh} />
|
||||
</EuiFlexItem>
|
||||
<CreateAnalyticsButton
|
||||
size="m"
|
||||
navigateToSourceSelection={navigateToSourceSelection}
|
||||
isDisabled={!canCreateAnalytics}
|
||||
/>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</MlPageHeader>
|
||||
|
||||
<NodeAvailableWarning />
|
||||
|
@ -71,16 +105,11 @@ export const Page: FC = () => {
|
|||
<SavedObjectsWarning onCloseFlyout={refresh} forceRefresh={isLoading} />
|
||||
<UpgradeWarning />
|
||||
|
||||
{selectedTabId === 'map' && (mapJobId || mapModelId) && (
|
||||
<JobMap analyticsId={mapJobId} modelId={mapModelId} />
|
||||
)}
|
||||
{selectedTabId === 'data_frame_analytics' && (
|
||||
<DataFrameAnalyticsList
|
||||
blockRefresh={blockRefresh}
|
||||
pageState={dfaPageState}
|
||||
updatePageState={setDfaPageState}
|
||||
/>
|
||||
)}
|
||||
<HelpMenu docLink={helpLink} />
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -24,6 +24,8 @@ import {
|
|||
FlyoutType,
|
||||
useJobInfoFlyouts,
|
||||
} from '../../../../jobs/components/job_details_flyout/job_details_flyout_context';
|
||||
import { useCreateAndNavigateToManagementMlLink } from '../../../../contexts/kibana/use_create_url';
|
||||
import { usePermissionCheck } from '../../../../capabilities/check_capabilities';
|
||||
interface Props {
|
||||
setIsIdSelectorFlyoutVisible: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
selectedId?: string;
|
||||
|
@ -92,9 +94,16 @@ export const AnalyticsIdSelectorControls: FC<Props> = ({
|
|||
setIsIdSelectorFlyoutVisible,
|
||||
selectedId,
|
||||
}) => {
|
||||
const [canGetDataFrameAnalytics, canCreateDataFrameAnalytics] = usePermissionCheck([
|
||||
'canGetDataFrameAnalytics',
|
||||
'canCreateDataFrameAnalytics',
|
||||
]);
|
||||
|
||||
const redirectToDfaJobManagement = useCreateAndNavigateToManagementMlLink('', 'analytics');
|
||||
|
||||
return (
|
||||
<>
|
||||
<EuiFlexGroup gutterSize="xs" alignItems="center">
|
||||
<EuiFlexGroup responsive={false} gutterSize="xs" alignItems="center">
|
||||
<EuiFlexItem grow={false}>
|
||||
{selectedId ? (
|
||||
<SelectorControl
|
||||
|
@ -125,6 +134,31 @@ export const AnalyticsIdSelectorControls: FC<Props> = ({
|
|||
/>
|
||||
</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem />
|
||||
|
||||
{canGetDataFrameAnalytics ? (
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonEmpty
|
||||
size="s"
|
||||
color="primary"
|
||||
onClick={redirectToDfaJobManagement}
|
||||
disabled={!canGetDataFrameAnalytics}
|
||||
data-test-subj="mlJobSelectorManageJobsButton"
|
||||
>
|
||||
{canCreateDataFrameAnalytics ? (
|
||||
<FormattedMessage
|
||||
id="xpack.ml.jobSelector.manageJobsLinkLabel"
|
||||
defaultMessage="Manage jobs"
|
||||
/>
|
||||
) : (
|
||||
<FormattedMessage
|
||||
id="xpack.ml.jobSelector.viewJobsLinkLabel"
|
||||
defaultMessage="View jobs"
|
||||
/>
|
||||
)}
|
||||
</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
) : null}
|
||||
</EuiFlexGroup>
|
||||
<EuiHorizontalRule />
|
||||
</>
|
||||
|
|
|
@ -18,12 +18,13 @@ import {
|
|||
type EuiThemeComputed,
|
||||
} from '@elastic/eui';
|
||||
import { JOB_MAP_NODE_TYPES } from '@kbn/ml-data-frame-analytics-utils';
|
||||
import { useMlKibana, useMlLocator } from '../../../contexts/kibana';
|
||||
import { useMlKibana } from '../../../contexts/kibana';
|
||||
import { Controls, Cytoscape, JobMapLegend } from './components';
|
||||
import { ML_PAGES } from '../../../../../common/constants/locator';
|
||||
import { useRefresh } from '../../../routing/use_refresh';
|
||||
import { useRefDimensions } from './components/use_ref_dimensions';
|
||||
import { useFetchAnalyticsMapData } from './use_fetch_analytics_map_data';
|
||||
import { useCreateAndNavigateToManagementMlLink } from '../../../contexts/kibana/use_create_url';
|
||||
|
||||
const getCytoscapeDivStyle = (theme: EuiThemeComputed) => ({
|
||||
background: `linear-gradient(
|
||||
|
@ -67,19 +68,15 @@ export const JobMap: FC<Props> = ({ defaultHeight, analyticsId, modelId, forceRe
|
|||
} = useFetchAnalyticsMapData();
|
||||
|
||||
const {
|
||||
services: {
|
||||
notifications,
|
||||
application: { navigateToUrl },
|
||||
},
|
||||
services: { notifications },
|
||||
} = useMlKibana();
|
||||
const locator = useMlLocator()!;
|
||||
const { euiTheme } = useEuiTheme();
|
||||
const refresh = useRefresh();
|
||||
|
||||
const redirectToAnalyticsManagementPage = async () => {
|
||||
const url = await locator.getUrl({ page: ML_PAGES.DATA_FRAME_ANALYTICS_JOBS_MANAGE });
|
||||
await navigateToUrl(url);
|
||||
};
|
||||
const redirectToAnalyticsManagementPage = useCreateAndNavigateToManagementMlLink(
|
||||
ML_PAGES.DATA_FRAME_ANALYTICS_JOBS_MANAGE,
|
||||
'analytics'
|
||||
);
|
||||
|
||||
const updateElements = (nodeId: string, nodeLabel: string, destIndexNode?: string) => {
|
||||
// If removing the root job just go back to the jobs list
|
||||
|
|
|
@ -80,7 +80,7 @@ export const Page: FC = () => {
|
|||
}
|
||||
|
||||
if (jobsExist === false) {
|
||||
return <AnalyticsEmptyPrompt />;
|
||||
return <AnalyticsEmptyPrompt showDocsLink />;
|
||||
}
|
||||
return (
|
||||
<>
|
||||
|
|
|
@ -14,22 +14,19 @@ import {
|
|||
EuiFlexGroup,
|
||||
EuiFlexGrid,
|
||||
EuiFlexItem,
|
||||
EuiIcon,
|
||||
EuiLink,
|
||||
EuiSpacer,
|
||||
EuiText,
|
||||
EuiBetaBadge,
|
||||
EuiTextAlign,
|
||||
} from '@elastic/eui';
|
||||
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { useTimefilter } from '@kbn/ml-date-picker';
|
||||
import { ENABLE_ESQL } from '@kbn/esql-utils';
|
||||
import { isFullLicense } from '../license';
|
||||
import { useMlKibana, useNavigateToPath } from '../contexts/kibana';
|
||||
import { useMlKibana } from '../contexts/kibana';
|
||||
import { HelpMenu } from '../components/help_menu';
|
||||
import { MlPageHeader } from '../components/page_header';
|
||||
import { ML_PAGES } from '../../locator';
|
||||
import { DataVisualizerGrid } from '../overview/data_visualizer_grid';
|
||||
|
||||
function startTrialDescription() {
|
||||
return (
|
||||
|
@ -66,7 +63,6 @@ export const DatavisualizerSelector: FC = () => {
|
|||
} = useMlKibana();
|
||||
const isEsqlEnabled = useMemo(() => uiSettings.get(ENABLE_ESQL), [uiSettings]);
|
||||
const helpLink = docLinks.links.ml.guide;
|
||||
const navigateToPath = useNavigateToPath();
|
||||
|
||||
const startTrialVisible =
|
||||
licenseManagement !== undefined &&
|
||||
|
@ -78,7 +74,6 @@ export const DatavisualizerSelector: FC = () => {
|
|||
console.error('File data visualizer plugin not available');
|
||||
return null;
|
||||
}
|
||||
const maxFileSize = dataVisualizer.getMaxBytesFormatted();
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -95,123 +90,14 @@ export const DatavisualizerSelector: FC = () => {
|
|||
<EuiText color="subdued">
|
||||
<FormattedMessage
|
||||
id="xpack.ml.datavisualizer.selector.dataVisualizerDescription"
|
||||
defaultMessage="The Machine Learning Data Visualizer tool helps you understand your data,
|
||||
defaultMessage="The Machine Learning Data Visualizer tool helps you understand your data
|
||||
by analyzing the metrics and fields in a log file or an existing Elasticsearch index."
|
||||
/>
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
<EuiSpacer size="xl" />
|
||||
<EuiFlexGrid gutterSize="xl" columns={2} style={{ maxWidth: '1000px' }}>
|
||||
<EuiFlexItem>
|
||||
<EuiCard
|
||||
hasBorder
|
||||
icon={<EuiIcon size="xxl" type="addDataApp" />}
|
||||
title={
|
||||
<FormattedMessage
|
||||
id="xpack.ml.datavisualizer.selector.importDataTitle"
|
||||
defaultMessage="Visualize data from a file"
|
||||
/>
|
||||
}
|
||||
description={
|
||||
<FormattedMessage
|
||||
id="xpack.ml.datavisualizer.selector.importDataDescription"
|
||||
defaultMessage="Import data from a log file. You can upload files up to {maxFileSize}."
|
||||
values={{ maxFileSize }}
|
||||
/>
|
||||
}
|
||||
footer={
|
||||
<EuiButton
|
||||
target="_self"
|
||||
onClick={() => navigateToPath('/filedatavisualizer')}
|
||||
data-test-subj="mlDataVisualizerUploadFileButton"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.datavisualizer.selector.uploadFileButtonLabel"
|
||||
defaultMessage="Select file"
|
||||
/>
|
||||
</EuiButton>
|
||||
}
|
||||
data-test-subj="mlDataVisualizerCardImportData"
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiCard
|
||||
hasBorder
|
||||
icon={<EuiIcon size="xxl" type="dataVisualizer" />}
|
||||
title={
|
||||
<FormattedMessage
|
||||
id="xpack.ml.datavisualizer.selector.selectDataViewTitle"
|
||||
defaultMessage="Visualize data from a data view"
|
||||
/>
|
||||
}
|
||||
description={''}
|
||||
footer={
|
||||
<EuiButton
|
||||
target="_self"
|
||||
onClick={() => navigateToPath('/datavisualizer_index_select')}
|
||||
data-test-subj="mlDataVisualizerSelectIndexButton"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.datavisualizer.selector.selectDataViewButtonLabel"
|
||||
defaultMessage="Select data view"
|
||||
/>
|
||||
</EuiButton>
|
||||
}
|
||||
data-test-subj="mlDataVisualizerCardIndexData"
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
{isEsqlEnabled ? (
|
||||
<EuiFlexItem>
|
||||
<EuiCard
|
||||
hasBorder
|
||||
icon={<EuiIcon size="xxl" type="dataVisualizer" />}
|
||||
title={
|
||||
<EuiTextAlign textAlign="center">
|
||||
<>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.datavisualizer.selector.selectESQLTitle"
|
||||
defaultMessage="Visualize data using ES|QL"
|
||||
/>{' '}
|
||||
<EuiBetaBadge
|
||||
label=""
|
||||
iconType="beaker"
|
||||
size="m"
|
||||
color="hollow"
|
||||
tooltipContent={
|
||||
<FormattedMessage
|
||||
id="xpack.ml.datavisualizer.selector.esqlTechnicalPreviewBadge.titleMsg"
|
||||
defaultMessage="ES|QL data visualizer is in technical preview."
|
||||
/>
|
||||
}
|
||||
tooltipPosition={'right'}
|
||||
/>
|
||||
</>
|
||||
</EuiTextAlign>
|
||||
}
|
||||
description={
|
||||
<FormattedMessage
|
||||
id="xpack.ml.datavisualizer.selector.technicalPreviewBadge.contentMsg"
|
||||
defaultMessage="Use ES|QL queries to visualize information about any data set."
|
||||
/>
|
||||
}
|
||||
footer={
|
||||
<EuiButton
|
||||
target="_self"
|
||||
onClick={() => navigateToPath(ML_PAGES.DATA_VISUALIZER_ESQL)}
|
||||
data-test-subj="mlDataVisualizerSelectESQLButton"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.datavisualizer.selector.useESQLButtonLabel"
|
||||
defaultMessage="Use ES|QL"
|
||||
/>
|
||||
</EuiButton>
|
||||
}
|
||||
data-test-subj="mlDataVisualizerCardESQLData"
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
) : null}
|
||||
</EuiFlexGrid>
|
||||
<DataVisualizerGrid buttonType="full" isEsqlEnabled={isEsqlEnabled} />
|
||||
{startTrialVisible === true && (
|
||||
<Fragment>
|
||||
<EuiSpacer size="xxl" />
|
||||
|
|
|
@ -27,6 +27,7 @@ import { checkPermission } from '../../capabilities/check_capabilities';
|
|||
import { MlPageHeader } from '../../components/page_header';
|
||||
import { useEnabledFeatures } from '../../contexts/ml';
|
||||
import { TechnicalPreviewBadge } from '../../components/technical_preview_badge';
|
||||
import { useMlManagementLocator } from '../../contexts/kibana/use_create_url';
|
||||
export const IndexDataVisualizerPage: FC<{ esql: boolean }> = ({ esql = false }) => {
|
||||
useTimefilter({ timeRangeSelector: false, autoRefreshSelector: false });
|
||||
const {
|
||||
|
@ -44,6 +45,7 @@ export const IndexDataVisualizerPage: FC<{ esql: boolean }> = ({ esql = false })
|
|||
const mlApi = useMlApi();
|
||||
const { showNodeInfo } = useEnabledFeatures();
|
||||
const mlLocator = useMlLocator()!;
|
||||
const mlManagementLocator = useMlManagementLocator();
|
||||
const mlFeaturesDisabled = !isFullLicense();
|
||||
getMlNodeCount(mlApi);
|
||||
|
||||
|
@ -85,13 +87,12 @@ export const IndexDataVisualizerPage: FC<{ esql: boolean }> = ({ esql = false })
|
|||
icon: 'createAdvancedJob',
|
||||
type: 'file',
|
||||
getUrl: async () => {
|
||||
return await mlLocator.getUrl({
|
||||
page: ML_PAGES.ANOMALY_DETECTION_CREATE_JOB_ADVANCED,
|
||||
pageState: {
|
||||
index: dataViewId,
|
||||
globalState,
|
||||
},
|
||||
});
|
||||
return (
|
||||
(await mlManagementLocator?.getRedirectUrl({
|
||||
sectionId: 'ml',
|
||||
appId: `anomaly_detection/${ML_PAGES.ANOMALY_DETECTION_CREATE_JOB_ADVANCED}?index=${dataViewId}`,
|
||||
})) ?? ''
|
||||
);
|
||||
},
|
||||
canDisplay: async () => {
|
||||
try {
|
||||
|
@ -122,13 +123,13 @@ export const IndexDataVisualizerPage: FC<{ esql: boolean }> = ({ esql = false })
|
|||
icon: 'classificationJob',
|
||||
type: 'file',
|
||||
getUrl: async () => {
|
||||
return await mlLocator.getUrl({
|
||||
page: ML_PAGES.DATA_FRAME_ANALYTICS_CREATE_JOB,
|
||||
pageState: {
|
||||
index: dataViewId,
|
||||
globalState,
|
||||
},
|
||||
});
|
||||
if (!mlManagementLocator) return '';
|
||||
return (
|
||||
(await mlManagementLocator?.getRedirectUrl({
|
||||
sectionId: 'ml',
|
||||
appId: `analytics/${ML_PAGES.DATA_FRAME_ANALYTICS_CREATE_JOB}?index=${dataViewId}`,
|
||||
})) ?? ''
|
||||
);
|
||||
},
|
||||
canDisplay: async () => {
|
||||
return (
|
||||
|
@ -153,13 +154,12 @@ export const IndexDataVisualizerPage: FC<{ esql: boolean }> = ({ esql = false })
|
|||
icon: m.logo?.icon ?? '',
|
||||
type: 'index',
|
||||
getUrl: async () => {
|
||||
return await mlLocator.getUrl({
|
||||
page: ML_PAGES.ANOMALY_DETECTION_CREATE_JOB_RECOGNIZER,
|
||||
pageState: {
|
||||
id: m.id,
|
||||
index: dataViewId,
|
||||
},
|
||||
});
|
||||
return (
|
||||
(await mlManagementLocator?.getRedirectUrl({
|
||||
sectionId: 'ml',
|
||||
appId: `anomaly_detection/${ML_PAGES.ANOMALY_DETECTION_CREATE_JOB_RECOGNIZER}?id=${m.id}&index=${dataViewId}`,
|
||||
})) ?? ''
|
||||
);
|
||||
},
|
||||
canDisplay: async () => {
|
||||
try {
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import {
|
||||
EuiFlyout,
|
||||
EuiFlyoutHeader,
|
||||
|
@ -22,12 +22,13 @@ import { i18n } from '@kbn/i18n';
|
|||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import useMountedState from 'react-use/lib/useMountedState';
|
||||
import type { CombinedJobWithStats } from '../../../../../common/types/anomaly_detection_jobs';
|
||||
import { useMlApi, useMlLocator, useNavigateToPath } from '../../../contexts/kibana';
|
||||
import { useMlApi } from '../../../contexts/kibana';
|
||||
import { JobDetails } from '../../jobs_list/components/job_details';
|
||||
import { loadFullJob } from '../../jobs_list/components/utils';
|
||||
import { useToastNotificationService } from '../../../services/toast_notification_service';
|
||||
import { ML_PAGES } from '../../../../../common/constants/locator';
|
||||
import { useJobInfoFlyouts } from './job_details_flyout_context';
|
||||
import { useCreateAndNavigateToManagementMlLink } from '../../../contexts/kibana/use_create_url';
|
||||
|
||||
const doNothing = () => {};
|
||||
export const JobDetailsFlyout = () => {
|
||||
|
@ -71,24 +72,17 @@ export const JobDetailsFlyout = () => {
|
|||
fetchJobDetails();
|
||||
}, [jobId, mlApi, displayErrorToast, isMounted]);
|
||||
|
||||
const navigateToPath = useNavigateToPath();
|
||||
const mlLocator = useMlLocator();
|
||||
const pageState = useMemo(() => ({ jobId }), [jobId]);
|
||||
const openJobsList = useCreateAndNavigateToManagementMlLink(
|
||||
ML_PAGES.ANOMALY_DETECTION_JOBS_MANAGE,
|
||||
'anomaly_detection',
|
||||
pageState
|
||||
);
|
||||
|
||||
if (!jobId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const openJobsList = async () => {
|
||||
const pageState = { jobId };
|
||||
if (mlLocator) {
|
||||
const url = await mlLocator.getUrl({
|
||||
page: ML_PAGES.ANOMALY_DETECTION_JOBS_MANAGE,
|
||||
pageState,
|
||||
});
|
||||
await navigateToPath(url);
|
||||
}
|
||||
};
|
||||
|
||||
return isDetailFlyoutOpen ? (
|
||||
<EuiFlyout
|
||||
data-test-subj="jobDetailsFlyout"
|
||||
|
|
|
@ -8,14 +8,18 @@
|
|||
import type { FC } from 'react';
|
||||
import React from 'react';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { EuiButton, EuiEmptyPrompt, EuiImage, EuiLink } from '@elastic/eui';
|
||||
import { EuiButton, EuiButtonEmpty } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
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';
|
||||
import { MLEmptyPromptCard } from '../../../../components/overview/ml_empty_prompt_card';
|
||||
|
||||
export const AnomalyDetectionEmptyState: FC = () => {
|
||||
export const AnomalyDetectionEmptyState: FC<{ showDocsLink?: boolean }> = ({
|
||||
showDocsLink = false,
|
||||
}) => {
|
||||
const canCreateJob = usePermissionCheck('canCreateJob');
|
||||
const disableCreateAnomalyDetectionJob = !canCreateJob || !mlNodesAvailable();
|
||||
|
||||
|
@ -23,43 +27,41 @@ 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 (
|
||||
<EuiEmptyPrompt
|
||||
<MLEmptyPromptCard
|
||||
layout="horizontal"
|
||||
hasBorder={false}
|
||||
hasBorder={true}
|
||||
hasShadow={false}
|
||||
icon={<EuiImage size="fullWidth" src={adImage} alt="anomaly_detection" />}
|
||||
title={
|
||||
<h2>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.overview.anomalyDetection.createFirstJobMessage"
|
||||
defaultMessage="Start detecting anomalies"
|
||||
/>
|
||||
</h2>
|
||||
}
|
||||
iconSrc={adImage}
|
||||
iconAlt={i18n.translate('xpack.ml.overview.anomalyDetection.title', {
|
||||
defaultMessage: 'Anomaly detection',
|
||||
})}
|
||||
title={i18n.translate('xpack.ml.overview.anomalyDetection.createFirstJobMessage', {
|
||||
defaultMessage: 'Spot anomalies faster',
|
||||
})}
|
||||
body={
|
||||
<>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.overview.anomalyDetection.emptyPromptText"
|
||||
defaultMessage="Anomaly detection enables you to find unusual behavior in time series data. Start automatically spotting the anomalies hiding in your data and resolve issues faster."
|
||||
defaultMessage="Start automatically spotting anomalies hiding in your time series data and resolve issues faster."
|
||||
/>
|
||||
</p>
|
||||
</>
|
||||
}
|
||||
actions={[
|
||||
...[
|
||||
<EuiButton
|
||||
fill
|
||||
color="primary"
|
||||
onClick={redirectToCreateJobSelectIndexPage}
|
||||
isDisabled={disableCreateAnomalyDetectionJob}
|
||||
|
@ -70,12 +72,23 @@ export const AnomalyDetectionEmptyState: FC = () => {
|
|||
defaultMessage="Create anomaly detection job"
|
||||
/>
|
||||
</EuiButton>,
|
||||
<EuiLink href={docLinks.links.ml.anomalyDetection} target="_blank" external>
|
||||
],
|
||||
...(showDocsLink
|
||||
? [
|
||||
<EuiButtonEmpty
|
||||
target="_blank"
|
||||
href={docLinks.links.ml.anomalyDetection}
|
||||
data-test-subj="mlAnalyticsReadDocumentationButton"
|
||||
iconType="popout"
|
||||
iconSide="left"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.common.readDocumentationLink"
|
||||
defaultMessage="Read documentation"
|
||||
/>
|
||||
</EuiLink>,
|
||||
</EuiButtonEmpty>,
|
||||
]
|
||||
: []),
|
||||
]}
|
||||
data-test-subj="mlAnomalyDetectionEmptyState"
|
||||
/>
|
||||
|
|
|
@ -23,7 +23,7 @@ import { isManagedJob } from '../../../jobs_utils';
|
|||
|
||||
export function actionsMenuContent(
|
||||
toastNotifications,
|
||||
application,
|
||||
share,
|
||||
mlApi,
|
||||
showEditJobFlyout,
|
||||
showDatafeedChartFlyout,
|
||||
|
@ -152,7 +152,7 @@ export function actionsMenuContent(
|
|||
return isJobBlocked(item) === false && canCreateJob;
|
||||
},
|
||||
onClick: (item) => {
|
||||
cloneJob(toastNotifications, application, mlApi, item.id);
|
||||
cloneJob(toastNotifications, share, mlApi, item.id);
|
||||
closeMenu(true);
|
||||
},
|
||||
'data-test-subj': 'mlActionButtonCloneJob',
|
||||
|
|
|
@ -78,7 +78,9 @@ export function extractJobDetails(job, basePath, refreshJobList) {
|
|||
calendars.items = job.calendars.map((c) => [
|
||||
'',
|
||||
<EuiLink
|
||||
href={basePath.prepend(`/app/ml/settings/calendars_list/edit_calendar/${c}?_g=()`)}
|
||||
href={basePath.prepend(
|
||||
`/app/management/ml/ad_settings/calendars_list/edit_calendar/${c}?_g=()`
|
||||
)}
|
||||
data-test-subj={`mlJobDetailsCalendar-${c}`}
|
||||
>
|
||||
{c}
|
||||
|
|
|
@ -364,7 +364,7 @@ export class JobsListUI extends Component {
|
|||
}),
|
||||
actions: actionsMenuContent(
|
||||
this.props.kibana.services.notifications.toasts,
|
||||
this.props.kibana.services.application,
|
||||
this.props.kibana.services.share,
|
||||
this.mlApi,
|
||||
this.props.showEditJobFlyout,
|
||||
this.props.showDatafeedChartFlyout,
|
||||
|
|
|
@ -21,7 +21,6 @@ import { DeleteJobModal } from '../delete_job_modal';
|
|||
import { ResetJobModal } from '../reset_job_modal';
|
||||
import { StartDatafeedModal } from '../start_datafeed_modal';
|
||||
import { MultiJobActions } from '../multi_job_actions';
|
||||
import { NewJobButton } from '../new_job_button';
|
||||
import { JobStatsBar } from '../jobs_stats_bar';
|
||||
import { NodeAvailableWarning } from '../../../../components/node_available_warning';
|
||||
import { JobsAwaitingNodeWarning } from '../../../../components/jobs_awaiting_node_warning';
|
||||
|
@ -41,6 +40,7 @@ import { removeNodeInfo } from '../../../../../../common/util/job_utils';
|
|||
import { jobCloningService } from '../../../../services/job_cloning_service';
|
||||
import { ANOMALY_DETECTOR_SAVED_OBJECT_TYPE } from '../../../../../../common/types/saved_objects';
|
||||
import { SpaceManagementContextWrapper } from '../../../../components/space_management_context_wrapper';
|
||||
import { DatePicker } from '../../../../components/ml_page/date_picker';
|
||||
|
||||
let blockingJobsRefreshTimeout = null;
|
||||
|
||||
|
@ -427,6 +427,10 @@ export class JobsListViewUI extends Component {
|
|||
return BLOCKED_JOBS_REFRESH_INTERVAL_MS;
|
||||
}
|
||||
|
||||
refreshJobs = () => {
|
||||
this.refreshJobSummaryList();
|
||||
};
|
||||
|
||||
renderJobsListComponents() {
|
||||
const { isRefreshing, loading, jobsSummaryList, jobsAwaitingNodeCount } = this.state;
|
||||
const jobIds = jobsSummaryList.map((j) => j.id);
|
||||
|
@ -448,20 +452,21 @@ export class JobsListViewUI extends Component {
|
|||
|
||||
<>
|
||||
<SpaceManagementContextWrapper>
|
||||
{noJobsFound ? <AnomalyDetectionEmptyState /> : null}
|
||||
{noJobsFound ? <AnomalyDetectionEmptyState showDocsLink /> : null}
|
||||
|
||||
{jobIds.length > 0 ? (
|
||||
<>
|
||||
<EuiFlexGroup justifyContent="spaceBetween">
|
||||
<EuiFlexGroup gutterSize="none">
|
||||
<EuiFlexItem grow={false}>
|
||||
<JobStatsBar
|
||||
jobsSummaryList={jobsSummaryList}
|
||||
showNodeInfo={this.props.showNodeInfo}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<NewJobButton />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={true} />
|
||||
<EuiFlexGroup justifyContent="flexEnd">
|
||||
<DatePicker />
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexGroup>
|
||||
|
||||
<EuiSpacer size="s" />
|
||||
|
|
|
@ -8,23 +8,32 @@
|
|||
import { usePermissionCheck } from '../../../../capabilities/check_capabilities';
|
||||
import { mlNodesAvailable } from '../../../../ml_nodes_check/check_ml_nodes';
|
||||
|
||||
import React from 'react';
|
||||
import React, { useCallback } from 'react';
|
||||
|
||||
import { EuiButton } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { useCreateAndNavigateToMlLink } from '../../../../contexts/kibana/use_create_url';
|
||||
import { ML_PAGES } from '../../../../../../common/constants/locator';
|
||||
import { useMlManagementLocator } from '../../../../contexts/kibana';
|
||||
|
||||
export function NewJobButton() {
|
||||
export function NewJobButton({ size = 's' }) {
|
||||
const canCreateJob = usePermissionCheck('canCreateJob');
|
||||
const buttonEnabled = canCreateJob && mlNodesAvailable();
|
||||
const newJob = useCreateAndNavigateToMlLink(ML_PAGES.ANOMALY_DETECTION_CREATE_JOB_SELECT_INDEX);
|
||||
const mlLocator = useMlManagementLocator();
|
||||
|
||||
const redirectToCreateJobSelectIndexPage = useCallback(async () => {
|
||||
if (!mlLocator || !canCreateJob) return;
|
||||
|
||||
await mlLocator.navigate({
|
||||
sectionId: 'ml',
|
||||
appId: `anomaly_detection/${ML_PAGES.ANOMALY_DETECTION_CREATE_JOB_SELECT_INDEX}`,
|
||||
});
|
||||
}, [mlLocator, canCreateJob]);
|
||||
|
||||
return (
|
||||
<EuiButton
|
||||
data-test-subj="mlCreateNewJobButton"
|
||||
onClick={newJob}
|
||||
size="s"
|
||||
onClick={redirectToCreateJobSelectIndexPage}
|
||||
size={size}
|
||||
disabled={buttonEnabled === false}
|
||||
fill
|
||||
iconType="plusInCircle"
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* 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 React from 'react';
|
||||
import { EuiButtonEmpty } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { useCreateAndNavigateToManagementMlLink } from '../../../../contexts/kibana/use_create_url';
|
||||
import { ML_PAGES } from '../../../../../locator';
|
||||
|
||||
export const SuppliedConfigurationsButton = () => {
|
||||
const redirectToSuppliedConfigurationsPage = useCreateAndNavigateToManagementMlLink(
|
||||
ML_PAGES.SUPPLIED_CONFIGURATIONS,
|
||||
'anomaly_detection'
|
||||
);
|
||||
|
||||
return (
|
||||
<EuiButtonEmpty
|
||||
size="m"
|
||||
iconType="listAdd"
|
||||
onClick={redirectToSuppliedConfigurationsPage}
|
||||
flush="left"
|
||||
data-test-subj="mlSuppliedConfigurationsButton"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.suppliedConfigurationsManagementLabel"
|
||||
defaultMessage="Supplied configurations"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
);
|
||||
};
|
||||
|
||||
export const AnomalyDetectionSettingsButton = () => {
|
||||
const redirectToAnomalyDetectionSettingsPage = useCreateAndNavigateToManagementMlLink(
|
||||
'',
|
||||
'ad_settings'
|
||||
);
|
||||
|
||||
return (
|
||||
<EuiButtonEmpty
|
||||
size="m"
|
||||
iconType="gear"
|
||||
onClick={redirectToAnomalyDetectionSettingsPage}
|
||||
flush="left"
|
||||
data-test-subj="mlAnomalyDetectionSettingsButton"
|
||||
>
|
||||
<FormattedMessage id="xpack.ml.anomalyDetectionSettingsLabel" defaultMessage="Settings" />
|
||||
</EuiButtonEmpty>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,12 @@
|
|||
/*
|
||||
* 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 {
|
||||
SuppliedConfigurationsButton,
|
||||
AnomalyDetectionSettingsButton,
|
||||
} from './anomaly_detection_actions';
|
||||
export { SynchronizeSavedObjectsButton } from './synchronize_saved_objects_button';
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* 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 React, { useMemo, useState } from 'react';
|
||||
import { EuiButtonEmpty } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { usePermissionCheck } from '../../../../capabilities/check_capabilities';
|
||||
import { JobSpacesSyncFlyout } from '../../../../components/job_spaces_sync';
|
||||
|
||||
export const SynchronizeSavedObjectsButton = ({ refreshJobs }: { refreshJobs: () => void }) => {
|
||||
const [showSyncFlyout, setShowSyncFlyout] = useState(false);
|
||||
function onCloseSyncFlyout() {
|
||||
if (typeof refreshJobs === 'function') {
|
||||
refreshJobs();
|
||||
}
|
||||
setShowSyncFlyout(false);
|
||||
}
|
||||
const [canCreateJob, canCreateDataFrameAnalytics, canCreateTrainedModels] = usePermissionCheck([
|
||||
'canCreateJob',
|
||||
'canCreateDataFrameAnalytics',
|
||||
'canCreateTrainedModels',
|
||||
]);
|
||||
|
||||
const canSync = useMemo(
|
||||
() => canCreateJob || canCreateDataFrameAnalytics || canCreateTrainedModels,
|
||||
[canCreateDataFrameAnalytics, canCreateJob, canCreateTrainedModels]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<EuiButtonEmpty
|
||||
disabled={!canSync}
|
||||
size="m"
|
||||
flush="left"
|
||||
iconType="inputOutput"
|
||||
onClick={() => setShowSyncFlyout(true)}
|
||||
data-test-subj="mlStackMgmtSyncButton"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.management.jobsList.syncFlyoutButton"
|
||||
defaultMessage="Synchronize saved objects"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
{showSyncFlyout && <JobSpacesSyncFlyout onClose={onCloseSyncFlyout} />}
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -9,6 +9,7 @@ import { each } from 'lodash';
|
|||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { parseInterval } from '@kbn/ml-parse-interval';
|
||||
import { MANAGEMENT_APP_LOCATOR } from '@kbn/deeplinks-management/constants';
|
||||
|
||||
import { toastNotificationServiceProvider } from '../../../services/toast_notification_service';
|
||||
import { stringMatch } from '../../../util/string_utils';
|
||||
|
@ -17,7 +18,6 @@ import { JOB_ACTION } from '../../../../../common/constants/job_actions';
|
|||
import { mlCalendarService } from '../../../services/calendar_service';
|
||||
import { jobCloningService } from '../../../services/job_cloning_service';
|
||||
import { ML_PAGES } from '../../../../../common/constants/locator';
|
||||
import { PLUGIN_ID } from '../../../../../common/constants/app';
|
||||
import { CREATED_BY_LABEL } from '../../../../../common/constants/new_job';
|
||||
|
||||
export function loadFullJob(mlApi, jobId) {
|
||||
|
@ -215,8 +215,13 @@ function showResults(toastNotifications, resp, action) {
|
|||
}
|
||||
}
|
||||
|
||||
export async function cloneJob(toastNotifications, application, mlApi, jobId) {
|
||||
export async function cloneJob(toastNotifications, share, mlApi, jobId) {
|
||||
try {
|
||||
const managementLocator = share.url.locators.get(MANAGEMENT_APP_LOCATOR);
|
||||
if (!managementLocator) {
|
||||
throw new Error('Could not find management locator');
|
||||
}
|
||||
|
||||
const [{ job: cloneableJob, datafeed }, originalJob] = await Promise.all([
|
||||
loadJobForCloning(mlApi, jobId),
|
||||
loadFullJob(mlApi, jobId),
|
||||
|
@ -283,7 +288,10 @@ export async function cloneJob(toastNotifications, application, mlApi, jobId) {
|
|||
|
||||
jobCloningService.stashJobCloningData(tempJobCloningData);
|
||||
|
||||
application.navigateToApp(PLUGIN_ID, { path: ML_PAGES.ANOMALY_DETECTION_CREATE_JOB });
|
||||
await managementLocator.navigate({
|
||||
sectionId: 'ml',
|
||||
appId: `anomaly_detection/${ML_PAGES.ANOMALY_DETECTION_CREATE_JOB}`,
|
||||
});
|
||||
} catch (error) {
|
||||
toastNotificationServiceProvider(toastNotifications).displayErrorToast(
|
||||
error,
|
||||
|
|
|
@ -7,10 +7,11 @@
|
|||
|
||||
import type { FC } from 'react';
|
||||
import React from 'react';
|
||||
import { useEuiTheme } from '@elastic/eui';
|
||||
import { EuiSpacer, useEuiTheme, EuiFlexItem, EuiFlexGroup } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { usePageUrlState } from '@kbn/ml-url-state';
|
||||
import type { ListingPageUrlState } from '@kbn/ml-url-state';
|
||||
import { css } from '@emotion/react';
|
||||
import { JobsListView } from './components/jobs_list_view';
|
||||
import { ML_PAGES } from '../../../../common/constants/locator';
|
||||
import { HelpMenu } from '../../components/help_menu';
|
||||
|
@ -19,15 +20,26 @@ 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';
|
||||
import { getMlNodeCount } from '../../ml_nodes_check/check_ml_nodes';
|
||||
import {
|
||||
AnomalyDetectionSettingsButton,
|
||||
SuppliedConfigurationsButton,
|
||||
SynchronizeSavedObjectsButton,
|
||||
} from './components/top_level_actions';
|
||||
import { NewJobButton } from './components/new_job_button';
|
||||
import { usePermissionCheck } from '../../capabilities/check_capabilities';
|
||||
import { ImportJobsFlyout } from '../../components/import_export_jobs/import_jobs_flyout';
|
||||
import { ExportJobsFlyout } from '../../components/import_export_jobs';
|
||||
|
||||
interface PageUrlState {
|
||||
pageKey: typeof ML_PAGES.ANOMALY_DETECTION_JOBS_MANAGE;
|
||||
pageKey: typeof ML_PAGES.ANOMALY_DETECTION_JOBS_MANAGE_FOR_URL;
|
||||
pageUrlState: ListingPageUrlState;
|
||||
}
|
||||
|
||||
interface JobsPageProps {
|
||||
isMlEnabledInSpace?: boolean;
|
||||
lastRefresh?: number;
|
||||
refreshList: () => void;
|
||||
}
|
||||
|
||||
export const getDefaultAnomalyDetectionJobsListState = (): ListingPageUrlState => ({
|
||||
|
@ -37,27 +49,65 @@ export const getDefaultAnomalyDetectionJobsListState = (): ListingPageUrlState =
|
|||
sortDirection: 'asc',
|
||||
});
|
||||
|
||||
export const JobsPage: FC<JobsPageProps> = ({ isMlEnabledInSpace, lastRefresh }) => {
|
||||
export const JobsPage: FC<JobsPageProps> = ({ isMlEnabledInSpace, lastRefresh, refreshList }) => {
|
||||
const [pageState, setPageState] = usePageUrlState<PageUrlState>(
|
||||
ML_PAGES.ANOMALY_DETECTION_JOBS_MANAGE,
|
||||
ML_PAGES.ANOMALY_DETECTION_JOBS_MANAGE_FOR_URL,
|
||||
getDefaultAnomalyDetectionJobsListState()
|
||||
);
|
||||
const {
|
||||
services: { docLinks },
|
||||
services: {
|
||||
docLinks,
|
||||
mlServices: { mlApi },
|
||||
},
|
||||
} = useMlKibana();
|
||||
const { euiTheme } = useEuiTheme();
|
||||
getMlNodeCount(mlApi);
|
||||
|
||||
const { showNodeInfo } = useEnabledFeatures();
|
||||
const helpLink = docLinks.links.ml.anomalyDetection;
|
||||
const [canCreateJob] = usePermissionCheck(['canCreateJob']);
|
||||
|
||||
return (
|
||||
<>
|
||||
<MlPageHeader>
|
||||
<FormattedMessage id="xpack.ml.jobsList.title" defaultMessage="Anomaly Detection Jobs" />
|
||||
<EuiFlexGroup wrap={true}>
|
||||
<EuiFlexItem grow={true} css={css({ minWidth: '400px' })}>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.jobsList.title"
|
||||
defaultMessage="Anomaly Detection Jobs"
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={true} />
|
||||
<EuiFlexItem grow={false} justifyContent="flexEnd">
|
||||
<EuiFlexGroup direction="row" gutterSize="s">
|
||||
<EuiFlexItem grow={false}>
|
||||
<SuppliedConfigurationsButton />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<AnomalyDetectionSettingsButton />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<SynchronizeSavedObjectsButton refreshJobs={refreshList} />
|
||||
</EuiFlexItem>
|
||||
|
||||
<EuiFlexItem grow={false}>
|
||||
<ExportJobsFlyout isDisabled={!canCreateJob} currentTab={'anomaly-detector'} />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<ImportJobsFlyout isDisabled={!canCreateJob} onImportComplete={refreshList} />
|
||||
</EuiFlexItem>
|
||||
|
||||
<EuiFlexItem grow={false}>
|
||||
<NewJobButton size="m" />
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</MlPageHeader>
|
||||
<HeaderMenuPortal>
|
||||
<JobsActionMenu />
|
||||
</HeaderMenuPortal>
|
||||
<EuiSpacer size="m" />
|
||||
<JobsListView
|
||||
euiTheme={euiTheme}
|
||||
isMlEnabledInSpace={isMlEnabledInSpace}
|
||||
|
@ -65,6 +115,7 @@ export const JobsPage: FC<JobsPageProps> = ({ isMlEnabledInSpace, lastRefresh })
|
|||
jobsViewState={pageState}
|
||||
onJobsViewStateUpdate={setPageState}
|
||||
showNodeInfo={showNodeInfo}
|
||||
canCreateJob={canCreateJob}
|
||||
/>
|
||||
<HelpMenu docLink={helpLink} />
|
||||
</>
|
||||
|
|
|
@ -21,6 +21,7 @@ import {
|
|||
SPARSE_DATA_AGGREGATIONS,
|
||||
} from '@kbn/ml-anomaly-utils';
|
||||
import { cloneDeep } from 'lodash';
|
||||
import type { MlLocatorParams } from '../../../../../../locator';
|
||||
import { jobCloningService } from '../../../../../services/job_cloning_service';
|
||||
import type {
|
||||
Job,
|
||||
|
@ -28,7 +29,6 @@ import type {
|
|||
Detector,
|
||||
} from '../../../../../../../common/types/anomaly_detection_jobs';
|
||||
import type { NewJobCapsService } from '../../../../../services/new_job_capabilities/new_job_capabilities_service';
|
||||
import type { NavigateToPath } from '../../../../../contexts/kibana';
|
||||
import { ML_PAGES } from '../../../../../../../common/constants/locator';
|
||||
import type { JobCreatorType } from '..';
|
||||
import { CREATED_BY_LABEL, JOB_TYPE } from '../../../../../../../common/constants/new_job';
|
||||
|
@ -237,29 +237,44 @@ export function isSparseDataJob(job: Job, datafeed: Datafeed): boolean {
|
|||
return false;
|
||||
}
|
||||
|
||||
export type NavigateToMlManagementLink = (
|
||||
_page: string,
|
||||
pageState?: MlLocatorParams['pageState']
|
||||
) => Promise<void>;
|
||||
|
||||
export function convertToMultiMetricJob(
|
||||
jobCreator: JobCreatorType,
|
||||
navigateToPath: NavigateToPath
|
||||
navigateToPath: NavigateToMlManagementLink
|
||||
) {
|
||||
jobCreator.createdBy = CREATED_BY_LABEL.MULTI_METRIC;
|
||||
jobCreator.modelPlot = false;
|
||||
jobCloningService.stashJobForCloning(jobCreator, true, true);
|
||||
navigateToPath(ML_PAGES.ANOMALY_DETECTION_CREATE_JOB_CONVERT_TO_MULTI_METRIC, true);
|
||||
navigateToPath(ML_PAGES.ANOMALY_DETECTION_CREATE_JOB_CONVERT_TO_MULTI_METRIC, {
|
||||
index: jobCreator.dataViewId,
|
||||
});
|
||||
}
|
||||
|
||||
export function convertToAdvancedJob(jobCreator: JobCreatorType, navigateToPath: NavigateToPath) {
|
||||
export function convertToAdvancedJob(
|
||||
jobCreator: JobCreatorType,
|
||||
navigateToPath: NavigateToMlManagementLink
|
||||
) {
|
||||
jobCreator.createdBy = null;
|
||||
jobCloningService.stashJobForCloning(jobCreator, true, true);
|
||||
navigateToPath(ML_PAGES.ANOMALY_DETECTION_CREATE_JOB_CONVERT_TO_ADVANCED, true);
|
||||
navigateToPath(ML_PAGES.ANOMALY_DETECTION_CREATE_JOB_CONVERT_TO_ADVANCED, {
|
||||
index: jobCreator.dataViewId,
|
||||
});
|
||||
}
|
||||
|
||||
export function resetAdvancedJob(jobCreator: JobCreatorType, navigateToPath: NavigateToPath) {
|
||||
export function resetAdvancedJob(
|
||||
jobCreator: JobCreatorType,
|
||||
navigateToPath: NavigateToMlManagementLink
|
||||
) {
|
||||
jobCreator.createdBy = null;
|
||||
jobCloningService.stashJobForCloning(jobCreator, true, false);
|
||||
navigateToPath(ML_PAGES.ANOMALY_DETECTION_CREATE_JOB);
|
||||
}
|
||||
|
||||
export function resetJob(jobCreator: JobCreatorType, navigateToPath: NavigateToPath) {
|
||||
export function resetJob(jobCreator: JobCreatorType, navigateToPath: NavigateToMlManagementLink) {
|
||||
jobCreator.jobId = '';
|
||||
jobCloningService.stashJobForCloning(jobCreator, true, true);
|
||||
navigateToPath(ML_PAGES.ANOMALY_DETECTION_CREATE_JOB);
|
||||
|
@ -267,12 +282,12 @@ export function resetJob(jobCreator: JobCreatorType, navigateToPath: NavigateToP
|
|||
|
||||
export function advancedStartDatafeed(
|
||||
jobCreator: JobCreatorType | null,
|
||||
navigateToPath: NavigateToPath
|
||||
navigateToPath: NavigateToMlManagementLink
|
||||
) {
|
||||
if (jobCreator !== null) {
|
||||
jobCloningService.stashJobForCloning(jobCreator, false, false);
|
||||
}
|
||||
navigateToPath('/jobs');
|
||||
navigateToPath(ML_PAGES.ANOMALY_DETECTION_JOBS_MANAGE);
|
||||
}
|
||||
|
||||
export function aggFieldPairsCanBeCharted(afs: AggFieldPair[]) {
|
||||
|
|
|
@ -21,7 +21,8 @@ import { KBN_FIELD_TYPES } from '@kbn/field-types';
|
|||
import { ML_JOB_AGGREGATION } from '@kbn/ml-anomaly-utils';
|
||||
import type { LensApi } from '@kbn/lens-plugin/public';
|
||||
import type { DashboardApi } from '@kbn/dashboard-plugin/public';
|
||||
import { ML_PAGES, ML_APP_LOCATOR } from '../../../../../common/constants/locator';
|
||||
import { ML_PAGES } from '../../../../../common/constants/locator';
|
||||
import { MlManagementLocatorInternal } from '../../../../locator/ml_management_locator';
|
||||
|
||||
export const COMPATIBLE_SERIES_TYPES = [
|
||||
'line',
|
||||
|
@ -51,9 +52,9 @@ export async function redirectToADJobWizards(
|
|||
lens: LensPublicStart
|
||||
) {
|
||||
const { query, filters, to, from, vis } = await getJobsItemsFromEmbeddable(embeddable, lens);
|
||||
const locator = share.url.locators.get(ML_APP_LOCATOR);
|
||||
const locator = new MlManagementLocatorInternal(share);
|
||||
|
||||
const url = await locator?.getUrl({
|
||||
const { url } = await locator?.getUrl({
|
||||
page: ML_PAGES.ANOMALY_DETECTION_CREATE_JOB_FROM_LENS,
|
||||
pageState: {
|
||||
vis: vis as unknown as SerializableRecord,
|
||||
|
|
|
@ -11,7 +11,8 @@ import { apiIsOfType } from '@kbn/presentation-publishing';
|
|||
import type { SharePluginStart } from '@kbn/share-plugin/public';
|
||||
import type { MapApi } from '@kbn/maps-plugin/public';
|
||||
import type { DashboardApi } from '@kbn/dashboard-plugin/public';
|
||||
import { ML_PAGES, ML_APP_LOCATOR } from '../../../../../common/constants/locator';
|
||||
import { ML_PAGES } from '../../../../../common/constants/locator';
|
||||
import { MlManagementLocatorInternal } from '../../../../locator/ml_management_locator';
|
||||
|
||||
export async function redirectToGeoJobWizard(
|
||||
embeddable: MapApi,
|
||||
|
@ -24,7 +25,7 @@ export async function redirectToGeoJobWizard(
|
|||
const { query, filters, to, from } = await getJobsItemsFromEmbeddable(embeddable);
|
||||
const embeddableQuery = embeddable.query$?.value;
|
||||
const embeddableFilters = embeddable.filters$?.value ?? [];
|
||||
const locator = share.url.locators.get(ML_APP_LOCATOR);
|
||||
const locator = new MlManagementLocatorInternal(share);
|
||||
|
||||
const pageState = {
|
||||
dashboard: { query, filters },
|
||||
|
@ -37,7 +38,7 @@ export async function redirectToGeoJobWizard(
|
|||
...(layerQuery ? { layer: { query: layerQuery } } : {}),
|
||||
};
|
||||
|
||||
const url = await locator?.getUrl({
|
||||
const { url } = await locator?.getUrl({
|
||||
page: ML_PAGES.ANOMALY_DETECTION_CREATE_JOB_FROM_MAP,
|
||||
pageState,
|
||||
});
|
||||
|
|
|
@ -9,9 +9,9 @@ import type { DataViewField, DataView } from '@kbn/data-views-plugin/common';
|
|||
import type { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types';
|
||||
import type { SharePluginStart } from '@kbn/share-plugin/public';
|
||||
import type { Query, TimeRange } from '@kbn/es-query';
|
||||
import { ML_APP_LOCATOR } from '../../../../../common/constants/locator';
|
||||
import { ML_PAGES } from '../../../../locator';
|
||||
import type { CategorizationType } from './quick_create_job';
|
||||
import { MlManagementLocatorInternal } from '../../../../locator/ml_management_locator';
|
||||
|
||||
export async function redirectToADJobWizards(
|
||||
categorizationType: CategorizationType,
|
||||
|
@ -23,9 +23,9 @@ export async function redirectToADJobWizards(
|
|||
timeRange: TimeRange,
|
||||
share: SharePluginStart
|
||||
) {
|
||||
const locator = share.url.locators.get(ML_APP_LOCATOR)!;
|
||||
const locator = new MlManagementLocatorInternal(share);
|
||||
|
||||
const url = await locator.getUrl({
|
||||
const { url } = await locator.getUrl({
|
||||
page: ML_PAGES.ANOMALY_DETECTION_CREATE_JOB_FROM_PATTERN_ANALYSIS,
|
||||
pageState: {
|
||||
categorizationType,
|
||||
|
|
|
@ -27,6 +27,7 @@ import { FormattedMessage } from '@kbn/i18n-react';
|
|||
import { SavedObjectFinder } from '@kbn/saved-objects-finder-plugin/public';
|
||||
import { extractErrorMessage } from '@kbn/ml-error-utils';
|
||||
|
||||
import { useNavigateToManagementMlLink } from '../../../../../../../contexts/kibana/use_create_url';
|
||||
import { JobCreatorContext } from '../../../job_creator_context';
|
||||
import type { AdvancedJobCreator } from '../../../../../common/job_creator';
|
||||
import { resetAdvancedJob } from '../../../../../common/job_creator/util/general';
|
||||
|
@ -36,7 +37,7 @@ import type {
|
|||
} from '../../../../../../../../../common/types/anomaly_detection_jobs';
|
||||
import type { DatafeedValidationResponse } from '../../../../../../../../../common/types/job_validation';
|
||||
|
||||
import { useMlKibana, useMlApi, useNavigateToPath } from '../../../../../../../contexts/kibana';
|
||||
import { useMlKibana, useMlApi } from '../../../../../../../contexts/kibana';
|
||||
|
||||
const fixedPageSize: number = 8;
|
||||
|
||||
|
@ -57,7 +58,7 @@ export const ChangeDataViewModal: FC<Props> = ({ onClose }) => {
|
|||
uiSettings,
|
||||
},
|
||||
} = useMlKibana();
|
||||
const navigateToPath = useNavigateToPath();
|
||||
const navigateToMlManagementLink = useNavigateToManagementMlLink('anomaly_detection');
|
||||
const { validateDatafeedPreview } = useMlApi();
|
||||
|
||||
const { jobCreator: jc } = useContext(JobCreatorContext);
|
||||
|
@ -119,9 +120,8 @@ export const ChangeDataViewModal: FC<Props> = ({ onClose }) => {
|
|||
const applyDataView = useCallback(() => {
|
||||
const newIndices = newDataViewTitle.split(',');
|
||||
jobCreator.indices = newIndices;
|
||||
resetAdvancedJob(jobCreator, navigateToPath);
|
||||
// exclude mlJobService from deps
|
||||
}, [jobCreator, newDataViewTitle, navigateToPath]);
|
||||
resetAdvancedJob(jobCreator, navigateToMlManagementLink);
|
||||
}, [jobCreator, newDataViewTitle, navigateToMlManagementLink]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
|
@ -26,23 +26,19 @@ import {
|
|||
} from '../../../../../../../../../settings/calendars/dst_utils';
|
||||
import { JobCreatorContext } from '../../../../../job_creator_context';
|
||||
import { Description } from './description';
|
||||
import { PLUGIN_ID } from '../../../../../../../../../../../common/constants/app';
|
||||
import type { MlCalendar } from '../../../../../../../../../../../common/types/calendars';
|
||||
import { useMlApi, useMlKibana } from '../../../../../../../../../contexts/kibana';
|
||||
import { useMlApi } from '../../../../../../../../../contexts/kibana';
|
||||
import { GLOBAL_CALENDAR } from '../../../../../../../../../../../common/constants/calendars';
|
||||
import { ML_PAGES } from '../../../../../../../../../../../common/constants/locator';
|
||||
import { DescriptionDst } from './description_dst';
|
||||
import { useMlManagementLink } from '../../../../../../../../../contexts/kibana/use_create_url';
|
||||
import { MANAGEMENT_SECTION_IDS } from '../../../../../../../../../management';
|
||||
|
||||
interface Props {
|
||||
isDst?: boolean;
|
||||
}
|
||||
|
||||
export const CalendarsSelection: FC<Props> = ({ isDst = false }) => {
|
||||
const {
|
||||
services: {
|
||||
application: { getUrlForApp },
|
||||
},
|
||||
} = useMlKibana();
|
||||
const mlApi = useMlApi();
|
||||
|
||||
const { jobCreator, jobCreatorUpdate } = useContext(JobCreatorContext);
|
||||
|
@ -90,9 +86,10 @@ export const CalendarsSelection: FC<Props> = ({ isDst = false }) => {
|
|||
},
|
||||
};
|
||||
|
||||
const manageCalendarsHref = getUrlForApp(PLUGIN_ID, {
|
||||
path: isDst ? ML_PAGES.CALENDARS_DST_MANAGE : ML_PAGES.CALENDARS_MANAGE,
|
||||
});
|
||||
const manageCalendarsHref = useMlManagementLink(
|
||||
isDst ? ML_PAGES.CALENDARS_DST_MANAGE : ML_PAGES.CALENDARS_MANAGE,
|
||||
MANAGEMENT_SECTION_IDS.AD_SETTINGS
|
||||
);
|
||||
|
||||
const Desc = isDst ? DescriptionDst : Description;
|
||||
|
||||
|
|
|
@ -10,14 +10,13 @@ import React, { Fragment, useContext } from 'react';
|
|||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { EuiFlexGroup, EuiFlexItem, EuiButtonEmpty } from '@elastic/eui';
|
||||
|
||||
import { useNavigateToPath } from '../../../../../../../contexts/kibana';
|
||||
|
||||
import { convertToMultiMetricJob } from '../../../../../common/job_creator/util/general';
|
||||
|
||||
import { JobCreatorContext } from '../../../job_creator_context';
|
||||
|
||||
import { BucketSpan } from '../bucket_span';
|
||||
import { SparseDataSwitch } from '../sparse_data';
|
||||
import { useNavigateToManagementMlLink } from '../../../../../../../contexts/kibana/use_create_url';
|
||||
|
||||
interface Props {
|
||||
setIsValid: (proceed: boolean) => void;
|
||||
|
@ -25,10 +24,10 @@ interface Props {
|
|||
|
||||
export const SingleMetricSettings: FC<Props> = ({ setIsValid }) => {
|
||||
const { jobCreator } = useContext(JobCreatorContext);
|
||||
const navigateToPath = useNavigateToPath();
|
||||
const navigateToMlManagement = useNavigateToManagementMlLink('anomaly_detection');
|
||||
|
||||
const convertToMultiMetric = () => {
|
||||
convertToMultiMetricJob(jobCreator, navigateToPath);
|
||||
convertToMultiMetricJob(jobCreator, navigateToMlManagement);
|
||||
};
|
||||
|
||||
return (
|
||||
|
|
|
@ -18,6 +18,7 @@ import {
|
|||
} from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { useNavigateToManagementMlLink } from '../../../../../contexts/kibana/use_create_url';
|
||||
import { createResultsUrl } from '../../../../../util/results_url';
|
||||
import { useMlKibana, useNavigateToPath } from '../../../../../contexts/kibana';
|
||||
import { PreviousButton } from '../wizard_nav';
|
||||
|
@ -51,7 +52,7 @@ export const SummaryStep: FC<StepProps> = ({ setCurrentStep, isCurrentStep }) =>
|
|||
} = useMlKibana();
|
||||
|
||||
const navigateToPath = useNavigateToPath();
|
||||
|
||||
const navigateToMlManagement = useNavigateToManagementMlLink('anomaly_detection');
|
||||
const { jobCreator, jobValidator, jobValidatorUpdated, resultsLoader } =
|
||||
useContext(JobCreatorContext);
|
||||
const [progress, setProgress] = useState(resultsLoader.progress);
|
||||
|
@ -107,7 +108,7 @@ export const SummaryStep: FC<StepProps> = ({ setCurrentStep, isCurrentStep }) =>
|
|||
try {
|
||||
await jobCreator.createJob();
|
||||
await jobCreator.createDatafeed();
|
||||
advancedStartDatafeed(showStartModal ? jobCreator : null, navigateToPath);
|
||||
advancedStartDatafeed(showStartModal ? jobCreator : null, navigateToMlManagement);
|
||||
} catch (error) {
|
||||
handleJobCreationError(error);
|
||||
}
|
||||
|
@ -135,11 +136,11 @@ export const SummaryStep: FC<StepProps> = ({ setCurrentStep, isCurrentStep }) =>
|
|||
}
|
||||
|
||||
function clickResetJob() {
|
||||
resetJob(jobCreator, navigateToPath);
|
||||
resetJob(jobCreator, navigateToMlManagement);
|
||||
}
|
||||
|
||||
const convertToAdvanced = () => {
|
||||
convertToAdvancedJob(jobCreator, navigateToPath);
|
||||
convertToAdvancedJob(jobCreator, navigateToMlManagement);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
|
|
|
@ -6,44 +6,51 @@
|
|||
*/
|
||||
|
||||
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,
|
||||
useNavigateToPath,
|
||||
useMlManagementLocator,
|
||||
} from '../../../../contexts/kibana';
|
||||
import { MlPageHeader } from '../../../../components/page_header';
|
||||
|
||||
export interface PageProps {
|
||||
nextStepPath: string;
|
||||
extraButtons?: React.ReactNode;
|
||||
}
|
||||
|
||||
const RESULTS_PER_PAGE = 20;
|
||||
|
||||
type SavedObject = SavedObjectCommon<FinderAttributes & { isTextBasedQuery?: boolean }>;
|
||||
|
||||
export const Page: FC<PageProps> = ({
|
||||
nextStepPath,
|
||||
extraButtons,
|
||||
}: {
|
||||
nextStepPath: string;
|
||||
extraButtons?: React.ReactNode;
|
||||
}) => {
|
||||
export const Page: FC<PageProps> = ({ nextStepPath, extraButtons }) => {
|
||||
const { contentManagement, uiSettings } = useMlKibana().services;
|
||||
const mlLocator = useMlManagementLocator();
|
||||
const navigateToPath = useNavigateToPath();
|
||||
|
||||
const onObjectSelection = useCallback(
|
||||
(id: string, type: string, name?: string) => {
|
||||
const onObjectSelection = async (id: string, type: string, name?: string) => {
|
||||
const urlPath = window.location.pathname;
|
||||
if (urlPath.includes('management')) {
|
||||
await mlLocator?.navigate({
|
||||
sectionId: 'ml',
|
||||
appId: `anomaly_detection/${nextStepPath}?${
|
||||
type === 'index-pattern' ? 'index' : 'savedSearchId'
|
||||
}=${encodeURIComponent(id)}`,
|
||||
});
|
||||
} else {
|
||||
navigateToPath(
|
||||
`${nextStepPath}?${
|
||||
type === 'index-pattern' ? 'index' : 'savedSearchId'
|
||||
}=${encodeURIComponent(id)}`
|
||||
);
|
||||
},
|
||||
[navigateToPath, nextStepPath]
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div data-test-subj="mlPageSourceSelection">
|
||||
|
|
|
@ -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/anomaly_detection/${redirectUrl}`);
|
||||
return Promise.reject();
|
||||
} catch (error) {
|
||||
return Promise.resolve();
|
||||
|
|
|
@ -19,7 +19,7 @@ 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 } from '../../../../contexts/kibana';
|
||||
|
||||
import { useDataSource } from '../../../../contexts/ml';
|
||||
import { DataRecognizer } from '../../../../components/data_recognizer';
|
||||
|
@ -39,7 +39,6 @@ export const Page: FC = () => {
|
|||
} = useMlKibana();
|
||||
|
||||
const dataSourceContext = useDataSource();
|
||||
const navigateToPath = useNavigateToPath();
|
||||
const onSelectDifferentIndex = useCreateAndNavigateToMlLink(
|
||||
ML_PAGES.ANOMALY_DETECTION_CREATE_JOB_SELECT_INDEX
|
||||
);
|
||||
|
@ -50,6 +49,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({
|
||||
|
@ -137,12 +147,12 @@ export const Page: FC = () => {
|
|||
dataVisualizerLink,
|
||||
recentlyAccessed
|
||||
);
|
||||
navigateToPath(`/jobs/new_job/datavisualizer${getUrlParams()}`);
|
||||
navigateToManagementPath(`/jobs/new_job/datavisualizer${getUrlParams()}`);
|
||||
};
|
||||
|
||||
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', {
|
||||
|
@ -158,7 +168,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', {
|
||||
|
@ -175,7 +185,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', {
|
||||
|
@ -192,7 +202,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', {
|
||||
|
@ -209,7 +219,7 @@ export const Page: FC = () => {
|
|||
id: 'mlJobTypeLinkAdvancedJob',
|
||||
},
|
||||
{
|
||||
onClick: () => navigateToPath(`/jobs/new_job/categorization${getUrlParams()}`),
|
||||
onClick: () => navigateToManagementPath(`/jobs/new_job/categorization${getUrlParams()}`),
|
||||
icon: {
|
||||
type: 'createGenericJob',
|
||||
ariaLabel: i18n.translate('xpack.ml.newJob.wizard.jobType.categorizationAriaLabel', {
|
||||
|
@ -225,7 +235,7 @@ export const Page: FC = () => {
|
|||
id: 'mlJobTypeLinkCategorizationJob',
|
||||
},
|
||||
{
|
||||
onClick: () => navigateToPath(`/jobs/new_job/rare${getUrlParams()}`),
|
||||
onClick: () => navigateToManagementPath(`/jobs/new_job/rare${getUrlParams()}`),
|
||||
icon: {
|
||||
type: 'createGenericJob',
|
||||
ariaLabel: i18n.translate('xpack.ml.newJob.wizard.jobType.rareAriaLabel', {
|
||||
|
@ -244,7 +254,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: 'createGeoJob',
|
||||
ariaLabel: i18n.translate('xpack.ml.newJob.wizard.jobType.geoAriaLabel', {
|
||||
|
|
|
@ -11,37 +11,180 @@ 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 { MlCapabilities } from '../../../common/types/capabilities';
|
||||
import type { MlFeatures, NLPSettings, ExperimentalFeatures } from '../../../common/constants/app';
|
||||
import type { MlStartDependencies } from '../../plugin';
|
||||
import type { ITelemetryClient } from '../services/telemetry/types';
|
||||
|
||||
export function registerManagementSection(
|
||||
export enum MANAGEMENT_SECTION_IDS {
|
||||
OVERVIEW = 'overview',
|
||||
ANOMALY_DETECTION = 'anomaly_detection',
|
||||
ANALYTICS = 'analytics',
|
||||
TRAINED_MODELS = 'trained_models',
|
||||
AD_SETTINGS = 'ad_settings',
|
||||
}
|
||||
export type ManagementSectionId = `${MANAGEMENT_SECTION_IDS}`;
|
||||
|
||||
const MANAGED_SECTIONS_SERVERLESS_CHECK: Record<
|
||||
ManagementSectionId,
|
||||
(mlFeatures: MlFeatures, isServerless: boolean, mlCapabilities: MlCapabilities) => boolean
|
||||
> = {
|
||||
[MANAGEMENT_SECTION_IDS.OVERVIEW]: (
|
||||
mlFeatures: MlFeatures,
|
||||
isServerless: boolean,
|
||||
mlCapabilities: MlCapabilities
|
||||
) => {
|
||||
const isEsProject = !mlFeatures.ad && !mlFeatures.dfa && mlFeatures.nlp;
|
||||
if (isEsProject) return true;
|
||||
|
||||
return (
|
||||
// Can see Memory Usage & Notifications
|
||||
mlCapabilities.canViewMlNodes ||
|
||||
(mlFeatures.nlp && mlCapabilities.canGetTrainedModels) ||
|
||||
(mlFeatures.dfa && mlCapabilities.canGetDataFrameAnalytics) ||
|
||||
(mlFeatures.ad && mlCapabilities.canGetJobs)
|
||||
);
|
||||
},
|
||||
[MANAGEMENT_SECTION_IDS.ANOMALY_DETECTION]: (
|
||||
mlFeatures: MlFeatures,
|
||||
isServerless: boolean,
|
||||
mlCapabilities: MlCapabilities
|
||||
) => {
|
||||
return mlFeatures.ad && mlCapabilities.isADEnabled && mlCapabilities.canGetJobs;
|
||||
},
|
||||
[MANAGEMENT_SECTION_IDS.ANALYTICS]: (
|
||||
mlFeatures: MlFeatures,
|
||||
isServerless: boolean,
|
||||
mlCapabilities: MlCapabilities
|
||||
) => {
|
||||
const isEsProject = !mlFeatures.ad && !mlFeatures.dfa && mlFeatures.nlp;
|
||||
if (isEsProject) return false;
|
||||
|
||||
return mlFeatures.dfa && mlCapabilities.isDFAEnabled && mlCapabilities.canGetDataFrameAnalytics;
|
||||
},
|
||||
[MANAGEMENT_SECTION_IDS.TRAINED_MODELS]: (
|
||||
mlFeatures: MlFeatures,
|
||||
isServerless: boolean,
|
||||
mlCapabilities: MlCapabilities
|
||||
) => {
|
||||
const isEsProject = isServerless && !mlFeatures.ad && !mlFeatures.dfa && mlFeatures.nlp;
|
||||
if (isEsProject) return true;
|
||||
return (mlFeatures.nlp || mlFeatures.dfa) && mlCapabilities.canGetTrainedModels;
|
||||
},
|
||||
[MANAGEMENT_SECTION_IDS.AD_SETTINGS]: (
|
||||
mlFeatures: MlFeatures,
|
||||
isServerless: boolean,
|
||||
mlCapabilities: MlCapabilities
|
||||
) => {
|
||||
return mlFeatures.ad && mlCapabilities.isADEnabled && mlCapabilities.canGetJobs;
|
||||
},
|
||||
};
|
||||
|
||||
export const MANAGEMENT_SECTIONS: Record<ManagementSectionId, string> = {
|
||||
[MANAGEMENT_SECTION_IDS.OVERVIEW]: i18n.translate('xpack.ml.management.overviewTitle', {
|
||||
defaultMessage: 'Overview',
|
||||
}),
|
||||
[MANAGEMENT_SECTION_IDS.ANOMALY_DETECTION]: i18n.translate(
|
||||
'xpack.ml.management.anomalyDetectionJobsTitle',
|
||||
{
|
||||
defaultMessage: 'Anomaly Detection Jobs',
|
||||
}
|
||||
),
|
||||
[MANAGEMENT_SECTION_IDS.ANALYTICS]: i18n.translate(
|
||||
'xpack.ml.management.dataFrameAnalyticsJobsTitle',
|
||||
{
|
||||
defaultMessage: 'Data Frame Analytics Jobs',
|
||||
}
|
||||
),
|
||||
[MANAGEMENT_SECTION_IDS.TRAINED_MODELS]: i18n.translate(
|
||||
'xpack.ml.management.trainedModelsTitle',
|
||||
{
|
||||
defaultMessage: 'Trained Models',
|
||||
}
|
||||
),
|
||||
[MANAGEMENT_SECTION_IDS.AD_SETTINGS]: i18n.translate('xpack.ml.management.settingsTitle', {
|
||||
defaultMessage: 'Anomaly Detection Settings',
|
||||
}),
|
||||
};
|
||||
|
||||
export function registerManagementSections(
|
||||
management: ManagementSetup,
|
||||
core: CoreSetup<MlStartDependencies>,
|
||||
deps: { usageCollection?: UsageCollectionSetup },
|
||||
deps: { usageCollection?: UsageCollectionSetup; telemetry?: ITelemetryClient },
|
||||
isServerless: boolean,
|
||||
mlFeatures: MlFeatures
|
||||
mlFeatures: MlFeatures,
|
||||
nlpSettings: NLPSettings,
|
||||
experimentalFeatures: ExperimentalFeatures,
|
||||
mlCapabilities: MlCapabilities
|
||||
) {
|
||||
const appName = i18n.translate('xpack.ml.management.jobsListTitle', {
|
||||
defaultMessage: 'Machine Learning',
|
||||
});
|
||||
Object.keys(MANAGEMENT_SECTIONS).forEach((id) => {
|
||||
const checkPermissionFn = MANAGED_SECTIONS_SERVERLESS_CHECK[id as ManagementSectionId];
|
||||
|
||||
return management.sections.section.insightsAndAlerting.registerApp({
|
||||
id: 'jobsListLink',
|
||||
title: appName,
|
||||
order: 4,
|
||||
if (!checkPermissionFn) {
|
||||
throw new Error(`Unable to check permission for ML management section ${id}`);
|
||||
}
|
||||
const shouldShowSection = checkPermissionFn(mlFeatures, isServerless, mlCapabilities);
|
||||
if (!shouldShowSection) return;
|
||||
|
||||
const sectionId = id as ManagementSectionId;
|
||||
const sectionTitle = MANAGEMENT_SECTIONS[sectionId];
|
||||
management.sections.section.machineLearning
|
||||
.registerApp({
|
||||
id: sectionId,
|
||||
title: sectionTitle,
|
||||
order: 1,
|
||||
async mount(params: ManagementAppMountParams) {
|
||||
const [{ chrome }] = await core.getStartServices();
|
||||
const { docTitle } = chrome;
|
||||
const [coreStart, pluginsStart] = await core.getStartServices();
|
||||
const {
|
||||
chrome: { docTitle },
|
||||
} = coreStart;
|
||||
|
||||
docTitle.change(appName);
|
||||
docTitle.change(sectionTitle);
|
||||
|
||||
const { mountApp } = await import('./jobs_list');
|
||||
const unmountAppCallback = await mountApp(core, params, deps, isServerless, mlFeatures);
|
||||
const mlDeps = {
|
||||
cases: pluginsStart.cases,
|
||||
charts: pluginsStart.charts,
|
||||
contentManagement: pluginsStart.contentManagement,
|
||||
dashboard: pluginsStart.dashboard,
|
||||
data: pluginsStart.data,
|
||||
dataViewEditor: pluginsStart.dataViewEditor,
|
||||
dataVisualizer: pluginsStart.dataVisualizer,
|
||||
fieldFormats: pluginsStart.fieldFormats,
|
||||
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,
|
||||
spaces: pluginsStart.spaces,
|
||||
...deps,
|
||||
};
|
||||
|
||||
const { mountApp } = await import('./mount_management_app');
|
||||
const unmountAppCallback = await mountApp(
|
||||
core,
|
||||
params,
|
||||
mlDeps,
|
||||
isServerless,
|
||||
mlFeatures,
|
||||
experimentalFeatures,
|
||||
nlpSettings,
|
||||
sectionId
|
||||
);
|
||||
|
||||
return () => {
|
||||
docTitle.reset();
|
||||
unmountAppCallback();
|
||||
};
|
||||
},
|
||||
hideFromSidebar: sectionId === MANAGEMENT_SECTION_IDS.AD_SETTINGS,
|
||||
})
|
||||
.enable();
|
||||
});
|
||||
}
|
||||
|
|
|
@ -11,7 +11,6 @@ import { Router } from '@kbn/shared-ux-router';
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage, I18nProvider } from '@kbn/i18n-react';
|
||||
import type { CoreStart } from '@kbn/core/public';
|
||||
|
||||
import {
|
||||
EuiButtonEmpty,
|
||||
EuiFlexGroup,
|
||||
|
@ -35,7 +34,10 @@ 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,
|
||||
usePermissionCheck,
|
||||
} from '../../../../capabilities/check_capabilities';
|
||||
|
||||
import { AccessDeniedPage } from '../access_denied_page';
|
||||
import { InsufficientLicensePage } from '../insufficient_license_page';
|
||||
|
@ -98,6 +100,7 @@ export const JobsListPage: FC<Props> = ({
|
|||
setInitialized(true);
|
||||
};
|
||||
|
||||
const [canCreateJob] = usePermissionCheck(['canCreateJob']);
|
||||
useEffect(() => {
|
||||
check();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
|
@ -205,8 +208,10 @@ export const JobsListPage: FC<Props> = ({
|
|||
>
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem grow={false}>
|
||||
{
|
||||
<>
|
||||
<EuiButtonEmpty
|
||||
disabled={!canCreateJob}
|
||||
onClick={() => setShowSyncFlyout(true)}
|
||||
data-test-subj="mlStackMgmtSyncButton"
|
||||
>
|
||||
|
@ -217,17 +222,21 @@ export const JobsListPage: FC<Props> = ({
|
|||
{showSyncFlyout && <JobSpacesSyncFlyout onClose={onCloseSyncFlyout} />}
|
||||
<EuiSpacer size="s" />
|
||||
</>
|
||||
}
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<ExportJobsFlyout
|
||||
isDisabled={false}
|
||||
isDisabled={!canCreateJob}
|
||||
currentTab={
|
||||
currentTabId === 'trained-model' ? 'anomaly-detector' : currentTabId
|
||||
}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<ImportJobsFlyout isDisabled={false} onImportComplete={refreshJobs} />
|
||||
<ImportJobsFlyout
|
||||
isDisabled={!canCreateJob}
|
||||
onImportComplete={refreshJobs}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
<SpaceManagement
|
||||
|
|
|
@ -261,7 +261,7 @@ export const RefreshButton: FC<{ onRefreshClick: () => void; isRefreshing: boole
|
|||
isRefreshing,
|
||||
}) => (
|
||||
<EuiButtonEmpty
|
||||
data-test-subj={`mlRefreshJobListButton${isRefreshing ? ' loading' : ' loaded'}`}
|
||||
data-test-subj={`mlDatePickerRefreshPageButton${isRefreshing ? ' loading' : ' loaded'}`}
|
||||
onClick={onRefreshClick}
|
||||
isLoading={isRefreshing}
|
||||
iconType={'refresh'}
|
||||
|
|
|
@ -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 { CoreStart } from '@kbn/core/public';
|
||||
import type { MlFeatures, NLPSettings, ExperimentalFeatures } from '../../../common/constants/app';
|
||||
import type { MlStartDependencies } from '../../plugin';
|
||||
import { App } from '../app';
|
||||
import type { ManagementSectionId } from '.';
|
||||
|
||||
const renderApp = (
|
||||
coreStart: CoreStart,
|
||||
params: ManagementAppMountParams,
|
||||
deps: any,
|
||||
isServerless: boolean,
|
||||
mlFeatures: MlFeatures,
|
||||
experimentalFeatures: ExperimentalFeatures,
|
||||
nlpSettings: NLPSettings,
|
||||
entryPoint: ManagementSectionId
|
||||
) => {
|
||||
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: ManagementSectionId
|
||||
) {
|
||||
const [coreStart] = await core.getStartServices();
|
||||
|
||||
return renderApp(
|
||||
coreStart,
|
||||
params,
|
||||
deps,
|
||||
isServerless,
|
||||
mlFeatures,
|
||||
experimentalFeatures,
|
||||
nlpSettings,
|
||||
entryPoint
|
||||
);
|
||||
}
|
|
@ -16,14 +16,13 @@ import { FormattedMessage } from '@kbn/i18n-react';
|
|||
import { useElasticChartsTheme } from '@kbn/charts-theme';
|
||||
import type { MemoryUsageInfo } from '../../../../common/types/trained_models';
|
||||
import type { JobType, MlSavedObjectType } from '../../../../common/types/saved_objects';
|
||||
import { useTrainedModelsApiService } from '../../services/ml_api_service/trained_models';
|
||||
import { LoadingWrapper } from '../../jobs/new_job/pages/components/charts/loading_wrapper';
|
||||
import { useFieldFormatter } from '../../contexts/kibana';
|
||||
|
||||
import { useRefresh } from '../../routing/use_refresh';
|
||||
import { getMemoryItemColor } from '../memory_item_colors';
|
||||
import { useToastNotificationService } from '../../services/toast_notification_service';
|
||||
import { useEnabledFeatures } from '../../contexts/ml';
|
||||
import { useMemoryUsage } from '../use_memory_usage';
|
||||
|
||||
interface Props {
|
||||
node?: string;
|
||||
|
@ -60,13 +59,10 @@ export const JobMemoryTreeMap: FC<Props> = ({ node, type, height }) => {
|
|||
|
||||
const bytesFormatter = useFieldFormatter(FIELD_FORMAT_IDS.BYTES);
|
||||
const { displayErrorToast } = useToastNotificationService();
|
||||
const refresh = useRefresh();
|
||||
const { loading, data: allData, error } = useMemoryUsage(node, type);
|
||||
const chartHeight = height ?? DEFAULT_CHART_HEIGHT;
|
||||
|
||||
const trainedModelsApiService = useTrainedModelsApiService();
|
||||
const [allData, setAllData] = useState<MemoryUsageInfo[]>([]);
|
||||
const [data, setData] = useState<MemoryUsageInfo[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [selectedOptions, setSelectedOptions] = useState<EuiComboBoxOptionOption[] | null>(null);
|
||||
const typeOptions = useMemo(() => {
|
||||
return Object.entries(TYPE_LABELS)
|
||||
|
@ -107,12 +103,9 @@ export const JobMemoryTreeMap: FC<Props> = ({ node, type, height }) => {
|
|||
[selectedOptions]
|
||||
);
|
||||
|
||||
const loadJobMemorySize = useCallback(async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const resp = await trainedModelsApiService.memoryUsage(type, node);
|
||||
setAllData(resp);
|
||||
} catch (error) {
|
||||
useEffect(
|
||||
function handleError() {
|
||||
if (error) {
|
||||
displayErrorToast(
|
||||
error,
|
||||
i18n.translate('xpack.ml.memoryUsage.treeMap.fetchFailedErrorMessage', {
|
||||
|
@ -120,8 +113,9 @@ export const JobMemoryTreeMap: FC<Props> = ({ node, type, height }) => {
|
|||
})
|
||||
);
|
||||
}
|
||||
setLoading(false);
|
||||
}, [trainedModelsApiService, type, node, displayErrorToast]);
|
||||
},
|
||||
[error, displayErrorToast]
|
||||
);
|
||||
|
||||
useEffect(
|
||||
function redrawOnFilterChange() {
|
||||
|
@ -130,12 +124,6 @@ export const JobMemoryTreeMap: FC<Props> = ({ node, type, height }) => {
|
|||
[selectedOptions, allData, filterData]
|
||||
);
|
||||
|
||||
useEffect(
|
||||
function updateOnTimerRefresh() {
|
||||
loadJobMemorySize();
|
||||
},
|
||||
[loadJobMemorySize, refresh]
|
||||
);
|
||||
return (
|
||||
<div
|
||||
style={{ height: chartHeight }}
|
||||
|
|
|
@ -1,76 +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, useState } from 'react';
|
||||
import { mlTimefilterRefresh$, useTimefilter } from '@kbn/ml-date-picker';
|
||||
import { EuiFlexGroup, EuiFlexItem, EuiTabs, EuiTab } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { NodesList } from './nodes_overview';
|
||||
import { MlPageHeader } from '../components/page_header';
|
||||
import { MemoryPage, JobMemoryTreeMap } from './memory_tree_map';
|
||||
import { SavedObjectsWarning } from '../components/saved_objects_warning';
|
||||
import { useEnabledFeatures } from '../contexts/ml';
|
||||
|
||||
enum TAB {
|
||||
NODES,
|
||||
MEMORY_USAGE,
|
||||
}
|
||||
|
||||
export const MemoryUsagePage: FC = () => {
|
||||
const [selectedTab, setSelectedTab] = useState<TAB>(TAB.NODES);
|
||||
useTimefilter({ timeRangeSelector: false, autoRefreshSelector: true });
|
||||
|
||||
const { showNodeInfo } = useEnabledFeatures();
|
||||
|
||||
const refresh = useCallback(() => {
|
||||
mlTimefilterRefresh$.next({
|
||||
lastRefresh: Date.now(),
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<MlPageHeader>
|
||||
<EuiFlexGroup responsive={false} wrap={false} alignItems={'center'} gutterSize={'m'}>
|
||||
<EuiFlexItem grow={false}>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.memoryUsage.memoryUsageHeader"
|
||||
defaultMessage="Memory Usage"
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</MlPageHeader>
|
||||
|
||||
<SavedObjectsWarning onCloseFlyout={refresh} />
|
||||
|
||||
{showNodeInfo ? (
|
||||
<>
|
||||
<EuiTabs data-test-subj="mlMemoryUsageTabs">
|
||||
<EuiTab
|
||||
isSelected={selectedTab === TAB.NODES}
|
||||
onClick={() => setSelectedTab(TAB.NODES)}
|
||||
data-test-subj="mlMemoryUsageTab-nodes"
|
||||
>
|
||||
<FormattedMessage id="xpack.ml.memoryUsage.nodesTab" defaultMessage="Nodes" />
|
||||
</EuiTab>
|
||||
<EuiTab
|
||||
isSelected={selectedTab === TAB.MEMORY_USAGE}
|
||||
onClick={() => setSelectedTab(TAB.MEMORY_USAGE)}
|
||||
data-test-subj="mlMemoryUsageTab-memory-usage"
|
||||
>
|
||||
<FormattedMessage id="xpack.ml.memoryUsage.memoryTab" defaultMessage="Memory usage" />
|
||||
</EuiTab>
|
||||
</EuiTabs>
|
||||
{selectedTab === TAB.NODES ? <NodesList /> : <MemoryPage />}
|
||||
</>
|
||||
) : (
|
||||
<JobMemoryTreeMap />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* 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 { useEffect, useState } from 'react';
|
||||
import useMountedState from 'react-use/lib/useMountedState';
|
||||
import { extractErrorProperties } from '@kbn/ml-error-utils';
|
||||
import { useRefresh } from '../routing/use_refresh';
|
||||
import { useTrainedModelsApiService } from '../services/ml_api_service/trained_models';
|
||||
import type { MlSavedObjectType } from '../../../common/types/saved_objects';
|
||||
import type { MemoryUsageInfo } from '../../../common/types/trained_models';
|
||||
|
||||
export const useMemoryUsage = (node?: string, type?: MlSavedObjectType) => {
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
const [data, setData] = useState<MemoryUsageInfo[]>([]);
|
||||
const [error, setError] = useState<string | undefined>();
|
||||
const refresh = useRefresh();
|
||||
const trainedModelsApiService = useTrainedModelsApiService();
|
||||
const isMounted = useMountedState();
|
||||
|
||||
useEffect(
|
||||
function getMemoryData() {
|
||||
const fetchData = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const resp = await trainedModelsApiService.memoryUsage(type, node);
|
||||
setError(undefined);
|
||||
setData(resp);
|
||||
} catch (e) {
|
||||
const err = extractErrorProperties(e);
|
||||
setError(err.message);
|
||||
}
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
if (!isMounted()) return;
|
||||
|
||||
fetchData();
|
||||
},
|
||||
[node, type, trainedModelsApiService, isMounted, refresh]
|
||||
);
|
||||
|
||||
return { loading, data, error };
|
||||
};
|
|
@ -585,7 +585,7 @@ export const ModelsList: FC<Props> = ({
|
|||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div data-test-subj="mlTrainedModelsList">
|
||||
<SpaceManagementContextWrapper>
|
||||
<SavedObjectsWarning onCloseFlyout={fetchModels} forceRefresh={isLoading} />
|
||||
<EuiFlexGroup justifyContent="spaceBetween">
|
||||
|
@ -744,6 +744,6 @@ export const ModelsList: FC<Props> = ({
|
|||
/>
|
||||
) : null}
|
||||
</SpaceManagementContextWrapper>
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -14,7 +14,11 @@ import {
|
|||
getAnalysisType,
|
||||
type DataFrameAnalysisConfigType,
|
||||
} from '@kbn/ml-data-frame-analytics-utils';
|
||||
import { useMlLink, useMlLocator, useNavigateToPath } from '../../../contexts/kibana';
|
||||
import {
|
||||
useMlLink,
|
||||
useMlManagementLocatorInternal,
|
||||
useNavigateToPath,
|
||||
} from '../../../contexts/kibana';
|
||||
import type { DataFrameAnalyticsListRow } from '../../../data_frame_analytics/pages/analytics_management/components/analytics_list/common';
|
||||
import { getViewLinkStatus } from '../../../data_frame_analytics/pages/analytics_management/components/action_view/get_view_link_status';
|
||||
import { ML_PAGES } from '../../../../../common/constants/locator';
|
||||
|
@ -59,7 +63,7 @@ export const ViewLink: FC<Props> = ({ item }) => {
|
|||
};
|
||||
|
||||
export function useTableActions(): Array<Action<DataFrameAnalyticsListRow>> {
|
||||
const locator = useMlLocator();
|
||||
const mlManagementLocator = useMlManagementLocatorInternal();
|
||||
const navigateToPath = useNavigateToPath();
|
||||
|
||||
return [
|
||||
|
@ -74,13 +78,16 @@ export function useTableActions(): Array<Action<DataFrameAnalyticsListRow>> {
|
|||
type: 'icon',
|
||||
icon: 'list',
|
||||
onClick: async (item) => {
|
||||
const path = await locator?.getUrl({
|
||||
const { url } = await mlManagementLocator?.getUrl(
|
||||
{
|
||||
page: ML_PAGES.DATA_FRAME_ANALYTICS_JOBS_MANAGE,
|
||||
pageState: {
|
||||
jobId: item.id,
|
||||
},
|
||||
});
|
||||
await navigateToPath(path);
|
||||
},
|
||||
'analytics'
|
||||
);
|
||||
await navigateToPath(url);
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
|
@ -22,8 +22,7 @@ import { ML_OVERVIEW_PANELS } from '../../../../../common/types/storage';
|
|||
import { AnalyticsTable } from './table';
|
||||
import { useGetAnalytics } from '../../../data_frame_analytics/pages/analytics_management/services/analytics_service';
|
||||
import type { DataFrameAnalyticsListRow } from '../../../data_frame_analytics/pages/analytics_management/components/analytics_list/common';
|
||||
import { useMlLink } from '../../../contexts/kibana';
|
||||
import { ML_PAGES } from '../../../../../common/constants/locator';
|
||||
import { useMlManagementLocator } from '../../../contexts/kibana';
|
||||
import { useRefresh } from '../../../routing/use_refresh';
|
||||
import type { GetDataFrameAnalyticsStatsResponseError } from '../../../services/ml_api_service/data_frame_analytics';
|
||||
import { AnalyticsEmptyPrompt } from '../../../data_frame_analytics/pages/analytics_management/components/empty_prompt';
|
||||
|
@ -35,14 +34,16 @@ interface Props {
|
|||
}
|
||||
export const AnalyticsPanel: FC<Props> = ({ setLazyJobCount }) => {
|
||||
const refresh = useRefresh();
|
||||
const mlManagementLocator = useMlManagementLocator();
|
||||
|
||||
const [analytics, setAnalytics] = useState<DataFrameAnalyticsListRow[]>([]);
|
||||
const [analyticsStats, setAnalyticsStats] = useState<StatEntry[] | undefined>(undefined);
|
||||
const [errorMessage, setErrorMessage] = useState<GetDataFrameAnalyticsStatsResponseError>();
|
||||
const [isInitialized, setIsInitialized] = useState(false);
|
||||
|
||||
const manageJobsLink = useMlLink({
|
||||
page: ML_PAGES.DATA_FRAME_ANALYTICS_JOBS_MANAGE,
|
||||
const manageJobsLink = mlManagementLocator?.useUrl({
|
||||
sectionId: 'ml',
|
||||
appId: 'analytics',
|
||||
});
|
||||
|
||||
const [panelsState, setPanelsState] = useStorage<
|
||||
|
@ -94,6 +95,7 @@ export const AnalyticsPanel: FC<Props> = ({ setLazyJobCount }) => {
|
|||
|
||||
return (
|
||||
<CollapsiblePanel
|
||||
dataTestSubj={'mlDataFrameAnalyticsPanel'}
|
||||
isOpen={panelsState.dfaJobs}
|
||||
onToggle={(update) => {
|
||||
setPanelsState({ ...panelsState, dfaJobs: update });
|
||||
|
|
|
@ -0,0 +1,140 @@
|
|||
/*
|
||||
* 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 { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { EuiButton } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import type { MlSummaryJobs } from '../../../../common/types/anomaly_detection_jobs';
|
||||
import { ML_PAGES } from '../../../locator';
|
||||
import adImage from '../../jobs/jobs_list/components/anomaly_detection_empty_state/anomaly_detection_kibana.png';
|
||||
import { usePermissionCheck } from '../../capabilities/check_capabilities';
|
||||
import { mlNodesAvailable } from '../../ml_nodes_check';
|
||||
import { useMlApi, useMlLocator, useMlManagementLocator } from '../../contexts/kibana';
|
||||
import { AnomalyDetectionEmptyState } from '../../jobs/jobs_list/components/anomaly_detection_empty_state/anomaly_detection_empty_state';
|
||||
import { MLEmptyPromptCard } from '../../components/overview/ml_empty_prompt_card';
|
||||
|
||||
export const AnomalyDetectionOverviewCard: FC = () => {
|
||||
const [canGetJobs, canCreateJob] = usePermissionCheck(['canGetJobs', 'canCreateJob']);
|
||||
const disableCreateAnomalyDetectionJob = !canCreateJob || !mlNodesAvailable();
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [hasADJobs, setHasADJobs] = useState(false);
|
||||
|
||||
const mlApi = useMlApi();
|
||||
const mlLocator = useMlLocator();
|
||||
const mlManagementLocator = useMlManagementLocator();
|
||||
|
||||
const loadJobs = useCallback(async () => {
|
||||
setIsLoading(true);
|
||||
|
||||
try {
|
||||
const jobsResult: MlSummaryJobs = await mlApi.jobs.jobsSummary([]);
|
||||
if (jobsResult?.length > 0) {
|
||||
setHasADJobs(true);
|
||||
}
|
||||
setIsLoading(false);
|
||||
} catch (e) {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [mlApi]);
|
||||
|
||||
useEffect(() => {
|
||||
loadJobs();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const redirectToMultiMetricExplorer = useCallback(async () => {
|
||||
if (!mlLocator) return;
|
||||
|
||||
await mlLocator.navigate({
|
||||
sectionId: 'ml',
|
||||
page: ML_PAGES.ANOMALY_EXPLORER,
|
||||
});
|
||||
}, [mlLocator]);
|
||||
|
||||
const redirectToManageJobs = useCallback(async () => {
|
||||
if (!mlManagementLocator) return;
|
||||
|
||||
await mlManagementLocator.navigate({
|
||||
sectionId: 'ml',
|
||||
appId: `anomaly_detection`,
|
||||
});
|
||||
}, [mlManagementLocator]);
|
||||
|
||||
const showEmptyState = !isLoading && !hasADJobs;
|
||||
|
||||
const availableActions = useMemo(() => {
|
||||
const actions: React.ReactNode[] = [];
|
||||
if (hasADJobs) {
|
||||
actions.push(
|
||||
<EuiButton
|
||||
color="primary"
|
||||
fill
|
||||
onClick={redirectToMultiMetricExplorer}
|
||||
isDisabled={!canGetJobs}
|
||||
data-test-subj="multiMetricExplorerButton"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.overview.anomalyDetection.anomalyExplorerButtonText"
|
||||
defaultMessage="Anomaly explorer"
|
||||
/>
|
||||
</EuiButton>
|
||||
);
|
||||
}
|
||||
if (canGetJobs && canCreateJob) {
|
||||
actions.push(
|
||||
<EuiButton
|
||||
color="primary"
|
||||
onClick={redirectToManageJobs}
|
||||
isDisabled={disableCreateAnomalyDetectionJob}
|
||||
data-test-subj="manageJobsButton"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.overview.anomalyDetection.manageJobsButton"
|
||||
defaultMessage="Manage jobs"
|
||||
/>
|
||||
</EuiButton>
|
||||
);
|
||||
}
|
||||
return actions;
|
||||
}, [
|
||||
disableCreateAnomalyDetectionJob,
|
||||
hasADJobs,
|
||||
canCreateJob,
|
||||
canGetJobs,
|
||||
redirectToMultiMetricExplorer,
|
||||
redirectToManageJobs,
|
||||
]);
|
||||
|
||||
return showEmptyState ? (
|
||||
<AnomalyDetectionEmptyState />
|
||||
) : (
|
||||
<MLEmptyPromptCard
|
||||
layout="horizontal"
|
||||
hasBorder={true}
|
||||
hasShadow={false}
|
||||
iconSrc={adImage}
|
||||
iconAlt={i18n.translate('xpack.ml.overview.anomalyDetection.title', {
|
||||
defaultMessage: 'Anomaly detection',
|
||||
})}
|
||||
title={i18n.translate('xpack.ml.overview.anomalyDetection.createFirstJobMessage', {
|
||||
defaultMessage: 'Spot anomalies faster',
|
||||
})}
|
||||
body={
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.overview.anomalyDetection.emptyPromptText"
|
||||
defaultMessage="Start automatically spotting anomalies hiding in your time series data and resolve issues faster."
|
||||
/>
|
||||
</p>
|
||||
}
|
||||
actions={availableActions}
|
||||
data-test-subj="mlOverviewAnomalyDetectionCard"
|
||||
/>
|
||||
);
|
||||
};
|
|
@ -8,12 +8,17 @@
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import type { Action } from '@elastic/eui/src/components/basic_table/action_types';
|
||||
import { useTimefilter } from '@kbn/ml-date-picker';
|
||||
import { useMlLocator, useNavigateToPath } from '../../../contexts/kibana';
|
||||
import {
|
||||
useMlLocator,
|
||||
useNavigateToPath,
|
||||
useMlManagementLocatorInternal,
|
||||
} from '../../../contexts/kibana';
|
||||
import { ML_PAGES } from '../../../../../common/constants/locator';
|
||||
import type { Group } from './anomaly_detection_panel';
|
||||
|
||||
export function useGroupActions(): Array<Action<Group>> {
|
||||
const locator = useMlLocator();
|
||||
const mlManagementLocator = useMlManagementLocatorInternal();
|
||||
const timefilter = useTimefilter();
|
||||
const navigateToPath = useNavigateToPath();
|
||||
|
||||
|
@ -32,13 +37,16 @@ export function useGroupActions(): Array<Action<Group>> {
|
|||
icon: 'list',
|
||||
type: 'icon',
|
||||
onClick: async (item) => {
|
||||
const path = await locator?.getUrl({
|
||||
const { url } = await mlManagementLocator?.getUrl(
|
||||
{
|
||||
page: ML_PAGES.ANOMALY_DETECTION_JOBS_MANAGE,
|
||||
pageState: {
|
||||
groupIds: [item.id],
|
||||
},
|
||||
});
|
||||
await navigateToPath(path);
|
||||
},
|
||||
'anomaly_detection'
|
||||
);
|
||||
await navigateToPath(url);
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
|
@ -14,10 +14,9 @@ import { FormattedMessage } from '@kbn/i18n-react';
|
|||
import { useStorage } from '@kbn/ml-local-storage';
|
||||
import type { MlStorageKey, TMlStorageMapped } from '../../../../../common/types/storage';
|
||||
import { ML_OVERVIEW_PANELS } from '../../../../../common/types/storage';
|
||||
import { ML_PAGES } from '../../../../../common/constants/locator';
|
||||
import { OverviewStatsBar } from '../../../components/collapsible_panel/collapsible_panel';
|
||||
import { CollapsiblePanel } from '../../../components/collapsible_panel';
|
||||
import { useMlApi, useMlKibana, useMlLink } from '../../../contexts/kibana';
|
||||
import { useMlApi, useMlKibana, useMlManagementLocator } from '../../../contexts/kibana';
|
||||
import { AnomalyDetectionTable } from './table';
|
||||
import { getGroupsFromJobs, getStatsBarData } from './utils';
|
||||
import type { Dictionary } from '../../../../../common/types/common';
|
||||
|
@ -57,6 +56,7 @@ export const AnomalyDetectionPanel: FC<Props> = ({ anomalyTimelineService, setLa
|
|||
services: { charts: chartsService },
|
||||
} = useMlKibana();
|
||||
const mlApi = useMlApi();
|
||||
const mlManagementLocator = useMlManagementLocator();
|
||||
|
||||
const { displayErrorToast } = useToastNotificationService();
|
||||
const { showNodeInfo } = useEnabledFeatures();
|
||||
|
@ -68,8 +68,9 @@ export const AnomalyDetectionPanel: FC<Props> = ({ anomalyTimelineService, setLa
|
|||
TMlStorageMapped<typeof ML_OVERVIEW_PANELS>
|
||||
>(ML_OVERVIEW_PANELS, overviewPanelDefaultState);
|
||||
|
||||
const manageJobsLink = useMlLink({
|
||||
page: ML_PAGES.ANOMALY_DETECTION_JOBS_MANAGE,
|
||||
const manageJobsLink = mlManagementLocator?.useUrl({
|
||||
sectionId: 'ml',
|
||||
appId: 'anomaly_detection',
|
||||
});
|
||||
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
@ -181,6 +182,7 @@ export const AnomalyDetectionPanel: FC<Props> = ({ anomalyTimelineService, setLa
|
|||
|
||||
return (
|
||||
<CollapsiblePanel
|
||||
dataTestSubj={'mlAnomalyDetectionPanel'}
|
||||
isOpen={panelsState.adJobs}
|
||||
onToggle={(update) => {
|
||||
setPanelsState({ ...panelsState, adJobs: update });
|
||||
|
|
|
@ -0,0 +1,127 @@
|
|||
/*
|
||||
* 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 { EuiButton } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { ML_PAGES } from '../../../locator';
|
||||
import dfaImage from '../../data_frame_analytics/pages/analytics_management/components/empty_prompt/data_frame_analytics_kibana.png';
|
||||
import { usePermissionCheck } from '../../capabilities/check_capabilities';
|
||||
import { useMlApi, useMlLocator, useMlManagementLocator } from '../../contexts/kibana';
|
||||
import { mlNodesAvailable } from '../../ml_nodes_check';
|
||||
import { MLEmptyPromptCard } from '../../components/overview/ml_empty_prompt_card';
|
||||
import { AnalyticsEmptyPrompt } from '../../data_frame_analytics/pages/analytics_management/components/empty_prompt/empty_prompt';
|
||||
export const DataFrameAnalyticsOverviewCard: FC = () => {
|
||||
const mlLocator = useMlLocator();
|
||||
const mlManagementLocator = useMlManagementLocator();
|
||||
|
||||
const [hasDFAs, setHasDFAs] = useState(false);
|
||||
const [canCreateDataFrameAnalytics, canStartStopDataFrameAnalytics, canGetDataFrameAnalytics] =
|
||||
usePermissionCheck([
|
||||
'canCreateDataFrameAnalytics',
|
||||
'canStartStopDataFrameAnalytics',
|
||||
'canGetDataFrameAnalytics',
|
||||
]);
|
||||
|
||||
const disabled =
|
||||
!mlNodesAvailable() || !canCreateDataFrameAnalytics || !canStartStopDataFrameAnalytics;
|
||||
|
||||
const navigateToResultsExplorer = useCallback(async () => {
|
||||
if (!mlLocator) return;
|
||||
|
||||
await mlLocator.navigate({
|
||||
sectionId: 'ml',
|
||||
page: ML_PAGES.DATA_FRAME_ANALYTICS_EXPLORATION,
|
||||
});
|
||||
}, [mlLocator]);
|
||||
|
||||
const navigateToDFAManagementPath = useCallback(async () => {
|
||||
if (!mlManagementLocator) return;
|
||||
|
||||
await mlManagementLocator.navigate({
|
||||
sectionId: 'ml',
|
||||
appId: `analytics`,
|
||||
});
|
||||
}, [mlManagementLocator]);
|
||||
|
||||
const availableActions = useMemo(() => {
|
||||
const actions: React.ReactNode[] = [];
|
||||
if (hasDFAs) {
|
||||
actions.push(
|
||||
<EuiButton
|
||||
fill
|
||||
color="primary"
|
||||
onClick={navigateToResultsExplorer}
|
||||
isDisabled={!canGetDataFrameAnalytics}
|
||||
data-test-subj="mlAnalyticsResultsExplorerButton"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.overview.dataFrameAnalytics.resultsExplorerButtonText"
|
||||
defaultMessage="Results explorer"
|
||||
/>
|
||||
</EuiButton>
|
||||
);
|
||||
}
|
||||
if (!disabled) {
|
||||
actions.push(
|
||||
<EuiButton
|
||||
color="primary"
|
||||
onClick={navigateToDFAManagementPath}
|
||||
isDisabled={disabled}
|
||||
data-test-subj="mlAnalyticsManageDFAJobsButton"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.overview.dataFrameAnalytics.manageJobsButton"
|
||||
defaultMessage="Manage jobs"
|
||||
/>
|
||||
</EuiButton>
|
||||
);
|
||||
}
|
||||
return actions;
|
||||
}, [
|
||||
disabled,
|
||||
navigateToDFAManagementPath,
|
||||
hasDFAs,
|
||||
navigateToResultsExplorer,
|
||||
canGetDataFrameAnalytics,
|
||||
]);
|
||||
|
||||
const mlApi = useMlApi();
|
||||
useEffect(() => {
|
||||
const fetchAnalytics = async () => {
|
||||
const analyticsConfigs = await mlApi.dataFrameAnalytics.getDataFrameAnalytics();
|
||||
if (analyticsConfigs?.count > 0) {
|
||||
setHasDFAs(true);
|
||||
}
|
||||
};
|
||||
fetchAnalytics();
|
||||
}, [mlApi]);
|
||||
|
||||
return !hasDFAs ? (
|
||||
<AnalyticsEmptyPrompt />
|
||||
) : (
|
||||
<MLEmptyPromptCard
|
||||
iconSrc={dfaImage}
|
||||
iconAlt={i18n.translate('xpack.ml.dataFrame.analyticsList.emptyPromptTitle', {
|
||||
defaultMessage: 'Trained analysis of your data',
|
||||
})}
|
||||
title={i18n.translate('xpack.ml.dataFrame.analyticsList.emptyPromptTitle', {
|
||||
defaultMessage: 'Trained analysis of your data',
|
||||
})}
|
||||
body={
|
||||
<FormattedMessage
|
||||
id="xpack.ml.overview.analyticsList.emptyPromptText"
|
||||
defaultMessage="Train outlier detection, regression, or classification machine learning models using data frame analytics."
|
||||
/>
|
||||
}
|
||||
actions={availableActions}
|
||||
data-test-subj="mlOverviewDataFrameAnalyticsCard"
|
||||
/>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* 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 from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import {
|
||||
EuiButtonIcon,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiSpacer,
|
||||
EuiText,
|
||||
EuiTitle,
|
||||
} from '@elastic/eui';
|
||||
|
||||
interface OverviewFooterItemProps {
|
||||
icon: 'machineLearningApp' | 'documentation' | 'dashboardApp';
|
||||
title: string;
|
||||
description: string;
|
||||
docLink: string;
|
||||
callToAction: React.ReactNode;
|
||||
}
|
||||
|
||||
export const OverviewFooterItem: FC<OverviewFooterItemProps> = ({
|
||||
icon,
|
||||
title,
|
||||
description,
|
||||
docLink,
|
||||
callToAction,
|
||||
}) => (
|
||||
<EuiFlexGroup direction="column" gutterSize="xs" alignItems="flexStart">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonIcon
|
||||
display="base"
|
||||
color="primary"
|
||||
href={docLink}
|
||||
iconType={icon}
|
||||
aria-label={i18n.translate('xpack.ml.overviewFooterItem.documentationLink', {
|
||||
defaultMessage: 'Documentation link',
|
||||
})}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiTitle size="xs">
|
||||
<h3>{title}</h3>
|
||||
</EuiTitle>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiText size="s">{description}</EuiText>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiSpacer size="s" />
|
||||
{callToAction}
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
);
|
Binary file not shown.
After Width: | Height: | Size: 48 KiB |
Binary file not shown.
After Width: | Height: | Size: 50 KiB |
|
@ -0,0 +1,142 @@
|
|||
/*
|
||||
* 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 from 'react';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { EuiBetaBadge, EuiFlexGrid, EuiFlexGroup, EuiFlexItem, EuiTitle } from '@elastic/eui';
|
||||
import { MLOverviewCard } from './overview_ml_page';
|
||||
import { ML_PAGES } from '../../locator';
|
||||
|
||||
export const DataVisualizerGrid: FC<{ buttonType?: 'empty' | 'full'; isEsqlEnabled: boolean }> = ({
|
||||
buttonType,
|
||||
isEsqlEnabled,
|
||||
}) => (
|
||||
<EuiFlexGrid gutterSize="m" columns={2}>
|
||||
{isEsqlEnabled ? (
|
||||
<MLOverviewCard
|
||||
layout="horizontal"
|
||||
path={ML_PAGES.DATA_VISUALIZER_ESQL}
|
||||
title={
|
||||
<EuiFlexGroup gutterSize="xs">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiTitle size="s">
|
||||
<h3>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.datavisualizer.selector.selectESQLTitle"
|
||||
defaultMessage="ES|QL"
|
||||
/>
|
||||
</h3>
|
||||
</EuiTitle>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiBetaBadge
|
||||
label=""
|
||||
iconType="beaker"
|
||||
size="m"
|
||||
color="hollow"
|
||||
tooltipContent={
|
||||
<FormattedMessage
|
||||
id="xpack.ml.datavisualizer.selector.esqlTechnicalPreviewBadge.titleMsg"
|
||||
defaultMessage="ES|QL data visualizer is in technical preview."
|
||||
/>
|
||||
}
|
||||
tooltipPosition={'right'}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
}
|
||||
description={i18n.translate(
|
||||
'xpack.ml.datavisualizer.selector.technicalPreviewBadge.contentMsg',
|
||||
{
|
||||
defaultMessage:
|
||||
'The Elasticsearch Query Language (ES|QL) provides a powerful way to filter, transform, and analyze data stored in Elasticsearch.',
|
||||
}
|
||||
)}
|
||||
iconType="esqlVis"
|
||||
buttonLabel={i18n.translate('xpack.ml.datavisualizer.selector.tryESQLNowButtonLabel', {
|
||||
defaultMessage: 'Try it now!',
|
||||
})}
|
||||
cardDataTestSubj="mlDataVisualizerSelectESQLCard"
|
||||
buttonDataTestSubj="mlDataVisualizerSelectESQLButton"
|
||||
buttonType={buttonType}
|
||||
/>
|
||||
) : null}
|
||||
<MLOverviewCard
|
||||
layout="horizontal"
|
||||
path="/filedatavisualizer"
|
||||
title={i18n.translate('xpack.ml.datavisualizer.selector.importDataTitle', {
|
||||
defaultMessage: 'Visualize data from a file',
|
||||
})}
|
||||
description={i18n.translate('xpack.ml.datavisualizer.selector.importDataDescription', {
|
||||
defaultMessage:
|
||||
'Upload your file, analyze its data, and optionally import the data into an index.',
|
||||
})}
|
||||
iconType="addDataApp"
|
||||
buttonLabel={i18n.translate('xpack.ml.datavisualizer.selector.uploadFileButtonLabel', {
|
||||
defaultMessage: 'Select file',
|
||||
})}
|
||||
cardDataTestSubj="mlDataVisualizerCardImportData"
|
||||
buttonDataTestSubj="mlDataVisualizerUploadFileButton"
|
||||
buttonType={buttonType}
|
||||
/>
|
||||
<MLOverviewCard
|
||||
layout="horizontal"
|
||||
path="/datavisualizer_index_select"
|
||||
title={i18n.translate('xpack.ml.datavisualizer.selector.selectDataViewTitle', {
|
||||
defaultMessage: 'Visualize data from a data view',
|
||||
})}
|
||||
description={i18n.translate('xpack.ml.datavisualizer.selector.selectDataViewTitle', {
|
||||
defaultMessage: 'Analyze data, its shape, and statistical metadata from a data view.',
|
||||
})}
|
||||
iconType="dataVisualizer"
|
||||
buttonLabel={i18n.translate('xpack.ml.datavisualizer.selector.selectDataViewButtonLabel', {
|
||||
defaultMessage: 'Select data view',
|
||||
})}
|
||||
cardDataTestSubj="mlDataVisualizerCardIndexData"
|
||||
buttonDataTestSubj="mlDataVisualizerSelectIndexButton"
|
||||
buttonType={buttonType}
|
||||
/>
|
||||
<MLOverviewCard
|
||||
layout="horizontal"
|
||||
path="/data_drift_index_select"
|
||||
title={
|
||||
<>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.datavisualizer.selector.selectDataDriftTitle"
|
||||
defaultMessage="Visualize data using data drift"
|
||||
/>{' '}
|
||||
<EuiBetaBadge
|
||||
label=""
|
||||
iconType="beaker"
|
||||
size="m"
|
||||
color="hollow"
|
||||
tooltipContent={
|
||||
<FormattedMessage
|
||||
id="xpack.ml.datavisualizer.selector.dataDriftTechnicalPreviewBadge.titleMsg"
|
||||
defaultMessage="Data drift visualizer is in technical preview."
|
||||
/>
|
||||
}
|
||||
tooltipPosition={'right'}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
description={i18n.translate('xpack.ml.datavisualizer.selector.dataDriftDescription', {
|
||||
defaultMessage:
|
||||
'Detecting data drifts enables you to identify potential performance issues.',
|
||||
})}
|
||||
iconType="visTagCloud"
|
||||
buttonLabel={i18n.translate('xpack.ml.datavisualizer.selector.selectDataViewButtonLabel', {
|
||||
defaultMessage: 'Compare data distribution',
|
||||
})}
|
||||
cardDataTestSubj="mlDataVisualizerCardDataDriftData"
|
||||
buttonDataTestSubj="mlDataVisualizerSelectDataDriftButton"
|
||||
buttonType={buttonType}
|
||||
/>
|
||||
</EuiFlexGrid>
|
||||
);
|
|
@ -0,0 +1,137 @@
|
|||
/*
|
||||
* 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, { useState } from 'react';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { EuiLink, EuiSpacer } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { mlTimefilterRefresh$, useTimefilter } from '@kbn/ml-date-picker';
|
||||
import { useStorage } from '@kbn/ml-local-storage';
|
||||
import { OverviewStatsBar } from '../components/collapsible_panel/collapsible_panel';
|
||||
import { ML_PAGES } from '../../../common/constants/locator';
|
||||
import type { MlStorageKey, TMlStorageMapped } from '../../../common/types/storage';
|
||||
import { ML_OVERVIEW_PANELS } from '../../../common/types/storage';
|
||||
import { CollapsiblePanel } from '../components/collapsible_panel';
|
||||
import { usePermissionCheck } from '../capabilities/check_capabilities';
|
||||
import { mlNodesAvailable } from '../ml_nodes_check';
|
||||
import { OverviewContent } from './components/content';
|
||||
import { NodeAvailableWarning } from '../components/node_available_warning';
|
||||
import { JobsAwaitingNodeWarning } from '../components/jobs_awaiting_node_warning';
|
||||
import { SavedObjectsWarning } from '../components/saved_objects_warning';
|
||||
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({
|
||||
nodes: true,
|
||||
adJobs: true,
|
||||
dfaJobs: true,
|
||||
});
|
||||
|
||||
export const OverviewPage: FC = () => {
|
||||
const [canViewMlNodes, canCreateJob] = usePermissionCheck(['canViewMlNodes', 'canCreateJob']);
|
||||
|
||||
const disableCreateAnomalyDetectionJob = !canCreateJob || !mlNodesAvailable();
|
||||
const {
|
||||
services: { docLinks },
|
||||
} = useMlKibana();
|
||||
const helpLink = docLinks.links.ml.guide;
|
||||
|
||||
const viewNodesLink = useMlLink({
|
||||
page: ML_PAGES.MEMORY_USAGE,
|
||||
});
|
||||
|
||||
const timefilter = useTimefilter({ timeRangeSelector: true, autoRefreshSelector: true });
|
||||
|
||||
const [adLazyJobCount, setAdLazyJobCount] = useState(0);
|
||||
const [dfaLazyJobCount, setDfaLazyJobCount] = useState(0);
|
||||
|
||||
const [panelsState, setPanelsState] = useStorage<
|
||||
MlStorageKey,
|
||||
TMlStorageMapped<typeof ML_OVERVIEW_PANELS>
|
||||
>(ML_OVERVIEW_PANELS, overviewPanelDefaultState);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<MlPageHeader>
|
||||
<PageTitle
|
||||
title={i18n.translate('xpack.ml.management.machineLearningOverview.overviewLabel', {
|
||||
defaultMessage: 'Machine Learning Overview',
|
||||
})}
|
||||
/>
|
||||
</MlPageHeader>
|
||||
<NodeAvailableWarning />
|
||||
<JobsAwaitingNodeWarning jobCount={adLazyJobCount + dfaLazyJobCount} />
|
||||
<SavedObjectsWarning
|
||||
onCloseFlyout={() => {
|
||||
const { from, to } = timefilter.getTime();
|
||||
const timeRange = { start: from, end: to };
|
||||
mlTimefilterRefresh$.next({
|
||||
lastRefresh: Date.now(),
|
||||
timeRange,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<UpgradeWarning />
|
||||
|
||||
{canViewMlNodes ? (
|
||||
<>
|
||||
<CollapsiblePanel
|
||||
isOpen={panelsState.nodes}
|
||||
onToggle={(update) => {
|
||||
setPanelsState({ ...panelsState, nodes: update });
|
||||
}}
|
||||
header={
|
||||
<FormattedMessage id="xpack.ml.overview.nodesPanel.header" defaultMessage="Nodes" />
|
||||
}
|
||||
headerItems={[
|
||||
<OverviewStatsBar
|
||||
inputStats={[
|
||||
{
|
||||
label: i18n.translate('xpack.ml.overview.nodesPanel.totalNodesLabel', {
|
||||
defaultMessage: 'Total',
|
||||
}),
|
||||
value: getMlNodesCount(),
|
||||
'data-test-subj': 'mlTotalNodesCount',
|
||||
},
|
||||
]}
|
||||
dataTestSub={'mlOverviewAnalyticsStatsBar'}
|
||||
/>,
|
||||
<EuiLink href={viewNodesLink}>
|
||||
{i18n.translate('xpack.ml.overview.nodesPanel.viewNodeLink', {
|
||||
defaultMessage: 'View nodes',
|
||||
})}
|
||||
</EuiLink>,
|
||||
]}
|
||||
ariaLabel={i18n.translate('xpack.ml.overview.nodesPanel.ariaLabel', {
|
||||
defaultMessage: 'overview panel',
|
||||
})}
|
||||
>
|
||||
<NodesList compactView />
|
||||
</CollapsiblePanel>
|
||||
<EuiSpacer size="m" />
|
||||
</>
|
||||
) : null}
|
||||
|
||||
<OverviewContent
|
||||
createAnomalyDetectionJobDisabled={disableCreateAnomalyDetectionJob}
|
||||
setAdLazyJobCount={setAdLazyJobCount}
|
||||
setDfaLazyJobCount={setDfaLazyJobCount}
|
||||
/>
|
||||
<HelpMenu docLink={helpLink} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// required for dynamic import using React.lazy()
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default OverviewPage;
|
|
@ -0,0 +1,487 @@
|
|||
/*
|
||||
* 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, { useMemo, useState, useEffect } from 'react';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import type { GetUserProfileResponse } from '@kbn/core-user-profile-browser';
|
||||
import type { EuiCardProps } from '@elastic/eui';
|
||||
import {
|
||||
EuiButton,
|
||||
EuiButtonEmpty,
|
||||
EuiButtonIcon,
|
||||
EuiCard,
|
||||
EuiFlexGrid,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiHorizontalRule,
|
||||
EuiImage,
|
||||
EuiLink,
|
||||
EuiPageHeader,
|
||||
EuiPageBody,
|
||||
EuiSpacer,
|
||||
EuiText,
|
||||
EuiTitle,
|
||||
useEuiTheme,
|
||||
} from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { ENABLE_ESQL } from '@kbn/esql-utils';
|
||||
import { UpgradeWarning } from '../components/upgrade';
|
||||
import { HelpMenu } from '../components/help_menu';
|
||||
import { useMlKibana, useNavigateToPath } from '../contexts/kibana';
|
||||
import { useCreateAndNavigateToManagementMlLink } from '../contexts/kibana/use_create_url';
|
||||
import { ML_PAGES } from '../../../common/constants/locator';
|
||||
import { MlPageHeader } from '../components/page_header';
|
||||
import { AnomalyDetectionOverviewCard } from './components/anomaly_detection_overview';
|
||||
import { DataFrameAnalyticsOverviewCard } from './components/data_frame_analytics_overview';
|
||||
import { useEnabledFeatures } from '../contexts/ml';
|
||||
import { DataVisualizerGrid } from './data_visualizer_grid';
|
||||
import { OverviewFooterItem } from './components/overview_ml_footer_item';
|
||||
import bannerImageLight from './components/welcome--light.png';
|
||||
import bannerImageDark from './components/welcome--dark.png';
|
||||
|
||||
export const overviewPanelDefaultState = Object.freeze({
|
||||
nodes: true,
|
||||
adJobs: true,
|
||||
dfaJobs: true,
|
||||
});
|
||||
|
||||
export const MLOverviewCard = ({
|
||||
layout,
|
||||
path,
|
||||
title,
|
||||
description,
|
||||
iconType,
|
||||
buttonLabel,
|
||||
cardDataTestSubj,
|
||||
buttonDataTestSubj,
|
||||
buttonType = 'empty',
|
||||
}: {
|
||||
path: string;
|
||||
iconType: string;
|
||||
buttonLabel: string;
|
||||
cardDataTestSubj: string;
|
||||
buttonDataTestSubj: string;
|
||||
buttonType: string | undefined;
|
||||
} & EuiCardProps) => {
|
||||
const navigateToPath = useNavigateToPath();
|
||||
const ButtonComponent = buttonType === 'empty' ? EuiButtonEmpty : EuiButton;
|
||||
|
||||
return (
|
||||
<EuiFlexItem data-test-subj={cardDataTestSubj}>
|
||||
<EuiCard
|
||||
layout={layout}
|
||||
data-test-subj={cardDataTestSubj}
|
||||
hasBorder
|
||||
icon={
|
||||
<EuiButtonIcon
|
||||
display="base"
|
||||
size="s"
|
||||
iconType={iconType}
|
||||
onClick={() => navigateToPath(path)}
|
||||
aria-labelledby="mlOverviewCardTitle"
|
||||
/>
|
||||
}
|
||||
title={title}
|
||||
titleSize="s"
|
||||
titleElement="h3"
|
||||
id="mlOverviewCardTitle"
|
||||
>
|
||||
<EuiFlexItem grow={true}>
|
||||
<EuiSpacer size="m" />
|
||||
<EuiText size="s">{description}</EuiText>
|
||||
</EuiFlexItem>
|
||||
<EuiSpacer size="m" />
|
||||
<ButtonComponent
|
||||
flush="left"
|
||||
target="_self"
|
||||
onClick={() => navigateToPath(path)}
|
||||
data-test-subj={buttonDataTestSubj}
|
||||
aria-label={buttonLabel}
|
||||
>
|
||||
{buttonLabel}
|
||||
</ButtonComponent>
|
||||
</EuiCard>
|
||||
</EuiFlexItem>
|
||||
);
|
||||
};
|
||||
|
||||
export const OverviewPage: FC = () => {
|
||||
const [user, setUser] = useState<GetUserProfileResponse | undefined>();
|
||||
const {
|
||||
services: { docLinks, uiSettings, userProfile },
|
||||
} = useMlKibana();
|
||||
const { colorMode } = useEuiTheme();
|
||||
const isDarkTheme = colorMode === 'DARK';
|
||||
const { isADEnabled, isDFAEnabled, isNLPEnabled } = useEnabledFeatures();
|
||||
const helpLink = docLinks.links.ml.guide;
|
||||
const trainedModelsDocLink = docLinks.links.ml.trainedModels;
|
||||
const navigateToPath = useNavigateToPath();
|
||||
const navigateToTrainedModels = useCreateAndNavigateToManagementMlLink(
|
||||
'',
|
||||
ML_PAGES.TRAINED_MODELS_MANAGE
|
||||
);
|
||||
const navigateToStackManagementMLOverview = useCreateAndNavigateToManagementMlLink(
|
||||
'',
|
||||
'overview'
|
||||
);
|
||||
const isEsqlEnabled = useMemo(() => uiSettings.get(ENABLE_ESQL), [uiSettings]);
|
||||
|
||||
useEffect(
|
||||
function loadUserName() {
|
||||
async function loadUser() {
|
||||
const currentUser = await userProfile.getCurrent();
|
||||
setUser(currentUser);
|
||||
}
|
||||
loadUser();
|
||||
},
|
||||
[userProfile]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<MlPageHeader>
|
||||
<EuiPageHeader
|
||||
alignItems="center"
|
||||
restrictWidth={1200}
|
||||
pageTitle={
|
||||
<EuiFlexGroup direction="column" gutterSize="s">
|
||||
{Boolean(user) ? (
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiText color="subdued">
|
||||
<h4>
|
||||
{user
|
||||
? i18n.translate(
|
||||
'xpack.ml.overview.welcomeBanner.header.greeting.customTitle',
|
||||
{
|
||||
defaultMessage: '👋 Hi {name}!',
|
||||
values: { name: user.user.username ?? '' },
|
||||
}
|
||||
)
|
||||
: i18n.translate(
|
||||
'xpack.ml.overview.welcomeBanner.header.greeting.defaultTitle',
|
||||
{
|
||||
defaultMessage: '👋 Hi',
|
||||
}
|
||||
)}
|
||||
</h4>
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
) : null}
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiTitle size="l">
|
||||
<h1>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.overview.welcomeBanner.header.title"
|
||||
defaultMessage="Welcome to the Machine Learning Hub"
|
||||
/>
|
||||
</h1>
|
||||
</EuiTitle>
|
||||
<EuiSpacer size="s" />
|
||||
<EuiText color="subdued">
|
||||
{i18n.translate('xpack.ml.overview.welcomeBanner.header.titleDescription', {
|
||||
defaultMessage:
|
||||
'Analyze your data and generate models for its patterns of behavior.',
|
||||
})}
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
}
|
||||
rightSideItems={[
|
||||
<EuiImage
|
||||
alt={i18n.translate('xpack.ml.overview.welcomeBanner.header.imageAlt', {
|
||||
defaultMessage: 'Welcome to the Machine Learning Hub',
|
||||
})}
|
||||
src={isDarkTheme ? bannerImageDark : bannerImageLight}
|
||||
size="l"
|
||||
/>,
|
||||
]}
|
||||
/>
|
||||
</MlPageHeader>
|
||||
<EuiPageBody restrictWidth={1200}>
|
||||
<UpgradeWarning />
|
||||
<EuiSpacer size="s" />
|
||||
<EuiFlexGroup gutterSize="m" direction="column">
|
||||
{isADEnabled || isDFAEnabled ? (
|
||||
<>
|
||||
<EuiFlexGroup direction="column">
|
||||
<EuiFlexItem>
|
||||
<EuiTitle size="s">
|
||||
<h2>
|
||||
{i18n.translate('xpack.ml.overview.analyzeYourDataTitle', {
|
||||
defaultMessage: 'Analyze your data',
|
||||
})}
|
||||
</h2>
|
||||
</EuiTitle>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiFlexGroup gutterSize="m">
|
||||
{isADEnabled ? (
|
||||
<EuiFlexItem data-test-subj="mlOverviewAnomalyDetectionCard">
|
||||
<AnomalyDetectionOverviewCard />
|
||||
</EuiFlexItem>
|
||||
) : null}
|
||||
{isDFAEnabled ? (
|
||||
<EuiFlexItem data-test-subj="mlOverviewCardDataFrameAnalytics">
|
||||
<DataFrameAnalyticsOverviewCard />
|
||||
</EuiFlexItem>
|
||||
) : null}
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
|
||||
<EuiSpacer size="s" />
|
||||
</>
|
||||
) : null}
|
||||
<EuiFlexGroup direction="column">
|
||||
<EuiTitle size="s">
|
||||
<h2>
|
||||
{i18n.translate('xpack.ml.overview.aiopsLabsTitle', {
|
||||
defaultMessage: 'AIOps Labs',
|
||||
})}
|
||||
</h2>
|
||||
</EuiTitle>
|
||||
<EuiFlexGrid gutterSize="m" columns={3}>
|
||||
<EuiFlexItem>
|
||||
<EuiCard
|
||||
textAlign="left"
|
||||
layout="vertical"
|
||||
hasBorder
|
||||
icon={
|
||||
<EuiButtonIcon
|
||||
display="base"
|
||||
size="s"
|
||||
onClick={() => navigateToPath('/aiops/log_rate_analysis_index_select')}
|
||||
iconType="logRateAnalysis"
|
||||
aria-label={i18n.translate('xpack.ml.overview.logRateAnalysis.title', {
|
||||
defaultMessage: 'Log Rate Analysis',
|
||||
})}
|
||||
/>
|
||||
}
|
||||
title={
|
||||
<FormattedMessage
|
||||
id="xpack.ml.overview.logRateAnalysis.title"
|
||||
defaultMessage="Log Rate Analysis"
|
||||
/>
|
||||
}
|
||||
titleElement="h3"
|
||||
titleSize="s"
|
||||
description={
|
||||
<>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.overview.logRateAnalysis.description"
|
||||
defaultMessage="Advanced statistical methods to identify reasons for increases or decreases in log rates and displays the statistically significant data in a tabular format."
|
||||
/>
|
||||
</>
|
||||
}
|
||||
footer={
|
||||
<EuiButton
|
||||
color="primary"
|
||||
target="_self"
|
||||
onClick={() => navigateToPath('/aiops/log_rate_analysis_index_select')}
|
||||
data-test-subj="mlOverviewCardLogRateAnalysisButton"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.overview.logRateAnalysis.startAnalysisButton"
|
||||
defaultMessage="Start analysis"
|
||||
/>
|
||||
</EuiButton>
|
||||
}
|
||||
data-test-subj="mlOverviewCardLogRateAnalysis"
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
|
||||
<EuiFlexItem>
|
||||
<EuiCard
|
||||
textAlign="left"
|
||||
layout="vertical"
|
||||
hasBorder
|
||||
icon={
|
||||
<EuiButtonIcon
|
||||
display="base"
|
||||
size="s"
|
||||
iconType="logPatternAnalysis"
|
||||
onClick={() => navigateToPath('/aiops/log_categorization_index_select')}
|
||||
aria-label={i18n.translate('xpack.ml.overview.logPatternAnalysisTitle', {
|
||||
defaultMessage: 'Log Pattern Analysis',
|
||||
})}
|
||||
/>
|
||||
}
|
||||
title={
|
||||
<FormattedMessage
|
||||
id="xpack.ml.overview.logPatternAnalysisTitle"
|
||||
defaultMessage="Log Pattern Analysis"
|
||||
/>
|
||||
}
|
||||
titleElement="h3"
|
||||
titleSize="s"
|
||||
description={
|
||||
<>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.overview.logPatternAnalysisDescription"
|
||||
defaultMessage="Find patterns in unstructured log messages and make it easier to examine your data."
|
||||
/>
|
||||
</>
|
||||
}
|
||||
footer={
|
||||
<EuiButton
|
||||
color="primary"
|
||||
target="_self"
|
||||
onClick={() => navigateToPath('/aiops/log_categorization_index_select')}
|
||||
data-test-subj="mlOverviewCardLogPatternAnalysisButton"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.overview.logPatternAnalysis.startAnalysisButton"
|
||||
defaultMessage="Start analysis"
|
||||
/>
|
||||
</EuiButton>
|
||||
}
|
||||
data-test-subj="mlOverviewCardLogPatternAnalysis"
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiCard
|
||||
textAlign="left"
|
||||
layout="vertical"
|
||||
hasBorder
|
||||
icon={
|
||||
<EuiButtonIcon
|
||||
display="base"
|
||||
size="s"
|
||||
iconType="changePointDetection"
|
||||
onClick={() => navigateToPath('/aiops/change_point_detection_index_select')}
|
||||
aria-label={i18n.translate('xpack.ml.overview.changePointDetection.title', {
|
||||
defaultMessage: 'Change Point Detection',
|
||||
})}
|
||||
/>
|
||||
}
|
||||
title={
|
||||
<FormattedMessage
|
||||
id="xpack.ml.overview.changePointDetection.title"
|
||||
defaultMessage="Change Point Detection"
|
||||
/>
|
||||
}
|
||||
titleElement="h3"
|
||||
titleSize="s"
|
||||
description={
|
||||
<>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.overview.changePointDetection.description"
|
||||
defaultMessage="Change point detection uses the change point aggregation to detect distribution changes, trend changes, and other statistically significant change points in a metric of your time series data."
|
||||
/>
|
||||
</>
|
||||
}
|
||||
footer={
|
||||
<EuiButton
|
||||
color="primary"
|
||||
target="_self"
|
||||
onClick={() => navigateToPath('/aiops/change_point_detection_index_select')}
|
||||
data-test-subj="mlOverviewCardChangePointDetectionButton"
|
||||
aria-label={i18n.translate(
|
||||
'xpack.ml.overview.changePointDetection.startDetectionButton',
|
||||
{
|
||||
defaultMessage: 'Start detection',
|
||||
}
|
||||
)}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.overview.changePointDetection.startDetectionButton"
|
||||
defaultMessage="Start detection"
|
||||
/>
|
||||
</EuiButton>
|
||||
}
|
||||
data-test-subj="mlOverviewCardChangePointDetection"
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGrid>
|
||||
</EuiFlexGroup>
|
||||
<EuiSpacer size="s" />
|
||||
<EuiFlexGroup direction="column">
|
||||
<EuiTitle size="s">
|
||||
<h2>
|
||||
{i18n.translate('xpack.ml.overview.visualizeYourDataTitle', {
|
||||
defaultMessage: 'Visualize your data',
|
||||
})}
|
||||
</h2>
|
||||
</EuiTitle>
|
||||
<DataVisualizerGrid isEsqlEnabled={isEsqlEnabled} />
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexGroup>
|
||||
<HelpMenu docLink={helpLink} />
|
||||
</EuiPageBody>
|
||||
<EuiHorizontalRule />
|
||||
<EuiFlexGroup>
|
||||
{isADEnabled || isNLPEnabled || isDFAEnabled ? (
|
||||
<EuiFlexItem>
|
||||
<OverviewFooterItem
|
||||
icon="dashboardApp"
|
||||
title={i18n.translate('xpack.ml.overview.manageMlAssetsTitle', {
|
||||
defaultMessage: 'Manage ML Assets',
|
||||
})}
|
||||
description={i18n.translate('xpack.ml.overview.manageMlAssetsDescription', {
|
||||
defaultMessage: 'Overview of your ML jobs, memory usage, and notifications.',
|
||||
})}
|
||||
docLink={helpLink}
|
||||
callToAction={
|
||||
<EuiLink onClick={navigateToStackManagementMLOverview}>
|
||||
{i18n.translate('xpack.ml.overview.goToManagmentLink', {
|
||||
defaultMessage: 'Go to Management',
|
||||
})}
|
||||
</EuiLink>
|
||||
}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
) : null}
|
||||
{isNLPEnabled || isDFAEnabled ? (
|
||||
<EuiFlexItem>
|
||||
<OverviewFooterItem
|
||||
icon="machineLearningApp"
|
||||
title={i18n.translate('xpack.ml.overview.trainedModelsTitle', {
|
||||
defaultMessage: 'Trained Models',
|
||||
})}
|
||||
description={i18n.translate('xpack.ml.overview.trainedModelsDescription', {
|
||||
defaultMessage:
|
||||
'Add or manage Trained Models. See deployment stats or add a new deployment.',
|
||||
})}
|
||||
docLink={trainedModelsDocLink}
|
||||
callToAction={
|
||||
<EuiLink onClick={navigateToTrainedModels}>
|
||||
{i18n.translate('xpack.ml.overview.manageTrainedModelsLink', {
|
||||
defaultMessage: 'Manage Trained Models',
|
||||
})}
|
||||
</EuiLink>
|
||||
}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
) : null}
|
||||
<EuiFlexItem>
|
||||
<OverviewFooterItem
|
||||
icon="documentation"
|
||||
title={i18n.translate('xpack.ml.overview.browseDocumentationTitle', {
|
||||
defaultMessage: 'Browse documentation',
|
||||
})}
|
||||
description={i18n.translate('xpack.ml.overview.browseDocumentationDescription', {
|
||||
defaultMessage: 'In-depth guides on Elastic Machine Learning.',
|
||||
})}
|
||||
docLink={helpLink}
|
||||
callToAction={
|
||||
<EuiLink href={helpLink} external target="_blank">
|
||||
{i18n.translate('xpack.ml.overview.startReadingDocsLink', {
|
||||
defaultMessage: 'Start Reading',
|
||||
})}
|
||||
</EuiLink>
|
||||
}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
// required for dynamic import using React.lazy()
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default OverviewPage;
|
|
@ -6,16 +6,18 @@
|
|||
*/
|
||||
|
||||
import type { FC } from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import React, { useMemo, useEffect, useState } from 'react';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { EuiLink, EuiSpacer } from '@elastic/eui';
|
||||
import { EuiSpacer, EuiTab, EuiTabs, EuiNotificationBadge } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { mlTimefilterRefresh$, useTimefilter } from '@kbn/ml-date-picker';
|
||||
import type { TimefilterContract } from '@kbn/data-plugin/public';
|
||||
import { mlTimefilterRefresh$ } from '@kbn/ml-date-picker';
|
||||
import { useStorage } from '@kbn/ml-local-storage';
|
||||
import { FIELD_FORMAT_IDS } from '@kbn/field-formats-plugin/common';
|
||||
import { useUrlState } from '@kbn/ml-url-state';
|
||||
import { OverviewStatsBar } from '../components/collapsible_panel/collapsible_panel';
|
||||
import { ML_PAGES } from '../../../common/constants/locator';
|
||||
import type { MlStorageKey, TMlStorageMapped } from '../../../common/types/storage';
|
||||
import { ML_OVERVIEW_PANELS } from '../../../common/types/storage';
|
||||
import { ML_OVERVIEW_PANELS, ML_OVERVIEW_PANELS_EXTENDED } from '../../../common/types/storage';
|
||||
import { CollapsiblePanel } from '../components/collapsible_panel';
|
||||
import { usePermissionCheck } from '../capabilities/check_capabilities';
|
||||
import { mlNodesAvailable } from '../ml_nodes_check';
|
||||
|
@ -25,19 +27,49 @@ import { JobsAwaitingNodeWarning } from '../components/jobs_awaiting_node_warnin
|
|||
import { SavedObjectsWarning } from '../components/saved_objects_warning';
|
||||
import { UpgradeWarning } from '../components/upgrade';
|
||||
import { HelpMenu } from '../components/help_menu';
|
||||
import { useMlKibana, useMlLink } from '../contexts/kibana';
|
||||
import { useMlKibana } from '../contexts/kibana';
|
||||
import { useMlNotifications } from '../contexts/ml/ml_notifications_context';
|
||||
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';
|
||||
import { MemoryPage } from '../memory_usage/memory_tree_map/memory_page';
|
||||
import { NotificationsList } from '../notifications/components/notifications_list';
|
||||
import { useMemoryUsage } from '../memory_usage/use_memory_usage';
|
||||
import { useFieldFormatter } from '../contexts/kibana';
|
||||
import type { MlSavedObjectType } from '../../../common/types/saved_objects';
|
||||
import { type StatEntry } from '../components/collapsible_panel/collapsible_panel';
|
||||
|
||||
export const overviewPanelDefaultState = Object.freeze({
|
||||
nodes: true,
|
||||
adJobs: true,
|
||||
dfaJobs: true,
|
||||
});
|
||||
const overviewPanelExtendedDefaultState = Object.freeze({
|
||||
memoryUsage: true,
|
||||
});
|
||||
const MEMORY_STATS_LABELS = {
|
||||
'anomaly-detector': i18n.translate('xpack.ml.overview.memoryUsagePanel.anomalyDetectionLabel', {
|
||||
defaultMessage: 'Anomaly detection',
|
||||
}),
|
||||
'data-frame-analytics': i18n.translate(
|
||||
'xpack.ml.overview.memoryUsagePanel.dataFrameAnalyticsLabel',
|
||||
{
|
||||
defaultMessage: 'Data frame analytics',
|
||||
}
|
||||
),
|
||||
'trained-model': i18n.translate('xpack.ml.overview.memoryUsagePanel.trainedModelsLabel', {
|
||||
defaultMessage: 'Trained models',
|
||||
}),
|
||||
};
|
||||
|
||||
export const OverviewPage: FC = () => {
|
||||
enum TAB_IDS {
|
||||
OVERVIEW = 'overview',
|
||||
NOTIFICATIONS = 'notifications',
|
||||
}
|
||||
export type TabIdType = (typeof TAB_IDS)[keyof typeof TAB_IDS];
|
||||
|
||||
export const OverviewPage: FC<{ timefilter: TimefilterContract }> = ({ timefilter }) => {
|
||||
const [canViewMlNodes, canCreateJob] = usePermissionCheck(['canViewMlNodes', 'canCreateJob']);
|
||||
|
||||
const disableCreateAnomalyDetectionJob = !canCreateJob || !mlNodesAvailable();
|
||||
|
@ -45,27 +77,201 @@ export const OverviewPage: FC = () => {
|
|||
services: { docLinks },
|
||||
} = useMlKibana();
|
||||
const helpLink = docLinks.links.ml.guide;
|
||||
const {
|
||||
data: memoryUsageData,
|
||||
error: memoryUsageError,
|
||||
loading: memoryUsageLoading,
|
||||
} = useMemoryUsage();
|
||||
const bytesFormatter = useFieldFormatter(FIELD_FORMAT_IDS.BYTES);
|
||||
const { notificationsCounts } = useMlNotifications();
|
||||
const errorsAndWarningCount =
|
||||
(notificationsCounts?.error ?? 0) + (notificationsCounts?.warning ?? 0);
|
||||
const [pageState, setPageState] = useUrlState('_g');
|
||||
|
||||
const viewNodesLink = useMlLink({
|
||||
page: ML_PAGES.MEMORY_USAGE,
|
||||
});
|
||||
|
||||
const timefilter = useTimefilter({ timeRangeSelector: true, autoRefreshSelector: true });
|
||||
|
||||
const selectedTabId = pageState?.tab ?? TAB_IDS.OVERVIEW;
|
||||
const setSelectedTabId = (tabId: TabIdType) => {
|
||||
setPageState({ tab: tabId });
|
||||
};
|
||||
const [adLazyJobCount, setAdLazyJobCount] = useState(0);
|
||||
const [dfaLazyJobCount, setDfaLazyJobCount] = useState(0);
|
||||
const [memoryUsageStats, setMemoryUsageStats] = useState<StatEntry[]>([]);
|
||||
|
||||
const [panelsState, setPanelsState] = useStorage<
|
||||
MlStorageKey,
|
||||
TMlStorageMapped<typeof ML_OVERVIEW_PANELS>
|
||||
>(ML_OVERVIEW_PANELS, overviewPanelDefaultState);
|
||||
|
||||
const [panelsExtendedState, setPanelsExtendedState] = useStorage<
|
||||
MlStorageKey,
|
||||
TMlStorageMapped<typeof ML_OVERVIEW_PANELS_EXTENDED>
|
||||
>(ML_OVERVIEW_PANELS_EXTENDED, overviewPanelExtendedDefaultState);
|
||||
|
||||
useEffect(
|
||||
function setUpMemoryUsageStats() {
|
||||
if (memoryUsageLoading || memoryUsageError) return;
|
||||
|
||||
const sumSizeByType = memoryUsageData.reduce((acc, current) => {
|
||||
const { type, size } = current;
|
||||
if (acc[type] === undefined) {
|
||||
acc[type] = size;
|
||||
} else {
|
||||
acc[type] = (acc[type] as number) + size;
|
||||
}
|
||||
return acc;
|
||||
}, {} as Record<MlSavedObjectType, number>);
|
||||
|
||||
const formattedSizes: StatEntry[] = [];
|
||||
|
||||
Object.keys(sumSizeByType).forEach((type) => {
|
||||
const size = sumSizeByType[type as MlSavedObjectType];
|
||||
formattedSizes.push({
|
||||
label: MEMORY_STATS_LABELS[type as MlSavedObjectType],
|
||||
value: bytesFormatter(size),
|
||||
'data-test-subj': `mlMemoryUsageStatsCount ${type}`,
|
||||
});
|
||||
});
|
||||
|
||||
setMemoryUsageStats(formattedSizes);
|
||||
},
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[memoryUsageLoading, memoryUsageError]
|
||||
);
|
||||
|
||||
const tabs = useMemo(
|
||||
() => [
|
||||
{
|
||||
id: TAB_IDS.OVERVIEW,
|
||||
name: (
|
||||
<FormattedMessage id="xpack.ml.overview.overviewTabLabel" defaultMessage="Overview" />
|
||||
),
|
||||
content: (
|
||||
<>
|
||||
<>
|
||||
<CollapsiblePanel
|
||||
dataTestSubj="mlMemoryUsagePanel"
|
||||
isOpen={panelsExtendedState.memoryUsage}
|
||||
onToggle={(update) => {
|
||||
setPanelsExtendedState({ ...panelsExtendedState, memoryUsage: update });
|
||||
}}
|
||||
header={
|
||||
<FormattedMessage
|
||||
id="xpack.ml.overview.memoryUsagePanel.header"
|
||||
defaultMessage="Memory Usage"
|
||||
/>
|
||||
}
|
||||
headerItems={[
|
||||
<OverviewStatsBar
|
||||
inputStats={memoryUsageStats}
|
||||
dataTestSub={'mlOverviewMemoryUsageStatsBar'}
|
||||
/>,
|
||||
]}
|
||||
ariaLabel={i18n.translate('xpack.ml.overview.memoryUsagePanel.ariaLabel', {
|
||||
defaultMessage: 'Memory usage panel',
|
||||
})}
|
||||
>
|
||||
<MemoryPage />
|
||||
</CollapsiblePanel>
|
||||
<EuiSpacer size="m" />
|
||||
</>
|
||||
{canViewMlNodes ? (
|
||||
<>
|
||||
<CollapsiblePanel
|
||||
dataTestSubj="mlNodesPanel"
|
||||
isOpen={panelsState.nodes}
|
||||
onToggle={(update) => {
|
||||
setPanelsState({ ...panelsState, nodes: update });
|
||||
}}
|
||||
header={
|
||||
<FormattedMessage
|
||||
id="xpack.ml.overview.nodesPanel.header"
|
||||
defaultMessage="Nodes"
|
||||
/>
|
||||
}
|
||||
headerItems={[
|
||||
<OverviewStatsBar
|
||||
inputStats={[
|
||||
{
|
||||
label: i18n.translate('xpack.ml.overview.nodesPanel.totalNodesLabel', {
|
||||
defaultMessage: 'Total',
|
||||
}),
|
||||
value: getMlNodesCount(),
|
||||
'data-test-subj': 'mlTotalNodesCount',
|
||||
},
|
||||
]}
|
||||
dataTestSub={'mlOverviewAnalyticsStatsBar'}
|
||||
/>,
|
||||
]}
|
||||
ariaLabel={i18n.translate('xpack.ml.overview.nodesPanel.ariaLabel', {
|
||||
defaultMessage: 'overview panel',
|
||||
})}
|
||||
>
|
||||
<NodesList />
|
||||
</CollapsiblePanel>
|
||||
<EuiSpacer size="m" />
|
||||
</>
|
||||
) : null}
|
||||
|
||||
<OverviewContent
|
||||
createAnomalyDetectionJobDisabled={disableCreateAnomalyDetectionJob}
|
||||
setAdLazyJobCount={setAdLazyJobCount}
|
||||
setDfaLazyJobCount={setDfaLazyJobCount}
|
||||
/>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
id: TAB_IDS.NOTIFICATIONS,
|
||||
name: (
|
||||
<FormattedMessage
|
||||
id="xpack.ml.overview.notificationsTabLabel"
|
||||
defaultMessage="Notifications"
|
||||
/>
|
||||
),
|
||||
append: errorsAndWarningCount ? (
|
||||
<EuiNotificationBadge
|
||||
aria-label={i18n.translate('xpack.ml.overview.notificationsIndicator.unreadErrors', {
|
||||
defaultMessage: 'Unread errors or warnings indicator.',
|
||||
})}
|
||||
data-test-subj={'mlNotificationErrorsIndicator'}
|
||||
>
|
||||
{errorsAndWarningCount}
|
||||
</EuiNotificationBadge>
|
||||
) : undefined,
|
||||
content: <NotificationsList />,
|
||||
},
|
||||
],
|
||||
[
|
||||
canViewMlNodes,
|
||||
disableCreateAnomalyDetectionJob,
|
||||
errorsAndWarningCount,
|
||||
memoryUsageStats,
|
||||
panelsState,
|
||||
panelsExtendedState,
|
||||
setPanelsState,
|
||||
setPanelsExtendedState,
|
||||
]
|
||||
);
|
||||
|
||||
const renderTabs = () => {
|
||||
return tabs.map((tab) => (
|
||||
<EuiTab
|
||||
key={tab.id}
|
||||
onClick={() => setSelectedTabId(tab.id)}
|
||||
isSelected={tab.id === selectedTabId}
|
||||
data-test-subj={`mlManagementOverviewPageTabs ${tab.id}`}
|
||||
append={tab.append}
|
||||
>
|
||||
{tab.name}
|
||||
</EuiTab>
|
||||
));
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div data-test-subj="mlStackManagementOverviewPage">
|
||||
<MlPageHeader>
|
||||
<PageTitle
|
||||
title={i18n.translate('xpack.ml.overview.overviewLabel', {
|
||||
defaultMessage: 'Overview',
|
||||
title={i18n.translate('xpack.ml.management.machineLearningOverview.overviewLabel', {
|
||||
defaultMessage: 'Machine Learning Overview',
|
||||
})}
|
||||
/>
|
||||
</MlPageHeader>
|
||||
|
@ -82,51 +288,9 @@ export const OverviewPage: FC = () => {
|
|||
}}
|
||||
/>
|
||||
<UpgradeWarning />
|
||||
|
||||
{canViewMlNodes ? (
|
||||
<>
|
||||
<CollapsiblePanel
|
||||
isOpen={panelsState.nodes}
|
||||
onToggle={(update) => {
|
||||
setPanelsState({ ...panelsState, nodes: update });
|
||||
}}
|
||||
header={
|
||||
<FormattedMessage id="xpack.ml.overview.nodesPanel.header" defaultMessage="Nodes" />
|
||||
}
|
||||
headerItems={[
|
||||
<OverviewStatsBar
|
||||
inputStats={[
|
||||
{
|
||||
label: i18n.translate('xpack.ml.overview.nodesPanel.totalNodesLabel', {
|
||||
defaultMessage: 'Total',
|
||||
}),
|
||||
value: getMlNodesCount(),
|
||||
'data-test-subj': 'mlTotalNodesCount',
|
||||
},
|
||||
]}
|
||||
dataTestSub={'mlOverviewAnalyticsStatsBar'}
|
||||
/>,
|
||||
<EuiLink href={viewNodesLink}>
|
||||
{i18n.translate('xpack.ml.overview.nodesPanel.viewNodeLink', {
|
||||
defaultMessage: 'View nodes',
|
||||
})}
|
||||
</EuiLink>,
|
||||
]}
|
||||
ariaLabel={i18n.translate('xpack.ml.overview.nodesPanel.ariaLabel', {
|
||||
defaultMessage: 'overview panel',
|
||||
})}
|
||||
>
|
||||
<NodesList compactView />
|
||||
</CollapsiblePanel>
|
||||
<EuiSpacer size="m" />
|
||||
</>
|
||||
) : null}
|
||||
|
||||
<OverviewContent
|
||||
createAnomalyDetectionJobDisabled={disableCreateAnomalyDetectionJob}
|
||||
setAdLazyJobCount={setAdLazyJobCount}
|
||||
setDfaLazyJobCount={setDfaLazyJobCount}
|
||||
/>
|
||||
<EuiTabs>{renderTabs()}</EuiTabs>
|
||||
<EuiSpacer />
|
||||
{tabs.find((tab) => tab.id === selectedTabId)?.content}
|
||||
<HelpMenu docLink={helpLink} />
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -7,9 +7,103 @@
|
|||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import type { ChromeBreadcrumb } from '@kbn/core/public';
|
||||
import type { ChromeBreadcrumb, CoreStart } from '@kbn/core/public';
|
||||
|
||||
import type { NavigateToPath } from '../contexts/kibana';
|
||||
import { ML_PAGES } from '../../../common/constants/locator';
|
||||
|
||||
export type NavigateToApp = CoreStart['application']['navigateToApp'];
|
||||
|
||||
type ManagementBreadcrumbType = ChromeBreadcrumb & {
|
||||
appId?: string;
|
||||
path?: string;
|
||||
};
|
||||
const stackManagementBreadcrumbText = i18n.translate(
|
||||
'xpack.ml.settings.breadcrumbs.stackManagementLabel',
|
||||
{
|
||||
defaultMessage: 'Stack Management',
|
||||
}
|
||||
);
|
||||
|
||||
export const ANOMALY_DETECTION_MANAGEMENT_BREADCRUMB: ManagementBreadcrumbType = {
|
||||
text: i18n.translate('xpack.ml.anomalyDetectionManagementBreadcrumbLabel', {
|
||||
defaultMessage: 'Anomaly Detection Jobs',
|
||||
}),
|
||||
appId: 'anomaly_detection',
|
||||
path: ML_PAGES.ANOMALY_DETECTION_JOBS_MANAGE,
|
||||
deepLinkId: 'ml:anomalyDetection',
|
||||
};
|
||||
|
||||
export const CREATE_JOB_MANAGEMENT_BREADCRUMB: ManagementBreadcrumbType = {
|
||||
text: i18n.translate('xpack.ml.createJobManagementBreadcrumbLabel', {
|
||||
defaultMessage: 'Create job',
|
||||
}),
|
||||
path: ML_PAGES.ANOMALY_DETECTION_CREATE_JOB,
|
||||
appId: `anomaly_detection`,
|
||||
};
|
||||
|
||||
export const DATA_FRAME_ANALYTICS_MANAGEMENT_BREADCRUMB: ManagementBreadcrumbType = {
|
||||
text: i18n.translate('xpack.ml.dataFrameAnalyticsManagementLabel', {
|
||||
defaultMessage: 'Data Frame Analytics Jobs',
|
||||
}),
|
||||
appId: 'analytics',
|
||||
path: ML_PAGES.DATA_FRAME_ANALYTICS_JOBS_MANAGE,
|
||||
deepLinkId: 'ml:dataFrameAnalytics',
|
||||
};
|
||||
|
||||
export const TRAINED_MODELS_MANAGEMENT_BREADCRUMB: ManagementBreadcrumbType = {
|
||||
text: i18n.translate('xpack.ml.trainedModelManagementLabel', {
|
||||
defaultMessage: 'Trained Models',
|
||||
}),
|
||||
appId: 'trained_models',
|
||||
path: '',
|
||||
deepLinkId: 'management:trained_models',
|
||||
};
|
||||
|
||||
export const SUPPLIED_CONFIGURATIONS_MANAGEMENT_BREADCRUMB: ManagementBreadcrumbType = {
|
||||
text: i18n.translate('xpack.ml.suppliedConfigurationsManagementLabel', {
|
||||
defaultMessage: 'Supplied configurations',
|
||||
}),
|
||||
appId: `anomaly_detection`,
|
||||
path: 'ad_supplied_configurations',
|
||||
deepLinkId: 'ml:suppliedConfigurations',
|
||||
};
|
||||
|
||||
export const SETTINGS_MANAGEMENT_BREADCRUMB: ManagementBreadcrumbType = {
|
||||
text: i18n.translate('xpack.ml.settingsBreadcrumbLabel', {
|
||||
defaultMessage: 'Anomaly Detection Settings',
|
||||
}),
|
||||
appId: 'ad_settings',
|
||||
path: '',
|
||||
deepLinkId: 'ml:settings',
|
||||
};
|
||||
|
||||
export const FILTER_LISTS_MANAGEMENT_BREADCRUMB: ManagementBreadcrumbType = {
|
||||
text: i18n.translate('xpack.ml.settings.breadcrumbs.filterListsManagementLabel', {
|
||||
defaultMessage: 'Filter lists',
|
||||
}),
|
||||
appId: 'ad_settings',
|
||||
path: ML_PAGES.FILTER_LISTS_MANAGE,
|
||||
deepLinkId: 'ml:filterListsSettings',
|
||||
};
|
||||
|
||||
export const CALENDAR_LISTS_MANAGEMENT_BREADCRUMB: ManagementBreadcrumbType = {
|
||||
text: i18n.translate('xpack.ml.settings.breadcrumbs.calendarListManagementLabel', {
|
||||
defaultMessage: 'Calendar management',
|
||||
}),
|
||||
appId: 'ad_settings',
|
||||
path: ML_PAGES.CALENDARS_MANAGE,
|
||||
deepLinkId: 'ml:calendarSettings',
|
||||
};
|
||||
|
||||
export const CALENDAR_DST_LISTS_MANAGEMENT_BREADCRUMB: ManagementBreadcrumbType = {
|
||||
text: i18n.translate('xpack.ml.settings.breadcrumbs.calendarListManagementLabel', {
|
||||
defaultMessage: 'Calendar DST management',
|
||||
}),
|
||||
appId: 'ad_settings',
|
||||
path: ML_PAGES.CALENDARS_DST_MANAGE,
|
||||
deepLinkId: 'ml:calendarSettings',
|
||||
};
|
||||
|
||||
export const ML_BREADCRUMB: ChromeBreadcrumb = Object.freeze({
|
||||
text: i18n.translate('xpack.ml.machineLearningBreadcrumbLabel', {
|
||||
|
@ -18,46 +112,6 @@ export const ML_BREADCRUMB: ChromeBreadcrumb = Object.freeze({
|
|||
href: '/',
|
||||
});
|
||||
|
||||
export const SETTINGS_BREADCRUMB: ChromeBreadcrumb = Object.freeze({
|
||||
text: i18n.translate('xpack.ml.settingsBreadcrumbLabel', {
|
||||
defaultMessage: 'Settings',
|
||||
}),
|
||||
href: '/settings',
|
||||
deepLinkId: 'ml:settings',
|
||||
});
|
||||
|
||||
export const ANOMALY_DETECTION_BREADCRUMB: ChromeBreadcrumb = Object.freeze({
|
||||
text: i18n.translate('xpack.ml.anomalyDetectionBreadcrumbLabel', {
|
||||
defaultMessage: 'Anomaly Detection',
|
||||
}),
|
||||
href: '/jobs',
|
||||
deepLinkId: 'ml:anomalyDetection',
|
||||
});
|
||||
|
||||
export const DATA_FRAME_ANALYTICS_BREADCRUMB: ChromeBreadcrumb = Object.freeze({
|
||||
text: i18n.translate('xpack.ml.dataFrameAnalyticsLabel', {
|
||||
defaultMessage: 'Data Frame Analytics',
|
||||
}),
|
||||
href: '/data_frame_analytics',
|
||||
deepLinkId: 'ml:dataFrameAnalytics',
|
||||
});
|
||||
|
||||
export const TRAINED_MODELS: ChromeBreadcrumb = Object.freeze({
|
||||
text: i18n.translate('xpack.ml.modelManagementLabel', {
|
||||
defaultMessage: 'Model Management',
|
||||
}),
|
||||
href: '/trained_models',
|
||||
deepLinkId: 'ml:modelManagement',
|
||||
});
|
||||
|
||||
export const SUPPLIED_CONFIGURATIONS: ChromeBreadcrumb = Object.freeze({
|
||||
text: i18n.translate('xpack.ml.suppliedConfigurationsLabel', {
|
||||
defaultMessage: 'Supplied configurations',
|
||||
}),
|
||||
href: '/supplied_configurations',
|
||||
deepLinkId: 'ml:suppliedConfigurations',
|
||||
});
|
||||
|
||||
export const DATA_VISUALIZER_BREADCRUMB: ChromeBreadcrumb = Object.freeze({
|
||||
text: i18n.translate('xpack.ml.datavisualizerBreadcrumbLabel', {
|
||||
defaultMessage: 'Data Visualizer',
|
||||
|
@ -113,52 +167,38 @@ export const CHANGE_POINT_DETECTION: ChromeBreadcrumb = Object.freeze({
|
|||
deepLinkId: 'ml:changePointDetections',
|
||||
});
|
||||
|
||||
export const CREATE_JOB_BREADCRUMB: ChromeBreadcrumb = Object.freeze({
|
||||
text: i18n.translate('xpack.ml.createJobsBreadcrumbLabel', {
|
||||
defaultMessage: 'Create job',
|
||||
}),
|
||||
href: '/jobs/new_job',
|
||||
});
|
||||
|
||||
export const CALENDAR_MANAGEMENT_BREADCRUMB: ChromeBreadcrumb = Object.freeze({
|
||||
text: i18n.translate('xpack.ml.settings.breadcrumbs.calendarManagementLabel', {
|
||||
defaultMessage: 'Calendar management',
|
||||
}),
|
||||
href: '/settings/calendars_list',
|
||||
deepLinkId: 'ml:calendarSettings',
|
||||
});
|
||||
|
||||
export const CALENDAR_DST_MANAGEMENT_BREADCRUMB: ChromeBreadcrumb = Object.freeze({
|
||||
text: i18n.translate('xpack.ml.settings.breadcrumbs.calendarManagementLabel', {
|
||||
defaultMessage: 'Calendar DST management',
|
||||
}),
|
||||
href: '/settings/calendars_dst_list',
|
||||
deepLinkId: 'ml:calendarSettings',
|
||||
});
|
||||
|
||||
export const FILTER_LISTS_BREADCRUMB: ChromeBreadcrumb = Object.freeze({
|
||||
text: i18n.translate('xpack.ml.settings.breadcrumbs.filterListsLabel', {
|
||||
defaultMessage: 'Filter lists',
|
||||
}),
|
||||
href: '/settings/filter_lists',
|
||||
deepLinkId: 'ml:filterListsSettings',
|
||||
});
|
||||
|
||||
export const DATA_DRIFT_BREADCRUMB: ChromeBreadcrumb = Object.freeze({
|
||||
text: i18n.translate('xpack.ml.settings.breadcrumbs.dataComparisonLabel', {
|
||||
defaultMessage: 'Data drift',
|
||||
defaultMessage: 'Data Drift',
|
||||
}),
|
||||
href: '/data_drift_index_select',
|
||||
deepLinkId: 'ml:dataDrift',
|
||||
});
|
||||
|
||||
export const DATA_DRIFT_INDEX_SELECT_BREADCRUMB: ChromeBreadcrumb = Object.freeze({
|
||||
text: i18n.translate('xpack.ml.settings.breadcrumbs.dataComparisonLabel', {
|
||||
defaultMessage: 'Select Data View',
|
||||
}),
|
||||
href: '/data_drift_index_select',
|
||||
deepLinkId: 'ml:dataDrift',
|
||||
});
|
||||
|
||||
const managementBreadcrumbs = {
|
||||
ANOMALY_DETECTION_MANAGEMENT_BREADCRUMB,
|
||||
CALENDAR_DST_LISTS_MANAGEMENT_BREADCRUMB,
|
||||
CALENDAR_LISTS_MANAGEMENT_BREADCRUMB,
|
||||
CREATE_JOB_MANAGEMENT_BREADCRUMB,
|
||||
DATA_FRAME_ANALYTICS_MANAGEMENT_BREADCRUMB,
|
||||
FILTER_LISTS_MANAGEMENT_BREADCRUMB,
|
||||
SUPPLIED_CONFIGURATIONS_MANAGEMENT_BREADCRUMB,
|
||||
SETTINGS_MANAGEMENT_BREADCRUMB,
|
||||
TRAINED_MODELS_MANAGEMENT_BREADCRUMB,
|
||||
};
|
||||
type ManagementBreadcrumb = keyof typeof managementBreadcrumbs;
|
||||
|
||||
const breadcrumbs = {
|
||||
ML_BREADCRUMB,
|
||||
SETTINGS_BREADCRUMB,
|
||||
ANOMALY_DETECTION_BREADCRUMB,
|
||||
DATA_FRAME_ANALYTICS_BREADCRUMB,
|
||||
TRAINED_MODELS,
|
||||
DATA_DRIFT_BREADCRUMB,
|
||||
DATA_DRIFT_INDEX_SELECT_BREADCRUMB,
|
||||
DATA_VISUALIZER_BREADCRUMB,
|
||||
AIOPS_BREADCRUMB_LOG_RATE_ANALYSIS,
|
||||
AIOPS_BREADCRUMB_LOG_PATTERN_ANALYSIS,
|
||||
|
@ -166,11 +206,6 @@ const breadcrumbs = {
|
|||
LOG_RATE_ANALYSIS,
|
||||
LOG_PATTERN_ANALYSIS,
|
||||
CHANGE_POINT_DETECTION,
|
||||
CREATE_JOB_BREADCRUMB,
|
||||
CALENDAR_MANAGEMENT_BREADCRUMB,
|
||||
CALENDAR_DST_MANAGEMENT_BREADCRUMB,
|
||||
FILTER_LISTS_BREADCRUMB,
|
||||
SUPPLIED_CONFIGURATIONS,
|
||||
};
|
||||
type Breadcrumb = keyof typeof breadcrumbs;
|
||||
|
||||
|
@ -200,3 +235,36 @@ export const getBreadcrumbWithUrlForApp = (
|
|||
: {}),
|
||||
};
|
||||
};
|
||||
|
||||
export const getStackManagementBreadcrumb = (navigateToApp: NavigateToApp): ChromeBreadcrumb => {
|
||||
return {
|
||||
text: stackManagementBreadcrumbText,
|
||||
onClick: (e) => {
|
||||
e.preventDefault();
|
||||
navigateToApp('management');
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const getMlManagementBreadcrumb = (
|
||||
breadcrumbName: ManagementBreadcrumb,
|
||||
navigateToApp: NavigateToApp
|
||||
): ChromeBreadcrumb => {
|
||||
const { appId, deepLinkId, path, text } = managementBreadcrumbs[breadcrumbName];
|
||||
return {
|
||||
text,
|
||||
onClick: (e) => {
|
||||
e.preventDefault();
|
||||
navigateToApp('management', { path: `/ml/${appId}/${path}` });
|
||||
},
|
||||
...(deepLinkId ? { deepLinkId } : {}),
|
||||
};
|
||||
};
|
||||
|
||||
export const getADSettingsBreadcrumbs = (navigateToApp: NavigateToApp) => {
|
||||
return [
|
||||
getStackManagementBreadcrumb(navigateToApp),
|
||||
getMlManagementBreadcrumb('ANOMALY_DETECTION_MANAGEMENT_BREADCRUMB', navigateToApp),
|
||||
getMlManagementBreadcrumb('SETTINGS_MANAGEMENT_BREADCRUMB', navigateToApp),
|
||||
];
|
||||
};
|
||||
|
|
|
@ -56,7 +56,7 @@ export interface PageProps {
|
|||
|
||||
export interface PageDependencies {
|
||||
history: AppMountParameters['history'];
|
||||
setHeaderActionMenu: AppMountParameters['setHeaderActionMenu'];
|
||||
setHeaderActionMenu?: AppMountParameters['setHeaderActionMenu'];
|
||||
setBreadcrumbs: ChromeStart['setBreadcrumbs'];
|
||||
}
|
||||
|
||||
|
@ -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>
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue