Skip to content

Commit

Permalink
[Synthetics] Improve is allowed UX (elastic#183953)
Browse files Browse the repository at this point in the history
## Summary

Improve isAllowed UX !!

When account is blocked, we will show the callout and disable creating
monitors in cloud

you can test the PR in two ways either by returning isServiceAllowed
false manually from the code or by disabling account in synthetics
service.

### Test cases

- [ ] Account block should only impact when user is on cloud and
manifest url is set
- [ ] User shouldn't be able to create monitors in the UI 
- [ ] Call out is shown on add/edit pages as well 

<img width="1723" alt="image"
src="https://github.com/elastic/kibana/assets/3505601/5dd4b8a6-8851-40dc-beab-6f2f0e551ca2">
  • Loading branch information
shahzad31 authored May 24, 2024
1 parent 1e9a2ed commit 0cce8a8
Show file tree
Hide file tree
Showing 39 changed files with 247 additions and 239 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export const MonitorManagementEnablementResultCodec = t.type({
canManageApiKeys: t.boolean,
areApiKeysEnabled: t.boolean,
isValidApiKey: t.boolean,
isServiceAllowed: t.boolean,
});

export type MonitorManagementEnablementResult = t.TypeOf<
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import React, { ReactNode } from 'react';
import { FormattedMessage } from '@kbn/i18n-react';
import { EuiCallOut, EuiToolTip, EuiCode } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { useEnablement } from '../../../hooks';
import { SERVICE_NOT_ALLOWED } from '../../monitors_page/management/disabled_callout';

export const FleetPermissionsCallout = () => {
return (
Expand Down Expand Up @@ -37,7 +39,18 @@ export const NoPermissionsTooltip = ({
canUsePublicLocations?: boolean;
children: ReactNode;
}) => {
const { isServiceAllowed } = useEnablement();

const disabledMessage = getRestrictionReasonLabel(canEditSynthetics, canUsePublicLocations);

if (!isServiceAllowed) {
return (
<EuiToolTip content={SERVICE_NOT_ALLOWED}>
<span>{children}</span>
</EuiToolTip>
);
}

if (disabledMessage) {
return (
<EuiToolTip content={disabledMessage}>
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,14 @@ describe('GettingStartedPage', () => {
<GettingStartedPage />,
{
state: {
syntheticsEnablement: {
loading: false,
enablement: {
canEnable: true,
isEnabled: true,
isServiceAllowed: true,
},
},
serviceLocations: {
locations: [],
locationsLoaded: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ describe('SimpleMonitorForm', () => {

// calls enabled API
await waitFor(async () => {
expect(dispatchSpy).toHaveBeenCalledTimes(3);
expect(dispatchSpy).toHaveBeenCalledTimes(6);
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@ import { runOnceMonitor } from '../../../state/manual_test_runs/api';

export const RunTestButton = ({
canUsePublicLocations = true,
isServiceAllowed,
}: {
canUsePublicLocations?: boolean;
isServiceAllowed?: boolean;
}) => {
const { formState, getValues, handleSubmit } = useFormContext();

Expand Down Expand Up @@ -60,7 +62,7 @@ export const RunTestButton = ({
<EuiButton
data-test-subj="syntheticsRunTestBtn"
color="success"
disabled={isDisabled || !canUsePublicLocations}
disabled={isDisabled || !canUsePublicLocations || !isServiceAllowed}
aria-label={TEST_NOW_ARIA_LABEL}
iconType="play"
onClick={handleSubmit(handleTestNow)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { EuiButton, EuiLink, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { useFormContext } from 'react-hook-form';
import { FETCH_STATUS } from '@kbn/observability-shared-plugin/public';
import { useEnablement } from '../../../hooks';
import { RunTestButton } from './run_test_btn';
import { useCanEditSynthetics } from '../../../../../hooks/use_capabilities';
import { useMonitorSave } from '../hooks/use_monitor_save';
Expand Down Expand Up @@ -45,6 +46,8 @@ export const ActionBar = ({

const canEditSynthetics = useCanEditSynthetics();

const { isServiceAllowed } = useEnablement();

const formSubmitter = (formData: Record<string, any>) => {
if (isValid) {
setMonitorData(format(formData, readOnly));
Expand Down Expand Up @@ -82,7 +85,10 @@ export const ActionBar = ({
</EuiLink>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<RunTestButton canUsePublicLocations={canUsePublicLocations} />
<RunTestButton
canUsePublicLocations={canUsePublicLocations}
isServiceAllowed={isServiceAllowed}
/>
</EuiFlexItem>
<EuiFlexItem grow={false} css={{ marginLeft: 'auto' }}>
<NoPermissionsTooltip
Expand All @@ -94,7 +100,7 @@ export const ActionBar = ({
isLoading={loading}
onClick={handleSubmit(formSubmitter)}
data-test-subj="syntheticsMonitorConfigSubmitButton"
disabled={!canEditSynthetics || !canUsePublicLocations}
disabled={!canEditSynthetics || !canUsePublicLocations || !isServiceAllowed}
>
{isEdit ? UPDATE_MONITOR_LABEL : CREATE_MONITOR_LABEL}
</EuiButton>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { useTrackPageview } from '@kbn/observability-shared-plugin/public';

import { useCanUsePublicLocations } from '../../../../hooks/use_capabilities';
import { CanUsePublicLocationsCallout } from './steps/can_use_public_locations_callout';
import { DisabledCallout } from '../monitors_page/management/disabled_callout';
import { useEnablement } from '../../hooks';
import { getServiceLocations, selectServiceLocationsState } from '../../state';
import { ServiceAllowedWrapper } from '../common/wrappers/service_allowed_wrapper';

import { useKibanaSpace } from './hooks';
import { MonitorSteps } from './steps';
Expand All @@ -29,6 +31,8 @@ export const MonitorAddPage = () => {

useEnablement();

const canUsePublicLocations = useCanUsePublicLocations();

const dispatch = useDispatch();
useEffect(() => {
dispatch(getServiceLocations());
Expand All @@ -39,17 +43,15 @@ export const MonitorAddPage = () => {
return <LocationsLoadingError />;
}

return locationsLoaded ? (
if (!locationsLoaded) {
return <LoadingState />;
}

return (
<MonitorForm space={space?.id}>
<DisabledCallout />
<CanUsePublicLocationsCallout canUsePublicLocations={canUsePublicLocations} />
<MonitorSteps stepMap={ADD_MONITOR_STEPS} />
</MonitorForm>
) : (
<LoadingState />
);
};

export const MonitorAddPageWithServiceAllowed = React.memo(() => (
<ServiceAllowedWrapper>
<MonitorAddPage />
</ServiceAllowedWrapper>
));
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,13 @@ import { EuiEmptyPrompt } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { useTrackPageview, useFetcher } from '@kbn/observability-shared-plugin/public';
import { IHttpFetchError, ResponseErrorBody } from '@kbn/core-http-browser';
import { CanUsePublicLocationsCallout } from './steps/can_use_public_locations_callout';
import { DisabledCallout } from '../monitors_page/management/disabled_callout';
import { useCanUsePublicLocations } from '../../../../hooks/use_capabilities';
import { EditMonitorNotFound } from './edit_monitor_not_found';
import { LoadingState } from '../monitors_page/overview/overview/monitor_detail_flyout';
import { ConfigKey, SourceType } from '../../../../../common/runtime_types';
import { getServiceLocations, selectServiceLocationsState } from '../../state';
import { ServiceAllowedWrapper } from '../common/wrappers/service_allowed_wrapper';
import { AlertingCallout } from '../common/alerting_callout/alerting_callout';
import { MonitorSteps } from './steps';
import { MonitorForm } from './form';
Expand Down Expand Up @@ -91,14 +92,15 @@ export const MonitorEditPage: React.FC = () => {

return data && locationsLoaded && !loading && !error ? (
<>
<DisabledCallout />
<CanUsePublicLocationsCallout canUsePublicLocations={canUsePublicLocations} />
<AlertingCallout isAlertingEnabled={data[ConfigKey.ALERT_CONFIG]?.status?.enabled} />
<MonitorForm
defaultValues={data}
readOnly={isReadOnly}
canUsePublicLocations={canUsePublicLocations}
>
<MonitorSteps
canUsePublicLocations={canUsePublicLocations}
stepMap={EDIT_MONITOR_STEPS(isReadOnly)}
isEditFlow={true}
readOnly={isReadOnly}
Expand All @@ -115,9 +117,3 @@ export const MonitorEditPage: React.FC = () => {
<LoadingState />
);
};

export const MonitorEditPageWithServiceAllowed = React.memo(() => (
<ServiceAllowedWrapper>
<MonitorEditPage />
</ServiceAllowedWrapper>
));
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* 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 { EuiCallOut, EuiSpacer } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n-react';

export const CanUsePublicLocationsCallout = ({
canUsePublicLocations,
}: {
canUsePublicLocations?: boolean;
}) => {
if (!canUsePublicLocations) {
return (
<>
<EuiCallOut
color="warning"
title={
<FormattedMessage
id="xpack.synthetics.publicLocations.readOnly.callout.title"
defaultMessage="You do not have permission to use Elastic managed locations"
/>
}
iconType="alert"
>
<p>
<FormattedMessage
id="xpack.synthetics.publicLocations.readOnly.callout.content"
defaultMessage="This monitor contains a Elastic managed location. To edit this monitor, you need to have permission to use Elastic managed locations."
/>
</p>
</EuiCallOut>
<EuiSpacer size="m" />
</>
);
}

return null;
};
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,13 @@ import { MonitorTypePortal } from './monitor_type_portal';
import { ReadOnlyCallout } from './read_only_callout';

export const MonitorSteps = ({
canUsePublicLocations,
stepMap,
projectId,
isEditFlow = false,
readOnly = false,
}: {
stepMap: StepMap;
readOnly?: boolean;
canUsePublicLocations?: boolean;
isEditFlow?: boolean;
projectId?: string;
}) => {
Expand All @@ -34,9 +32,7 @@ export const MonitorSteps = ({

return (
<>
{isEditFlow && (
<ReadOnlyCallout projectId={projectId} canUsePublicLocations={canUsePublicLocations} />
)}
{isEditFlow && <ReadOnlyCallout projectId={projectId} />}
{isEditFlow ? (
steps.map((step) => (
<div key={step.title}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,7 @@ import React from 'react';
import { EuiCallOut, EuiSpacer } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n-react';

export const ReadOnlyCallout = ({
projectId,
canUsePublicLocations,
}: {
projectId?: string;
canUsePublicLocations?: boolean;
}) => {
export const ReadOnlyCallout = ({ projectId }: { projectId?: string }) => {
if (projectId) {
return (
<>
Expand All @@ -40,30 +34,5 @@ export const ReadOnlyCallout = ({
);
}

if (!canUsePublicLocations) {
return (
<>
<EuiCallOut
color="warning"
title={
<FormattedMessage
id="xpack.synthetics.publicLocations.readOnly.callout.title"
defaultMessage="You do not have permission to use Elastic managed locations"
/>
}
iconType="alert"
>
<p>
<FormattedMessage
id="xpack.synthetics.publicLocations.readOnly.callout.content"
defaultMessage="This monitor contains a Elastic managed location. To edit this monitor, you need to have permission to use Elastic managed locations."
/>
</p>
</EuiCallOut>
<EuiSpacer size="m" />
</>
);
}

return null;
};
Loading

0 comments on commit 0cce8a8

Please sign in to comment.