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/insights/EndpointBottleneckInsight/EndpointBottleneckInsight.stories.tsx b/src/components/Insights/common/insights/EndpointBottleneckInsight/EndpointBottleneckInsight.stories.tsx
new file mode 100644
index 000000000..3c48997e8
--- /dev/null
+++ b/src/components/Insights/common/insights/EndpointBottleneckInsight/EndpointBottleneckInsight.stories.tsx
@@ -0,0 +1,23 @@
+import { Meta, StoryObj } from "@storybook/react";
+import { EndpointBottleneckInsight } from ".";
+import { mockedSpanBottleneckInsight } from "./mockData";
+
+// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction
+const meta: Meta = {
+ title: "Insights/common/insights/EndpointBottleneckInsight",
+ component: EndpointBottleneckInsight,
+ 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: {
+ insight: mockedSpanBottleneckInsight
+ }
+};
diff --git a/src/components/Insights/common/insights/EndpointBottleneckInsight/index.tsx b/src/components/Insights/common/insights/EndpointBottleneckInsight/index.tsx
new file mode 100644
index 000000000..4077bdb3f
--- /dev/null
+++ b/src/components/Insights/common/insights/EndpointBottleneckInsight/index.tsx
@@ -0,0 +1,80 @@
+import { getDurationString } from "../../../../../utils/getDurationString";
+import { roundTo } from "../../../../../utils/roundTo";
+import { sendTrackingEvent } from "../../../../../utils/sendTrackingEvent";
+import { trackingEvents } from "../../../tracking";
+import { Info } from "../../Info";
+import { InsightCard } from "../../InsightCard";
+import { ColumnsContainer } from "../../InsightCard/ColumnsContainer";
+import { KeyValue } from "../../InsightCard/KeyValue";
+import { ContentContainer, Description, Details } from "../styles";
+import * as s from "./styles";
+import { EndpointBottleneckInsightProps } from "./types";
+
+export const EndpointBottleneckInsight = (
+ props: EndpointBottleneckInsightProps
+) => {
+ const { insight } = props;
+ const { span, ticketLink } = insight;
+
+ const handleSpanLinkClick = (spanCodeObjectId: string) => {
+ props.onAssetLinkClick(spanCodeObjectId, insight.type);
+ };
+
+ const handleTicketInfoButtonClick = (
+ spanCodeObjectId: string,
+ event: string
+ ) => {
+ sendTrackingEvent(trackingEvents.JIRA_TICKET_INFO_BUTTON_CLICKED, {
+ insightType: insight.type
+ });
+ props.onJiraTicketCreate &&
+ props.onJiraTicketCreate(insight, spanCodeObjectId, event);
+ };
+
+ const spanName = span.spanInfo.displayName;
+ const spanCodeObjectId = span.spanInfo.spanCodeObjectId;
+
+ return (
+
+
+ Asset
+ handleSpanLinkClick(spanCodeObjectId)}
+ />
+
+
+
+ {roundTo(span.probabilityOfBeingBottleneck * 100, 2)}%
+
+
+ }
+ >
+ {span.requestPercentage}%
+
+
+ {getDurationString(span.avgDurationWhenBeingBottleneck)}
+
+
+
+ }
+ onRecalculate={props.onRecalculate}
+ onRefresh={props.onRefresh}
+ onJiraButtonClick={handleTicketInfoButtonClick}
+ />
+ );
+};
diff --git a/src/components/Insights/common/insights/EndpointBottleneckInsight/mockData.ts b/src/components/Insights/common/insights/EndpointBottleneckInsight/mockData.ts
new file mode 100644
index 000000000..d13ee250a
--- /dev/null
+++ b/src/components/Insights/common/insights/EndpointBottleneckInsight/mockData.ts
@@ -0,0 +1,103 @@
+import {
+ EndpointBottleneckInsight,
+ InsightCategory,
+ InsightScope,
+ InsightType
+} from "../../../types";
+
+export const mockedSpanBottleneckInsight: EndpointBottleneckInsight = {
+ sourceSpanCodeObjectInsight: "sourceSpanCodeObjectInsightId",
+ id: "60b55792-8262-4c5d-9628-7dce7979ad6d",
+ firstDetected: "2023-12-05T17:25:47.010Z",
+ lastDetected: "2024-01-05T13:14:47.010Z",
+ criticality: 0.7,
+ firstCommitId: "b3f7b3f",
+ lastCommitId: "a1b2c3d",
+ deactivatedCommitId: null,
+ reopenCount: 0,
+ ticketLink: null,
+ impact: 0,
+ name: "Bottleneck Detected",
+ type: InsightType.EndpointBottleneck,
+ category: InsightCategory.Performance,
+ specifity: 3,
+ importance: 2,
+ span: {
+ spanInfo: {
+ name: "DelayAsync",
+ displayName: "DelayAsync",
+ instrumentationLibrary: "SampleInsightsController",
+ spanCodeObjectId: "span:SampleInsightsController$_$DelayAsync",
+ methodCodeObjectId: null,
+ kind: "Internal",
+ codeObjectId: null
+ },
+ probabilityOfBeingBottleneck: 0.6923076923076923,
+ avgDurationWhenBeingBottleneck: {
+ value: 2,
+ unit: "sec",
+ raw: 2002883447.4474475
+ },
+ criticality: 0,
+ ticketLink: "https://digma.ai/1",
+ requestPercentage: 90,
+ p50: {
+ fraction: 0,
+ maxDuration: {
+ value: 0,
+ unit: "ns",
+ raw: 0
+ }
+ },
+ p95: {
+ fraction: 0,
+ maxDuration: {
+ value: 0,
+ unit: "ns",
+ raw: 0
+ }
+ },
+ p99: {
+ fraction: 0,
+ maxDuration: {
+ value: 0,
+ unit: "ns",
+ raw: 0
+ }
+ }
+ },
+ scope: InsightScope.EntrySpan,
+ endpointSpan: "HTTP GET SampleInsights/lock/{milisec}",
+ spanCodeObjectId:
+ "span:OpenTelemetry.Instrumentation.AspNetCore$_$HTTP GET SampleInsights/lock/{milisec}",
+ route: "epHTTP:HTTP GET SampleInsights/lock/{milisec}",
+ serviceName: "Sample.MoneyTransfer.API",
+ spanInfo: {
+ name: "HTTP GET SampleInsights/lock/{milisec}",
+ displayName: "HTTP GET SampleInsights/lock/{milisec}",
+ instrumentationLibrary: "OpenTelemetry.Instrumentation.AspNetCore",
+ spanCodeObjectId:
+ "span:OpenTelemetry.Instrumentation.AspNetCore$_$HTTP GET SampleInsights/lock/{milisec}",
+ methodCodeObjectId:
+ "method:Sample.MoneyTransfer.API.Controllers.SampleInsightsController$_$Lock(Double)",
+ kind: "Server",
+ codeObjectId:
+ "Sample.MoneyTransfer.API.Controllers.SampleInsightsController$_$Lock(Double)"
+ },
+ shortDisplayInfo: {
+ title: "",
+ targetDisplayName: "HTTP GET SampleInsights/lock/{milisec}",
+ subtitle: "2 spans",
+ description: "WaitForLock 4.58 sec"
+ },
+ codeObjectId:
+ "Sample.MoneyTransfer.API.Controllers.SampleInsightsController$_$Lock(Double)",
+ decorators: null,
+ environment: "BOB-LAPTOP[LOCAL]",
+ severity: 0,
+ isRecalculateEnabled: true,
+ prefixedCodeObjectId:
+ "method:Sample.MoneyTransfer.API.Controllers.SampleInsightsController$_$Lock(Double)",
+ customStartTime: null,
+ actualStartTime: "2023-06-16T11:10:30.349Z"
+};
diff --git a/src/components/Insights/common/insights/EndpointBottleneckInsight/styles.ts b/src/components/Insights/common/insights/EndpointBottleneckInsight/styles.ts
new file mode 100644
index 000000000..39395a748
--- /dev/null
+++ b/src/components/Insights/common/insights/EndpointBottleneckInsight/styles.ts
@@ -0,0 +1,6 @@
+import styled from "styled-components";
+import { ListItem } from "../../InsightCard/ListItem";
+
+export const SpanListItem = styled(ListItem)`
+ padding: 4px;
+`;
diff --git a/src/components/Insights/common/insights/EndpointBottleneckInsight/types.ts b/src/components/Insights/common/insights/EndpointBottleneckInsight/types.ts
new file mode 100644
index 000000000..ae9b728ed
--- /dev/null
+++ b/src/components/Insights/common/insights/EndpointBottleneckInsight/types.ts
@@ -0,0 +1,13 @@
+import {
+ EndpointBottleneckInsight,
+ InsightProps,
+ InsightType
+} from "../../../types";
+
+export interface EndpointBottleneckInsightProps extends InsightProps {
+ insight: EndpointBottleneckInsight;
+ onAssetLinkClick: (
+ spanCodeObjectId: string,
+ insightType: InsightType
+ ) => void;
+}
diff --git a/src/components/Insights/common/insights/EndpointNPlusOneInsight/index.tsx b/src/components/Insights/common/insights/EndpointNPlusOneInsight/index.tsx
index 1c2a605b3..15682a308 100644
--- a/src/components/Insights/common/insights/EndpointNPlusOneInsight/index.tsx
+++ b/src/components/Insights/common/insights/EndpointNPlusOneInsight/index.tsx
@@ -2,10 +2,9 @@ import { useContext } from "react";
import { getDurationString } from "../../../../../utils/getDurationString";
import { sendTrackingEvent } from "../../../../../utils/sendTrackingEvent";
import { ConfigContext } from "../../../../common/App/ConfigContext";
-import { InfoCircleIcon } from "../../../../common/icons/InfoCircleIcon";
-import { Tooltip } from "../../../../common/v3/Tooltip";
import { trackingEvents } from "../../../tracking";
import { InsightType, Trace } from "../../../types";
+import { Info } from "../../Info";
import { InsightCard } from "../../InsightCard";
import { ColumnsContainer } from "../../InsightCard/ColumnsContainer";
import { KeyValue } from "../../InsightCard/KeyValue";
@@ -81,14 +80,10 @@ export const EndpointNPlusOneInsight = (
{span.occurrences}
-
- Requests
-
-
-
+
}
>
{span.requestPercentage}%
diff --git a/src/components/Insights/common/insights/SpanEndpointBottleneckInsight/SpanEndpointBottleneckInsight.stories.tsx b/src/components/Insights/common/insights/SpanEndpointBottleneckInsight/SpanEndpointBottleneckInsight.stories.tsx
new file mode 100644
index 000000000..33f8aab16
--- /dev/null
+++ b/src/components/Insights/common/insights/SpanEndpointBottleneckInsight/SpanEndpointBottleneckInsight.stories.tsx
@@ -0,0 +1,48 @@
+import { Meta, StoryObj } from "@storybook/react";
+import { SpanBottleneckEndpoints } from ".";
+import { mockedBottleneckInsight } from "../../../BottleneckInsight/mockData";
+
+// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction
+const meta: Meta = {
+ title: "Insights/common/insights/SpanBottleneckEndpoints",
+ component: SpanBottleneckEndpoints,
+ 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: {
+ insight: {
+ ...mockedBottleneckInsight,
+ slowEndpoints: [
+ ...mockedBottleneckInsight.slowEndpoints,
+ {
+ ...mockedBottleneckInsight.slowEndpoints[0],
+ requestPercentage: 100,
+ endpointInfo: {
+ ...mockedBottleneckInsight.slowEndpoints[0].endpointInfo,
+ route: `${mockedBottleneckInsight.slowEndpoints[0].endpointInfo.route}1`,
+ spanCodeObjectId: `${mockedBottleneckInsight.slowEndpoints[0].endpointInfo.spanCodeObjectId}1`
+ }
+ }
+ ]
+ }
+ }
+};
+
+export const WithoutInsight: Story = {};
+
+export const WithNoSlowEndpoints: Story = {
+ args: {
+ insight: {
+ ...mockedBottleneckInsight,
+ slowEndpoints: []
+ }
+ }
+};
diff --git a/src/components/Insights/common/insights/SpanEndpointBottleneckInsight/index.tsx b/src/components/Insights/common/insights/SpanEndpointBottleneckInsight/index.tsx
new file mode 100644
index 000000000..bdf1bff0b
--- /dev/null
+++ b/src/components/Insights/common/insights/SpanEndpointBottleneckInsight/index.tsx
@@ -0,0 +1,126 @@
+import { ReactNode, useState } from "react";
+import { getDurationString } from "../../../../../utils/getDurationString";
+import { roundTo } from "../../../../../utils/roundTo";
+import { sendTrackingEvent } from "../../../../../utils/sendTrackingEvent";
+import { trimEndpointScheme } from "../../../../../utils/trimEndpointScheme";
+import { trackingEvents } from "../../../tracking";
+import { BottleneckEndpointInfo } from "../../../types";
+import { Info } from "../../Info";
+import { InsightCard } from "../../InsightCard";
+import { ColumnsContainer } from "../../InsightCard/ColumnsContainer";
+import { KeyValue } from "../../InsightCard/KeyValue";
+import { ListItem } from "../../InsightCard/ListItem";
+import { Select } from "../../InsightCard/Select";
+import { ContentContainer, Description, Details } from "../styles";
+import * as s from "./styles";
+import { SpanEndpointBottleneckEndpointsProps } from "./types";
+
+const renderOptions = (
+ endpoints: BottleneckEndpointInfo[],
+ handleLinkClick: (spanCodeObjectId?: string) => void
+): { label: string; customContent: ReactNode; value: string }[] => {
+ return endpoints.map((x) => {
+ const spanCodeObjectId = x.endpointInfo.spanCodeObjectId;
+ const route = trimEndpointScheme(x.endpointInfo.route);
+ return {
+ label: route,
+ customContent: (
+
+ {x.endpointInfo.serviceName}
+ handleLinkClick(spanCodeObjectId)}
+ />
+
+ ),
+ value: spanCodeObjectId
+ };
+ });
+};
+
+export const SpanBottleneckEndpoints = (
+ props: SpanEndpointBottleneckEndpointsProps
+) => {
+ const [selectedEndpoint, setSelectedEndpoint] = useState(
+ props.insight?.slowEndpoints.length ? props.insight.slowEndpoints[0] : null
+ );
+
+ const handleSpanLinkClick = (spanCodeObjectId?: string) => {
+ spanCodeObjectId &&
+ props.onAssetLinkClick(spanCodeObjectId, props?.insight.type);
+ };
+
+ const handleCreateJiraTicketButtonClick = (event: string) => {
+ sendTrackingEvent(trackingEvents.JIRA_TICKET_INFO_BUTTON_CLICKED, {
+ insightType: props.insight.type
+ });
+ props.onJiraTicketCreate &&
+ props.onJiraTicketCreate(props.insight, undefined, event);
+ };
+
+ const endpoints = props.insight.slowEndpoints;
+
+ if (endpoints.length === 0) {
+ return null;
+ }
+
+ return (
+
+
+ Affected Endpoints ({endpoints.length})
+
+ {selectedEndpoint && (
+
+
+ {roundTo(
+ selectedEndpoint.probabilityOfBeingBottleneck * 100,
+ 2
+ )}
+ %
+
+
+ }
+ >
+ {selectedEndpoint.requestPercentage}%
+
+
+ {getDurationString(
+ selectedEndpoint.avgDurationWhenBeingBottleneck
+ )}
+
+
+ )}
+
+ }
+ />
+ );
+};
diff --git a/src/components/Insights/common/insights/SpanEndpointBottleneckInsight/styles.ts b/src/components/Insights/common/insights/SpanEndpointBottleneckInsight/styles.ts
new file mode 100644
index 000000000..861aa220a
--- /dev/null
+++ b/src/components/Insights/common/insights/SpanEndpointBottleneckInsight/styles.ts
@@ -0,0 +1,8 @@
+import styled from "styled-components";
+import { footnoteRegularTypography } from "../../../../common/App/typographies";
+
+export const SelectedItem = styled.div`
+ display: flex;
+ align-items: center;
+ ${footnoteRegularTypography}
+`;
diff --git a/src/components/Insights/common/insights/SpanEndpointBottleneckInsight/types.ts b/src/components/Insights/common/insights/SpanEndpointBottleneckInsight/types.ts
new file mode 100644
index 000000000..c227559b4
--- /dev/null
+++ b/src/components/Insights/common/insights/SpanEndpointBottleneckInsight/types.ts
@@ -0,0 +1,14 @@
+import {
+ InsightProps,
+ InsightType,
+ SpanEndpointBottleneckInsight
+} from "../../../types";
+
+export interface SpanEndpointBottleneckEndpointsProps extends InsightProps {
+ insight: SpanEndpointBottleneckInsight;
+
+ onAssetLinkClick: (
+ spanCodeObjectId: string,
+ insightType: InsightType
+ ) => void;
+}
diff --git a/src/components/Insights/common/insights/SpanNPlusOneInsight/index.tsx b/src/components/Insights/common/insights/SpanNPlusOneInsight/index.tsx
index 62d989b72..d4831692c 100644
--- a/src/components/Insights/common/insights/SpanNPlusOneInsight/index.tsx
+++ b/src/components/Insights/common/insights/SpanNPlusOneInsight/index.tsx
@@ -1,12 +1,11 @@
-import { useContext, useState } from "react";
+import { ReactNode, useContext, useState } from "react";
import { getDurationString } from "../../../../../utils/getDurationString";
import { sendTrackingEvent } from "../../../../../utils/sendTrackingEvent";
import { trimEndpointScheme } from "../../../../../utils/trimEndpointScheme";
import { ConfigContext } from "../../../../common/App/ConfigContext";
-import { InfoCircleIcon } from "../../../../common/icons/InfoCircleIcon";
-import { Tooltip } from "../../../../common/v3/Tooltip";
import { trackingEvents } from "../../../tracking";
-import { InsightType, Trace } from "../../../types";
+import { InsightType, NPlusOneEndpointInfo, Trace } from "../../../types";
+import { Info } from "../../Info";
import { InsightCard } from "../../InsightCard";
import { ColumnsContainer } from "../../InsightCard/ColumnsContainer";
import { KeyValue } from "../../InsightCard/KeyValue";
@@ -16,6 +15,29 @@ import { ContentContainer, Description, Details } from "../styles";
import * as s from "./styles";
import { SpanNPlusOneInsightProps } from "./types";
+const renderOptions = (
+ endpoints: NPlusOneEndpointInfo[],
+ handleLinkClick: (spanCodeObjectId?: string) => void
+): { label: string; customContent: ReactNode; value: string }[] => {
+ return endpoints.map((x) => {
+ const spanCodeObjectId = x.endpointInfo.entrySpanCodeObjectId;
+ const route = trimEndpointScheme(x.endpointInfo.route);
+ return {
+ label: route,
+ customContent: (
+
+ {x.endpointInfo.serviceName}
+ handleLinkClick(spanCodeObjectId)}
+ />
+
+ ),
+ value: spanCodeObjectId
+ };
+ });
+};
+
export const SpanNPlusOneInsight = (props: SpanNPlusOneInsightProps) => {
const {
insight: { type, endpoints, ticketLink }
@@ -81,23 +103,10 @@ export const SpanNPlusOneInsight = (props: SpanNPlusOneInsightProps) => {
setSelectedEndpoint(selected);
}}
- options={props.insight.endpoints.map((x) => {
- const spanCodeObjectId = x.endpointInfo.entrySpanCodeObjectId;
- const route = trimEndpointScheme(x.endpointInfo.route);
- return {
- label: route,
- customContent: (
-
- {x.endpointInfo.serviceName}
- handleSpanLinkClick(spanCodeObjectId)}
- />
-
- ),
- value: spanCodeObjectId
- };
- })}
+ options={renderOptions(
+ props.insight.endpoints,
+ handleSpanLinkClick
+ )}
/>
@@ -108,14 +117,10 @@ export const SpanNPlusOneInsight = (props: SpanNPlusOneInsightProps) => {
-
- Requests
-
-
-
+
}
>
{selectedEndpoint.requestPercentage}%
diff --git a/src/components/Insights/types.ts b/src/components/Insights/types.ts
index 247f9e21c..7abbee0fb 100644
--- a/src/components/Insights/types.ts
+++ b/src/components/Insights/types.ts
@@ -268,41 +268,43 @@ interface Percentile {
maxDuration: Duration;
}
+export interface BottleneckEndpointInfo {
+ endpointInfo: {
+ route: string;
+ instrumentationLibrary: string;
+ serviceName: string;
+ codeObjectId: string;
+ spanCodeObjectId: string;
+ spanName: string;
+ };
+ probabilityOfBeingBottleneck: number;
+ avgDurationWhenBeingBottleneck: Duration;
+ impact: number;
+ severity: number;
+ criticality: number;
+ requestPercentage: number;
+
+ /**
+ * @deprecated
+ */
+ p50: Percentile;
+ /**
+ * @deprecated
+ */
+ p95: Percentile;
+ /**
+ * @deprecated
+ */
+ p99: Percentile;
+}
+
export interface SpanEndpointBottleneckInsight extends SpanInsight {
name: "Bottleneck";
type: InsightType.SpanEndpointBottleneck;
category: InsightCategory.Performance;
specifity: InsightSpecificity.TargetFound;
importance: InsightImportance.Critical;
- slowEndpoints: {
- endpointInfo: {
- route: string;
- instrumentationLibrary: string;
- serviceName: string;
- codeObjectId: string;
- spanCodeObjectId: string;
- spanName: string;
- };
- probabilityOfBeingBottleneck: number;
- avgDurationWhenBeingBottleneck: Duration;
- impact: number;
- severity: number;
- criticality: number;
- requestPercentage: number;
-
- /**
- * @deprecated
- */
- p50: Percentile;
- /**
- * @deprecated
- */
- p95: Percentile;
- /**
- * @deprecated
- */
- p99: Percentile;
- }[];
+ slowEndpoints: BottleneckEndpointInfo[];
/**
* @deprecated
@@ -444,6 +446,7 @@ export interface EndpointBottleneckInsight extends EndpointInsight {
avgDurationWhenBeingBottleneck: Duration;
criticality: number;
ticketLink: string | null;
+ requestPercentage: number;
/**
* @deprecated
@@ -538,6 +541,24 @@ export interface SpanScalingBadlyInsight extends SpanInsight {
spanInstrumentationLibrary: string;
}
+export interface NPlusOneEndpointInfo {
+ endpointInfo: {
+ route: string;
+ instrumentationLibrary: string;
+ spanCodeObjectId: string;
+ entrySpanCodeObjectId: string;
+ serviceName: string;
+ };
+ occurrences: number;
+ criticality: number;
+ impact: number;
+ severity: number;
+ traceId: string;
+ duration: Duration;
+ commitId: string;
+ requestPercentage: number;
+}
+
export interface SpanNPlusOneInsight extends SpanInsight {
name: "N+1";
type: InsightType.SpanNPlusOne;
@@ -549,23 +570,7 @@ export interface SpanNPlusOneInsight extends SpanInsight {
clientSpanName: string | null;
clientSpanCodeObjectId: string | null;
duration: Duration;
- endpoints: {
- endpointInfo: {
- route: string;
- instrumentationLibrary: string;
- spanCodeObjectId: string;
- entrySpanCodeObjectId: string;
- serviceName: string;
- };
- occurrences: number;
- criticality: number;
- impact: number;
- severity: number;
- traceId: string;
- duration: Duration;
- commitId: string;
- requestPercentage: number;
- }[];
+ endpoints: NPlusOneEndpointInfo[];
/**
* @deprecated
diff --git a/src/components/common/v3/JiraButton/index.tsx b/src/components/common/v3/JiraButton/index.tsx
index 9a1f1e454..cd3dcbbe5 100644
--- a/src/components/common/v3/JiraButton/index.tsx
+++ b/src/components/common/v3/JiraButton/index.tsx
@@ -42,13 +42,13 @@ export const JiraButton = (props: JiraButtonProps) => {
{
icon: ,
label: "View",
- id: ticketLink,
+ id: "view",
onClick: handleViewButtonClick
},
{
icon: ,
label: "Edit",
- id: props.spanCodeObjectId ?? "",
+ id: "edit",
onClick: () => openTicketInfo("edit menu item click")
}
]}