diff --git a/public/assets/images/admin/home/overviewWidgetMetricsDivider_dark.svg b/public/assets/images/admin/home/overviewWidgetMetricsDivider_dark.svg
new file mode 100644
index 000000000..d171f9f38
--- /dev/null
+++ b/public/assets/images/admin/home/overviewWidgetMetricsDivider_dark.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/public/assets/images/admin/home/overviewWidgetMetricsDivider_light.svg b/public/assets/images/admin/home/overviewWidgetMetricsDivider_light.svg
new file mode 100644
index 000000000..0d38740ae
--- /dev/null
+++ b/public/assets/images/admin/home/overviewWidgetMetricsDivider_light.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/src/components/Admin/Header/EnvironmentSelect/index.tsx b/src/components/Admin/Header/EnvironmentSelect/index.tsx
new file mode 100644
index 000000000..cbd7e9a32
--- /dev/null
+++ b/src/components/Admin/Header/EnvironmentSelect/index.tsx
@@ -0,0 +1,65 @@
+import { useEffect, useMemo } from "react";
+import {
+ useAdminDispatch,
+ useAdminSelector
+} from "../../../../containers/Admin/hooks";
+import { useGetEnvironmentsQuery } from "../../../../redux/services/digma";
+import { setEnvironmentId } from "../../../../redux/slices/scopeSlice";
+import { CodeIcon } from "../../../common/icons/12px/CodeIcon";
+import { InfinityIcon } from "../../../common/icons/InfinityIcon";
+import { sortEnvironments } from "../../../common/IssuesReport/utils";
+import * as s from "./styles";
+
+export const EnvironmentSelect = () => {
+ const { data: environments } = useGetEnvironmentsQuery();
+ const sortedEnvironments = useMemo(
+ () => sortEnvironments(environments ?? []),
+ [environments]
+ );
+ const selectedEnvironmentId = useAdminSelector(
+ (state) => state.scope.environmentId
+ );
+ const selectedEnvironment = useMemo(
+ () =>
+ sortedEnvironments?.find((x) => x.id === selectedEnvironmentId) ?? null,
+ [selectedEnvironmentId, sortedEnvironments]
+ );
+ const dispatch = useAdminDispatch();
+
+ const handleEnvironmentChanged = (option: string | string[]) => {
+ const newItem = Array.isArray(option) ? option[0] : option;
+ dispatch(setEnvironmentId(newItem));
+ };
+
+ useEffect(() => {
+ if (
+ sortedEnvironments &&
+ sortedEnvironments.length > 0 &&
+ !selectedEnvironmentId
+ ) {
+ dispatch(setEnvironmentId(sortedEnvironments[0].id));
+ }
+ }, [dispatch, sortedEnvironments, selectedEnvironmentId]);
+
+ return (
+ ({
+ label: x.name,
+ value: x.id,
+ enabled: true,
+ selected: x.id === selectedEnvironmentId
+ }))}
+ showSelectedState={true}
+ icon={(props) =>
+ selectedEnvironment?.type === "Public" ? (
+
+ ) : (
+
+ )
+ }
+ onChange={handleEnvironmentChanged}
+ placeholder={selectedEnvironment?.name ?? "Select Environments"}
+ disabled={sortedEnvironments.length === 0}
+ />
+ );
+};
diff --git a/src/components/Admin/Header/EnvironmentSelect/styles.ts b/src/components/Admin/Header/EnvironmentSelect/styles.ts
new file mode 100644
index 000000000..4aed449f6
--- /dev/null
+++ b/src/components/Admin/Header/EnvironmentSelect/styles.ts
@@ -0,0 +1,7 @@
+import styled from "styled-components";
+
+import { Select as CommonSelect } from "../../../common/v3/Select";
+
+export const Select = styled(CommonSelect)`
+ width: 180px;
+`;
diff --git a/src/components/Admin/Header/index.tsx b/src/components/Admin/Header/index.tsx
index be8deb329..bcf698370 100644
--- a/src/components/Admin/Header/index.tsx
+++ b/src/components/Admin/Header/index.tsx
@@ -1,4 +1,5 @@
import { Route, Routes } from "react-router-dom";
+import { EnvironmentSelect } from "./EnvironmentSelect";
import { Greeting } from "./Greeting";
import * as s from "./styles";
@@ -7,7 +8,12 @@ export const Header = () => (
}
+ element={
+
+
+
+
+ }
/>
Reports} />
diff --git a/src/components/Admin/Header/styles.ts b/src/components/Admin/Header/styles.ts
index 4207558e2..8c64bcd1d 100644
--- a/src/components/Admin/Header/styles.ts
+++ b/src/components/Admin/Header/styles.ts
@@ -10,3 +10,10 @@ export const Header = styled.header`
box-sizing: border-box;
color: ${({ theme }) => theme.colors.v3.text.primary};
`;
+
+export const HomeHeader = styled.div`
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ gap: 8px;
+`;
diff --git a/src/components/Admin/Home/Overview/OverviewWidget/styles.ts b/src/components/Admin/Home/Overview/OverviewWidget/styles.ts
index 2fa992d18..09e58d9e7 100644
--- a/src/components/Admin/Home/Overview/OverviewWidget/styles.ts
+++ b/src/components/Admin/Home/Overview/OverviewWidget/styles.ts
@@ -4,10 +4,7 @@ export const Container = styled.div`
display: flex;
flex-direction: column;
padding: 16px;
- gap: 8px;
border-radius: 12px;
- width: 279px;
- height: 108px;
border: 1px solid ${({ theme }) => theme.colors.v3.stroke.primary};
flex-shrink: 0;
box-sizing: border-box;
diff --git a/src/components/Admin/Home/Overview/TopIssuesWidget/index.tsx b/src/components/Admin/Home/Overview/TopIssuesWidget/index.tsx
new file mode 100644
index 000000000..594128a7f
--- /dev/null
+++ b/src/components/Admin/Home/Overview/TopIssuesWidget/index.tsx
@@ -0,0 +1,93 @@
+import { useTheme } from "styled-components";
+import { useAdminSelector } from "../../../../../containers/Admin/hooks";
+import { getFeatureFlagValue } from "../../../../../featureFlags";
+import { useGetAboutQuery } from "../../../../../redux/services/digma";
+import { FeatureFlag } from "../../../../../types";
+import { getThemeKind } from "../../../../common/App/styles";
+import { WarningTriangleIcon } from "../../../../common/icons/12px/WarningTriangleIcon";
+import { MeterHighIcon } from "../../../../common/icons/16px/MeterHighIcon";
+import { ChevronIcon } from "../../../../common/icons/20px/ChevronIcon";
+import { Direction } from "../../../../common/icons/types";
+import { OverviewWidget } from "../OverviewWidget";
+import * as s from "./styles";
+import type { TopIssuesWidgetProps } from "./types";
+
+const ISSUES_LIMIT = 10;
+
+export const TopIssuesWidget = ({ onGetIssues }: TopIssuesWidgetProps) => {
+ const theme = useTheme();
+ const themeKind = getThemeKind(theme);
+ const environmentId = useAdminSelector((state) => state.scope.environmentId);
+
+ const handleByCriticalityButtonClick = () => {
+ onGetIssues({
+ query: {
+ environment: environmentId ?? undefined
+ },
+ limit: ISSUES_LIMIT
+ });
+ };
+
+ const handleBySeverityButtonClick = () => {
+ onGetIssues({
+ query: {
+ environment: environmentId ?? undefined,
+ sortBy: "severity"
+ },
+ limit: ISSUES_LIMIT
+ });
+ };
+
+ const { data: about } = useGetAboutQuery();
+
+ const isBySeverityButtonVisible = Boolean(
+ about &&
+ getFeatureFlagValue(
+ about,
+ FeatureFlag.IS_INSIGHT_SEVERITY_SORTING_ENABLED
+ )
+ );
+
+ return (
+
+
+ Top 10 Issues
+
+
+
+
+
+ By Criticality
+
+
+
+
+ {isBySeverityButtonVisible && (
+ <>
+
+
+
+
+
+ By Severity
+
+
+
+
+ >
+ )}
+
+
+
+ );
+};
diff --git a/src/components/Admin/Home/Overview/TopIssuesWidget/styles.ts b/src/components/Admin/Home/Overview/TopIssuesWidget/styles.ts
new file mode 100644
index 000000000..f38762be7
--- /dev/null
+++ b/src/components/Admin/Home/Overview/TopIssuesWidget/styles.ts
@@ -0,0 +1,62 @@
+import styled from "styled-components";
+import {
+ subheading1MediumTypography,
+ subheading2RegularTypography
+} from "../../../../common/App/typographies";
+
+export const Container = styled.div`
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+ flex-grow: 1;
+`;
+
+export const Title = styled.span`
+ ${subheading1MediumTypography}
+ color: ${({ theme }) => theme.colors.v3.text.primary};
+`;
+
+export const ButtonsContainer = styled.div`
+ display: flex;
+ align-items: flex-end;
+`;
+
+export const IconContainer = styled.span`
+ height: 40px;
+ width: 40px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ border-radius: 50%;
+ background: ${({ theme }) => theme.colors.v3.surface.primary};
+`;
+
+export const Button = styled.button`
+ ${subheading2RegularTypography}
+ color: ${({ theme }) => theme.colors.v3.text.primary};
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ background: none;
+ border: none;
+ text-transform: capitalize;
+ padding: 0;
+ cursor: pointer;
+`;
+
+export const ByCriticalityButton = styled(Button)`
+ padding-right: 10px;
+`;
+
+export const BySeverityButton = styled(Button)`
+ padding-left: 23px;
+`;
+
+export const ChevronIconContainer = styled.div`
+ color: ${({ theme }) => theme.colors.v3.icon.tertiary};
+`;
+
+export const Divider = styled.img`
+ width: 20px;
+ height: 69px;
+`;
diff --git a/src/components/Admin/Home/Overview/TopIssuesWidget/types.ts b/src/components/Admin/Home/Overview/TopIssuesWidget/types.ts
new file mode 100644
index 000000000..4e9444436
--- /dev/null
+++ b/src/components/Admin/Home/Overview/TopIssuesWidget/types.ts
@@ -0,0 +1,5 @@
+import type { IssuesSidebarQuery } from "../../../common/IssuesSidebarOverlay/types";
+
+export interface TopIssuesWidgetProps {
+ onGetIssues: (query: IssuesSidebarQuery) => void;
+}
diff --git a/src/components/Admin/Home/Overview/index.tsx b/src/components/Admin/Home/Overview/index.tsx
index b0652b542..e1080c9ba 100644
--- a/src/components/Admin/Home/Overview/index.tsx
+++ b/src/components/Admin/Home/Overview/index.tsx
@@ -2,24 +2,23 @@ import { useTheme } from "styled-components";
import { getThemeKind } from "../../../common/App/styles";
import { HomeSection } from "../HomeSection";
import * as s from "./styles";
+import { TopIssuesWidget } from "./TopIssuesWidget";
+import type { OverviewProps } from "./types";
-export const Overview = () => {
+export const Overview = ({ onGetIssues }: OverviewProps) => {
const theme = useTheme();
const themeKind = getThemeKind(theme);
return (
- {Array(4)
- .fill(null)
- .map((_, i) => (
-
- ))}
+
+ {[1, 2, 3].map((x) => (
+
+ ))}
);
diff --git a/src/components/Admin/Home/Overview/types.ts b/src/components/Admin/Home/Overview/types.ts
new file mode 100644
index 000000000..f35f206fd
--- /dev/null
+++ b/src/components/Admin/Home/Overview/types.ts
@@ -0,0 +1,5 @@
+import type { IssuesSidebarQuery } from "../../common/IssuesSidebarOverlay/types";
+
+export interface OverviewProps {
+ onGetIssues: (query: IssuesSidebarQuery) => void;
+}
diff --git a/src/components/Admin/Home/index.tsx b/src/components/Admin/Home/index.tsx
index 620225cae..5b38cae18 100644
--- a/src/components/Admin/Home/index.tsx
+++ b/src/components/Admin/Home/index.tsx
@@ -1,12 +1,33 @@
+import { useState } from "react";
+import { IssuesSidebarOverlay } from "../common/IssuesSidebarOverlay";
+import type { IssuesSidebarQuery } from "../common/IssuesSidebarOverlay/types";
import { Environments } from "./Environments";
import { Overview } from "./Overview";
import { Reports } from "./Reports";
import * as s from "./styles";
-export const Home = () => (
-
-
-
-
-
-);
+export const Home = () => {
+ const [issuesSidebarQuery, setIssuesSidebarQuery] =
+ useState();
+
+ const handleIssuesSidebarClose = () => {
+ setIssuesSidebarQuery(undefined);
+ };
+
+ const handleGetIssues = (query: IssuesSidebarQuery) => {
+ setIssuesSidebarQuery(query);
+ };
+
+ return (
+
+
+
+
+
+
+ );
+};
diff --git a/src/components/Admin/Reports/CodeIssues/index.tsx b/src/components/Admin/Reports/CodeIssues/index.tsx
index 0c654a9c3..3d4ea04bf 100644
--- a/src/components/Admin/Reports/CodeIssues/index.tsx
+++ b/src/components/Admin/Reports/CodeIssues/index.tsx
@@ -1,10 +1,12 @@
-import { useEffect, useRef, useState } from "react";
-import { CSSTransition } from "react-transition-group";
+import { useMemo, useState } from "react";
import {
useAdminDispatch,
useAdminSelector
} from "../../../../containers/Admin/hooks";
-import type { IssueCriticality } from "../../../../redux/services/types";
+import type {
+ GetIssuesPayload,
+ IssueCriticality
+} from "../../../../redux/services/types";
import {
setCriticalityLevels,
setPeriodInDays,
@@ -21,7 +23,7 @@ import {
} from "../../../../redux/slices/issuesReportSlice";
import { IssuesReport } from "../../../common/IssuesReport";
import type { TargetScope } from "../../../common/IssuesReport/types";
-import { IssuesSidebar } from "./IssuesSidebar";
+import { IssuesSidebarOverlay } from "../../common/IssuesSidebarOverlay";
import * as s from "./styles";
export const MIN_SIDEBAR_WIDTH = 382; // in pixels
@@ -43,21 +45,10 @@ export const getDefaultSidebarWidth = (windowWidth: number) => {
export const CodeIssues = () => {
const [isIssuesSidebarOpen, setIsIssuesSidebarOpen] = useState(false);
- const [scope, setScope] = useState<{ value: string; displayName?: string }>();
+ const [scope, setScope] = useState();
const [activeTileIds, setActiveTileIds] = useState(
undefined
);
- const sidebarContainerRef = useRef(null);
- const overlayRef = useRef(null);
- const [isIssuesSidebarTransitioning, setIsIssuesSidebarTransitioning] =
- useState(false);
- const [windowWidth, setWindowWidth] = useState(window.innerWidth);
- const defaultSidebarWidth = getDefaultSidebarWidth(windowWidth);
- const [isResizeHandlePressed, setIsResizeHandlePressed] = useState(false);
- const [startX, setStartX] = useState(0);
- const [left, setLeft] = useState(windowWidth - defaultSidebarWidth);
- const [startLeft, setStartLeft] = useState(0);
-
const selectedEnvironmentId = useAdminSelector(
(state) => state.codeIssuesReport.selectedEnvironmentId
);
@@ -82,6 +73,30 @@ export const CodeIssues = () => {
(state) => state.codeIssuesReport.selectedEndpoints
);
+ const query: Partial = useMemo(
+ () => ({
+ environment: selectedEnvironmentId ?? undefined,
+ scopedSpanCodeObjectId:
+ viewLevel === "endpoints" ? scope?.value : undefined,
+ services:
+ viewLevel === "services"
+ ? scope?.value
+ ? [scope.value]
+ : []
+ : viewLevel === "endpoints" && selectedService
+ ? [selectedService]
+ : []
+ }),
+ [scope?.value, viewLevel, selectedEnvironmentId, selectedService]
+ );
+
+ const issuesSidebarQuery = useMemo(
+ () => ({
+ query
+ }),
+ [query]
+ );
+
const dispatch = useAdminDispatch();
const handleTileTitleClick = (
@@ -145,65 +160,6 @@ export const CodeIssues = () => {
setActiveTileIds(undefined);
};
- const handleIssuesSidebarTransitionStart = () => {
- setIsIssuesSidebarTransitioning(true);
- };
-
- const handleIssuesSidebarTransitionEnd = () => {
- setIsIssuesSidebarTransitioning(false);
- };
-
- const handleResizeHandleMouseDown = (e: React.MouseEvent) => {
- setIsResizeHandlePressed(true);
- setStartX(e.clientX);
- setStartLeft(left);
- };
-
- const handleWindowResize = () => {
- setWindowWidth(window.innerWidth);
- };
-
- useEffect(() => {
- window.addEventListener("resize", handleWindowResize);
-
- return () => {
- window.removeEventListener("resize", handleWindowResize);
- };
- }, []);
-
- useEffect(() => {
- const newLeft = windowWidth - getDefaultSidebarWidth(windowWidth);
- setLeft(newLeft);
- }, [windowWidth]);
-
- useEffect(() => {
- if (!isResizeHandlePressed) {
- return;
- }
-
- const handleMouseMove = (e: MouseEvent) => {
- const newLeft = startLeft + (e.clientX - startX);
- if (
- newLeft >= windowWidth - MAX_SIDEBAR_WIDTH &&
- newLeft <= windowWidth - MIN_SIDEBAR_WIDTH
- ) {
- setLeft(newLeft);
- }
- };
-
- const handleMouseUp = () => {
- setIsResizeHandlePressed(false);
- };
-
- document.addEventListener("mousemove", handleMouseMove);
- document.addEventListener("mouseup", handleMouseUp);
-
- return () => {
- document.removeEventListener("mousemove", handleMouseMove);
- document.removeEventListener("mouseup", handleMouseUp);
- };
- }, [isResizeHandlePressed, windowWidth, startX, startLeft, left]);
-
return (
{
onSelectedServiceChange={handleSelectedServiceChange}
activeTileIds={activeTileIds}
/>
-
-
-
-
-
-
-
-
+
);
};
diff --git a/src/components/Admin/Reports/CodeIssues/IssuesSidebar/SuggestionBar/SuggestionBar.stories.tsx b/src/components/Admin/common/IssuesSidebarOverlay/IssuesSidebar/SuggestionBar/SuggestionBar.stories.tsx
similarity index 100%
rename from src/components/Admin/Reports/CodeIssues/IssuesSidebar/SuggestionBar/SuggestionBar.stories.tsx
rename to src/components/Admin/common/IssuesSidebarOverlay/IssuesSidebar/SuggestionBar/SuggestionBar.stories.tsx
diff --git a/src/components/Admin/Reports/CodeIssues/IssuesSidebar/SuggestionBar/index.tsx b/src/components/Admin/common/IssuesSidebarOverlay/IssuesSidebar/SuggestionBar/index.tsx
similarity index 98%
rename from src/components/Admin/Reports/CodeIssues/IssuesSidebar/SuggestionBar/index.tsx
rename to src/components/Admin/common/IssuesSidebarOverlay/IssuesSidebar/SuggestionBar/index.tsx
index 562a1e606..1b79ced41 100644
--- a/src/components/Admin/Reports/CodeIssues/IssuesSidebar/SuggestionBar/index.tsx
+++ b/src/components/Admin/common/IssuesSidebarOverlay/IssuesSidebar/SuggestionBar/index.tsx
@@ -1,5 +1,6 @@
import { useEffect, useRef, useState } from "react";
import { useGetIssueRecommendationsQuery } from "../../../../../../redux/services/digma";
+import { RecommendationPriority } from "../../../../../../redux/services/types";
import { intersperse } from "../../../../../../utils/intersperse";
import { CrossIcon } from "../../../../../common/icons/16px/CrossIcon";
import { LightBulbWithScrewIcon } from "../../../../../common/icons/16px/LightBulbWithScrewIcon";
@@ -10,7 +11,6 @@ import { Toggle } from "../../../../../common/v3/Toggle";
import type { ToggleOption } from "../../../../../common/v3/Toggle/types";
import * as s from "./styles";
import type { AssetsViewMode, SuggestionBarProps } from "./types";
-import { RecommendationPriority } from "./types";
const assetsViewModeToggleOptions: ToggleOption[] = [
{ label: "Action items", value: "actionItems" },
diff --git a/src/components/Admin/Reports/CodeIssues/IssuesSidebar/SuggestionBar/styles.ts b/src/components/Admin/common/IssuesSidebarOverlay/IssuesSidebar/SuggestionBar/styles.ts
similarity index 100%
rename from src/components/Admin/Reports/CodeIssues/IssuesSidebar/SuggestionBar/styles.ts
rename to src/components/Admin/common/IssuesSidebarOverlay/IssuesSidebar/SuggestionBar/styles.ts
diff --git a/src/components/Admin/Reports/CodeIssues/IssuesSidebar/SuggestionBar/types.ts b/src/components/Admin/common/IssuesSidebarOverlay/IssuesSidebar/SuggestionBar/types.ts
similarity index 60%
rename from src/components/Admin/Reports/CodeIssues/IssuesSidebar/SuggestionBar/types.ts
rename to src/components/Admin/common/IssuesSidebarOverlay/IssuesSidebar/SuggestionBar/types.ts
index 27903b2c9..c5cc51807 100644
--- a/src/components/Admin/Reports/CodeIssues/IssuesSidebar/SuggestionBar/types.ts
+++ b/src/components/Admin/common/IssuesSidebarOverlay/IssuesSidebar/SuggestionBar/types.ts
@@ -4,9 +4,3 @@ export interface SuggestionBarProps {
}
export type AssetsViewMode = "actionItems" | "code";
-
-export enum RecommendationPriority {
- Low = "low",
- Medium = "medium",
- High = "high"
-}
diff --git a/src/components/Admin/Reports/CodeIssues/IssuesSidebar/index.tsx b/src/components/Admin/common/IssuesSidebarOverlay/IssuesSidebar/index.tsx
similarity index 84%
rename from src/components/Admin/Reports/CodeIssues/IssuesSidebar/index.tsx
rename to src/components/Admin/common/IssuesSidebarOverlay/IssuesSidebar/index.tsx
index 2ddb09c89..6107fb2f5 100644
--- a/src/components/Admin/Reports/CodeIssues/IssuesSidebar/index.tsx
+++ b/src/components/Admin/common/IssuesSidebarOverlay/IssuesSidebar/index.tsx
@@ -53,12 +53,12 @@ const getInsightToShowJiraHint = (insights: CodeObjectInsight[]): number => {
export const IssuesSidebar = ({
onClose,
- scope,
- environmentId,
- viewLevel,
isTransitioning,
isResizing,
- onResizeHandleMouseDown
+ onResizeHandleMouseDown,
+ query,
+ scopeDisplayName,
+ isPaginationEnabled = true
}: IssuesSidebarProps) => {
const [infoToOpenJiraTicket, setInfoToOpenJiraTicket] =
useState>();
@@ -78,15 +78,12 @@ export const IssuesSidebar = ({
const theme = useTheme();
const { data, isFetching, refetch } = useGetIssuesQuery(
{
- environment: environmentId,
- scopedSpanCodeObjectId:
- viewLevel === "endpoints" && scope ? scope.value : undefined,
showDismissed: viewMode === ViewMode.OnlyDismissed,
- services: viewLevel === "services" && scope ? [scope.value] : undefined,
sortBy: "criticalinsights",
sortOrder: "desc",
page,
- pageSize: PAGE_SIZE
+ pageSize: PAGE_SIZE,
+ ...query
},
{
refetchOnMountOrArgChange: true
@@ -178,8 +175,8 @@ export const IssuesSidebar = ({
const extendedScope: Scope = {
span: {
- displayName: scope?.displayName ?? scope?.value ?? "",
- spanCodeObjectId: scope?.value ?? "",
+ displayName: scopeDisplayName ?? query?.scopedSpanCodeObjectId ?? "",
+ spanCodeObjectId: query?.scopedSpanCodeObjectId ?? "",
methodId: null,
serviceName: null,
role: null
@@ -205,13 +202,15 @@ export const IssuesSidebar = ({
onClick={handleSidebarCloseButtonClick}
/>
-
+ {scopeDisplayName && (
+
+ )}
@@ -220,28 +219,26 @@ export const IssuesSidebar = ({
{data ? (
data.insights.length > 0 ? (
- {environmentId &&
- data.insights.map((insight, i) => (
-
- ))}
+ {data.insights.map((insight, i) => (
+
+ ))}
) : (
- {totalCount > 0 && (
+ {isPaginationEnabled && totalCount > 0 && (
<>
diff --git a/src/components/Admin/Reports/CodeIssues/IssuesSidebar/styles.ts b/src/components/Admin/common/IssuesSidebarOverlay/IssuesSidebar/styles.ts
similarity index 100%
rename from src/components/Admin/Reports/CodeIssues/IssuesSidebar/styles.ts
rename to src/components/Admin/common/IssuesSidebarOverlay/IssuesSidebar/styles.ts
diff --git a/src/components/Admin/Reports/CodeIssues/IssuesSidebar/types.ts b/src/components/Admin/common/IssuesSidebarOverlay/IssuesSidebar/types.ts
similarity index 63%
rename from src/components/Admin/Reports/CodeIssues/IssuesSidebar/types.ts
rename to src/components/Admin/common/IssuesSidebarOverlay/IssuesSidebar/types.ts
index 23d558eb6..5df782c58 100644
--- a/src/components/Admin/Reports/CodeIssues/IssuesSidebar/types.ts
+++ b/src/components/Admin/common/IssuesSidebarOverlay/IssuesSidebar/types.ts
@@ -1,17 +1,14 @@
import type { MouseEvent } from "react";
-import type { IssuesReportViewLevel } from "../../../../../redux/slices/issuesReportSlice";
+import type { GetIssuesPayload } from "../../../../../redux/services/types";
export interface IssuesSidebarProps {
onClose: () => void;
- environmentId?: string;
- scope?: {
- value: string;
- displayName?: string;
- };
- viewLevel: IssuesReportViewLevel;
+ scopeDisplayName?: string;
isTransitioning: boolean;
isResizing?: boolean;
onResizeHandleMouseDown: (e: MouseEvent) => void;
+ query?: GetIssuesPayload;
+ isPaginationEnabled?: boolean;
}
export interface DrawerContainerProps {
diff --git a/src/components/Admin/common/IssuesSidebarOverlay/index.tsx b/src/components/Admin/common/IssuesSidebarOverlay/index.tsx
new file mode 100644
index 000000000..8e4267680
--- /dev/null
+++ b/src/components/Admin/common/IssuesSidebarOverlay/index.tsx
@@ -0,0 +1,155 @@
+import { useEffect, useRef, useState } from "react";
+import { CSSTransition } from "react-transition-group";
+import { isUndefined } from "../../../../typeGuards/isUndefined";
+import { IssuesSidebar } from "./IssuesSidebar";
+import * as s from "./styles";
+import type { IssuesSidebarOverlayProps } from "./types";
+
+export const MIN_SIDEBAR_WIDTH = 382; // in pixels
+export const MAX_SIDEBAR_WIDTH = 640; // in pixels
+export const DEFAULT_SIDEBAR_WIDTH_RATIO = 0.33;
+
+export const getDefaultSidebarWidth = (windowWidth: number) => {
+ const defaultWidth = windowWidth * DEFAULT_SIDEBAR_WIDTH_RATIO;
+ if (defaultWidth > MAX_SIDEBAR_WIDTH) {
+ return MAX_SIDEBAR_WIDTH;
+ }
+
+ if (defaultWidth < MIN_SIDEBAR_WIDTH) {
+ return MIN_SIDEBAR_WIDTH;
+ }
+
+ return defaultWidth;
+};
+
+export const IssuesSidebarOverlay = ({
+ isSidebarOpen,
+ onSidebarClose,
+ issuesSidebarQuery,
+ scopeDisplayName
+}: IssuesSidebarOverlayProps) => {
+ const sidebarContainerRef = useRef(null);
+ const overlayRef = useRef(null);
+ const [isIssuesSidebarTransitioning, setIsIssuesSidebarTransitioning] =
+ useState(false);
+ const [windowWidth, setWindowWidth] = useState(window.innerWidth);
+ const defaultSidebarWidth = getDefaultSidebarWidth(windowWidth);
+ const [isResizeHandlePressed, setIsResizeHandlePressed] = useState(false);
+ const [startX, setStartX] = useState(0);
+ const [left, setLeft] = useState(windowWidth - defaultSidebarWidth);
+ const [startLeft, setStartLeft] = useState(0);
+ const isPaginationEnabled = isUndefined(issuesSidebarQuery?.limit);
+
+ const handleIssuesSidebarClose = () => {
+ onSidebarClose();
+ };
+
+ const handleIssuesSidebarTransitionStart = () => {
+ setIsIssuesSidebarTransitioning(true);
+ };
+
+ const handleIssuesSidebarTransitionEnd = () => {
+ setIsIssuesSidebarTransitioning(false);
+ };
+
+ const handleResizeHandleMouseDown = (e: React.MouseEvent) => {
+ setIsResizeHandlePressed(true);
+ setStartX(e.clientX);
+ setStartLeft(left);
+ };
+
+ const handleWindowResize = () => {
+ setWindowWidth(window.innerWidth);
+ };
+
+ useEffect(() => {
+ window.addEventListener("resize", handleWindowResize);
+
+ return () => {
+ window.removeEventListener("resize", handleWindowResize);
+ };
+ }, []);
+
+ useEffect(() => {
+ const newLeft = windowWidth - getDefaultSidebarWidth(windowWidth);
+ setLeft(newLeft);
+ }, [windowWidth]);
+
+ useEffect(() => {
+ if (!isResizeHandlePressed) {
+ return;
+ }
+
+ const handleMouseMove = (e: MouseEvent) => {
+ const newLeft = startLeft + (e.clientX - startX);
+ if (
+ newLeft >= windowWidth - MAX_SIDEBAR_WIDTH &&
+ newLeft <= windowWidth - MIN_SIDEBAR_WIDTH
+ ) {
+ setLeft(newLeft);
+ }
+ };
+
+ const handleMouseUp = () => {
+ setIsResizeHandlePressed(false);
+ };
+
+ document.addEventListener("mousemove", handleMouseMove);
+ document.addEventListener("mouseup", handleMouseUp);
+
+ return () => {
+ document.removeEventListener("mousemove", handleMouseMove);
+ document.removeEventListener("mouseup", handleMouseUp);
+ };
+ }, [isResizeHandlePressed, windowWidth, startX, startLeft, left]);
+
+ return (
+ <>
+
+
+
+
+
+
+
+
+ >
+ );
+};
diff --git a/src/components/Admin/common/IssuesSidebarOverlay/styles.ts b/src/components/Admin/common/IssuesSidebarOverlay/styles.ts
new file mode 100644
index 000000000..913c0b2bd
--- /dev/null
+++ b/src/components/Admin/common/IssuesSidebarOverlay/styles.ts
@@ -0,0 +1,60 @@
+import styled from "styled-components";
+import type { IssuesSidebarContainerProps, OverlayProps } from "./types";
+
+export const TRANSITION_DURATION = 300;
+export const overlayTransitionClassName = "overlay";
+export const sidebarContainerTransitionClassName = "sidebarContainer";
+
+export const Overlay = styled.div`
+ position: fixed;
+ inset: 0;
+ background: #000;
+ opacity: ${({ $isVisible }) => ($isVisible ? 0.6 : 0)};
+
+ ${({ $transitionClassName, $transitionDuration }) => `
+ &.${$transitionClassName}-enter {
+ opacity: 0;
+ }
+
+ &.${$transitionClassName}-enter-active {
+ opacity: 0.6;
+ transition: opacity ${$transitionDuration}ms ease-out;
+ }
+
+ &.${$transitionClassName}-exit {
+ opacity: 0.6;
+ }
+
+ &.${$transitionClassName}-exit-active {
+ opacity: 0;
+ transition: opacity ${$transitionDuration}ms ease-out;
+ }
+ `}
+`;
+
+export const IssuesSidebarContainer = styled.div`
+ position: absolute;
+ right: 0;
+ top: 0;
+ bottom: 0;
+
+ ${({ $transitionClassName, $transitionDuration }) => `
+ &.${$transitionClassName}-enter {
+ transform: translateX(100%);
+ }
+
+ &.${$transitionClassName}-enter-active {
+ transform: translateX(0);
+ transition: transform ${$transitionDuration}ms ease-out;
+ }
+
+ &.${$transitionClassName}-exit {
+ transform: translateX(0);
+ }
+
+ &.${$transitionClassName}-exit-active {
+ transform: translateX(100%);
+ transition: transform ${$transitionDuration}ms ease-out;
+ }
+ `}
+`;
diff --git a/src/components/Admin/common/IssuesSidebarOverlay/types.ts b/src/components/Admin/common/IssuesSidebarOverlay/types.ts
new file mode 100644
index 000000000..0fdeaef49
--- /dev/null
+++ b/src/components/Admin/common/IssuesSidebarOverlay/types.ts
@@ -0,0 +1,24 @@
+import type { GetIssuesPayload } from "../../../../redux/services/types";
+
+export interface IssuesSidebarQuery {
+ query?: GetIssuesPayload;
+ limit?: number;
+}
+
+export interface IssuesSidebarOverlayProps {
+ isSidebarOpen: boolean;
+ onSidebarClose: () => void;
+ issuesSidebarQuery?: IssuesSidebarQuery;
+ scopeDisplayName?: string;
+}
+
+export interface OverlayProps {
+ $transitionDuration: number;
+ $transitionClassName: string;
+ $isVisible: boolean;
+}
+
+export interface IssuesSidebarContainerProps {
+ $transitionDuration: number;
+ $transitionClassName: string;
+}
diff --git a/src/components/Insights/InsightsCatalog/InsightsPage/InsightCardRenderer/index.tsx b/src/components/Insights/InsightsCatalog/InsightsPage/InsightCardRenderer/index.tsx
index 15febd7bc..798af1b76 100644
--- a/src/components/Insights/InsightsCatalog/InsightsPage/InsightCardRenderer/index.tsx
+++ b/src/components/Insights/InsightsCatalog/InsightsPage/InsightCardRenderer/index.tsx
@@ -64,7 +64,6 @@ export const InsightCardRenderer = ({
onRefresh,
isMarkAsReadButtonEnabled,
viewMode,
- environmentId,
onDismissalChange,
onOpenSuggestion,
tooltipBoundaryRef
@@ -75,7 +74,8 @@ export const InsightCardRenderer = ({
const handleHistogramButtonClick = (
spanCodeObjectId: string,
insightType: InsightType,
- displayName: string
+ displayName: string,
+ environmentId: string
) => {
if (platform === "Web") {
switch (insightType) {
diff --git a/src/components/Insights/InsightsCatalog/InsightsPage/InsightCardRenderer/insightCards/SpanDurationsInsightCard/types.ts b/src/components/Insights/InsightsCatalog/InsightsPage/InsightCardRenderer/insightCards/SpanDurationsInsightCard/types.ts
index 329faf0a1..4e0ae1a54 100644
--- a/src/components/Insights/InsightsCatalog/InsightsPage/InsightCardRenderer/insightCards/SpanDurationsInsightCard/types.ts
+++ b/src/components/Insights/InsightsCatalog/InsightsPage/InsightCardRenderer/insightCards/SpanDurationsInsightCard/types.ts
@@ -6,7 +6,8 @@ export interface SpanDurationsInsightCardProps extends InsightCardCommonProps {
onHistogramButtonClick: (
spanCodeObjectId: string,
insightType: InsightType,
- displayName: string
+ displayName: string,
+ environmentId: string
) => void;
onLiveButtonClick: (codeObjectId: string) => void;
}
diff --git a/src/components/Insights/InsightsCatalog/InsightsPage/InsightCardRenderer/insightCards/SpanScalingInsightCard/types.ts b/src/components/Insights/InsightsCatalog/InsightsPage/InsightCardRenderer/insightCards/SpanScalingInsightCard/types.ts
index ab4d6df50..0799c8dc7 100644
--- a/src/components/Insights/InsightsCatalog/InsightsPage/InsightCardRenderer/insightCards/SpanScalingInsightCard/types.ts
+++ b/src/components/Insights/InsightsCatalog/InsightsPage/InsightCardRenderer/insightCards/SpanScalingInsightCard/types.ts
@@ -19,6 +19,7 @@ export interface SpanScalingInsightCardProps extends InsightCardCommonProps {
onHistogramButtonClick: (
spanCodeObjectId: string,
insightType: InsightType,
- displayName: string
+ displayName: string,
+ environmentId: string
) => void;
}
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 17c962d39..9f9ed919f 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
@@ -133,7 +133,8 @@ export const InsightCard = ({
onOpenHistogram(
insight.spanInfo.spanCodeObjectId,
insight.type,
- insight.spanInfo.displayName
+ insight.spanInfo.displayName,
+ insight.environment
);
}
};
diff --git a/src/components/Insights/InsightsCatalog/InsightsPage/InsightCardRenderer/insightCards/common/InsightCard/types.ts b/src/components/Insights/InsightsCatalog/InsightsPage/InsightCardRenderer/insightCards/common/InsightCard/types.ts
index b6468872a..42f2c4cd3 100644
--- a/src/components/Insights/InsightsCatalog/InsightsPage/InsightCardRenderer/insightCards/common/InsightCard/types.ts
+++ b/src/components/Insights/InsightsCatalog/InsightsPage/InsightCardRenderer/insightCards/common/InsightCard/types.ts
@@ -16,7 +16,8 @@ export interface InsightCardProps {
onOpenHistogram?: (
spanCodeObjectId: string,
insightType: InsightType,
- displayName: string
+ displayName: string,
+ environmentId: string
) => void;
onRecalculate: (insightId: string) => void;
onRefresh: (insightType: InsightType, spanCodeObjectId?: string) => void;
diff --git a/src/components/Insights/InsightsCatalog/InsightsPage/InsightCardRenderer/types.ts b/src/components/Insights/InsightsCatalog/InsightsPage/InsightCardRenderer/types.ts
index f3478c1ac..74a2cac56 100644
--- a/src/components/Insights/InsightsCatalog/InsightsPage/InsightCardRenderer/types.ts
+++ b/src/components/Insights/InsightsCatalog/InsightsPage/InsightCardRenderer/types.ts
@@ -13,7 +13,6 @@ export interface InsightCardRendererProps {
onRefresh: () => void;
isMarkAsReadButtonEnabled: boolean;
viewMode: InsightCardViewMode;
- environmentId: string;
onDismissalChange: (action: string, insightId: string) => void;
onOpenSuggestion?: (insightId: string) => void;
tooltipBoundaryRef?: RefObject;
diff --git a/src/components/Insights/InsightsCatalog/InsightsPage/index.tsx b/src/components/Insights/InsightsCatalog/InsightsPage/index.tsx
index 8b8cb50db..21430fbbd 100644
--- a/src/components/Insights/InsightsCatalog/InsightsPage/index.tsx
+++ b/src/components/Insights/InsightsCatalog/InsightsPage/index.tsx
@@ -227,7 +227,6 @@ export const InsightsPage = ({
onRefresh={onRefresh}
isMarkAsReadButtonEnabled={isMarkAsReadButtonEnabled}
viewMode={isAtSpan ? "full" : "compact"}
- environmentId={environment.id}
onDismissalChange={handleDismissalChange}
tooltipBoundaryRef={listRef}
/>
diff --git a/src/components/Insights/InsightsCatalog/index.tsx b/src/components/Insights/InsightsCatalog/index.tsx
index 95a79a326..af37141b8 100644
--- a/src/components/Insights/InsightsCatalog/index.tsx
+++ b/src/components/Insights/InsightsCatalog/index.tsx
@@ -225,28 +225,26 @@ export const InsightsCatalog = ({
onChange={handleSearchInputChange}
value={searchInputValue}
/>
- {
- setSorting(val);
- }}
- options={[
- ...(isIssuesView
- ? [
- {
- value: SORTING_CRITERION.CRITICAL_INSIGHTS,
- label: "Critical issues",
- defaultOrder: SORTING_ORDER.DESC
- }
- ]
- : []),
- {
- value: SORTING_CRITERION.LATEST,
- label: "Latest",
- defaultOrder: SORTING_ORDER.DESC
- }
- ]}
- defaultSorting={sorting}
- />
+ {isIssuesView && (
+ {
+ setSorting(val);
+ }}
+ options={[
+ {
+ value: SORTING_CRITERION.CRITICAL_INSIGHTS,
+ label: "Critical issues",
+ defaultOrder: SORTING_ORDER.DESC
+ },
+ {
+ value: SORTING_CRITERION.LATEST,
+ label: "Latest",
+ defaultOrder: SORTING_ORDER.DESC
+ }
+ ]}
+ defaultSorting={sorting}
+ />
+ )}
>
)}
diff --git a/src/components/common/App/typographies.ts b/src/components/common/App/typographies.ts
index 7de8878ae..7cbfcb7dd 100644
--- a/src/components/common/App/typographies.ts
+++ b/src/components/common/App/typographies.ts
@@ -220,6 +220,11 @@ export const subheading1RegularTypography = css`
font-weight: ${typographies.subheading1.fontWeight.regular};
`;
+export const subheading1MediumTypography = css`
+ font-size: ${typographies.subheading1.fontSize}px;
+ font-weight: ${typographies.subheading1.fontWeight.medium};
+`;
+
export const subheading1SemiboldTypography = css`
font-size: ${typographies.subheading1.fontSize}px;
font-weight: ${typographies.subheading1.fontWeight.semibold};
diff --git a/src/containers/Admin/store.ts b/src/containers/Admin/store.ts
index 8227e5ab8..422899208 100644
--- a/src/containers/Admin/store.ts
+++ b/src/containers/Admin/store.ts
@@ -9,6 +9,7 @@ import { appSlice } from "../../redux/slices/appSlice";
import { authSlice } from "../../redux/slices/authSlice";
import issuesReportSlice from "../../redux/slices/issuesReportSlice";
import { persistSlice } from "../../redux/slices/persistSlice";
+import { scopeSlice } from "../../redux/slices/scopeSlice";
import { getRememberEnhancer } from "../../redux/utils/getRememberEnhancer";
import { APP_ID } from "./constants";
@@ -19,6 +20,7 @@ const persistPrefix = `${PERSIST_PREFIX}${APP_ID}-`;
const reducer = rememberReducer({
app: appSlice.reducer,
+ scope: scopeSlice.reducer,
auth: authSlice.reducer,
codeIssuesReport: issuesReportSlice,
persist: persistSlice.reducer,
diff --git a/src/featureFlags.ts b/src/featureFlags.ts
index a0a054c15..51224956f 100644
--- a/src/featureFlags.ts
+++ b/src/featureFlags.ts
@@ -29,7 +29,8 @@ export const featureFlagMinBackendVersions: Record = {
[FeatureFlag.IS_GLOBAL_ERROR_PIN_ENABLED]: "0.3.147",
[FeatureFlag.IS_GLOBAL_ERROR_DISMISS_ENABLED]: "0.3.148",
[FeatureFlag.IS_GLOBAL_ERROR_LAST_DAYS_FILTER_ENABLED]: "0.3.149",
- [FeatureFlag.IS_DURATION_BREAKDOWN_PERCENTAGE_OF_CALLS_ENABLED]: "0.3.193"
+ [FeatureFlag.IS_DURATION_BREAKDOWN_PERCENTAGE_OF_CALLS_ENABLED]: "0.3.193",
+ [FeatureFlag.IS_INSIGHT_SEVERITY_SORTING_ENABLED]: "0.3.204"
};
export const getFeatureFlagValue = (
diff --git a/src/redux/services/types.ts b/src/redux/services/types.ts
index e2c4fdb31..424244aaf 100644
--- a/src/redux/services/types.ts
+++ b/src/redux/services/types.ts
@@ -1,4 +1,3 @@
-import type { RecommendationPriority } from "../../components/Admin/Reports/CodeIssues/IssuesSidebar/SuggestionBar/types";
import type {
DeploymentType,
EnvironmentType
@@ -217,6 +216,12 @@ export interface IssueRecommendationSource {
url: string;
}
+export enum RecommendationPriority {
+ Low = "low",
+ Medium = "medium",
+ High = "high"
+}
+
export interface IssueRecommendation {
title: string;
priority: RecommendationPriority;
diff --git a/src/redux/slices/persistSlice.ts b/src/redux/slices/persistSlice.ts
index 741833df3..0ab5d7ed0 100644
--- a/src/redux/slices/persistSlice.ts
+++ b/src/redux/slices/persistSlice.ts
@@ -13,7 +13,7 @@ const initialState: PersistState = {
};
export const persistSlice = createSlice({
- name: "persistSlice",
+ name: "persist",
initialState,
reducers: {
setIsInsightJiraTicketHintShown: (
diff --git a/src/redux/slices/scopeSlice.ts b/src/redux/slices/scopeSlice.ts
new file mode 100644
index 000000000..7a67f197d
--- /dev/null
+++ b/src/redux/slices/scopeSlice.ts
@@ -0,0 +1,32 @@
+import type { PayloadAction } from "@reduxjs/toolkit";
+import { createSlice } from "@reduxjs/toolkit";
+import { globalClear } from "../actions";
+import { STATE_VERSION } from "../constants";
+import type { BaseState } from "./types";
+
+export interface ScopeState extends BaseState {
+ environmentId: string | null;
+}
+
+const initialState: ScopeState = {
+ version: STATE_VERSION,
+ environmentId: null
+};
+
+export const scopeSlice = createSlice({
+ name: "scope",
+ initialState,
+ reducers: {
+ setEnvironmentId: (state, action: PayloadAction) => {
+ state.environmentId = action.payload;
+ },
+ clear: () => initialState
+ },
+ extraReducers: (builder) => {
+ builder.addCase(globalClear, () => initialState);
+ }
+});
+
+export const { setEnvironmentId } = scopeSlice.actions;
+
+export default scopeSlice.reducer;
diff --git a/src/types.ts b/src/types.ts
index c098e8c86..db7696fc2 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -29,7 +29,8 @@ export enum FeatureFlag {
IS_GLOBAL_ERROR_PIN_ENABLED,
IS_GLOBAL_ERROR_DISMISS_ENABLED,
IS_GLOBAL_ERROR_LAST_DAYS_FILTER_ENABLED,
- IS_DURATION_BREAKDOWN_PERCENTAGE_OF_CALLS_ENABLED
+ IS_DURATION_BREAKDOWN_PERCENTAGE_OF_CALLS_ENABLED,
+ IS_INSIGHT_SEVERITY_SORTING_ENABLED
}
export enum InsightType {