diff --git a/src/components/Assets/AssetsViewScopeConfiguration/AssetsViewScopeConfiguration.stories.tsx b/src/components/Assets/AssetsViewScopeConfiguration/AssetsViewScopeConfiguration.stories.tsx
index 330a6d595..e750929bc 100644
--- a/src/components/Assets/AssetsViewScopeConfiguration/AssetsViewScopeConfiguration.stories.tsx
+++ b/src/components/Assets/AssetsViewScopeConfiguration/AssetsViewScopeConfiguration.stories.tsx
@@ -31,7 +31,8 @@ export const Default: Story = {
},
hasErrors: false,
issuesInsightsCount: 0,
- analyticsInsightsCount: 0
+ analyticsInsightsCount: 0,
+ unreadInsightsCount: 0
},
assetsCount: 1,
onAssetViewChange: () => {
diff --git a/src/components/Assets/AssetsViewScopeConfiguration/index.tsx b/src/components/Assets/AssetsViewScopeConfiguration/index.tsx
index 85b0634b8..c1853c0a5 100644
--- a/src/components/Assets/AssetsViewScopeConfiguration/index.tsx
+++ b/src/components/Assets/AssetsViewScopeConfiguration/index.tsx
@@ -1,5 +1,6 @@
import { useEffect, useState } from "react";
import { isNumber } from "../../../typeGuards/isNumber";
+import { formatUnit } from "../../../utils/formatUnit";
import { ArrowIcon } from "../../common/icons/12px/ArrowIcon";
import { TreeNodesIcon } from "../../common/icons/12px/TreeNodesIcon";
import { Toggle } from "../../common/v3/Toggle";
@@ -64,7 +65,7 @@ export const AssetsViewScopeConfiguration = ({
{isNumber(assetsCount) ? ` ${assetsCount} ` : " "}
- {assetTypeDescription} asset{assetsCount === 1 ? "" : "s"}
+ {assetTypeDescription} {formatUnit(assetsCount || 0, "asset")}
);
diff --git a/src/components/Insights/DurationBreakdownInsight/index.tsx b/src/components/Insights/DurationBreakdownInsight/index.tsx
index 2aa086425..03b9b30f3 100644
--- a/src/components/Insights/DurationBreakdownInsight/index.tsx
+++ b/src/components/Insights/DurationBreakdownInsight/index.tsx
@@ -40,6 +40,9 @@ const getDurationTitle = (breakdownEntry: SpanDurationBreakdownEntry) => {
return {title};
};
+/**
+ * @deprecated
+ */
export const DurationBreakdownInsight = (
props: DurationBreakdownInsightProps
) => {
diff --git a/src/components/Insights/DurationChange/index.tsx b/src/components/Insights/DurationChange/index.tsx
index 092423acb..0f7f4fbf6 100644
--- a/src/components/Insights/DurationChange/index.tsx
+++ b/src/components/Insights/DurationChange/index.tsx
@@ -99,6 +99,9 @@ const renderArrowIcon = (
);
};
+/**
+ * @deprecated
+ */
export const DurationChange = (props: DurationChangeProps) => {
const theme = useTheme();
diff --git a/src/components/Insights/DurationInsight/index.tsx b/src/components/Insights/DurationInsight/index.tsx
index 3ac1ba9a3..dfa112674 100644
--- a/src/components/Insights/DurationInsight/index.tsx
+++ b/src/components/Insights/DurationInsight/index.tsx
@@ -132,6 +132,9 @@ const calculateBars = (
return newBars;
};
+/**
+ * @deprecated
+ */
export const DurationInsight = (props: DurationInsightProps) => {
// const config = useContext(ConfigContext);
const theme = useTheme();
diff --git a/src/components/Insights/DurationSlowdownSourceInsight/index.tsx b/src/components/Insights/DurationSlowdownSourceInsight/index.tsx
index e9ba2b76e..489e410e1 100644
--- a/src/components/Insights/DurationSlowdownSourceInsight/index.tsx
+++ b/src/components/Insights/DurationSlowdownSourceInsight/index.tsx
@@ -6,6 +6,9 @@ import { DurationSlowdownSource } from "../types";
import * as s from "./styles";
import { DurationSlowdownSourceInsightProps } from "./types";
+/**
+ * @deprecated
+ */
export const DurationSlowdownSourceInsight = (
props: DurationSlowdownSourceInsightProps
) => {
diff --git a/src/components/Insights/EndpointNPlusOneInsight/index.tsx b/src/components/Insights/EndpointNPlusOneInsight/index.tsx
index 4c4d1c8f7..f81140697 100644
--- a/src/components/Insights/EndpointNPlusOneInsight/index.tsx
+++ b/src/components/Insights/EndpointNPlusOneInsight/index.tsx
@@ -21,6 +21,9 @@ import { EndpointNPlusOneInsightProps } from "./types";
const FRACTION_MIN_LIMIT = 0.01;
const PAGE_SIZE = 3;
+/**
+ * @deprecated
+ */
export const EndpointNPlusOneInsight = (
props: EndpointNPlusOneInsightProps
) => {
diff --git a/src/components/Insights/EndpointQueryOptimizationInsight/index.tsx b/src/components/Insights/EndpointQueryOptimizationInsight/index.tsx
index 5861fbf02..796f59a67 100644
--- a/src/components/Insights/EndpointQueryOptimizationInsight/index.tsx
+++ b/src/components/Insights/EndpointQueryOptimizationInsight/index.tsx
@@ -19,6 +19,9 @@ import { EndpointQueryOptimizationInsightProps } from "./types";
const PAGE_SIZE = 3;
+/**
+ * @deprecated
+ */
export const EndpointQueryOptimizationInsight = (
props: EndpointQueryOptimizationInsightProps
) => {
diff --git a/src/components/Insights/ErrorsInsight/index.tsx b/src/components/Insights/ErrorsInsight/index.tsx
index a1399acb4..91f9296f9 100644
--- a/src/components/Insights/ErrorsInsight/index.tsx
+++ b/src/components/Insights/ErrorsInsight/index.tsx
@@ -4,6 +4,9 @@ import { Description, Link } from "../styles";
import * as s from "./styles";
import { ErrorsInsightProps } from "./types";
+/**
+ * @deprecated
+ */
export const ErrorsInsight = (props: ErrorsInsightProps) => {
const handleErrorLinkClick = (errorId: string) => {
props.onErrorSelect(errorId, props.insight.type);
diff --git a/src/components/Insights/ExcessiveAPICallsInsight/index.tsx b/src/components/Insights/ExcessiveAPICallsInsight/index.tsx
index 45e05ddfe..e26d2aa8f 100644
--- a/src/components/Insights/ExcessiveAPICallsInsight/index.tsx
+++ b/src/components/Insights/ExcessiveAPICallsInsight/index.tsx
@@ -14,6 +14,9 @@ import { ExcessiveAPICallsInsightProps } from "./types";
const PAGE_SIZE = 3;
+/**
+ * @deprecated
+ */
export const ExcessiveAPICallsInsight = (
props: ExcessiveAPICallsInsightProps
) => {
diff --git a/src/components/Insights/HighNumberOfQueriesInsight/index.tsx b/src/components/Insights/HighNumberOfQueriesInsight/index.tsx
index 88b8e5b32..112ecb782 100644
--- a/src/components/Insights/HighNumberOfQueriesInsight/index.tsx
+++ b/src/components/Insights/HighNumberOfQueriesInsight/index.tsx
@@ -14,6 +14,9 @@ import { Trace } from "../types";
import * as s from "./styles";
import { HighNumberOfQueriesInsightProps } from "./types";
+/**
+ * @deprecated
+ */
export const HighNumberOfQueriesInsight = (
props: HighNumberOfQueriesInsightProps
) => {
diff --git a/src/components/Insights/InsightCard/index.tsx b/src/components/Insights/InsightCard/index.tsx
index 0e175a20e..44c67eb67 100644
--- a/src/components/Insights/InsightCard/index.tsx
+++ b/src/components/Insights/InsightCard/index.tsx
@@ -31,6 +31,9 @@ const RECALCULATE = "recalculate";
const DEFAULT_PERCENTILE = 0.5;
const IS_NEW_TIME_LIMIT = 1000 * 60 * 10; // in milliseconds
+/**
+ * @deprecated
+ */
export const InsightCard = (props: InsightCardProps) => {
const [isExpanded, setIsExpanded] = useState(false);
const [isKebabMenuOpen, setIsKebabMenuOpen] = useState(false);
diff --git a/src/components/Insights/InsightList/index.tsx b/src/components/Insights/InsightList/index.tsx
index 702d3d382..6f4a20b2d 100644
--- a/src/components/Insights/InsightList/index.tsx
+++ b/src/components/Insights/InsightList/index.tsx
@@ -23,6 +23,7 @@ import { ErrorsInsight } from "../ErrorsInsight";
import { ExcessiveAPICallsInsight } from "../ExcessiveAPICallsInsight";
import { HighNumberOfQueriesInsight } from "../HighNumberOfQueriesInsight";
import { InsightCard } from "../InsightCard";
+import { ViewMode } from "../InsightsCatalog/types";
import { NPlusOneInsight } from "../NPlusOneInsight";
import { NoObservabilityCard } from "../NoObservabilityCard";
import { NoScalingIssueInsight } from "../NoScalingIssueInsight";
@@ -233,8 +234,11 @@ const renderInsightCard = (
spanCodeObjectId: string | undefined,
event?: string
) => void,
- isJiraHintEnabled: boolean
+ isJiraHintEnabled: boolean,
+ viewMode: ViewMode
): JSX.Element | undefined => {
+ const isMarkAsReadButtonEnabled = viewMode === ViewMode.OnlyUnread;
+
const handleErrorSelect = (errorId: string, insightType: InsightType) => {
sendTrackingEvent(globalTrackingEvents.USER_ACTION, {
action: `Follow ${insightType} link`
@@ -362,6 +366,7 @@ const renderInsightCard = (
onRecalculate={handleRecalculate}
onRefresh={handleRefresh}
onGoToSpan={handleGoToSpan}
+ isMarkAsReadButtonEnabled={isMarkAsReadButtonEnabled}
/>
);
}
@@ -374,6 +379,7 @@ const renderInsightCard = (
onRecalculate={handleRecalculate}
onRefresh={handleRefresh}
onGoToSpan={handleGoToSpan}
+ isMarkAsReadButtonEnabled={isMarkAsReadButtonEnabled}
/>
);
}
@@ -387,6 +393,7 @@ const renderInsightCard = (
onRecalculate={handleRecalculate}
onRefresh={handleRefresh}
onGoToSpan={handleGoToSpan}
+ isMarkAsReadButtonEnabled={isMarkAsReadButtonEnabled}
/>
);
}
@@ -401,6 +408,7 @@ const renderInsightCard = (
onJiraTicketCreate={onJiraTicketCreate}
isJiraHintEnabled={isJiraHintEnabled}
onGoToSpan={handleGoToSpan}
+ isMarkAsReadButtonEnabled={isMarkAsReadButtonEnabled}
/>
);
}
@@ -415,6 +423,7 @@ const renderInsightCard = (
onJiraTicketCreate={onJiraTicketCreate}
isJiraHintEnabled={isJiraHintEnabled}
onGoToSpan={handleGoToSpan}
+ isMarkAsReadButtonEnabled={isMarkAsReadButtonEnabled}
/>
);
}
@@ -426,6 +435,7 @@ const renderInsightCard = (
onRecalculate={handleRecalculate}
onRefresh={handleRefresh}
onGoToSpan={handleGoToSpan}
+ isMarkAsReadButtonEnabled={isMarkAsReadButtonEnabled}
/>
);
}
@@ -441,6 +451,7 @@ const renderInsightCard = (
onRecalculate={handleRecalculate}
onRefresh={handleRefresh}
onGoToSpan={handleGoToSpan}
+ isMarkAsReadButtonEnabled={isMarkAsReadButtonEnabled}
/>
);
}
@@ -454,6 +465,7 @@ const renderInsightCard = (
onRecalculate={handleRecalculate}
onRefresh={handleRefresh}
onGoToSpan={handleGoToSpan}
+ isMarkAsReadButtonEnabled={isMarkAsReadButtonEnabled}
/>
);
}
@@ -469,6 +481,7 @@ const renderInsightCard = (
onJiraTicketCreate={onJiraTicketCreate}
isJiraHintEnabled={isJiraHintEnabled}
onGoToSpan={handleGoToSpan}
+ isMarkAsReadButtonEnabled={isMarkAsReadButtonEnabled}
/>
);
}
@@ -484,6 +497,7 @@ const renderInsightCard = (
onJiraTicketCreate={onJiraTicketCreate}
isJiraHintEnabled={isJiraHintEnabled}
onGoToSpan={handleGoToSpan}
+ isMarkAsReadButtonEnabled={isMarkAsReadButtonEnabled}
/>
);
}
@@ -500,6 +514,7 @@ const renderInsightCard = (
onJiraTicketCreate={onJiraTicketCreate}
isJiraHintEnabled={isJiraHintEnabled}
onGoToSpan={handleGoToSpan}
+ isMarkAsReadButtonEnabled={isMarkAsReadButtonEnabled}
/>
);
}
@@ -527,6 +542,7 @@ const renderInsightCard = (
onRecalculate={handleRecalculate}
onRefresh={handleRefresh}
onGoToSpan={handleGoToSpan}
+ isMarkAsReadButtonEnabled={isMarkAsReadButtonEnabled}
/>
);
}
@@ -539,6 +555,7 @@ const renderInsightCard = (
onRecalculate={handleRecalculate}
onRefresh={handleRefresh}
onGoToSpan={handleGoToSpan}
+ isMarkAsReadButtonEnabled={isMarkAsReadButtonEnabled}
/>
);
}
@@ -552,6 +569,7 @@ const renderInsightCard = (
onRecalculate={handleRecalculate}
onRefresh={handleRefresh}
onGoToSpan={handleGoToSpan}
+ isMarkAsReadButtonEnabled={isMarkAsReadButtonEnabled}
/>
);
}
@@ -565,6 +583,7 @@ const renderInsightCard = (
onRecalculate={handleRecalculate}
onRefresh={handleRefresh}
onGoToSpan={handleGoToSpan}
+ isMarkAsReadButtonEnabled={isMarkAsReadButtonEnabled}
/>
);
}
@@ -579,6 +598,7 @@ const renderInsightCard = (
onRecalculate={handleRecalculate}
onRefresh={handleRefresh}
onGoToSpan={handleGoToSpan}
+ isMarkAsReadButtonEnabled={isMarkAsReadButtonEnabled}
/>
);
}
@@ -593,6 +613,7 @@ const renderInsightCard = (
onRecalculate={handleRecalculate}
onRefresh={handleRefresh}
onGoToSpan={handleGoToSpan}
+ isMarkAsReadButtonEnabled={isMarkAsReadButtonEnabled}
/>
);
}
@@ -608,6 +629,7 @@ const renderInsightCard = (
onJiraTicketCreate={onJiraTicketCreate}
isJiraHintEnabled={isJiraHintEnabled}
onGoToSpan={handleGoToSpan}
+ isMarkAsReadButtonEnabled={isMarkAsReadButtonEnabled}
/>
);
}
@@ -620,6 +642,7 @@ const renderInsightCard = (
onRecalculate={handleRecalculate}
onRefresh={handleRefresh}
onGoToSpan={handleGoToSpan}
+ isMarkAsReadButtonEnabled={isMarkAsReadButtonEnabled}
/>
);
}
@@ -636,6 +659,7 @@ const renderInsightCard = (
onJiraTicketCreate={onJiraTicketCreate}
isJiraHintEnabled={isJiraHintEnabled}
onGoToSpan={handleGoToSpan}
+ isMarkAsReadButtonEnabled={isMarkAsReadButtonEnabled}
/>
);
}
@@ -651,6 +675,7 @@ const renderInsightCard = (
onRefresh={handleRefresh}
onJiraTicketCreate={onJiraTicketCreate}
onGoToSpan={handleGoToSpan}
+ isMarkAsReadButtonEnabled={isMarkAsReadButtonEnabled}
/>
);
}
@@ -659,6 +684,9 @@ const renderInsightCard = (
const IS_INSIGHT_JIRA_TICKET_HINT_SHOWN_PERSISTENCE_KEY =
"isInsightJiraTicketHintShown";
+/**
+ * @deprecated
+ */
export const InsightList = (props: InsightListProps) => {
const [insightGroups, setInsightGroups] = useState([]);
const [isAutofixing, setIsAutofixing] = useState(false);
@@ -748,7 +776,8 @@ export const InsightList = (props: InsightListProps) => {
return renderInsightCard(
insight,
handleShowJiraTicket,
- isJiraHintEnabled
+ isJiraHintEnabled,
+ props.viewMode
);
})
) : (
diff --git a/src/components/Insights/InsightList/types.ts b/src/components/Insights/InsightList/types.ts
index 32fb84273..839604249 100644
--- a/src/components/Insights/InsightList/types.ts
+++ b/src/components/Insights/InsightList/types.ts
@@ -1,3 +1,4 @@
+import { ViewMode } from "../InsightsCatalog/types";
import { GenericCodeObjectInsight, MethodSpan } from "../types";
export interface InsightListProps {
@@ -13,6 +14,7 @@ export interface InsightListProps {
insight: GenericCodeObjectInsight,
spanCodeObjectId?: string
) => void;
+ viewMode: ViewMode;
}
export interface isInsightJiraTicketHintShownPayload {
diff --git a/src/components/Insights/InsightsCatalog/InsightsCatalog.stories.tsx b/src/components/Insights/InsightsCatalog/InsightsCatalog.stories.tsx
new file mode 100644
index 000000000..3ea49e5f6
--- /dev/null
+++ b/src/components/Insights/InsightsCatalog/InsightsCatalog.stories.tsx
@@ -0,0 +1,41 @@
+import { Meta, StoryObj } from "@storybook/react";
+import { InsightsCatalog } from ".";
+import { SORTING_ORDER } from "../../common/SortingSelector/types";
+import { mockedSpanBottleneckInsight } from "../common/insights/EndpointBottleneckInsight/mockData";
+import { SORTING_CRITERION } from "./types";
+
+// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction
+const meta: Meta = {
+ title: "Insights/InsightsCatalog",
+ component: InsightsCatalog,
+ 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: {
+ insights: [{ ...mockedSpanBottleneckInsight, isRead: false }],
+ totalCount: 1,
+ dismissedCount: 1,
+ defaultQuery: {
+ page: 0,
+ sorting: {
+ criterion: SORTING_CRITERION.LATEST,
+ order: SORTING_ORDER.DESC
+ },
+ searchQuery: null,
+ showDismissed: false,
+ insightViewType: "Issues",
+ showUnreadOnly: false
+ },
+ isDismissalEnabled: true,
+ unreadCount: 1,
+ isMarkingAsReadEnabled: true
+ }
+};
diff --git a/src/components/Insights/InsightsCatalog/index.tsx b/src/components/Insights/InsightsCatalog/index.tsx
index 32fce607e..116f2e534 100644
--- a/src/components/Insights/InsightsCatalog/index.tsx
+++ b/src/components/Insights/InsightsCatalog/index.tsx
@@ -2,10 +2,14 @@ import { useCallback, useContext, useEffect, useState } from "react";
import { usePrevious } from "../../../hooks/usePrevious";
import { useTheme } from "styled-components";
+import { actions as globalActions } from "../../../actions";
import { useDebounce } from "../../../hooks/useDebounce";
import { isNumber } from "../../../typeGuards/isNumber";
+import { isString } from "../../../typeGuards/isString";
import { isUndefined } from "../../../typeGuards/isUndefined";
+import { formatUnit } from "../../../utils/formatUnit";
import { sendTrackingEvent } from "../../../utils/sendTrackingEvent";
+import { ChangeScopePayload } from "../../Navigation/types";
import { ConfigContext } from "../../common/App/ConfigContext";
import { Pagination } from "../../common/Pagination";
import { SearchInput } from "../../common/SearchInput";
@@ -20,13 +24,10 @@ import { Tooltip } from "../../common/v3/Tooltip";
import { InsightsPage } from "../InsightsPage";
import { trackingEvents } from "../tracking";
import * as s from "./styles";
-import { InsightsCatalogProps, SORTING_CRITERION } from "./types";
+import { InsightsCatalogProps, SORTING_CRITERION, ViewMode } from "./types";
+import { useMarkingAllAsRead } from "./useMarkingAllAsRead";
const PAGE_SIZE = 10;
-enum ViewMode {
- All,
- OnlyDismissed
-}
export const InsightsCatalog = (props: InsightsCatalogProps) => {
const { insights, onJiraTicketCreate, defaultQuery, totalCount } = props;
@@ -50,11 +51,22 @@ export const InsightsCatalog = (props: InsightsCatalogProps) => {
const [mode, setMode] = useState(ViewMode.All);
const previousMode = usePrevious(mode);
const theme = useTheme();
+ const { isMarkingAllAsReadInProgress, markAllAsRead } = useMarkingAllAsRead(
+ config.scope?.span || null
+ );
+ const previousIsMarkingAllAsReadInProgress = usePrevious(
+ isMarkingAllAsReadInProgress
+ );
- const isViewModeButtonVisible =
+ const isDismissalViewModeButtonVisible =
props.isDismissalEnabled &&
(isUndefined(props.dismissedCount) || props.dismissedCount > 0); // isUndefined - check for backward compatibility, always show when BE does not return this counter
+ const isMarkingAsReadToolbarVisible =
+ props.isMarkingAsReadEnabled &&
+ isNumber(props.unreadCount) &&
+ props.unreadCount > 0;
+
const refreshData = useCallback(
() =>
props.onQueryChange({
@@ -62,7 +74,8 @@ export const InsightsCatalog = (props: InsightsCatalogProps) => {
page,
sorting,
searchQuery: debouncedSearchInputValue,
- showDismissed: mode === ViewMode.OnlyDismissed
+ showDismissed: mode === ViewMode.OnlyDismissed,
+ showUnreadOnly: mode === ViewMode.OnlyUnread
}),
[
page,
@@ -81,12 +94,48 @@ export const InsightsCatalog = (props: InsightsCatalogProps) => {
refreshData();
};
- const handleViewModeChange = () => {
+
+ const handleDismissalViewModeButtonClick = () => {
const newMode =
mode === ViewMode.All ? ViewMode.OnlyDismissed : ViewMode.All;
setMode(newMode);
};
+ const handleUnreadOnlyLinkClick = () => {
+ setMode(ViewMode.OnlyUnread);
+ };
+
+ const handleReadAllLinkClick = () => {
+ markAllAsRead();
+ };
+
+ const handleBackToAllInsightsButtonClick = () => {
+ setMode(ViewMode.All);
+ };
+
+ useEffect(() => {
+ if (previousIsMarkingAllAsReadInProgress && !isMarkingAllAsReadInProgress) {
+ refreshData();
+
+ // Trigger SET_SCOPE response message from the plugin with actual unread insights count
+ window.sendMessageToDigma({
+ action: globalActions.CHANGE_SCOPE,
+ payload: {
+ span: config.scope?.span
+ ? {
+ spanCodeObjectId: config.scope.span.spanCodeObjectId
+ }
+ : null
+ }
+ });
+ }
+ }, [
+ isMarkingAllAsReadInProgress,
+ previousIsMarkingAllAsReadInProgress,
+ refreshData,
+ config.scope
+ ]);
+
useEffect(() => {
if (!previousScope || previousScope !== config.scope?.span) {
setSearchInputValue("");
@@ -108,11 +157,16 @@ export const InsightsCatalog = (props: InsightsCatalogProps) => {
refreshData();
}, []);
+ useEffect(() => {
+ setPage(0);
+ }, [mode]);
+
useEffect(() => {
if (
(isNumber(previousPage) && previousPage !== page) ||
(previousSorting && previousSorting !== sorting) ||
- previousSearchQuery !== debouncedSearchInputValue ||
+ (isString(previousSearchQuery) &&
+ previousSearchQuery !== debouncedSearchInputValue) ||
previousMode !== mode
) {
refreshData();
@@ -132,59 +186,96 @@ export const InsightsCatalog = (props: InsightsCatalogProps) => {
return (
<>
- {
- setSearchInputValue(val);
- }}
- value={searchInputValue}
- />
- {
- setSorting(val);
- }}
- options={[
- ...(defaultQuery.insightViewType === "Issues"
- ? [
- {
- value: SORTING_CRITERION.CRITICAL_INSIGHTS,
- label: "Critical issues",
- defaultOrder: SORTING_ORDER.DESC
- }
- ]
- : []),
- {
- value: SORTING_CRITERION.LATEST,
- label: "Latest",
- defaultOrder: SORTING_ORDER.DESC
- }
- ]}
- default={defaultQuery.sorting}
- />
-
-
+ {
+ setSearchInputValue(val);
+ }}
+ value={searchInputValue}
/>
-
-
- {mode === ViewMode.OnlyDismissed && (
-
-
- )}
+
+
+
+
+ {mode === ViewMode.All ? (
+ isMarkingAsReadToolbarVisible && (
+
+
+ {props.unreadCount}
+ unread {formatUnit(props.unreadCount || 0, "issue")}
+
+
+
+ Unread only
+
+ /
+
+ Read all
+
+
+
+ )
+ ) : (
+
+
+
+
+
+ Back to All Issues
+
+ {mode === ViewMode.OnlyDismissed && (
+
+ {props.dismissedCount}
+ dismissed {formatUnit(props.dismissedCount || 0, "issue")}
+
+ )}
+ {mode === ViewMode.OnlyUnread && isMarkingAsReadToolbarVisible && (
+
+ Read all
+
+ )}
+
+ )}
+
{
}
onJiraTicketCreate={onJiraTicketCreate}
onRefresh={props.onRefresh}
+ viewMode={mode}
/>
{totalCount > 0 && (
@@ -213,7 +305,7 @@ export const InsightsCatalog = (props: InsightsCatalogProps) => {
>
)}
- {isViewModeButtonVisible && (
+ {isDismissalViewModeButtonVisible && (
diff --git a/src/components/Insights/InsightsCatalog/styles.ts b/src/components/Insights/InsightsCatalog/styles.ts
index 2bc520ee9..648b94575 100644
--- a/src/components/Insights/InsightsCatalog/styles.ts
+++ b/src/components/Insights/InsightsCatalog/styles.ts
@@ -4,6 +4,7 @@ import {
subscriptRegularTypography
} from "../../common/App/typographies";
import { Button } from "../../common/v3/Button";
+import { Link } from "../../common/v3/Link";
export const Footer = styled.div`
display: flex;
@@ -40,21 +41,66 @@ export const FooterPageItemsCount = styled.span`
export const Toolbar = styled.div`
display: flex;
- justify-content: space-between;
+ flex-direction: column;
padding: 8px;
gap: 8px;
`;
-export const DismissedDescription = styled.div`
+export const ToolbarRow = styled.div`
+ display: flex;
+ gap: 8px;
+ justify-content: space-between;
+ align-items: center;
+`;
+
+export const ViewModeToolbarRow = styled(ToolbarRow)`
+ padding: 4px 0;
+`;
+
+export const BackToAllInsightsButtonIconContainer = styled.div`
+ display: flex;
+ color: ${({ theme }) => theme.colors.v3.icon.disabled};
+`;
+
+export const BackToAllInsightsButton = styled.button`
+ ${subscriptRegularTypography}
+
+ font-family: inherit;
+ padding: 0;
+ margin: 0;
+ background: none;
+ border: none;
+ display: flex;
+ gap: 4px;
+ align-items: center;
+ color: ${({ theme }) => theme.colors.v3.text.primary};
+`;
+
+export const MarkingAsReadToolbarActionsContainer = styled.div`
+ ${subscriptRegularTypography}
+
+ display: flex;
+ gap: 8px;
+ color: ${({ theme }) => theme.colors.v3.text.tertiary};
+ align-items: center;
+`;
+
+export const MarkingAsReadToolbarActionLink = styled(Link)`
+ ${subscriptRegularTypography}
+
+ text-decoration: underline;
+`;
+
+export const InsightCountDescription = styled.div`
${subscriptRegularTypography}
display: flex;
align-items: center;
- color: ${({ theme }) => theme.colors.v3.text.disabled};
+ color: ${({ theme }) => theme.colors.v3.text.tertiary};
+ gap: 5px;
`;
-export const DismissedCount = styled.span`
+export const InsightCount = styled.span`
color: ${({ theme }) => theme.colors.v3.text.primary};
- padding-right: 4px;
`;
export const InsightsViewModeToolbar = styled(Toolbar)`
diff --git a/src/components/Insights/InsightsCatalog/types.ts b/src/components/Insights/InsightsCatalog/types.ts
index 69cb5a607..22049621e 100644
--- a/src/components/Insights/InsightsCatalog/types.ts
+++ b/src/components/Insights/InsightsCatalog/types.ts
@@ -12,6 +12,14 @@ export interface InsightsCatalogProps {
defaultQuery: InsightsQuery;
onRefresh: () => void;
isDismissalEnabled: boolean;
+ unreadCount?: number;
+ isMarkingAsReadEnabled: boolean;
+}
+
+export enum ViewMode {
+ All,
+ OnlyDismissed,
+ OnlyUnread
}
export enum SORTING_CRITERION {
diff --git a/src/components/Insights/InsightsCatalog/useMarkingAllAsRead.ts b/src/components/Insights/InsightsCatalog/useMarkingAllAsRead.ts
new file mode 100644
index 000000000..07b5fc78f
--- /dev/null
+++ b/src/components/Insights/InsightsCatalog/useMarkingAllAsRead.ts
@@ -0,0 +1,43 @@
+import { useEffect, useState } from "react";
+import { dispatcher } from "../../../dispatcher";
+import { ScopeSpan } from "../../common/App/types";
+import { actions } from "../actions";
+import { MarkAllAsReadPayload } from "../common/InsightCard/types";
+
+export const useMarkingAllAsRead = (scope: ScopeSpan | null) => {
+ const [isMarkingAllAsReadInProgress, setIsMarkingAllAsReadInProgress] =
+ useState(false);
+
+ const markAllAsRead = () => {
+ window.sendMessageToDigma({
+ action: actions.MARK_ALL_AS_READ,
+ payload: {
+ scope
+ }
+ });
+ setIsMarkingAllAsReadInProgress(true);
+ };
+
+ useEffect(() => {
+ const handleMarkAsReadResponse = () => {
+ setIsMarkingAllAsReadInProgress(false);
+ };
+
+ dispatcher.addActionListener(
+ actions.SET_MARK_ALL_AS_READ_RESPONSE,
+ handleMarkAsReadResponse
+ );
+
+ return () => {
+ dispatcher.removeActionListener(
+ actions.SET_MARK_ALL_AS_READ_RESPONSE,
+ handleMarkAsReadResponse
+ );
+ };
+ }, [scope]);
+
+ return {
+ isMarkingAllAsReadInProgress,
+ markAllAsRead
+ };
+};
diff --git a/src/components/Insights/InsightsPage/InsightsPage.stories.tsx b/src/components/Insights/InsightsPage/InsightsPage.stories.tsx
index edf21e947..103c4ad77 100644
--- a/src/components/Insights/InsightsPage/InsightsPage.stories.tsx
+++ b/src/components/Insights/InsightsPage/InsightsPage.stories.tsx
@@ -2,6 +2,7 @@ import { Meta, StoryObj } from "@storybook/react";
import { InsightsPage } from ".";
import { ConfigContext, initialState } from "../../common/App/ConfigContext";
import { Scope } from "../../common/App/types";
+import { ViewMode } from "../InsightsCatalog/types";
import { mockedSpanBottleneckInsight } from "../common/insights/EndpointBottleneckInsight/mockData";
import { InsightsPageProps } from "./types";
@@ -27,7 +28,8 @@ const scope: Scope = {
},
hasErrors: false,
issuesInsightsCount: 0,
- analyticsInsightsCount: 0
+ analyticsInsightsCount: 0,
+ unreadInsightsCount: 0
};
const props: InsightsPageProps = {
@@ -39,7 +41,8 @@ const props: InsightsPageProps = {
onRefresh: () => {
return undefined;
},
- page: 0
+ page: 0,
+ viewMode: ViewMode.All
};
export const WithInsights: Story = {
diff --git a/src/components/Insights/InsightsPage/index.tsx b/src/components/Insights/InsightsPage/index.tsx
index 3d1fc1372..0f5d4ed95 100644
--- a/src/components/Insights/InsightsPage/index.tsx
+++ b/src/components/Insights/InsightsPage/index.tsx
@@ -11,6 +11,7 @@ import { ChangeScopePayload, ChangeViewPayload } from "../../Navigation/types";
import { ConfigContext } from "../../common/App/ConfigContext";
import { EmptyState } from "../../common/EmptyState";
import { CardsIcon } from "../../common/icons/CardsIcon";
+import { ViewMode } from "../InsightsCatalog/types";
import { actions } from "../actions";
import { DurationBreakdownInsight } from "../common/insights/DurationBreakdownInsight";
import { DurationInsight } from "../common/insights/DurationInsight";
@@ -91,8 +92,10 @@ const renderInsightCard = (
event?: string
) => void,
isJiraHintEnabled: boolean,
- onRefresh: () => void
+ onRefresh: () => void,
+ viewMode: ViewMode
): JSX.Element | undefined => {
+ const isMarkAsReadButtonEnabled = viewMode === ViewMode.OnlyUnread;
// const handleErrorSelect = (errorId: string, insightType: InsightType) => {
// sendTrackingEvent(globalTrackingEvents.USER_ACTION, {
// action: `Follow ${insightType} link`
@@ -211,6 +214,7 @@ const renderInsightCard = (
onRecalculate={handleRecalculate}
onRefresh={onRefresh}
onGoToSpan={handleGoToSpan}
+ isMarkAsReadButtonEnabled={isMarkAsReadButtonEnabled}
/>
);
}
@@ -224,6 +228,7 @@ const renderInsightCard = (
onRecalculate={handleRecalculate}
onRefresh={onRefresh}
onGoToSpan={handleGoToSpan}
+ isMarkAsReadButtonEnabled={isMarkAsReadButtonEnabled}
/>
);
}
@@ -238,6 +243,7 @@ const renderInsightCard = (
onRecalculate={handleRecalculate}
onRefresh={onRefresh}
onGoToSpan={handleGoToSpan}
+ isMarkAsReadButtonEnabled={isMarkAsReadButtonEnabled}
/>
);
}
@@ -254,6 +260,7 @@ const renderInsightCard = (
isJiraHintEnabled={isJiraHintEnabled}
onGoToSpan={handleGoToSpan}
onTraceButtonClick={handleTraceButtonClick}
+ isMarkAsReadButtonEnabled={isMarkAsReadButtonEnabled}
/>
);
}
@@ -270,6 +277,7 @@ const renderInsightCard = (
isJiraHintEnabled={isJiraHintEnabled}
onGoToSpan={handleGoToSpan}
onTraceButtonClick={handleTraceButtonClick}
+ isMarkAsReadButtonEnabled={isMarkAsReadButtonEnabled}
/>
);
}
@@ -282,6 +290,7 @@ const renderInsightCard = (
onRecalculate={handleRecalculate}
onRefresh={onRefresh}
onGoToSpan={handleGoToSpan}
+ isMarkAsReadButtonEnabled={isMarkAsReadButtonEnabled}
/>
);
}
@@ -298,6 +307,7 @@ const renderInsightCard = (
onRecalculate={handleRecalculate}
onRefresh={onRefresh}
onGoToSpan={handleGoToSpan}
+ isMarkAsReadButtonEnabled={isMarkAsReadButtonEnabled}
/>
);
}
@@ -314,6 +324,7 @@ const renderInsightCard = (
onJiraTicketCreate={onJiraTicketCreate}
isJiraHintEnabled={isJiraHintEnabled}
onGoToSpan={handleGoToSpan}
+ isMarkAsReadButtonEnabled={isMarkAsReadButtonEnabled}
/>
);
}
@@ -330,6 +341,7 @@ const renderInsightCard = (
onJiraTicketCreate={onJiraTicketCreate}
isJiraHintEnabled={isJiraHintEnabled}
onGoToSpan={handleGoToSpan}
+ isMarkAsReadButtonEnabled={isMarkAsReadButtonEnabled}
/>
);
}
@@ -347,6 +359,7 @@ const renderInsightCard = (
onJiraTicketCreate={onJiraTicketCreate}
isJiraHintEnabled={isJiraHintEnabled}
onGoToSpan={handleGoToSpan}
+ isMarkAsReadButtonEnabled={isMarkAsReadButtonEnabled}
/>
);
}
@@ -360,6 +373,7 @@ const renderInsightCard = (
onRecalculate={handleRecalculate}
onRefresh={onRefresh}
onGoToSpan={handleGoToSpan}
+ isMarkAsReadButtonEnabled={isMarkAsReadButtonEnabled}
/>
);
}
@@ -372,6 +386,7 @@ const renderInsightCard = (
onRecalculate={handleRecalculate}
onRefresh={onRefresh}
onGoToSpan={handleGoToSpan}
+ isMarkAsReadButtonEnabled={isMarkAsReadButtonEnabled}
/>
);
}
@@ -386,6 +401,7 @@ const renderInsightCard = (
onRecalculate={handleRecalculate}
onRefresh={onRefresh}
onGoToSpan={handleGoToSpan}
+ isMarkAsReadButtonEnabled={isMarkAsReadButtonEnabled}
/>
);
}
@@ -401,6 +417,7 @@ const renderInsightCard = (
onRecalculate={handleRecalculate}
onRefresh={onRefresh}
onGoToSpan={handleGoToSpan}
+ isMarkAsReadButtonEnabled={isMarkAsReadButtonEnabled}
/>
);
}
@@ -415,6 +432,7 @@ const renderInsightCard = (
onRecalculate={handleRecalculate}
onRefresh={onRefresh}
onGoToSpan={handleGoToSpan}
+ isMarkAsReadButtonEnabled={isMarkAsReadButtonEnabled}
/>
);
}
@@ -430,6 +448,7 @@ const renderInsightCard = (
onJiraTicketCreate={onJiraTicketCreate}
isJiraHintEnabled={isJiraHintEnabled}
onGoToSpan={handleGoToSpan}
+ isMarkAsReadButtonEnabled={isMarkAsReadButtonEnabled}
/>
);
}
@@ -442,6 +461,7 @@ const renderInsightCard = (
onRecalculate={handleRecalculate}
onRefresh={onRefresh}
onGoToSpan={handleGoToSpan}
+ isMarkAsReadButtonEnabled={isMarkAsReadButtonEnabled}
/>
);
}
@@ -458,6 +478,7 @@ const renderInsightCard = (
onJiraTicketCreate={onJiraTicketCreate}
isJiraHintEnabled={isJiraHintEnabled}
onGoToSpan={handleGoToSpan}
+ isMarkAsReadButtonEnabled={isMarkAsReadButtonEnabled}
/>
);
}
@@ -475,6 +496,7 @@ const renderInsightCard = (
onJiraTicketCreate={onJiraTicketCreate}
isJiraHintEnabled={isJiraHintEnabled}
onGoToSpan={handleGoToSpan}
+ isMarkAsReadButtonEnabled={isMarkAsReadButtonEnabled}
/>
);
}
@@ -491,6 +513,7 @@ const renderInsightCard = (
onJiraTicketCreate={onJiraTicketCreate}
isJiraHintEnabled={isJiraHintEnabled}
onGoToSpan={handleGoToSpan}
+ isMarkAsReadButtonEnabled={isMarkAsReadButtonEnabled}
/>
);
}
@@ -577,7 +600,8 @@ export const InsightsPage = (props: InsightsPageProps) => {
!isUndefined(isInsightJiraTicketHintShown) &&
!isInsightJiraTicketHintShown?.value &&
j === insightIndexWithJiraHint,
- props.onRefresh
+ props.onRefresh,
+ props.viewMode
);
})
) : props.isFilteringEnabled ? (
diff --git a/src/components/Insights/InsightsPage/types.ts b/src/components/Insights/InsightsPage/types.ts
index 2cee144d6..ef468ddb9 100644
--- a/src/components/Insights/InsightsPage/types.ts
+++ b/src/components/Insights/InsightsPage/types.ts
@@ -1,3 +1,4 @@
+import { ViewMode } from "../InsightsCatalog/types";
import { GenericCodeObjectInsight, InsightType } from "../types";
export interface InsightsPageProps {
@@ -9,6 +10,7 @@ export interface InsightsPageProps {
) => void;
onRefresh: () => void;
page: number;
+ viewMode: ViewMode;
}
export interface isInsightJiraTicketHintShownPayload {
diff --git a/src/components/Insights/NPlusOneInsight/index.tsx b/src/components/Insights/NPlusOneInsight/index.tsx
index bb7fa678a..f217f9484 100644
--- a/src/components/Insights/NPlusOneInsight/index.tsx
+++ b/src/components/Insights/NPlusOneInsight/index.tsx
@@ -16,6 +16,9 @@ import { Trace } from "../types";
import * as s from "./styles";
import { NPlusOneInsightProps } from "./types";
+/**
+ * @deprecated
+ */
export const NPlusOneInsight = (props: NPlusOneInsightProps) => {
const config = useContext(ConfigContext);
diff --git a/src/components/Insights/NoScalingIssueInsight/index.tsx b/src/components/Insights/NoScalingIssueInsight/index.tsx
index 3d1a35637..d56f6d726 100644
--- a/src/components/Insights/NoScalingIssueInsight/index.tsx
+++ b/src/components/Insights/NoScalingIssueInsight/index.tsx
@@ -3,6 +3,9 @@ import { ChartIcon } from "../../common/icons/ChartIcon";
import { InsightCard } from "../InsightCard";
import { NoScalingIssueInsightProps } from "./types";
+/**
+ * @deprecated
+ */
export const NoScalingIssueInsight = (props: NoScalingIssueInsightProps) => {
const handleHistogramButtonClick = () => {
props.insight.spanInfo &&
diff --git a/src/components/Insights/PerformanceAtScaleInsight/index.tsx b/src/components/Insights/PerformanceAtScaleInsight/index.tsx
index d6f2493fb..e198a0d11 100644
--- a/src/components/Insights/PerformanceAtScaleInsight/index.tsx
+++ b/src/components/Insights/PerformanceAtScaleInsight/index.tsx
@@ -15,6 +15,9 @@ import * as s from "./styles";
const MIN_CONCURRENCY_STATES_TO_EVALUATE_SCALE = 4;
+/**
+ * @deprecated
+ */
export const PerformanceAtScaleInsight = (
props: PerformanceAtScaleInsightProps
) => {
diff --git a/src/components/Insights/QueryOptimizationInsight/index.tsx b/src/components/Insights/QueryOptimizationInsight/index.tsx
index f2a14505d..540336873 100644
--- a/src/components/Insights/QueryOptimizationInsight/index.tsx
+++ b/src/components/Insights/QueryOptimizationInsight/index.tsx
@@ -14,6 +14,9 @@ import { Trace } from "../types";
import * as s from "./styles";
import { QueryOptimizationInsightProps } from "./types";
+/**
+ * @deprecated
+ */
export const QueryOptimizationInsight = (
props: QueryOptimizationInsightProps
) => {
diff --git a/src/components/Insights/RequestBreakdownInsight/index.tsx b/src/components/Insights/RequestBreakdownInsight/index.tsx
index d1b3ee9a0..a2091d439 100644
--- a/src/components/Insights/RequestBreakdownInsight/index.tsx
+++ b/src/components/Insights/RequestBreakdownInsight/index.tsx
@@ -43,6 +43,9 @@ const sortByType = (a: Component, b: Component) =>
const columnHelper = createColumnHelper();
+/**
+ * @deprecated
+ */
export const RequestBreakdownInsight = (
props: RequestBreakdownInsightProps
) => {
diff --git a/src/components/Insights/ScalingIssueInsight/index.tsx b/src/components/Insights/ScalingIssueInsight/index.tsx
index 78f94f607..7dec380ea 100644
--- a/src/components/Insights/ScalingIssueInsight/index.tsx
+++ b/src/components/Insights/ScalingIssueInsight/index.tsx
@@ -17,6 +17,9 @@ import { Trace } from "../types";
import * as s from "./styles";
import { ScalingIssueInsightProps } from "./types";
+/**
+ * @deprecated
+ */
export const ScalingIssueInsight = (props: ScalingIssueInsightProps) => {
const config = useContext(ConfigContext);
diff --git a/src/components/Insights/SessionInViewInsight/index.tsx b/src/components/Insights/SessionInViewInsight/index.tsx
index 38707844a..f754dd7b4 100644
--- a/src/components/Insights/SessionInViewInsight/index.tsx
+++ b/src/components/Insights/SessionInViewInsight/index.tsx
@@ -13,6 +13,9 @@ import { SessionInViewInsightProps } from "./types";
const PAGE_SIZE = 3;
+/**
+ * @deprecated
+ */
export const SessionInViewInsight = (props: SessionInViewInsightProps) => {
const config = useContext(ConfigContext);
diff --git a/src/components/Insights/SlowEndpointInsight/index.tsx b/src/components/Insights/SlowEndpointInsight/index.tsx
index d99a999b9..17aa44707 100644
--- a/src/components/Insights/SlowEndpointInsight/index.tsx
+++ b/src/components/Insights/SlowEndpointInsight/index.tsx
@@ -4,6 +4,9 @@ import { InsightCard } from "../InsightCard";
import { Description } from "../styles";
import { SlowEndpointInsightProps } from "./types";
+/**
+ * @deprecated
+ */
export const SlowEndpointInsight = (props: SlowEndpointInsightProps) => {
const diff =
(props.insight.median.raw / props.insight.endpointsMedianOfMedians.raw -
diff --git a/src/components/Insights/SpanBottleneckInsight/index.tsx b/src/components/Insights/SpanBottleneckInsight/index.tsx
index ffadc06c8..3e59993c7 100644
--- a/src/components/Insights/SpanBottleneckInsight/index.tsx
+++ b/src/components/Insights/SpanBottleneckInsight/index.tsx
@@ -10,6 +10,9 @@ import { trackingEvents } from "../tracking";
import * as s from "./styles";
import { SpanBottleneckInsightProps } from "./types";
+/**
+ * @deprecated
+ */
export const SpanBottleneckInsight = (props: SpanBottleneckInsightProps) => {
const handleSpanLinkClick = (spanCodeObjectId: string) => {
props.onAssetLinkClick(spanCodeObjectId, props.insight.type);
diff --git a/src/components/Insights/SpanNexusInsight/index.tsx b/src/components/Insights/SpanNexusInsight/index.tsx
index 233838d40..a8736493b 100644
--- a/src/components/Insights/SpanNexusInsight/index.tsx
+++ b/src/components/Insights/SpanNexusInsight/index.tsx
@@ -7,6 +7,10 @@ import { SpanNexusInsightProps } from "./types";
const getTagType = (isHigh: boolean) => {
return isHigh ? "mediumSeverity" : "default";
};
+
+/**
+ * @deprecated
+ */
export const SpanNexusInsight = (props: SpanNexusInsightProps) => {
const { insight } = props;
const {
diff --git a/src/components/Insights/TopUsageInsight/index.tsx b/src/components/Insights/TopUsageInsight/index.tsx
index 74627241f..65a297089 100644
--- a/src/components/Insights/TopUsageInsight/index.tsx
+++ b/src/components/Insights/TopUsageInsight/index.tsx
@@ -13,6 +13,9 @@ import { TopUsageInsightProps } from "./types";
const PAGE_SIZE = 3;
+/**
+ * @deprecated
+ */
export const TopUsageInsight = (props: TopUsageInsightProps) => {
const config = useContext(ConfigContext);
diff --git a/src/components/Insights/TrafficInsight/index.tsx b/src/components/Insights/TrafficInsight/index.tsx
index ba98a2338..de1559334 100644
--- a/src/components/Insights/TrafficInsight/index.tsx
+++ b/src/components/Insights/TrafficInsight/index.tsx
@@ -39,6 +39,9 @@ const getDescription = (insightType: InsightType): string => {
}
};
+/**
+ * @deprecated
+ */
export const TrafficInsight = (props: TrafficInsightProps) => {
const valueString = getValueString(props.insight.maxCallsIn1Min);
diff --git a/src/components/Insights/actions.ts b/src/components/Insights/actions.ts
index 009e93530..6b4aee9c6 100644
--- a/src/components/Insights/actions.ts
+++ b/src/components/Insights/actions.ts
@@ -32,5 +32,9 @@ export const actions = addPrefix(ACTION_PREFIX, {
DISMISS: "DISMISS",
UNDISMISS: "UNDISMISS",
SET_DISMISS_RESPONSE: "SET_DISMISS_RESPONSE",
- SET_UNDISMISS_RESPONSE: "SET_UNDISMISS_RESPONSE"
+ SET_UNDISMISS_RESPONSE: "SET_UNDISMISS_RESPONSE",
+ MARK_AS_READ: "MARK_AS_READ",
+ SET_MARK_AS_READ_RESPONSE: "SET_MARK_AS_READ_RESPONSE",
+ MARK_ALL_AS_READ: "MARK_ALL_AS_READ",
+ SET_MARK_ALL_AS_READ_RESPONSE: "SET_MARK_ALL_AS_READ_RESPONSE"
});
diff --git a/src/components/Insights/common/InsightCard/InsightCard.stories.tsx b/src/components/Insights/common/InsightCard/InsightCard.stories.tsx
index 352135fdf..0cbdb4539 100644
--- a/src/components/Insights/common/InsightCard/InsightCard.stories.tsx
+++ b/src/components/Insights/common/InsightCard/InsightCard.stories.tsx
@@ -21,14 +21,20 @@ type Story = StoryObj;
export const Default: Story = {
args: {
isAsync: true,
- insight: { ...mockedEndpointNPlusOneInsight, criticality: 0.9 }
+ insight: {
+ ...mockedEndpointNPlusOneInsight,
+ isRead: true
+ }
}
};
export const JiraButtonIsPrimary: Story = {
args: {
isAsync: true,
- insight: { ...mockedEndpointNPlusOneInsight, criticality: 0.9 },
+ insight: {
+ ...mockedEndpointNPlusOneInsight,
+ isRead: true
+ },
onGoToLive: undefined,
onPin: undefined,
onGoToTrace: undefined,
@@ -39,7 +45,10 @@ export const JiraButtonIsPrimary: Story = {
export const LinkedJiraTicket: Story = {
args: {
isAsync: true,
- insight: { ...mockedEndpointNPlusOneInsight, criticality: 0.9 },
+ insight: {
+ ...mockedEndpointNPlusOneInsight,
+ isRead: true
+ },
jiraTicketInfo: {
ticketLink: "some",
spanCodeObjectId: "test",
@@ -57,8 +66,8 @@ export const Dismissed: Story = {
isAsync: true,
insight: {
...mockedEndpointNPlusOneInsight,
- criticality: 0.9,
- isDismissed: true
+ isDismissed: true,
+ isRead: true
},
jiraTicketInfo: {
ticketLink: "some",
@@ -72,7 +81,7 @@ export const Dismissed: Story = {
}
};
-export const WithNewVersion: Story = {
+export const WithDisabledDismissed: Story = {
decorators: [
(Story) => (
{
- const [isLoading, setIsLoading] = useState(false);
+export const useDismissal = (insightId: string) => {
+ const [isDismissalChangeInProgress, setIsDismissalChangeInProgress] =
+ useState(false);
useEffect(() => {
const handleDismissed = (data: any) => {
if (insightId === (data as DismissResponsePayload).insightId) {
- setIsLoading(false);
+ setIsDismissalChangeInProgress(false);
}
};
@@ -27,7 +28,7 @@ export const useDismissalHandler = (insightId: string) => {
useEffect(() => {
const handleUndismissed = (data: any) => {
if (insightId === (data as UndismissResponsePayload).insightId) {
- setIsLoading(false);
+ setIsDismissalChangeInProgress(false);
}
};
@@ -42,10 +43,10 @@ export const useDismissalHandler = (insightId: string) => {
handleUndismissed
);
};
- }, []);
+ }, [insightId]);
return {
- isLoading,
+ isDismissalChangeInProgress,
dismiss: () => {
window.sendMessageToDigma({
action: actions.DISMISS,
@@ -53,7 +54,7 @@ export const useDismissalHandler = (insightId: string) => {
insightId
}
});
- setIsLoading(true);
+ setIsDismissalChangeInProgress(true);
},
show: () => {
window.sendMessageToDigma({
@@ -62,7 +63,7 @@ export const useDismissalHandler = (insightId: string) => {
insightId: insightId
}
});
- setIsLoading(true);
+ setIsDismissalChangeInProgress(true);
}
};
};
diff --git a/src/components/Insights/common/InsightCard/hooks/useMarkingAsRead.ts b/src/components/Insights/common/InsightCard/hooks/useMarkingAsRead.ts
new file mode 100644
index 000000000..1afc9dd56
--- /dev/null
+++ b/src/components/Insights/common/InsightCard/hooks/useMarkingAsRead.ts
@@ -0,0 +1,46 @@
+import { useEffect, useState } from "react";
+import { dispatcher } from "../../../../../dispatcher";
+import { actions } from "../../../actions";
+import { MarkAsReadPayload, SetMarkAsReadResponsePayload } from "../types";
+
+export const useMarkingAsRead = (insightId: string) => {
+ const [isMarkingAsReadInProgress, setIsMarkingAsReadInProgress] =
+ useState(false);
+
+ const markAsRead = () => {
+ window.sendMessageToDigma({
+ action: actions.MARK_AS_READ,
+ payload: {
+ insightIds: [insightId]
+ }
+ });
+ setIsMarkingAsReadInProgress(true);
+ };
+
+ useEffect(() => {
+ const handleMarkAsReadResponse = (data: any) => {
+ if (
+ (data as SetMarkAsReadResponsePayload).insightIds.includes(insightId)
+ ) {
+ setIsMarkingAsReadInProgress(false);
+ }
+ };
+
+ dispatcher.addActionListener(
+ actions.SET_MARK_AS_READ_RESPONSE,
+ handleMarkAsReadResponse
+ );
+
+ return () => {
+ dispatcher.removeActionListener(
+ actions.SET_MARK_AS_READ_RESPONSE,
+ handleMarkAsReadResponse
+ );
+ };
+ }, [insightId]);
+
+ return {
+ isMarkingAsReadInProgress,
+ markAsRead
+ };
+};
diff --git a/src/components/Insights/common/InsightCard/index.tsx b/src/components/Insights/common/InsightCard/index.tsx
index 40ff5f0c0..f98820996 100644
--- a/src/components/Insights/common/InsightCard/index.tsx
+++ b/src/components/Insights/common/InsightCard/index.tsx
@@ -1,5 +1,14 @@
import React, { useContext, useEffect, useState } from "react";
+import { actions as globalActions } from "../../../../actions";
+import { getFeatureFlagValue } from "../../../../featureFlags";
+import { usePrevious } from "../../../../hooks/usePrevious";
import { isString } from "../../../../typeGuards/isString";
+import { FeatureFlag } from "../../../../types";
+import { sendTrackingEvent } from "../../../../utils/sendTrackingEvent";
+import { Spinner } from "../../../Navigation/CodeButtonMenu/Spinner";
+import { ChangeScopePayload } from "../../../Navigation/types";
+import { ConfigContext } from "../../../common/App/ConfigContext";
+import { CheckmarkCircleIcon } from "../../../common/icons/12px/CheckmarkCircleIcon";
import { TraceIcon } from "../../../common/icons/12px/TraceIcon";
import { HistogramIcon } from "../../../common/icons/16px/HistogramIcon";
import { LiveIcon } from "../../../common/icons/16px/LiveIcon";
@@ -10,22 +19,16 @@ import { Button } from "../../../common/v3/Button";
import { BaseButtonProps } from "../../../common/v3/Button/types";
import { JiraButton } from "../../../common/v3/JiraButton";
import { Tooltip } from "../../../common/v3/Tooltip";
-import { isEndpointInsight, isSpanInsight } from "../../typeGuards";
-import { InsightHeader } from "./InsightHeader";
-import * as s from "./styles";
-import { InsightCardProps } from "./types";
-
-import { getFeatureFlagValue } from "../../../../featureFlags";
-import { usePrevious } from "../../../../hooks/usePrevious";
-import { FeatureFlag } from "../../../../types";
-import { sendTrackingEvent } from "../../../../utils/sendTrackingEvent";
-import { Spinner } from "../../../Navigation/CodeButtonMenu/Spinner";
-import { ConfigContext } from "../../../common/App/ConfigContext";
import { trackingEvents } from "../../tracking";
+import { isEndpointInsight, isSpanInsight } from "../../typeGuards";
import { InsightStatus } from "../../types";
+import { InsightHeader } from "./InsightHeader";
import { ProductionAffectionBar } from "./ProductionAffectionBar";
import { RecalculateBar } from "./RecalculateBar";
-import { useDismissalHandler } from "./useDismissalHandler";
+import { useDismissal } from "./hooks/useDismissal";
+import { useMarkingAsRead } from "./hooks/useMarkingAsRead";
+import * as s from "./styles";
+import { InsightCardProps } from "./types";
const IS_NEW_TIME_LIMIT = 1000 * 60 * 10; // in milliseconds
const HIGH_CRITICALITY_THRESHOLD = 0.8;
@@ -34,22 +37,48 @@ export const InsightCard = (props: InsightCardProps) => {
const [isRecalculatingStarted, setIsRecalculatingStarted] = useState(false);
const [isDismissConfirmationOpened, setDismissConfirmationOpened] =
useState(false);
- const { isLoading, dismiss, show } = useDismissalHandler(props.insight.id);
- const previousLoading = usePrevious(isLoading);
+ const { isDismissalChangeInProgress, dismiss, show } = useDismissal(
+ props.insight.id
+ );
+ const { isMarkingAsReadInProgress, markAsRead } = useMarkingAsRead(
+ props.insight.id
+ );
+ const isOperationInProgress =
+ isDismissalChangeInProgress || isMarkingAsReadInProgress;
+ const previousIsOperationInProgress = usePrevious(isOperationInProgress);
const config = useContext(ConfigContext);
const [insightStatus, setInsightStatus] = useState(props.insight.status);
const isCritical = props.insight.criticality > HIGH_CRITICALITY_THRESHOLD;
+
// TODO: remove and refresh the insight data
useEffect(() => {
setInsightStatus(props.insight.status);
}, [props.insight.status]);
useEffect(() => {
- if (previousLoading && !isLoading) {
+ if (previousIsOperationInProgress && !isOperationInProgress) {
props.onRefresh(props.insight.type);
+
+ // Trigger SET_SCOPE response message from the plugin with actual unread insights count
+ window.sendMessageToDigma({
+ action: globalActions.CHANGE_SCOPE,
+ payload: {
+ span: config.scope?.span
+ ? {
+ spanCodeObjectId: config.scope.span.spanCodeObjectId
+ }
+ : null
+ }
+ });
}
- }, [isLoading, previousLoading, props.onRefresh]);
+ }, [
+ previousIsOperationInProgress,
+ isOperationInProgress,
+ props.onRefresh,
+ props.insight.type,
+ config.scope
+ ]);
const handleRecheckButtonClick = () => {
props.insight.prefixedCodeObjectId &&
@@ -141,12 +170,41 @@ export const InsightCard = (props: InsightCardProps) => {
);
};
+ const handleClick = () => {
+ if (
+ !props.isMarkAsReadButtonEnabled &&
+ props.insight.isReadable &&
+ props.insight.isRead === false
+ ) {
+ markAsRead();
+ }
+ };
+
const renderActions = () => {
const buttonsToRender: {
tooltip: string;
button: React.ComponentType;
}[] = [];
+ if (
+ props.isMarkAsReadButtonEnabled &&
+ props.insight.isReadable &&
+ props.insight.isRead === false
+ ) {
+ buttonsToRender.push({
+ tooltip: "Mark as read",
+ button: (btnProps) => (
+
+ )
+ });
+ }
+
props.onOpenHistogram &&
buttonsToRender.push({
tooltip: "Open Histogram",
@@ -253,9 +311,13 @@ export const InsightCard = (props: InsightCardProps) => {
? Date.now() - new Date(props.insight.firstDetected).valueOf() <
IS_NEW_TIME_LIMIT
: false;
+
return (
{
{props.insight.isDismissed ? (
) : (
setDismissConfirmationOpened(true)}
/>
)}
- {isLoading && }
+ {isDismissalChangeInProgress && }
)}
{renderActions()}
diff --git a/src/components/Insights/common/InsightCard/styles.ts b/src/components/Insights/common/InsightCard/styles.ts
index 38e51262e..77f98ac75 100644
--- a/src/components/Insights/common/InsightCard/styles.ts
+++ b/src/components/Insights/common/InsightCard/styles.ts
@@ -46,6 +46,18 @@ export const StyledInsightCard = styled(Card)`
background: ${theme.colors.v3.surface.sidePanelHeader};
`
: ""}
+ ${({ $isRead, $isReadable, theme }) =>
+ $isReadable && $isRead === false
+ ? css`
+ background: ${theme.colors.v3.surface.brandDarkest};
+ border: 1px solid ${theme.colors.v3.stroke.primary};
+
+ &:hover {
+ background: ${theme.colors.v3.surface.brandDark};
+ border: 1px solid ${theme.colors.v3.stroke.primaryLight};
+ }
+ `
+ : ""}
`;
export const DismissButton = styled(Button)`
diff --git a/src/components/Insights/common/InsightCard/types.ts b/src/components/Insights/common/InsightCard/types.ts
index 30a25c574..fcf56e249 100644
--- a/src/components/Insights/common/InsightCard/types.ts
+++ b/src/components/Insights/common/InsightCard/types.ts
@@ -1,5 +1,6 @@
import { ReactNode } from "react";
import { InsightType } from "../../../../types";
+import { ScopeSpan } from "../../../common/App/types";
import { CardProps } from "../../../common/Card/types";
import { GenericCodeObjectInsight } from "../../types";
@@ -29,10 +30,13 @@ export interface InsightCardProps {
event: string
) => void;
onGoToSpan: (spanCodeObjectId: string) => void;
+ isMarkAsReadButtonEnabled: boolean;
}
export interface StyledInsightCardProps extends CardProps {
$isDismissed?: boolean;
+ $isRead?: boolean;
+ $isReadable?: boolean;
}
export interface DismissResponsePayload {
@@ -46,3 +50,23 @@ export interface UndismissResponsePayload {
status: "success" | "failure";
error?: string;
}
+
+export interface MarkAsReadPayload {
+ insightIds: string[];
+}
+
+export interface MarkAllAsReadPayload {
+ scope: ScopeSpan | null;
+}
+
+export interface SetMarkAsReadResponsePayload {
+ insightIds: string[];
+ status: "success" | "failure";
+ error?: string;
+}
+
+export interface SetMarkAllAsReadResponsePayload {
+ scope: ScopeSpan | null;
+ status: "success" | "failure";
+ error?: string;
+}
diff --git a/src/components/Insights/common/insights/DurationBreakdownInsight/index.tsx b/src/components/Insights/common/insights/DurationBreakdownInsight/index.tsx
index f02fe7cd1..871f53922 100644
--- a/src/components/Insights/common/insights/DurationBreakdownInsight/index.tsx
+++ b/src/components/Insights/common/insights/DurationBreakdownInsight/index.tsx
@@ -128,6 +128,7 @@ export const DurationBreakdownInsight = (
onRecalculate={props.onRecalculate}
onRefresh={props.onRefresh}
onGoToSpan={props.onGoToSpan}
+ isMarkAsReadButtonEnabled={props.isMarkAsReadButtonEnabled}
/>
);
};
diff --git a/src/components/Insights/common/insights/DurationInsight/index.tsx b/src/components/Insights/common/insights/DurationInsight/index.tsx
index 9c983dfa9..03b2e31ae 100644
--- a/src/components/Insights/common/insights/DurationInsight/index.tsx
+++ b/src/components/Insights/common/insights/DurationInsight/index.tsx
@@ -431,6 +431,7 @@ export const DurationInsight = (props: DurationInsightProps) => {
props.insight.spanInfo ? props.onHistogramButtonClick : undefined
}
onGoToSpan={props.onGoToSpan}
+ isMarkAsReadButtonEnabled={props.isMarkAsReadButtonEnabled}
/>
);
};
diff --git a/src/components/Insights/common/insights/EndpointBottleneckInsight/index.tsx b/src/components/Insights/common/insights/EndpointBottleneckInsight/index.tsx
index 69d02796c..90d4b77d9 100644
--- a/src/components/Insights/common/insights/EndpointBottleneckInsight/index.tsx
+++ b/src/components/Insights/common/insights/EndpointBottleneckInsight/index.tsx
@@ -90,6 +90,7 @@ export const EndpointBottleneckInsight = (
onJiraButtonClick={handleTicketInfoButtonClick}
onGoToSpan={props.onGoToSpan}
onGoToTrace={span.traceId ? handleTraceButtonClick : undefined}
+ isMarkAsReadButtonEnabled={props.isMarkAsReadButtonEnabled}
/>
);
};
diff --git a/src/components/Insights/common/insights/EndpointChattyApiV2Insight/index.tsx b/src/components/Insights/common/insights/EndpointChattyApiV2Insight/index.tsx
index c3ba6b0ee..a9e3fb42b 100644
--- a/src/components/Insights/common/insights/EndpointChattyApiV2Insight/index.tsx
+++ b/src/components/Insights/common/insights/EndpointChattyApiV2Insight/index.tsx
@@ -10,7 +10,8 @@ export const EndpointChattyApiV2Insight = ({
onTraceButtonClick,
onRecalculate,
onRefresh,
- onGoToSpan
+ onGoToSpan,
+ isMarkAsReadButtonEnabled
}: EndpointChattyApiV2InsightProps) => {
const handleSpanLinkClick = (spanCodeObjectId: string) => {
onAssetLinkClick(spanCodeObjectId, insight.type);
@@ -58,6 +59,7 @@ export const EndpointChattyApiV2Insight = ({
)
: undefined
}
+ isMarkAsReadButtonEnabled={isMarkAsReadButtonEnabled}
/>
);
};
diff --git a/src/components/Insights/common/insights/EndpointNPlusOneInsight/index.tsx b/src/components/Insights/common/insights/EndpointNPlusOneInsight/index.tsx
index 1afb9a74d..6d51a1c4a 100644
--- a/src/components/Insights/common/insights/EndpointNPlusOneInsight/index.tsx
+++ b/src/components/Insights/common/insights/EndpointNPlusOneInsight/index.tsx
@@ -85,6 +85,7 @@ export const EndpointNPlusOneInsight = (
onRecalculate={props.onRecalculate}
onRefresh={props.onRefresh}
onGoToSpan={props.onGoToSpan}
+ isMarkAsReadButtonEnabled={props.isMarkAsReadButtonEnabled}
/>
);
};
diff --git a/src/components/Insights/common/insights/EndpointQueryOptimizationInsight/index.tsx b/src/components/Insights/common/insights/EndpointQueryOptimizationInsight/index.tsx
index d74891e28..a1bec078c 100644
--- a/src/components/Insights/common/insights/EndpointQueryOptimizationInsight/index.tsx
+++ b/src/components/Insights/common/insights/EndpointQueryOptimizationInsight/index.tsx
@@ -116,6 +116,7 @@ export const EndpointQueryOptimizationInsight = (
isHintEnabled: props.isJiraHintEnabled
}}
onGoToSpan={props.onGoToSpan}
+ isMarkAsReadButtonEnabled={props.isMarkAsReadButtonEnabled}
/>
);
};
diff --git a/src/components/Insights/common/insights/EndpointQueryOptimizationV2Insight/index.tsx b/src/components/Insights/common/insights/EndpointQueryOptimizationV2Insight/index.tsx
index 6e99180b4..a173ace4a 100644
--- a/src/components/Insights/common/insights/EndpointQueryOptimizationV2Insight/index.tsx
+++ b/src/components/Insights/common/insights/EndpointQueryOptimizationV2Insight/index.tsx
@@ -1,6 +1,4 @@
-import { useContext } from "react";
import { getDurationString } from "../../../../../utils/getDurationString";
-import { ConfigContext } from "../../../../common/App/ConfigContext";
import { InsightType, Trace } from "../../../types";
import { InsightCard } from "../../InsightCard";
import { ColumnsContainer } from "../../InsightCard/ColumnsContainer";
@@ -17,10 +15,9 @@ export const EndpointQueryOptimizationV2Insight = ({
isJiraHintEnabled,
onRecalculate,
onRefresh,
- onGoToSpan
+ onGoToSpan,
+ isMarkAsReadButtonEnabled
}: EndpointQueryOptimizationV2InsightProps) => {
- const config = useContext(ConfigContext);
-
const handleSpanLinkClick = (spanCodeObjectId: string) => {
onAssetLinkClick(spanCodeObjectId, insight.type);
};
@@ -80,6 +77,7 @@ export const EndpointQueryOptimizationV2Insight = ({
isHintEnabled: isJiraHintEnabled
}}
onGoToSpan={onGoToSpan}
+ isMarkAsReadButtonEnabled={isMarkAsReadButtonEnabled}
/>
);
};
diff --git a/src/components/Insights/common/insights/EndpointSlowdownSourceInsight/index.tsx b/src/components/Insights/common/insights/EndpointSlowdownSourceInsight/index.tsx
index dd80c600b..c541a58bc 100644
--- a/src/components/Insights/common/insights/EndpointSlowdownSourceInsight/index.tsx
+++ b/src/components/Insights/common/insights/EndpointSlowdownSourceInsight/index.tsx
@@ -71,6 +71,7 @@ export const EndpointSlowdownSourceInsight = (
onRecalculate={props.onRecalculate}
onRefresh={props.onRefresh}
onGoToSpan={props.onGoToSpan}
+ isMarkAsReadButtonEnabled={props.isMarkAsReadButtonEnabled}
/>
);
};
diff --git a/src/components/Insights/common/insights/ExcessiveAPICallsInsight/index.tsx b/src/components/Insights/common/insights/ExcessiveAPICallsInsight/index.tsx
index b44722591..2d6d97bdb 100644
--- a/src/components/Insights/common/insights/ExcessiveAPICallsInsight/index.tsx
+++ b/src/components/Insights/common/insights/ExcessiveAPICallsInsight/index.tsx
@@ -90,6 +90,7 @@ export const ExcessiveAPICallsInsight = (
onRecalculate={props.onRecalculate}
onRefresh={props.onRefresh}
onGoToSpan={props.onGoToSpan}
+ isMarkAsReadButtonEnabled={props.isMarkAsReadButtonEnabled}
/>
);
};
diff --git a/src/components/Insights/common/insights/HighNumberOfQueriesInsight/index.tsx b/src/components/Insights/common/insights/HighNumberOfQueriesInsight/index.tsx
index 914dc03b0..0fd82713b 100644
--- a/src/components/Insights/common/insights/HighNumberOfQueriesInsight/index.tsx
+++ b/src/components/Insights/common/insights/HighNumberOfQueriesInsight/index.tsx
@@ -85,6 +85,7 @@ export const HighNumberOfQueriesInsight = (
: undefined
}
onGoToSpan={props.onGoToSpan}
+ isMarkAsReadButtonEnabled={props.isMarkAsReadButtonEnabled}
/>
);
};
diff --git a/src/components/Insights/common/insights/RequestBreakdownInsight/index.tsx b/src/components/Insights/common/insights/RequestBreakdownInsight/index.tsx
index 26b4d2f00..34b1ddd80 100644
--- a/src/components/Insights/common/insights/RequestBreakdownInsight/index.tsx
+++ b/src/components/Insights/common/insights/RequestBreakdownInsight/index.tsx
@@ -236,6 +236,7 @@ export const RequestBreakdownInsight = (
onRefresh={props.onRefresh}
isAsync={props.insight.hasAsyncSpans}
onGoToSpan={props.onGoToSpan}
+ isMarkAsReadButtonEnabled={props.isMarkAsReadButtonEnabled}
/>
);
};
diff --git a/src/components/Insights/common/insights/ScalingIssueInsight/index.tsx b/src/components/Insights/common/insights/ScalingIssueInsight/index.tsx
index 54533277f..8a58913b4 100644
--- a/src/components/Insights/common/insights/ScalingIssueInsight/index.tsx
+++ b/src/components/Insights/common/insights/ScalingIssueInsight/index.tsx
@@ -156,6 +156,7 @@ export const ScalingIssueInsight = (props: ScalingIssueInsightProps) => {
onRecalculate={props.onRecalculate}
onRefresh={props.onRefresh}
onGoToSpan={props.onGoToSpan}
+ isMarkAsReadButtonEnabled={props.isMarkAsReadButtonEnabled}
/>
);
};
diff --git a/src/components/Insights/common/insights/SessionInViewInsight/index.tsx b/src/components/Insights/common/insights/SessionInViewInsight/index.tsx
index bf2f28751..4e5f34636 100644
--- a/src/components/Insights/common/insights/SessionInViewInsight/index.tsx
+++ b/src/components/Insights/common/insights/SessionInViewInsight/index.tsx
@@ -91,6 +91,7 @@ export const SessionInViewInsight = (props: SessionInViewInsightProps) => {
onRecalculate={props.onRecalculate}
onRefresh={props.onRefresh}
onGoToSpan={props.onGoToSpan}
+ isMarkAsReadButtonEnabled={props.isMarkAsReadButtonEnabled}
/>
);
};
diff --git a/src/components/Insights/common/insights/SlowEndpointInsight/index.tsx b/src/components/Insights/common/insights/SlowEndpointInsight/index.tsx
index 96750140b..dc77123f1 100644
--- a/src/components/Insights/common/insights/SlowEndpointInsight/index.tsx
+++ b/src/components/Insights/common/insights/SlowEndpointInsight/index.tsx
@@ -32,6 +32,7 @@ export const SlowEndpointInsight = (props: SlowEndpointInsightProps) => {
onRecalculate={props.onRecalculate}
onRefresh={props.onRefresh}
onGoToSpan={props.onGoToSpan}
+ isMarkAsReadButtonEnabled={props.isMarkAsReadButtonEnabled}
/>
);
};
diff --git a/src/components/Insights/common/insights/SpanEndpointBottleneckInsight/index.tsx b/src/components/Insights/common/insights/SpanEndpointBottleneckInsight/index.tsx
index 9a12d9c11..c186be29f 100644
--- a/src/components/Insights/common/insights/SpanEndpointBottleneckInsight/index.tsx
+++ b/src/components/Insights/common/insights/SpanEndpointBottleneckInsight/index.tsx
@@ -160,6 +160,7 @@ export const SpanEndpointBottleneckInsight = (
onRecalculate={props.onRecalculate}
onRefresh={props.onRefresh}
onGoToSpan={props.onGoToSpan}
+ isMarkAsReadButtonEnabled={props.isMarkAsReadButtonEnabled}
/>
);
};
diff --git a/src/components/Insights/common/insights/SpanNPlusOneInsight/index.tsx b/src/components/Insights/common/insights/SpanNPlusOneInsight/index.tsx
index d5d5c23ac..17028092e 100644
--- a/src/components/Insights/common/insights/SpanNPlusOneInsight/index.tsx
+++ b/src/components/Insights/common/insights/SpanNPlusOneInsight/index.tsx
@@ -138,6 +138,7 @@ export const SpanNPlusOneInsight = (props: SpanNPlusOneInsightProps) => {
isHintEnabled: props.isJiraHintEnabled
}}
onGoToSpan={props.onGoToSpan}
+ isMarkAsReadButtonEnabled={props.isMarkAsReadButtonEnabled}
/>
);
};
diff --git a/src/components/Insights/common/insights/SpanNexusInsight/index.tsx b/src/components/Insights/common/insights/SpanNexusInsight/index.tsx
index 092ce1ef3..2695c7400 100644
--- a/src/components/Insights/common/insights/SpanNexusInsight/index.tsx
+++ b/src/components/Insights/common/insights/SpanNexusInsight/index.tsx
@@ -50,6 +50,7 @@ export const SpanNexusInsight = (props: SpanNexusInsightProps) => {
onRecalculate={props.onRecalculate}
onRefresh={props.onRefresh}
onGoToSpan={props.onGoToSpan}
+ isMarkAsReadButtonEnabled={props.isMarkAsReadButtonEnabled}
/>
);
};
diff --git a/src/components/Insights/common/insights/SpanQueryOptimizationInsight/index.tsx b/src/components/Insights/common/insights/SpanQueryOptimizationInsight/index.tsx
index a0685bb7c..915121dae 100644
--- a/src/components/Insights/common/insights/SpanQueryOptimizationInsight/index.tsx
+++ b/src/components/Insights/common/insights/SpanQueryOptimizationInsight/index.tsx
@@ -123,6 +123,7 @@ export const SpanQueryOptimizationInsight = (
: undefined
}
onGoToSpan={props.onGoToSpan}
+ isMarkAsReadButtonEnabled={props.isMarkAsReadButtonEnabled}
/>
);
};
diff --git a/src/components/Insights/common/insights/TopUsageInsight/index.tsx b/src/components/Insights/common/insights/TopUsageInsight/index.tsx
index 978f1a444..8f8787a48 100644
--- a/src/components/Insights/common/insights/TopUsageInsight/index.tsx
+++ b/src/components/Insights/common/insights/TopUsageInsight/index.tsx
@@ -267,6 +267,7 @@ export const TopUsageInsight = (props: TopUsageInsightProps) => {
onRecalculate={props.onRecalculate}
onRefresh={props.onRefresh}
onGoToSpan={props.onGoToSpan}
+ isMarkAsReadButtonEnabled={props.isMarkAsReadButtonEnabled}
/>
);
};
diff --git a/src/components/Insights/common/insights/TrafficInsight/index.tsx b/src/components/Insights/common/insights/TrafficInsight/index.tsx
index 45118d063..e68d11d63 100644
--- a/src/components/Insights/common/insights/TrafficInsight/index.tsx
+++ b/src/components/Insights/common/insights/TrafficInsight/index.tsx
@@ -63,6 +63,7 @@ export const TrafficInsight = (props: TrafficInsightProps) => {
onRecalculate={props.onRecalculate}
onRefresh={props.onRefresh}
onGoToSpan={props.onGoToSpan}
+ isMarkAsReadButtonEnabled={props.isMarkAsReadButtonEnabled}
/>
);
};
diff --git a/src/components/Insights/index.tsx b/src/components/Insights/index.tsx
index 5f03499e7..291cc2ced 100644
--- a/src/components/Insights/index.tsx
+++ b/src/components/Insights/index.tsx
@@ -1,4 +1,10 @@
-import { useContext, useEffect, useLayoutEffect, useState } from "react";
+import {
+ KeyboardEvent,
+ useContext,
+ useEffect,
+ useLayoutEffect,
+ useState
+} from "react";
import { actions as globalActions } from "../../actions";
import { SLACK_WORKSPACE_URL } from "../../constants";
import { getFeatureFlagValue } from "../../featureFlags";
@@ -22,7 +28,6 @@ import { OpenTelemetryLogoCrossedSmallIcon } from "../common/icons/OpenTelemetry
import { SlackLogoIcon } from "../common/icons/SlackLogoIcon";
import { InsightsCatalog } from "./InsightsCatalog";
import { SORTING_CRITERION } from "./InsightsCatalog/types";
-import { useInsightsData } from "./common/useInsightsData";
import * as s from "./styles";
import { BottleneckInsightTicket } from "./tickets/BottleneckInsightTicket";
import { EndpointHighNumberOfQueriesInsightTicket } from "./tickets/EndpointHighNumberOfQueriesInsightTicket";
@@ -62,6 +67,7 @@ import {
SpanNPlusOneInsight,
SpanScalingBadlyInsight
} from "./types";
+import { useInsightsData } from "./useInsightsData";
const REFRESH_INTERVAL = isNumber(window.insightsRefreshInterval)
? window.insightsRefreshInterval
@@ -204,7 +210,8 @@ export const Insights = (props: InsightsProps) => {
},
searchQuery: null,
showDismissed: false,
- insightViewType: props.insightViewType
+ insightViewType: props.insightViewType,
+ showUnreadOnly: false
};
// const [isAutofixing, setIsAutofixing] = useState(false);
const [query, setQuery] = useState(DEFAULT_QUERY);
@@ -226,6 +233,13 @@ export const Insights = (props: InsightsProps) => {
props.insightViewType === "Issues"
);
+ const isMarkingAsReadEnabled = Boolean(
+ getFeatureFlagValue(
+ config,
+ FeatureFlag.IS_INSIGHT_MARKING_AS_READ_ENABLED
+ ) && props.insightViewType === "Issues"
+ );
+
useLayoutEffect(() => {
sendMessage(globalActions.GET_STATE);
}, []);
@@ -304,19 +318,29 @@ export const Insights = (props: InsightsProps) => {
setInfoToOpenJiraTicket(undefined);
};
+ const handleQueryChange = (query: InsightsQuery) => {
+ setQuery(query);
+ };
+
+ const handleOverlayKeyDown = (e: KeyboardEvent) => {
+ if (e.key === "Escape") {
+ setInfoToOpenJiraTicket(undefined);
+ }
+ };
+
const renderDefaultContent = (data: InsightsData): JSX.Element => {
return (
{
- setQuery(query);
- }}
+ onQueryChange={handleQueryChange}
onRefresh={refresh}
defaultQuery={DEFAULT_QUERY}
dismissedCount={data.dismissedCount}
isDismissalEnabled={isDismissalEnabled}
+ unreadCount={data.unreadCount}
+ isMarkingAsReadEnabled={isMarkingAsReadEnabled}
/>
);
};
@@ -414,7 +438,7 @@ export const Insights = (props: InsightsProps) => {
{renderContent(data, isInitialLoading)}
{infoToOpenJiraTicket && (
-
+
{/* {config.userRegistrationEmail ? ( */}
{true ? ( // eslint-disable-line no-constant-condition
diff --git a/src/components/Insights/tickets/EndpointQueryOptimizationTicket/index.tsx b/src/components/Insights/tickets/EndpointQueryOptimizationTicket/index.tsx
index c31a23910..8cb130f70 100644
--- a/src/components/Insights/tickets/EndpointQueryOptimizationTicket/index.tsx
+++ b/src/components/Insights/tickets/EndpointQueryOptimizationTicket/index.tsx
@@ -17,6 +17,9 @@ import { DigmaSignature } from "../common/DigmaSignature";
import { QueryOptimizationEndpoints } from "../common/QueryOptimizationEndpoints";
import { InsightTicketProps } from "../types";
+/**
+ * @deprecated
+ */
export const EndpointQueryOptimizationInsightTicket = (
props: InsightTicketProps
) => {
diff --git a/src/components/Insights/typeGuards.ts b/src/components/Insights/typeGuards.ts
index 8d32b59fc..b33f91530 100644
--- a/src/components/Insights/typeGuards.ts
+++ b/src/components/Insights/typeGuards.ts
@@ -81,7 +81,9 @@ export const isEndpointHighUsageInsight = (
): insight is EndpointHighUsageInsight =>
insight.type === InsightType.HighUsage;
-// obsolete
+/**
+ * @deprecated
+ */
export const isEndpointSlowestSpansInsight = (
insight: CodeObjectInsight
): insight is EndpointSlowestSpansInsight =>
@@ -100,7 +102,9 @@ export const isSpanNPlusOneInsight = (
insight: CodeObjectInsight
): insight is SpanNPlusOneInsight => insight.type === InsightType.SpanNPlusOne;
-// obsolete
+/**
+ * @deprecated
+ */
export const isEndpointSuspectedNPlusOneInsight = (
insight: CodeObjectInsight
): insight is EndpointSuspectedNPlusOneInsight =>
@@ -111,7 +115,9 @@ export const isEndpointSpanNPlusOneInsight = (
): insight is EndpointSpanNPlusOneInsight =>
insight.type === InsightType.EndpointSpanNPlusOneV2;
-// obsolete
+/**
+ * @deprecated
+ */
export const isEndpointQueryOptimizationInsight = (
insight: CodeObjectInsight
): insight is EndpointQueryOptimizationInsight =>
@@ -140,7 +146,9 @@ export const isEndpointSlowdownSourceInsight = (
): insight is EndpointSlowdownSourceInsight =>
insight.type === InsightType.EndpointSlowdownSource;
-// obsolete
+/**
+ * @deprecated
+ */
export const isEndpointDurationSlowdownInsight = (
insight: CodeObjectInsight
): insight is EndpointDurationSlowdownInsight =>
@@ -151,13 +159,17 @@ export const isEndpointBreakdownInsight = (
): insight is EndpointBreakdownInsight =>
insight.type === InsightType.EndpointBreakdown;
-// obsolete
+/**
+ * @deprecated
+ */
export const isSpanScalingWellInsight = (
insight: CodeObjectInsight
): insight is SpanScalingWellInsight =>
insight.type === InsightType.SpanScalingWell;
-// obsolete
+/**
+ * @deprecated
+ */
export const isSpanScalingInsufficientDataInsight = (
insight: CodeObjectInsight
): insight is SpanScalingInsufficientDataInsight =>
@@ -168,7 +180,9 @@ export const isSessionInViewEndpointInsight = (
): insight is SessionInViewEndpointInsight =>
insight.type === InsightType.EndpointSessionInView;
-// obsolete
+/**
+ * @deprecated
+ */
export const isChattyApiEndpointInsight = (
insight: CodeObjectInsight
): insight is ChattyApiEndpointInsight =>
diff --git a/src/components/Insights/types.ts b/src/components/Insights/types.ts
index 6b2f310b1..5c6f6b3c6 100644
--- a/src/components/Insights/types.ts
+++ b/src/components/Insights/types.ts
@@ -75,6 +75,7 @@ export interface InsightsData {
insightsStatus: InsightsStatus; // ?? default
viewMode: ViewMode; // Insights
dismissedCount?: number;
+ unreadCount?: number;
// methods: Method[]; // empty
// assetId?: string; // remove
@@ -109,6 +110,7 @@ export interface InsightProps {
) => void;
onGoToSpan: (spanCodeObjectId: string) => void;
isJiraHintEnabled?: boolean;
+ isMarkAsReadButtonEnabled: boolean;
}
export interface InsightTicketInfo {
@@ -206,6 +208,8 @@ export interface CodeObjectInsight extends Insight {
isDismissed?: boolean;
isDismissible?: boolean;
status?: InsightStatus;
+ isRead?: boolean;
+ isReadable?: boolean;
}
export interface SpanInsight extends CodeObjectInsight {
@@ -902,6 +906,7 @@ export interface InsightsQuery {
sorting: Sorting;
searchQuery: string | null;
showDismissed: boolean;
+ showUnreadOnly: boolean;
insightViewType: InsightViewType;
}
diff --git a/src/components/Insights/common/useInsightsData.ts b/src/components/Insights/useInsightsData.ts
similarity index 90%
rename from src/components/Insights/common/useInsightsData.ts
rename to src/components/Insights/useInsightsData.ts
index b1e43b123..f787e5a64 100644
--- a/src/components/Insights/common/useInsightsData.ts
+++ b/src/components/Insights/useInsightsData.ts
@@ -1,20 +1,20 @@
import { useContext, useEffect, useMemo, useRef, useState } from "react";
-import { actions as globalActions } from "../../../actions";
-import { dispatcher } from "../../../dispatcher";
-import { usePrevious } from "../../../hooks/usePrevious";
-import { ConfigContext } from "../../common/App/ConfigContext";
+import { actions as globalActions } from "../../actions";
+import { dispatcher } from "../../dispatcher";
+import { usePrevious } from "../../hooks/usePrevious";
+import { ConfigContext } from "../common/App/ConfigContext";
import {
GlobalState,
InsightsQuery as InsightsDataQuery
-} from "../../common/App/types";
-import { actions } from "../actions";
+} from "../common/App/types";
+import { actions } from "./actions";
import {
InsightsData,
InsightsQuery,
InsightsStatus,
ScopedInsightsQuery,
ViewMode
-} from "../types";
+} from "./types";
interface UseInsightDataProps {
refreshInterval: number;
@@ -29,7 +29,8 @@ const getData = (query: ScopedInsightsQuery, state?: GlobalState) => {
page: query.page,
scopedSpanCodeObjectId: query.scopedSpanCodeObjectId,
showDismissed: query.showDismissed,
- insightViewType: query.insightViewType
+ insightViewType: query.insightViewType,
+ showUnreadOnly: query.showUnreadOnly
};
window.sendMessageToDigma({
diff --git a/src/components/Navigation/KebabMenu/index.tsx b/src/components/Navigation/KebabMenu/index.tsx
index 6a3e346b0..a3d00ed51 100644
--- a/src/components/Navigation/KebabMenu/index.tsx
+++ b/src/components/Navigation/KebabMenu/index.tsx
@@ -1,53 +1,48 @@
import { useContext } from "react";
import { actions as globalActions } from "../../../actions";
-import {
- OpenInstallationWizardPayload,
- SetObservabilityPayload
-} from "../../../types";
+import { OpenInstallationWizardPayload } from "../../../types";
import { isDigmaEngineRunning } from "../../../utils/isDigmaEngineRunning";
import { sendTrackingEvent } from "../../../utils/sendTrackingEvent";
import { ConfigContext } from "../../common/App/ConfigContext";
-import { ToggleSwitch } from "../../common/ToggleSwitch";
import { DigmaLogoFlatIcon } from "../../common/icons/16px/DigmaLogoFlatIcon";
-import { OpenTelemetryLogoIcon } from "../../common/icons/16px/OpenTelemetryLogoIcon";
+import { FourPointedStarIcon } from "../../common/icons/16px/FourPointedStarIcon";
import { LocalEngineIcon } from "../../common/icons/LocalEngineIcon";
import { MenuList } from "../common/MenuList";
-import { ListItemIconContainer } from "../common/MenuList/styles";
import { Popup } from "../common/Popup";
import { trackingEvents } from "../tracking";
-import * as s from "./styles";
+import { OpenDocumentationPayload } from "../types";
import { KebabMenuProps } from "./types";
export const KebabMenu = (props: KebabMenuProps) => {
const config = useContext(ConfigContext);
- const handleLocalEngineClick = () => {
- sendTrackingEvent(trackingEvents.LOCAL_ENGINE_LINK_CLICKED);
+ const handleOnboardingClick = () => {
+ sendTrackingEvent(trackingEvents.ONBOARDING_LINK_CLICKED);
window.sendMessageToDigma({
action: globalActions.OPEN_INSTALLATION_WIZARD,
payload: {
- skipInstallationStep: false
+ skipInstallationStep: true
}
});
props.onClose();
};
- const handleObservabilityChange = (value: boolean) => {
- sendTrackingEvent(trackingEvents.OBSERVABILITY_TOGGLE_SWITCHED, { value });
- window.sendMessageToDigma({
- action: globalActions.SET_OBSERVABILITY,
+ const handleLocalEngineClick = () => {
+ sendTrackingEvent(trackingEvents.LOCAL_ENGINE_LINK_CLICKED);
+ window.sendMessageToDigma({
+ action: globalActions.OPEN_INSTALLATION_WIZARD,
payload: {
- isObservabilityEnabled: value
+ skipInstallationStep: false
}
});
+ props.onClose();
};
- const handleOnboardingClick = () => {
- sendTrackingEvent(trackingEvents.ONBOARDING_LINK_CLICKED);
- window.sendMessageToDigma({
- action: globalActions.OPEN_INSTALLATION_WIZARD,
+ const handleInsightsOverviewClick = () => {
+ window.sendMessageToDigma({
+ action: globalActions.OPEN_DOCUMENTATION,
payload: {
- skipInstallationStep: true
+ page: "environment-types"
}
});
props.onClose();
@@ -59,38 +54,28 @@ export const KebabMenu = (props: KebabMenuProps) => {
showGroupNames={false}
showGroupDividers={true}
items={[
- {
- id: "observability",
- groupName: "settings",
- customContent: (
-
-
-
-
- Observability
-
-
-
-
- )
- },
{
id: "onboarding",
- groupName: "settings",
label: "Digma Onboarding",
icon: ,
onClick: handleOnboardingClick
},
{
id: "localEngine",
- groupName: "settings",
label: "Local Engine",
- icon: ,
+ icon: (
+
+ ),
onClick: handleLocalEngineClick
+ },
+ {
+ id: "insightsOverview",
+ label: "Insights Overview",
+ icon: ,
+ onClick: handleInsightsOverviewClick
}
]}
/>
diff --git a/src/components/Navigation/KebabMenu/styles.ts b/src/components/Navigation/KebabMenu/styles.ts
index 34e0a7223..49b9870f3 100644
--- a/src/components/Navigation/KebabMenu/styles.ts
+++ b/src/components/Navigation/KebabMenu/styles.ts
@@ -1,16 +1,5 @@
import styled from "styled-components";
-export const ObservabilityListItem = styled.div`
- display: flex;
- align-items: center;
- gap: 6px;
- padding: 4px 8px;
-`;
-
-export const ObservabilityToggleSwitchContainer = styled.div`
- margin-left: auto;
-`;
-
export const LocalEngineStatusBadge = styled.div`
border-radius: 1px;
border: 2px solid ${({ theme }) => theme.colors.v3.status.backgroundSuccess};
diff --git a/src/components/Navigation/ScopeBar/ScopeBar.stories.tsx b/src/components/Navigation/ScopeBar/ScopeBar.stories.tsx
index 8a624ff08..f8eb28512 100644
--- a/src/components/Navigation/ScopeBar/ScopeBar.stories.tsx
+++ b/src/components/Navigation/ScopeBar/ScopeBar.stories.tsx
@@ -40,7 +40,8 @@ const mockedScope: Scope = {
},
hasErrors: false,
issuesInsightsCount: 0,
- analyticsInsightsCount: 0
+ analyticsInsightsCount: 0,
+ unreadInsightsCount: 0
};
const mockedCodeContext: CodeContext = {
diff --git a/src/components/Navigation/Tabs/index.tsx b/src/components/Navigation/Tabs/index.tsx
index cca5029e0..0d7dc2d52 100644
--- a/src/components/Navigation/Tabs/index.tsx
+++ b/src/components/Navigation/Tabs/index.tsx
@@ -1,5 +1,6 @@
import { useContext } from "react";
import { getFeatureFlagValue } from "../../../featureFlags";
+import { isNumber } from "../../../typeGuards/isNumber";
import { FeatureFlag } from "../../../types";
import { sendTrackingEvent } from "../../../utils/sendTrackingEvent";
import { ConfigContext } from "../../common/App/ConfigContext";
@@ -56,6 +57,12 @@ export const Tabs = (props: TabsProps) => {
{tabs.map((tab) => {
const tooltipMessage = getTabTooltipMessage(tab, config.scope);
const isDisabled = getIsTabDisabled(tab, config.scope);
+ const isNewIndicatorVisible =
+ tab.hasNewData ||
+ (tab.id === "insights" &&
+ config.scope &&
+ isNumber(config.scope.unreadInsightsCount) &&
+ config.scope.unreadInsightsCount > 0);
return (
{
onClick={() => handleTabClick(tab)}
>
{tab.title}
- {tab.hasNewData && }
+ {isNewIndicatorVisible && }
{config.scope?.hasErrors &&
["errorsDetails", "errors"].includes(tab.id) && (
diff --git a/src/components/Navigation/mockData.ts b/src/components/Navigation/mockData.ts
index fe3296691..afeca65d6 100644
--- a/src/components/Navigation/mockData.ts
+++ b/src/components/Navigation/mockData.ts
@@ -7,7 +7,7 @@ export const mockedViewsData: SetViewsPayload = {
isSelected: true,
hasNewData: false,
isHidden: false,
- title: "Insights",
+ title: "Issues",
id: "insights",
cardName: "insights"
},
diff --git a/src/components/RecentActivity/EnvironmentPanel/index.tsx b/src/components/RecentActivity/EnvironmentPanel/index.tsx
index f031bddd0..ba1fbff6c 100644
--- a/src/components/RecentActivity/EnvironmentPanel/index.tsx
+++ b/src/components/RecentActivity/EnvironmentPanel/index.tsx
@@ -1,23 +1,28 @@
-import { useEffect, useState } from "react";
+import { useContext, useEffect, useState } from "react";
import useDimensions from "react-cool-dimensions";
import { RECENT_ACTIVITY_CONTAINER_ID } from "..";
import { actions as globalActions } from "../../../actions";
import { SLACK_WORKSPACE_URL } from "../../../constants";
+import { SetObservabilityPayload } from "../../../types";
import { openURLInDefaultBrowser } from "../../../utils/openURLInDefaultBrowser";
+import { sendTrackingEvent } from "../../../utils/sendTrackingEvent";
import { MenuList } from "../../Navigation/common/MenuList";
+import { ListItemIconContainer } from "../../Navigation/common/MenuList/styles";
import { Popup } from "../../Navigation/common/Popup";
-import { OpenDocumentationPayload } from "../../Navigation/types";
+import { ConfigContext } from "../../common/App/ConfigContext";
import { NewButton } from "../../common/NewButton";
import { NewPopover } from "../../common/NewPopover";
+import { ToggleSwitch } from "../../common/ToggleSwitch";
import { PlusIcon } from "../../common/icons/12px/PlusIcon";
-import { FourPointedStarIcon } from "../../common/icons/16px/FourPointedStarIcon";
import { HammerIcon } from "../../common/icons/16px/HammerIcon";
+import { OpenTelemetryLogoIcon } from "../../common/icons/16px/OpenTelemetryLogoIcon";
import { SlackLogoIcon } from "../../common/icons/16px/SlackLogoIcon";
import { ChevronIcon } from "../../common/icons/ChevronIcon";
import { DigmaLogoIcon } from "../../common/icons/DigmaLogoIcon";
import { ThreeDotsIcon } from "../../common/icons/ThreeDotsIcon";
import { Direction } from "../../common/icons/types";
import { AddEnvironmentDialog } from "../AddEnvironmentDialog";
+import { trackingEvents } from "../tracking";
import { ExtendedEnvironment } from "../types";
import { EnvironmentTab } from "./EnvironmentTab";
import * as s from "./styles";
@@ -35,6 +40,7 @@ export const EnvironmentPanel = (props: EnvironmentPanelProps) => {
const [isAddEnvironmentDialogOpen, setIsAddEnvironmentDialogOpen] =
useState(false);
const [isKebabMenuOpen, setIsKebabMenuOpen] = useState(false);
+ const config = useContext(ConfigContext);
useEffect(() => {
const entry = environmentListContainerDimensions.entry;
@@ -147,19 +153,22 @@ export const EnvironmentPanel = (props: EnvironmentPanelProps) => {
const boundaryEl =
document.getElementById(RECENT_ACTIVITY_CONTAINER_ID) || undefined;
- const handleTroubleshootingClick = () => {
- window.sendMessageToDigma({
- action: globalActions.OPEN_TROUBLESHOOTING_GUIDE
+ const handleObservabilityChange = (value: boolean) => {
+ sendTrackingEvent(trackingEvents.OBSERVABILITY_TOGGLE_SWITCHED, {
+ value
+ });
+ window.sendMessageToDigma({
+ action: globalActions.SET_OBSERVABILITY,
+ payload: {
+ isObservabilityEnabled: value
+ }
});
setIsKebabMenuOpen(false);
};
- const handleInsightsOverviewClick = () => {
- window.sendMessageToDigma({
- action: globalActions.OPEN_DOCUMENTATION,
- payload: {
- page: "environment-types"
- }
+ const handleTroubleshootingClick = () => {
+ window.sendMessageToDigma({
+ action: globalActions.OPEN_TROUBLESHOOTING_GUIDE
});
setIsKebabMenuOpen(false);
};
@@ -180,12 +189,25 @@ export const EnvironmentPanel = (props: EnvironmentPanelProps) => {
- ),
- onClick: handleInsightsOverviewClick
+ id: "observability",
+ customContent: (
+
+
+
+
+ Observability
+
+
+
+
+ )
},
{
id: "troubleshooting",
diff --git a/src/components/RecentActivity/EnvironmentPanel/styles.ts b/src/components/RecentActivity/EnvironmentPanel/styles.ts
index 955296e10..3fe56a8e7 100644
--- a/src/components/RecentActivity/EnvironmentPanel/styles.ts
+++ b/src/components/RecentActivity/EnvironmentPanel/styles.ts
@@ -79,3 +79,14 @@ export const ButtonsContainer = styled.div`
gap: 4px;
align-items: center;
`;
+
+export const ObservabilityListItem = styled.div`
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ padding: 4px 8px;
+`;
+
+export const ObservabilityToggleSwitchContainer = styled.div`
+ margin-left: auto;
+`;
diff --git a/src/components/RecentActivity/index.tsx b/src/components/RecentActivity/index.tsx
index 9adbb45a2..bf093a932 100644
--- a/src/components/RecentActivity/index.tsx
+++ b/src/components/RecentActivity/index.tsx
@@ -474,7 +474,7 @@ export const RecentActivity = (props: RecentActivityProps) => {
{environmentToDelete && (
-
+
{
)}
{isRegistrationPopupVisible && (
-
+
{
});
};
+ const handleOverlayKeyDown = (e: KeyboardEvent) => {
+ if (e.key === "Escape") {
+ setTestToOpenTicketPopup(undefined);
+ }
+ };
+
const renderContent = () => {
if (isInitialLoading) {
return (
@@ -317,7 +330,7 @@ export const Tests = (props: TestsProps) => {
{renderContent()}
{testToOpenTicketPopup && (
-
+
{/* {config.userRegistrationEmail ? ( */}
{true ? ( // eslint-disable-line no-constant-condition
diff --git a/src/components/common/App/types.ts b/src/components/common/App/types.ts
index 6bd2172fc..8c8147e87 100644
--- a/src/components/common/App/types.ts
+++ b/src/components/common/App/types.ts
@@ -43,14 +43,16 @@ export interface CodeDetails {
codeObjectId: string;
}
+export interface ScopeSpan {
+ displayName: string;
+ spanCodeObjectId: string;
+ methodId?: string;
+ serviceName: string | null;
+ role: "Entry" | "Internal" | "Unknown" | null;
+}
+
export interface Scope {
- span: {
- displayName: string;
- spanCodeObjectId: string;
- methodId?: string;
- serviceName: string | null;
- role: "Entry" | "Internal" | "Unknown" | null;
- } | null;
+ span: ScopeSpan | null;
code: {
relatedCodeDetailsList: CodeDetails[];
codeDetailsList: CodeDetails[];
@@ -58,6 +60,7 @@ export interface Scope {
hasErrors: boolean;
issuesInsightsCount: number;
analyticsInsightsCount: number;
+ unreadInsightsCount: number;
}
export interface InsightsQuery {
@@ -68,6 +71,7 @@ export interface InsightsQuery {
scopedSpanCodeObjectId?: string | null;
showDismissed: boolean;
insightViewType: InsightViewType;
+ showUnreadOnly: boolean;
}
export interface GlobalState {
diff --git a/src/components/common/Pagination/index.tsx b/src/components/common/Pagination/index.tsx
index 9b2c986da..ed555489c 100644
--- a/src/components/common/Pagination/index.tsx
+++ b/src/components/common/Pagination/index.tsx
@@ -1,3 +1,4 @@
+import { useLayoutEffect } from "react";
import { DefaultTheme, useTheme } from "styled-components";
import { ChevronIcon } from "../icons/ChevronIcon";
import { DoubleChevronIcon } from "../icons/DoubleChevronIcon";
@@ -28,24 +29,36 @@ const getPaginationButtonIconColor = (
}
};
-export const Pagination = (props: PaginationProps) => {
+export const Pagination = ({
+ page,
+ pageSize,
+ itemsCount,
+ onPageChange,
+ extendedNavigation
+}: PaginationProps) => {
const theme = useTheme();
- const pageCount = Math.ceil(props.itemsCount / props.pageSize);
+ const pageCount = Math.ceil(itemsCount / pageSize);
- const isPrevDisabled = props.page === 0;
- const isNextDisabled = props.page === pageCount - 1;
+ const isPrevDisabled = page === 0 || pageCount === 0;
+ const isNextDisabled = page === pageCount - 1 || pageCount === 0;
const handleButtonClick = (page: number) => {
- props.onPageChange(page);
+ onPageChange(page);
};
+ useLayoutEffect(() => {
+ if (page > pageCount - 1) {
+ onPageChange(Math.max(pageCount - 1, 0));
+ }
+ }, [page, pageCount, onPageChange]);
+
return (
<>
- {(pageCount > 1 || props.extendedNavigation) && (
+ {(pageCount > 1 || extendedNavigation) && (
- {props.extendedNavigation && (
+ {extendedNavigation && (
handleButtonClick(0)}
@@ -59,7 +72,7 @@ export const Pagination = (props: PaginationProps) => {
)}
handleButtonClick(props.page - 1)}
+ onClick={() => handleButtonClick(page - 1)}
>
{
- {props.page + 1} / {pageCount}
+ {page + 1} / {pageCount}
handleButtonClick(props.page + 1)}
+ onClick={() => handleButtonClick(page + 1)}
>
{
size={14}
/>
- {props.extendedNavigation && (
+ {extendedNavigation && (
handleButtonClick(pageCount - 1)}
diff --git a/src/components/common/icons/12px/CheckmarkCircleIcon.tsx b/src/components/common/icons/12px/CheckmarkCircleIcon.tsx
new file mode 100644
index 000000000..4558b1327
--- /dev/null
+++ b/src/components/common/icons/12px/CheckmarkCircleIcon.tsx
@@ -0,0 +1,34 @@
+import React from "react";
+import { useIconProps } from "../hooks";
+import { IconProps } from "../types";
+
+const CheckmarkCircleIconComponent = (props: IconProps) => {
+ const { size, color } = useIconProps(props);
+
+ return (
+
+ );
+};
+
+export const CheckmarkCircleIcon = React.memo(CheckmarkCircleIconComponent);
diff --git a/src/components/common/icons/LocalEngineIcon.tsx b/src/components/common/icons/LocalEngineIcon.tsx
index 327b4028e..bfb717072 100644
--- a/src/components/common/icons/LocalEngineIcon.tsx
+++ b/src/components/common/icons/LocalEngineIcon.tsx
@@ -2,17 +2,19 @@ import React from "react";
import { useIconProps } from "./hooks";
import { IconProps } from "./types";
-const LocalEngineIconComponent = (
- props: IconProps & { isActive?: boolean }
-) => {
- const { size, color } = useIconProps(props);
+interface LocalEngineIconProps extends IconProps {
+ width?: number;
+ isActive?: boolean;
+}
+
+const LocalEngineIconComponent = (props: LocalEngineIconProps) => {
+ const { color } = useIconProps(props);
const status = props.isActive ? "#6EBD9C" : undefined;
return (