diff --git a/.vscode/settings.json b/.vscode/settings.json index 765ecd323..554d6a084 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,5 +4,6 @@ "source.organizeImports": "explicit" }, "editor.defaultFormatter": "esbenp.prettier-vscode", - "stylelint.validate": ["typescript"] + "stylelint.validate": ["typescript"], + "cSpell.words": ["uuidv"] } diff --git a/package-lock.json b/package-lock.json index 1d21909d9..776f04ff8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,7 +26,8 @@ "react-transition-group": "^4.4.5", "recharts": "^2.6.2", "semver": "^7.5.4", - "styled-components": "^6.1.0" + "styled-components": "^6.1.0", + "uuid": "^9.0.1" }, "devDependencies": { "@babel/core": "^7.23.2", @@ -48,6 +49,7 @@ "@types/react-syntax-highlighter": "^15.5.7", "@types/react-transition-group": "^4.4.5", "@types/semver": "^7.5.6", + "@types/uuid": "^9.0.8", "@typescript-eslint/eslint-plugin": "^5.49.0", "@typescript-eslint/parser": "^5.49.0", "babel-loader": "^9.1.3", @@ -6644,6 +6646,12 @@ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.7.tgz", "integrity": "sha512-cputDpIbFgLUaGQn6Vqg3/YsJwxUwHLO13v3i5ouxT4lat0khip9AEWxtERujXV9wxIB1EyF97BSJFt6vpdI8g==" }, + "node_modules/@types/uuid": { + "version": "9.0.8", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", + "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==", + "dev": true + }, "node_modules/@types/yargs": { "version": "16.0.5", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", @@ -18611,7 +18619,6 @@ "version": "9.0.1", "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "dev": true, "funding": [ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" @@ -23675,6 +23682,12 @@ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.7.tgz", "integrity": "sha512-cputDpIbFgLUaGQn6Vqg3/YsJwxUwHLO13v3i5ouxT4lat0khip9AEWxtERujXV9wxIB1EyF97BSJFt6vpdI8g==" }, + "@types/uuid": { + "version": "9.0.8", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", + "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==", + "dev": true + }, "@types/yargs": { "version": "16.0.5", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", @@ -32395,8 +32408,7 @@ "uuid": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "dev": true + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==" }, "v8-compile-cache-lib": { "version": "3.0.1", diff --git a/package.json b/package.json index c8ed7b83e..0d90331e8 100644 --- a/package.json +++ b/package.json @@ -73,6 +73,7 @@ "@types/react-syntax-highlighter": "^15.5.7", "@types/react-transition-group": "^4.4.5", "@types/semver": "^7.5.6", + "@types/uuid": "^9.0.8", "@typescript-eslint/eslint-plugin": "^5.49.0", "@typescript-eslint/parser": "^5.49.0", "babel-loader": "^9.1.3", @@ -120,6 +121,7 @@ "react-transition-group": "^4.4.5", "recharts": "^2.6.2", "semver": "^7.5.4", - "styled-components": "^6.1.0" + "styled-components": "^6.1.0", + "uuid": "^9.0.1" } } diff --git a/src/components/Assets/AssetList/index.tsx b/src/components/Assets/AssetList/index.tsx index 5666a0079..f7d3d4c66 100644 --- a/src/components/Assets/AssetList/index.tsx +++ b/src/components/Assets/AssetList/index.tsx @@ -271,7 +271,7 @@ export const AssetList = (props: AssetListProps) => { (Array.isArray(previousServices) && previousServices !== props.services) || (previousFilters && previousFilters !== props.filters) || - (previousViewScope && previousViewScope !== props.scopeViewOptions) + previousViewScope !== props.scopeViewOptions ) { getData( props.assetTypeId, diff --git a/src/components/Assets/AssetList/types.ts b/src/components/Assets/AssetList/types.ts index 83aa8a4b6..c7c881b18 100644 --- a/src/components/Assets/AssetList/types.ts +++ b/src/components/Assets/AssetList/types.ts @@ -9,7 +9,7 @@ export interface AssetListProps { services?: string[]; filters?: AssetFilterQuery; searchQuery: string; - scopeViewOptions?: AssetScopeOption; + scopeViewOptions: AssetScopeOption | null; } export enum SORTING_CRITERION { diff --git a/src/components/Assets/AssetTypeList/index.tsx b/src/components/Assets/AssetTypeList/index.tsx index d4a3fab5a..a11ff7b81 100644 --- a/src/components/Assets/AssetTypeList/index.tsx +++ b/src/components/Assets/AssetTypeList/index.tsx @@ -123,7 +123,7 @@ export const AssetTypeList = (props: AssetTypeListProps) => { (previousFilters && previousFilters !== props.filters) || (isString(previousSearchQuery) && previousSearchQuery !== props.searchQuery) || - (previousViewScope && previousViewScope !== props.scopeViewOptions) + previousViewScope !== props.scopeViewOptions ) { getData( props.filters, @@ -144,7 +144,8 @@ export const AssetTypeList = (props: AssetTypeListProps) => { previousSearchQuery, props.searchQuery, isComplexFilterEnabled, - props.scopeViewOptions + props.scopeViewOptions, + previousViewScope ]); useEffect(() => { diff --git a/src/components/Assets/AssetTypeList/types.ts b/src/components/Assets/AssetTypeList/types.ts index 7ded73ad6..a92730ba8 100644 --- a/src/components/Assets/AssetTypeList/types.ts +++ b/src/components/Assets/AssetTypeList/types.ts @@ -7,7 +7,7 @@ export interface AssetTypeListProps { services?: string[]; filters?: AssetFilterQuery; searchQuery: string; - scopeViewOptions?: AssetScopeOption; + scopeViewOptions: AssetScopeOption | null; } export interface AssetCategoriesData { diff --git a/src/components/Assets/AssetsViewScopeConfiguration/index.tsx b/src/components/Assets/AssetsViewScopeConfiguration/index.tsx index 94e4ba168..6204b464e 100644 --- a/src/components/Assets/AssetsViewScopeConfiguration/index.tsx +++ b/src/components/Assets/AssetsViewScopeConfiguration/index.tsx @@ -1,5 +1,4 @@ -import { useContext, useEffect, useState } from "react"; -import { ConfigContext } from "../../common/App/ConfigContext"; +import { useEffect, useState } from "react"; import { ToggleSwitch } from "../../common/ToggleSwitch"; import * as s from "./styles"; import { AssetsViewConfigurationProps as AssetsViewScopeConfigurationProps } from "./types"; @@ -9,11 +8,10 @@ export const AssetsViewScopeConfiguration = ( ) => { const [isEntry, setIsEntry] = useState(false); const [isDirect, setIsDirect] = useState(false); - const { scope } = useContext(ConfigContext); + const scope = props.currentScope; useEffect(() => { const isEntryPoint = !scope || scope.span?.role === "Entry"; - props.onAssetViewChanged({ scopedSpanCodeObjectId: scope?.span?.spanCodeObjectId, isDirect: !isEntryPoint diff --git a/src/components/Assets/AssetsViewScopeConfiguration/types.ts b/src/components/Assets/AssetsViewScopeConfiguration/types.ts index 001b32b03..09e48dcce 100644 --- a/src/components/Assets/AssetsViewScopeConfiguration/types.ts +++ b/src/components/Assets/AssetsViewScopeConfiguration/types.ts @@ -1,5 +1,8 @@ +import { Scope } from "../../common/App/types"; + export interface AssetsViewConfigurationProps { - onAssetViewChanged: (isDirect: AssetScopeOption) => void; + onAssetViewChanged: (assetViewScope: AssetScopeOption) => void; + currentScope: Scope; } export interface AssetScopeOption { diff --git a/src/components/Assets/index.tsx b/src/components/Assets/index.tsx index cf6f6bdad..f744880fa 100644 --- a/src/components/Assets/index.tsx +++ b/src/components/Assets/index.tsx @@ -1,6 +1,7 @@ import { ChangeEvent, useContext, + useEffect, useLayoutEffect, useMemo, useState @@ -33,7 +34,8 @@ export const Assets = () => { const [searchInputValue, setSearchInputValue] = useState(""); const debouncedSearchInputValue = useDebounce(searchInputValue, 1000); const [selectedServices, setSelectedServices] = useState(); - const [assetScopeOption, setAssetScopeOption] = useState(); + const [assetScopeOption, setAssetScopeOption] = + useState(null); const [selectedFilters, setSelectedFilters] = useState(); const config = useContext(ConfigContext); @@ -68,6 +70,12 @@ export const Assets = () => { }); }, []); + useEffect(() => { + if (!config.scope?.span) { + setAssetScopeOption(null); + } + }, [config.scope]); + const handleBackButtonClick = () => { setSelectedAssetTypeId(null); }; @@ -167,6 +175,7 @@ export const Assets = () => { {config.scope && config.scope.span && ( { setAssetScopeOption(val); }} diff --git a/src/components/Insights/InsightsCatalog/index.tsx b/src/components/Insights/InsightsCatalog/index.tsx index 29a7c98fc..ae9aa3760 100644 --- a/src/components/Insights/InsightsCatalog/index.tsx +++ b/src/components/Insights/InsightsCatalog/index.tsx @@ -2,7 +2,6 @@ import { useEffect, useState } from "react"; import { usePrevious } from "../../../hooks/usePrevious"; import { isNumber } from "../../../typeGuards/isNumber"; -import { isString } from "../../../typeGuards/isString"; import { Pagination } from "../../common/Pagination"; import { SearchInput } from "../../common/SearchInput"; import { SortingSelector } from "../../common/SortingSelector"; @@ -40,8 +39,7 @@ export const InsightsCatalog = (props: InsightsCatalogProps) => { if ( (isNumber(previousPage) && previousPage !== page) || (previousSorting && previousSorting !== sorting) || - (isString(previousSearchQuery) && - previousSearchQuery !== searchInputValue) + previousSearchQuery !== searchInputValue ) { props.onQueryChange({ page, @@ -90,23 +88,26 @@ export const InsightsCatalog = (props: InsightsCatalogProps) => { insights={insights} isFilteringEnabled={searchInputValue !== null} onJiraTicketCreate={onJiraTicketCreate} + onRefresh={props.onRefresh} /> - - - Showing{" "} - - {pageStartItemNumber} - {pageEndItemNumber} - {" "} - of {totalCount} - - - + {totalCount > 0 && ( + + + Showing{" "} + + {pageStartItemNumber} - {pageEndItemNumber} + {" "} + of {totalCount} + + + + )} ); }; diff --git a/src/components/Insights/InsightsCatalog/types.ts b/src/components/Insights/InsightsCatalog/types.ts index 56245d1a2..383f2f62d 100644 --- a/src/components/Insights/InsightsCatalog/types.ts +++ b/src/components/Insights/InsightsCatalog/types.ts @@ -9,6 +9,7 @@ export interface InsightsCatalogProps { ) => void; onQueryChange: (query: InsightsQuery) => void; defaultQuery: InsightsQuery; + onRefresh: () => void; } export enum SORTING_CRITERION { diff --git a/src/components/Insights/InsightsPage/index.tsx b/src/components/Insights/InsightsPage/index.tsx index 3f2aa8de2..6848cd85c 100644 --- a/src/components/Insights/InsightsPage/index.tsx +++ b/src/components/Insights/InsightsPage/index.tsx @@ -1,4 +1,5 @@ import { useEffect } from "react"; +import { v4 as uuidv4 } from "uuid"; import { usePersistence } from "../../../hooks/usePersistence"; import { usePrevious } from "../../../hooks/usePrevious"; import { trackingEvents as globalTrackingEvents } from "../../../trackingEvents"; @@ -83,7 +84,8 @@ const renderInsightCard = ( spanCodeObjectId: string | undefined, event?: string ) => void, - isJiraHintEnabled: boolean + isJiraHintEnabled: boolean, + onRefresh: () => void ): JSX.Element | undefined => { const handleErrorSelect = (errorId: string, insightType: InsightType) => { sendTrackingEvent(globalTrackingEvents.USER_ACTION, { @@ -185,59 +187,50 @@ const renderInsightCard = ( }); }; - const handleRefresh = (insightType: InsightType) => { - window.sendMessageToDigma({ - action: actions.REFRESH_ALL, - payload: { - insightType - } - }); - }; - if (isSpanDurationsInsight(insight)) { return ( ); } if (isSpanDurationBreakdownInsight(insight)) { return ( ); } if (isSpanUsagesInsight(insight)) { return ( ); } if (isSpanEndpointBottleneckInsight(insight)) { return ( @@ -246,11 +239,11 @@ const renderInsightCard = ( if (isEndpointSlowestSpansInsight(insight)) { return ( @@ -259,10 +252,10 @@ const renderInsightCard = ( if (isSlowEndpointInsight(insight)) { return ( ); } @@ -273,34 +266,34 @@ const renderInsightCard = ( ) { return ( ); } if (isCodeObjectErrorsInsight(insight)) { return ( ); } if (isEndpointSuspectedNPlusOneInsight(insight)) { return ( @@ -309,12 +302,12 @@ const renderInsightCard = ( if (isSpanNPlusOneInsight(insight)) { return ( @@ -323,13 +316,13 @@ const renderInsightCard = ( if (isSpanScalingBadlyInsight(insight)) { return ( ); } @@ -343,18 +336,18 @@ const renderInsightCard = ( } onRecalculate={handleRecalculate} - onRefresh={handleRefresh} + onRefresh={onRefresh} /> ); } if (isEndpointDurationSlowdownInsight(insight)) { return ( ); } @@ -362,10 +355,10 @@ const renderInsightCard = ( if (isEndpointBreakdownInsight(insight)) { return ( ); } @@ -373,11 +366,11 @@ const renderInsightCard = ( if (isSpanScalingWellInsight(insight)) { return ( ); } @@ -385,11 +378,11 @@ const renderInsightCard = ( if (isSpanScalingInsufficientDataInsight(insight)) { return ( ); } @@ -397,12 +390,12 @@ const renderInsightCard = ( if (isSessionInViewEndpointInsight(insight)) { return ( ); } @@ -410,12 +403,12 @@ const renderInsightCard = ( if (isChattyApiEndpointInsight(insight)) { return ( ); } @@ -423,11 +416,11 @@ const renderInsightCard = ( if (isEndpointHighNumberOfQueriesInsight(insight)) { return ( @@ -437,10 +430,10 @@ const renderInsightCard = ( if (isSpanNexusInsight(insight)) { return ( ); } @@ -448,12 +441,12 @@ const renderInsightCard = ( if (isSpanQueryOptimizationInsight(insight)) { return ( @@ -463,12 +456,12 @@ const renderInsightCard = ( if (isEndpointQueryOptimizationInsight(insight)) { return ( ); @@ -525,7 +518,8 @@ export const InsightsPage = (props: InsightPageProps) => { return renderInsightCard( insight, handleShowJiraTicket, - j === insightIndexWithJiraHint + j === insightIndexWithJiraHint, + props.onRefresh ); }) ) : props.isFilteringEnabled ? ( diff --git a/src/components/Insights/InsightsPage/types.ts b/src/components/Insights/InsightsPage/types.ts index 2736fb55a..c4e8b5f9f 100644 --- a/src/components/Insights/InsightsPage/types.ts +++ b/src/components/Insights/InsightsPage/types.ts @@ -7,6 +7,7 @@ export interface InsightPageProps { insight: GenericCodeObjectInsight, spanCodeObjectId?: string ) => void; + onRefresh: () => void; } export interface isInsightJiraTicketHintShownPayload { diff --git a/src/components/Insights/common/useInsightsData.ts b/src/components/Insights/common/useInsightsData.ts index 6e818879a..2b6867c6f 100644 --- a/src/components/Insights/common/useInsightsData.ts +++ b/src/components/Insights/common/useInsightsData.ts @@ -46,12 +46,13 @@ export const useInsightsData = (props: UseInsightDataProps) => { const previousLastSetDataTimeStamp = usePrevious(lastSetDataTimeStamp); const refreshTimerId = useRef(); const { scope, environment } = useContext(ConfigContext); + const query = { + ...props.query, + scopedSpanCodeObjectId: scope?.span?.spanCodeObjectId || null + }; useEffect(() => { - getData({ - ...props.query, - scopedSpanCodeObjectId: scope?.span?.spanCodeObjectId || null - }); + getData(query); setIsInitialLoading(true); setIsLoading(true); @@ -95,14 +96,11 @@ export const useInsightsData = (props: UseInsightDataProps) => { if (previousLastSetDataTimeStamp !== lastSetDataTimeStamp) { window.clearTimeout(refreshTimerId.current); refreshTimerId.current = window.setTimeout( - () => { - getData({ - ...props.query, - scopedSpanCodeObjectId: scope?.span?.spanCodeObjectId || null - }); + (insightsQuery: ScopedInsightsQuery) => { + getData(insightsQuery); }, props.refreshInterval, - props.query + query ); } @@ -112,9 +110,9 @@ export const useInsightsData = (props: UseInsightDataProps) => { }, [ lastSetDataTimeStamp, previousLastSetDataTimeStamp, - props.query, - scope, - environment + query, + environment, + props.refreshInterval ]); useEffect(() => { @@ -124,10 +122,11 @@ export const useInsightsData = (props: UseInsightDataProps) => { scopedSpanCodeObjectId: scope?.span?.spanCodeObjectId || null }); }, [props.query, scope, environment]); + return { isInitialLoading, - previousData, data, - isLoading + isLoading, + refresh: () => getData(query) }; }; diff --git a/src/components/Insights/index.tsx b/src/components/Insights/index.tsx index 10ac7ca90..677d5a87b 100644 --- a/src/components/Insights/index.tsx +++ b/src/components/Insights/index.tsx @@ -144,7 +144,7 @@ const sendMessage = (action: string, data?: object) => { export const Insights = (props: InsightsProps) => { const [isAutofixing, setIsAutofixing] = useState(false); const [query, setQuery] = useState(DEFAULT_QUERY); - const { isLoading, isInitialLoading, data, previousData } = useInsightsData({ + const { isLoading, isInitialLoading, data, refresh } = useInsightsData({ refreshInterval: REFRESH_INTERVAL, data: props.data, query @@ -241,6 +241,7 @@ export const Insights = (props: InsightsProps) => { onQueryChange={(query: InsightsQuery) => { setQuery(query); }} + onRefresh={refresh} defaultQuery={DEFAULT_QUERY} /> );