From c41e0f566232875776d6b48bc9dd0f2708f5cb3c Mon Sep 17 00:00:00 2001 From: Kyrylo Shmidt Date: Tue, 8 Jul 2025 08:55:56 +0200 Subject: [PATCH 1/9] Support links to Span/Error in Web Admin --- .../Admin/ErrorLinkResolver/index.tsx | 52 ++++++++++++ .../Admin/ErrorLinkResolver/styles.ts | 13 +++ src/components/Admin/Header/index.tsx | 10 +++ src/components/Admin/Home/index.tsx | 84 ++++++++++++++++++- .../Admin/SpanLinkResolver/index.tsx | 52 ++++++++++++ .../Admin/SpanLinkResolver/styles.ts | 13 +++ .../RepositorySidebar/Issues/index.tsx | 11 ++- .../common/RepositorySidebarOverlay/index.tsx | 8 +- .../common/RepositorySidebarOverlay/types.ts | 1 + .../AdditionalInfo/RelatedIssues/index.tsx | 8 +- src/components/Agentic/Incidents/styles.ts | 8 -- .../insightCards/common/InsightCard/index.tsx | 2 +- .../Insights/InsightsCatalog/index.tsx | 5 ++ src/containers/Admin/router.tsx | 4 + src/redux/services/digma.ts | 12 +++ src/redux/services/types.ts | 8 ++ src/utils/getWebAdminLinkForSpan.ts | 12 +++ src/utils/getWebAdminLinkToError.ts | 12 +++ 18 files changed, 295 insertions(+), 20 deletions(-) create mode 100644 src/components/Admin/ErrorLinkResolver/index.tsx create mode 100644 src/components/Admin/ErrorLinkResolver/styles.ts create mode 100644 src/components/Admin/SpanLinkResolver/index.tsx create mode 100644 src/components/Admin/SpanLinkResolver/styles.ts create mode 100644 src/utils/getWebAdminLinkForSpan.ts create mode 100644 src/utils/getWebAdminLinkToError.ts diff --git a/src/components/Admin/ErrorLinkResolver/index.tsx b/src/components/Admin/ErrorLinkResolver/index.tsx new file mode 100644 index 000000000..cf53757f6 --- /dev/null +++ b/src/components/Admin/ErrorLinkResolver/index.tsx @@ -0,0 +1,52 @@ +import { Navigate, useParams } from "react-router"; +import { useGetErrorEnvironmentQuery } from "../../../redux/services/digma"; +import { Spinner } from "../../common/v3/Spinner"; +import { TAB_IDS } from "../../Navigation/Tabs/types"; +import type { TabLocation } from "../common/RepositorySidebarOverlay/types"; +import * as s from "./styles"; + +const REFRESH_INTERVAL = 10 * 1000; // in milliseconds + +export const ErrorLinkResolver = () => { + const params = useParams(); + + const { data } = useGetErrorEnvironmentQuery( + { + id: params.id ?? "" + }, + { + skip: !params.id, + pollingInterval: REFRESH_INTERVAL + } + ); + + if (!data) { + return ( + + + + + + ); + } + + const sidebarLocation: TabLocation = { + id: TAB_IDS.ERRORS, + path: params.id + }; + + if (data) { + return ( + + ); + } +}; diff --git a/src/components/Admin/ErrorLinkResolver/styles.ts b/src/components/Admin/ErrorLinkResolver/styles.ts new file mode 100644 index 000000000..cda60c3ed --- /dev/null +++ b/src/components/Admin/ErrorLinkResolver/styles.ts @@ -0,0 +1,13 @@ +import styled from "styled-components"; + +export const Container = styled.div` + display: flex; + height: 100%; +`; + +export const LoadingContainer = styled.div` + display: flex; + align-items: center; + justify-content: center; + flex-grow: 1; +`; diff --git a/src/components/Admin/Header/index.tsx b/src/components/Admin/Header/index.tsx index 2f7aa971b..d93104f3e 100644 --- a/src/components/Admin/Header/index.tsx +++ b/src/components/Admin/Header/index.tsx @@ -16,6 +16,16 @@ export const Header = () => ( } /> + + + + + } + /> + { + if (!locationParam) { + return undefined; + } + + try { + const parsedLocation = JSON.parse(locationParam) as TabLocation; + return parsedLocation; + } catch { + return undefined; + } +}; + export const Home = () => { const environmentId = useAdminSelector( (state) => state.codeIssuesReport.selectedEnvironmentId @@ -29,11 +48,26 @@ export const Home = () => { const [searchParams, setSearchParams] = useStableSearchParams(); const environmentParam = searchParams.get("environment"); const issuesParam = searchParams.get("issues"); + const [sidebarScope, setSidebarScope] = useState( + searchParams.get("sidebar-scope") ?? undefined + ); + const [sidebarLocation, setSidebarLocation] = useState( + getRepositorySidebarLocation(searchParams.get("sidebar-view")) + ); const servicesParam = useMemo( () => searchParams.getAll("services"), [searchParams] ); + const { data: spanInfo } = useGetSpanByIdQuery( + { + id: sidebarScope ?? "" + }, + { + skip: !sidebarScope + } + ); + const queries: Record = useMemo( () => ({ "top-criticality": { @@ -64,6 +98,8 @@ export const Home = () => { const handleRepositorySidebarClose = () => { setIssuesQuery(undefined); + setSidebarScope(undefined); + setSidebarLocation(undefined); }; // TODO: replace with useEffect @@ -112,7 +148,48 @@ export const Home = () => { }); }, [setSearchParams, issuesQuery]); - const repositorySidebarQuery = issuesQuery ? queries[issuesQuery] : undefined; + useEffect(() => { + setSearchParams((params) => { + if (sidebarScope) { + params.set("sidebar-scope", sidebarScope); + } else { + params.delete("sidebar-scope"); + } + return params; + }); + }, [setSearchParams, sidebarScope]); + + useEffect(() => { + setSearchParams((params) => { + if (sidebarLocation) { + params.set("sidebar-view", JSON.stringify(sidebarLocation)); + } else { + params.delete("sidebar-view"); + } + return params; + }); + }, [setSearchParams, sidebarLocation]); + + const repositorySidebarQuery: RepositorySidebarQuery | undefined = + sidebarScope && spanInfo + ? { + query: { + environment: environmentId ?? undefined, + scopedSpanCodeObjectId: spanInfo?.spanCodeObjectId + } + } + : sidebarLocation + ? { + query: { + environment: environmentId ?? undefined + } + } + : issuesQuery + ? queries[issuesQuery] + : undefined; + const isRepositorySidebarOpen = Boolean( + repositorySidebarQuery ?? sidebarLocation + ); return ( @@ -124,7 +201,8 @@ export const Home = () => { diff --git a/src/components/Admin/SpanLinkResolver/index.tsx b/src/components/Admin/SpanLinkResolver/index.tsx new file mode 100644 index 000000000..6570f8dd4 --- /dev/null +++ b/src/components/Admin/SpanLinkResolver/index.tsx @@ -0,0 +1,52 @@ +import { Navigate, useParams } from "react-router"; +import { useGetSpanByIdQuery } from "../../../redux/services/digma"; +import { Spinner } from "../../common/v3/Spinner"; +import { TAB_IDS } from "../../Navigation/Tabs/types"; +import type { TabLocation } from "../common/RepositorySidebarOverlay/types"; +import * as s from "./styles"; + +const REFRESH_INTERVAL = 10 * 1000; // in milliseconds + +export const SpanLinkResolver = () => { + const params = useParams(); + + const { data } = useGetSpanByIdQuery( + { + id: params.id ?? "" + }, + { + skip: !params.id, + pollingInterval: REFRESH_INTERVAL + } + ); + + if (!data) { + return ( + + + + + + ); + } + + const sidebarLocation: TabLocation = { + id: TAB_IDS.ISSUES + }; + + if (data) { + return ( + + ); + } +}; diff --git a/src/components/Admin/SpanLinkResolver/styles.ts b/src/components/Admin/SpanLinkResolver/styles.ts new file mode 100644 index 000000000..cda60c3ed --- /dev/null +++ b/src/components/Admin/SpanLinkResolver/styles.ts @@ -0,0 +1,13 @@ +import styled from "styled-components"; + +export const Container = styled.div` + display: flex; + height: 100%; +`; + +export const LoadingContainer = styled.div` + display: flex; + align-items: center; + justify-content: center; + flex-grow: 1; +`; diff --git a/src/components/Admin/common/RepositorySidebarOverlay/RepositorySidebar/Issues/index.tsx b/src/components/Admin/common/RepositorySidebarOverlay/RepositorySidebar/Issues/index.tsx index 30b1e36fa..bb0cc7e02 100644 --- a/src/components/Admin/common/RepositorySidebarOverlay/RepositorySidebar/Issues/index.tsx +++ b/src/components/Admin/common/RepositorySidebarOverlay/RepositorySidebar/Issues/index.tsx @@ -10,6 +10,7 @@ import { setIssuesInsightInfoToOpenTicket, setScope } from "../../../../../../redux/slices/repositorySlice"; +import { initialState } from "../../../../../../store/insights/insightsSlice"; import { useStore } from "../../../../../../store/useStore"; import { useInsightsData } from "../../../../../Insights/hooks/useInsightsData"; import { InsightsContent } from "../../../../../Insights/InsightsContent"; @@ -24,7 +25,7 @@ export const Issues = ({ onScopeChange, onGoToTab }: IssuesProps) => { - const { setInsightViewType } = useStore.getState(); + const { setInsightViewType, setInsightsSorting } = useStore.getState(); const [isDrawerTransitioning, setIsDrawerTransitioning] = useState(false); const drawerRef = useRef(null); const dispatch = useAdminDispatch(); @@ -110,6 +111,14 @@ export const Issues = ({ ); }, [query, dispatch]); + // Set sorting on query change + useEffect(() => { + setInsightsSorting({ + criterion: query?.sortBy ?? initialState.sorting.criterion, + order: query?.sortOrder ?? initialState.sorting.order + }); + }, [query?.sortBy, query?.sortOrder, setInsightsSorting]); + return ( { const [isSidebarTransitioning, setIsSidebarTransitioning] = useState(false); - const [currentTabLocation, setCurrentTabLocation] = - useState(initialTabLocation); + const [currentTabLocation, setCurrentTabLocation] = useState( + sidebarLocation ?? initialTabLocation + ); const [currentSpanCodeObjectId, setCurrentSpanCodeObjectId] = useState( sidebarQuery?.query?.scopedSpanCodeObjectId ); diff --git a/src/components/Admin/common/RepositorySidebarOverlay/types.ts b/src/components/Admin/common/RepositorySidebarOverlay/types.ts index 2c5124088..4dd81405c 100644 --- a/src/components/Admin/common/RepositorySidebarOverlay/types.ts +++ b/src/components/Admin/common/RepositorySidebarOverlay/types.ts @@ -8,6 +8,7 @@ export interface RepositorySidebarOverlayProps { isSidebarOpen: boolean; onSidebarClose: () => void; sidebarQuery?: RepositorySidebarQuery; + sidebarLocation?: TabLocation; scopeDisplayName?: string; } diff --git a/src/components/Agentic/IncidentDetails/AdditionalInfo/RelatedIssues/index.tsx b/src/components/Agentic/IncidentDetails/AdditionalInfo/RelatedIssues/index.tsx index 449427078..df879cdc5 100644 --- a/src/components/Agentic/IncidentDetails/AdditionalInfo/RelatedIssues/index.tsx +++ b/src/components/Agentic/IncidentDetails/AdditionalInfo/RelatedIssues/index.tsx @@ -5,9 +5,9 @@ import { useStableSearchParams } from "../../../../../hooks/useStableSearchParam import { useGetIncidentQuery } from "../../../../../redux/services/digma"; import type { GenericIncidentIssue } from "../../../../../redux/services/types"; import { sendUserActionTrackingEvent } from "../../../../../utils/actions/sendUserActionTrackingEvent"; -import { getIdeLauncherLinkForError } from "../../../../../utils/getIdeLauncherLinkForError"; -import { getIdeLauncherLinkForSpan } from "../../../../../utils/getIdeLauncherLinkForSpan"; import { getInsightTypeInfo } from "../../../../../utils/getInsightTypeInfo"; +import { getWebAdminLinkForSpan } from "../../../../../utils/getWebAdminLinkForSpan"; +import { getWebAdminLinkForError } from "../../../../../utils/getWebAdminLinkToError"; import { roundTo } from "../../../../../utils/roundTo"; import type { TagType } from "../../../../common/v3/Tag/types"; import { Tooltip } from "../../../../common/v3/Tooltip"; @@ -97,7 +97,7 @@ export const RelatedIssues = () => { {issue.span_uid ? ( @@ -118,7 +118,7 @@ export const RelatedIssues = () => { diff --git a/src/components/Agentic/Incidents/styles.ts b/src/components/Agentic/Incidents/styles.ts index afa36d770..06b8583d3 100644 --- a/src/components/Agentic/Incidents/styles.ts +++ b/src/components/Agentic/Incidents/styles.ts @@ -1,13 +1,5 @@ import styled from "styled-components"; -export const Container = styled.div` - display: flex; - flex-direction: column; - flex-grow: 1; - height: 100%; - overflow: hidden; -`; - export const LoadingContainer = styled.div` display: flex; align-items: center; diff --git a/src/components/Insights/InsightsCatalog/InsightsPage/InsightCardRenderer/insightCards/common/InsightCard/index.tsx b/src/components/Insights/InsightsCatalog/InsightsPage/InsightCardRenderer/insightCards/common/InsightCard/index.tsx index b2e0d5854..585f741a1 100644 --- a/src/components/Insights/InsightsCatalog/InsightsPage/InsightCardRenderer/insightCards/common/InsightCard/index.tsx +++ b/src/components/Insights/InsightsCatalog/InsightsPage/InsightCardRenderer/insightCards/common/InsightCard/index.tsx @@ -534,7 +534,7 @@ export const InsightCard = ({ actions.push("pin"); } - if (areInsightSuggestionsEnabled && onOpenSuggestion) { + if (areInsightSuggestionsEnabled && !isAgenticEnabled && onOpenSuggestion) { actions.push("openSuggestion"); } diff --git a/src/components/Insights/InsightsCatalog/index.tsx b/src/components/Insights/InsightsCatalog/index.tsx index 4e9d2fe4a..aede21d3c 100644 --- a/src/components/Insights/InsightsCatalog/index.tsx +++ b/src/components/Insights/InsightsCatalog/index.tsx @@ -62,6 +62,11 @@ const getSortingOptions = ( label: "Criticality", defaultOrder: SortingOrder.Desc }, + { + value: InsightsSortingCriterion.Severity, + label: "Severity", + defaultOrder: SortingOrder.Desc + }, { value: InsightsSortingCriterion.Latest, label: "Latest", diff --git a/src/containers/Admin/router.tsx b/src/containers/Admin/router.tsx index 7bc4773e5..fe13c0377 100644 --- a/src/containers/Admin/router.tsx +++ b/src/containers/Admin/router.tsx @@ -2,8 +2,10 @@ import type { RouteObject } from "react-router"; import { Navigate, createBrowserRouter, useRouteError } from "react-router"; import { Admin } from "../../components/Admin"; import { Environments } from "../../components/Admin/Environments"; +import { ErrorLinkResolver } from "../../components/Admin/ErrorLinkResolver"; import { Home } from "../../components/Admin/Home"; import { CodeIssues } from "../../components/Admin/Reports/CodeIssues"; +import { SpanLinkResolver } from "../../components/Admin/SpanLinkResolver"; import { RejectedTraces } from "../../components/Admin/Troubleshooting/RejectedTraces"; export const routes: RouteObject[] = [ @@ -53,6 +55,8 @@ export const routes: RouteObject[] = [ } ] }, + { path: "navigate/errors/:id", element: }, + { path: "navigate/:id", element: }, { path: "*", element: diff --git a/src/redux/services/digma.ts b/src/redux/services/digma.ts index b027465bd..d2e913373 100644 --- a/src/redux/services/digma.ts +++ b/src/redux/services/digma.ts @@ -35,6 +35,8 @@ import type { GetEnvironmentServicesPayload, GetEnvironmentServicesResponse, GetEnvironmentsResponse, + GetErrorEnvironmentPayload, + GetErrorEnvironmentResponse, GetErrorPayload, GetErrorResponse, GetErrorsPayload, @@ -194,6 +196,15 @@ export const digmaApi = createApi({ }), providesTags: ["Error"] }), + getErrorEnvironment: builder.query< + GetErrorEnvironmentResponse, + GetErrorEnvironmentPayload + >({ + query: ({ id }) => ({ + url: "CodeAnalytics/codeObjects/error_environment", + params: { errorId: id } + }) + }), getSpanInsight: builder.query< GetSpanInsightResponse, GetSpanInsightPayload @@ -764,6 +775,7 @@ export const { useGetBlockedTracesQuery, useGetErrorsQuery, useGetErrorQuery, + useGetErrorEnvironmentQuery, useGetGlobalErrorsQuery, useGetGlobalErrorFiltersQuery, useGetErrorTimeseriesQuery, diff --git a/src/redux/services/types.ts b/src/redux/services/types.ts index 6e5a55c84..02fa3d16f 100644 --- a/src/redux/services/types.ts +++ b/src/redux/services/types.ts @@ -850,6 +850,14 @@ export interface GetErrorResponse { originServices: ErrorOriginService[] | null; } +export interface GetErrorEnvironmentPayload { + id: string; +} + +export interface GetErrorEnvironmentResponse { + environmentId: string; +} + export enum GlobalErrorsSortingCriterion { Criticality = "Criticality", Latest = "Latest" diff --git a/src/utils/getWebAdminLinkForSpan.ts b/src/utils/getWebAdminLinkForSpan.ts new file mode 100644 index 000000000..fe727dbe7 --- /dev/null +++ b/src/utils/getWebAdminLinkForSpan.ts @@ -0,0 +1,12 @@ +export const getWebAdminLinkForSpan = ( + spanUid?: string +): string | undefined => { + if (!spanUid) { + return; + } + + const baseURL = `${window.location.origin}/admin/navigate/${spanUid}`; + const url = new URL(baseURL); + + return url.toString(); +}; diff --git a/src/utils/getWebAdminLinkToError.ts b/src/utils/getWebAdminLinkToError.ts new file mode 100644 index 000000000..87b34b33f --- /dev/null +++ b/src/utils/getWebAdminLinkToError.ts @@ -0,0 +1,12 @@ +export const getWebAdminLinkForError = ( + errorId?: string +): string | undefined => { + if (!errorId) { + return; + } + + const baseURL = `${window.location.origin}/admin/navigate/errors/${errorId}`; + const url = new URL(baseURL); + + return url.toString(); +}; From 9da928920f70888000c21843560cfb455a06c502 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 8 Jul 2025 07:00:37 +0000 Subject: [PATCH 2/9] Bump version to 16.3.0-alpha.0 [skip ci] --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index fb84727ef..a9e2fa280 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "digma-ui", - "version": "16.2.1", + "version": "16.3.0-alpha.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "digma-ui", - "version": "16.2.1", + "version": "16.3.0-alpha.0", "license": "MIT", "dependencies": { "@codemirror/lang-json": "^6.0.2", diff --git a/package.json b/package.json index c0991824a..fa6a3f03f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "digma-ui", - "version": "16.2.1", + "version": "16.3.0-alpha.0", "description": "Digma UI", "scripts": { "lint:eslint": "eslint --cache .", From e41c3d33a4c5ad1de51946385c02a601ab716b9a Mon Sep 17 00:00:00 2001 From: Kyrylo Shmidt Date: Tue, 8 Jul 2025 10:48:46 +0200 Subject: [PATCH 3/9] Fix Dialog styles --- .../Agentic/IncidentTemplate/MCPServerDialog/index.tsx | 4 +++- .../CreateIncidentChatOverlay/index.tsx | 5 ++--- .../CreateIncidentChatOverlay/styles.ts | 5 +++++ src/components/Agentic/common/Dialog/index.tsx | 9 +++++++-- src/components/Agentic/common/Dialog/styles.ts | 3 ++- src/components/Agentic/common/Dialog/types.ts | 1 + 6 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/components/Agentic/IncidentTemplate/MCPServerDialog/index.tsx b/src/components/Agentic/IncidentTemplate/MCPServerDialog/index.tsx index 136bca8ce..e4da05c75 100644 --- a/src/components/Agentic/IncidentTemplate/MCPServerDialog/index.tsx +++ b/src/components/Agentic/IncidentTemplate/MCPServerDialog/index.tsx @@ -154,8 +154,10 @@ export const MCPServerDialog = ({ onClose(); }; + const title = serverData?.uid ? "Set MCP Server" : "Add MCP Server"; + return ( - + {steps[currentStep]} ); diff --git a/src/components/Agentic/IncidentsContainer/CreateIncidentChatOverlay/index.tsx b/src/components/Agentic/IncidentsContainer/CreateIncidentChatOverlay/index.tsx index 399566118..beafbbbc3 100644 --- a/src/components/Agentic/IncidentsContainer/CreateIncidentChatOverlay/index.tsx +++ b/src/components/Agentic/IncidentsContainer/CreateIncidentChatOverlay/index.tsx @@ -14,7 +14,6 @@ import { setIsCreateIncidentChatOpen } from "../../../../redux/slices/incidentsS import { isString } from "../../../../typeGuards/isString"; import { sendUserActionTrackingEvent } from "../../../../utils/actions/sendUserActionTrackingEvent"; import { CancelConfirmation } from "../../../common/CancelConfirmation"; -import { Dialog } from "../../common/Dialog"; import { trackingEvents } from "../../tracking"; import * as s from "./styles"; @@ -197,7 +196,7 @@ export const CreateIncidentChatOverlay = () => { return ( <> - @@ -211,7 +210,7 @@ export const CreateIncidentChatOverlay = () => { onNavigateToIncident={handleIncidentNavigate} typeInitialMessages={true} /> - + {isCloseConfirmationDialogVisible && ( diff --git a/src/components/Agentic/IncidentsContainer/CreateIncidentChatOverlay/styles.ts b/src/components/Agentic/IncidentsContainer/CreateIncidentChatOverlay/styles.ts index 342d9dac4..d0d76c24e 100644 --- a/src/components/Agentic/IncidentsContainer/CreateIncidentChatOverlay/styles.ts +++ b/src/components/Agentic/IncidentsContainer/CreateIncidentChatOverlay/styles.ts @@ -1,11 +1,16 @@ import styled from "styled-components"; import { Overlay } from "../../../common/Overlay"; import { AgentChat } from "../../common/AgentChat"; +import { Dialog } from "../../common/Dialog"; export const StyledOverlay = styled(Overlay)` align-items: center; `; +export const StyledDialog = styled(Dialog)` + height: 437px; +`; + export const StyledAgentChat = styled(AgentChat)` ${/* TODO: change to color from the theme */ ""} background: #000; diff --git a/src/components/Agentic/common/Dialog/index.tsx b/src/components/Agentic/common/Dialog/index.tsx index 2669b5383..0942fa715 100644 --- a/src/components/Agentic/common/Dialog/index.tsx +++ b/src/components/Agentic/common/Dialog/index.tsx @@ -3,8 +3,13 @@ import { CrossIcon } from "../../../common/icons/12px/CrossIcon"; import * as s from "./styles"; import type { DialogProps } from "./types"; -export const Dialog = ({ title, onClose, children }: DialogProps) => ( - +export const Dialog = ({ + title, + onClose, + children, + className +}: DialogProps) => ( + {isString(title) ? title : null} diff --git a/src/components/Agentic/common/Dialog/styles.ts b/src/components/Agentic/common/Dialog/styles.ts index 0bcdba8e8..70f7149b7 100644 --- a/src/components/Agentic/common/Dialog/styles.ts +++ b/src/components/Agentic/common/Dialog/styles.ts @@ -4,7 +4,8 @@ export const Container = styled.div` display: flex; flex-direction: column; width: 635px; - min-height: 420px; + min-height: 437px; + box-sizing: border-box; max-height: fit-content; padding: 12px; gap: 16px; diff --git a/src/components/Agentic/common/Dialog/types.ts b/src/components/Agentic/common/Dialog/types.ts index 9ff98e2aa..ffc6dd960 100644 --- a/src/components/Agentic/common/Dialog/types.ts +++ b/src/components/Agentic/common/Dialog/types.ts @@ -4,4 +4,5 @@ export interface DialogProps { title?: string; onClose: () => void; children: ReactNode; + className?: string; } From 6efe0e45423d45c58dfc577e8aafef2398b84d41 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 8 Jul 2025 08:58:05 +0000 Subject: [PATCH 4/9] Bump version to 16.3.0-alpha.1 [skip ci] --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index a9e2fa280..e3cb2c9ce 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "digma-ui", - "version": "16.3.0-alpha.0", + "version": "16.3.0-alpha.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "digma-ui", - "version": "16.3.0-alpha.0", + "version": "16.3.0-alpha.1", "license": "MIT", "dependencies": { "@codemirror/lang-json": "^6.0.2", diff --git a/package.json b/package.json index fa6a3f03f..d07fe3b42 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "digma-ui", - "version": "16.3.0-alpha.0", + "version": "16.3.0-alpha.1", "description": "Digma UI", "scripts": { "lint:eslint": "eslint --cache .", From 8062a47b00266617ad625ada9799571bd7192a2d Mon Sep 17 00:00:00 2001 From: Kyrylo Shmidt Date: Tue, 8 Jul 2025 11:14:39 +0200 Subject: [PATCH 5/9] Fix SpanInfo styles --- src/components/Navigation/SpanInfo/styles.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/Navigation/SpanInfo/styles.ts b/src/components/Navigation/SpanInfo/styles.ts index 3e6fabacb..62e516e5d 100644 --- a/src/components/Navigation/SpanInfo/styles.ts +++ b/src/components/Navigation/SpanInfo/styles.ts @@ -10,6 +10,7 @@ export const Container = styled.div` overflow: hidden; gap: 8px; padding: 8px 8px 12px; + width: 100%; `; export const StyledCodeSnippet = styled(CodeSnippet)` From 07127f4838b612af6e4a206dbf4611279d29f549 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 8 Jul 2025 09:15:57 +0000 Subject: [PATCH 6/9] Bump version to 16.3.0-alpha.2 [skip ci] --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index e3cb2c9ce..18f0c1934 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "digma-ui", - "version": "16.3.0-alpha.1", + "version": "16.3.0-alpha.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "digma-ui", - "version": "16.3.0-alpha.1", + "version": "16.3.0-alpha.2", "license": "MIT", "dependencies": { "@codemirror/lang-json": "^6.0.2", diff --git a/package.json b/package.json index d07fe3b42..f6780d07b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "digma-ui", - "version": "16.3.0-alpha.1", + "version": "16.3.0-alpha.2", "description": "Digma UI", "scripts": { "lint:eslint": "eslint --cache .", From d958b709b2f5c2f4996497728fb2eb63551b7027 Mon Sep 17 00:00:00 2001 From: Kyrylo Shmidt Date: Tue, 8 Jul 2025 11:31:23 +0200 Subject: [PATCH 7/9] Fix links to errors --- .../Admin/common/RepositorySidebarOverlay/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Admin/common/RepositorySidebarOverlay/index.tsx b/src/components/Admin/common/RepositorySidebarOverlay/index.tsx index 6661749d3..1f07697f8 100644 --- a/src/components/Admin/common/RepositorySidebarOverlay/index.tsx +++ b/src/components/Admin/common/RepositorySidebarOverlay/index.tsx @@ -157,10 +157,10 @@ export const RepositorySidebarOverlay = ({ }, { spanCodeObjectId: newSpanCodeObjectId, - tabLocation: { id: TAB_IDS.ISSUES } + tabLocation: sidebarLocation ?? { id: TAB_IDS.ISSUES } } ); - }, [history, sidebarQuery?.query?.scopedSpanCodeObjectId]); + }, [history, sidebarQuery?.query?.scopedSpanCodeObjectId, sidebarLocation]); const handleSidebarClose = useCallback(() => { dispatch(clear()); From 1ee8cce5b314e3a903fd2d4677fa45a2bf7cfc1b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 8 Jul 2025 09:36:35 +0000 Subject: [PATCH 8/9] Bump version to 16.3.0-alpha.3 [skip ci] --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 18f0c1934..7ac500487 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "digma-ui", - "version": "16.3.0-alpha.2", + "version": "16.3.0-alpha.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "digma-ui", - "version": "16.3.0-alpha.2", + "version": "16.3.0-alpha.3", "license": "MIT", "dependencies": { "@codemirror/lang-json": "^6.0.2", diff --git a/package.json b/package.json index f6780d07b..8007ee9f5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "digma-ui", - "version": "16.3.0-alpha.2", + "version": "16.3.0-alpha.3", "description": "Digma UI", "scripts": { "lint:eslint": "eslint --cache .", From 69b40d0725a88085bef6d237aeb6c9cf2007097c Mon Sep 17 00:00:00 2001 From: Kyrylo Shmidt Date: Tue, 8 Jul 2025 13:48:50 +0200 Subject: [PATCH 9/9] Add Investigate button for errors --- .../FlowStack/index.tsx | 46 +++++++++ src/components/Errors/NewErrorCard/index.tsx | 96 +++++++++++++++---- src/components/Errors/tracking.ts | 3 + .../insightCards/common/InsightCard/index.tsx | 20 ++-- src/redux/services/digma.ts | 19 ++-- src/redux/services/types.ts | 9 +- 6 files changed, 152 insertions(+), 41 deletions(-) diff --git a/src/components/Errors/ErrorDetails/ErrorDetailsCardContent/FlowStack/index.tsx b/src/components/Errors/ErrorDetails/ErrorDetailsCardContent/FlowStack/index.tsx index e7d556061..a0cab7c9d 100644 --- a/src/components/Errors/ErrorDetails/ErrorDetailsCardContent/FlowStack/index.tsx +++ b/src/components/Errors/ErrorDetails/ErrorDetailsCardContent/FlowStack/index.tsx @@ -3,7 +3,9 @@ import { v4 as uuidv4 } from "uuid"; import type { DataFetcherConfiguration } from "../../../../../hooks/useFetchData"; import { useFetchData } from "../../../../../hooks/useFetchData"; import { platform } from "../../../../../platform"; +import { useInvestigateMutation } from "../../../../../redux/services/digma"; import type { ErrorFlowFrame } from "../../../../../redux/services/types"; +import { useConfigSelector } from "../../../../../store/config/useConfigSelector"; import { useErrorsSelector } from "../../../../../store/errors/useErrorsSelector"; import { useStore } from "../../../../../store/useStore"; import { isNull } from "../../../../../typeGuards/isNull"; @@ -13,6 +15,11 @@ import { openURLInDefaultBrowser } from "../../../../../utils/actions/openURLInD import { sendUserActionTrackingEvent } from "../../../../../utils/actions/sendUserActionTrackingEvent"; import { getIdeLauncherLinkForError } from "../../../../../utils/getIdeLauncherLinkForError"; import { openBrowserTabWithContent } from "../../../../../utils/openBrowserTabWithContent"; +import { + InvestigateButton, + InvestigateButtonSpinner +} from "../../../../Insights/InsightsCatalog/InsightsPage/InsightCardRenderer/insightCards/common/InsightCard/styles"; +import { LightBulbWithScrewIcon } from "../../../../common/icons/16px/LightBulbWithScrewIcon"; import { TraceIcon } from "../../../../common/icons/16px/TraceIcon"; import { CodeIcon } from "../../../../common/icons/CodeIcon"; import { NewButton } from "../../../../common/v3/NewButton"; @@ -44,6 +51,8 @@ export const FlowStack = ({ data, errorId }: FlowStackProps) => { useErrorsSelector(); const { setErrorDetailsWorkspaceItemsOnly } = useStore.getState(); const stacksContainerRef = useRef(null); + const { isAgenticEnabled } = useConfigSelector(); + const [investigate, investigateResult] = useInvestigateMutation(); const frameStacks = useMemo(() => data.frameStacks ?? [], [data]); @@ -133,6 +142,28 @@ export const FlowStack = ({ data, errorId }: FlowStackProps) => { } }; + const handleInvestigateButtonClick = () => { + sendUserActionTrackingEvent( + trackingEvents.ERROR_CARD_INVESTIGATE_BUTTON_CLICKED + ); + + void investigate({ + data: { + targetId: errorId, + targetType: "error" + } + }) + .unwrap() + .then((data) => { + const incidentId = data.incidentId; + openURLInDefaultBrowser(`/agentic/incidents/${incidentId}`); + }) + .catch(() => { + // eslint-disable-next-line no-console + console.error("Failed to create incident from error"); + }); + }; + const handleWorkspaceOnlyToggleSwitchChange = (value: boolean) => { sendUserActionTrackingEvent(trackingEvents.WORKSPACE_ONLY_TOGGLE_SWITCHED, { value @@ -237,6 +268,21 @@ export const FlowStack = ({ data, errorId }: FlowStackProps) => { )} + {platform === "Web" && isAgenticEnabled && ( + + : LightBulbWithScrewIcon + } + onClick={handleInvestigateButtonClick} + isDisabled={investigateResult.isLoading} + label={ + investigateResult.isLoading ? "Investigating..." : "Investigate" + } + /> + )} diff --git a/src/components/Errors/NewErrorCard/index.tsx b/src/components/Errors/NewErrorCard/index.tsx index 6461ca896..fb9142ee9 100644 --- a/src/components/Errors/NewErrorCard/index.tsx +++ b/src/components/Errors/NewErrorCard/index.tsx @@ -3,6 +3,8 @@ import { CSSTransition } from "react-transition-group"; import { getFeatureFlagValue } from "../../../featureFlags"; import { usePrevious } from "../../../hooks/usePrevious"; import { platform } from "../../../platform"; +import { useInvestigateMutation } from "../../../redux/services/digma"; +import { useConfigSelector } from "../../../store/config/useConfigSelector"; import { ViewMode } from "../../../store/errors/errorsSlice"; import { useErrorsSelector } from "../../../store/errors/useErrorsSelector"; import { useStore } from "../../../store/useStore"; @@ -17,10 +19,15 @@ import { import type { Option } from "../../common/AffectedEndpointsSelector/types"; import { CodeIcon } from "../../common/icons/16px/CodeIcon"; import { HistogramIcon } from "../../common/icons/16px/HistogramIcon"; +import { LightBulbWithScrewIcon } from "../../common/icons/16px/LightBulbWithScrewIcon"; import { PinFillIcon } from "../../common/icons/16px/PinFillIcon"; import { PinIcon } from "../../common/icons/16px/PinIcon"; import { NewIconButton } from "../../common/v3/NewIconButton"; import { Tooltip } from "../../common/v3/Tooltip"; +import { + InvestigateButton, + InvestigateButtonSpinner +} from "../../Insights/InsightsCatalog/InsightsPage/InsightCardRenderer/insightCards/common/InsightCard/styles"; import { TimestampKeyValue } from "../ErrorCard/TimestampKeyValue"; import { usePinning } from "../GlobalErrorsList/usePinning"; import { getTagType, HIGH_SEVERITY_SCORE_THRESHOLD } from "../Score"; @@ -46,8 +53,10 @@ export const NewErrorCard = ({ const [isHistogramVisible, setIsHistogramVisible] = useState(false); const chartContainerRef = useRef(null); const { globalErrorsList } = useErrorsSelector(); + const { isAgenticEnabled } = useConfigSelector(); const { setGlobalErrorsViewMode } = useStore.getState(); const [isPinned, setIsPinned] = useState(Boolean(data.pinnedAt)); + const [investigate, investigateResult] = useInvestigateMutation(); const isOccurrenceChartEnabled = getFeatureFlagValue( backendInfo, @@ -228,6 +237,28 @@ export const NewErrorCard = ({ show(); }; + const handleInvestigateButtonClick = () => { + sendUserActionTrackingEvent( + trackingEvents.ERROR_CARD_INVESTIGATE_BUTTON_CLICKED + ); + + void investigate({ + data: { + targetId: id, + targetType: "error" + } + }) + .unwrap() + .then((data) => { + const incidentId = data.incidentId; + openURLInDefaultBrowser(`/agentic/incidents/${incidentId}`); + }) + .catch(() => { + // eslint-disable-next-line no-console + console.error("Failed to create incident from error"); + }); + }; + const isCritical = score.score > HIGH_SEVERITY_SCORE_THRESHOLD; const selectedOption = useMemo( @@ -241,34 +272,61 @@ export const NewErrorCard = ({ const toolbarActions = [ ...(isPinEnabled ? [ - + + + ] : []), ...(isOccurrenceChartEnabled && selectedEndpointKey ? [ - + > + + ] : []), ...(platform === "Web" ? [ - + + + ] + : []), + ...(platform === "Web" && isAgenticEnabled + ? [ + + : LightBulbWithScrewIcon + } + onClick={handleInvestigateButtonClick} + isDisabled={investigateResult.isLoading} + label={ + investigateResult.isLoading ? "Investigating..." : "Investigate" + } /> ] : []) diff --git a/src/components/Errors/tracking.ts b/src/components/Errors/tracking.ts index 2614c3415..9b77a2b56 100644 --- a/src/components/Errors/tracking.ts +++ b/src/components/Errors/tracking.ts @@ -10,6 +10,7 @@ export const trackingEvents = addPrefix( FLOW_PAGINATION_BUTTON_CLICKED: "flow pagination button clicked", TRACE_BUTTON_CLICKED: "trace button clicked", OPEN_IN_IDE_BUTTON_CLICKED: "open in ide button clicked", + INVESTIGATE_BUTTON_CLICKED: "investigate button clicked", ERROR_STACK_TRACE_ITEM_CLICKED: "error stack trace item clicked", RAW_ERROR_STACK_TRACE_BUTTON_CLICKED: "raw error stack trace button clicked", @@ -46,6 +47,8 @@ export const trackingEvents = addPrefix( ERROR_CARD_PIN_UNPIN_BUTTON_CLICKED: "error card pin unpin button clicked", ERROR_CARD_OPEN_IN_IDE_BUTTON_CLICKED: "error card open in ide button clicked", + ERROR_CARD_INVESTIGATE_BUTTON_CLICKED: + "error card investigate button clicked", ERROR_CARD_DISMISS_BUTTON_CLICKED: "error card dismiss button clicked", ERROR_CARD_PIN_UNDISMISS_BUTTON_CLICKED: "error card pin undismiss button clicked" diff --git a/src/components/Insights/InsightsCatalog/InsightsPage/InsightCardRenderer/insightCards/common/InsightCard/index.tsx b/src/components/Insights/InsightsCatalog/InsightsPage/InsightCardRenderer/insightCards/common/InsightCard/index.tsx index 585f741a1..f0ddb1837 100644 --- a/src/components/Insights/InsightsCatalog/InsightsPage/InsightCardRenderer/insightCards/common/InsightCard/index.tsx +++ b/src/components/Insights/InsightsCatalog/InsightsPage/InsightCardRenderer/insightCards/common/InsightCard/index.tsx @@ -2,7 +2,7 @@ import { Fragment, useEffect, useRef, useState } from "react"; import { usePrevious } from "../../../../../../../../hooks/usePrevious"; import { platform } from "../../../../../../../../platform"; import { - useCreateIncidentFromInsightMutation, + useInvestigateMutation, useMarkInsightReadMutation, useRecheckInsightMutation } from "../../../../../../../../redux/services/digma"; @@ -77,8 +77,7 @@ export const InsightCard = ({ } = useConfigSelector(); const cardRef = useRef(null); const [showInfo, setShowInfo] = useState(false); - const [createIncidentFromInsight, createIncidentFromInsightResult] = - useCreateIncidentFromInsightMutation(); + const [investigate, investigateResult] = useInvestigateMutation(); const isCritical = insight.criticality > HIGH_CRITICALITY_THRESHOLD; @@ -288,8 +287,11 @@ export const InsightCard = ({ } ); - void createIncidentFromInsight({ - insightId: insight.id + void investigate({ + data: { + targetId: insight.id, + targetType: "issue" + } }) .unwrap() .then((data) => { @@ -465,16 +467,14 @@ export const InsightCard = ({ return ( : LightBulbWithScrewIcon } onClick={handleInvestigateButtonClick} - isDisabled={createIncidentFromInsightResult.isLoading} + isDisabled={investigateResult.isLoading} label={ - createIncidentFromInsightResult.isLoading - ? "Investigating..." - : "Investigate" + investigateResult.isLoading ? "Investigating..." : "Investigate" } /> ); diff --git a/src/redux/services/digma.ts b/src/redux/services/digma.ts index d2e913373..ef4d3455a 100644 --- a/src/redux/services/digma.ts +++ b/src/redux/services/digma.ts @@ -2,11 +2,11 @@ import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react"; import { isString } from "../../typeGuards/isString"; import type { AddMCPServerPayload, + AgenticInvestigatePayload, + AgenticInvestigateResponse, CloseIncidentPayload, CreateEnvironmentPayload, CreateEnvironmentResponse, - CreateIncidentFromInsightPayload, - CreateIncidentFromInsightResponse, DeleteEnvironmentPayload, DeleteEnvironmentResponse, DeleteIncidentAgentDirectivePayload, @@ -753,13 +753,14 @@ export const digmaApi = createApi({ }), invalidatesTags: ["IncidentAgentMCPServer"] }), - createIncidentFromInsight: builder.mutation< - CreateIncidentFromInsightResponse, - CreateIncidentFromInsightPayload + investigate: builder.mutation< + AgenticInvestigateResponse, + AgenticInvestigatePayload >({ - query: ({ insightId }) => ({ - url: `Agentic/issue-entry/${window.encodeURIComponent(insightId)}`, - method: "POST" + query: ({ data }) => ({ + url: `Agentic/investigate`, + method: "POST", + body: data }) }) }) @@ -833,5 +834,5 @@ export const { useAddIncidentAgentMCPServerMutation, useUpdateIncidentAgentMCPServerMutation, useDeleteIncidentAgentMCPServerMutation, - useCreateIncidentFromInsightMutation + useInvestigateMutation } = digmaApi; diff --git a/src/redux/services/types.ts b/src/redux/services/types.ts index 02fa3d16f..43b0d0d8a 100644 --- a/src/redux/services/types.ts +++ b/src/redux/services/types.ts @@ -1296,10 +1296,13 @@ export interface DeleteMCPServerPayload { id: string; } -export interface CreateIncidentFromInsightPayload { - insightId: string; +export interface AgenticInvestigatePayload { + data: { + targetId: string; + targetType: "issue" | "error"; + }; } -export interface CreateIncidentFromInsightResponse { +export interface AgenticInvestigateResponse { incidentId: string; }