diff --git a/src/actions.ts b/src/actions.ts index 07fca64a7..ace97cb57 100644 --- a/src/actions.ts +++ b/src/actions.ts @@ -34,5 +34,8 @@ export const actions = addPrefix(ACTION_PREFIX, { OPEN_DASHBOARD: "OPEN_DASHBOARD", OPEN_INSTALLATION_WIZARD: "OPEN_INSTALLATION_WIZARD", SET_SCOPE: "SET_SCOPE", - CHANGE_SCOPE: "CHANGE_SCOPE" + CHANGE_SCOPE: "CHANGE_SCOPE", + SET_STATE: "SET_STATE", + GET_STATE: "GET_STATE", + UPDATE_STATE: "UPDATE_STATE" }); diff --git a/src/components/Assets/AssetList/AssetEntry/AssetEntry.stories.tsx b/src/components/Assets/AssetList/AssetEntry/AssetEntry.stories.tsx index eaf5adb9e..5f16f38a1 100644 --- a/src/components/Assets/AssetList/AssetEntry/AssetEntry.stories.tsx +++ b/src/components/Assets/AssetList/AssetEntry/AssetEntry.stories.tsx @@ -43,15 +43,18 @@ export const Default: Story = { insights: [ { type: "Errors", - importance: 5 + importance: 5, + criticality: 0.9 }, { type: "HotSpot", - importance: 2 + importance: 2, + criticality: 0.5 }, { type: "LowUsage", - importance: 6 + importance: 6, + criticality: 0.1 } ], latestSpanTimestamp: "2023-02-20T14:36:03.480951Z", diff --git a/src/components/Assets/AssetList/AssetEntry/index.tsx b/src/components/Assets/AssetList/AssetEntry/index.tsx index d3c0f2eec..41d532696 100644 --- a/src/components/Assets/AssetList/AssetEntry/index.tsx +++ b/src/components/Assets/AssetList/AssetEntry/index.tsx @@ -4,7 +4,7 @@ import { getFeatureFlagValue } from "../../../../featureFlags"; import { isString } from "../../../../typeGuards/isString"; import { FeatureFlag, InsightType } from "../../../../types"; import { formatTimeDistance } from "../../../../utils/formatTimeDistance"; -import { getInsightImportanceColor } from "../../../../utils/getInsightImportanceColor"; +import { getInsightCriticalityColor } from "../../../../utils/getInsightCriticalityColor"; import { getInsightTypeInfo } from "../../../../utils/getInsightTypeInfo"; import { getInsightTypeOrderPriority } from "../../../../utils/getInsightTypeOrderPriority"; import { InsightImportance } from "../../../Insights/types"; @@ -62,7 +62,7 @@ export const AssetEntry = (props: AssetEntryProps) => { const sortedInsights = [...filteredInsights].sort( (a, b) => - a.importance - b.importance || + b.criticality - a.criticality || getInsightTypeOrderPriority(a.type) - getInsightTypeOrderPriority(b.type) ); @@ -96,8 +96,8 @@ export const AssetEntry = (props: AssetEntryProps) => { {isNew && } {sortedInsights.map((insight) => { const insightTypeInfo = getInsightTypeInfo(insight.type); - const insightIconColor = getInsightImportanceColor( - insight.importance, + const insightIconColor = getInsightCriticalityColor( + insight.criticality, theme ); diff --git a/src/components/Assets/AssetList/AssetList.stories.tsx b/src/components/Assets/AssetList/AssetList.stories.tsx index 6c8e2c827..8016ca1f3 100644 --- a/src/components/Assets/AssetList/AssetList.stories.tsx +++ b/src/components/Assets/AssetList/AssetList.stories.tsx @@ -45,23 +45,28 @@ export const Default: Story = { insights: [ { type: "SlowestSpans", - importance: 2 + importance: 2, + criticality: 0.5 }, { type: "SlowEndpoint", - importance: 2 + importance: 2, + criticality: 0.5 }, { type: "SpanScalingInsufficientData", - importance: 5 + importance: 5, + criticality: 0.5 }, { type: "EndpointBreakdown", - importance: 6 + importance: 6, + criticality: 0.5 }, { type: "LowUsage", - importance: 6 + importance: 6, + criticality: 0.5 } ], impactScores: { @@ -90,23 +95,28 @@ export const Default: Story = { insights: [ { type: "HotSpot", - importance: 2 + importance: 2, + criticality: 0.5 }, { type: "SpanScalingInsufficientData", - importance: 5 + importance: 5, + criticality: 0.5 }, { type: "HighUsage", - importance: 5 + importance: 5, + criticality: 0.5 }, { type: "Errors", - importance: 5 + importance: 5, + criticality: 0.5 }, { type: "EndpointBreakdown", - importance: 6 + importance: 6, + criticality: 0.5 } ], impactScores: { @@ -135,23 +145,28 @@ export const Default: Story = { insights: [ { type: "HotSpot", - importance: 2 + importance: 2, + criticality: 0.5 }, { type: "SpanScalingInsufficientData", - importance: 5 + importance: 5, + criticality: 0.5 }, { type: "Errors", - importance: 5 + importance: 5, + criticality: 0.5 }, { type: "LowUsage", - importance: 6 + importance: 6, + criticality: 0.5 }, { type: "EndpointBreakdown", - importance: 6 + importance: 6, + criticality: 0.5 } ], impactScores: { @@ -180,23 +195,28 @@ export const Default: Story = { insights: [ { type: "HotSpot", - importance: 2 + importance: 2, + criticality: 0.5 }, { type: "SpanScalingInsufficientData", - importance: 5 + importance: 5, + criticality: 0.5 }, { type: "Errors", - importance: 5 + importance: 5, + criticality: 0.5 }, { type: "LowUsage", - importance: 6 + importance: 6, + criticality: 0.5 }, { type: "EndpointBreakdown", - importance: 6 + importance: 6, + criticality: 0.5 } ], impactScores: { @@ -225,23 +245,28 @@ export const Default: Story = { insights: [ { type: "HotSpot", - importance: 2 + importance: 2, + criticality: 0.5 }, { type: "SpanScalingInsufficientData", - importance: 5 + importance: 5, + criticality: 0.5 }, { type: "Errors", - importance: 5 + importance: 5, + criticality: 0.5 }, { type: "LowUsage", - importance: 6 + importance: 6, + criticality: 0.5 }, { type: "EndpointBreakdown", - importance: 6 + importance: 6, + criticality: 0.5 } ], impactScores: { @@ -270,19 +295,23 @@ export const Default: Story = { insights: [ { type: "SlowestSpans", - importance: 2 + importance: 2, + criticality: 0.5 }, { type: "SpanScalingInsufficientData", - importance: 5 + importance: 5, + criticality: 0.5 }, { type: "EndpointBreakdown", - importance: 6 + importance: 6, + criticality: 0.5 }, { type: "LowUsage", - importance: 6 + importance: 6, + criticality: 0.5 } ], impactScores: { @@ -311,19 +340,23 @@ export const Default: Story = { insights: [ { type: "SlowestSpans", - importance: 2 + importance: 2, + criticality: 0.5 }, { type: "SpanScalingInsufficientData", - importance: 5 + importance: 5, + criticality: 0.5 }, { type: "LowUsage", - importance: 6 + importance: 6, + criticality: 0.5 }, { type: "EndpointBreakdown", - importance: 6 + importance: 6, + criticality: 0.5 } ], impactScores: { @@ -352,19 +385,23 @@ export const Default: Story = { insights: [ { type: "EndpointSpaNPlusOne", - importance: 3 + importance: 3, + criticality: 0.5 }, { type: "SpanScalingInsufficientData", - importance: 5 + importance: 5, + criticality: 0.5 }, { type: "LowUsage", - importance: 6 + importance: 6, + criticality: 0.5 }, { type: "EndpointBreakdown", - importance: 6 + importance: 6, + criticality: 0.5 } ], impactScores: { @@ -393,19 +430,23 @@ export const Default: Story = { insights: [ { type: "EndpointSpaNPlusOne", - importance: 3 + importance: 3, + criticality: 0.5 }, { type: "SpanScalingInsufficientData", - importance: 5 + importance: 5, + criticality: 0.5 }, { type: "LowUsage", - importance: 6 + importance: 6, + criticality: 0.5 }, { type: "EndpointBreakdown", - importance: 6 + importance: 6, + criticality: 0.5 } ], impactScores: { @@ -434,19 +475,23 @@ export const Default: Story = { insights: [ { type: "SpanScalingInsufficientData", - importance: 5 + importance: 5, + criticality: 0.5 }, { type: "Errors", - importance: 5 + importance: 5, + criticality: 0.5 }, { type: "LowUsage", - importance: 6 + importance: 6, + criticality: 0.5 }, { type: "EndpointBreakdown", - importance: 6 + importance: 6, + criticality: 0.5 } ], impactScores: { @@ -476,19 +521,23 @@ export const Default: Story = { insights: [ { type: "SpanScalingInsufficientData", - importance: 5 + importance: 5, + criticality: 0.5 }, { type: "Errors", - importance: 5 + importance: 5, + criticality: 0.5 }, { type: "LowUsage", - importance: 6 + importance: 6, + criticality: 0.5 }, { type: "EndpointBreakdown", - importance: 6 + importance: 6, + criticality: 0.5 } ], impactScores: { @@ -516,15 +565,18 @@ export const Default: Story = { insights: [ { type: "SpanScalingInsufficientData", - importance: 5 + importance: 5, + criticality: 0.5 }, { type: "EndpointBreakdown", - importance: 6 + importance: 6, + criticality: 0.5 }, { type: "LowUsage", - importance: 6 + importance: 6, + criticality: 0.5 } ], impactScores: { @@ -552,15 +604,18 @@ export const Default: Story = { insights: [ { type: "SpanScalingInsufficientData", - importance: 5 + importance: 5, + criticality: 0.5 }, { type: "EndpointBreakdown", - importance: 6 + importance: 6, + criticality: 0.5 }, { type: "LowUsage", - importance: 6 + importance: 6, + criticality: 0.5 } ], impactScores: { @@ -589,15 +644,18 @@ export const Default: Story = { insights: [ { type: "SpanScalingInsufficientData", - importance: 5 + importance: 5, + criticality: 0.5 }, { type: "EndpointBreakdown", - importance: 6 + importance: 6, + criticality: 0.5 }, { type: "LowUsage", - importance: 6 + importance: 6, + criticality: 0.5 } ], impactScores: { @@ -626,15 +684,18 @@ export const Default: Story = { insights: [ { type: "SpanScalingInsufficientData", - importance: 5 + importance: 5, + criticality: 0.5 }, { type: "EndpointBreakdown", - importance: 6 + importance: 6, + criticality: 0.5 }, { type: "LowUsage", - importance: 6 + importance: 6, + criticality: 0.5 } ], impactScores: { @@ -663,15 +724,18 @@ export const Default: Story = { insights: [ { type: "SpanScalingInsufficientData", - importance: 5 + importance: 5, + criticality: 0.5 }, { type: "LowUsage", - importance: 6 + importance: 6, + criticality: 0.5 }, { type: "EndpointBreakdown", - importance: 6 + importance: 6, + criticality: 0.5 } ], impactScores: { @@ -700,15 +764,18 @@ export const Default: Story = { insights: [ { type: "SpanScalingInsufficientData", - importance: 5 + importance: 5, + criticality: 0.5 }, { type: "LowUsage", - importance: 6 + importance: 6, + criticality: 0.5 }, { type: "EndpointBreakdown", - importance: 6 + importance: 6, + criticality: 0.5 } ], impactScores: { @@ -737,15 +804,18 @@ export const Default: Story = { insights: [ { type: "SpanScalingInsufficientData", - importance: 5 + importance: 5, + criticality: 0.5 }, { type: "EndpointBreakdown", - importance: 6 + importance: 6, + criticality: 0.5 }, { type: "LowUsage", - importance: 6 + importance: 6, + criticality: 0.5 } ], impactScores: { @@ -774,15 +844,18 @@ export const Default: Story = { insights: [ { type: "SpanScalingInsufficientData", - importance: 5 + importance: 5, + criticality: 0.5 }, { type: "LowUsage", - importance: 6 + importance: 6, + criticality: 0.5 }, { type: "EndpointBreakdown", - importance: 6 + importance: 6, + criticality: 0.5 } ], impactScores: { @@ -811,15 +884,18 @@ export const Default: Story = { insights: [ { type: "SpanScalingInsufficientData", - importance: 5 + importance: 5, + criticality: 0.5 }, { type: "EndpointBreakdown", - importance: 6 + importance: 6, + criticality: 0.5 }, { type: "LowUsage", - importance: 6 + importance: 6, + criticality: 0.5 } ], impactScores: { diff --git a/src/components/Assets/AssetList/types.ts b/src/components/Assets/AssetList/types.ts index c7c881b18..e11b816d6 100644 --- a/src/components/Assets/AssetList/types.ts +++ b/src/components/Assets/AssetList/types.ts @@ -47,6 +47,7 @@ export interface SortingOrderIconContainerProps { export interface Insight { type: string; importance: number; + criticality: number; } export interface ImpactScores { diff --git a/src/components/Assets/AssetsViewScopeConfiguration/index.tsx b/src/components/Assets/AssetsViewScopeConfiguration/index.tsx index 6204b464e..a9e32b6b9 100644 --- a/src/components/Assets/AssetsViewScopeConfiguration/index.tsx +++ b/src/components/Assets/AssetsViewScopeConfiguration/index.tsx @@ -22,7 +22,7 @@ export const AssetsViewScopeConfiguration = ( return ( - Show for selected assets + Assets filtered to current scope { ); } - if (!selectedFilters && !selectedServices) { + if ( + !config.environments?.length || + (!selectedFilters && !selectedServices) + ) { return ; } @@ -154,7 +157,6 @@ export const Assets = () => { - Assets {window.assetsSearch === true && ( { props.onJiraTicketCreate(props.insight, undefined, event); }; + const slowEndpoints = props.insight.slowEndpoints || []; + return ( { content={ <> - {props.insight.slowEndpoints.map((endpoint, i) => { + {slowEndpoints.map((endpoint, i) => { const endpointName = `${ endpoint.endpointInfo.serviceName }:${trimEndpointScheme(endpoint.endpointInfo.route)}`; diff --git a/src/components/Insights/BottleneckInsight/mockData.ts b/src/components/Insights/BottleneckInsight/mockData.ts index 6ec96c3ef..76eb46472 100644 --- a/src/components/Insights/BottleneckInsight/mockData.ts +++ b/src/components/Insights/BottleneckInsight/mockData.ts @@ -6,6 +6,7 @@ import { } from "../types"; export const mockedBottleneckInsight: SpanEndpointBottleneckInsight = { + sourceSpanCodeObjectInsight: "sourceSpanCodeObjectInsightId", id: "60b55792-8262-4c5d-9628-1cce7979ac6d", firstDetected: "2023-12-05T17:25:47.010Z", lastDetected: "2024-01-05T13:14:47.010Z", @@ -43,11 +44,16 @@ export const mockedBottleneckInsight: SpanEndpointBottleneckInsight = { spanName: "HTTP GET SampleInsights/lock/{milisec}" }, probabilityOfBeingBottleneck: 0.36877828054298645, + avgFractionWhenBeingBottleneck: 50, avgDurationWhenBeingBottleneck: { value: 3.89, unit: "sec", raw: 3887134558.2822084 }, + impact: 0.14877828054298645, + severity: 0.2877828054298645, + criticality: 0.5687782805429865, + requestPercentage: 35, p50: { fraction: 0, maxDuration: { diff --git a/src/components/Insights/DurationBreakdownInsight/DurationBreakdownInsight.stories.tsx b/src/components/Insights/DurationBreakdownInsight/DurationBreakdownInsight.stories.tsx index 063e23179..1c0bbc5a4 100644 --- a/src/components/Insights/DurationBreakdownInsight/DurationBreakdownInsight.stories.tsx +++ b/src/components/Insights/DurationBreakdownInsight/DurationBreakdownInsight.stories.tsx @@ -20,6 +20,7 @@ type Story = StoryObj; export const Default: Story = { args: { insight: { + sourceSpanCodeObjectInsight: "sourceSpanCodeObjectInsightId", id: "60b55792-8262-4c5d-9628-7cce7979ac6d", firstDetected: "2023-12-05T17:25:47.010Z", lastDetected: "2024-01-05T13:14:47.010Z", diff --git a/src/components/Insights/DurationInsight/DurationInsight.stories.tsx b/src/components/Insights/DurationInsight/DurationInsight.stories.tsx index df40a87c5..1cf110bad 100644 --- a/src/components/Insights/DurationInsight/DurationInsight.stories.tsx +++ b/src/components/Insights/DurationInsight/DurationInsight.stories.tsx @@ -20,6 +20,7 @@ type Story = StoryObj; export const WithAverage: Story = { args: { insight: { + sourceSpanCodeObjectInsight: "sourceSpanCodeObjectInsightId", id: "60b55792-8262-4c5d-9628-7cce7979ad6d", firstDetected: "2023-12-05T17:25:47.010Z", lastDetected: "2024-01-05T13:14:47.010Z", @@ -136,6 +137,7 @@ export const WithAverage: Story = { export const WithChange: Story = { args: { insight: { + sourceSpanCodeObjectInsight: "sourceSpanCodeObjectInsightId", id: "60b55792-8262-4c5d-9628-7cce7979ad6d", firstDetected: null, lastDetected: null, @@ -242,6 +244,7 @@ export const WithChange: Story = { export const WithEvaluatingChange: Story = { args: { insight: { + sourceSpanCodeObjectInsight: "sourceSpanCodeObjectInsightId", id: "60b55792-8262-4c5d-9628-7cce7979ad6d", firstDetected: null, lastDetected: null, @@ -348,6 +351,7 @@ export const WithEvaluatingChange: Story = { export const HistogramWithManyBars: Story = { args: { insight: { + sourceSpanCodeObjectInsight: "sourceSpanCodeObjectInsightId", id: "60b55792-8262-4c5d-9628-7cce7979ad6d", firstDetected: null, lastDetected: null, @@ -4872,6 +4876,7 @@ export const HistogramWithManyBars: Story = { export const HistogramWithGaps: Story = { args: { insight: { + sourceSpanCodeObjectInsight: "sourceSpanCodeObjectInsightId", id: "60b55792-8262-4c5d-9628-7cce7979ad6d", firstDetected: null, lastDetected: null, @@ -5126,6 +5131,7 @@ export const HistogramWithGaps: Story = { export const HistogramWithAFewBars: Story = { args: { insight: { + sourceSpanCodeObjectInsight: "sourceSpanCodeObjectInsightId", id: "60b55792-8262-4c5d-9628-7cce7979ad6d", firstDetected: null, lastDetected: null, @@ -5280,6 +5286,7 @@ export const HistogramWithAFewBars: Story = { export const EmptyStateBug: Story = { args: { insight: { + sourceSpanCodeObjectInsight: "sourceSpanCodeObjectInsightId", id: "60b55792-8262-4c5d-9628-7cce7979ad6d", name: "Performance Stats", type: InsightType.SpanDurations, diff --git a/src/components/Insights/DurationSlowdownSourceInsight/DurationSlowdownSourceInsight.stories.tsx b/src/components/Insights/DurationSlowdownSourceInsight/DurationSlowdownSourceInsight.stories.tsx index 4f602ec3f..b9307512c 100644 --- a/src/components/Insights/DurationSlowdownSourceInsight/DurationSlowdownSourceInsight.stories.tsx +++ b/src/components/Insights/DurationSlowdownSourceInsight/DurationSlowdownSourceInsight.stories.tsx @@ -20,6 +20,7 @@ type Story = StoryObj; export const WithEvaluatingChange: Story = { args: { insight: { + sourceSpanCodeObjectInsight: "sourceSpanCodeObjectInsightId", id: "60b55792-8262-4c5d-9628-7cde7979ad6d", firstDetected: "2023-12-05T17:25:47.010Z", lastDetected: "2024-01-05T13:14:47.010Z", diff --git a/src/components/Insights/EndpointNPlusOneInsight/mockData.ts b/src/components/Insights/EndpointNPlusOneInsight/mockData.ts index 80c92f8de..bf1c8d8b4 100644 --- a/src/components/Insights/EndpointNPlusOneInsight/mockData.ts +++ b/src/components/Insights/EndpointNPlusOneInsight/mockData.ts @@ -6,6 +6,7 @@ import { } from "../types"; export const mockedEndpointNPlusOneInsight: EndpointSuspectedNPlusOneInsight = { + sourceSpanCodeObjectInsight: "sourceSpanCodeObjectInsightId", id: "60b55792-8262-4c5d-9628-7cce7919ad6d", firstDetected: "2023-12-05T17:25:47.010Z", lastDetected: "2024-01-05T13:14:47.010Z", diff --git a/src/components/Insights/EndpointQueryOptimizationInsight/mockData.ts b/src/components/Insights/EndpointQueryOptimizationInsight/mockData.ts index 689a4156f..68c6589cc 100644 --- a/src/components/Insights/EndpointQueryOptimizationInsight/mockData.ts +++ b/src/components/Insights/EndpointQueryOptimizationInsight/mockData.ts @@ -7,6 +7,7 @@ import { export const mockedEndpointQueryOptimizationInsight: EndpointQueryOptimizationInsight = { + sourceSpanCodeObjectInsight: "sourceSpanCodeObjectInsightId", id: "60b55792-8262-4c5d-9628-7cce7989ad6d", firstDetected: "2023-12-05T17:25:47.010Z", lastDetected: "2024-01-05T13:14:47.010Z", diff --git a/src/components/Insights/ErrorsInsight/ErrorsInsight.stories.tsx b/src/components/Insights/ErrorsInsight/ErrorsInsight.stories.tsx index c9f454065..f7f9ef336 100644 --- a/src/components/Insights/ErrorsInsight/ErrorsInsight.stories.tsx +++ b/src/components/Insights/ErrorsInsight/ErrorsInsight.stories.tsx @@ -20,6 +20,7 @@ type Story = StoryObj; export const Default: Story = { args: { insight: { + sourceSpanCodeObjectInsight: "sourceSpanCodeObjectInsightId", id: "60b55792-8262-4c5d-9628-3cce7979ad6d", firstDetected: "2023-12-05T17:25:47.010Z", lastDetected: "2024-01-05T13:14:47.010Z", diff --git a/src/components/Insights/ExcessiveAPICallsInsight/ExcessiveAPICallsInsight.stories.tsx b/src/components/Insights/ExcessiveAPICallsInsight/ExcessiveAPICallsInsight.stories.tsx index ae26aaf24..0a9f566f7 100644 --- a/src/components/Insights/ExcessiveAPICallsInsight/ExcessiveAPICallsInsight.stories.tsx +++ b/src/components/Insights/ExcessiveAPICallsInsight/ExcessiveAPICallsInsight.stories.tsx @@ -20,6 +20,7 @@ type Story = StoryObj; export const Default: Story = { args: { insight: { + sourceSpanCodeObjectInsight: "sourceSpanCodeObjectInsightId", id: "60b55792-8262-4c5d-9688-7cce7979ad6d", firstDetected: "2023-12-05T17:25:47.010Z", lastDetected: "2024-01-05T13:14:47.010Z", diff --git a/src/components/Insights/ExcessiveAPICallsInsight/index.tsx b/src/components/Insights/ExcessiveAPICallsInsight/index.tsx index e46a9d90e..a4addf574 100644 --- a/src/components/Insights/ExcessiveAPICallsInsight/index.tsx +++ b/src/components/Insights/ExcessiveAPICallsInsight/index.tsx @@ -62,21 +62,23 @@ export const ExcessiveAPICallsInsight = ( {config.isJaegerEnabled && traceId && ( - - handleTraceButtonClick( - { - name: spanName, - id: traceId - }, - props.insight.type, - spanCodeObjectId - ) - } - > - Trace - + + + handleTraceButtonClick( + { + name: spanName, + id: traceId + }, + props.insight.type, + spanCodeObjectId + ) + } + > + Trace + + )} ); diff --git a/src/components/Insights/HighNumberOfQueriesInsight/mockData.ts b/src/components/Insights/HighNumberOfQueriesInsight/mockData.ts index ff1ac43da..669203bd3 100644 --- a/src/components/Insights/HighNumberOfQueriesInsight/mockData.ts +++ b/src/components/Insights/HighNumberOfQueriesInsight/mockData.ts @@ -7,6 +7,7 @@ import { export const mockedHighNumberOfQueriesInsight: EndpointHighNumberOfQueriesInsight = { + sourceSpanCodeObjectInsight: "sourceSpanCodeObjectInsightId", id: "60b55792-8262-4c5d-9623-7cce7979ad6d", firstDetected: "2023-12-05T17:25:47.010Z", lastDetected: "2024-01-05T13:14:47.010Z", @@ -24,12 +25,6 @@ export const mockedHighNumberOfQueriesInsight: EndpointHighNumberOfQueriesInsigh importance: 3, queriesCount: 250, typicalCount: 4, - medianDuration: { - value: 150, - unit: "ms", - raw: 150000000.0 - }, - requestFraction: 0.3, traceId: "00D37A4E7208E0F6E89AA7E2E37446A6", scope: InsightScope.EntrySpan, endpointSpan: "HTTP POST /owners/{ownerId}/pets/new", diff --git a/src/components/Insights/InsightCard/InsightCard.stories.tsx b/src/components/Insights/InsightCard/InsightCard.stories.tsx index 619869388..83e7f98ba 100644 --- a/src/components/Insights/InsightCard/InsightCard.stories.tsx +++ b/src/components/Insights/InsightCard/InsightCard.stories.tsx @@ -2,7 +2,11 @@ import { Meta, StoryObj } from "@storybook/react"; import { InsightCard } from "."; import { InsightType } from "../../../types"; import { Button } from "../../common/Button"; -import { InsightCategory, InsightScope } from "../types"; +import { + EndpointSuspectedNPlusOneInsight, + InsightCategory, + InsightScope +} from "../types"; // More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction const meta: Meta = { @@ -21,6 +25,7 @@ type Story = StoryObj; export const Default: Story = { args: { data: { + sourceSpanCodeObjectInsight: "sourceSpanCodeObjectInsightId", id: "60b55792-8262-4c5d-9638-7cce7979ad6d", firstDetected: "2023-12-05T17:25:47.010Z", lastDetected: "2024-01-05T13:14:47.010Z", @@ -93,12 +98,12 @@ export const Default: Story = { ], environment: "SAMPLE_ENV", severity: 0, - isRecalculateEnabled: false, + isRecalculateEnabled: true, prefixedCodeObjectId: "method:org.springframework.samples.petclinic.sample.SampleInsightsController$_$genNPlusOneWithoutInternalSpan", customStartTime: null, actualStartTime: "2023-06-16T10:30:33.027Z" - }, + } as EndpointSuspectedNPlusOneInsight, stats: "Some stats", isAsync: true, content: ( diff --git a/src/components/Insights/InsightJiraTicket/InsightJiraTicket.stories.tsx b/src/components/Insights/InsightJiraTicket/InsightJiraTicket.stories.tsx index db7f6d3b0..2a2d447d9 100644 --- a/src/components/Insights/InsightJiraTicket/InsightJiraTicket.stories.tsx +++ b/src/components/Insights/InsightJiraTicket/InsightJiraTicket.stories.tsx @@ -1,7 +1,7 @@ import { Meta, StoryObj } from "@storybook/react"; import { InsightJiraTicket } from "."; import { InsightType } from "../../../types"; -import { InsightCategory, InsightScope } from "../types"; +import { InsightCategory, InsightScope, SpanUsagesInsight } from "../types"; // More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction const meta: Meta = { @@ -17,7 +17,8 @@ export default meta; type Story = StoryObj; -const insight = { +const insight: SpanUsagesInsight = { + sourceSpanCodeObjectInsight: "sourceSpanCodeObjectInsightId", id: "60b55792-8262-4d5d-9628-7cce7979ad6d", firstDetected: "2023-12-05T17:25:47.010Z", lastDetected: "2024-01-05T13:14:47.010Z", @@ -58,7 +59,8 @@ const insight = { severity: 0, prefixedCodeObjectId: "span:SampleInsightsController$_$DelayAsync", customStartTime: null, - actualStartTime: "2023-06-17T00:00:00.000Z" + actualStartTime: "2023-06-17T00:00:00.000Z", + ticketLink: null }; export const Linked: Story = { @@ -66,7 +68,7 @@ export const Linked: Story = { summary: "Summary text", description: { content: "Multiline\ndescription text", isLoading: false }, attachment: { url: "https://www.example.com", fileName: "attachment.ext" }, - insight: { ticketLink: "https://digma.ai/ticket/1", ...insight } + insight: { ...insight, ticketLink: "https://digma.ai/ticket/1" } } }; @@ -75,6 +77,6 @@ export const Unlinked: Story = { summary: "", description: { content: "Multiline\ndescription text", isLoading: false }, attachment: { url: "https://www.example.com", fileName: "attachment.ext" }, - insight: { ticketLink: null, ...insight } + insight } }; diff --git a/src/components/Insights/InsightJiraTicket/index.tsx b/src/components/Insights/InsightJiraTicket/index.tsx index 131a2c4bc..b5611b306 100644 --- a/src/components/Insights/InsightJiraTicket/index.tsx +++ b/src/components/Insights/InsightJiraTicket/index.tsx @@ -6,7 +6,11 @@ import { isValidHttpUrl } from "../../../utils/isValidUrl"; import { ConfigContext } from "../../common/App/ConfigContext"; import { JiraTicket } from "../../common/JiraTicket"; import { actions } from "../actions"; -import { InsightJiraTicketProps, LinkTicketResponse } from "./types"; +import { + InsightJiraTicketProps, + InsightsGetDataListQuery, + LinkTicketResponse +} from "./types"; export const InsightJiraTicket = (props: InsightJiraTicketProps) => { const [errorMessage, setErrorMessage] = useState(); @@ -59,9 +63,11 @@ export const InsightJiraTicket = (props: InsightJiraTicketProps) => { setErrorMessage(linkTicketResponse.message); } - window.sendMessageToDigma({ - action: actions.GET_DATA - }); + config.state?.insights?.query && + window.sendMessageToDigma({ + action: actions.GET_DATA_LIST, + payload: { query: config.state.insights.query } + }); props.onReloadSpanInsight && props.onReloadSpanInsight(); }; @@ -77,7 +83,7 @@ export const InsightJiraTicket = (props: InsightJiraTicketProps) => { handleInsightTicketLink ); }; - }, []); + }, [config.state?.insights?.query]); useEffect(() => { if (props.relatedInsight) { diff --git a/src/components/Insights/InsightJiraTicket/types.ts b/src/components/Insights/InsightJiraTicket/types.ts index c323d65bb..d8502c9b8 100644 --- a/src/components/Insights/InsightJiraTicket/types.ts +++ b/src/components/Insights/InsightJiraTicket/types.ts @@ -1,4 +1,5 @@ import { ReactNode } from "react"; +import { InsightsQuery } from "../../common/App/types"; import { GenericCodeObjectInsight } from "../types"; export interface InsightJiraTicketProps { @@ -22,3 +23,7 @@ export interface LinkTicketResponse { codeObjectId: string; insightType: string; } + +export interface InsightsGetDataListQuery { + query: InsightsQuery; +} diff --git a/src/components/Insights/InsightList/index.tsx b/src/components/Insights/InsightList/index.tsx index 30e08ee34..702d3d382 100644 --- a/src/components/Insights/InsightList/index.tsx +++ b/src/components/Insights/InsightList/index.tsx @@ -1,5 +1,6 @@ import { useEffect, useState } from "react"; import { DefaultTheme, useTheme } from "styled-components"; +import { actions as globalActions } from "../../../actions"; import { usePersistence } from "../../../hooks/usePersistence"; import { trackingEvents as globalTrackingEvents } from "../../../trackingEvents"; import { isUndefined } from "../../../typeGuards/isUndefined"; @@ -7,6 +8,7 @@ import { InsightType } from "../../../types"; import { getInsightTypeInfo } from "../../../utils/getInsightTypeInfo"; import { getInsightTypeOrderPriority } from "../../../utils/getInsightTypeOrderPriority"; import { sendTrackingEvent } from "../../../utils/sendTrackingEvent"; +import { ChangeScopePayload } from "../../Navigation/types"; import { Card } from "../../common/Card"; import { Tooltip } from "../../common/Tooltip"; import { EndpointIcon } from "../../common/icons/EndpointIcon"; @@ -51,6 +53,7 @@ import { isEndpointQueryOptimizationInsight, isEndpointSlowestSpansInsight, isEndpointSuspectedNPlusOneInsight, + isFunctionInsight, isSessionInViewEndpointInsight, isSlowEndpointInsight, isSpanDurationBreakdownInsight, @@ -66,15 +69,19 @@ import { isSpanUsagesInsight } from "../typeGuards"; import { - EndpointInsight, GenericCodeObjectInsight, + GenericEndpointInsight, + GenericSpanInsight, InsightGroup, MethodSpan, - SpanInsight, Trace } from "../types"; import * as s from "./styles"; -import { InsightListProps, isInsightJiraTicketHintShownPayload } from "./types"; +import { + InsightListProps, + RecalculatePayload, + isInsightJiraTicketHintShownPayload +} from "./types"; const getInsightToShowJiraHint = ( insightGroups: InsightGroup[] @@ -126,9 +133,9 @@ const groupInsights = ( }; const ungroupedInsights: GenericCodeObjectInsight[] = []; - const spanInsightGroups: { [key: string]: SpanInsight[] } = {}; + const spanInsightGroups: { [key: string]: GenericSpanInsight[] } = {}; const endpointInsightGroups: { - [key: string]: (EndpointInsight | SpanInsight)[]; + [key: string]: (GenericEndpointInsight | GenericSpanInsight)[]; } = {}; for (const insight of sortedInsights) { @@ -145,7 +152,7 @@ const groupInsights = ( const displayName = insight.spanInfo?.displayName; - if (!displayName) { + if (isFunctionInsight(insight) || !displayName) { ungroupedInsights.push(insight); continue; } @@ -315,15 +322,11 @@ const renderInsightCard = ( }); }; - const handleRecalculate = ( - prefixedCodeObjectId: string, - insightType: InsightType - ) => { - window.sendMessageToDigma({ + const handleRecalculate = (insightId: string) => { + window.sendMessageToDigma({ action: actions.RECALCULATE, payload: { - prefixedCodeObjectId, - insightType + id: insightId } }); }; @@ -337,6 +340,17 @@ const renderInsightCard = ( }); }; + const handleGoToSpan = (spanCodeObjectId: string) => { + window.sendMessageToDigma({ + action: globalActions.CHANGE_SCOPE, + payload: { + span: { + spanCodeObjectId + } + } + }); + }; + if (isSpanDurationsInsight(insight)) { return ( ); } @@ -358,6 +373,7 @@ const renderInsightCard = ( onAssetLinkClick={handleAssetLinkClick} onRecalculate={handleRecalculate} onRefresh={handleRefresh} + onGoToSpan={handleGoToSpan} /> ); } @@ -370,6 +386,7 @@ const renderInsightCard = ( onTraceButtonClick={handleTraceButtonClick} onRecalculate={handleRecalculate} onRefresh={handleRefresh} + onGoToSpan={handleGoToSpan} /> ); } @@ -383,6 +400,7 @@ const renderInsightCard = ( onRefresh={handleRefresh} onJiraTicketCreate={onJiraTicketCreate} isJiraHintEnabled={isJiraHintEnabled} + onGoToSpan={handleGoToSpan} /> ); } @@ -396,6 +414,7 @@ const renderInsightCard = ( onRefresh={handleRefresh} onJiraTicketCreate={onJiraTicketCreate} isJiraHintEnabled={isJiraHintEnabled} + onGoToSpan={handleGoToSpan} /> ); } @@ -406,6 +425,7 @@ const renderInsightCard = ( insight={insight} onRecalculate={handleRecalculate} onRefresh={handleRefresh} + onGoToSpan={handleGoToSpan} /> ); } @@ -420,6 +440,7 @@ const renderInsightCard = ( insight={insight} onRecalculate={handleRecalculate} onRefresh={handleRefresh} + onGoToSpan={handleGoToSpan} /> ); } @@ -432,6 +453,7 @@ const renderInsightCard = ( onExpandButtonClick={handleErrorsExpandButtonClick} onRecalculate={handleRecalculate} onRefresh={handleRefresh} + onGoToSpan={handleGoToSpan} /> ); } @@ -446,6 +468,7 @@ const renderInsightCard = ( onRefresh={handleRefresh} onJiraTicketCreate={onJiraTicketCreate} isJiraHintEnabled={isJiraHintEnabled} + onGoToSpan={handleGoToSpan} /> ); } @@ -460,6 +483,7 @@ const renderInsightCard = ( onRefresh={handleRefresh} onJiraTicketCreate={onJiraTicketCreate} isJiraHintEnabled={isJiraHintEnabled} + onGoToSpan={handleGoToSpan} /> ); } @@ -475,6 +499,7 @@ const renderInsightCard = ( onRefresh={handleRefresh} onJiraTicketCreate={onJiraTicketCreate} isJiraHintEnabled={isJiraHintEnabled} + onGoToSpan={handleGoToSpan} /> ); } @@ -501,6 +526,7 @@ const renderInsightCard = ( onAssetLinkClick={handleAssetLinkClick} onRecalculate={handleRecalculate} onRefresh={handleRefresh} + onGoToSpan={handleGoToSpan} /> ); } @@ -512,6 +538,7 @@ const renderInsightCard = ( insight={insight} onRecalculate={handleRecalculate} onRefresh={handleRefresh} + onGoToSpan={handleGoToSpan} /> ); } @@ -524,6 +551,7 @@ const renderInsightCard = ( onHistogramButtonClick={handleHistogramButtonClick} onRecalculate={handleRecalculate} onRefresh={handleRefresh} + onGoToSpan={handleGoToSpan} /> ); } @@ -536,6 +564,7 @@ const renderInsightCard = ( onHistogramButtonClick={handleHistogramButtonClick} onRecalculate={handleRecalculate} onRefresh={handleRefresh} + onGoToSpan={handleGoToSpan} /> ); } @@ -549,6 +578,7 @@ const renderInsightCard = ( onTraceButtonClick={handleTraceButtonClick} onRecalculate={handleRecalculate} onRefresh={handleRefresh} + onGoToSpan={handleGoToSpan} /> ); } @@ -562,6 +592,7 @@ const renderInsightCard = ( onTraceButtonClick={handleTraceButtonClick} onRecalculate={handleRecalculate} onRefresh={handleRefresh} + onGoToSpan={handleGoToSpan} /> ); } @@ -576,6 +607,7 @@ const renderInsightCard = ( onRefresh={handleRefresh} onJiraTicketCreate={onJiraTicketCreate} isJiraHintEnabled={isJiraHintEnabled} + onGoToSpan={handleGoToSpan} /> ); } @@ -587,6 +619,7 @@ const renderInsightCard = ( insight={insight} onRecalculate={handleRecalculate} onRefresh={handleRefresh} + onGoToSpan={handleGoToSpan} /> ); } @@ -602,6 +635,7 @@ const renderInsightCard = ( onRefresh={handleRefresh} onJiraTicketCreate={onJiraTicketCreate} isJiraHintEnabled={isJiraHintEnabled} + onGoToSpan={handleGoToSpan} /> ); } @@ -616,6 +650,7 @@ const renderInsightCard = ( onRecalculate={handleRecalculate} onRefresh={handleRefresh} onJiraTicketCreate={onJiraTicketCreate} + onGoToSpan={handleGoToSpan} /> ); } diff --git a/src/components/Insights/InsightList/types.ts b/src/components/Insights/InsightList/types.ts index 5c53dd29e..32fb84273 100644 --- a/src/components/Insights/InsightList/types.ts +++ b/src/components/Insights/InsightList/types.ts @@ -18,3 +18,7 @@ export interface InsightListProps { export interface isInsightJiraTicketHintShownPayload { value: boolean; } + +export interface RecalculatePayload { + id: string; +} diff --git a/src/components/Insights/Insights.stories.tsx b/src/components/Insights/Insights.stories.tsx index 7b18fec0d..a34b50a6a 100644 --- a/src/components/Insights/Insights.stories.tsx +++ b/src/components/Insights/Insights.stories.tsx @@ -8,9 +8,11 @@ import { mockedSpanNexusInsight } from "./SpanNexusInsight/mockData"; import { CodeObjectErrorsInsight, ComponentType, + EndpointSlowestSpansInsight, InsightCategory, InsightScope, InsightsStatus, + SpanNPlusOneInsight, ViewMode } from "./types"; @@ -53,6 +55,7 @@ export const Default: Story = { // needsObservabilityFix: false, insights: [ { + sourceSpanCodeObjectInsight: "sourceSpanCodeObjectInsightId", id: "60b55792-8362-4c5d-9628-7cce7979ad6d", firstDetected: "2023-12-05T17:25:47.010Z", lastDetected: "2024-01-05T13:14:47.010Z", @@ -158,8 +161,10 @@ export const Default: Story = { "method:org.springframework.samples.petclinic.domain.OwnerValidation$_$ValidateOwner", customStartTime: null, actualStartTime: "2023-07-27T08:23:56.500827Z" - }, + } as SpanNPlusOneInsight, { + hasAsyncSpans: false, + sourceSpanCodeObjectInsight: "sourceSpanCodeObjectInsightId", id: "62b55792-8262-4c5d-9628-7cce7979ad6d", firstDetected: "2023-12-05T17:25:47.010Z", lastDetected: "2024-01-05T13:14:47.010Z", @@ -232,6 +237,7 @@ export const Default: Story = { actualStartTime: "2023-06-26T00:00:00.000Z" }, { + sourceSpanCodeObjectInsight: "sourceSpanCodeObjectInsightId", id: "60b55792-3262-4c5d-9628-7cce7979ad6d", firstDetected: "2023-12-05T17:25:47.010Z", lastDetected: "2024-01-05T13:14:47.010Z", @@ -287,6 +293,7 @@ export const Default: Story = { actualStartTime: "2023-06-26T13:53:53.645Z" }, { + sourceSpanCodeObjectInsight: "sourceSpanCodeObjectInsightId", id: "60b55792-4262-4c5d-9628-7cce7979ad6d", firstDetected: "2023-12-05T17:25:47.010Z", lastDetected: "2024-01-05T13:14:47.010Z", @@ -328,6 +335,7 @@ export const Default: Story = { actualStartTime: "2023-06-26T13:53:57.956Z" }, { + sourceSpanCodeObjectInsight: "sourceSpanCodeObjectInsightId", id: "60b55792-8252-4c5d-9628-7cce7979ad6d", firstDetected: "2023-12-05T17:25:47.010Z", lastDetected: "2024-01-05T13:14:47.010Z", @@ -385,6 +393,7 @@ export const Default: Story = { actualStartTime: "2023-06-12T13:48:59.404Z" }, { + sourceSpanCodeObjectInsight: "sourceSpanCodeObjectInsightId", id: "60b55792-8262-4c5d-5628-7cce7979ad6d", firstDetected: "2023-12-05T17:25:47.010Z", lastDetected: "2024-01-05T13:14:47.010Z", @@ -474,13 +483,14 @@ export const Default: Story = { decorators: null, environment: "BOB-LAPTOP[LOCAL]", severity: 0, - isRecalculateEnabled: false, + isRecalculateEnabled: true, prefixedCodeObjectId: "method:Sample.MoneyTransfer.API.Controllers.TransferController$_$TransferFunds(TransferRequest)", customStartTime: null, actualStartTime: "2023-06-12T13:49:08.186Z" - }, + } as EndpointSlowestSpansInsight, { + sourceSpanCodeObjectInsight: "sourceSpanCodeObjectInsightId", id: "60b55792-8262-4c5d-9628-6cce7979ad6d", firstDetected: "2023-12-05T17:25:47.010Z", lastDetected: "2024-01-05T13:14:47.010Z", @@ -557,6 +567,7 @@ export const Default: Story = { actualStartTime: "2023-06-12T13:49:03.486Z" }, { + sourceSpanCodeObjectInsight: "sourceSpanCodeObjectInsightId", id: "60b55792-8262-4c5d-9628-7cce8979ad6d", firstDetected: "2023-12-05T17:25:47.010Z", lastDetected: "2024-01-05T13:14:47.010Z", @@ -650,9 +661,11 @@ export const Default: Story = { prefixedCodeObjectId: "method:Sample.MoneyTransfer.API.Controllers.TransferController$_$TransferFunds(TransferRequest)", customStartTime: null, - actualStartTime: "2023-06-13T00:00:00.000Z" + actualStartTime: "2023-06-13T00:00:00.000Z", + isAsync: false }, { + sourceSpanCodeObjectInsight: "sourceSpanCodeObjectInsightId", id: "60b55792-8262-4c5d-9628-7cce9979ad6d", firstDetected: "2023-12-05T17:25:47.010Z", lastDetected: "2024-01-05T13:14:47.010Z", @@ -806,6 +819,7 @@ export const NoObservability: Story = { }; const errorsInsight: CodeObjectErrorsInsight = { + sourceSpanCodeObjectInsight: "sourceSpanCodeObjectInsightId", id: "60b55792-8262-4c5d-9628-7cce7979ad1d", firstDetected: "2023-12-05T17:25:47.010Z", lastDetected: "2024-01-05T13:14:47.010Z", diff --git a/src/components/Insights/InsightsPage/index.tsx b/src/components/Insights/InsightsPage/index.tsx index df7c410c2..9dbf34754 100644 --- a/src/components/Insights/InsightsPage/index.tsx +++ b/src/components/Insights/InsightsPage/index.tsx @@ -7,48 +7,43 @@ import { isNumber } from "../../../typeGuards/isNumber"; import { isUndefined } from "../../../typeGuards/isUndefined"; import { InsightType } from "../../../types"; import { sendTrackingEvent } from "../../../utils/sendTrackingEvent"; +import { ChangeScopePayload } from "../../Navigation/types"; import { ConfigContext } from "../../common/App/ConfigContext"; import { Card } from "../../common/Card"; import { EmptyState } from "../../common/EmptyState"; import { CardsIcon } from "../../common/icons/CardsIcon"; -import { BottleneckInsight } from "../BottleneckInsight"; -import { DurationBreakdownInsight } from "../DurationBreakdownInsight"; -import { DurationInsight } from "../DurationInsight"; -import { DurationSlowdownSourceInsight } from "../DurationSlowdownSourceInsight"; -import { EndpointNPlusOneInsight } from "../EndpointNPlusOneInsight"; -import { EndpointQueryOptimizationInsight } from "../EndpointQueryOptimizationInsight"; -import { ErrorsInsight } from "../ErrorsInsight"; -import { ExcessiveAPICallsInsight } from "../ExcessiveAPICallsInsight"; -import { HighNumberOfQueriesInsight } from "../HighNumberOfQueriesInsight"; -import { InsightCard } from "../InsightCard"; -import { NPlusOneInsight } from "../NPlusOneInsight"; -import { NoScalingIssueInsight } from "../NoScalingIssueInsight"; -import { PerformanceAtScaleInsight } from "../PerformanceAtScaleInsight"; -import { QueryOptimizationInsight } from "../QueryOptimizationInsight"; -import { RequestBreakdownInsight } from "../RequestBreakdownInsight"; -import { ScalingIssueInsight } from "../ScalingIssueInsight"; -import { SessionInViewInsight } from "../SessionInViewInsight"; -import { SlowEndpointInsight } from "../SlowEndpointInsight"; -import { SpanBottleneckInsight } from "../SpanBottleneckInsight"; -import { SpanNexusInsight } from "../SpanNexusInsight"; -import { TopUsageInsight } from "../TopUsageInsight"; -import { TrafficInsight } from "../TrafficInsight"; import { actions } from "../actions"; +import { DurationBreakdownInsight } from "../common/insights/DurationBreakdownInsight"; +import { DurationInsight } from "../common/insights/DurationInsight"; +import { EndpointBottleneckInsight } from "../common/insights/EndpointBottleneckInsight"; +import { EndpointNPlusOneInsight } from "../common/insights/EndpointNPlusOneInsight"; +import { EndpointQueryOptimizationInsight } from "../common/insights/EndpointQueryOptimizationInsight"; +import { EndpointSlowdownSourceInsight } from "../common/insights/EndpointSlowdownSourceInsight"; +import { ExcessiveAPICallsInsight } from "../common/insights/ExcessiveAPICallsInsight"; +import { HighNumberOfQueriesInsight } from "../common/insights/HighNumberOfQueriesInsight"; +import { RequestBreakdownInsight } from "../common/insights/RequestBreakdownInsight"; +import { ScalingIssueInsight } from "../common/insights/ScalingIssueInsight"; +import { SessionInViewInsight } from "../common/insights/SessionInViewInsight"; +import { SlowEndpointInsight } from "../common/insights/SlowEndpointInsight"; +import { SpanEndpointBottleneckInsight } from "../common/insights/SpanEndpointBottleneckInsight"; +import { SpanNPlusOneInsight } from "../common/insights/SpanNPlusOneInsight"; +import { SpanNexusInsight } from "../common/insights/SpanNexusInsight"; +import { SpanQueryOptimizationInsight } from "../common/insights/SpanQueryOptimizationInsight"; +import { TopUsageInsight } from "../common/insights/TopUsageInsight"; +import { TrafficInsight } from "../common/insights/TrafficInsight"; import { Description } from "../styles"; import { trackingEvents } from "../tracking"; import { isChattyApiEndpointInsight, - isCodeObjectErrorsInsight, - isCodeObjectHotSpotInsight, + isEndpointBottleneckInsight, isEndpointBreakdownInsight, - isEndpointDurationSlowdownInsight, isEndpointHighNumberOfQueriesInsight, isEndpointHighUsageInsight, isEndpointLowUsageInsight, isEndpointNormalUsageInsight, isEndpointQueryOptimizationInsight, - isEndpointSlowestSpansInsight, - isEndpointSuspectedNPlusOneInsight, + isEndpointSlowdownSourceInsight, + isEndpointSpanNPlusOneInsight, isSessionInViewEndpointInsight, isSlowEndpointInsight, isSpanDurationBreakdownInsight, @@ -58,13 +53,16 @@ import { isSpanNexusInsight, isSpanQueryOptimizationInsight, isSpanScalingBadlyInsight, - isSpanScalingInsufficientDataInsight, - isSpanScalingWellInsight, isSpanUsagesInsight } from "../typeGuards"; import { CodeObjectInsight, GenericCodeObjectInsight, Trace } from "../types"; import * as s from "./styles"; -import { InsightPageProps, isInsightJiraTicketHintShownPayload } from "./types"; +import { + InsightPageProps, + MarkInsightTypesAsViewedPayload, + RecalculatePayload, + isInsightJiraTicketHintShownPayload +} from "./types"; const getInsightToShowJiraHint = (insights: CodeObjectInsight[]): number => { const insightsWithJiraButton = [ @@ -93,23 +91,23 @@ const renderInsightCard = ( isJiraHintEnabled: boolean, onRefresh: () => void ): JSX.Element | undefined => { - const handleErrorSelect = (errorId: string, insightType: InsightType) => { - sendTrackingEvent(globalTrackingEvents.USER_ACTION, { - action: `Follow ${insightType} link` - }); - window.sendMessageToDigma({ - action: actions.GO_TO_ERROR, - payload: { - errorId - } - }); - }; + // const handleErrorSelect = (errorId: string, insightType: InsightType) => { + // sendTrackingEvent(globalTrackingEvents.USER_ACTION, { + // action: `Follow ${insightType} link` + // }); + // window.sendMessageToDigma({ + // action: actions.GO_TO_ERROR, + // payload: { + // errorId + // } + // }); + // }; - const handleErrorsExpandButtonClick = () => { - window.sendMessageToDigma({ - action: actions.GO_TO_ERRORS - }); - }; + // const handleErrorsExpandButtonClick = () => { + // window.sendMessageToDigma({ + // action: actions.GO_TO_ERRORS + // }); + // }; const handleHistogramButtonClick = ( instrumentationLibrary: string, @@ -180,15 +178,22 @@ const renderInsightCard = ( }); }; - const handleRecalculate = ( - prefixedCodeObjectId: string, - insightType: InsightType - ) => { - window.sendMessageToDigma({ + const handleRecalculate = (insightId: string) => { + window.sendMessageToDigma({ action: actions.RECALCULATE, payload: { - prefixedCodeObjectId, - insightType + id: insightId + } + }); + }; + + const handleGoToSpan = (spanCodeObjectId: string) => { + window.sendMessageToDigma({ + action: globalActions.CHANGE_SCOPE, + payload: { + span: { + spanCodeObjectId + } } }); }; @@ -203,9 +208,11 @@ const renderInsightCard = ( onCompareButtonClick={handleCompareButtonClick} onRecalculate={handleRecalculate} onRefresh={onRefresh} + onGoToSpan={handleGoToSpan} /> ); } + if (isSpanDurationBreakdownInsight(insight)) { return ( ); } + if (isSpanUsagesInsight(insight)) { return ( ); } - if (isSpanEndpointBottleneckInsight(insight)) { + + if (isEndpointBottleneckInsight(insight)) { return ( - ); } - if (isEndpointSlowestSpansInsight(insight)) { + + if (isSpanEndpointBottleneckInsight(insight)) { return ( - ); } + if (isSlowEndpointInsight(insight)) { return ( ); } + if ( isEndpointLowUsageInsight(insight) || isEndpointNormalUsageInsight(insight) || @@ -276,22 +293,12 @@ const renderInsightCard = ( insight={insight} onRecalculate={handleRecalculate} onRefresh={onRefresh} + onGoToSpan={handleGoToSpan} /> ); } - if (isCodeObjectErrorsInsight(insight)) { - return ( - - ); - } - if (isEndpointSuspectedNPlusOneInsight(insight)) { + + if (isEndpointSpanNPlusOneInsight(insight)) { return ( ); } + if (isSpanNPlusOneInsight(insight)) { return ( - ); } + if (isSpanScalingBadlyInsight(insight)) { return ( ); } - if (isCodeObjectHotSpotInsight(insight)) { - return ( - - Major errors occur or propagate through this function - - } - onRecalculate={handleRecalculate} - onRefresh={onRefresh} - /> - ); - } - if (isEndpointDurationSlowdownInsight(insight)) { + + if (isEndpointSlowdownSourceInsight(insight)) { return ( - ); } @@ -367,30 +367,7 @@ const renderInsightCard = ( insight={insight} onRecalculate={handleRecalculate} onRefresh={onRefresh} - /> - ); - } - - if (isSpanScalingWellInsight(insight)) { - return ( - - ); - } - - if (isSpanScalingInsufficientDataInsight(insight)) { - return ( - ); } @@ -404,6 +381,7 @@ const renderInsightCard = ( onTraceButtonClick={handleTraceButtonClick} onRecalculate={handleRecalculate} onRefresh={onRefresh} + onGoToSpan={handleGoToSpan} /> ); } @@ -417,6 +395,7 @@ const renderInsightCard = ( onTraceButtonClick={handleTraceButtonClick} onRecalculate={handleRecalculate} onRefresh={onRefresh} + onGoToSpan={handleGoToSpan} /> ); } @@ -431,6 +410,7 @@ const renderInsightCard = ( onRefresh={onRefresh} onJiraTicketCreate={onJiraTicketCreate} isJiraHintEnabled={isJiraHintEnabled} + onGoToSpan={handleGoToSpan} /> ); } @@ -442,13 +422,14 @@ const renderInsightCard = ( insight={insight} onRecalculate={handleRecalculate} onRefresh={onRefresh} + onGoToSpan={handleGoToSpan} /> ); } if (isSpanQueryOptimizationInsight(insight)) { return ( - ); } @@ -472,6 +454,7 @@ const renderInsightCard = ( onRefresh={onRefresh} onJiraTicketCreate={onJiraTicketCreate} isJiraHintEnabled={isJiraHintEnabled} + onGoToSpan={handleGoToSpan} /> ); } @@ -506,7 +489,7 @@ export const InsightsPage = (props: InsightPageProps) => { }, [previousPage, props.page, config, previousConfig]); useEffect(() => { - window.sendMessageToDigma({ + window.sendMessageToDigma({ action: actions.MARK_INSIGHT_TYPES_AS_VIEWED, payload: { insightTypes: props.insights.map((x) => ({ diff --git a/src/components/Insights/InsightsPage/types.ts b/src/components/Insights/InsightsPage/types.ts index 53a9e870c..a12a2b305 100644 --- a/src/components/Insights/InsightsPage/types.ts +++ b/src/components/Insights/InsightsPage/types.ts @@ -1,4 +1,4 @@ -import { GenericCodeObjectInsight } from "../types"; +import { GenericCodeObjectInsight, InsightType } from "../types"; export interface InsightPageProps { insights: GenericCodeObjectInsight[]; @@ -14,3 +14,14 @@ export interface InsightPageProps { export interface isInsightJiraTicketHintShownPayload { value: boolean; } + +export interface MarkInsightTypesAsViewedPayload { + insightTypes: { + type: InsightType; + reopenCount: number; + }[]; +} + +export interface RecalculatePayload { + id: string; +} diff --git a/src/components/Insights/NPlusOneInsight/index.tsx b/src/components/Insights/NPlusOneInsight/index.tsx index d85fe011b..bb7fa678a 100644 --- a/src/components/Insights/NPlusOneInsight/index.tsx +++ b/src/components/Insights/NPlusOneInsight/index.tsx @@ -44,6 +44,8 @@ export const NPlusOneInsight = (props: NPlusOneInsightProps) => { const spanCodeObjectId = props.insight.clientSpanCodeObjectId || undefined; const traceId = props.insight.traceId; + const endpoints = props.insight.endpoints || []; + return ( { Affected endpoints: - {props.insight.endpoints.map((x) => { + {endpoints.map((x) => { const spanCodeObjectId = x.endpointInfo.entrySpanCodeObjectId; const route = trimEndpointScheme(x.endpointInfo.route); diff --git a/src/components/Insights/NPlusOneInsight/mockData.ts b/src/components/Insights/NPlusOneInsight/mockData.ts index 338c6c527..7a51ce12e 100644 --- a/src/components/Insights/NPlusOneInsight/mockData.ts +++ b/src/components/Insights/NPlusOneInsight/mockData.ts @@ -2,6 +2,7 @@ import { InsightType } from "../../../types"; import { InsightCategory, InsightScope, SpanNPlusOneInsight } from "../types"; export const mockedNPlusOneInsight: SpanNPlusOneInsight = { + sourceSpanCodeObjectInsight: "sourceSpanCodeObjectInsightId", id: "60b55792-8262-4c5d-9628-8cce7979ad6d", firstDetected: "2023-12-05T17:25:47.010Z", lastDetected: "2024-01-05T13:14:47.010Z", @@ -54,7 +55,15 @@ export const mockedNPlusOneInsight: SpanNPlusOneInsight = { occurrences: 100, criticality: 0.8, impact: 0, - severity: 0 + severity: 0, + requestPercentage: 50, + traceId: "00D37A4E7208E0F6E89AA7E2E37446A6", + commitId: "a1b2c3d", + duration: { + value: 1.64, + unit: "sec", + raw: 1636050588.0 + } } ], scope: InsightScope.Span, diff --git a/src/components/Insights/NoScalingIssueInsight/NoScalingIssueInsight.stories.tsx b/src/components/Insights/NoScalingIssueInsight/NoScalingIssueInsight.stories.tsx index 84a187e36..088953766 100644 --- a/src/components/Insights/NoScalingIssueInsight/NoScalingIssueInsight.stories.tsx +++ b/src/components/Insights/NoScalingIssueInsight/NoScalingIssueInsight.stories.tsx @@ -20,6 +20,7 @@ type Story = StoryObj; export const Default: Story = { args: { insight: { + sourceSpanCodeObjectInsight: "sourceSpanCodeObjectInsightId", id: "60b55792-8262-4c7d-9628-7cce7979ad6d", firstDetected: "2023-12-05T17:25:47.010Z", lastDetected: "2024-01-05T13:14:47.010Z", diff --git a/src/components/Insights/PerformanceAtScaleInsight/PerformanceAtScaleInsight.stories.tsx b/src/components/Insights/PerformanceAtScaleInsight/PerformanceAtScaleInsight.stories.tsx index 5cf80a451..5326df24c 100644 --- a/src/components/Insights/PerformanceAtScaleInsight/PerformanceAtScaleInsight.stories.tsx +++ b/src/components/Insights/PerformanceAtScaleInsight/PerformanceAtScaleInsight.stories.tsx @@ -20,6 +20,7 @@ type Story = StoryObj; export const Default: Story = { args: { insight: { + sourceSpanCodeObjectInsight: "sourceSpanCodeObjectInsightId", id: "60b55492-8262-4c5d-9628-7cce7979ad6d", firstDetected: "2023-12-05T17:25:47.010Z", lastDetected: "2024-01-05T13:14:47.010Z", diff --git a/src/components/Insights/PerformanceAtScaleInsight/styles.ts b/src/components/Insights/PerformanceAtScaleInsight/styles.ts index ad206066f..d260f5cc7 100644 --- a/src/components/Insights/PerformanceAtScaleInsight/styles.ts +++ b/src/components/Insights/PerformanceAtScaleInsight/styles.ts @@ -44,7 +44,7 @@ export const TableHeaderCell = styled.th` padding: 0; &:last-child { - text-align: right; + text-align: end; } `; @@ -57,11 +57,11 @@ export const TableBodyCell = styled.td` padding: 0; &:first-child { - text-align: left; + text-align: start; } &:last-child { - text-align: right; + text-align: end; } `; diff --git a/src/components/Insights/QueryOptimizationInsight/index.tsx b/src/components/Insights/QueryOptimizationInsight/index.tsx index 9b34c906b..f2a14505d 100644 --- a/src/components/Insights/QueryOptimizationInsight/index.tsx +++ b/src/components/Insights/QueryOptimizationInsight/index.tsx @@ -49,6 +49,7 @@ export const QueryOptimizationInsight = ( const spanCodeObjectId = props.insight.spanInfo?.spanCodeObjectId || undefined; const traceId = props.insight.traceId; + const endpoints = props.insight.endpoints || []; return ( Affected endpoints: - {props.insight.endpoints.map((x) => { + {endpoints.map((x) => { const spanCodeObjectId = x.endpointInfo.spanCodeObjectId; const route = trimEndpointScheme(x.endpointInfo.route); diff --git a/src/components/Insights/QueryOptimizationInsight/mockData.ts b/src/components/Insights/QueryOptimizationInsight/mockData.ts index da81088e0..841d06801 100644 --- a/src/components/Insights/QueryOptimizationInsight/mockData.ts +++ b/src/components/Insights/QueryOptimizationInsight/mockData.ts @@ -6,6 +6,7 @@ import { } from "../types"; export const mockedQueryOptimizationInsight: QueryOptimizationInsight = { + sourceSpanCodeObjectInsight: "sourceSpanCodeObjectInsightId", id: "60b55792-8262-4c8d-9628-7cce7979ad6d", firstDetected: "2023-12-05T17:25:47.010Z", lastDetected: "2024-01-05T13:14:47.010Z", diff --git a/src/components/Insights/RequestBreakdownInsight/RequestBreakdownInsight.stories.tsx b/src/components/Insights/RequestBreakdownInsight/RequestBreakdownInsight.stories.tsx index 7e6fdb610..4ca60c889 100644 --- a/src/components/Insights/RequestBreakdownInsight/RequestBreakdownInsight.stories.tsx +++ b/src/components/Insights/RequestBreakdownInsight/RequestBreakdownInsight.stories.tsx @@ -23,6 +23,7 @@ export default meta; type Story = StoryObj; const data: EndpointBreakdownInsight = { + sourceSpanCodeObjectInsight: "sourceSpanCodeObjectInsightId", id: "60b55792-8262-4c5d-9628-7cce7979dd6d", firstDetected: "2023-12-05T17:25:47.010Z", lastDetected: "2024-01-05T13:14:47.010Z", diff --git a/src/components/Insights/RequestBreakdownInsight/styles.ts b/src/components/Insights/RequestBreakdownInsight/styles.ts index d03eb11ed..976306801 100644 --- a/src/components/Insights/RequestBreakdownInsight/styles.ts +++ b/src/components/Insights/RequestBreakdownInsight/styles.ts @@ -97,7 +97,7 @@ export const TableHead = styled.thead` `; export const TableHeaderCell = styled.th` - text-align: left; + text-align: start; font-weight: 400; padding-left: 4px; padding-bottom: 8px; @@ -128,7 +128,7 @@ export const TableBodyCell = styled.td` &:last-child { padding: 4px 4px 4px 22px; - text-align: right; + text-align: end; } `; diff --git a/src/components/Insights/ScalingIssueInsight/index.tsx b/src/components/Insights/ScalingIssueInsight/index.tsx index 6e98ffda2..78f94f607 100644 --- a/src/components/Insights/ScalingIssueInsight/index.tsx +++ b/src/components/Insights/ScalingIssueInsight/index.tsx @@ -1,6 +1,7 @@ import { useContext } from "react"; import { InsightType } from "../../../types"; import { getDurationString } from "../../../utils/getDurationString"; +import { sendTrackingEvent } from "../../../utils/sendTrackingEvent"; import { trimEndpointScheme } from "../../../utils/trimEndpointScheme"; import { ConfigContext } from "../../common/App/ConfigContext"; import { Button } from "../../common/Button"; @@ -9,14 +10,12 @@ import { ChartIcon } from "../../common/icons/ChartIcon"; import { CrosshairIcon } from "../../common/icons/CrosshairIcon"; import { InsightCard } from "../InsightCard"; import { Criticality } from "../common/Criticality"; +import { JiraButton } from "../common/JiraButton"; import { Description, Link } from "../styles"; +import { trackingEvents } from "../tracking"; import { Trace } from "../types"; import * as s from "./styles"; import { ScalingIssueInsightProps } from "./types"; -import { JiraButton } from "../common/JiraButton"; -import { sendTrackingEvent } from "../../../utils/sendTrackingEvent"; -import { trackingEvents } from "../tracking"; -import { ButtonsContainer } from "./styles"; export const ScalingIssueInsight = (props: ScalingIssueInsightProps) => { const config = useContext(ConfigContext); @@ -55,6 +54,8 @@ export const ScalingIssueInsight = (props: ScalingIssueInsightProps) => { props.onJiraTicketCreate(props.insight, spanCodeObjectId, event); }; + const affectedEndpoints = props.insight.affectedEndpoints || []; + return ( { })} )} - {props.insight.affectedEndpoints.length > 0 && ( + {affectedEndpoints.length > 0 && ( Affected endpoints: - {props.insight.affectedEndpoints.map((endpoint) => { + {affectedEndpoints.map((endpoint) => { const endpointRoute = trimEndpointScheme(endpoint.route); return ( diff --git a/src/components/Insights/ScalingIssueInsight/mockData.ts b/src/components/Insights/ScalingIssueInsight/mockData.ts index 2915783e1..ca1c101dd 100644 --- a/src/components/Insights/ScalingIssueInsight/mockData.ts +++ b/src/components/Insights/ScalingIssueInsight/mockData.ts @@ -1,11 +1,12 @@ import { InsightType } from "../../../types"; import { - SpanScalingBadlyInsight, InsightCategory, - InsightScope + InsightScope, + SpanScalingBadlyInsight } from "../types"; export const mockedSpanScalingInsight: SpanScalingBadlyInsight = { + sourceSpanCodeObjectInsight: "sourceSpanCodeObjectInsightId", id: "90b55792-8262-4c5d-9628-7cce7979ad6d", firstDetected: "2023-12-05T17:25:47.010Z", lastDetected: "2024-01-05T13:14:47.010Z", diff --git a/src/components/Insights/SessionInViewInsight/SessionInViewInsight.stories.tsx b/src/components/Insights/SessionInViewInsight/SessionInViewInsight.stories.tsx index 0b86f65a8..22ecf1ce5 100644 --- a/src/components/Insights/SessionInViewInsight/SessionInViewInsight.stories.tsx +++ b/src/components/Insights/SessionInViewInsight/SessionInViewInsight.stories.tsx @@ -20,6 +20,7 @@ type Story = StoryObj; export const Default: Story = { args: { insight: { + sourceSpanCodeObjectInsight: "sourceSpanCodeObjectInsightId", id: "60b55792-8262-4c5a-9628-7cce7979ad6d", firstDetected: "2023-12-05T17:25:47.010Z", lastDetected: "2024-01-05T13:14:47.010Z", diff --git a/src/components/Insights/SlowEndpointInsight/SlowEndpointInsight.stories.tsx b/src/components/Insights/SlowEndpointInsight/SlowEndpointInsight.stories.tsx index 217bef041..9999a6d01 100644 --- a/src/components/Insights/SlowEndpointInsight/SlowEndpointInsight.stories.tsx +++ b/src/components/Insights/SlowEndpointInsight/SlowEndpointInsight.stories.tsx @@ -20,6 +20,7 @@ type Story = StoryObj; export const Default: Story = { args: { insight: { + sourceSpanCodeObjectInsight: "sourceSpanCodeObjectInsightId", id: "60b55792-8262-4c3d-9628-7cce7979ad6d", firstDetected: "2023-12-05T17:25:47.010Z", lastDetected: "2024-01-05T13:14:47.010Z", diff --git a/src/components/Insights/SpanBottleneckInsight/mockData.ts b/src/components/Insights/SpanBottleneckInsight/mockData.ts index 7bbecdffa..cbff1e07d 100644 --- a/src/components/Insights/SpanBottleneckInsight/mockData.ts +++ b/src/components/Insights/SpanBottleneckInsight/mockData.ts @@ -6,6 +6,7 @@ import { } from "../types"; export const mockedSpanBottleneckInsight: EndpointSlowestSpansInsight = { + sourceSpanCodeObjectInsight: "sourceSpanCodeObjectInsightId", id: "60b55792-8262-4c5d-9628-7dce7979ad6d", firstDetected: "2023-12-05T17:25:47.010Z", lastDetected: "2024-01-05T13:14:47.010Z", diff --git a/src/components/Insights/SpanNexusInsight/mockData.ts b/src/components/Insights/SpanNexusInsight/mockData.ts index 0dc1395b6..f07736609 100644 --- a/src/components/Insights/SpanNexusInsight/mockData.ts +++ b/src/components/Insights/SpanNexusInsight/mockData.ts @@ -2,6 +2,7 @@ import { InsightType } from "../../../types"; import { InsightCategory, InsightScope, SpanNexusInsight } from "../types"; export const mockedSpanNexusInsight: SpanNexusInsight = { + sourceSpanCodeObjectInsight: "sourceSpanCodeObjectInsightId", id: "60b54792-8262-4c5d-9628-7cce7979ad6d", firstDetected: "2023-12-05T17:25:47.010Z", lastDetected: "2024-01-05T13:14:47.010Z", diff --git a/src/components/Insights/TopUsageInsight/TopUsageInsight.stories.tsx b/src/components/Insights/TopUsageInsight/TopUsageInsight.stories.tsx index 27571f955..f23daf0fa 100644 --- a/src/components/Insights/TopUsageInsight/TopUsageInsight.stories.tsx +++ b/src/components/Insights/TopUsageInsight/TopUsageInsight.stories.tsx @@ -20,6 +20,7 @@ type Story = StoryObj; export const Default: Story = { args: { insight: { + sourceSpanCodeObjectInsight: "sourceSpanCodeObjectInsightId", id: "60b55792-8162-4c5d-9628-7cce7979ad6d", firstDetected: "2023-12-05T17:25:47.010Z", lastDetected: "2024-01-05T13:14:47.010Z", diff --git a/src/components/Insights/TrafficInsight/TrafficInsight.stories.tsx b/src/components/Insights/TrafficInsight/TrafficInsight.stories.tsx index babc350ce..299d0bbcd 100644 --- a/src/components/Insights/TrafficInsight/TrafficInsight.stories.tsx +++ b/src/components/Insights/TrafficInsight/TrafficInsight.stories.tsx @@ -20,6 +20,7 @@ type Story = StoryObj; export const LowTraffic: Story = { args: { insight: { + sourceSpanCodeObjectInsight: "sourceSpanCodeObjectInsightId", id: "60b55792-8262-3c5d-9628-7cce7979ad6d", firstDetected: "2023-12-05T17:25:47.010Z", lastDetected: "2024-01-05T13:14:47.010Z", @@ -82,6 +83,7 @@ export const LowTraffic: Story = { export const HighTraffic: Story = { args: { insight: { + sourceSpanCodeObjectInsight: "sourceSpanCodeObjectInsightId", id: "60b55792-8262-3c5d-9628-7cce7979ad6d", firstDetected: "2023-12-05T17:25:47.010Z", lastDetected: "2024-01-05T13:14:47.010Z", diff --git a/src/components/Insights/common/DurationChange/index.tsx b/src/components/Insights/common/DurationChange/index.tsx index c7778f1bd..f1545837c 100644 --- a/src/components/Insights/common/DurationChange/index.tsx +++ b/src/components/Insights/common/DurationChange/index.tsx @@ -93,7 +93,7 @@ export const DurationChange = (props: DurationChangeProps) => { diff --git a/src/components/Insights/common/Info/index.tsx b/src/components/Insights/common/Info/index.tsx new file mode 100644 index 000000000..1b2a4e30b --- /dev/null +++ b/src/components/Insights/common/Info/index.tsx @@ -0,0 +1,12 @@ +import { InfoCircleIcon } from "../../../common/icons/InfoCircleIcon"; +import { Tooltip } from "../../../common/v3/Tooltip"; +import * as s from "./styles"; + +export const Info = (props: { text: string; name: string }) => ( + + +
{props.name}
+ +
+
+); diff --git a/src/components/Insights/common/Info/styles.ts b/src/components/Insights/common/Info/styles.ts new file mode 100644 index 000000000..7ba3943ef --- /dev/null +++ b/src/components/Insights/common/Info/styles.ts @@ -0,0 +1,7 @@ +import styled from "styled-components"; + +export const InfoContainer = styled.div` + display: flex; + gap: 4px; + align-items: center; +`; diff --git a/src/components/Insights/common/InsightCard/IconButton/styles.ts b/src/components/Insights/common/InsightCard/IconButton/styles.ts index 5a5816be7..07613805f 100644 --- a/src/components/Insights/common/InsightCard/IconButton/styles.ts +++ b/src/components/Insights/common/InsightCard/IconButton/styles.ts @@ -8,7 +8,7 @@ export const Button = styled.button` padding: 4px; border: none; background: none; - color: ${({ theme }) => theme.colors.v3.icon.disabled}; + color: ${({ theme }) => theme.colors.v3.icon.tertiary}; &:disabled { cursor: initial; diff --git a/src/components/Insights/common/InsightCard/InsightCard.stories.tsx b/src/components/Insights/common/InsightCard/InsightCard.stories.tsx index 96c932da4..759aff60f 100644 --- a/src/components/Insights/common/InsightCard/InsightCard.stories.tsx +++ b/src/components/Insights/common/InsightCard/InsightCard.stories.tsx @@ -19,6 +19,35 @@ type Story = StoryObj; export const Default: Story = { args: { isAsync: true, - insight: mockedEndpointNPlusOneInsight + insight: { ...mockedEndpointNPlusOneInsight, criticality: 0.9 } + } +}; + +export const JiraButtonIsPrimary: Story = { + args: { + isAsync: true, + insight: { ...mockedEndpointNPlusOneInsight, criticality: 0.9 }, + onGoToLive: undefined, + onDismiss: undefined, + onPin: undefined, + onGoToTrace: undefined, + onOpenHistogram: undefined + } +}; + +export const LinkedJiraTicket: Story = { + args: { + isAsync: true, + insight: { ...mockedEndpointNPlusOneInsight, criticality: 0.9 }, + jiraTicketInfo: { + ticketLink: "some", + spanCodeObjectId: "test", + isHintEnabled: false + }, + onGoToLive: undefined, + onDismiss: undefined, + onPin: undefined, + onGoToTrace: undefined, + onOpenHistogram: undefined } }; diff --git a/src/components/Insights/common/InsightCard/InsightHeader/AsyncTag/styles.ts b/src/components/Insights/common/InsightCard/InsightHeader/AsyncTag/styles.ts index c557d044a..d5c972b9d 100644 --- a/src/components/Insights/common/InsightCard/InsightHeader/AsyncTag/styles.ts +++ b/src/components/Insights/common/InsightCard/InsightHeader/AsyncTag/styles.ts @@ -1,8 +1,10 @@ import styled from "styled-components"; +import { footnoteRegularTypography } from "../../../../../common/App/typographies"; import { Tag as TagCommon } from "../../../../../common/v3/Tag"; export const AsyncTag = styled(TagCommon)` + ${footnoteRegularTypography} + color: ${({ theme }) => theme.colors.v3.text.primary}; background: ${({ theme }) => theme.colors.v3.surface.brandDark}; - font-size: 12px; `; diff --git a/src/components/Insights/common/InsightCard/InsightHeader/index.tsx b/src/components/Insights/common/InsightCard/InsightHeader/index.tsx index 99577dbd3..b72dd0ce3 100644 --- a/src/components/Insights/common/InsightCard/InsightHeader/index.tsx +++ b/src/components/Insights/common/InsightCard/InsightHeader/index.tsx @@ -1,5 +1,13 @@ -import { getInsightTypeInfo } from "../../../../../utils/getInsightTypeInfo"; +import { useContext } from "react"; +import { + InsightTypeInfo, + getInsightTypeInfo +} from "../../../../../utils/getInsightTypeInfo"; +import { roundTo } from "../../../../../utils/roundTo"; +import { ConfigContext } from "../../../../common/App/ConfigContext"; import { InfoCircleIcon } from "../../../../common/icons/InfoCircleIcon"; +import { WarningTriangleIcon } from "../../../../common/icons/WarningTriangleIcon"; +import { Link } from "../../../../common/v3/Link"; import { NewTag } from "../../../../common/v3/NewTag"; import { Tag } from "../../../../common/v3/Tag"; import { TagType } from "../../../../common/v3/Tag/types"; @@ -8,51 +16,92 @@ import { AsyncTag } from "./AsyncTag"; import * as s from "./styles"; import { InsightHeaderProps } from "./types"; -export const getTagType = (importance: number): TagType => { - if (importance === 0) { - return "default"; +const getTagType = (criticality: number): TagType => { + if (criticality < 0.2) { + return "lowSeverity"; } - if (importance < 3) { - return "highSeverity"; - } - if (importance < 7) { + + if (criticality < 0.6) { return "mediumSeverity"; } - return "lowSeverity"; + + return "highSeverity"; +}; + +const getTagTitle = ( + insightTypeInfo: InsightTypeInfo | undefined, + criticality: number +) => { + const title = `${ + insightTypeInfo ? `${insightTypeInfo.label}\n` : "" + }Criticality: ${roundTo(criticality * 100, 0)}%`; + + return {title}; }; export const InsightHeader = (props: InsightHeaderProps) => { + const config = useContext(ConfigContext); + const insightTypeInfo = getInsightTypeInfo(props.insightType); - const tagType = getTagType(props.importance); + const tagType = getTagType(props.criticality); + const tagTitle = getTagTitle(insightTypeInfo, props.criticality); + + const handleSpanLinkClick = () => { + if (props.spanInfo) { + props.onSpanLinkClick(props.spanInfo.spanCodeObjectId); + } + }; return ( - {insightTypeInfo && ( - } - /> - )} - - {insightTypeInfo?.label} - {insightTypeInfo?.description && ( - }> - - - + + {insightTypeInfo && ( + + + } + /> )} - - - {props.isAsync && } - {props.isNew && } - {props.isActive && ( - - - Active - - )} - + + {insightTypeInfo?.label} + {insightTypeInfo?.description && ( + }> + + + + + )} + + + {props.criticality > 0.8 && ( + + + + + + )} + {props.isAsync && } + {props.isNew && } + {props.isActive && ( + + + Active + + )} + + + {!config.scope?.span && props.spanInfo && ( + + + + {props.spanInfo.displayName} + + + + )} ); }; diff --git a/src/components/Insights/common/InsightCard/InsightHeader/styles.ts b/src/components/Insights/common/InsightCard/InsightHeader/styles.ts index 817f0d98a..4d9672e21 100644 --- a/src/components/Insights/common/InsightCard/InsightHeader/styles.ts +++ b/src/components/Insights/common/InsightCard/InsightHeader/styles.ts @@ -1,12 +1,30 @@ import styled from "styled-components"; -import { bodyMediumTypography } from "../../../../common/App/typographies"; +import { + bodyMediumTypography, + footnoteRegularTypography +} from "../../../../common/App/typographies"; export const Container = styled.div` + display: flex; + flex-direction: column; + gap: 4px; +`; + +export const TitleRow = styled.div` display: flex; align-items: center; gap: 8px; `; +export const SpanInfoRow = styled.div` + display: flex; + padding: 0 32px; +`; + +export const TagTitle = styled.span` + white-space: pre; +`; + export const InfoContainer = styled.div` display: flex; `; @@ -19,16 +37,22 @@ export const Label = styled.div` align-items: center; `; -export const Tags = styled.div` +export const BadgeContainer = styled.div` margin-left: auto; display: flex; + align-items: center; gap: 8px; height: 24px; - font-size: 12px; - line-height: 16px; +`; + +export const WarningTriangleContainer = styled.div` + display: flex; + color: ${({ theme }) => theme.colors.v3.status.high}; `; export const Active = styled.div` + ${footnoteRegularTypography} + display: flex; align-items: center; gap: 4px; diff --git a/src/components/Insights/common/InsightCard/InsightHeader/types.ts b/src/components/Insights/common/InsightCard/InsightHeader/types.ts index 1520ffd21..6afed02bb 100644 --- a/src/components/Insights/common/InsightCard/InsightHeader/types.ts +++ b/src/components/Insights/common/InsightCard/InsightHeader/types.ts @@ -1,3 +1,5 @@ +import { SpanInfo } from "../../../../../types"; + export interface InsightHeaderProps { insightType: string; isActive?: boolean; @@ -5,4 +7,7 @@ export interface InsightHeaderProps { importance: number; isAsync?: boolean; isNew?: boolean; + criticality: number; + spanInfo?: SpanInfo | null; + onSpanLinkClick: (spanCodeObjectId: string) => void; } diff --git a/src/components/Insights/common/InsightCard/KeyValue/index.tsx b/src/components/Insights/common/InsightCard/KeyValue/index.tsx index 590d776db..fb4bb298e 100644 --- a/src/components/Insights/common/InsightCard/KeyValue/index.tsx +++ b/src/components/Insights/common/InsightCard/KeyValue/index.tsx @@ -1,11 +1,17 @@ +import { ForwardedRef, forwardRef } from "react"; import * as s from "./styles"; import { KeyValueProps } from "./types"; -export const KeyValue = (props: KeyValueProps) => { +const KeyValueComponent = ( + props: KeyValueProps, + ref: ForwardedRef +) => { return ( - + {props.label} {props.children} ); }; + +export const KeyValue = forwardRef(KeyValueComponent); diff --git a/src/components/Insights/common/InsightCard/KeyValue/styles.ts b/src/components/Insights/common/InsightCard/KeyValue/styles.ts index 08b90f906..4ee1589f7 100644 --- a/src/components/Insights/common/InsightCard/KeyValue/styles.ts +++ b/src/components/Insights/common/InsightCard/KeyValue/styles.ts @@ -1,5 +1,8 @@ import styled from "styled-components"; -import { caption1RegularTypography } from "../../../../common/App/typographies"; +import { + footnoteRegularTypography, + subscriptRegularTypography +} from "../../../../common/App/typographies"; export const Container = styled.div` display: flex; @@ -8,10 +11,13 @@ export const Container = styled.div` `; export const Key = styled.div` + ${footnoteRegularTypography} + color: ${({ theme }) => theme.colors.v3.text.secondary}; - ${caption1RegularTypography} `; export const Value = styled.div` + ${subscriptRegularTypography} + color: ${({ theme }) => theme.colors.v3.text.primary}; `; diff --git a/src/components/Insights/common/InsightCard/KeyValue/types.ts b/src/components/Insights/common/InsightCard/KeyValue/types.ts index 76b1890e6..2903167c6 100644 --- a/src/components/Insights/common/InsightCard/KeyValue/types.ts +++ b/src/components/Insights/common/InsightCard/KeyValue/types.ts @@ -3,4 +3,5 @@ import { ReactNode } from "react"; export interface KeyValueProps { label: ReactNode; children: ReactNode; + className?: string; } diff --git a/src/components/Insights/common/InsightCard/ListItem/index.tsx b/src/components/Insights/common/InsightCard/ListItem/index.tsx index b03113d73..59d8b3888 100644 --- a/src/components/Insights/common/InsightCard/ListItem/index.tsx +++ b/src/components/Insights/common/InsightCard/ListItem/index.tsx @@ -1,27 +1,26 @@ -import { MouseEvent } from "react"; +import { ForwardedRef, MouseEvent, forwardRef } from "react"; +import { Link } from "../../../../common/v3/Link"; import { Tooltip } from "../../../../common/v3/Tooltip"; import * as s from "./styles"; import { ListItemProps } from "./types"; -export const ListItem = ({ - name, - onClick, - className, - buttons -}: ListItemProps) => { +const ListItemComponent = ( + { name, onClick, className, buttons }: ListItemProps, + ref: ForwardedRef +) => { const handleClick = (e: MouseEvent) => { e.preventDefault(); onClick(); }; return ( - + - - {name} - + {name} {buttons && {buttons}} ); }; + +export const ListItem = forwardRef(ListItemComponent); diff --git a/src/components/Insights/common/InsightCard/ListItem/styles.ts b/src/components/Insights/common/InsightCard/ListItem/styles.ts index 16aa8bf4c..39dc20c96 100644 --- a/src/components/Insights/common/InsightCard/ListItem/styles.ts +++ b/src/components/Insights/common/InsightCard/ListItem/styles.ts @@ -1,5 +1,4 @@ import styled from "styled-components"; -import { footnoteRegularTypography } from "../../../../common/App/typographies"; export const Container = styled.div` display: flex; @@ -9,18 +8,8 @@ export const Container = styled.div` padding: 4px; border-radius: 4px; background: ${({ theme }) => theme.colors.v3.surface.primary}; -`; - -export const Link = styled.a` - ${footnoteRegularTypography} - - cursor: pointer; - color: ${({ theme }) => theme.colors.v3.text.link}; - text-decoration: none; - display: block; - text-overflow: ellipsis; - white-space: nowrap; - overflow: hidden; + height: 28px; + box-sizing: border-box; `; export const ButtonsContainer = styled.div` diff --git a/src/components/Insights/common/InsightCard/PercentileViewModeToggle/PercentileViewModeToggle.stories.tsx b/src/components/Insights/common/InsightCard/PercentileViewModeToggle/PercentileViewModeToggle.stories.tsx new file mode 100644 index 000000000..5ffc91ca4 --- /dev/null +++ b/src/components/Insights/common/InsightCard/PercentileViewModeToggle/PercentileViewModeToggle.stories.tsx @@ -0,0 +1,22 @@ +import { Meta, StoryObj } from "@storybook/react"; +import { PercentileViewModeToggle } from "."; + +// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction +const meta: Meta = { + title: "Insights/common/InsightCard/PercentileViewModeToggle", + component: PercentileViewModeToggle, + parameters: { + // More on how to position stories at: https://storybook.js.org/docs/react/configure/story-layout + layout: "fullscreen" + } +}; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = { + args: { + viewMode: 0.5 + } +}; diff --git a/src/components/Insights/common/InsightCard/PercentileViewModeToggle/index.tsx b/src/components/Insights/common/InsightCard/PercentileViewModeToggle/index.tsx new file mode 100644 index 000000000..20a22c17b --- /dev/null +++ b/src/components/Insights/common/InsightCard/PercentileViewModeToggle/index.tsx @@ -0,0 +1,17 @@ +import { PERCENTILES } from "../../../../../constants"; +import { Toggle } from "../../../../common/v3/Toggle"; +import { PercentileViewModeToggleProps } from "./types"; + +export const PercentileViewModeToggle = ({ + viewMode, + onChange +}: PercentileViewModeToggleProps) => ( + + options={PERCENTILES.map((percentile) => ({ + value: percentile.percentile, + label: percentile.label + }))} + value={viewMode} + onValueChange={onChange} + /> +); diff --git a/src/components/Insights/common/InsightCard/PercentileViewModeToggle/types.ts b/src/components/Insights/common/InsightCard/PercentileViewModeToggle/types.ts new file mode 100644 index 000000000..5ae5f64d3 --- /dev/null +++ b/src/components/Insights/common/InsightCard/PercentileViewModeToggle/types.ts @@ -0,0 +1,4 @@ +export interface PercentileViewModeToggleProps { + viewMode: number; + onChange: (viewMode: number) => void; +} diff --git a/src/components/Insights/common/InsightCard/Select/index.tsx b/src/components/Insights/common/InsightCard/Select/index.tsx index c7ff6160d..0346d7a5d 100644 --- a/src/components/Insights/common/InsightCard/Select/index.tsx +++ b/src/components/Insights/common/InsightCard/Select/index.tsx @@ -1,4 +1,4 @@ -import { useState } from "react"; +import { MouseEvent, useState } from "react"; import { MenuList } from "../../../../Navigation/common/MenuList"; import { Popup } from "../../../../Navigation/common/Popup"; import { NewPopover } from "../../../../common/NewPopover"; @@ -18,7 +18,12 @@ export const Select = (props: SelectProps) => { props.onChange(option.value); }; - const handleExpandButtonClick = () => { + const handleSelectBarClick = (e: MouseEvent) => { + // Prevent the dropdown from opening when clicking on a link inside the selected item + if ((e.target as HTMLElement).tagName === "A") { + return; + } + setIsOpen(!isOpen); }; @@ -36,11 +41,16 @@ export const Select = (props: SelectProps) => { /> } - onOpenChange={props.isDisabled || !isOpen ? undefined : setIsOpen} + onOpenChange={props.isDisabled ? undefined : setIsOpen} + useClickInteraction={false} isOpen={props.isDisabled ? false : isOpen} placement={"bottom-start"} > - + {selectedOption ? ( {selectedOption.customContent ? ( @@ -53,7 +63,7 @@ export const Select = (props: SelectProps) => { props.placeholder )} - + ` @@ -27,6 +23,7 @@ export const SelectBar = styled.div` display: flex; align-items: center; box-shadow: 1 1 4px 0 rgb(0 0 0 / 25%); + cursor: ${({ $isDisabled }) => ($isDisabled ? "initial" : "pointer")}; border: 1px solid ${({ theme, $isOpen }) => $isOpen diff --git a/src/components/Insights/common/InsightCard/index.tsx b/src/components/Insights/common/InsightCard/index.tsx index 060671e2f..bd8932fc8 100644 --- a/src/components/Insights/common/InsightCard/index.tsx +++ b/src/components/Insights/common/InsightCard/index.tsx @@ -1,4 +1,4 @@ -import { useState } from "react"; +import React, { useState } from "react"; import { isString } from "../../../../typeGuards/isString"; import { formatTimeDistance } from "../../../../utils/formatTimeDistance"; import { Link } from "../../../common/Link"; @@ -8,11 +8,11 @@ import { LiveIcon } from "../../../common/icons/16px/LiveIcon"; import { PinIcon } from "../../../common/icons/16px/PinIcon"; import { RecalculateIcon } from "../../../common/icons/16px/RecalculateIcon"; import { Button } from "../../../common/v3/Button"; +import { BaseButtonProps } from "../../../common/v3/Button/types"; import { Card } from "../../../common/v3/Card"; import { JiraButton } from "../../../common/v3/JiraButton"; import { Tooltip } from "../../../common/v3/Tooltip"; import { isSpanInsight } from "../../typeGuards"; -import { IconButton } from "./IconButton"; import { InsightHeader } from "./InsightHeader"; import * as s from "./styles"; import { InsightCardProps } from "./types"; @@ -22,16 +22,13 @@ const IS_NEW_TIME_LIMIT = 1000 * 60 * 10; // in milliseconds export const InsightCard = (props: InsightCardProps) => { const [isRecalculatingStarted, setIsRecalculatingStarted] = useState(false); const handleRefreshLinkClick = () => { - props.onRefresh && props.onRefresh(props.insight.type); + props.onRefresh(props.insight.type); }; const handleRecalculateClick = () => { props.insight.prefixedCodeObjectId && props.onRecalculate && - props.onRecalculate( - props.insight.prefixedCodeObjectId, - props.insight.type - ); + props.onRecalculate(props.insight.id); setIsRecalculatingStarted(true); }; @@ -47,6 +44,12 @@ export const InsightCard = (props: InsightCardProps) => { ); }; + const handleSpanLinkClick = () => { + if (isSpanInsight(props.insight) && props.insight.spanInfo) { + props.onGoToSpan(props.insight.spanInfo.spanCodeObjectId); + } + }; + const renderRecalculationBlock = ( actualStartTime: string, customStartTime: string | null, @@ -91,6 +94,110 @@ export const InsightCard = (props: InsightCardProps) => { } }; + const renderActions = () => { + const buttonsToRender: { + tooltip: string; + button: React.ComponentType; + }[] = []; + + props.onOpenHistogram && + buttonsToRender.push({ + tooltip: "Open Histogram", + button: (btnProps) => ( +