diff --git a/src/components/Assets/index.tsx b/src/components/Assets/index.tsx index 6bd88a3aa..c1a382160 100644 --- a/src/components/Assets/index.tsx +++ b/src/components/Assets/index.tsx @@ -13,6 +13,7 @@ import { import { useDebounce } from "../../hooks/useDebounce"; import { usePrevious } from "../../hooks/usePrevious"; import { FeatureFlag } from "../../types"; +import { sendTrackingEvent } from "../../utils/sendTrackingEvent"; import { ConfigContext } from "../common/App/ConfigContext"; import { EmptyState } from "../common/EmptyState"; import { SearchInput } from "../common/SearchInput"; @@ -28,6 +29,7 @@ import { NoDataMessage } from "./NoDataMessage"; import { ServicesFilter } from "./ServicesFilter"; import { actions } from "./actions"; import * as s from "./styles"; +import { trackingEvents } from "./tracking"; import { DataRefresher } from "./types"; export const Assets = () => { @@ -113,6 +115,10 @@ export const Assets = () => { }; const handleRefresh = () => { + sendTrackingEvent(trackingEvents.REFRESH_BUTTON_CLICKED, { + view: !selectedAssetTypeId ? "asset categories" : "assets" + }); + const currentRefresher = !selectedAssetTypeId ? assetTypeListDataRefresher : assetListDataRefresher; diff --git a/src/components/Assets/tracking.ts b/src/components/Assets/tracking.ts index c55fc99fd..35b00b901 100644 --- a/src/components/Assets/tracking.ts +++ b/src/components/Assets/tracking.ts @@ -5,7 +5,8 @@ const TRACKING_PREFIX = "assets"; export const trackingEvents = addPrefix( TRACKING_PREFIX, { - FILTER_APPLIED: "filter applied" + FILTER_APPLIED: "filter applied", + REFRESH_BUTTON_CLICKED: "refresh button clicked" }, " " ); diff --git a/src/components/Insights/BottleneckInsight/mockData.ts b/src/components/Insights/BottleneckInsight/mockData.ts index 5f4622257..eeec76047 100644 --- a/src/components/Insights/BottleneckInsight/mockData.ts +++ b/src/components/Insights/BottleneckInsight/mockData.ts @@ -22,6 +22,8 @@ export const mockedBottleneckInsight: SpanEndpointBottleneckInsight = { category: InsightCategory.Performance, specifity: 3, importance: 2, + isDismissed: false, + isDismissible: true, span: { name: "WaitForLock", displayName: "WaitForLock", diff --git a/src/components/Insights/DurationBreakdownInsight/DurationBreakdownInsight.stories.tsx b/src/components/Insights/DurationBreakdownInsight/DurationBreakdownInsight.stories.tsx index 1c0bbc5a4..779da4860 100644 --- a/src/components/Insights/DurationBreakdownInsight/DurationBreakdownInsight.stories.tsx +++ b/src/components/Insights/DurationBreakdownInsight/DurationBreakdownInsight.stories.tsx @@ -28,6 +28,8 @@ export const Default: Story = { firstCommitId: "b3f7b3f", lastCommitId: "a1b2c3d", deactivatedCommitId: null, + isDismissed: false, + isDismissible: true, reopenCount: 0, ticketLink: null, impact: 0, diff --git a/src/components/Insights/DurationInsight/DurationInsight.stories.tsx b/src/components/Insights/DurationInsight/DurationInsight.stories.tsx index 1cf110bad..1c8904a5d 100644 --- a/src/components/Insights/DurationInsight/DurationInsight.stories.tsx +++ b/src/components/Insights/DurationInsight/DurationInsight.stories.tsx @@ -34,6 +34,8 @@ export const WithAverage: Story = { name: "Performance Stats", type: InsightType.SpanDurations, category: InsightCategory.Performance, + isDismissed: false, + isDismissible: true, specifity: 4, isRecalculateEnabled: true, spanCodeObjectId: "span:SampleInsightsController$_$DelayAsync", @@ -151,6 +153,8 @@ export const WithChange: Story = { name: "Performance Stats", type: InsightType.SpanDurations, category: InsightCategory.Performance, + isDismissed: false, + isDismissible: true, specifity: 4, isRecalculateEnabled: true, spanCodeObjectId: "span:SampleInsightsController$_$DelayAsync", @@ -254,6 +258,8 @@ export const WithEvaluatingChange: Story = { deactivatedCommitId: null, reopenCount: 0, ticketLink: null, + isDismissed: false, + isDismissible: true, impact: 0, name: "Performance Stats", type: InsightType.SpanDurations, @@ -368,6 +374,8 @@ export const HistogramWithManyBars: Story = { specifity: 4, isRecalculateEnabled: true, spanCodeObjectId: "span:SampleInsightsController$_$DelayAsync", + isDismissed: false, + isDismissible: true, span: { name: "DelayAsync", displayName: "DelayAsync", @@ -4893,6 +4901,8 @@ export const HistogramWithGaps: Story = { specifity: 4, isRecalculateEnabled: true, spanCodeObjectId: "span:SampleInsightsController$_$DelayAsync", + isDismissed: false, + isDismissible: true, span: { name: "DelayAsync", displayName: "DelayAsync", @@ -5141,6 +5151,8 @@ export const HistogramWithAFewBars: Story = { deactivatedCommitId: null, reopenCount: 0, ticketLink: null, + isDismissed: false, + isDismissible: true, impact: 0, name: "Performance Stats", type: InsightType.SpanDurations, @@ -5293,6 +5305,8 @@ export const EmptyStateBug: Story = { category: InsightCategory.Performance, specifity: 4, isRecalculateEnabled: true, + isDismissed: false, + isDismissible: true, spanCodeObjectId: "span:OpenTelemetry.Instrumentation.AspNetCore$_$HTTP GET SampleInsights/HttpCall", span: { diff --git a/src/components/Insights/DurationSlowdownSourceInsight/DurationSlowdownSourceInsight.stories.tsx b/src/components/Insights/DurationSlowdownSourceInsight/DurationSlowdownSourceInsight.stories.tsx index b9307512c..6a473e62b 100644 --- a/src/components/Insights/DurationSlowdownSourceInsight/DurationSlowdownSourceInsight.stories.tsx +++ b/src/components/Insights/DurationSlowdownSourceInsight/DurationSlowdownSourceInsight.stories.tsx @@ -36,6 +36,8 @@ export const WithEvaluatingChange: Story = { category: InsightCategory.Performance, specifity: 4, importance: 2, + isDismissed: false, + isDismissible: true, durationSlowdownSources: [ { percentile: "0.5", diff --git a/src/components/Insights/EndpointNPlusOneInsight/mockData.ts b/src/components/Insights/EndpointNPlusOneInsight/mockData.ts index cdf4a4fb4..554a82871 100644 --- a/src/components/Insights/EndpointNPlusOneInsight/mockData.ts +++ b/src/components/Insights/EndpointNPlusOneInsight/mockData.ts @@ -23,6 +23,8 @@ export const mockedEndpointNPlusOneInsight: EndpointSuspectedNPlusOneInsight = { category: InsightCategory.Performance, specifity: 2, importance: 3, + isDismissed: false, + isDismissible: true, spans: [ { occurrences: 200, diff --git a/src/components/Insights/EndpointQueryOptimizationInsight/mockData.ts b/src/components/Insights/EndpointQueryOptimizationInsight/mockData.ts index 68c6589cc..3bce4c02f 100644 --- a/src/components/Insights/EndpointQueryOptimizationInsight/mockData.ts +++ b/src/components/Insights/EndpointQueryOptimizationInsight/mockData.ts @@ -21,6 +21,8 @@ export const mockedEndpointQueryOptimizationInsight: EndpointQueryOptimizationIn name: "Query Optimization", type: InsightType.EndpointQueryOptimization, category: InsightCategory.Performance, + isDismissed: false, + isDismissible: true, specifity: 2, importance: 3, spans: [ diff --git a/src/components/Insights/ErrorsInsight/ErrorsInsight.stories.tsx b/src/components/Insights/ErrorsInsight/ErrorsInsight.stories.tsx index f7f9ef336..74ee68520 100644 --- a/src/components/Insights/ErrorsInsight/ErrorsInsight.stories.tsx +++ b/src/components/Insights/ErrorsInsight/ErrorsInsight.stories.tsx @@ -40,6 +40,8 @@ export const Default: Story = { errorCount: 2, unhandledCount: 0, unexpectedCount: 0, + isDismissed: false, + isDismissible: true, topErrors: [ { uid: "c4436bfe-1736-11ee-9651-0242ac1a0004", diff --git a/src/components/Insights/ExcessiveAPICallsInsight/ExcessiveAPICallsInsight.stories.tsx b/src/components/Insights/ExcessiveAPICallsInsight/ExcessiveAPICallsInsight.stories.tsx index 0a9f566f7..d296dca82 100644 --- a/src/components/Insights/ExcessiveAPICallsInsight/ExcessiveAPICallsInsight.stories.tsx +++ b/src/components/Insights/ExcessiveAPICallsInsight/ExcessiveAPICallsInsight.stories.tsx @@ -36,6 +36,8 @@ export const Default: Story = { category: InsightCategory.Performance, specifity: 2, importance: 3, + isDismissed: false, + isDismissible: true, spans: [ { repeats: 29, diff --git a/src/components/Insights/HighNumberOfQueriesInsight/mockData.ts b/src/components/Insights/HighNumberOfQueriesInsight/mockData.ts index 669203bd3..b92dea23a 100644 --- a/src/components/Insights/HighNumberOfQueriesInsight/mockData.ts +++ b/src/components/Insights/HighNumberOfQueriesInsight/mockData.ts @@ -24,6 +24,8 @@ export const mockedHighNumberOfQueriesInsight: EndpointHighNumberOfQueriesInsigh specifity: 2, importance: 3, queriesCount: 250, + isDismissed: false, + isDismissible: true, typicalCount: 4, traceId: "00D37A4E7208E0F6E89AA7E2E37446A6", scope: InsightScope.EntrySpan, diff --git a/src/components/Insights/InsightJiraTicket/InsightJiraTicket.stories.tsx b/src/components/Insights/InsightJiraTicket/InsightJiraTicket.stories.tsx index 2a2d447d9..f5f8388cd 100644 --- a/src/components/Insights/InsightJiraTicket/InsightJiraTicket.stories.tsx +++ b/src/components/Insights/InsightJiraTicket/InsightJiraTicket.stories.tsx @@ -38,6 +38,8 @@ const insight: SpanUsagesInsight = { sampleTrace: null, flows: [], scope: InsightScope.Span, + isDismissed: false, + isDismissible: true, spanInfo: { name: "DelayAsync", displayName: "DelayAsync", diff --git a/src/components/Insights/Insights.stories.tsx b/src/components/Insights/Insights.stories.tsx index a34b50a6a..6c133c5ac 100644 --- a/src/components/Insights/Insights.stories.tsx +++ b/src/components/Insights/Insights.stories.tsx @@ -234,7 +234,9 @@ export const Default: Story = { prefixedCodeObjectId: "method:Sample.MoneyTransfer.API.Controllers.TransferController$_$TransferFunds(TransferRequest)", customStartTime: null, - actualStartTime: "2023-06-26T00:00:00.000Z" + actualStartTime: "2023-06-26T00:00:00.000Z", + isDismissed: false, + isDismissible: true }, { sourceSpanCodeObjectInsight: "sourceSpanCodeObjectInsightId", @@ -290,7 +292,9 @@ export const Default: Story = { prefixedCodeObjectId: "method:Sample.MoneyTransfer.API.Controllers.TransferController$_$TransferFunds(TransferRequest)", customStartTime: null, - actualStartTime: "2023-06-26T13:53:53.645Z" + actualStartTime: "2023-06-26T13:53:53.645Z", + isDismissed: false, + isDismissible: true }, { sourceSpanCodeObjectInsight: "sourceSpanCodeObjectInsightId", @@ -332,7 +336,9 @@ export const Default: Story = { prefixedCodeObjectId: "method:Sample.MoneyTransfer.API.Controllers.TransferController$_$TransferFunds(TransferRequest)", customStartTime: null, - actualStartTime: "2023-06-26T13:53:57.956Z" + actualStartTime: "2023-06-26T13:53:57.956Z", + isDismissed: false, + isDismissible: true }, { sourceSpanCodeObjectInsight: "sourceSpanCodeObjectInsightId", @@ -351,6 +357,8 @@ export const Default: Story = { category: InsightCategory.Usage, specifity: 4, importance: 6, + isDismissed: false, + isDismissible: true, decorators: [ { title: "Low Usage", @@ -564,7 +572,9 @@ export const Default: Story = { prefixedCodeObjectId: "method:Sample.MoneyTransfer.API.Controllers.TransferController$_$TransferFunds(TransferRequest)", customStartTime: null, - actualStartTime: "2023-06-12T13:49:03.486Z" + actualStartTime: "2023-06-12T13:49:03.486Z", + isDismissed: false, + isDismissible: true }, { sourceSpanCodeObjectInsight: "sourceSpanCodeObjectInsightId", @@ -583,6 +593,8 @@ export const Default: Story = { category: InsightCategory.Performance, specifity: 4, isRecalculateEnabled: true, + isDismissed: false, + isDismissible: true, spanCodeObjectId: "span:OpenTelemetry.Instrumentation.AspNetCore$_$HTTP POST Transfer/TransferFunds", span: { @@ -727,7 +739,9 @@ export const Default: Story = { prefixedCodeObjectId: "method:Sample.MoneyTransfer.API.Controllers.TransferController$_$TransferFunds(TransferRequest)", customStartTime: null, - actualStartTime: "2023-06-20T00:00:00.000Z" + actualStartTime: "2023-06-20T00:00:00.000Z", + isDismissed: false, + isDismissible: true }, mockedEndpointNPlusOneInsight, mockedBottleneckInsight, @@ -872,7 +886,9 @@ const errorsInsight: CodeObjectErrorsInsight = { prefixedCodeObjectId: "method:Sample.MoneyTransfer.API.Controllers.TransferController$_$TransferFunds(TransferRequest)", customStartTime: null, - actualStartTime: "2023-06-26T13:53:53.645Z" + actualStartTime: "2023-06-26T13:53:53.645Z", + isDismissed: false, + isDismissible: true }; export const NoObservabilityWithInsights: Story = { diff --git a/src/components/Insights/InsightsCatalog/index.tsx b/src/components/Insights/InsightsCatalog/index.tsx index 5d8087a85..f36353dad 100644 --- a/src/components/Insights/InsightsCatalog/index.tsx +++ b/src/components/Insights/InsightsCatalog/index.tsx @@ -1,20 +1,31 @@ import { useCallback, useContext, useEffect, useState } from "react"; import { usePrevious } from "../../../hooks/usePrevious"; +import { useTheme } from "styled-components"; import { useDebounce } from "../../../hooks/useDebounce"; import { isNumber } from "../../../typeGuards/isNumber"; +import { sendTrackingEvent } from "../../../utils/sendTrackingEvent"; import { ConfigContext } from "../../common/App/ConfigContext"; import { Pagination } from "../../common/Pagination"; import { SearchInput } from "../../common/SearchInput"; import { SortingSelector } from "../../common/SortingSelector"; import { SORTING_ORDER, Sorting } from "../../common/SortingSelector/types"; +import { ChevronIcon } from "../../common/icons/16px/ChevronIcon"; +import { GroupIcon } from "../../common/icons/16px/GroupIcon"; import { RefreshIcon } from "../../common/icons/16px/RefreshIcon"; +import { Direction } from "../../common/icons/types"; +import { Button } from "../../common/v3/Button"; import { Tooltip } from "../../common/v3/Tooltip"; import { InsightsPage } from "../InsightsPage"; +import { trackingEvents } from "../tracking"; import * as s from "./styles"; import { InsightsCatalogProps, SORTING_CRITERION } from "./types"; const PAGE_SIZE = 10; +enum ViewMode { + All, + OnlyDismissed +} export const InsightsCatalog = (props: InsightsCatalogProps) => { const { insights, onJiraTicketCreate, defaultQuery, totalCount } = props; @@ -35,16 +46,34 @@ export const InsightsCatalog = (props: InsightsCatalogProps) => { const config = useContext(ConfigContext); const previousConfig = usePrevious(config); const previousScope = usePrevious(config.scope?.span); + const [mode, setMode] = useState(ViewMode.All); + const previousMode = usePrevious(mode); + const theme = useTheme(); + const refreshData = useCallback( () => props.onQueryChange({ page, sorting, - searchQuery: debouncedSearchInputValue + searchQuery: debouncedSearchInputValue, + showDismissed: mode === ViewMode.OnlyDismissed }), - [page, sorting, debouncedSearchInputValue, props] + [page, sorting, debouncedSearchInputValue, props, mode] ); + const handleRefreshButtonClick = () => { + sendTrackingEvent(trackingEvents.REFRESH_BUTTON_CLICKED, { + viewMode: mode + }); + + refreshData(); + }; + const handleViewModeChange = () => { + const newMode = + mode === ViewMode.All ? ViewMode.OnlyDismissed : ViewMode.All; + setMode(newMode); + }; + useEffect(() => { if (!previousScope || previousScope !== config.scope?.span) { setSearchInputValue(""); @@ -70,7 +99,8 @@ export const InsightsCatalog = (props: InsightsCatalogProps) => { if ( (isNumber(previousPage) && previousPage !== page) || (previousSorting && previousSorting !== sorting) || - previousSearchQuery !== debouncedSearchInputValue + previousSearchQuery !== debouncedSearchInputValue || + previousMode !== mode ) { refreshData(); } @@ -82,7 +112,9 @@ export const InsightsCatalog = (props: InsightsCatalogProps) => { debouncedSearchInputValue, previousSearchQuery, props.onQueryChange, - refreshData + refreshData, + mode, + previousMode ]); return ( @@ -117,10 +149,26 @@ export const InsightsCatalog = (props: InsightsCatalogProps) => { refreshData()} + onClick={handleRefreshButtonClick} /> + {mode === ViewMode.OnlyDismissed && ( + +