diff --git a/src/components/Assets/AssetList/index.tsx b/src/components/Assets/AssetList/index.tsx index f7d3d4c66..c2bc39f0f 100644 --- a/src/components/Assets/AssetList/index.tsx +++ b/src/components/Assets/AssetList/index.tsx @@ -1,4 +1,11 @@ -import { useContext, useEffect, useMemo, useRef, useState } from "react"; +import { + useCallback, + useContext, + useEffect, + useMemo, + useRef, + useState +} from "react"; import { DefaultTheme, useTheme } from "styled-components"; import { dispatcher } from "../../../dispatcher"; import { getFeatureFlagValue } from "../../../featureFlags"; @@ -196,16 +203,6 @@ export const AssetList = (props: AssetListProps) => { const previousServices = usePrevious(props.services); const previousFilters = usePrevious(props.filters); const previousViewScope = usePrevious(props.scopeViewOptions); - - const entries = data?.data || []; - - const assetTypeInfo = getAssetTypeInfo(props.assetTypeId); - - const isOverallImpactHidden = getFeatureFlagValue( - config, - FeatureFlag.IS_ASSETS_OVERALL_IMPACT_HIDDEN - ); - const isComplexFilterEnabled = useMemo( () => Boolean( @@ -217,6 +214,39 @@ export const AssetList = (props: AssetListProps) => { [config] ); + const refreshData = useCallback(() => { + getData( + props.assetTypeId, + page, + sorting, + props.searchQuery, + props.filters, + props.services, + isComplexFilterEnabled, + props.scopeViewOptions?.isDirect, + props.scopeViewOptions?.scopedSpanCodeObjectId + ); + }, [ + isComplexFilterEnabled, + page, + props.assetTypeId, + props.filters, + props.scopeViewOptions?.isDirect, + props.scopeViewOptions?.scopedSpanCodeObjectId, + props.searchQuery, + props.services, + sorting + ]); + + const entries = data?.data || []; + + const assetTypeInfo = getAssetTypeInfo(props.assetTypeId); + + const isOverallImpactHidden = getFeatureFlagValue( + config, + FeatureFlag.IS_ASSETS_OVERALL_IMPACT_HIDDEN + ); + const areAnyFiltersApplied = checkIfAnyFiltersApplied( isComplexFilterEnabled, props.filters, @@ -231,17 +261,11 @@ export const AssetList = (props: AssetListProps) => { : Object.values(SORTING_CRITERION); useEffect(() => { - getData( - props.assetTypeId, - page, - sorting, - props.searchQuery, - props.filters, - props.services, - isComplexFilterEnabled, - props.scopeViewOptions?.isDirect, - props.scopeViewOptions?.scopedSpanCodeObjectId - ); + props.setRefresher(refreshData); + }, [refreshData]); + + useEffect(() => { + refreshData(); setIsInitialLoading(true); const handleAssetsData = (data: unknown, timeStamp: number) => { @@ -273,67 +297,36 @@ export const AssetList = (props: AssetListProps) => { (previousFilters && previousFilters !== props.filters) || previousViewScope !== props.scopeViewOptions ) { - getData( - props.assetTypeId, - page, - sorting, - props.searchQuery, - props.filters, - props.services, - isComplexFilterEnabled, - props.scopeViewOptions?.isDirect, - props.scopeViewOptions?.scopedSpanCodeObjectId - ); + refreshData(); } }, [ - props.assetTypeId, - previousAssetTypeId, - previousSearchQuery, - props.searchQuery, - previousSorting, - sorting, - previousPage, + config.environment?.originalName, page, + previousAssetTypeId, previousEnvironment, - config.environment, - props.services, - previousServices, - props.filters, previousFilters, - isComplexFilterEnabled, + previousPage, + previousSearchQuery, + previousServices, + previousSorting, previousViewScope, - props.scopeViewOptions + props.assetTypeId, + props.filters, + props.scopeViewOptions, + props.searchQuery, + props.services, + refreshData, + sorting ]); useEffect(() => { if (previousLastSetDataTimeStamp !== lastSetDataTimeStamp) { window.clearTimeout(refreshTimerId.current); refreshTimerId.current = window.setTimeout(() => { - getData( - props.assetTypeId, - page, - sorting, - props.searchQuery, - props.filters, - props.services, - isComplexFilterEnabled, - props.scopeViewOptions?.isDirect, - props.scopeViewOptions?.scopedSpanCodeObjectId - ); + refreshData(); }, REFRESH_INTERVAL); } - }, [ - lastSetDataTimeStamp, - previousLastSetDataTimeStamp, - props.assetTypeId, - page, - sorting, - props.searchQuery, - props.services, - props.filters, - isComplexFilterEnabled, - props.scopeViewOptions - ]); + }, [lastSetDataTimeStamp, previousLastSetDataTimeStamp, refreshData]); useEffect(() => { if (props.data) { diff --git a/src/components/Assets/AssetList/types.ts b/src/components/Assets/AssetList/types.ts index e11b816d6..b8e34fa21 100644 --- a/src/components/Assets/AssetList/types.ts +++ b/src/components/Assets/AssetList/types.ts @@ -10,6 +10,7 @@ export interface AssetListProps { filters?: AssetFilterQuery; searchQuery: string; scopeViewOptions: AssetScopeOption | null; + setRefresher: (refresher: () => void) => void; } export enum SORTING_CRITERION { diff --git a/src/components/Assets/AssetTypeList/index.tsx b/src/components/Assets/AssetTypeList/index.tsx index 7b0b7e641..f5179062a 100644 --- a/src/components/Assets/AssetTypeList/index.tsx +++ b/src/components/Assets/AssetTypeList/index.tsx @@ -1,4 +1,11 @@ -import { useContext, useEffect, useMemo, useRef, useState } from "react"; +import { + useCallback, + useContext, + useEffect, + useMemo, + useRef, + useState +} from "react"; import { dispatcher } from "../../../dispatcher"; import { getFeatureFlagValue } from "../../../featureFlags"; import { usePrevious } from "../../../hooks/usePrevious"; @@ -69,7 +76,6 @@ export const AssetTypeList = (props: AssetTypeListProps) => { const previousFilters = usePrevious(props.filters); const previousSearchQuery = usePrevious(props.searchQuery); const previousViewScope = usePrevious(props.scopeViewOptions); - const isComplexFilterEnabled = useMemo( () => Boolean( @@ -81,6 +87,30 @@ export const AssetTypeList = (props: AssetTypeListProps) => { [config] ); + const refreshData = useCallback( + () => + getData( + props.filters, + props.services, + props.searchQuery, + isComplexFilterEnabled, + props.scopeViewOptions?.isDirect, + props.scopeViewOptions?.scopedSpanCodeObjectId + ), + [ + isComplexFilterEnabled, + props.filters, + props.scopeViewOptions?.isDirect, + props.scopeViewOptions?.scopedSpanCodeObjectId, + props.searchQuery, + props.services + ] + ); + + useEffect(() => { + props.setRefresher(refreshData); + }, [refreshData]); + const areAnyFiltersApplied = checkIfAnyFiltersApplied( isComplexFilterEnabled, props.filters, @@ -89,14 +119,7 @@ export const AssetTypeList = (props: AssetTypeListProps) => { ); useEffect(() => { - getData( - props.filters, - props.services, - props.searchQuery, - isComplexFilterEnabled, - props.scopeViewOptions?.isDirect, - props.scopeViewOptions?.scopedSpanCodeObjectId - ); + refreshData(); setIsInitialLoading(true); const handleCategoriesData = (data: unknown, timeStamp: number) => { @@ -130,52 +153,30 @@ export const AssetTypeList = (props: AssetTypeListProps) => { previousSearchQuery !== props.searchQuery) || previousViewScope !== props.scopeViewOptions ) { - getData( - props.filters, - props.services, - props.searchQuery, - isComplexFilterEnabled, - props.scopeViewOptions?.isDirect, - props.scopeViewOptions?.scopedSpanCodeObjectId - ); + refreshData(); } }, [ + config.environment?.originalName, previousEnvironment, - config.environment, - previousServices, - props.services, previousFilters, - props.filters, previousSearchQuery, - props.searchQuery, - isComplexFilterEnabled, + previousServices, + previousViewScope, + props.filters, props.scopeViewOptions, - previousViewScope + props.searchQuery, + props.services, + refreshData ]); useEffect(() => { if (previousLastSetDataTimeStamp !== lastSetDataTimeStamp) { window.clearTimeout(refreshTimerId.current); refreshTimerId.current = window.setTimeout(() => { - getData( - props.filters, - props.services, - props.searchQuery, - isComplexFilterEnabled, - props.scopeViewOptions?.isDirect, - props.scopeViewOptions?.scopedSpanCodeObjectId - ); + refreshData(); }, REFRESH_INTERVAL); } - }, [ - props.services, - previousLastSetDataTimeStamp, - lastSetDataTimeStamp, - props.filters, - props.searchQuery, - isComplexFilterEnabled, - props.scopeViewOptions - ]); + }, [lastSetDataTimeStamp, previousLastSetDataTimeStamp, refreshData]); useEffect(() => { if (props.data) { diff --git a/src/components/Assets/AssetTypeList/types.ts b/src/components/Assets/AssetTypeList/types.ts index 1c2bd5576..d54f1d8a6 100644 --- a/src/components/Assets/AssetTypeList/types.ts +++ b/src/components/Assets/AssetTypeList/types.ts @@ -10,6 +10,7 @@ export interface AssetTypeListProps { filters?: AssetFilterQuery; searchQuery: string; scopeViewOptions: AssetScopeOption | null; + setRefresher: (refresher: () => void) => void; } export interface AssetCategoriesData { diff --git a/src/components/Assets/index.tsx b/src/components/Assets/index.tsx index 630d8b1a8..6bd88a3aa 100644 --- a/src/components/Assets/index.tsx +++ b/src/components/Assets/index.tsx @@ -16,6 +16,8 @@ import { FeatureFlag } from "../../types"; import { ConfigContext } from "../common/App/ConfigContext"; import { EmptyState } from "../common/EmptyState"; import { SearchInput } from "../common/SearchInput"; +import { RefreshIcon } from "../common/icons/16px/RefreshIcon"; +import { Tooltip } from "../common/v3/Tooltip"; import { AssetList } from "./AssetList"; import { AssetTypeList } from "./AssetTypeList"; import { AssetsFilter } from "./AssetsFilter"; @@ -26,6 +28,7 @@ import { NoDataMessage } from "./NoDataMessage"; import { ServicesFilter } from "./ServicesFilter"; import { actions } from "./actions"; import * as s from "./styles"; +import { DataRefresher } from "./types"; export const Assets = () => { const [selectedAssetTypeId, setSelectedAssetTypeId] = useState( @@ -39,6 +42,10 @@ export const Assets = () => { const [selectedFilters, setSelectedFilters] = useState(); const config = useContext(ConfigContext); const previousScope = usePrevious(config.scope?.span); + const [assetTypeListDataRefresher, setAssetTypeListRefresher] = + useState(null); + const [assetListDataRefresher, setAssetListRefresher] = + useState(null); const isServiceFilterVisible = getFeatureFlagValue( config, @@ -105,6 +112,14 @@ export const Assets = () => { setSelectedFilters(filters); }; + const handleRefresh = () => { + const currentRefresher = !selectedAssetTypeId + ? assetTypeListDataRefresher + : assetListDataRefresher; + + currentRefresher && currentRefresher.refresh(); + }; + const renderContent = () => { if (isBackendUpgradeMessageVisible) { return ( @@ -137,6 +152,9 @@ export const Assets = () => { filters={selectedFilters} searchQuery={debouncedSearchInputValue} scopeViewOptions={assetScopeOption} + setRefresher={(refresher) => { + setAssetTypeListRefresher({ refresh: refresher }); + }} /> ); } @@ -149,6 +167,9 @@ export const Assets = () => { filters={selectedFilters} searchQuery={debouncedSearchInputValue} scopeViewOptions={assetScopeOption} + setRefresher={(refresher) => { + setAssetListRefresher({ refresh: refresher }); + }} /> ); }; @@ -177,6 +198,13 @@ export const Assets = () => { /> ) )} + + + {config.scope && config.scope.span && ( @@ -189,7 +217,6 @@ export const Assets = () => { )} - {renderContent()} ); diff --git a/src/components/Assets/styles.ts b/src/components/Assets/styles.ts index 93aaf3cdd..e305121ce 100644 --- a/src/components/Assets/styles.ts +++ b/src/components/Assets/styles.ts @@ -1,5 +1,6 @@ import styled from "styled-components"; import { grayScale } from "../common/App/v2colors"; +import { Button } from "../common/v3/Button"; export const Container = styled.div` height: 100%; @@ -122,3 +123,22 @@ export const UpgradeMessage = styled.div` text-align: center; gap: 8px; `; + +export const RefreshButton = styled(Button)` + color: ${({ theme }) => theme.colors.v3.icon.tertiary}; + border: 1px solid ${({ theme }) => theme.colors.stroke.primary}; + background: ${({ theme }) => { + switch (theme.mode) { + case "light": + return grayScale[50]; + case "dark": + case "dark-jetbrains": + return grayScale[1000]; + } + }}; + + &:hover:enabled { + color: ${({ theme }) => theme.colors.v3.icon.white}; + background: none; + } +`; diff --git a/src/components/Assets/types.ts b/src/components/Assets/types.ts index 6ba235c99..95ea05cb0 100644 --- a/src/components/Assets/types.ts +++ b/src/components/Assets/types.ts @@ -1,3 +1,7 @@ export interface ServiceData { services: string[]; } + +export interface DataRefresher { + refresh: () => void; +}