[APM][ECO] Start tour after modal dismissed (#189504)

closes https://github.com/elastic/kibana/issues/189386

FYI: The service groups tour is still visible here because it'll be
removed on another PR.


https://github.com/user-attachments/assets/0ff85a78-1ebc-48a1-ad0c-aafa1b7766e5
This commit is contained in:
Cauê Marcondes 2024-07-31 09:36:24 +01:00 committed by GitHub
parent 1d406182e2
commit 20a9d4b045
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 45 additions and 47 deletions

View file

@ -28,9 +28,9 @@ import { usePreferredDataSourceAndBucketSize } from '../../../../hooks/use_prefe
import { useProgressiveFetcher } from '../../../../hooks/use_progressive_fetcher';
import { NoEntitiesEmptyState } from './table/no_entities_empty_state';
import { Welcome } from '../../../shared/entity_enablement/welcome_modal';
import { useServiceEcoTour } from '../../../../hooks/use_eco_tour';
import { useKibana } from '../../../../context/kibana_context/use_kibana';
import { ApmPluginStartDeps, ApmServices } from '../../../../plugin';
import { useEntityManagerEnablementContext } from '../../../../context/entity_manager_context/use_entity_manager_enablement_context';
type MainStatisticsApiResponse = APIReturnType<'GET /internal/apm/entities/services'>;
@ -149,8 +149,8 @@ export function MultiSignalInventory() {
const [searchQuery, setSearchQuery] = React.useState('');
const { services } = useKibana<ApmPluginStartDeps & ApmServices>();
const { mainStatisticsData, mainStatisticsStatus } = useServicesEntitiesMainStatisticsFetcher();
const { tourState, hideModal } = useServiceEcoTour();
const mainStatisticsFetch = useServicesEntitiesMainStatisticsFetcher();
const { tourState, updateTourState } = useEntityManagerEnablementContext();
const initialSortField = ServiceInventoryFieldName.Throughput;
@ -175,6 +175,10 @@ export function MultiSignalInventory() {
}
}, [services.telemetry, data?.hasData]);
function handleModalClose() {
updateTourState({ isModalVisible: false, isTourActive: true });
}
return (
<>
{!data?.hasData && status === FETCH_STATUS.SUCCESS ? (
@ -219,8 +223,8 @@ export function MultiSignalInventory() {
)}
<Welcome
isModalVisible={tourState.isModalVisible ?? false}
onClose={() => hideModal()}
onConfirm={() => hideModal()}
onClose={handleModalClose}
onConfirm={handleModalClose}
/>
</>
);

View file

@ -14,7 +14,6 @@ import {
import { i18n } from '@kbn/i18n';
import React, { useState } from 'react';
import { useApmPluginContext } from '../../../../context/apm_plugin/use_apm_plugin_context';
import { useServiceEcoTour } from '../../../../hooks/use_eco_tour';
import { useKibana } from '../../../../context/kibana_context/use_kibana';
import { ApmPluginStartDeps, ApmServices } from '../../../../plugin';
import { EntityInventoryAddDataParams } from '../../../../services/telemetry';
@ -24,6 +23,7 @@ import {
addApmData,
} from '../../../shared/add_data_buttons/buttons';
import { ServiceEcoTour } from '../../../shared/entity_enablement/service_eco_tour';
import { useEntityManagerEnablementContext } from '../../../../context/entity_manager_context/use_entity_manager_enablement_context';
const addData = i18n.translate('xpack.apm.addDataContextMenu.link', {
defaultMessage: 'Add data',
@ -31,7 +31,7 @@ const addData = i18n.translate('xpack.apm.addDataContextMenu.link', {
export function AddDataContextMenu() {
const [popoverOpen, setPopoverOpen] = useState(false);
const { tourState, hideTour } = useServiceEcoTour();
const { tourState, updateTourState } = useEntityManagerEnablementContext();
const { services } = useKibana<ApmPluginStartDeps & ApmServices>();
const {
core: {
@ -94,7 +94,7 @@ export function AddDataContextMenu() {
];
const handleTourClose = () => {
hideTour();
updateTourState({ isTourActive: false });
setPopoverOpen(false);
};
return (

View file

@ -28,12 +28,10 @@ import { useEntityManagerEnablementContext } from '../../../context/entity_manag
import { FeedbackModal } from './feedback_modal';
import { ServiceInventoryView } from '../../../context/entity_manager_context/entity_manager_context';
import { Unauthorized } from './unauthorized_modal';
import { useServiceEcoTour } from '../../../hooks/use_eco_tour';
export function EntityEnablement({ label, tooltip }: { label: string; tooltip?: string }) {
const [isFeedbackModalVisible, setsIsFeedbackModalVisible] = useState(false);
const [isUnauthorizedModalVisible, setsIsUnauthorizedModalVisible] = useState(false);
const { tourState, showModal } = useServiceEcoTour();
const {
services: { entityManager },
@ -46,6 +44,8 @@ export function EntityEnablement({ label, tooltip }: { label: string; tooltip?:
refetch,
setServiceInventoryViewLocalStorageSetting,
isEntityCentricExperienceViewEnabled,
tourState,
updateTourState,
} = useEntityManagerEnablementContext();
const [isPopoverOpen, togglePopover] = useToggle(false);
@ -60,7 +60,7 @@ export function EntityEnablement({ label, tooltip }: { label: string; tooltip?:
if (isEntityManagerEnabled) {
setServiceInventoryViewLocalStorageSetting(ServiceInventoryView.entity);
if (tourState.isModalVisible === undefined) {
showModal();
updateTourState({ isModalVisible: true });
}
return;
}
@ -73,7 +73,7 @@ export function EntityEnablement({ label, tooltip }: { label: string; tooltip?:
setServiceInventoryViewLocalStorageSetting(ServiceInventoryView.entity);
if (tourState.isModalVisible === undefined) {
showModal();
updateTourState({ isModalVisible: true });
}
refetch();
} else {

View file

@ -8,7 +8,7 @@
import React from 'react';
import { i18n } from '@kbn/i18n';
import { EuiText, EuiTourStep } from '@elastic/eui';
import { useServiceEcoTour } from '../../../hooks/use_eco_tour';
import { useEntityManagerEnablementContext } from '../../../context/entity_manager_context/use_entity_manager_enablement_context';
export function ServiceEcoTour({
children,
@ -17,7 +17,7 @@ export function ServiceEcoTour({
children: React.ReactElement;
onFinish: () => void;
}) {
const { tourState } = useServiceEcoTour();
const { tourState } = useEntityManagerEnablementContext();
return (
<EuiTourStep

View file

@ -24,6 +24,8 @@ export interface EntityManagerEnablementContextValue {
serviceInventoryViewLocalStorageSetting: ServiceInventoryView;
setServiceInventoryViewLocalStorageSetting: (view: ServiceInventoryView) => void;
isEntityCentricExperienceViewEnabled: boolean;
tourState: TourState;
updateTourState: (newState: Partial<TourState>) => void;
}
export enum ServiceInventoryView {
@ -35,6 +37,15 @@ export const EntityManagerEnablementContext = createContext(
{} as EntityManagerEnablementContextValue
);
interface TourState {
isModalVisible?: boolean;
isTourActive: boolean;
}
const TOUR_INITIAL_STATE: TourState = {
isModalVisible: undefined,
isTourActive: false,
};
export function EntityManagerEnablementContextProvider({
children,
}: {
@ -43,6 +54,7 @@ export function EntityManagerEnablementContextProvider({
const { core } = useApmPluginContext();
const { services } = useKibana<ApmPluginStartDeps & ApmServices>();
const { isEnabled: isEntityManagerEnabled, status, refetch } = useEntityManager();
const [tourState, setTourState] = useLocalStorage('apm.serviceEcoTour', TOUR_INITIAL_STATE);
const [serviceInventoryViewLocalStorageSetting, setServiceInventoryViewLocalStorageSetting] =
useLocalStorage(SERVICE_INVENTORY_STORAGE_KEY, ServiceInventoryView.classic);
@ -57,6 +69,19 @@ export function EntityManagerEnablementContextProvider({
serviceInventoryViewLocalStorageSetting === ServiceInventoryView.entity &&
isEntityCentricExperienceSettingEnabled;
function handleServiceInventoryViewChange(nextView: ServiceInventoryView) {
setServiceInventoryViewLocalStorageSetting(nextView);
// Updates the telemetry context variable every time the user switches views
serviceInventoryViewType$.next({ serviceInventoryViewType: nextView });
services.telemetry.reportEntityExperienceStatusChange({
status: nextView === ServiceInventoryView.entity ? 'enabled' : 'disabled',
});
}
function handleTourStateUpdate(newTourState: Partial<TourState>) {
setTourState({ ...tourState, ...newTourState });
}
return (
<EntityManagerEnablementContext.Provider
value={{
@ -65,15 +90,10 @@ export function EntityManagerEnablementContextProvider({
isEnablementPending: status === ENTITY_FETCH_STATUS.LOADING,
refetch,
serviceInventoryViewLocalStorageSetting,
setServiceInventoryViewLocalStorageSetting: (nextView) => {
setServiceInventoryViewLocalStorageSetting(nextView);
// Updates the telemetry context variable every time the user switches views
serviceInventoryViewType$.next({ serviceInventoryViewType: nextView });
services.telemetry.reportEntityExperienceStatusChange({
status: nextView === ServiceInventoryView.entity ? 'enabled' : 'disabled',
});
},
setServiceInventoryViewLocalStorageSetting: handleServiceInventoryViewChange,
isEntityCentricExperienceViewEnabled,
tourState,
updateTourState: handleTourStateUpdate,
}}
>
{children}

View file

@ -1,26 +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 { useLocalStorage } from './use_local_storage';
type TourState = 'isModalVisible' | 'isTourActive';
const INITIAL_STATE: Record<TourState, boolean | undefined> = {
isModalVisible: undefined,
isTourActive: true,
};
export function useServiceEcoTour() {
const [tourState, setTourState] = useLocalStorage('apm.serviceEcoTour', INITIAL_STATE);
return {
tourState,
hideModal: () => setTourState({ ...tourState, isModalVisible: false }),
showModal: () => setTourState({ ...tourState, isModalVisible: true }),
hideTour: () => setTourState({ ...tourState, isTourActive: false }),
};
}