From ef62336ae6fa39fc2c5d356accb0bb945f61b90e Mon Sep 17 00:00:00 2001 From: Kyrylo Shmidt Date: Sun, 8 Sep 2024 06:28:44 +0200 Subject: [PATCH 1/3] Introduce assets store --- .github/workflows/build.yml | 2 +- src/actions.ts | 1 - .../Assets/AssetList/AssetList.stories.tsx | 2 - src/components/Assets/AssetList/index.tsx | 90 +++++---- src/components/Assets/AssetList/types.ts | 5 - .../AssetTypeList/AssetTypeList.stories.tsx | 4 +- src/components/Assets/AssetTypeList/index.tsx | 80 ++++---- src/components/Assets/AssetTypeList/types.ts | 5 - src/components/Assets/AssetsFilter/index.tsx | 190 +++++++++--------- src/components/Assets/AssetsFilter/types.ts | 18 +- .../AssetsViewScopeConfiguration.stories.tsx | 22 +- .../AssetsViewScopeConfiguration/index.tsx | 35 ++-- .../AssetsViewScopeConfiguration/types.ts | 11 +- src/components/Assets/index.tsx | 74 ++----- src/components/Assets/utils.tsx | 13 +- .../Report/Cards/DiscoveredAssets/index.tsx | 2 +- .../Report/Cards/DiscoveredIssues/index.tsx | 2 +- .../Report/ReportHeader/Ribbon/index.tsx | 10 +- src/components/Errors/ErrorDetails/styles.ts | 9 +- .../AffectedEndpointsSelector/index.tsx | 4 +- .../common/IssueCompactCard/index.tsx | 2 +- .../Insights/Issues/IssuesFilter/index.tsx | 2 +- src/components/Main/index.tsx | 13 +- src/components/Main/tracking.ts | 5 +- src/components/Navigation/KebabMenu/index.tsx | 2 + src/components/Navigation/ScopeBar/index.tsx | 10 + src/components/Navigation/SpanInfo/index.tsx | 5 + src/components/common/App/index.tsx | 22 +- src/store/assets/assetsSlice.ts | 80 ++++++++ src/store/assets/useAssetsSelector.ts | 3 + src/store/useStore.ts | 3 +- 31 files changed, 394 insertions(+), 332 deletions(-) create mode 100644 src/store/assets/assetsSlice.ts create mode 100644 src/store/assets/useAssetsSelector.ts diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7850d719c..8bcdf7d9b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -3,7 +3,7 @@ name: Lint & build on: push: branches: ["main"] - # Github Actions do not support YAML anchors yet,so we have to repeat + # Github Actions don't support YAML anchors yet, so we have to repeat # the paths-ignore in both push and pull_request events. # More info: https://github.com/actions/runner/issues/1182 paths-ignore: diff --git a/src/actions.ts b/src/actions.ts index 594902b55..d2539944f 100644 --- a/src/actions.ts +++ b/src/actions.ts @@ -20,7 +20,6 @@ export const actions = addPrefix(ACTION_PREFIX, { OPEN_DOCUMENTATION: "OPEN_DOCUMENTATION", SET_DIGMA_API_URL: "SET_DIGMA_API_URL", SET_USER_REGISTRATION_EMAIL: "SET_USER_REGISTRATION_EMAIL", - SET_ENVIRONMENT: "SET_ENVIRONMENT", SET_IS_OBSERVABILITY_ENABLED: "SET_IS_OBSERVABILITY_ENABLED", SET_OBSERVABILITY: "SET_OBSERVABILITY", GET_BACKEND_INFO: "GET_BACKEND_INFO", diff --git a/src/components/Assets/AssetList/AssetList.stories.tsx b/src/components/Assets/AssetList/AssetList.stories.tsx index f376e16ad..9b1303009 100644 --- a/src/components/Assets/AssetList/AssetList.stories.tsx +++ b/src/components/Assets/AssetList/AssetList.stories.tsx @@ -38,7 +38,6 @@ const mockedConfig: ConfigContextData = { export const Default: Story = { args: { - searchQuery: "", setRefresher: () => { return undefined; }, @@ -64,7 +63,6 @@ export const WithPerformanceImpact: Story = { ) ], args: { - searchQuery: "", setRefresher: () => { return undefined; }, diff --git a/src/components/Assets/AssetList/index.tsx b/src/components/Assets/AssetList/index.tsx index 5cf7c3379..98667a344 100644 --- a/src/components/Assets/AssetList/index.tsx +++ b/src/components/Assets/AssetList/index.tsx @@ -3,7 +3,9 @@ import { DefaultTheme, useTheme } from "styled-components"; import { DigmaMessageError } from "../../../api/types"; import { dispatcher } from "../../../dispatcher"; import { usePrevious } from "../../../hooks/usePrevious"; +import { useAssetsSelector } from "../../../store/assets/useAssetsSelector"; import { useConfigSelector } from "../../../store/config/useConfigSelector"; +import { useStore } from "../../../store/useStore"; import { isEnvironment } from "../../../typeGuards/isEnvironment"; import { changeScope } from "../../../utils/actions/changeScope"; import { SCOPE_CHANGE_EVENTS } from "../../Main/types"; @@ -18,6 +20,7 @@ import { ChevronIcon } from "../../common/icons/ChevronIcon"; import { SortIcon } from "../../common/icons/SortIcon"; import { Direction } from "../../common/icons/types"; import { AssetFilterQuery } from "../AssetsFilter/types"; +import { ViewMode } from "../AssetsViewScopeConfiguration/types"; import { actions } from "../actions"; import { checkIfAnyFiltersApplied, getAssetTypeInfo } from "../utils"; import { AssetEntry as AssetEntryComponent } from "./AssetEntry"; @@ -90,9 +93,9 @@ const getData = ( page: number, sorting: Sorting, searchQuery: string, - filters: AssetFilterQuery = { services: [], operations: [], insights: [] }, - isDirect?: boolean, - scopedSpanCodeObjectId?: string + filters: AssetFilterQuery, + viewMode: ViewMode, + scopeSpanCodeObjectId?: string ) => { window.sendMessageToDigma({ action: actions.GET_DATA, @@ -103,36 +106,40 @@ const getData = ( pageSize: PAGE_SIZE, sortBy: sorting.criterion, sortOrder: sorting.order, - directOnly: isDirect, - scopedSpanCodeObjectId, + directOnly: viewMode === "children", + scopedSpanCodeObjectId: scopeSpanCodeObjectId, ...(searchQuery.length > 0 ? { displayName: searchQuery } : {}), - ...(scopedSpanCodeObjectId ? { ...filters, services: [] } : filters) + ...(scopeSpanCodeObjectId ? { ...filters, services: [] } : filters) } } }); }; export const AssetList = ({ - searchQuery, assetTypeId, - filters, - scopeViewOptions, onAssetCountChange, setRefresher, onGoToAllAssets }: AssetListProps) => { - const [data, setData] = useState(); + const { + assets: data, + sorting, + page, + viewMode, + search, + filters + } = useAssetsSelector(); + const { + setAssets: setData, + setSorting, + setAssetsPage: setPage + } = useStore.getState(); const previousData = usePrevious(data); const [lastSetDataTimeStamp, setLastSetDataTimeStamp] = useState(); const previousLastSetDataTimeStamp = usePrevious(lastSetDataTimeStamp); - const [sorting, setSorting] = useState({ - criterion: SORTING_CRITERION.CRITICAL_INSIGHTS, - order: SORTING_ORDER.DESC - }); const [isSortingMenuOpen, setIsSortingMenuOpen] = useState(false); const theme = useTheme(); const sortingMenuChevronColor = getSortingMenuChevronColor(theme); - const [page, setPage] = useState(0); const filteredCount = data?.filteredCount ?? 0; const pageStartItemNumber = page * PAGE_SIZE + 1; const pageEndItemNumber = Math.min( @@ -140,29 +147,32 @@ export const AssetList = ({ filteredCount ); const listRef = useRef(null); - const { environment, backendInfo, scope } = useConfigSelector(); const refreshTimerId = useRef(); + const { environment, backendInfo, scope } = useConfigSelector(); const previousEnvironment = usePrevious(environment); - const previousViewScope = usePrevious(scopeViewOptions); - const isServicesFilterEnabled = !scope?.span?.spanCodeObjectId; + const previousViewMode = usePrevious(viewMode); + const scopeSpanCodeObjectId = scope?.span?.spanCodeObjectId; + const previousScopeSpanCodeObjectId = usePrevious(scopeSpanCodeObjectId); + const isServicesFilterEnabled = !scopeSpanCodeObjectId; + const isInitialLoading = !data; const refreshData = useCallback(() => { getData( assetTypeId, page, sorting, - searchQuery, + search, filters, - scopeViewOptions?.isDirect, - scopeViewOptions?.scopedSpanCodeObjectId + viewMode, + scopeSpanCodeObjectId ); }, [ page, assetTypeId, filters, - scopeViewOptions?.isDirect, - scopeViewOptions?.scopedSpanCodeObjectId, - searchQuery, + viewMode, + scopeSpanCodeObjectId, + search, sorting ]); @@ -179,12 +189,10 @@ export const AssetList = ({ const areAnyFiltersApplied = checkIfAnyFiltersApplied( filters, - searchQuery, + search, isServicesFilterEnabled ); - const isInitialLoading = !data; - useEffect(() => { refreshData(); }, [refreshData]); @@ -207,7 +215,7 @@ export const AssetList = ({ dispatcher.removeActionListener(actions.SET_DATA, handleAssetsData); window.clearTimeout(refreshTimerId.current); }; - }, []); + }, [setData]); useEffect(() => { if (data && previousData?.filteredCount !== data?.filteredCount) { @@ -223,15 +231,18 @@ export const AssetList = ({ if ( (isEnvironment(previousEnvironment) && previousEnvironment.id !== environment?.id) || - previousViewScope !== scopeViewOptions + viewMode !== previousViewMode || + previousScopeSpanCodeObjectId !== scopeSpanCodeObjectId ) { refreshData(); } }, [ environment?.id, previousEnvironment, - previousViewScope, - scopeViewOptions, + previousViewMode, + viewMode, + scopeSpanCodeObjectId, + previousScopeSpanCodeObjectId, refreshData ]); @@ -254,21 +265,30 @@ export const AssetList = ({ order: SORTING_ORDER.DESC }); } - }, [isImpactHidden, sorting]); + }, [isImpactHidden, sorting, setSorting]); useEffect(() => { setPage(0); - }, [environment?.id, searchQuery, sorting, assetTypeId, scopeViewOptions]); + }, [ + environment?.id, + search, + sorting, + assetTypeId, + viewMode, + scopeSpanCodeObjectId, + setPage + ]); useEffect(() => { listRef.current?.scrollTo(0, 0); }, [ environment?.id, - searchQuery, + search, sorting, page, assetTypeId, - scopeViewOptions + viewMode, + scopeSpanCodeObjectId ]); const handleAllAssetsLinkClick = () => { diff --git a/src/components/Assets/AssetList/types.ts b/src/components/Assets/AssetList/types.ts index 5adc2d363..5e8918742 100644 --- a/src/components/Assets/AssetList/types.ts +++ b/src/components/Assets/AssetList/types.ts @@ -1,13 +1,8 @@ import { Duration } from "../../../globals"; -import { AssetFilterQuery } from "../AssetsFilter/types"; -import { AssetScopeOption } from "../AssetsViewScopeConfiguration/types"; export interface AssetListProps { onGoToAllAssets: () => void; assetTypeId: string; - filters?: AssetFilterQuery; - searchQuery: string; - scopeViewOptions: AssetScopeOption | null; setRefresher: (refresher: () => void) => void; onAssetCountChange: (count: number) => void; } diff --git a/src/components/Assets/AssetTypeList/AssetTypeList.stories.tsx b/src/components/Assets/AssetTypeList/AssetTypeList.stories.tsx index 57266a477..064bd767c 100644 --- a/src/components/Assets/AssetTypeList/AssetTypeList.stories.tsx +++ b/src/components/Assets/AssetTypeList/AssetTypeList.stories.tsx @@ -22,8 +22,7 @@ export const Default: Story = { args: { setRefresher: () => { return undefined; - }, - searchQuery: "" + } }, play: () => { window.setTimeout(() => { @@ -65,7 +64,6 @@ export const Default: Story = { export const Empty: Story = { args: { - searchQuery: "", setRefresher: () => { return undefined; } diff --git a/src/components/Assets/AssetTypeList/index.tsx b/src/components/Assets/AssetTypeList/index.tsx index 902153e21..ca931776b 100644 --- a/src/components/Assets/AssetTypeList/index.tsx +++ b/src/components/Assets/AssetTypeList/index.tsx @@ -2,11 +2,14 @@ import { useCallback, useEffect, useRef, useState } from "react"; import { DigmaMessageError } from "../../../api/types"; import { dispatcher } from "../../../dispatcher"; import { usePrevious } from "../../../hooks/usePrevious"; +import { useAssetsSelector } from "../../../store/assets/useAssetsSelector"; import { useConfigSelector } from "../../../store/config/useConfigSelector"; +import { useStore } from "../../../store/useStore"; import { isEnvironment } from "../../../typeGuards/isEnvironment"; import { isNull } from "../../../typeGuards/isNull"; import { isString } from "../../../typeGuards/isString"; import { AssetFilterQuery } from "../AssetsFilter/types"; +import { ViewMode } from "../AssetsViewScopeConfiguration/types"; import { NoDataMessage } from "../NoDataMessage"; import { actions } from "../actions"; import { checkIfAnyFiltersApplied, getAssetTypeInfo } from "../utils"; @@ -31,18 +34,18 @@ export const ASSET_TYPE_IDS = [ ]; const getData = ( - filters: AssetFilterQuery = { services: [], operations: [], insights: [] }, + filters: AssetFilterQuery, searchQuery: string, - isDirect?: boolean, - scopedSpanCodeObjectId?: string + viewMode: ViewMode, + scopeSpanCodeObjectId?: string ) => { window.sendMessageToDigma({ action: actions.GET_CATEGORIES_DATA, payload: { query: { - directOnly: isDirect, - scopedSpanCodeObjectId, - ...(scopedSpanCodeObjectId + directOnly: viewMode === "children", + scopedSpanCodeObjectId: scopeSpanCodeObjectId, + ...(scopeSpanCodeObjectId ? { ...filters, services: [] @@ -54,44 +57,37 @@ const getData = ( }); }; -const getAssetCount = (assetCategoriesData: AssetCategoriesData) => - assetCategoriesData.assetCategories.reduce((acc, cur) => acc + cur.count, 0); +const getAssetCount = (data: AssetCategoryData[]) => + data.reduce((acc, cur) => acc + cur.count, 0); export const AssetTypeList = ({ - filters, - searchQuery, - scopeViewOptions, setRefresher, onAssetCountChange, onAssetTypeSelect }: AssetTypeListProps) => { - const [data, setData] = useState(); + const { + search, + viewMode, + filters, + assetCategories: data + } = useAssetsSelector(); + const { setAssetCategories: setData } = useStore.getState(); + const previousSearch = usePrevious(search); + const previousViewMode = usePrevious(viewMode); const previousData = usePrevious(data); const [lastSetDataTimeStamp, setLastSetDataTimeStamp] = useState(); const previousLastSetDataTimeStamp = usePrevious(lastSetDataTimeStamp); const { scope, environment } = useConfigSelector(); + const scopeSpanCodeObjectId = scope?.span?.spanCodeObjectId; + const previousScopeSpanCodeObjectId = usePrevious(scopeSpanCodeObjectId); const previousEnvironment = usePrevious(environment); const refreshTimerId = useRef(); - const previousSearchQuery = usePrevious(searchQuery); - const previousViewScope = usePrevious(scopeViewOptions); const isServicesFilterEnabled = !scope?.span?.spanCodeObjectId; - const isInitialLoading = !data; const refreshData = useCallback( - () => - getData( - filters, - searchQuery, - scopeViewOptions?.isDirect, - scopeViewOptions?.scopedSpanCodeObjectId - ), - [ - filters, - scopeViewOptions?.isDirect, - scopeViewOptions?.scopedSpanCodeObjectId, - searchQuery - ] + () => getData(filters, search, viewMode, scopeSpanCodeObjectId), + [filters, scopeSpanCodeObjectId, viewMode, search] ); useEffect(() => { @@ -100,7 +96,7 @@ export const AssetTypeList = ({ const areAnyFiltersApplied = checkIfAnyFiltersApplied( filters, - searchQuery, + search, isServicesFilterEnabled ); @@ -114,8 +110,9 @@ export const AssetTypeList = ({ timeStamp: number, error: DigmaMessageError | undefined ) => { + const assetCategoriesData = data as AssetCategoriesData; if (!error) { - setData(data as AssetCategoriesData); + setData(assetCategoriesData.assetCategories); } setLastSetDataTimeStamp(timeStamp); }; @@ -132,7 +129,7 @@ export const AssetTypeList = ({ ); window.clearTimeout(refreshTimerId.current); }; - }, []); + }, [setData]); useEffect(() => { if (data && previousData !== data) { @@ -144,18 +141,21 @@ export const AssetTypeList = ({ if ( (isEnvironment(previousEnvironment) && previousEnvironment.id !== environment?.id) || - (isString(previousSearchQuery) && previousSearchQuery !== searchQuery) || - previousViewScope !== scopeViewOptions + (isString(previousSearch) && previousSearch !== search) || + previousViewMode !== viewMode || + previousScopeSpanCodeObjectId !== scopeSpanCodeObjectId ) { refreshData(); } }, [ environment?.id, previousEnvironment, - previousSearchQuery, - previousViewScope, - scopeViewOptions, - searchQuery, + search, + previousSearch, + previousScopeSpanCodeObjectId, + scopeSpanCodeObjectId, + viewMode, + previousViewMode, refreshData ]); @@ -176,7 +176,7 @@ export const AssetTypeList = ({ return ; } - if (data?.assetCategories.every((x) => x.count === 0)) { + if (data?.every((x) => x.count === 0)) { if (areAnyFiltersApplied) { return ; } @@ -189,9 +189,7 @@ export const AssetTypeList = ({ } const assetTypeListItems = ASSET_TYPE_IDS.map((assetTypeId) => { - const assetTypeData = data?.assetCategories.find( - (x) => x.name === assetTypeId - ); + const assetTypeData = data?.find((x) => x.name === assetTypeId); const assetTypeInfo = getAssetTypeInfo(assetTypeId); if (assetTypeData && assetTypeInfo) { diff --git a/src/components/Assets/AssetTypeList/types.ts b/src/components/Assets/AssetTypeList/types.ts index cee38169c..b6b4ee112 100644 --- a/src/components/Assets/AssetTypeList/types.ts +++ b/src/components/Assets/AssetTypeList/types.ts @@ -1,13 +1,8 @@ import { MemoExoticComponent } from "react"; import { IconProps } from "../../common/icons/types"; -import { AssetFilterQuery } from "../AssetsFilter/types"; -import { AssetScopeOption } from "../AssetsViewScopeConfiguration/types"; export interface AssetTypeListProps { onAssetTypeSelect: (assetTypeId: string) => void; - filters?: AssetFilterQuery; - searchQuery: string; - scopeViewOptions: AssetScopeOption | null; setRefresher: (refresher: () => void) => void; onAssetCountChange: (count: number) => void; } diff --git a/src/components/Assets/AssetsFilter/index.tsx b/src/components/Assets/AssetsFilter/index.tsx index c234267d3..2fac6be8c 100644 --- a/src/components/Assets/AssetsFilter/index.tsx +++ b/src/components/Assets/AssetsFilter/index.tsx @@ -1,13 +1,15 @@ -import { ComponentType, useEffect, useState } from "react"; +import { ComponentType, useEffect, useMemo, useState } from "react"; import { dispatcher } from "../../../dispatcher"; +import { getFeatureFlagValue } from "../../../featureFlags"; import { usePersistence } from "../../../hooks/usePersistence"; import { usePrevious } from "../../../hooks/usePrevious"; +import { useAssetsSelector } from "../../../store/assets/useAssetsSelector"; import { useConfigSelector } from "../../../store/config/useConfigSelector"; import { useStore } from "../../../store/useStore"; import { isEnvironment } from "../../../typeGuards/isEnvironment"; import { isNull } from "../../../typeGuards/isNull"; import { isUndefined } from "../../../typeGuards/isUndefined"; -import { InsightType } from "../../../types"; +import { FeatureFlag, InsightType } from "../../../types"; import { sendTrackingEvent } from "../../../utils/actions/sendTrackingEvent"; import { sendUserActionTrackingEvent } from "../../../utils/actions/sendUserActionTrackingEvent"; import { getInsightTypeInfo } from "../../../utils/getInsightTypeInfo"; @@ -16,15 +18,14 @@ import { WrenchIcon } from "../../common/icons/12px/WrenchIcon"; import { EndpointIcon } from "../../common/icons/EndpointIcon"; import { SparkleIcon } from "../../common/icons/SparkleIcon"; import { IconProps } from "../../common/icons/types"; -import { AssetScopeOption } from "../AssetsViewScopeConfiguration/types"; import { actions } from "../actions"; import { trackingEvents } from "../tracking"; import * as s from "./styles"; import { AssetFilterCategory, AssetFilterQuery, - AssetsFilterProps, AssetsFiltersData, + GetAssetFiltersDataParams, GetAssetFiltersDataPayload } from "./types"; @@ -34,24 +35,19 @@ const getData = ({ services, operations, insights, - assetScopeOption, + viewMode, + scopeSpanCodeObjectId, searchQuery -}: { - services: string[]; - operations: string[]; - insights: InsightType[]; - assetScopeOption: AssetScopeOption | null; - searchQuery: string; -}) => { +}: GetAssetFiltersDataParams) => { window.sendMessageToDigma({ action: actions.GET_ASSET_FILTERS_DATA, payload: { query: { - services, + services: scopeSpanCodeObjectId ? services : [], operations, insights, - directOnly: Boolean(assetScopeOption?.isDirect), - scopedSpanCodeObjectId: assetScopeOption?.scopedSpanCodeObjectId, + directOnly: viewMode === "children", + scopedSpanCodeObjectId: scopeSpanCodeObjectId, ...(searchQuery?.length > 0 ? { displayName: searchQuery } : {}) } } @@ -88,23 +84,22 @@ const renderFilterCategory = ( ); }; -export const AssetsFilter = ({ - onApply, - filters, - assetScopeOption, - searchQuery -}: AssetsFilterProps) => { - const [data, setData] = useState<{ data: AssetsFiltersData | null }>(); +export const AssetsFilter = () => { + const [data, setData] = useState(); const previousData = usePrevious(data); + const { filters, search: searchQuery, viewMode } = useAssetsSelector(); + const { + setAssetsFilters: setFilters, + setSelectedServices: setGloballySelectedServices + } = useStore.getState(); const [isOpen, setIsOpen] = useState(false); const previousIsOpen = usePrevious(isOpen); const { selectedServices: globallySelectedServices, environment, - scope + scope, + backendInfo } = useConfigSelector(); - const { setSelectedServices: setGloballySelectedServices } = - useStore.getState(); const [persistedFilters, setPersistedFilters] = usePersistence(PERSISTENCE_KEY, "project"); const previousPersistedFilters = usePrevious(persistedFilters); @@ -118,6 +113,34 @@ export const AssetsFilter = ({ const [selectedInsights, setSelectedInsights] = useState([]); const previousEnvironment = usePrevious(environment); const previousScope = usePrevious(scope); + const scopeSpanCodeObjectId = scope?.span?.spanCodeObjectId; + const areExtendedAssetsFiltersEnabled = getFeatureFlagValue( + backendInfo, + FeatureFlag.ARE_EXTENDED_ASSETS_FILTERS_ENABLED + ); + + const query = useMemo( + () => ({ + services: isServicesFilterEnabled ? selectedServices : [], + operations: filters.operations, + insights: filters.insights as InsightType[], + viewMode: areExtendedAssetsFiltersEnabled ? viewMode : undefined, + scopeSpanCodeObjectId: areExtendedAssetsFiltersEnabled + ? scopeSpanCodeObjectId + : undefined, + searchQuery: areExtendedAssetsFiltersEnabled ? searchQuery : "" + }), + [ + isServicesFilterEnabled, + selectedServices, + filters.operations, + filters.insights, + viewMode, + searchQuery, + scopeSpanCodeObjectId, + areExtendedAssetsFiltersEnabled + ] + ); // Get data after filters have been rehydrated useEffect(() => { @@ -126,6 +149,7 @@ export const AssetsFilter = ({ !isUndefined(persistedFilters) ) { getData({ + ...query, services: isServicesFilterEnabled ? selectedServices : [], operations: persistedFilters?.operations ?? [ ...selectedEndpoints, @@ -133,9 +157,7 @@ export const AssetsFilter = ({ ...selectedInternals ], insights: - (persistedFilters?.insights as InsightType[]) || selectedInsights, - assetScopeOption, - searchQuery + (persistedFilters?.insights as InsightType[]) || selectedInsights }); } }, [ @@ -146,17 +168,15 @@ export const AssetsFilter = ({ selectedConsumers, selectedInternals, selectedInsights, - scope, - assetScopeOption, - searchQuery, - globallySelectedServices, - isServicesFilterEnabled + isServicesFilterEnabled, + query ]); + // Handle filters data response useEffect(() => { const handleData = (data: unknown) => { const filtersData = data as AssetsFiltersData | null; - setData({ data: filtersData }); + setData(filtersData); }; dispatcher.addActionListener(actions.SET_ASSET_FILTERS_DATA, handleData); @@ -180,31 +200,33 @@ export const AssetsFilter = ({ operations: [], insights: [] }; + setFilters(defaultFilters); setPersistedFilters(defaultFilters); if (isServicesFilterEnabled) { setGloballySelectedServices(defaultFilters.services); } - onApply(defaultFilters); getData({ - ...defaultFilters, - assetScopeOption, - searchQuery + ...query, + ...defaultFilters }); } }, [ + setFilters, previousEnvironment, environment, setPersistedFilters, - onApply, - assetScopeOption, - searchQuery, isServicesFilterEnabled, - setGloballySelectedServices + setGloballySelectedServices, + areExtendedAssetsFiltersEnabled, + query ]); // Clear filters and get data when scope is changed, but keep selected services useEffect(() => { - if (previousScope && previousScope !== scope) { + if ( + previousScope && + previousScope.span?.spanCodeObjectId !== scopeSpanCodeObjectId + ) { const newFilters = { services: selectedServices, operations: [], @@ -214,68 +236,44 @@ export const AssetsFilter = ({ if (isServicesFilterEnabled) { setGloballySelectedServices(newFilters.services); } - onApply(newFilters); + setFilters(newFilters); getData({ - ...newFilters, - assetScopeOption, - searchQuery + ...query, + ...newFilters }); } }, [ + setFilters, setPersistedFilters, setGloballySelectedServices, - onApply, previousScope, - scope, - assetScopeOption, - searchQuery, + scopeSpanCodeObjectId, selectedServices, - isServicesFilterEnabled + isServicesFilterEnabled, + areExtendedAssetsFiltersEnabled, + query ]); // Get data when the popover is opened useEffect(() => { if (isOpen && !previousIsOpen) { - getData({ - services: isServicesFilterEnabled ? selectedServices : [], - operations: [ - ...selectedEndpoints, - ...selectedConsumers, - ...selectedInternals - ], - insights: selectedInsights, - assetScopeOption, - searchQuery - }); + getData(query); } - }, [ - isOpen, - previousIsOpen, - scope, - selectedConsumers, - selectedEndpoints, - selectedInsights, - selectedInternals, - selectedServices, - assetScopeOption, - searchQuery, - isServicesFilterEnabled - ]); - + }, [isOpen, previousIsOpen, query]); // Apply filters when data is loaded useEffect(() => { - if (previousData === data || isNull(data?.data)) { + if (previousData === data || isNull(data)) { return; } const servicesToSelect = - data?.data?.categories + data?.categories .find((x) => x.categoryName === "Services") ?.entries?.filter((x) => x.selected) .map((x) => x.name) ?? []; setSelectedServices(servicesToSelect); - const operationsCategory = data?.data?.categories.find( + const operationsCategory = data?.categories.find( (x) => x.categoryName === "Operations" ); @@ -300,7 +298,7 @@ export const AssetsFilter = ({ .map((x) => x.name) ?? []; setSelectedInternals(internalsToSelect); - const insightsToSelect = (data?.data?.categories + const insightsToSelect = (data?.categories .find((x) => x.categoryName === "Insights") ?.entries?.filter((x) => x.selected) .map((x) => x.name) ?? []) as InsightType[]; @@ -321,13 +319,13 @@ export const AssetsFilter = ({ if (isServicesFilterEnabled) { setGloballySelectedServices(filtersQuery.services); } - onApply(filtersQuery); + setFilters(filtersQuery); } }, [ previousData, data, filters, - onApply, + setFilters, selectedServices, selectedEndpoints, selectedConsumers, @@ -342,7 +340,7 @@ export const AssetsFilter = ({ useEffect(() => { if (previousIsOpen && !isOpen) { const filtersQuery = { - services: selectedServices, + services: isServicesFilterEnabled ? selectedServices : [], operations: [ ...selectedEndpoints, ...selectedConsumers, @@ -350,7 +348,7 @@ export const AssetsFilter = ({ ], insights: selectedInsights }; - onApply(filtersQuery); + setFilters(filtersQuery); setPersistedFilters(filtersQuery); if (isServicesFilterEnabled) { setGloballySelectedServices(filtersQuery.services); @@ -360,7 +358,6 @@ export const AssetsFilter = ({ }, [ previousIsOpen, isOpen, - onApply, selectedConsumers, selectedEndpoints, selectedInsights, @@ -368,16 +365,16 @@ export const AssetsFilter = ({ selectedServices, setPersistedFilters, setGloballySelectedServices, - isServicesFilterEnabled + isServicesFilterEnabled, + setFilters ]); const handleClearFiltersButtonClick = () => { getData({ + ...query, services: isServicesFilterEnabled ? [] : selectedServices, insights: [], - operations: [], - assetScopeOption, - searchQuery + operations: [] }); }; @@ -412,22 +409,21 @@ export const AssetsFilter = ({ } getData({ + ...query, services, operations: [...endpoints, ...consumers, ...internals], - insights, - assetScopeOption, - searchQuery + insights }); }; - const servicesCategory = data?.data?.categories.find( + const servicesCategory = data?.categories.find( (x) => x.categoryName === "Services" ) ?? { categoryName: "Services", entries: [] }; - const operationsCategory = data?.data?.categories.find( + const operationsCategory = data?.categories.find( (x) => x.categoryName === "Operations" ); const endpointsCategory = operationsCategory?.categories?.find( @@ -449,7 +445,7 @@ export const AssetsFilter = ({ entries: [] }; - const insightsCategory = data?.data?.categories.find( + const insightsCategory = data?.categories.find( (x) => x.categoryName === "Insights" ) ?? { categoryName: "Insights", @@ -535,7 +531,7 @@ export const AssetsFilter = ({ void; - assetScopeOption: AssetScopeOption | null; - searchQuery: string; -} +import { InsightType } from "../../Insights/types"; export interface AssetFilterEntry { enabled: boolean; @@ -35,3 +28,12 @@ export interface AssetFilterQuery { export interface GetAssetFiltersDataPayload { query: AssetFilterQuery; } + +export interface GetAssetFiltersDataParams { + services: string[]; + operations: string[]; + insights: InsightType[]; + viewMode?: string; + scopeSpanCodeObjectId?: string; + searchQuery: string; +} diff --git a/src/components/Assets/AssetsViewScopeConfiguration/AssetsViewScopeConfiguration.stories.tsx b/src/components/Assets/AssetsViewScopeConfiguration/AssetsViewScopeConfiguration.stories.tsx index 2424e38e6..5ac7d8448 100644 --- a/src/components/Assets/AssetsViewScopeConfiguration/AssetsViewScopeConfiguration.stories.tsx +++ b/src/components/Assets/AssetsViewScopeConfiguration/AssetsViewScopeConfiguration.stories.tsx @@ -18,26 +18,6 @@ type Story = StoryObj; export const Default: Story = { args: { - currentScope: { - span: { - displayName: "displayName", - spanCodeObjectId: "spanCodeObjectId", - serviceName: null, - role: "Entry", - methodId: null - }, - code: { - relatedCodeDetailsList: [], - codeDetailsList: [] - }, - hasErrors: false, - issuesInsightsCount: 0, - analyticsInsightsCount: 0, - unreadInsightsCount: 0 - }, - assetsCount: 1, - onAssetViewChange: () => { - return undefined; - } + assetsCount: 1 } }; diff --git a/src/components/Assets/AssetsViewScopeConfiguration/index.tsx b/src/components/Assets/AssetsViewScopeConfiguration/index.tsx index 5bf7719a5..3d4e932b2 100644 --- a/src/components/Assets/AssetsViewScopeConfiguration/index.tsx +++ b/src/components/Assets/AssetsViewScopeConfiguration/index.tsx @@ -1,4 +1,7 @@ -import { useEffect, useState } from "react"; +import { useEffect } from "react"; +import { useAssetsSelector } from "../../../store/assets/useAssetsSelector"; +import { useConfigSelector } from "../../../store/config/useConfigSelector"; +import { useStore } from "../../../store/useStore"; import { isNumber } from "../../../typeGuards/isNumber"; import { formatUnit } from "../../../utils/formatUnit"; import { ArrowIcon } from "../../common/icons/12px/ArrowIcon"; @@ -6,37 +9,23 @@ import { TreeNodesIcon } from "../../common/icons/12px/TreeNodesIcon"; import { Toggle } from "../../common/v3/Toggle"; import { ToggleOption } from "../../common/v3/Toggle/types"; import * as s from "./styles"; -import { AssetsViewConfigurationProps as AssetsViewScopeConfigurationProps } from "./types"; - -type ViewMode = "descendants" | "children"; +import { AssetsViewScopeConfigurationProps, ViewMode } from "./types"; export const AssetsViewScopeConfiguration = ({ - currentScope, - onAssetViewChange, assetsCount }: AssetsViewScopeConfigurationProps) => { - const [viewMode, setViewMode] = useState("descendants"); + const { scope } = useConfigSelector(); + const { viewMode } = useAssetsSelector(); + const { setAssetsViewMode } = useStore.getState(); useEffect(() => { - const isEntryPoint = !currentScope || currentScope.span?.role === "Entry"; - - setViewMode(isEntryPoint ? "descendants" : "children"); + const isEntryPoint = !scope || scope.span?.role === "Entry"; - onAssetViewChange({ - scopedSpanCodeObjectId: currentScope?.span?.spanCodeObjectId, - isDirect: !isEntryPoint - }); - }, [currentScope, onAssetViewChange]); + setAssetsViewMode(isEntryPoint ? "descendants" : "children"); + }, [scope, setAssetsViewMode]); const handleToggleOptionChange = (value: ViewMode) => { - setViewMode(value); - - if (onAssetViewChange) { - onAssetViewChange({ - isDirect: value === "children", - scopedSpanCodeObjectId: currentScope?.span?.spanCodeObjectId - }); - } + setAssetsViewMode(value); }; const toggleOptions: ToggleOption[] = [ diff --git a/src/components/Assets/AssetsViewScopeConfiguration/types.ts b/src/components/Assets/AssetsViewScopeConfiguration/types.ts index da43b8398..8963799f8 100644 --- a/src/components/Assets/AssetsViewScopeConfiguration/types.ts +++ b/src/components/Assets/AssetsViewScopeConfiguration/types.ts @@ -1,12 +1,5 @@ -import { Scope } from "../../common/App/types"; - -export interface AssetsViewConfigurationProps { - onAssetViewChange: (assetViewScope: AssetScopeOption) => void; - currentScope: Scope; +export interface AssetsViewScopeConfigurationProps { assetsCount?: number; } -export interface AssetScopeOption { - scopedSpanCodeObjectId?: string; - isDirect: boolean; -} +export type ViewMode = "descendants" | "children"; diff --git a/src/components/Assets/index.tsx b/src/components/Assets/index.tsx index 21f715ac3..ab9488001 100644 --- a/src/components/Assets/index.tsx +++ b/src/components/Assets/index.tsx @@ -1,10 +1,10 @@ import { useCallback, useEffect, useMemo, useState } from "react"; import { useParams } from "react-router-dom"; -import { getFeatureFlagValue } from "../../featureFlags"; import { useDebounce } from "../../hooks/useDebounce"; import { usePrevious } from "../../hooks/usePrevious"; +import { useAssetsSelector } from "../../store/assets/useAssetsSelector"; import { useConfigSelector } from "../../store/config/useConfigSelector"; -import { FeatureFlag } from "../../types"; +import { useStore } from "../../store/useStore"; import { sendUserActionTrackingEvent } from "../../utils/actions/sendUserActionTrackingEvent"; import { useHistory } from "../Main/useHistory"; import { EmptyState } from "../common/EmptyState"; @@ -14,49 +14,44 @@ import { Tooltip } from "../common/v3/Tooltip"; import { AssetList } from "./AssetList"; import { AssetTypeList } from "./AssetTypeList"; import { AssetsFilter } from "./AssetsFilter"; -import { AssetFilterQuery } from "./AssetsFilter/types"; import { AssetsViewScopeConfiguration } from "./AssetsViewScopeConfiguration"; -import { AssetScopeOption } from "./AssetsViewScopeConfiguration/types"; import { NoDataMessage } from "./NoDataMessage"; import * as s from "./styles"; import { trackingEvents } from "./tracking"; import { DataRefresher } from "./types"; +const SEARCH_INPUT_DEBOUNCE_DELAY = 1000; // in milliseconds + export const Assets = () => { const [assetsCount, setAssetsCount] = useState(); const params = useParams(); const selectedAssetTypeId = useMemo(() => params.typeId, [params]); - const [searchInputValue, setSearchInputValue] = useState(""); - const debouncedSearchInputValue = useDebounce(searchInputValue, 1000); - const [assetScopeOption, setAssetScopeOption] = - useState(null); - const [selectedFilters, setSelectedFilters] = useState(); - const { scope, environments, backendInfo } = useConfigSelector(); - const previousScopeSpanCodeObjectId = usePrevious( - scope?.span?.spanCodeObjectId + const { search, filters } = useAssetsSelector(); + const { setSearch } = useStore.getState(); + const [searchInputValue, setSearchInputValue] = useState(search); + const debouncedSearchInputValue = useDebounce( + searchInputValue, + SEARCH_INPUT_DEBOUNCE_DELAY ); + const { scope, environments } = useConfigSelector(); + const scopeSpanCodeObjectId = scope?.span?.spanCodeObjectId; + const previousScopeSpanCodeObjectId = usePrevious(scopeSpanCodeObjectId); const [assetTypeListDataRefresher, setAssetTypeListRefresher] = useState(null); const [assetListDataRefresher, setAssetListRefresher] = useState(null); const { goTo } = useHistory(); const isBackendUpgradeMessageVisible = false; - const areExtendedAssetsFiltersEnabled = getFeatureFlagValue( - backendInfo, - FeatureFlag.ARE_EXTENDED_ASSETS_FILTERS_ENABLED - ); useEffect(() => { - if (!scope?.span) { - setAssetScopeOption(null); + if (previousScopeSpanCodeObjectId !== scopeSpanCodeObjectId) { + setSearchInputValue(""); } - }, [scope]); + }, [scopeSpanCodeObjectId, previousScopeSpanCodeObjectId, setSearch]); useEffect(() => { - if (previousScopeSpanCodeObjectId !== scope?.span?.spanCodeObjectId) { - setSearchInputValue(""); - } - }, [scope?.span?.spanCodeObjectId, previousScopeSpanCodeObjectId]); + setSearch(debouncedSearchInputValue); + }, [debouncedSearchInputValue, setSearch]); const handleGoToAllAssets = () => { goTo(".."); @@ -70,10 +65,6 @@ export const Assets = () => { goTo(assetTypeId); }; - const handleApplyFilters = (filters: AssetFilterQuery) => { - setSelectedFilters(filters); - }; - const handleRefresh = () => { sendUserActionTrackingEvent(trackingEvents.REFRESH_BUTTON_CLICKED, { view: !selectedAssetTypeId ? "asset categories" : "assets" @@ -92,10 +83,6 @@ export const Assets = () => { setAssetsCount(count); }, []); - const handleAssetViewModeChange = useCallback((val: AssetScopeOption) => { - setAssetScopeOption(val); - }, []); - const handleAssetTypeListRefresherChange = useCallback( (refresher: () => void) => { setAssetTypeListRefresher({ refresh: refresher }); @@ -131,7 +118,7 @@ export const Assets = () => { return ; } - if (!selectedFilters) { + if (!filters) { return ; } @@ -139,9 +126,6 @@ export const Assets = () => { return ( @@ -152,9 +136,6 @@ export const Assets = () => { @@ -166,11 +147,7 @@ export const Assets = () => { {scope?.span && ( - + )} @@ -178,16 +155,7 @@ export const Assets = () => { onChange={handleSearchInputChange} value={searchInputValue} /> - + Boolean( - filters && - [ - ...filters.insights, - ...filters.operations, - ...(isServicesFilterEnabled ? filters.services : []) - ].length > 0 + [ + ...filters.insights, + ...filters.operations, + ...(isServicesFilterEnabled ? filters.services : []) + ].length > 0 ) || searchQuery.length > 0; diff --git a/src/components/Dashboard/Report/Cards/DiscoveredAssets/index.tsx b/src/components/Dashboard/Report/Cards/DiscoveredAssets/index.tsx index b11d6981c..b8d62a1db 100644 --- a/src/components/Dashboard/Report/Cards/DiscoveredAssets/index.tsx +++ b/src/components/Dashboard/Report/Cards/DiscoveredAssets/index.tsx @@ -19,7 +19,7 @@ export const DiscoveredAssets = ({ return ( ); diff --git a/src/components/Dashboard/Report/Cards/DiscoveredIssues/index.tsx b/src/components/Dashboard/Report/Cards/DiscoveredIssues/index.tsx index 73c92ca18..63965d806 100644 --- a/src/components/Dashboard/Report/Cards/DiscoveredIssues/index.tsx +++ b/src/components/Dashboard/Report/Cards/DiscoveredIssues/index.tsx @@ -12,7 +12,7 @@ export const DiscoveredIssues = ({ }: DiscoveredIssuesProps) => { return ( { {/* */} diff --git a/src/components/Errors/ErrorDetails/styles.ts b/src/components/Errors/ErrorDetails/styles.ts index b1760c7c6..e6fad6588 100644 --- a/src/components/Errors/ErrorDetails/styles.ts +++ b/src/components/Errors/ErrorDetails/styles.ts @@ -1,6 +1,9 @@ import styled from "styled-components"; import { Card } from "../../common/v3/Card"; -import { Content as CardContent } from "../../common/v3/Card/styles"; +import { + Content as CardContent, + Header as CardHeader +} from "../../common/v3/Card/styles"; export const Container = styled.div` padding: 8px; @@ -15,11 +18,11 @@ export const ErrorDetailsCard = styled(Card)` min-height: 450px; overflow: hidden; - & > ${CardContent}:first-child { + & > ${CardHeader} { height: initial; } - & > ${CardContent}:last-child { + & > ${CardContent} { overflow: hidden; } `; diff --git a/src/components/Insights/InsightsCatalog/InsightsPage/AffectedEndpointsSelector/index.tsx b/src/components/Insights/InsightsCatalog/InsightsPage/AffectedEndpointsSelector/index.tsx index 05c4b51d6..770a6ff2a 100644 --- a/src/components/Insights/InsightsCatalog/InsightsPage/AffectedEndpointsSelector/index.tsx +++ b/src/components/Insights/InsightsCatalog/InsightsPage/AffectedEndpointsSelector/index.tsx @@ -60,8 +60,8 @@ export const AffectedEndpointsSelector = ({ listHeader={ diff --git a/src/components/Insights/InsightsCatalog/InsightsPage/insightCards/common/IssueCompactCard/index.tsx b/src/components/Insights/InsightsCatalog/InsightsPage/insightCards/common/IssueCompactCard/index.tsx index a4bfe3e26..e1fdaf770 100644 --- a/src/components/Insights/InsightsCatalog/InsightsPage/insightCards/common/IssueCompactCard/index.tsx +++ b/src/components/Insights/InsightsCatalog/InsightsPage/insightCards/common/IssueCompactCard/index.tsx @@ -176,7 +176,7 @@ export const IssueCompactCard = ({ {metric && } {insight.status && statusInfo && ( {statusInfo.label}} + title={{statusInfo.label}} placement={"top"} fullWidth={true} > diff --git a/src/components/Insights/Issues/IssuesFilter/index.tsx b/src/components/Insights/Issues/IssuesFilter/index.tsx index f1d30e79c..549f9509e 100644 --- a/src/components/Insights/Issues/IssuesFilter/index.tsx +++ b/src/components/Insights/Issues/IssuesFilter/index.tsx @@ -259,7 +259,7 @@ export const IssuesFilter = () => { diff --git a/src/components/Main/index.tsx b/src/components/Main/index.tsx index 4e581aeec..fca9c88ed 100644 --- a/src/components/Main/index.tsx +++ b/src/components/Main/index.tsx @@ -50,8 +50,15 @@ const getURLToNavigateOnCodeLensClick = (scope: Scope): string | undefined => { export const Main = () => { const location = useLocation(); - const { environments, environment, scope, userInfo, backendInfo } = - useConfigSelector(); + const { + environments, + environment, + scope, + userInfo, + backendInfo, + selectedServices + } = useConfigSelector(); + const { setSelectedServices } = useStore.getState(); const previousEnvironment = usePrevious(environment); const userId = userInfo?.id; const previousUserId = usePrevious(userId); @@ -63,8 +70,6 @@ export const Main = () => { "project" ); const previousPersistedServices = usePrevious(persistedServices); - const selectedServices = useConfigSelector().selectedServices; - const { setSelectedServices } = useStore.getState(); const isInitialized = useMemo( () => !isUndefined(persistedServices), [persistedServices] diff --git a/src/components/Main/tracking.ts b/src/components/Main/tracking.ts index 9b3e3712a..3f473eaed 100644 --- a/src/components/Main/tracking.ts +++ b/src/components/Main/tracking.ts @@ -17,7 +17,10 @@ export const trackingEvents = addPrefix( "promotion registration close button clicked", PROMOTION_DISCARDED: "promotion discarded", PROMOTION_REGISTRATION_FORM_OPENED: "promotion registration form opened", - LOGIN_SCREEN_VIEWED: "login screen viewed" + LOGIN_SCREEN_VIEWED: "login screen viewed", + SCOPE_BAR_EXPAND_BUTTON_CLICKED: "span info expand button clicked", + SCOPE_BAR_COLLAPSE_BUTTON_CLICKED: "span info collapse button clicked", + SPAN_INFO_COLLAPSE_BUTTON_CLICKED: "span info collapse button clicked" }, " " ); diff --git a/src/components/Navigation/KebabMenu/index.tsx b/src/components/Navigation/KebabMenu/index.tsx index 8cc56800d..36a301020 100644 --- a/src/components/Navigation/KebabMenu/index.tsx +++ b/src/components/Navigation/KebabMenu/index.tsx @@ -56,6 +56,7 @@ export const KebabMenu = ({ onClose }: KebabMenuProps) => { const handleOpenDocsClick = () => { sendUserActionTrackingEvent(trackingEvents.OPEN_DOCS_CLICKED); openURLInDefaultBrowser(DIGMA_DOCUMENTATION); + onClose(); }; const handleDashboardClick = () => { @@ -68,6 +69,7 @@ export const KebabMenu = ({ onClose }: KebabMenuProps) => { } }); } + onClose(); }; // const handleReportClick = () => { diff --git a/src/components/Navigation/ScopeBar/index.tsx b/src/components/Navigation/ScopeBar/index.tsx index b5e275b1c..489a09f84 100644 --- a/src/components/Navigation/ScopeBar/index.tsx +++ b/src/components/Navigation/ScopeBar/index.tsx @@ -2,6 +2,7 @@ import { useEffect, useState } from "react"; import { history } from "../../../containers/Main/history"; import { isString } from "../../../typeGuards/isString"; import { sendUserActionTrackingEvent } from "../../../utils/actions/sendUserActionTrackingEvent"; +import { trackingEvents as mainTrackingEvents } from "../../Main/tracking"; import { CodeDetails, Scope } from "../../common/App/types"; import { NewPopover } from "../../common/NewPopover"; import { CrosshairIcon } from "../../common/icons/16px/CrosshairIcon"; @@ -108,6 +109,15 @@ export const ScopeBar = ({ }; const handleExpandCollapseButtonClick = () => { + if (isExpanded) { + sendUserActionTrackingEvent( + mainTrackingEvents.SCOPE_BAR_COLLAPSE_BUTTON_CLICKED + ); + } else { + sendUserActionTrackingEvent( + mainTrackingEvents.SCOPE_BAR_EXPAND_BUTTON_CLICKED + ); + } onExpandCollapseChange(!isExpanded); }; diff --git a/src/components/Navigation/SpanInfo/index.tsx b/src/components/Navigation/SpanInfo/index.tsx index 2b079cc04..98b7f4be9 100644 --- a/src/components/Navigation/SpanInfo/index.tsx +++ b/src/components/Navigation/SpanInfo/index.tsx @@ -1,6 +1,8 @@ +import { sendUserActionTrackingEvent } from "../../../utils/actions/sendUserActionTrackingEvent"; import { GlobeIcon } from "../../common/icons/16px/GlobeIcon"; import { WrenchIcon } from "../../common/icons/16px/WrenchIcon"; import { Tooltip } from "../../common/v3/Tooltip"; +import { trackingEvents } from "../../Main/tracking"; import * as s from "./styles"; import { SpanInfoProps } from "./types"; @@ -18,6 +20,9 @@ const getLanguage = (assetTypeId: string) => { export const SpanInfo = ({ onCollapse, data }: SpanInfoProps) => { const handleCollapseButtonClick = () => { + sendUserActionTrackingEvent( + trackingEvents.SPAN_INFO_COLLAPSE_BUTTON_CLICKED + ); onCollapse(); }; diff --git a/src/components/common/App/index.tsx b/src/components/common/App/index.tsx index 4e10e79d5..c6ae12cbd 100644 --- a/src/components/common/App/index.tsx +++ b/src/components/common/App/index.tsx @@ -487,7 +487,27 @@ export const App = ({ theme, children, id }: AppProps) => { handleSetRunConfiguration ); }; - }, []); + }, [ + setJaegerURL, + setIsJaegerEnabled, + setIsDigmaEngineInstalled, + setIsDigmaEngineRunning, + setDigmaStatus, + setIsDockerInstalled, + setIsDockerComposeInstalled, + setDigmaApiUrl, + setUserRegistrationEmail, + setIsObservabilityEnabled, + setBackendInfo, + setEnvironments, + setEnvironment, + setScope, + setUserInfo, + setInsightStats, + setRunConfig, + setIsDigmathonGameFinished, + setIsMicrometerProject + ]); const styledComponentsTheme = getStyledComponentsTheme( currentTheme, diff --git a/src/store/assets/assetsSlice.ts b/src/store/assets/assetsSlice.ts new file mode 100644 index 000000000..a93da3f37 --- /dev/null +++ b/src/store/assets/assetsSlice.ts @@ -0,0 +1,80 @@ +import { createSlice } from "zustand-slices"; +import { + AssetsData, + Sorting, + SORTING_CRITERION, + SORTING_ORDER +} from "../../components/Assets/AssetList/types"; +import { AssetCategoryData } from "../../components/Assets/AssetTypeList/types"; +import { AssetFilterQuery } from "../../components/Assets/AssetsFilter/types"; +import { ViewMode } from "../../components/Assets/AssetsViewScopeConfiguration/types"; + +export interface AssetsState { + assetCategories: AssetCategoryData[] | null; + areAssetCategoriesLoading: boolean; + selectedAssetCategory: string | null; + assets: AssetsData | null; + areAssetsLoading: boolean; + filters: AssetFilterQuery; + viewMode: ViewMode; + search: string; + page: number; + sorting: Sorting; +} + +const allFiltersInitialState: { + filters: AssetFilterQuery; + viewMode: ViewMode; + search: string; + page: number; + sorting: Sorting; +} = { + filters: { + services: [], + operations: [], + insights: [] + }, + viewMode: "descendants", + search: "", + page: 0, + sorting: { + criterion: SORTING_CRITERION.CRITICAL_INSIGHTS, + order: SORTING_ORDER.DESC + } +}; + +export const initialState: AssetsState = { + ...allFiltersInitialState, + assetCategories: null, + areAssetCategoriesLoading: false, + selectedAssetCategory: null, + assets: null, + areAssetsLoading: false +}; + +const set = (update: Partial) => (state: AssetsState) => ({ + ...state, + ...update +}); + +export const assetsSlice = createSlice({ + name: "assets", + value: initialState, + actions: { + setAssetCategories: (categories: AssetCategoryData[]) => + set({ assetCategories: categories }), + setAreAssetCategoriesLoading: (isLoading: boolean) => + set({ areAssetCategoriesLoading: isLoading }), + setSelectedAssetCategory: (category: string | null) => + set({ selectedAssetCategory: category }), + setAssets: (assets: AssetsData) => set({ assets }), + setAreAssetsLoading: (isLoading: boolean) => + set({ areAssetsLoading: isLoading }), + setAssetsFilters: (filters: AssetFilterQuery) => set({ filters }), + setAssetsViewMode: (viewMode: ViewMode) => set({ viewMode }), + setAssetsSearch: (search: string) => set({ search }), + setAssetsPage: (page: number) => set({ page }), + setAssetsSorting: (sorting: Sorting) => set({ sorting }), + resetAssets: () => set(initialState) + } +}); diff --git a/src/store/assets/useAssetsSelector.ts b/src/store/assets/useAssetsSelector.ts new file mode 100644 index 000000000..759e16d26 --- /dev/null +++ b/src/store/assets/useAssetsSelector.ts @@ -0,0 +1,3 @@ +import { useStore } from "../useStore"; + +export const useAssetsSelector = () => useStore((state) => state.assets); diff --git a/src/store/useStore.ts b/src/store/useStore.ts index 827bee2ba..d022aa7cc 100644 --- a/src/store/useStore.ts +++ b/src/store/useStore.ts @@ -1,12 +1,13 @@ import { create } from "zustand"; import { withSlices } from "zustand-slices"; import { Scope } from "../components/common/App/types"; +import { assetsSlice } from "./assets/assetsSlice"; import { configSlice } from "./config/configSlice"; import { insightsSlice } from "./insights/insightsSlice"; import { withMutableActions } from "./withMutableActions"; export const useStore = create( - withMutableActions(withSlices(configSlice, insightsSlice), { + withMutableActions(withSlices(configSlice, insightsSlice, assetsSlice), { setScope: (scope: Scope) => (_, set) => { set((state) => state.config.scope?.span?.spanCodeObjectId !== From 53189f8d3167ace5aca47f35ed5a89387aed4865 Mon Sep 17 00:00:00 2001 From: Kyrylo Shmidt Date: Sun, 8 Sep 2024 07:33:45 +0200 Subject: [PATCH 2/3] Fix actions with the same name --- src/components/Assets/AssetList/index.tsx | 6 ++--- src/components/Assets/AssetTypeList/index.tsx | 4 ++-- src/components/Assets/AssetsFilter/index.tsx | 1 + src/components/Assets/index.tsx | 2 +- .../InsightsCatalog/FilterPanel/index.tsx | 2 +- .../Insights/InsightsCatalog/index.tsx | 8 +++---- .../Insights/Issues/IssuesFilter/index.tsx | 7 ++++-- .../Insights/Issues/useIssuesFilters.ts | 2 +- src/components/Insights/index.tsx | 6 ++--- src/components/Insights/useInsightsData.ts | 3 ++- src/store/assets/assetsSlice.ts | 8 +++---- src/store/insights/insightsSlice.ts | 23 ++++++++++--------- 12 files changed, 39 insertions(+), 33 deletions(-) diff --git a/src/components/Assets/AssetList/index.tsx b/src/components/Assets/AssetList/index.tsx index 2c8562f0b..6c9d893d3 100644 --- a/src/components/Assets/AssetList/index.tsx +++ b/src/components/Assets/AssetList/index.tsx @@ -133,8 +133,9 @@ export const AssetList = ({ } = useAssetsSelector(); const { setAssets: setData, - setSorting, - setAssetsPage: setPage + setAssetsSorting: setSorting, + setAssetsPage: setPage, + setShowAssetsHeaderToolBox } = useStore.getState(); const previousData = usePrevious(data); const [lastSetDataTimeStamp, setLastSetDataTimeStamp] = useState(); @@ -157,7 +158,6 @@ export const AssetList = ({ const previousScopeSpanCodeObjectId = usePrevious(scopeSpanCodeObjectId); const isServicesFilterEnabled = !scopeSpanCodeObjectId; const isInitialLoading = !data; - const { setShowAssetsHeaderToolBox } = useStore.getState(); const refreshData = useCallback(() => { getData( diff --git a/src/components/Assets/AssetTypeList/index.tsx b/src/components/Assets/AssetTypeList/index.tsx index 5de670c2c..aec78e61e 100644 --- a/src/components/Assets/AssetTypeList/index.tsx +++ b/src/components/Assets/AssetTypeList/index.tsx @@ -76,7 +76,8 @@ export const AssetTypeList = ({ filters, assetCategoriesData: data } = useAssetsSelector(); - const { setAssetCategoriesData: setData } = useStore.getState(); + const { setAssetCategoriesData: setData, setShowAssetsHeaderToolBox } = + useStore.getState(); const previousSearch = usePrevious(search); const previousViewMode = usePrevious(viewMode); const previousData = usePrevious(data); @@ -88,7 +89,6 @@ export const AssetTypeList = ({ const previousEnvironment = usePrevious(environment); const refreshTimerId = useRef(); const isServicesFilterEnabled = !scope?.span?.spanCodeObjectId; - const { setShowAssetsHeaderToolBox } = useStore.getState(); const [showNoDataWithParents, setShowNoDataWithParents] = useState(false); const isInitialLoading = !data; diff --git a/src/components/Assets/AssetsFilter/index.tsx b/src/components/Assets/AssetsFilter/index.tsx index 2fac6be8c..dfae3da2e 100644 --- a/src/components/Assets/AssetsFilter/index.tsx +++ b/src/components/Assets/AssetsFilter/index.tsx @@ -260,6 +260,7 @@ export const AssetsFilter = () => { getData(query); } }, [isOpen, previousIsOpen, query]); + // Apply filters when data is loaded useEffect(() => { if (previousData === data || isNull(data)) { diff --git a/src/components/Assets/index.tsx b/src/components/Assets/index.tsx index 490fd7e67..d935101f2 100644 --- a/src/components/Assets/index.tsx +++ b/src/components/Assets/index.tsx @@ -27,7 +27,7 @@ export const Assets = () => { const params = useParams(); const selectedAssetTypeId = useMemo(() => params.typeId, [params]); const { search, filters } = useAssetsSelector(); - const { setSearch } = useStore.getState(); + const { setAssetsSearch: setSearch } = useStore.getState(); const [searchInputValue, setSearchInputValue] = useState(search); const debouncedSearchInputValue = useDebounce( searchInputValue, diff --git a/src/components/Insights/InsightsCatalog/FilterPanel/index.tsx b/src/components/Insights/InsightsCatalog/FilterPanel/index.tsx index b0d9f92f9..71bc16da2 100644 --- a/src/components/Insights/InsightsCatalog/FilterPanel/index.tsx +++ b/src/components/Insights/InsightsCatalog/FilterPanel/index.tsx @@ -12,7 +12,7 @@ export const FilterPanel = ({ unreadCount }: FilterPanelProps) => { const { filters } = useInsightsSelector(); - const { setFilters } = useStore.getState(); + const { setInsightsFilters: setFilters } = useStore.getState(); const handleFilterChipClick = (selectedFilter?: InsightFilterType) => { const newFilters = new Set(filters); diff --git a/src/components/Insights/InsightsCatalog/index.tsx b/src/components/Insights/InsightsCatalog/index.tsx index 0a2853fd3..f2a70effc 100644 --- a/src/components/Insights/InsightsCatalog/index.tsx +++ b/src/components/Insights/InsightsCatalog/index.tsx @@ -67,10 +67,10 @@ export const InsightsCatalog = ({ onRefresh }: InsightsCatalogProps) => { const { - setViewMode: setMode, - setPage, - setSorting, - setSearch + setInsightsViewMode: setMode, + setInsightsPage: setPage, + setInsightsSorting: setSorting, + setInsightsSearch: setSearch } = useStore.getState(); const { diff --git a/src/components/Insights/Issues/IssuesFilter/index.tsx b/src/components/Insights/Issues/IssuesFilter/index.tsx index 549f9509e..69da1203e 100644 --- a/src/components/Insights/Issues/IssuesFilter/index.tsx +++ b/src/components/Insights/Issues/IssuesFilter/index.tsx @@ -22,8 +22,11 @@ import { trackingEvents } from "./tracking"; export const IssuesFilter = () => { const { filteredInsightTypes, filters } = useInsightsSelector(); const { selectedServices, backendInfo, scope } = useConfigSelector(); - const { setSelectedServices, setFilteredInsightTypes, setFilters } = - useStore.getState(); + const { + setSelectedServices, + setInsightsFilteredInsightTypes: setFilteredInsightTypes, + setInsightsFilters: setFilters + } = useStore.getState(); const isCriticalOnly = useMemo( () => filters.includes("criticality"), [filters] diff --git a/src/components/Insights/Issues/useIssuesFilters.ts b/src/components/Insights/Issues/useIssuesFilters.ts index 7bd22ac2f..5b174314a 100644 --- a/src/components/Insights/Issues/useIssuesFilters.ts +++ b/src/components/Insights/Issues/useIssuesFilters.ts @@ -30,7 +30,7 @@ export const useIssuesFilters = () => { viewMode, filters } = useInsightsSelector(); - const { setIssuesFilters: setData } = useStore.getState(); + const { setInsightsIssuesFilters: setData } = useStore.getState(); const [lastSetDataTimeStamp, setLastSetDataTimeStamp] = useState(); const previousLastSetDataTimeStamp = usePrevious(lastSetDataTimeStamp); const refreshTimerId = useRef(); diff --git a/src/components/Insights/index.tsx b/src/components/Insights/index.tsx index 17dbb541c..eeb11aaf4 100644 --- a/src/components/Insights/index.tsx +++ b/src/components/Insights/index.tsx @@ -242,9 +242,9 @@ export const Insights = ({ insightViewType }: InsightsProps) => { isRegistrationEnabled && !userRegistrationEmail; const { setInsightViewType, - setFilteredInsightTypes, - setFilters, - insightsReset: reset + setInsightsFilteredInsightTypes: setFilteredInsightTypes, + setInsightsFilters: setFilters, + resetInsights: reset } = useStore.getState(); const { insightViewType: storedInsightViewType, diff --git a/src/components/Insights/useInsightsData.ts b/src/components/Insights/useInsightsData.ts index fd76a1668..6ece7b824 100644 --- a/src/components/Insights/useInsightsData.ts +++ b/src/components/Insights/useInsightsData.ts @@ -145,7 +145,8 @@ export const useInsightsData = ({ isDataLoading: isLoading, insightViewType } = useInsightsSelector(); - const { setData, setIsDataLoading: setIsLoading } = useStore.getState(); + const { setInsightsData: setData, setIsInsightsDataLoading: setIsLoading } = + useStore.getState(); const isInitialLoading = !data && isLoading; const [lastSetDataTimeStamp, setLastSetDataTimeStamp] = useState(); const previousLastSetDataTimeStamp = usePrevious(lastSetDataTimeStamp); diff --git a/src/store/assets/assetsSlice.ts b/src/store/assets/assetsSlice.ts index 465a74486..8545a1f10 100644 --- a/src/store/assets/assetsSlice.ts +++ b/src/store/assets/assetsSlice.ts @@ -11,7 +11,7 @@ import { ViewMode } from "../../components/Assets/AssetsViewScopeConfiguration/t export interface AssetsState { assetCategoriesData: AssetCategoriesData | null; - areAssetCategoriesLoading: boolean; + isAssetCategoriesDataLoading: boolean; selectedAssetCategory: string | null; assets: AssetsData | null; areAssetsLoading: boolean; @@ -47,7 +47,7 @@ const allFiltersInitialState: { export const initialState: AssetsState = { ...allFiltersInitialState, assetCategoriesData: null, - areAssetCategoriesLoading: false, + isAssetCategoriesDataLoading: false, selectedAssetCategory: null, assets: null, areAssetsLoading: false, @@ -65,8 +65,8 @@ export const assetsSlice = createSlice({ actions: { setAssetCategoriesData: (data: AssetCategoriesData) => set({ assetCategoriesData: data }), - setAreAssetCategoriesLoading: (isLoading: boolean) => - set({ areAssetCategoriesLoading: isLoading }), + setIsAssetCategoriesDataLoading: (isLoading: boolean) => + set({ isAssetCategoriesDataLoading: isLoading }), setSelectedAssetCategory: (category: string | null) => set({ selectedAssetCategory: category }), setAssets: (assets: AssetsData) => set({ assets }), diff --git a/src/store/insights/insightsSlice.ts b/src/store/insights/insightsSlice.ts index 1394fda06..d8ed3c787 100644 --- a/src/store/insights/insightsSlice.ts +++ b/src/store/insights/insightsSlice.ts @@ -51,21 +51,22 @@ export const insightsSlice = createSlice({ name: "insights", value: initialState, actions: { - setData: (data: InsightsData) => set({ data }), - setIsDataLoading: (isDataLoading: boolean) => set({ isDataLoading }), - setSearch: (search: string) => set({ search }), - setPage: (page: number) => set({ page }), - setSorting: (sorting: Sorting) => set({ sorting }), - setViewMode: (viewMode: ViewMode) => set({ viewMode }), - setFilters: (filters: InsightFilterType[]) => set({ filters }), - setFilteredInsightTypes: (filteredInsightTypes: string[]) => + setInsightsData: (data: InsightsData) => set({ data }), + setIsInsightsDataLoading: (isDataLoading: boolean) => + set({ isDataLoading }), + setInsightsSearch: (search: string) => set({ search }), + setInsightsPage: (page: number) => set({ page }), + setInsightsSorting: (sorting: Sorting) => set({ sorting }), + setInsightsViewMode: (viewMode: ViewMode) => set({ viewMode }), + setInsightsFilters: (filters: InsightFilterType[]) => set({ filters }), + setInsightsFilteredInsightTypes: (filteredInsightTypes: string[]) => set({ filteredInsightTypes }), setInsightViewType: (insightViewType: InsightViewType) => set({ insightViewType }), - setIssuesFilters: (issuesFilters: IssuesFiltersData) => + setInsightsIssuesFilters: (issuesFilters: IssuesFiltersData) => set({ issuesFilters }), - setAreIssuesFiltersLoading: (areIssuesFiltersLoading: boolean) => + setAreInsightsIssuesFiltersLoading: (areIssuesFiltersLoading: boolean) => set({ areIssuesFiltersLoading }), - insightsReset: () => set(initialState) + resetInsights: () => set(initialState) } }); From 6537bc761f3b171ec80ae6d079c55861e4b8af25 Mon Sep 17 00:00:00 2001 From: opoliarush <156646693+opoliarush@users.noreply.github.com> Date: Mon, 9 Sep 2024 11:27:20 +0300 Subject: [PATCH 3/3] Update src/components/Main/tracking.ts --- src/components/Main/tracking.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Main/tracking.ts b/src/components/Main/tracking.ts index 3f473eaed..bad164eef 100644 --- a/src/components/Main/tracking.ts +++ b/src/components/Main/tracking.ts @@ -19,7 +19,7 @@ export const trackingEvents = addPrefix( PROMOTION_REGISTRATION_FORM_OPENED: "promotion registration form opened", LOGIN_SCREEN_VIEWED: "login screen viewed", SCOPE_BAR_EXPAND_BUTTON_CLICKED: "span info expand button clicked", - SCOPE_BAR_COLLAPSE_BUTTON_CLICKED: "span info collapse button clicked", + SCOPE_BAR_COLLAPSE_BUTTON_CLICKED: "scope bar collapse button clicked", SPAN_INFO_COLLAPSE_BUTTON_CLICKED: "span info collapse button clicked" }, " "