[Logs UI] ML log integration splash screen (#69288)

Co-authored-by: Brandon Morelli <bmorelli25@gmail.com>
Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
Alejandro Fernández 2020-06-25 10:18:32 +02:00 committed by GitHub
parent 2b72de5231
commit f6c9ca20ed
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 232 additions and 13 deletions

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 64 KiB

View file

@ -13,3 +13,4 @@ export * from './missing_results_privileges_prompt';
export * from './missing_setup_privileges_prompt';
export * from './ml_unavailable_prompt';
export * from './setup_status_unknown_prompt';
export * from './subscription_splash_content';

View file

@ -0,0 +1,174 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import React, { useEffect } from 'react';
import { i18n } from '@kbn/i18n';
import {
EuiPage,
EuiPageBody,
EuiPageContent,
EuiFlexGroup,
EuiFlexItem,
EuiSpacer,
EuiTitle,
EuiText,
EuiButton,
EuiButtonEmpty,
EuiImage,
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { LoadingPage } from '../../loading_page';
import { useKibana } from '../../../../../../../src/plugins/kibana_react/public';
import { euiStyled } from '../../../../../observability/public';
import { useTrialStatus } from '../../../hooks/use_trial_status';
export const SubscriptionSplashContent: React.FC = () => {
const { services } = useKibana();
const { loadState, isTrialAvailable, checkTrialAvailability } = useTrialStatus();
useEffect(() => {
checkTrialAvailability();
}, [checkTrialAvailability]);
if (loadState === 'pending') {
return (
<LoadingPage
message={i18n.translate('xpack.infra.logs.logAnalysis.splash.loadingMessage', {
defaultMessage: 'Checking license...',
})}
/>
);
}
const canStartTrial = isTrialAvailable && loadState === 'resolved';
let title;
let description;
let cta;
if (canStartTrial) {
title = (
<FormattedMessage
id="xpack.infra.logs.logAnalysis.splash.startTrialTitle"
defaultMessage="To access anomaly detection, start a free trial"
/>
);
description = (
<FormattedMessage
id="xpack.infra.logs.logAnalysis.splash.startTrialDescription"
defaultMessage="Our free, 14-day trial includes machine learning features, which enable you to detect anomalies in your logs."
/>
);
cta = (
<EuiButton
fullWidth={false}
fill
href={services.http.basePath.prepend('/app/management/stack/license_management')}
>
<FormattedMessage
id="xpack.infra.logs.logAnalysis.splash.startTrialCta"
defaultMessage="Start trial"
/>
</EuiButton>
);
} else {
title = (
<FormattedMessage
id="xpack.infra.logs.logAnalysis.splash.updateSubscriptionTitle"
defaultMessage="To access anomaly detection, upgrade to a Platinum Subscription"
/>
);
description = (
<FormattedMessage
id="xpack.infra.logs.logAnalysis.splash.updateSubscriptionDescription"
defaultMessage="You must have a Platinum Subscription to use machine learning features."
/>
);
cta = (
<EuiButton fullWidth={false} fill href="https://www.elastic.co/subscriptions">
<FormattedMessage
id="xpack.infra.logs.logAnalysis.splash.updateSubscriptionCta"
defaultMessage="Upgrade subscription"
/>
</EuiButton>
);
}
return (
<SubscriptionPage>
<EuiPageBody>
<SubscriptionPageContent verticalPosition="center" horizontalPosition="center">
<EuiFlexGroup>
<EuiFlexItem>
<EuiTitle size="m">
<h2>{title}</h2>
</EuiTitle>
<EuiSpacer size="xl" />
<EuiText>
<p>{description}</p>
</EuiText>
<EuiSpacer />
<div>{cta}</div>
</EuiFlexItem>
<EuiFlexItem>
<EuiImage
alt={i18n.translate('xpack.infra.logs.logAnalysis.splash.splashImageAlt', {
defaultMessage: 'Placeholder image',
})}
url={services.http.basePath.prepend(
'/plugins/infra/assets/anomaly_chart_minified.svg'
)}
size="fullWidth"
/>
</EuiFlexItem>
</EuiFlexGroup>
<SubscriptionPageFooter>
<EuiTitle size="xs">
<h3>
<FormattedMessage
id="xpack.infra.logs.logAnalysis.splash.learnMoreTitle"
defaultMessage="Want to learn more?"
/>
</h3>
</EuiTitle>
<EuiButtonEmpty
flush="left"
iconType="training"
target="_blank"
color="text"
href="https://www.elastic.co/guide/en/kibana/master/xpack-logs-analysis.html"
>
<FormattedMessage
id="xpack.infra.logs.logAnalysis.splash.learnMoreLink"
defaultMessage="Read documentation"
/>
</EuiButtonEmpty>
</SubscriptionPageFooter>
</SubscriptionPageContent>
</EuiPageBody>
</SubscriptionPage>
);
};
const SubscriptionPage = euiStyled(EuiPage)`
height: 100%
`;
const SubscriptionPageContent = euiStyled(EuiPageContent)`
max-width: 768px !important;
`;
const SubscriptionPageFooter = euiStyled.div`
background: ${(props) => props.theme.eui.euiColorLightestShade};
margin: 0 -${(props) => props.theme.eui.paddingSizes.l} -${(props) =>
props.theme.eui.paddingSizes.l};
padding: ${(props) => props.theme.eui.paddingSizes.l};
`;

View file

@ -0,0 +1,51 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { boolean } from 'io-ts';
import { i18n } from '@kbn/i18n';
import { useState } from 'react';
import { useKibana } from '../../../../../src/plugins/kibana_react/public';
import { API_BASE_PATH as LICENSE_MANAGEMENT_API_BASE_PATH } from '../../../license_management/common/constants';
import { useTrackedPromise } from '../utils/use_tracked_promise';
import { decodeOrThrow } from '../../common/runtime_types';
interface UseTrialStatusState {
loadState: 'uninitialized' | 'pending' | 'resolved' | 'rejected';
isTrialAvailable: boolean;
checkTrialAvailability: () => void;
}
export function useTrialStatus(): UseTrialStatusState {
const { services } = useKibana();
const [isTrialAvailable, setIsTrialAvailable] = useState<boolean>(false);
const [loadState, checkTrialAvailability] = useTrackedPromise(
{
createPromise: async () => {
const response = await services.http.get(`${LICENSE_MANAGEMENT_API_BASE_PATH}/start_trial`);
return decodeOrThrow(boolean)(response);
},
onResolve: (response) => {
setIsTrialAvailable(response);
},
onReject: (error) => {
services.notifications.toasts.addDanger(
i18n.translate('xpack.infra.trialStatus.trialStatusNetworkErrorMessage', {
defaultMessage: 'We could not determine if the trial license is available',
})
);
},
},
[services]
);
return {
loadState: loadState.state,
isTrialAvailable,
checkTrialAvailability,
};
}

View file

@ -12,7 +12,7 @@ import {
LogAnalysisSetupStatusUnknownPrompt,
MissingResultsPrivilegesPrompt,
MissingSetupPrivilegesPrompt,
MlUnavailablePrompt,
SubscriptionSplashContent,
} from '../../../components/logging/log_analysis_setup';
import { SourceErrorPage } from '../../../components/source_error_page';
import { SourceLoadingPage } from '../../../components/source_loading_page';
@ -50,7 +50,7 @@ export const LogEntryCategoriesPageContent = () => {
} else if (hasFailedLoadingSource) {
return <SourceErrorPage errorMessage={loadSourceFailureMessage ?? ''} retry={loadSource} />;
} else if (!hasLogAnalysisCapabilites) {
return <MlUnavailablePrompt />;
return <SubscriptionSplashContent />;
} else if (!hasLogAnalysisReadCapabilities) {
return <MissingResultsPrivilegesPrompt />;
} else if (setupStatus.type === 'initializing') {

View file

@ -12,7 +12,7 @@ import {
LogAnalysisSetupStatusUnknownPrompt,
MissingResultsPrivilegesPrompt,
MissingSetupPrivilegesPrompt,
MlUnavailablePrompt,
SubscriptionSplashContent,
} from '../../../components/logging/log_analysis_setup';
import { SourceErrorPage } from '../../../components/source_error_page';
import { SourceLoadingPage } from '../../../components/source_loading_page';
@ -50,7 +50,7 @@ export const LogEntryRatePageContent = () => {
} else if (hasFailedLoadingSource) {
return <SourceErrorPage errorMessage={loadSourceFailureMessage ?? ''} retry={loadSource} />;
} else if (!hasLogAnalysisCapabilites) {
return <MlUnavailablePrompt />;
return <SubscriptionSplashContent />;
} else if (!hasLogAnalysisReadCapabilities) {
return <MissingResultsPrivilegesPrompt />;
} else if (setupStatus.type === 'initializing') {

View file

@ -17,7 +17,6 @@ import { HelpCenterContent } from '../../components/help_center_content';
import { AppNavigation } from '../../components/navigation/app_navigation';
import { RoutedTabs } from '../../components/navigation/routed_tabs';
import { ColumnarPage } from '../../components/page';
import { useLogAnalysisCapabilitiesContext } from '../../containers/logs/log_analysis';
import { useLogSourceContext } from '../../containers/logs/log_source';
import { RedirectWithQueryParams } from '../../utils/redirect_with_query_params';
import { LogEntryCategoriesPage } from './log_entry_categories';
@ -28,7 +27,6 @@ import { AlertDropdown } from '../../components/alerting/logs/alert_dropdown';
export const LogsPageContent: React.FunctionComponent = () => {
const uiCapabilities = useKibana().services.application?.capabilities;
const logAnalysisCapabilities = useLogAnalysisCapabilitiesContext();
const { initialize } = useLogSourceContext();
@ -79,13 +77,7 @@ export const LogsPageContent: React.FunctionComponent = () => {
<AppNavigation aria-label={pageTitle}>
<EuiFlexGroup gutterSize={'none'} alignItems={'center'}>
<EuiFlexItem>
<RoutedTabs
tabs={
logAnalysisCapabilities.hasLogAnalysisCapabilites
? [streamTab, logRateTab, logCategoriesTab, settingsTab]
: [streamTab, settingsTab]
}
/>
<RoutedTabs tabs={[streamTab, logRateTab, logCategoriesTab, settingsTab]} />
</EuiFlexItem>
<EuiFlexItem grow={false}>
<AlertDropdown />