From 6ae67c5bc95a461211bd82190a40dc448aac3653 Mon Sep 17 00:00:00 2001 From: Kyrylo Shmidt Date: Thu, 15 Aug 2024 16:05:33 +0200 Subject: [PATCH] Make service filter global --- src/components/Assets/AssetList/index.tsx | 32 +-- src/components/Assets/AssetTypeList/index.tsx | 28 +-- src/components/Assets/AssetsFilter/index.tsx | 221 ++++++++++++++---- src/components/Assets/AssetsFilter/types.ts | 11 + src/components/Assets/index.tsx | 3 + src/components/Assets/utils.tsx | 10 +- .../Insights/InsightsCatalog/index.tsx | 7 +- .../Insights/Issues/IssuesFilter/index.tsx | 39 ++-- src/components/Insights/index.tsx | 17 +- src/components/Insights/useInsightsData.ts | 10 +- src/components/Main/index.tsx | 42 +++- src/containers/Main/stores/useGlobalStore.ts | 4 + .../Main/stores/useInsightsStore.ts | 4 - 13 files changed, 302 insertions(+), 126 deletions(-) diff --git a/src/components/Assets/AssetList/index.tsx b/src/components/Assets/AssetList/index.tsx index 2019a68ce..b24d3adb0 100644 --- a/src/components/Assets/AssetList/index.tsx +++ b/src/components/Assets/AssetList/index.tsx @@ -101,7 +101,7 @@ const getData = ( page: number, sorting: Sorting, searchQuery: string, - filters: AssetFilterQuery | undefined, + filters: AssetFilterQuery = { services: [], operations: [], insights: [] }, isDirect?: boolean, scopedSpanCodeObjectId?: string ) => { @@ -116,14 +116,8 @@ const getData = ( sortOrder: sorting.order, directOnly: isDirect, scopedSpanCodeObjectId, - ...(searchQuery && searchQuery.length > 0 - ? { displayName: searchQuery } - : {}), - ...(filters ?? { - services: [], - operations: [], - insights: [] - }) + ...(searchQuery.length > 0 ? { displayName: searchQuery } : {}), + ...(scopedSpanCodeObjectId ? { ...filters, services: [] } : filters) } } }); @@ -140,7 +134,6 @@ export const AssetList = ({ }: AssetListProps) => { const [data, setData] = useState(); const previousData = usePrevious(data); - const [isInitialLoading, setIsInitialLoading] = useState(false); const [lastSetDataTimeStamp, setLastSetDataTimeStamp] = useState(); const previousLastSetDataTimeStamp = usePrevious(lastSetDataTimeStamp); const [sorting, setSorting] = useState({ @@ -164,6 +157,8 @@ export const AssetList = ({ const refreshTimerId = useRef(); const previousEnvironment = usePrevious(environment); const previousViewScope = usePrevious(scopeViewOptions); + const scope = useGlobalStore.use.scope(); + const isServicesFilterEnabled = !scope?.span?.spanCodeObjectId; const refreshData = useCallback(() => { getData( @@ -196,13 +191,16 @@ export const AssetList = ({ const sortingCriteria = getSortingCriteria(isImpactHidden); - const areAnyFiltersApplied = checkIfAnyFiltersApplied(filters, searchQuery); + const areAnyFiltersApplied = checkIfAnyFiltersApplied( + filters, + searchQuery, + isServicesFilterEnabled + ); + + const isInitialLoading = !data; useEffect(() => { refreshData(); - if (!data) { - setIsInitialLoading(true); - } }, [refreshData]); useEffect(() => { @@ -260,12 +258,6 @@ export const AssetList = ({ } }, [lastSetDataTimeStamp, previousLastSetDataTimeStamp, refreshData]); - useEffect(() => { - if (!previousData && data) { - setIsInitialLoading(false); - } - }, [previousData, data]); - useEffect(() => { if ( isImpactHidden && diff --git a/src/components/Assets/AssetTypeList/index.tsx b/src/components/Assets/AssetTypeList/index.tsx index 371b10994..45d13e29b 100644 --- a/src/components/Assets/AssetTypeList/index.tsx +++ b/src/components/Assets/AssetTypeList/index.tsx @@ -31,7 +31,7 @@ export const ASSET_TYPE_IDS = [ ]; const getData = ( - filters: AssetFilterQuery | undefined, + filters: AssetFilterQuery = { services: [], operations: [], insights: [] }, searchQuery: string, isDirect?: boolean, scopedSpanCodeObjectId?: string @@ -42,7 +42,12 @@ const getData = ( query: { directOnly: isDirect, scopedSpanCodeObjectId, - ...(filters ?? { services: [], operations: [], insights: [] }), + ...(scopedSpanCodeObjectId + ? { + ...filters, + services: [] + } + : filters), ...(searchQuery.length > 0 ? { displayName: searchQuery } : {}) } } @@ -64,13 +69,15 @@ export const AssetTypeList = ({ const previousData = usePrevious(data); const [lastSetDataTimeStamp, setLastSetDataTimeStamp] = useState(); const previousLastSetDataTimeStamp = usePrevious(lastSetDataTimeStamp); - const [isInitialLoading, setIsInitialLoading] = useState(false); const scope = useGlobalStore.use.scope(); const environment = useGlobalStore.use.environment(); 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( () => @@ -92,13 +99,14 @@ export const AssetTypeList = ({ setRefresher(refreshData); }, [refreshData, setRefresher]); - const areAnyFiltersApplied = checkIfAnyFiltersApplied(filters, searchQuery); + const areAnyFiltersApplied = checkIfAnyFiltersApplied( + filters, + searchQuery, + isServicesFilterEnabled + ); useEffect(() => { refreshData(); - if (!data) { - setIsInitialLoading(true); - } }, [refreshData]); useEffect(() => { @@ -161,12 +169,6 @@ export const AssetTypeList = ({ } }, [lastSetDataTimeStamp, previousLastSetDataTimeStamp, refreshData]); - useEffect(() => { - if (!previousData && data) { - setIsInitialLoading(false); - } - }, [previousData, data]); - const handleAssetTypeClick = (assetTypeId: string) => { onAssetTypeSelect(assetTypeId); }; diff --git a/src/components/Assets/AssetsFilter/index.tsx b/src/components/Assets/AssetsFilter/index.tsx index 96dc30a32..07fb9c1f4 100644 --- a/src/components/Assets/AssetsFilter/index.tsx +++ b/src/components/Assets/AssetsFilter/index.tsx @@ -17,6 +17,7 @@ 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"; @@ -24,11 +25,40 @@ import { AssetFilterCategory, AssetFilterQuery, AssetsFilterProps, - AssetsFiltersData + AssetsFiltersData, + GetAssetFiltersDataPayload } from "./types"; const PERSISTENCE_KEY = "assetsFilters"; +const getData = ({ + services, + operations, + insights, + assetScopeOption, + searchQuery +}: { + services: string[]; + operations: string[]; + insights: InsightType[]; + assetScopeOption: AssetScopeOption | null; + searchQuery: string; +}) => { + window.sendMessageToDigma({ + action: actions.GET_ASSET_FILTERS_DATA, + payload: { + query: { + services, + operations, + insights, + directOnly: Boolean(assetScopeOption?.isDirect), + scopedSpanCodeObjectId: assetScopeOption?.scopedSpanCodeObjectId, + ...(searchQuery.length > 0 ? { displayName: searchQuery } : {}) + } + } + }); +}; + const renderFilterCategory = ( category: AssetFilterCategory, icon: ComponentType, @@ -59,55 +89,52 @@ const renderFilterCategory = ( ); }; -export const AssetsFilter = ({ onApply, filters }: AssetsFilterProps) => { +export const AssetsFilter = ({ + onApply, + filters, + assetScopeOption, + searchQuery +}: AssetsFilterProps) => { const [data, setData] = useState<{ data: AssetsFiltersData | null }>(); const previousData = usePrevious(data); const [isOpen, setIsOpen] = useState(false); const previousIsOpen = usePrevious(isOpen); + const globallySelectedServices = useGlobalStore.use.selectedServices(); + const setGloballySelectedServices = useGlobalStore.use.setSelectedServices(); const [persistedFilters, setPersistedFilters] = usePersistence(PERSISTENCE_KEY, "project"); const previousPersistedFilters = usePrevious(persistedFilters); - const [selectedServices, setSelectedServices] = useState([]); + const scope = useGlobalStore.use.scope(); + const isServicesFilterEnabled = !scope?.span?.spanCodeObjectId; + const [selectedServices, setSelectedServices] = useState( + isServicesFilterEnabled ? globallySelectedServices ?? [] : [] + ); const [selectedEndpoints, setSelectedEndpoints] = useState([]); const [selectedConsumers, setSelectedConsumers] = useState([]); const [selectedInternals, setSelectedInternals] = useState([]); const [selectedInsights, setSelectedInsights] = useState([]); const environment = useGlobalStore.use.environment(); - const scope = useGlobalStore.use.scope(); const previousEnvironment = usePrevious(environment); const previousScope = usePrevious(scope); - const getData = ( - services: string[], - operations: string[], - insights: InsightType[] - ) => { - window.sendMessageToDigma({ - action: actions.GET_ASSET_FILTERS_DATA, - payload: { - query: { - services, - operations, - insights - } - } - }); - }; - + // Get data after filters have been rehydrated useEffect(() => { if ( isUndefined(previousPersistedFilters) && - previousPersistedFilters !== persistedFilters + !isUndefined(persistedFilters) ) { - getData( - persistedFilters?.services ?? selectedServices, - persistedFilters?.operations ?? [ + getData({ + services: isServicesFilterEnabled ? selectedServices : [], + operations: persistedFilters?.operations ?? [ ...selectedEndpoints, ...selectedConsumers, ...selectedInternals ], - (persistedFilters?.insights as InsightType[]) || selectedInsights - ); + insights: + (persistedFilters?.insights as InsightType[]) || selectedInsights, + assetScopeOption, + searchQuery + }); } }, [ previousPersistedFilters, @@ -116,7 +143,12 @@ export const AssetsFilter = ({ onApply, filters }: AssetsFilterProps) => { selectedEndpoints, selectedConsumers, selectedInternals, - selectedInsights + selectedInsights, + scope, + assetScopeOption, + searchQuery, + globallySelectedServices, + isServicesFilterEnabled ]); useEffect(() => { @@ -135,11 +167,11 @@ export const AssetsFilter = ({ onApply, filters }: AssetsFilterProps) => { }; }, []); + // Clear filters and get data when environment is changed useEffect(() => { if ( - (isEnvironment(previousEnvironment) && - previousEnvironment.id !== environment?.id) || - (previousScope && previousScope !== scope) + isEnvironment(previousEnvironment) && + previousEnvironment.id !== environment?.id ) { const defaultFilters = { services: [], @@ -147,28 +179,88 @@ export const AssetsFilter = ({ onApply, filters }: AssetsFilterProps) => { insights: [] }; setPersistedFilters(defaultFilters); + if (isServicesFilterEnabled) { + setGloballySelectedServices(defaultFilters.services); + } onApply(defaultFilters); - getData([], [], []); + getData({ + ...defaultFilters, + assetScopeOption, + searchQuery + }); } }, [ previousEnvironment, environment, setPersistedFilters, onApply, + assetScopeOption, + searchQuery, + isServicesFilterEnabled, + setGloballySelectedServices + ]); + + // Clear filters and get data when scope is changed, but keep selected services + useEffect(() => { + if (previousScope && previousScope !== scope) { + const newFilters = { + services: selectedServices, + operations: [], + insights: [] + }; + setPersistedFilters(newFilters); + if (isServicesFilterEnabled) { + setGloballySelectedServices(newFilters.services); + } + onApply(newFilters); + getData({ + ...newFilters, + assetScopeOption, + searchQuery + }); + } + }, [ + setPersistedFilters, + setGloballySelectedServices, + onApply, previousScope, - scope + scope, + assetScopeOption, + searchQuery, + selectedServices, + isServicesFilterEnabled ]); + // Get data when the popover is opened useEffect(() => { if (isOpen && !previousIsOpen) { - getData( - selectedServices, - [...selectedEndpoints, ...selectedConsumers, ...selectedInternals], - selectedInsights - ); + getData({ + services: isServicesFilterEnabled ? selectedServices : [], + operations: [ + ...selectedEndpoints, + ...selectedConsumers, + ...selectedInternals + ], + insights: selectedInsights, + assetScopeOption, + searchQuery + }); } - }, [isOpen, previousIsOpen]); + }, [ + isOpen, + previousIsOpen, + scope, + selectedConsumers, + selectedEndpoints, + selectedInsights, + selectedInternals, + selectedServices, + assetScopeOption, + searchQuery, + isServicesFilterEnabled + ]); + // Apply filters when data is loaded useEffect(() => { if (previousData === data || isNull(data?.data)) { return; @@ -224,6 +316,9 @@ export const AssetsFilter = ({ onApply, filters }: AssetsFilterProps) => { }; setPersistedFilters(filtersQuery); + if (isServicesFilterEnabled) { + setGloballySelectedServices(filtersQuery.services); + } onApply(filtersQuery); } }, [ @@ -236,9 +331,12 @@ export const AssetsFilter = ({ onApply, filters }: AssetsFilterProps) => { selectedConsumers, selectedInternals, selectedInsights, - setPersistedFilters + setPersistedFilters, + setGloballySelectedServices, + isServicesFilterEnabled ]); + // Apply filters when the popover is closed useEffect(() => { if (previousIsOpen && !isOpen) { const filtersQuery = { @@ -252,6 +350,9 @@ export const AssetsFilter = ({ onApply, filters }: AssetsFilterProps) => { }; onApply(filtersQuery); setPersistedFilters(filtersQuery); + if (isServicesFilterEnabled) { + setGloballySelectedServices(filtersQuery.services); + } sendTrackingEvent(trackingEvents.FILTER_APPLIED); } }, [ @@ -263,11 +364,19 @@ export const AssetsFilter = ({ onApply, filters }: AssetsFilterProps) => { selectedInsights, selectedInternals, selectedServices, - setPersistedFilters + setPersistedFilters, + setGloballySelectedServices, + isServicesFilterEnabled ]); const handleClearFiltersButtonClick = () => { - getData([], [], []); + getData({ + services: isServicesFilterEnabled ? [] : selectedServices, + insights: [], + operations: [], + assetScopeOption, + searchQuery + }); }; const handleSelectedItemsChange = ( @@ -300,7 +409,13 @@ export const AssetsFilter = ({ onApply, filters }: AssetsFilterProps) => { break; } - getData(services, [...endpoints, ...consumers, ...internals], insights); + getData({ + services, + operations: [...endpoints, ...consumers, ...internals], + insights, + assetScopeOption, + searchQuery + }); }; const servicesCategory = data?.data?.categories.find( @@ -340,7 +455,7 @@ export const AssetsFilter = ({ onApply, filters }: AssetsFilterProps) => { }; const selectedFilters = [ - ...selectedServices, + ...(isServicesFilterEnabled ? selectedServices : []), ...selectedEndpoints, ...selectedConsumers, ...selectedInternals, @@ -353,13 +468,17 @@ export const AssetsFilter = ({ onApply, filters }: AssetsFilterProps) => { content={ Filters - Services - {renderFilterCategory( - servicesCategory, - WrenchIcon, - selectedServices.length > 0 ? "Services" : "All", - selectedServices, - handleSelectedItemsChange + {isServicesFilterEnabled && ( + <> + Services + {renderFilterCategory( + servicesCategory, + WrenchIcon, + selectedServices.length > 0 ? "Services" : "All", + selectedServices, + handleSelectedItemsChange + )} + )} Operations {renderFilterCategory( diff --git a/src/components/Assets/AssetsFilter/types.ts b/src/components/Assets/AssetsFilter/types.ts index 88b186e66..c4990b541 100644 --- a/src/components/Assets/AssetsFilter/types.ts +++ b/src/components/Assets/AssetsFilter/types.ts @@ -1,6 +1,10 @@ +import { AssetScopeOption } from "../AssetsViewScopeConfiguration/types"; + export interface AssetsFilterProps { filters?: AssetFilterQuery; onApply: (filter: AssetFilterQuery) => void; + assetScopeOption: AssetScopeOption | null; + searchQuery: string; } export interface AssetFilterEntry { @@ -23,4 +27,11 @@ export interface AssetFilterQuery { services: string[]; operations: string[]; insights: string[]; + scopedSpanCodeObjectId?: string; + directOnly?: boolean; + displayName?: string; +} + +export interface GetAssetFiltersDataPayload { + query: AssetFilterQuery; } diff --git a/src/components/Assets/index.tsx b/src/components/Assets/index.tsx index a304b978c..102f7e7ba 100644 --- a/src/components/Assets/index.tsx +++ b/src/components/Assets/index.tsx @@ -174,6 +174,9 @@ export const Assets = () => { Boolean( filters && - [...filters.insights, ...filters.operations, ...filters.services].length > - 0 + [ + ...filters.insights, + ...filters.operations, + ...(isServicesFilterEnabled ? filters.services : []) + ].length > 0 ) || searchQuery.length > 0; diff --git a/src/components/Insights/InsightsCatalog/index.tsx b/src/components/Insights/InsightsCatalog/index.tsx index 1de4f53fb..7a3b7901a 100644 --- a/src/components/Insights/InsightsCatalog/index.tsx +++ b/src/components/Insights/InsightsCatalog/index.tsx @@ -77,7 +77,7 @@ export const InsightsCatalog = ({ const setSorting = useInsightsStore.use.setSorting(); const filters = useInsightsStore.use.filters(); const filteredInsightTypes = useInsightsStore.use.filteredInsightTypes(); - const filteredServices = useInsightsStore.use.filteredServices(); + const selectedServices = useGlobalStore.use.selectedServices(); const data = useInsightsStore.use.data(); const insights = data?.insights ?? []; const totalCount = data?.totalCount ?? 0; @@ -113,11 +113,14 @@ export const InsightsCatalog = ({ PROMOTION_COMPLETED_PERSISTENCE_KEY, "application" ); + const isServicesFilterEnabled = !scopeSpanCodeObjectId; const appliedFilterCount = filters.length + (filteredInsightTypes.length > 0 ? 1 : 0) + - (filteredServices.length > 0 ? 1 : 0); + (isServicesFilterEnabled && selectedServices && selectedServices.length > 0 + ? 1 + : 0); const areSpanEnvironmentsEnabled = getFeatureFlagValue( backendInfo, diff --git a/src/components/Insights/Issues/IssuesFilter/index.tsx b/src/components/Insights/Issues/IssuesFilter/index.tsx index 6fc85a596..b139bb732 100644 --- a/src/components/Insights/Issues/IssuesFilter/index.tsx +++ b/src/components/Insights/Issues/IssuesFilter/index.tsx @@ -24,10 +24,10 @@ import { trackingEvents } from "./tracking"; export const IssuesFilter = () => { const [isOpen, setIsOpen] = useState(false); const filteredInsightTypes = useInsightsStore.use.filteredInsightTypes(); - const filteredServices = useInsightsStore.use.filteredServices(); + const selectedServices = useGlobalStore.use.selectedServices(); const setFilteredInsightTypes = useInsightsStore.use.setFilteredInsightTypes(); - const setFilteredServices = useInsightsStore.use.setFilteredServices(); + const setSelectedServices = useGlobalStore.use.setSelectedServices(); const filters = useInsightsStore.use.filters(); const backendInfo = useGlobalStore.use.backendInfo(); const setFilters = useInsightsStore.use.setFilters(); @@ -38,6 +38,16 @@ export const IssuesFilter = () => { const isUnreadOnly = useMemo(() => filters.includes("unread"), [filters]); const { data } = useIssuesFilters(); const previousData = usePrevious(data); + const scope = useGlobalStore.use.scope(); + const scopeSpanCodeObjectId = scope?.span?.spanCodeObjectId; + const isServicesFilterEnabled = + Boolean( + getFeatureFlagValue(backendInfo, FeatureFlag.ARE_ISSUES_FILTERS_ENABLED) + ) && !scopeSpanCodeObjectId; + const filteredServices = useMemo( + () => selectedServices ?? [], + [selectedServices] + ); useEffect(() => { if (previousData && previousData !== data) { @@ -51,13 +61,13 @@ export const IssuesFilter = () => { } } - if (filteredServices.length > 0) { + if (isServicesFilterEnabled && filteredServices.length > 0) { const newSelection = filteredServices.filter((e) => Boolean(data?.services?.find((x) => x === e)) ); if (newSelection.length !== filteredServices.length) { - setFilteredServices(newSelection); + setSelectedServices(newSelection); } } } @@ -67,8 +77,9 @@ export const IssuesFilter = () => { filteredInsightTypes, setFilteredInsightTypes, filters, - setFilteredServices, - filteredServices + setSelectedServices, + filteredServices, + isServicesFilterEnabled ]); const handleClearFiltersButtonClick = () => { @@ -77,7 +88,10 @@ export const IssuesFilter = () => { ); setFilteredInsightTypes([]); setFilters([]); - setFilteredServices([]); + + if (isServicesFilterEnabled) { + setSelectedServices([]); + } }; const handleIssueTypesChange = (value: string | string[]) => { @@ -93,7 +107,7 @@ export const IssuesFilter = () => { filterType: "service" }); const newFilteredServices = Array.isArray(value) ? value : [value]; - setFilteredServices(newFilteredServices); + setSelectedServices(newFilteredServices); }; const handleCloseButtonClick = () => { @@ -170,7 +184,7 @@ export const IssuesFilter = () => { readStatusFilterOptions.find((x) => x.selected)?.label ?? "All"; const selectedFiltersCount = (filteredInsightTypes.length > 0 ? 1 : 0) + - (filteredServices.length > 0 ? 1 : 0) + + (isServicesFilterEnabled && filteredServices.length > 0 ? 1 : 0) + (isCriticalOnly ? 1 : 0) + (isUnreadOnly ? 1 : 0); @@ -225,12 +239,7 @@ export const IssuesFilter = () => { )} showSelectedState={isUnreadOnly} /> - {Boolean( - getFeatureFlagValue( - backendInfo, - FeatureFlag.ARE_ISSUES_FILTERS_ENABLED - ) - ) && ( + {isServicesFilterEnabled && ( <> Services