From 9b4b385a6eb2c23f625f7681f26a496e7a8b8cff Mon Sep 17 00:00:00 2001 From: Kyrylo Shmidt Date: Wed, 24 Jul 2024 15:42:24 +0200 Subject: [PATCH 01/13] New navigation --- src/components/Assets/AssetList/index.tsx | 2 +- src/components/Assets/AssetTypeList/index.tsx | 2 +- src/components/Assets/AssetsFilter/index.tsx | 2 +- src/components/Assets/index.tsx | 2 +- src/components/Errors/index.tsx | 2 +- src/components/Highlights/Impact/index.tsx | 2 +- .../Highlights/Impact/useImpactData.ts | 2 +- .../Highlights/Performance/index.tsx | 2 +- .../Performance/usePerformanceData.ts | 2 +- src/components/Highlights/Scaling/index.tsx | 2 +- .../Highlights/Scaling/useScalingData.ts | 2 +- .../Highlights/SpanInfo/useSpanInfoData.ts | 2 +- .../EndpointBottleneckHighlightCard/index.tsx | 2 +- .../index.tsx | 2 +- .../index.tsx | 2 +- .../index.tsx | 2 +- .../index.tsx | 2 +- .../index.tsx | 2 +- .../index.tsx | 2 +- .../HotSpotHighlightCard/index.tsx | 2 +- .../SpaNPlusOneHighlightCard/index.tsx | 2 +- .../index.tsx | 2 +- .../index.tsx | 2 +- .../SpanScalingHighlightCard/index.tsx | 2 +- src/components/Highlights/TopIssues/index.tsx | 2 +- .../Highlights/TopIssues/useTopIssuesData.ts | 2 +- src/components/Highlights/index.tsx | 2 +- .../EnvironmentChip.stories.tsx | 39 ++ .../EnvironmentChip/index.tsx | 29 ++ .../EnvironmentChip/styles.ts | 27 ++ .../EnvironmentChip/types.ts | 11 + .../EnvironmentSelector/index.tsx | 128 ++++++ .../EnvironmentSelector/styles.ts | 19 + .../EnvironmentSelector/types.ts | 5 + .../FilterChip/FilterChip.stories.tsx | 43 ++ .../FilterPanel/FilterChip/index.tsx | 54 +++ .../FilterPanel/FilterChip/styles.ts | 105 +++++ .../FilterPanel/FilterChip/types.ts | 18 + .../FilterPanel.stories.tsx} | 17 +- .../InsightsCatalog/FilterPanel/index.tsx | 83 ++++ .../InsightsCatalog/FilterPanel/styles.ts | 7 + .../InsightsCatalog/FilterPanel/types.ts | 5 + .../InsightsCatalog.stories.tsx | 44 +- .../InsightsCatalog/InsightsPage/index.tsx | 4 +- .../index.tsx | 2 +- .../SpaNPlusOneInsightCard/index.tsx | 2 +- .../index.tsx | 2 +- .../index.tsx | 2 +- ...anQueryOptimizationInsightCard.stories.tsx | 9 - .../index.tsx | 14 +- .../SpanScalingInsightCard/index.tsx | 2 +- .../SpanUsagesInsightCard/index.tsx | 2 +- .../InsightCard/InsightHeader/index.tsx | 2 +- .../insightCards/common/InsightCard/index.tsx | 2 +- .../InsightsCatalog/InsightsStats/index.tsx | 85 ---- .../InsightsCatalog/InsightsStats/styles.ts | 80 ---- .../InsightsCatalog/InsightsStats/types.ts | 12 - .../Insights/InsightsCatalog/index.tsx | 313 ++++++------- .../Insights/InsightsCatalog/styles.ts | 23 +- .../Insights/InsightsCatalog/types.ts | 24 +- .../Insights/Issues/IssuesFilter/index.tsx | 212 ++++++--- .../Insights/Issues/IssuesFilter/types.ts | 12 +- src/components/Insights/Issues/types.ts | 4 +- .../Insights/Issues/useIssuesFilters.ts | 63 +-- src/components/Insights/index.tsx | 160 +++---- .../EndpointBottleneckInsightTicket/index.tsx | 2 + .../index.tsx | 2 + .../index.tsx | 4 +- .../index.tsx | 4 +- .../SpaNPlusOneInsightTicket/index.tsx | 4 +- .../index.tsx | 2 + .../index.tsx | 4 +- .../index.tsx | 4 +- .../SpanScalingInsightTicket/index.tsx | 4 +- .../common/InsightJiraTicket/index.tsx | 14 +- .../common/InsightJiraTicket/types.ts | 1 + .../Insights/insightTickets/types.ts | 1 + src/components/Insights/tracking.ts | 4 +- src/components/Insights/useInsightsData.ts | 299 +++++++------ .../InstallationWizard/InstallStep/index.tsx | 2 +- src/components/Main/index.tsx | 6 +- src/components/Main/useHistory.tsx | 2 +- .../EnvironmentBar/EnvironmentMenu/index.tsx | 34 ++ .../EnvironmentBar/EnvironmentMenu/styles.ts | 6 + .../EnvironmentBar/EnvironmentMenu/types.ts | 6 + .../Navigation/EnvironmentBar/index.tsx | 57 ++- .../Navigation/EnvironmentBar/styles.ts | 71 +-- .../Navigation/EnvironmentBar/types.ts | 1 - .../HistoryNavigationPanel/index.tsx | 28 +- .../HistoryNavigationPanel/styles.ts | 8 +- .../HistoryNavigationPanel/types.ts | 3 + src/components/Navigation/KebabMenu/index.tsx | 29 +- .../Navigation/ScopeBar/ScopeBar.stories.tsx | 11 +- src/components/Navigation/ScopeBar/index.tsx | 49 +-- src/components/Navigation/ScopeBar/styles.ts | 34 +- src/components/Navigation/ScopeBar/types.ts | 4 - src/components/Navigation/Tabs/index.tsx | 2 +- .../Navigation/common/Bar/Bar.stories.tsx | 24 + .../Navigation/common/Bar/index.tsx | 14 + .../Navigation/common/Bar/styles.ts | 13 + src/components/Navigation/common/Bar/types.ts | 6 + .../Navigation/common/IconButton/index.tsx | 3 +- .../Navigation/common/IconButton/styles.ts | 8 +- .../Navigation/common/IconButton/types.ts | 5 + .../Navigation/common/MenuList/index.tsx | 18 +- .../Navigation/common/MenuList/styles.ts | 22 +- .../Navigation/common/MenuList/types.ts | 7 + src/components/Navigation/index.tsx | 411 +++++++++--------- src/components/Navigation/styles.ts | 8 +- src/components/Navigation/tracking.ts | 2 +- src/components/Navigation/types.ts | 4 + .../EnvironmentVariableCode/index.tsx | 2 +- src/components/Tests/TestTicket/index.tsx | 2 +- src/components/Tests/index.tsx | 2 +- src/components/common/App/index.tsx | 2 +- src/components/common/App/types.ts | 5 + src/components/common/Chip/Chip.stories.tsx | 24 + src/components/common/Chip/index.tsx | 15 + src/components/common/Chip/styles.ts | 49 +++ src/components/common/Chip/types.ts | 6 + .../common/EnvironmentIcon/index.tsx | 9 +- .../common/EnvironmentIcon/types.ts | 1 + src/components/common/icons/12px/EyeIcon.tsx | 39 ++ ...sightsIcon.tsx => FourPointedStarIcon.tsx} | 8 +- .../common/icons/12px/GlobeIcon.tsx | 34 ++ .../common/icons/12px/WarningTriangleIcon.tsx | 38 ++ .../icons/16px/{GroupIcon.tsx => EyeIcon.tsx} | 8 +- .../common/icons/16px/FunnelIcon.tsx | 27 ++ src/components/common/icons/16px/HomeIcon.tsx | 7 +- .../common/icons/16px/LogoutIcon.tsx | 26 ++ .../common/icons/LocalEngineIcon.tsx | 47 +- src/components/common/v3/IconButton/index.tsx | 2 + .../NewIconButton/NewIconButton.stories.tsx | 30 ++ .../common/v3/NewIconButton/index.tsx | 36 ++ .../common/v3/NewIconButton/styles.ts | 171 ++++++++ .../common/v3/NewIconButton/types.ts | 18 + src/components/common/v3/Select/index.tsx | 17 +- src/components/common/v3/Select/types.ts | 1 + .../{globalStore.ts => useGlobalStore.ts} | 0 .../Main/stores/useInsightsStore.ts | 82 ++++ src/hooks/useFetchData.ts | 3 +- src/types.ts | 10 +- 142 files changed, 2400 insertions(+), 1287 deletions(-) create mode 100644 src/components/Insights/InsightsCatalog/EnvironmentSelector/EnvironmentChip/EnvironmentChip.stories.tsx create mode 100644 src/components/Insights/InsightsCatalog/EnvironmentSelector/EnvironmentChip/index.tsx create mode 100644 src/components/Insights/InsightsCatalog/EnvironmentSelector/EnvironmentChip/styles.ts create mode 100644 src/components/Insights/InsightsCatalog/EnvironmentSelector/EnvironmentChip/types.ts create mode 100644 src/components/Insights/InsightsCatalog/EnvironmentSelector/index.tsx create mode 100644 src/components/Insights/InsightsCatalog/EnvironmentSelector/styles.ts create mode 100644 src/components/Insights/InsightsCatalog/EnvironmentSelector/types.ts create mode 100644 src/components/Insights/InsightsCatalog/FilterPanel/FilterChip/FilterChip.stories.tsx create mode 100644 src/components/Insights/InsightsCatalog/FilterPanel/FilterChip/index.tsx create mode 100644 src/components/Insights/InsightsCatalog/FilterPanel/FilterChip/styles.ts create mode 100644 src/components/Insights/InsightsCatalog/FilterPanel/FilterChip/types.ts rename src/components/Insights/InsightsCatalog/{InsightsStats/InsightsStats.stories.tsx => FilterPanel/FilterPanel.stories.tsx} (61%) create mode 100644 src/components/Insights/InsightsCatalog/FilterPanel/index.tsx create mode 100644 src/components/Insights/InsightsCatalog/FilterPanel/styles.ts create mode 100644 src/components/Insights/InsightsCatalog/FilterPanel/types.ts delete mode 100644 src/components/Insights/InsightsCatalog/InsightsStats/index.tsx delete mode 100644 src/components/Insights/InsightsCatalog/InsightsStats/styles.ts delete mode 100644 src/components/Insights/InsightsCatalog/InsightsStats/types.ts create mode 100644 src/components/Navigation/EnvironmentBar/EnvironmentMenu/index.tsx create mode 100644 src/components/Navigation/EnvironmentBar/EnvironmentMenu/styles.ts create mode 100644 src/components/Navigation/EnvironmentBar/EnvironmentMenu/types.ts create mode 100644 src/components/Navigation/HistoryNavigationPanel/types.ts create mode 100644 src/components/Navigation/common/Bar/Bar.stories.tsx create mode 100644 src/components/Navigation/common/Bar/index.tsx create mode 100644 src/components/Navigation/common/Bar/styles.ts create mode 100644 src/components/Navigation/common/Bar/types.ts create mode 100644 src/components/common/Chip/Chip.stories.tsx create mode 100644 src/components/common/Chip/index.tsx create mode 100644 src/components/common/Chip/styles.ts create mode 100644 src/components/common/Chip/types.ts create mode 100644 src/components/common/icons/12px/EyeIcon.tsx rename src/components/common/icons/12px/{InsightsIcon.tsx => FourPointedStarIcon.tsx} (75%) create mode 100644 src/components/common/icons/12px/GlobeIcon.tsx create mode 100644 src/components/common/icons/12px/WarningTriangleIcon.tsx rename src/components/common/icons/16px/{GroupIcon.tsx => EyeIcon.tsx} (81%) create mode 100644 src/components/common/icons/16px/FunnelIcon.tsx create mode 100644 src/components/common/icons/16px/LogoutIcon.tsx create mode 100644 src/components/common/v3/NewIconButton/NewIconButton.stories.tsx create mode 100644 src/components/common/v3/NewIconButton/index.tsx create mode 100644 src/components/common/v3/NewIconButton/styles.ts create mode 100644 src/components/common/v3/NewIconButton/types.ts rename src/containers/Main/stores/{globalStore.ts => useGlobalStore.ts} (100%) create mode 100644 src/containers/Main/stores/useInsightsStore.ts diff --git a/src/components/Assets/AssetList/index.tsx b/src/components/Assets/AssetList/index.tsx index f6537c2bd..2019a68ce 100644 --- a/src/components/Assets/AssetList/index.tsx +++ b/src/components/Assets/AssetList/index.tsx @@ -1,7 +1,7 @@ import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { DefaultTheme, useTheme } from "styled-components"; import { DigmaMessageError } from "../../../api/types"; -import { useGlobalStore } from "../../../containers/Main/stores/globalStore"; +import { useGlobalStore } from "../../../containers/Main/stores/useGlobalStore"; import { dispatcher } from "../../../dispatcher"; import { usePrevious } from "../../../hooks/usePrevious"; import { isEnvironment } from "../../../typeGuards/isEnvironment"; diff --git a/src/components/Assets/AssetTypeList/index.tsx b/src/components/Assets/AssetTypeList/index.tsx index 0994e0c18..371b10994 100644 --- a/src/components/Assets/AssetTypeList/index.tsx +++ b/src/components/Assets/AssetTypeList/index.tsx @@ -1,6 +1,6 @@ import { useCallback, useEffect, useRef, useState } from "react"; import { DigmaMessageError } from "../../../api/types"; -import { useGlobalStore } from "../../../containers/Main/stores/globalStore"; +import { useGlobalStore } from "../../../containers/Main/stores/useGlobalStore"; import { dispatcher } from "../../../dispatcher"; import { usePrevious } from "../../../hooks/usePrevious"; import { isEnvironment } from "../../../typeGuards/isEnvironment"; diff --git a/src/components/Assets/AssetsFilter/index.tsx b/src/components/Assets/AssetsFilter/index.tsx index 2251c7ac2..96dc30a32 100644 --- a/src/components/Assets/AssetsFilter/index.tsx +++ b/src/components/Assets/AssetsFilter/index.tsx @@ -1,5 +1,5 @@ import { ComponentType, useEffect, useState } from "react"; -import { useGlobalStore } from "../../../containers/Main/stores/globalStore"; +import { useGlobalStore } from "../../../containers/Main/stores/useGlobalStore"; import { dispatcher } from "../../../dispatcher"; import { usePersistence } from "../../../hooks/usePersistence"; import { usePrevious } from "../../../hooks/usePrevious"; diff --git a/src/components/Assets/index.tsx b/src/components/Assets/index.tsx index a8611fc56..a304b978c 100644 --- a/src/components/Assets/index.tsx +++ b/src/components/Assets/index.tsx @@ -1,6 +1,6 @@ import { useCallback, useEffect, useMemo, useState } from "react"; import { useParams } from "react-router-dom"; -import { useGlobalStore } from "../../containers/Main/stores/globalStore"; +import { useGlobalStore } from "../../containers/Main/stores/useGlobalStore"; import { useDebounce } from "../../hooks/useDebounce"; import { usePrevious } from "../../hooks/usePrevious"; import { sendUserActionTrackingEvent } from "../../utils/actions/sendUserActionTrackingEvent"; diff --git a/src/components/Errors/index.tsx b/src/components/Errors/index.tsx index aecc76254..e791bcaca 100644 --- a/src/components/Errors/index.tsx +++ b/src/components/Errors/index.tsx @@ -1,5 +1,5 @@ import { useParams } from "react-router-dom"; -import { useGlobalStore } from "../../containers/Main/stores/globalStore"; +import { useGlobalStore } from "../../containers/Main/stores/useGlobalStore"; import { useHistory } from "../Main/useHistory"; import { ErrorDetails } from "./ErrorDetails"; import { ErrorsList } from "./ErrorsList"; diff --git a/src/components/Highlights/Impact/index.tsx b/src/components/Highlights/Impact/index.tsx index 4ac6f3584..7afa431fa 100644 --- a/src/components/Highlights/Impact/index.tsx +++ b/src/components/Highlights/Impact/index.tsx @@ -1,7 +1,7 @@ import { Row, createColumnHelper } from "@tanstack/react-table"; import { useEffect } from "react"; import { PERFORMANCE_IMPACT_DOCUMENTATION_URL } from "../../../constants"; -import { useGlobalStore } from "../../../containers/Main/stores/globalStore"; +import { useGlobalStore } from "../../../containers/Main/stores/useGlobalStore"; import { openURLInDefaultBrowser } from "../../../utils/actions/openURLInDefaultBrowser"; import { sendUserActionTrackingEvent } from "../../../utils/actions/sendUserActionTrackingEvent"; import { SCOPE_CHANGE_EVENTS } from "../../Main/types"; diff --git a/src/components/Highlights/Impact/useImpactData.ts b/src/components/Highlights/Impact/useImpactData.ts index 1f6d91386..61d554ff7 100644 --- a/src/components/Highlights/Impact/useImpactData.ts +++ b/src/components/Highlights/Impact/useImpactData.ts @@ -1,5 +1,5 @@ import { useCallback, useEffect, useRef, useState } from "react"; -import { useGlobalStore } from "../../../containers/Main/stores/globalStore"; +import { useGlobalStore } from "../../../containers/Main/stores/useGlobalStore"; import { dispatcher } from "../../../dispatcher"; import { usePrevious } from "../../../hooks/usePrevious"; import { actions as mainActions } from "../../Main/actions"; diff --git a/src/components/Highlights/Performance/index.tsx b/src/components/Highlights/Performance/index.tsx index ebc95b4bc..c035c942d 100644 --- a/src/components/Highlights/Performance/index.tsx +++ b/src/components/Highlights/Performance/index.tsx @@ -1,6 +1,6 @@ import { Row, createColumnHelper } from "@tanstack/react-table"; import { useEffect, useState } from "react"; -import { useGlobalStore } from "../../../containers/Main/stores/globalStore"; +import { useGlobalStore } from "../../../containers/Main/stores/useGlobalStore"; import { usePrevious } from "../../../hooks/usePrevious"; import { isBoolean } from "../../../typeGuards/isBoolean"; import { sendUserActionTrackingEvent } from "../../../utils/actions/sendUserActionTrackingEvent"; diff --git a/src/components/Highlights/Performance/usePerformanceData.ts b/src/components/Highlights/Performance/usePerformanceData.ts index b74ff768c..c8f03d0f9 100644 --- a/src/components/Highlights/Performance/usePerformanceData.ts +++ b/src/components/Highlights/Performance/usePerformanceData.ts @@ -1,5 +1,5 @@ import { useCallback, useEffect, useRef, useState } from "react"; -import { useGlobalStore } from "../../../containers/Main/stores/globalStore"; +import { useGlobalStore } from "../../../containers/Main/stores/useGlobalStore"; import { dispatcher } from "../../../dispatcher"; import { usePrevious } from "../../../hooks/usePrevious"; import { actions as mainActions } from "../../Main/actions"; diff --git a/src/components/Highlights/Scaling/index.tsx b/src/components/Highlights/Scaling/index.tsx index 1e09e4307..106994361 100644 --- a/src/components/Highlights/Scaling/index.tsx +++ b/src/components/Highlights/Scaling/index.tsx @@ -1,7 +1,7 @@ import { Row, createColumnHelper } from "@tanstack/react-table"; import { useEffect } from "react"; import { SCALING_ISSUE_DOCUMENTATION_URL } from "../../../constants"; -import { useGlobalStore } from "../../../containers/Main/stores/globalStore"; +import { useGlobalStore } from "../../../containers/Main/stores/useGlobalStore"; import { openURLInDefaultBrowser } from "../../../utils/actions/openURLInDefaultBrowser"; import { sendUserActionTrackingEvent } from "../../../utils/actions/sendUserActionTrackingEvent"; import { getDurationString } from "../../../utils/getDurationString"; diff --git a/src/components/Highlights/Scaling/useScalingData.ts b/src/components/Highlights/Scaling/useScalingData.ts index 6b6202032..1b8b6a8c6 100644 --- a/src/components/Highlights/Scaling/useScalingData.ts +++ b/src/components/Highlights/Scaling/useScalingData.ts @@ -1,5 +1,5 @@ import { useCallback, useEffect, useRef, useState } from "react"; -import { useGlobalStore } from "../../../containers/Main/stores/globalStore"; +import { useGlobalStore } from "../../../containers/Main/stores/useGlobalStore"; import { dispatcher } from "../../../dispatcher"; import { usePrevious } from "../../../hooks/usePrevious"; import { actions as mainActions } from "../../Main/actions"; diff --git a/src/components/Highlights/SpanInfo/useSpanInfoData.ts b/src/components/Highlights/SpanInfo/useSpanInfoData.ts index 9b6668b1d..ead9f0185 100644 --- a/src/components/Highlights/SpanInfo/useSpanInfoData.ts +++ b/src/components/Highlights/SpanInfo/useSpanInfoData.ts @@ -1,5 +1,5 @@ import { useCallback, useEffect, useRef, useState } from "react"; -import { useGlobalStore } from "../../../containers/Main/stores/globalStore"; +import { useGlobalStore } from "../../../containers/Main/stores/useGlobalStore"; import { dispatcher } from "../../../dispatcher"; import { usePrevious } from "../../../hooks/usePrevious"; import { actions as mainActions } from "../../Main/actions"; diff --git a/src/components/Highlights/TopIssues/highlightCards/EndpointBottleneckHighlightCard/index.tsx b/src/components/Highlights/TopIssues/highlightCards/EndpointBottleneckHighlightCard/index.tsx index 1a0f7762b..e37752638 100644 --- a/src/components/Highlights/TopIssues/highlightCards/EndpointBottleneckHighlightCard/index.tsx +++ b/src/components/Highlights/TopIssues/highlightCards/EndpointBottleneckHighlightCard/index.tsx @@ -1,5 +1,5 @@ import { Row, createColumnHelper } from "@tanstack/react-table"; -import { useGlobalStore } from "../../../../../containers/Main/stores/globalStore"; +import { useGlobalStore } from "../../../../../containers/Main/stores/useGlobalStore"; import { Duration } from "../../../../../globals"; import { sendUserActionTrackingEvent } from "../../../../../utils/actions/sendUserActionTrackingEvent"; import { getDurationString } from "../../../../../utils/getDurationString"; diff --git a/src/components/Highlights/TopIssues/highlightCards/EndpointChattyApiV2HighlightCard/index.tsx b/src/components/Highlights/TopIssues/highlightCards/EndpointChattyApiV2HighlightCard/index.tsx index 5403960ec..0487a138d 100644 --- a/src/components/Highlights/TopIssues/highlightCards/EndpointChattyApiV2HighlightCard/index.tsx +++ b/src/components/Highlights/TopIssues/highlightCards/EndpointChattyApiV2HighlightCard/index.tsx @@ -1,5 +1,5 @@ import { Row, createColumnHelper } from "@tanstack/react-table"; -import { useGlobalStore } from "../../../../../containers/Main/stores/globalStore"; +import { useGlobalStore } from "../../../../../containers/Main/stores/useGlobalStore"; import { sendUserActionTrackingEvent } from "../../../../../utils/actions/sendUserActionTrackingEvent"; import { SCOPE_CHANGE_EVENTS } from "../../../../Main/types"; import { Table } from "../../../common/Table"; diff --git a/src/components/Highlights/TopIssues/highlightCards/EndpointHighNumberOfQueriesHighlightCard/index.tsx b/src/components/Highlights/TopIssues/highlightCards/EndpointHighNumberOfQueriesHighlightCard/index.tsx index d74b2ce3a..d21ce7f7c 100644 --- a/src/components/Highlights/TopIssues/highlightCards/EndpointHighNumberOfQueriesHighlightCard/index.tsx +++ b/src/components/Highlights/TopIssues/highlightCards/EndpointHighNumberOfQueriesHighlightCard/index.tsx @@ -1,5 +1,5 @@ import { Row, createColumnHelper } from "@tanstack/react-table"; -import { useGlobalStore } from "../../../../../containers/Main/stores/globalStore"; +import { useGlobalStore } from "../../../../../containers/Main/stores/useGlobalStore"; import { sendUserActionTrackingEvent } from "../../../../../utils/actions/sendUserActionTrackingEvent"; import { SCOPE_CHANGE_EVENTS } from "../../../../Main/types"; import { Table } from "../../../common/Table"; diff --git a/src/components/Highlights/TopIssues/highlightCards/EndpointQueryOptimizationV2HighlightCard/index.tsx b/src/components/Highlights/TopIssues/highlightCards/EndpointQueryOptimizationV2HighlightCard/index.tsx index 790dc4412..493e1fe0d 100644 --- a/src/components/Highlights/TopIssues/highlightCards/EndpointQueryOptimizationV2HighlightCard/index.tsx +++ b/src/components/Highlights/TopIssues/highlightCards/EndpointQueryOptimizationV2HighlightCard/index.tsx @@ -1,5 +1,5 @@ import { Row, createColumnHelper } from "@tanstack/react-table"; -import { useGlobalStore } from "../../../../../containers/Main/stores/globalStore"; +import { useGlobalStore } from "../../../../../containers/Main/stores/useGlobalStore"; import { sendUserActionTrackingEvent } from "../../../../../utils/actions/sendUserActionTrackingEvent"; import { getDurationString } from "../../../../../utils/getDurationString"; import { SCOPE_CHANGE_EVENTS } from "../../../../Main/types"; diff --git a/src/components/Highlights/TopIssues/highlightCards/EndpointSessionInViewHighlightCard/index.tsx b/src/components/Highlights/TopIssues/highlightCards/EndpointSessionInViewHighlightCard/index.tsx index c78f8cde4..e7b66b4a5 100644 --- a/src/components/Highlights/TopIssues/highlightCards/EndpointSessionInViewHighlightCard/index.tsx +++ b/src/components/Highlights/TopIssues/highlightCards/EndpointSessionInViewHighlightCard/index.tsx @@ -1,5 +1,5 @@ import { Row, createColumnHelper } from "@tanstack/react-table"; -import { useGlobalStore } from "../../../../../containers/Main/stores/globalStore"; +import { useGlobalStore } from "../../../../../containers/Main/stores/useGlobalStore"; import { sendUserActionTrackingEvent } from "../../../../../utils/actions/sendUserActionTrackingEvent"; import { SCOPE_CHANGE_EVENTS } from "../../../../Main/types"; import { Table } from "../../../common/Table"; diff --git a/src/components/Highlights/TopIssues/highlightCards/EndpointSlowdownSourceHighlightCard/index.tsx b/src/components/Highlights/TopIssues/highlightCards/EndpointSlowdownSourceHighlightCard/index.tsx index 454b53095..f9fd09132 100644 --- a/src/components/Highlights/TopIssues/highlightCards/EndpointSlowdownSourceHighlightCard/index.tsx +++ b/src/components/Highlights/TopIssues/highlightCards/EndpointSlowdownSourceHighlightCard/index.tsx @@ -1,5 +1,5 @@ import { Row, createColumnHelper } from "@tanstack/react-table"; -import { useGlobalStore } from "../../../../../containers/Main/stores/globalStore"; +import { useGlobalStore } from "../../../../../containers/Main/stores/useGlobalStore"; import { sendUserActionTrackingEvent } from "../../../../../utils/actions/sendUserActionTrackingEvent"; import { getDurationString } from "../../../../../utils/getDurationString"; import { SCOPE_CHANGE_EVENTS } from "../../../../Main/types"; diff --git a/src/components/Highlights/TopIssues/highlightCards/EndpointSpanNPlusOneHighlightCard/index.tsx b/src/components/Highlights/TopIssues/highlightCards/EndpointSpanNPlusOneHighlightCard/index.tsx index ae2ea9e22..4459d4f2c 100644 --- a/src/components/Highlights/TopIssues/highlightCards/EndpointSpanNPlusOneHighlightCard/index.tsx +++ b/src/components/Highlights/TopIssues/highlightCards/EndpointSpanNPlusOneHighlightCard/index.tsx @@ -1,5 +1,5 @@ import { Row, createColumnHelper } from "@tanstack/react-table"; -import { useGlobalStore } from "../../../../../containers/Main/stores/globalStore"; +import { useGlobalStore } from "../../../../../containers/Main/stores/useGlobalStore"; import { Duration } from "../../../../../globals"; import { sendUserActionTrackingEvent } from "../../../../../utils/actions/sendUserActionTrackingEvent"; import { getDurationString } from "../../../../../utils/getDurationString"; diff --git a/src/components/Highlights/TopIssues/highlightCards/HotSpotHighlightCard/index.tsx b/src/components/Highlights/TopIssues/highlightCards/HotSpotHighlightCard/index.tsx index 8875993a2..7b2b70198 100644 --- a/src/components/Highlights/TopIssues/highlightCards/HotSpotHighlightCard/index.tsx +++ b/src/components/Highlights/TopIssues/highlightCards/HotSpotHighlightCard/index.tsx @@ -1,5 +1,5 @@ import { Row, createColumnHelper } from "@tanstack/react-table"; -import { useGlobalStore } from "../../../../../containers/Main/stores/globalStore"; +import { useGlobalStore } from "../../../../../containers/Main/stores/useGlobalStore"; import { sendUserActionTrackingEvent } from "../../../../../utils/actions/sendUserActionTrackingEvent"; import { SCOPE_CHANGE_EVENTS } from "../../../../Main/types"; import { Table } from "../../../common/Table"; diff --git a/src/components/Highlights/TopIssues/highlightCards/SpaNPlusOneHighlightCard/index.tsx b/src/components/Highlights/TopIssues/highlightCards/SpaNPlusOneHighlightCard/index.tsx index dd8a1d770..e3ef3fccc 100644 --- a/src/components/Highlights/TopIssues/highlightCards/SpaNPlusOneHighlightCard/index.tsx +++ b/src/components/Highlights/TopIssues/highlightCards/SpaNPlusOneHighlightCard/index.tsx @@ -1,5 +1,5 @@ import { Row, createColumnHelper } from "@tanstack/react-table"; -import { useGlobalStore } from "../../../../../containers/Main/stores/globalStore"; +import { useGlobalStore } from "../../../../../containers/Main/stores/useGlobalStore"; import { Duration } from "../../../../../globals"; import { sendUserActionTrackingEvent } from "../../../../../utils/actions/sendUserActionTrackingEvent"; import { getDurationString } from "../../../../../utils/getDurationString"; diff --git a/src/components/Highlights/TopIssues/highlightCards/SpanEndpointBottleneckHighlightCard/index.tsx b/src/components/Highlights/TopIssues/highlightCards/SpanEndpointBottleneckHighlightCard/index.tsx index 37bf7c2fa..e40ac9b91 100644 --- a/src/components/Highlights/TopIssues/highlightCards/SpanEndpointBottleneckHighlightCard/index.tsx +++ b/src/components/Highlights/TopIssues/highlightCards/SpanEndpointBottleneckHighlightCard/index.tsx @@ -1,5 +1,5 @@ import { Row, createColumnHelper } from "@tanstack/react-table"; -import { useGlobalStore } from "../../../../../containers/Main/stores/globalStore"; +import { useGlobalStore } from "../../../../../containers/Main/stores/useGlobalStore"; import { Duration } from "../../../../../globals"; import { sendUserActionTrackingEvent } from "../../../../../utils/actions/sendUserActionTrackingEvent"; import { getDurationString } from "../../../../../utils/getDurationString"; diff --git a/src/components/Highlights/TopIssues/highlightCards/SpanQueryOptimizationHighlightCard/index.tsx b/src/components/Highlights/TopIssues/highlightCards/SpanQueryOptimizationHighlightCard/index.tsx index 80b08910b..1528373bc 100644 --- a/src/components/Highlights/TopIssues/highlightCards/SpanQueryOptimizationHighlightCard/index.tsx +++ b/src/components/Highlights/TopIssues/highlightCards/SpanQueryOptimizationHighlightCard/index.tsx @@ -1,5 +1,5 @@ import { Row, createColumnHelper } from "@tanstack/react-table"; -import { useGlobalStore } from "../../../../../containers/Main/stores/globalStore"; +import { useGlobalStore } from "../../../../../containers/Main/stores/useGlobalStore"; import { Duration } from "../../../../../globals"; import { sendUserActionTrackingEvent } from "../../../../../utils/actions/sendUserActionTrackingEvent"; import { getDurationString } from "../../../../../utils/getDurationString"; diff --git a/src/components/Highlights/TopIssues/highlightCards/SpanScalingHighlightCard/index.tsx b/src/components/Highlights/TopIssues/highlightCards/SpanScalingHighlightCard/index.tsx index 7611e4467..40e9d286c 100644 --- a/src/components/Highlights/TopIssues/highlightCards/SpanScalingHighlightCard/index.tsx +++ b/src/components/Highlights/TopIssues/highlightCards/SpanScalingHighlightCard/index.tsx @@ -1,5 +1,5 @@ import { Row, createColumnHelper } from "@tanstack/react-table"; -import { useGlobalStore } from "../../../../../containers/Main/stores/globalStore"; +import { useGlobalStore } from "../../../../../containers/Main/stores/useGlobalStore"; import { sendUserActionTrackingEvent } from "../../../../../utils/actions/sendUserActionTrackingEvent"; import { SCOPE_CHANGE_EVENTS } from "../../../../Main/types"; import { Table } from "../../../common/Table"; diff --git a/src/components/Highlights/TopIssues/index.tsx b/src/components/Highlights/TopIssues/index.tsx index 83ced6bf4..7ff650aff 100644 --- a/src/components/Highlights/TopIssues/index.tsx +++ b/src/components/Highlights/TopIssues/index.tsx @@ -1,6 +1,6 @@ import { Fragment, useEffect, useState } from "react"; import { v4 as uuidv4 } from "uuid"; -import { useGlobalStore } from "../../../containers/Main/stores/globalStore"; +import { useGlobalStore } from "../../../containers/Main/stores/useGlobalStore"; import { usePagination } from "../../../hooks/usePagination"; import { usePrevious } from "../../../hooks/usePrevious"; import { CrossCircleIcon } from "../../common/icons/16px/CrossCircleIcon"; diff --git a/src/components/Highlights/TopIssues/useTopIssuesData.ts b/src/components/Highlights/TopIssues/useTopIssuesData.ts index 91a141228..d2f70de19 100644 --- a/src/components/Highlights/TopIssues/useTopIssuesData.ts +++ b/src/components/Highlights/TopIssues/useTopIssuesData.ts @@ -1,5 +1,5 @@ import { useCallback, useEffect, useRef, useState } from "react"; -import { useGlobalStore } from "../../../containers/Main/stores/globalStore"; +import { useGlobalStore } from "../../../containers/Main/stores/useGlobalStore"; import { dispatcher } from "../../../dispatcher"; import { usePrevious } from "../../../hooks/usePrevious"; import { actions as mainActions } from "../../Main/actions"; diff --git a/src/components/Highlights/index.tsx b/src/components/Highlights/index.tsx index 87520d8f9..f77733abc 100644 --- a/src/components/Highlights/index.tsx +++ b/src/components/Highlights/index.tsx @@ -1,4 +1,4 @@ -import { useGlobalStore } from "../../containers/Main/stores/globalStore"; +import { useGlobalStore } from "../../containers/Main/stores/useGlobalStore"; import { getFeatureFlagValue } from "../../featureFlags"; import { FeatureFlag } from "../../types"; import { Impact } from "./Impact"; diff --git a/src/components/Insights/InsightsCatalog/EnvironmentSelector/EnvironmentChip/EnvironmentChip.stories.tsx b/src/components/Insights/InsightsCatalog/EnvironmentSelector/EnvironmentChip/EnvironmentChip.stories.tsx new file mode 100644 index 000000000..ff268d8c7 --- /dev/null +++ b/src/components/Insights/InsightsCatalog/EnvironmentSelector/EnvironmentChip/EnvironmentChip.stories.tsx @@ -0,0 +1,39 @@ +import { Meta, StoryObj } from "@storybook/react"; + +import { EnvironmentChip } from "."; +import { Environment } from "../../../../common/App/types"; + +// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction +const meta: Meta = { + title: "Insights/InsightsCatalog/EnvironmentSelector/EnvironmentChip", + component: EnvironmentChip, + 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; + +const mockedEnvironment: Environment = { + type: "Public", + id: "env1", + name: "Env1" +}; + +// More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args +export const Default: Story = { + args: { + environment: mockedEnvironment, + isActive: false + } +}; + +export const Active: Story = { + args: { + environment: mockedEnvironment, + isActive: true + } +}; diff --git a/src/components/Insights/InsightsCatalog/EnvironmentSelector/EnvironmentChip/index.tsx b/src/components/Insights/InsightsCatalog/EnvironmentSelector/EnvironmentChip/index.tsx new file mode 100644 index 000000000..2c799fc5d --- /dev/null +++ b/src/components/Insights/InsightsCatalog/EnvironmentSelector/EnvironmentChip/index.tsx @@ -0,0 +1,29 @@ +import { EnvironmentIcon } from "../../../../common/EnvironmentIcon"; +import { Tooltip } from "../../../../common/v3/Tooltip"; +import * as s from "./styles"; +import { EnvironmentChipProps } from "./types"; + +export const EnvironmentChip = ({ + environment, + onClick, + isActive +}: EnvironmentChipProps) => { + const handleClick = () => { + if (!isActive) { + onClick(environment.id); + } + }; + + const environmentName = environment.name; + + return ( + + + + + + {environmentName} + + + ); +}; diff --git a/src/components/Insights/InsightsCatalog/EnvironmentSelector/EnvironmentChip/styles.ts b/src/components/Insights/InsightsCatalog/EnvironmentSelector/EnvironmentChip/styles.ts new file mode 100644 index 000000000..6dccb6ea0 --- /dev/null +++ b/src/components/Insights/InsightsCatalog/EnvironmentSelector/EnvironmentChip/styles.ts @@ -0,0 +1,27 @@ +import styled, { css } from "styled-components"; +import { Chip } from "../../../../common/Chip"; +import { activeStyles } from "../../../../common/Chip/styles"; +import { StyledChipProps } from "./types"; +export const StyledChip = styled(Chip)` + gap: 4px; + + ${({ isActive }) => (isActive ? activeStyles : "")} + ${({ isActive }) => + isActive + ? css` + cursor: initial; + ` + : ""} +`; + +export const IconContainer = styled.div` + display: flex; + color: ${({ theme }) => theme.colors.v3.icon.secondary}; + opacity: 0.5; +`; + +export const Name = styled.span` + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +`; diff --git a/src/components/Insights/InsightsCatalog/EnvironmentSelector/EnvironmentChip/types.ts b/src/components/Insights/InsightsCatalog/EnvironmentSelector/EnvironmentChip/types.ts new file mode 100644 index 000000000..f97888d8a --- /dev/null +++ b/src/components/Insights/InsightsCatalog/EnvironmentSelector/EnvironmentChip/types.ts @@ -0,0 +1,11 @@ +import { Environment } from "../../../../common/App/types"; + +export interface EnvironmentChipProps { + environment: Environment; + onClick: (environment: string) => void; + isActive: boolean; +} + +export interface StyledChipProps { + isActive: boolean; +} diff --git a/src/components/Insights/InsightsCatalog/EnvironmentSelector/index.tsx b/src/components/Insights/InsightsCatalog/EnvironmentSelector/index.tsx new file mode 100644 index 000000000..b575de1dc --- /dev/null +++ b/src/components/Insights/InsightsCatalog/EnvironmentSelector/index.tsx @@ -0,0 +1,128 @@ +import { useState } from "react"; +import useDimensions from "react-cool-dimensions"; +import { useGlobalStore } from "../../../../containers/Main/stores/useGlobalStore"; +import { changeScope } from "../../../../utils/actions/changeScope"; +import { sendUserActionTrackingEvent } from "../../../../utils/actions/sendUserActionTrackingEvent"; +import { Environment } from "../../../common/App/types"; +import { NewPopover } from "../../../common/NewPopover"; +import { NewButton } from "../../../common/v3/NewButton"; +import { EnvironmentMenu } from "../../../Navigation/EnvironmentBar/EnvironmentMenu"; +import { trackingEvents } from "../../tracking"; +import { EnvironmentChip } from "./EnvironmentChip"; +import * as s from "./styles"; +import { EnvironmentSelectorProps } from "./types"; + +const ENVIRONMENT_CHIP_COUNT = 3; + +const getSlidingWindow = (arr: T[], start: number, length: number) => { + const result = []; + const arrLength = arr.length; + + for (let i = 0; i < length; i++) { + const currentIndex = (start + i + arrLength) % arrLength; + result.push(arr[currentIndex]); + } + + return result; +}; + +export const EnvironmentSelector = ({ + environments +}: EnvironmentSelectorProps) => { + const scope = useGlobalStore.use.scope(); + const environment = useGlobalStore.use.environment(); + const [isMenuOpen, setIsMenuOpen] = useState(false); + const { observe, width } = useDimensions(); + + if (environments.length < 2) { + return null; + } + + const changeEnvironment = (environmentId: string) => { + sendUserActionTrackingEvent(trackingEvents.ENVIRONMENT_SELECTED); + + changeScope({ + span: scope?.span + ? { + spanCodeObjectId: scope.span.spanCodeObjectId + } + : null, + environmentId: environmentId + }); + }; + + const handleMenuItemClick = (environment: Environment) => { + setIsMenuOpen(false); + changeEnvironment(environment.id); + }; + + const handleEnvironmentChipClick = (environmentId: string) => { + changeEnvironment(environmentId); + }; + + const handleEnvironmentMenuOpenChange = (isOpen: boolean) => { + setIsMenuOpen(isOpen); + }; + + const handleEnvironmentMenuButtonClick = () => { + sendUserActionTrackingEvent(trackingEvents.ENVIRONMENT_MENU_BUTTON_CLICKED); + + setIsMenuOpen(!isMenuOpen); + }; + + const environmentIndex = environments.findIndex( + (x) => x.id === environment?.id + ); + + const environmentsWithChips = getSlidingWindow( + environments, + environmentIndex - 1, + ENVIRONMENT_CHIP_COUNT + ); + + const renderEnvironmentMenuButton = () => ( + + ); + + return ( + + + {environmentsWithChips.map((x) => ( + + ))} + + {environments.length > ENVIRONMENT_CHIP_COUNT && ( + <> + {/* // TODO: refactor this to use only popover */} + {isMenuOpen ? ( + + } + onOpenChange={handleEnvironmentMenuOpenChange} + isOpen={isMenuOpen} + placement={"bottom-end"} + width={width} + > + {renderEnvironmentMenuButton()} + + ) : ( + renderEnvironmentMenuButton() + )} + + )} + + ); +}; diff --git a/src/components/Insights/InsightsCatalog/EnvironmentSelector/styles.ts b/src/components/Insights/InsightsCatalog/EnvironmentSelector/styles.ts new file mode 100644 index 000000000..7bc77e4d7 --- /dev/null +++ b/src/components/Insights/InsightsCatalog/EnvironmentSelector/styles.ts @@ -0,0 +1,19 @@ +import styled from "styled-components"; + +export const Container = styled.div` + display: flex; + gap: 4px; + overflow: hidden; + flex-grow: 1; +`; + +export const EnvironmentsContainer = styled.div` + display: flex; + gap: 4px; + overflow: hidden; + flex-grow: 1; + + & > * { + flex: 1 1 0; + } +`; diff --git a/src/components/Insights/InsightsCatalog/EnvironmentSelector/types.ts b/src/components/Insights/InsightsCatalog/EnvironmentSelector/types.ts new file mode 100644 index 000000000..51a8eb487 --- /dev/null +++ b/src/components/Insights/InsightsCatalog/EnvironmentSelector/types.ts @@ -0,0 +1,5 @@ +import { Environment } from "../../../common/App/types"; + +export interface EnvironmentSelectorProps { + environments: Environment[]; +} diff --git a/src/components/Insights/InsightsCatalog/FilterPanel/FilterChip/FilterChip.stories.tsx b/src/components/Insights/InsightsCatalog/FilterPanel/FilterChip/FilterChip.stories.tsx new file mode 100644 index 000000000..afb414ab1 --- /dev/null +++ b/src/components/Insights/InsightsCatalog/FilterPanel/FilterChip/FilterChip.stories.tsx @@ -0,0 +1,43 @@ +import { Meta, StoryObj } from "@storybook/react"; +import { FilterChip } from "."; + +// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction +const meta: Meta = { + title: "Insights/InsightsCatalog/FilterPanel/FilterChip", + component: FilterChip, + 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 All: Story = { + args: { + disabled: false, + selected: false, + type: "all", + count: 100 + } +}; + +export const Critical: Story = { + args: { + disabled: false, + selected: false, + type: "critical", + count: 100 + } +}; + +export const Unread: Story = { + args: { + disabled: false, + selected: false, + type: "unread", + count: 12 + } +}; diff --git a/src/components/Insights/InsightsCatalog/FilterPanel/FilterChip/index.tsx b/src/components/Insights/InsightsCatalog/FilterPanel/FilterChip/index.tsx new file mode 100644 index 000000000..7967f53ad --- /dev/null +++ b/src/components/Insights/InsightsCatalog/FilterPanel/FilterChip/index.tsx @@ -0,0 +1,54 @@ +import { ComponentType } from "react"; +import { isNumber } from "../../../../../typeGuards/isNumber"; +import { Tooltip } from "../../../../common/v3/Tooltip"; +import * as s from "./styles"; +import { FilterChipComponentProps, FilterChipProps, FilterType } from "./types"; + +const filtersData: Record< + FilterType, + { + Component: ComponentType; + label: string; + } +> = { + critical: { + Component: s.CriticalFilterChip, + label: "Critical" + }, + unread: { + Component: s.UnreadFilterChip, + label: "Unread" + }, + all: { + Component: s.FilterChip, + label: "All" + } +}; + +export const FilterChip = ({ + disabled, + selected, + type, + onClick, + count +}: FilterChipProps) => { + const { Component, label } = filtersData[type]; + return ( + + + {label} + {isNumber(count) ? ( + {count} + ) : ( + + N/A + + )} + + + ); +}; diff --git a/src/components/Insights/InsightsCatalog/FilterPanel/FilterChip/styles.ts b/src/components/Insights/InsightsCatalog/FilterPanel/FilterChip/styles.ts new file mode 100644 index 000000000..93fc1b8b2 --- /dev/null +++ b/src/components/Insights/InsightsCatalog/FilterPanel/FilterChip/styles.ts @@ -0,0 +1,105 @@ +import styled, { css } from "styled-components"; +import { footnoteRegularTypography } from "../../../../common/App/typographies"; +import { Chip } from "../../../../common/Chip"; +import { activeStyles } from "../../../../common/Chip/styles"; +import { FilterChipComponentProps } from "./types"; + +export const StatCounter = styled.div` + ${footnoteRegularTypography} + + height: 16px; + display: flex; + justify-content: center; + padding: 0 4px; + color: ${({ theme }) => theme.colors.v3.text.primary}; + border-radius: 2px; + background: ${({ theme }) => theme.colors.v3.surface.highlight}; +`; + +const filterChipActiveStyles = css` + ${activeStyles} + + & ${StatCounter} { + background: ${({ theme }) => theme.colors.v3.surface.gray}; + } +`; + +export const FilterChip = styled(Chip)` + gap: 4px; + + ${({ $selected }) => ($selected ? filterChipActiveStyles : "")} + + &:disabled { + background: transparent; + + & ${StatCounter} { + color: ${({ theme }) => theme.colors.v3.text.disabled}; + background: ${({ theme }) => theme.colors.v3.surface.sidePanelHeader}; + } + } + + &:hover:enabled { + & ${StatCounter} { + background: ${({ theme }) => theme.colors.v3.surface.gray}; + } + } + + &:active:enabled { + ${filterChipActiveStyles} + } +`; + +const criticalFilterChipActiveStyles = css` + border: 1px solid ${({ theme }) => theme.colors.v3.status.high}; + background: ${({ theme }) => theme.colors.v3.pieChart.darkRed}; + + & ${StatCounter} { + background: ${({ theme }) => theme.colors.v3.status.high}; + } +`; + +export const CriticalFilterChip = styled(FilterChip)` + border: 1px solid ${({ theme }) => theme.colors.v3.status.backgroundHigh}; + background: ${({ theme }) => theme.colors.v3.pieChart.darkRed}; + + & ${StatCounter} { + background: ${({ theme }) => theme.colors.v3.status.high}; + } + + ${({ $selected }) => ($selected ? criticalFilterChipActiveStyles : "")} + + &:hover:enabled { + border: 1px solid ${({ theme }) => theme.colors.v3.status.high}; + background: ${({ theme }) => theme.colors.v3.status.backgroundHigh}; + + & ${StatCounter} { + background: ${({ theme }) => theme.colors.v3.status.high}; + } + } + + &:active:enabled { + ${criticalFilterChipActiveStyles} + } +`; + +const unreadFilterChipActiveStyles = css` + border: 1px solid ${({ theme }) => theme.colors.v3.stroke.brandPrimary}; + background: ${({ theme }) => theme.colors.v3.surface.brandDarkest}; + + & ${StatCounter} { + background: ${({ theme }) => theme.colors.v3.surface.brandTertiary}; + } +`; + +export const UnreadFilterChip = styled(FilterChip)` + ${({ $selected }) => ($selected ? unreadFilterChipActiveStyles : "")} + + &:hover:enabled { + border: 1px solid ${({ theme }) => theme.colors.v3.stroke.brandPrimary}; + background: ${({ theme }) => theme.colors.v3.surface.brandDark}; + } + + &:active:enabled { + ${unreadFilterChipActiveStyles} + } +`; diff --git a/src/components/Insights/InsightsCatalog/FilterPanel/FilterChip/types.ts b/src/components/Insights/InsightsCatalog/FilterPanel/FilterChip/types.ts new file mode 100644 index 000000000..c9d4635c9 --- /dev/null +++ b/src/components/Insights/InsightsCatalog/FilterPanel/FilterChip/types.ts @@ -0,0 +1,18 @@ +import { ReactNode } from "react"; + +export type FilterType = "all" | "unread" | "critical"; + +export interface FilterChipComponentProps { + disabled?: boolean; + $selected?: boolean; + onClick: () => void; + children: ReactNode; +} + +export interface FilterChipProps { + disabled?: boolean; + selected?: boolean; + onClick: () => void; + type: FilterType; + count?: number; +} diff --git a/src/components/Insights/InsightsCatalog/InsightsStats/InsightsStats.stories.tsx b/src/components/Insights/InsightsCatalog/FilterPanel/FilterPanel.stories.tsx similarity index 61% rename from src/components/Insights/InsightsCatalog/InsightsStats/InsightsStats.stories.tsx rename to src/components/Insights/InsightsCatalog/FilterPanel/FilterPanel.stories.tsx index ec499cc8d..a6c4a827e 100644 --- a/src/components/Insights/InsightsCatalog/InsightsStats/InsightsStats.stories.tsx +++ b/src/components/Insights/InsightsCatalog/FilterPanel/FilterPanel.stories.tsx @@ -1,11 +1,10 @@ import { Meta, StoryObj } from "@storybook/react"; -import { fn } from "@storybook/test"; -import { InsightStats } from "."; +import { FilterPanel } from "."; // More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction -const meta: Meta = { - title: "Insights/InsightsCatalog/InsightStats", - component: InsightStats, +const meta: Meta = { + title: "Insights/InsightsCatalog/FilterPanel", + component: FilterPanel, parameters: { // More on how to position stories at: https://storybook.js.org/docs/react/configure/story-layout layout: "fullscreen" @@ -20,14 +19,12 @@ export const Default: Story = { args: { allIssuesCount: 100, criticalCount: 101, - unreadCount: 12, - onChange: fn() + unreadCount: 102 } }; -export const Old: Story = { +export const WithoutAllAndUnreadCounts: Story = { args: { - unreadCount: 12, - onChange: fn() + unreadCount: 102 } }; diff --git a/src/components/Insights/InsightsCatalog/FilterPanel/index.tsx b/src/components/Insights/InsightsCatalog/FilterPanel/index.tsx new file mode 100644 index 000000000..9517aa263 --- /dev/null +++ b/src/components/Insights/InsightsCatalog/FilterPanel/index.tsx @@ -0,0 +1,83 @@ +import { useEffect, useMemo } from "react"; +import { useGlobalStore } from "../../../../containers/Main/stores/useGlobalStore"; +import { useInsightsStore } from "../../../../containers/Main/stores/useInsightsStore"; +import { usePrevious } from "../../../../hooks/usePrevious"; +import { sendUserActionTrackingEvent } from "../../../../utils/actions/sendUserActionTrackingEvent"; +import { InsightFilterType } from "../types"; +import { FilterChip } from "./FilterChip"; +import * as s from "./styles"; +import { FilterPanelProps } from "./types"; + +export const FilterPanel = ({ + criticalCount, + allIssuesCount, + unreadCount +}: FilterPanelProps) => { + const filters = useInsightsStore.use.filters(); + const setFilters = useInsightsStore.use.setFilters(); + const environment = useGlobalStore.use.environment(); + const environmentId = environment?.id; + const previousEnvironmentId = usePrevious(environmentId); + const scope = useGlobalStore.use.scope(); + const scopeSpanCodeObjectId = useMemo( + () => scope?.span?.spanCodeObjectId, + [scope] + ); + const previousScopeSpanCodeObjectId = usePrevious(scopeSpanCodeObjectId); + + useEffect(() => { + if ( + previousEnvironmentId !== environmentId || + previousScopeSpanCodeObjectId !== scopeSpanCodeObjectId + ) { + setFilters([]); + } + }, [ + previousEnvironmentId, + environmentId, + previousScopeSpanCodeObjectId, + scopeSpanCodeObjectId, + setFilters + ]); + + const handleSelectionChange = (selectedFilter: InsightFilterType | null) => { + const selection = selectedFilter ? [...filters] : []; + + if (selectedFilter) { + const indexOfSelected = filters.indexOf(selectedFilter); + if (indexOfSelected !== -1) { + selection.splice(indexOfSelected, 1); + } else { + selection.push(selectedFilter); + } + } + + sendUserActionTrackingEvent(`issues filter changed`, { selection }); + setFilters(selection); + }; + + return ( + + handleSelectionChange("criticality")} + count={criticalCount} + type={"critical"} + /> + handleSelectionChange("unread")} + count={unreadCount} + type={"unread"} + /> + handleSelectionChange(null)} + count={allIssuesCount} + type={"all"} + /> + + ); +}; diff --git a/src/components/Insights/InsightsCatalog/FilterPanel/styles.ts b/src/components/Insights/InsightsCatalog/FilterPanel/styles.ts new file mode 100644 index 000000000..aa09fc2be --- /dev/null +++ b/src/components/Insights/InsightsCatalog/FilterPanel/styles.ts @@ -0,0 +1,7 @@ +import styled from "styled-components"; + +export const Stats = styled.div` + display: flex; + gap: 4px; + overflow: hidden; +`; diff --git a/src/components/Insights/InsightsCatalog/FilterPanel/types.ts b/src/components/Insights/InsightsCatalog/FilterPanel/types.ts new file mode 100644 index 000000000..d51a65f03 --- /dev/null +++ b/src/components/Insights/InsightsCatalog/FilterPanel/types.ts @@ -0,0 +1,5 @@ +export interface FilterPanelProps { + allIssuesCount?: number; + unreadCount: number; + criticalCount?: number; +} diff --git a/src/components/Insights/InsightsCatalog/InsightsCatalog.stories.tsx b/src/components/Insights/InsightsCatalog/InsightsCatalog.stories.tsx index 659925df8..0db03ddff 100644 --- a/src/components/Insights/InsightsCatalog/InsightsCatalog.stories.tsx +++ b/src/components/Insights/InsightsCatalog/InsightsCatalog.stories.tsx @@ -1,11 +1,6 @@ import { Meta, StoryObj } from "@storybook/react"; -import { fn } from "@storybook/test"; import { InsightsCatalog } from "."; import { ConfigContext, initialState } from "../../common/App/ConfigContext"; -import { SORTING_ORDER } from "../../common/SortingSelector/types"; -import { mockedEndpointBottleneckInsight } from "./InsightsPage/insightCards/EndpointBottleneckInsightCard/mockData"; -import { mockedSpanDurationsInsight } from "./InsightsPage/insightCards/SpanDurationsInsightCard/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 = { @@ -21,46 +16,9 @@ export default meta; type Story = StoryObj; -export const Default: Story = { - args: { - insightViewType: "Issues", - onQueryChange: fn(), - insights: [ - { ...mockedEndpointBottleneckInsight, isRead: false }, - { - ...mockedSpanDurationsInsight, - average: { - value: 110.74, - unit: "ms", - raw: 110735000 - }, - standardDeviation: { - value: 12.55, - unit: "ms", - raw: 12548500 - } - } - ], - totalCount: 1, - dismissedCount: 1, - defaultQuery: { - page: 0, - sorting: { - criterion: SORTING_CRITERION.LATEST, - order: SORTING_ORDER.DESC - }, - searchQuery: null, - showDismissed: false, - insightViewType: "Issues", - showUnreadOnly: false, - filters: [] - }, - unreadCount: 1 - } -}; +export const Default: Story = {}; export const WithStats: Story = { - ...Default, decorators: [ (Story) => ( { listRef.current?.scrollTo(0, 0); - }, [scope?.span, environment?.id, page]); + }, [scope?.span?.spanCodeObjectId, environment?.id, page]); useEffect(() => { window.sendMessageToDigma({ diff --git a/src/components/Insights/InsightsCatalog/InsightsPage/insightCards/EndpointSessionInViewInsightCard/index.tsx b/src/components/Insights/InsightsCatalog/InsightsPage/insightCards/EndpointSessionInViewInsightCard/index.tsx index de692320e..17187bb2f 100644 --- a/src/components/Insights/InsightsCatalog/InsightsPage/insightCards/EndpointSessionInViewInsightCard/index.tsx +++ b/src/components/Insights/InsightsCatalog/InsightsPage/insightCards/EndpointSessionInViewInsightCard/index.tsx @@ -1,4 +1,4 @@ -import { useGlobalStore } from "../../../../../../containers/Main/stores/globalStore"; +import { useGlobalStore } from "../../../../../../containers/Main/stores/useGlobalStore"; import { usePagination } from "../../../../../../hooks/usePagination"; import { TraceIcon } from "../../../../../common/icons/12px/TraceIcon"; import { Button } from "../../../../../common/v3/Button"; diff --git a/src/components/Insights/InsightsCatalog/InsightsPage/insightCards/SpaNPlusOneInsightCard/index.tsx b/src/components/Insights/InsightsCatalog/InsightsPage/insightCards/SpaNPlusOneInsightCard/index.tsx index 49ef7b3a7..7c5e51ad1 100644 --- a/src/components/Insights/InsightsCatalog/InsightsPage/insightCards/SpaNPlusOneInsightCard/index.tsx +++ b/src/components/Insights/InsightsCatalog/InsightsPage/insightCards/SpaNPlusOneInsightCard/index.tsx @@ -1,5 +1,5 @@ import { ReactNode, useState } from "react"; -import { useGlobalStore } from "../../../../../../containers/Main/stores/globalStore"; +import { useGlobalStore } from "../../../../../../containers/Main/stores/useGlobalStore"; import { getDurationString } from "../../../../../../utils/getDurationString"; import { trimEndpointScheme } from "../../../../../../utils/trimEndpointScheme"; import { TraceIcon } from "../../../../../common/icons/12px/TraceIcon"; diff --git a/src/components/Insights/InsightsCatalog/InsightsPage/insightCards/SpanDurationBreakdownInsightCard/index.tsx b/src/components/Insights/InsightsCatalog/InsightsPage/insightCards/SpanDurationBreakdownInsightCard/index.tsx index a16b84d50..3d97c977d 100644 --- a/src/components/Insights/InsightsCatalog/InsightsPage/insightCards/SpanDurationBreakdownInsightCard/index.tsx +++ b/src/components/Insights/InsightsCatalog/InsightsPage/insightCards/SpanDurationBreakdownInsightCard/index.tsx @@ -5,7 +5,7 @@ import { useReactTable } from "@tanstack/react-table"; import { useEffect, useMemo, useState } from "react"; -import { useGlobalStore } from "../../../../../../containers/Main/stores/globalStore"; +import { useGlobalStore } from "../../../../../../containers/Main/stores/useGlobalStore"; import { getFeatureFlagValue } from "../../../../../../featureFlags"; import { usePagination } from "../../../../../../hooks/usePagination"; import { usePrevious } from "../../../../../../hooks/usePrevious"; diff --git a/src/components/Insights/InsightsCatalog/InsightsPage/insightCards/SpanEndpointBottleneckInsightCard/index.tsx b/src/components/Insights/InsightsCatalog/InsightsPage/insightCards/SpanEndpointBottleneckInsightCard/index.tsx index 625bfd061..93d32f3a7 100644 --- a/src/components/Insights/InsightsCatalog/InsightsPage/insightCards/SpanEndpointBottleneckInsightCard/index.tsx +++ b/src/components/Insights/InsightsCatalog/InsightsPage/insightCards/SpanEndpointBottleneckInsightCard/index.tsx @@ -1,5 +1,5 @@ import { ReactNode, useState } from "react"; -import { useGlobalStore } from "../../../../../../containers/Main/stores/globalStore"; +import { useGlobalStore } from "../../../../../../containers/Main/stores/useGlobalStore"; import { isNull } from "../../../../../../typeGuards/isNull"; import { getDurationString } from "../../../../../../utils/getDurationString"; import { trimEndpointScheme } from "../../../../../../utils/trimEndpointScheme"; diff --git a/src/components/Insights/InsightsCatalog/InsightsPage/insightCards/SpanQueryOptimizationInsightCard/SpanQueryOptimizationInsightCard.stories.tsx b/src/components/Insights/InsightsCatalog/InsightsPage/insightCards/SpanQueryOptimizationInsightCard/SpanQueryOptimizationInsightCard.stories.tsx index 60654dacc..a239c000e 100644 --- a/src/components/Insights/InsightsCatalog/InsightsPage/insightCards/SpanQueryOptimizationInsightCard/SpanQueryOptimizationInsightCard.stories.tsx +++ b/src/components/Insights/InsightsCatalog/InsightsPage/insightCards/SpanQueryOptimizationInsightCard/SpanQueryOptimizationInsightCard.stories.tsx @@ -23,15 +23,6 @@ export const Default: Story = { } }; -export const NoSpanCodeObjectId: Story = { - args: { - insight: { - ...mockedSpanQueryOptimizationInsight, - spanInfo: null - } - } -}; - export const ManyEndpoints: Story = { args: { insight: { diff --git a/src/components/Insights/InsightsCatalog/InsightsPage/insightCards/SpanQueryOptimizationInsightCard/index.tsx b/src/components/Insights/InsightsCatalog/InsightsPage/insightCards/SpanQueryOptimizationInsightCard/index.tsx index 4eafa3d53..e014dffc1 100644 --- a/src/components/Insights/InsightsCatalog/InsightsPage/insightCards/SpanQueryOptimizationInsightCard/index.tsx +++ b/src/components/Insights/InsightsCatalog/InsightsPage/insightCards/SpanQueryOptimizationInsightCard/index.tsx @@ -7,7 +7,7 @@ import { InsightCard } from "../common/InsightCard"; import { ColumnsContainer } from "../common/InsightCard/ColumnsContainer"; import { KeyValue } from "../common/InsightCard/KeyValue"; import { ListItem } from "../common/InsightCard/ListItem"; -import { AssetLink, ContentContainer, Description, Details } from "../styles"; +import { ContentContainer, Description } from "../styles"; import * as s from "./styles"; import { SpanQueryOptimizationInsightCardProps } from "./types"; @@ -59,18 +59,6 @@ export const SpanQueryOptimizationInsightCard = ({ insight={insight} content={ - {spanCodeObjectId && ( -
- - Query is slow compared to other{" "} - {insight.dbStatement.toUpperCase()} requests - - handleAssetLinkClick(spanCodeObjectId)} - /> -
- )} {getDurationString(insight.duration)} diff --git a/src/components/Insights/InsightsCatalog/InsightsPage/insightCards/SpanScalingInsightCard/index.tsx b/src/components/Insights/InsightsCatalog/InsightsPage/insightCards/SpanScalingInsightCard/index.tsx index 2cfbff3f2..8b706d6ad 100644 --- a/src/components/Insights/InsightsCatalog/InsightsPage/insightCards/SpanScalingInsightCard/index.tsx +++ b/src/components/Insights/InsightsCatalog/InsightsPage/insightCards/SpanScalingInsightCard/index.tsx @@ -1,4 +1,4 @@ -import { useGlobalStore } from "../../../../../../containers/Main/stores/globalStore"; +import { useGlobalStore } from "../../../../../../containers/Main/stores/useGlobalStore"; import { usePagination } from "../../../../../../hooks/usePagination"; import { getDurationString } from "../../../../../../utils/getDurationString"; import { trimEndpointScheme } from "../../../../../../utils/trimEndpointScheme"; diff --git a/src/components/Insights/InsightsCatalog/InsightsPage/insightCards/SpanUsagesInsightCard/index.tsx b/src/components/Insights/InsightsCatalog/InsightsPage/insightCards/SpanUsagesInsightCard/index.tsx index 5e2770425..1b103877d 100644 --- a/src/components/Insights/InsightsCatalog/InsightsPage/insightCards/SpanUsagesInsightCard/index.tsx +++ b/src/components/Insights/InsightsCatalog/InsightsPage/insightCards/SpanUsagesInsightCard/index.tsx @@ -5,7 +5,7 @@ import { useReactTable } from "@tanstack/react-table"; import { useEffect, useState } from "react"; -import { useGlobalStore } from "../../../../../../containers/Main/stores/globalStore"; +import { useGlobalStore } from "../../../../../../containers/Main/stores/useGlobalStore"; import { usePagination } from "../../../../../../hooks/usePagination"; import { usePrevious } from "../../../../../../hooks/usePrevious"; import { isNumber } from "../../../../../../typeGuards/isNumber"; diff --git a/src/components/Insights/InsightsCatalog/InsightsPage/insightCards/common/InsightCard/InsightHeader/index.tsx b/src/components/Insights/InsightsCatalog/InsightsPage/insightCards/common/InsightCard/InsightHeader/index.tsx index 3b703dc39..edbc52224 100644 --- a/src/components/Insights/InsightsCatalog/InsightsPage/insightCards/common/InsightCard/InsightHeader/index.tsx +++ b/src/components/Insights/InsightsCatalog/InsightsPage/insightCards/common/InsightCard/InsightHeader/index.tsx @@ -1,4 +1,4 @@ -import { useGlobalStore } from "../../../../../../../../containers/Main/stores/globalStore"; +import { useGlobalStore } from "../../../../../../../../containers/Main/stores/useGlobalStore"; import { isString } from "../../../../../../../../typeGuards/isString"; import { formatTimeDistance } from "../../../../../../../../utils/formatTimeDistance"; import { getInsightTypeInfo } from "../../../../../../../../utils/getInsightTypeInfo"; diff --git a/src/components/Insights/InsightsCatalog/InsightsPage/insightCards/common/InsightCard/index.tsx b/src/components/Insights/InsightsCatalog/InsightsPage/insightCards/common/InsightCard/index.tsx index 2cbe736b7..41eb084f1 100644 --- a/src/components/Insights/InsightsCatalog/InsightsPage/insightCards/common/InsightCard/index.tsx +++ b/src/components/Insights/InsightsCatalog/InsightsPage/insightCards/common/InsightCard/index.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useState } from "react"; -import { useGlobalStore } from "../../../../../../../containers/Main/stores/globalStore"; +import { useGlobalStore } from "../../../../../../../containers/Main/stores/useGlobalStore"; import { usePrevious } from "../../../../../../../hooks/usePrevious"; import { isString } from "../../../../../../../typeGuards/isString"; import { sendUserActionTrackingEvent } from "../../../../../../../utils/actions/sendUserActionTrackingEvent"; diff --git a/src/components/Insights/InsightsCatalog/InsightsStats/index.tsx b/src/components/Insights/InsightsCatalog/InsightsStats/index.tsx deleted file mode 100644 index 913170e34..000000000 --- a/src/components/Insights/InsightsCatalog/InsightsStats/index.tsx +++ /dev/null @@ -1,85 +0,0 @@ -import { useEffect, useState } from "react"; -import { useGlobalStore } from "../../../../containers/Main/stores/globalStore"; -import { isNumber } from "../../../../typeGuards/isNumber"; -import { sendUserActionTrackingEvent } from "../../../../utils/actions/sendUserActionTrackingEvent"; -import { Tooltip } from "../../../common/v3/Tooltip"; -import { InsightFilterType } from "../types"; -import * as s from "./styles"; -import { InsightStatsProps } from "./types"; - -export const InsightStats = ({ - onChange, - criticalCount, - allIssuesCount, - unreadCount -}: InsightStatsProps) => { - const [selectedFilters, setSelectedFilters] = useState( - [] - ); - - const environment = useGlobalStore.use.environment(); - const scope = useGlobalStore.use.scope(); - - useEffect(() => { - setSelectedFilters([]); - onChange([]); - }, [environment?.id, scope?.span?.spanCodeObjectId, onChange]); - - const handleSelectionChange = (selectedFilter: InsightFilterType) => { - const selection = [...selectedFilters]; - const indexOfSelected = selectedFilters.indexOf(selectedFilter); - - if (indexOfSelected !== -1) { - selection.splice(indexOfSelected, 1); - } else { - selection.push(selectedFilter); - } - - sendUserActionTrackingEvent(`issues filter changed`, { selection }); - setSelectedFilters(selection); - onChange(selection); - }; - - return ( - - handleSelectionChange("criticality")} - > - {isNumber(criticalCount) ? ( - {criticalCount} - ) : ( - - )} - Critical issues - - handleSelectionChange("unread")} - > - {unreadCount} - Unread issues - - - {isNumber(allIssuesCount) ? ( - {allIssuesCount} - ) : ( - - )} - All issues - - - ); -}; - -const NotAssignedValue = () => ( - - N/A - -); diff --git a/src/components/Insights/InsightsCatalog/InsightsStats/styles.ts b/src/components/Insights/InsightsCatalog/InsightsStats/styles.ts deleted file mode 100644 index 7c2d4f881..000000000 --- a/src/components/Insights/InsightsCatalog/InsightsStats/styles.ts +++ /dev/null @@ -1,80 +0,0 @@ -import styled from "styled-components"; -import { - bodyBoldTypography, - caption1MediumTypography -} from "../../../common/App/typographies"; -import { StatsProps } from "./types"; - -export const Stats = styled.div` - display: flex; - border-radius: 4px; - border: 1px solid ${({ theme }) => theme.colors.v3.stroke.tertiary}; - gap: 8px; - padding: 8px; - align-items: stretch; -`; - -export const Stat = styled.button` - font-family: inherit; - display: flex; - padding: 8px 16px; - flex-direction: column; - align-items: center; - gap: 4px; - flex: 1 0 0; - border-radius: 8px; - background: none; - border: none; - - &:disabled { - border: 1px solid ${({ theme }) => theme.colors.v3.stroke.tertiary}; - background: none; - color: ${({ theme }) => theme.colors.v3.text.disabled}; - } - - &:disabled > span { - color: ${({ theme }) => theme.colors.v3.text.disabled}; - } -`; - -export const CriticalStat = styled(Stat)` - border: 1px solid - ${({ $selected, theme }) => - $selected - ? theme.colors.v3.status.high - : theme.colors.v3.status.backgroundHigh}; - background: ${({ theme }) => theme.colors.v3.pieChart.darkRed}; - - &:hover:enabled { - border: 1px solid ${({ theme }) => theme.colors.v3.status.high}; - background: ${({ theme }) => theme.colors.v3.status.backgroundHigh}; - } -`; - -export const UnreadStat = styled(Stat)` - border: 1px solid - ${({ $selected, theme }) => - $selected - ? theme.colors.v3.stroke.brandPrimary - : theme.colors.v3.stroke.dark}; - background: ${({ $selected, theme }) => - $selected - ? theme.colors.v3.surface.brandDarkest - : theme.colors.v3.surface.sidePanelHeader}; - - &:hover:enabled { - border: 1px solid ${({ theme }) => theme.colors.v3.stroke.brandPrimary}; - background: ${({ theme }) => theme.colors.v3.surface.brandDark}; - } -`; - -export const StatCounter = styled.span` - ${bodyBoldTypography} - color: ${({ theme }) => theme.colors.v3.text.primary}; - line-height: 18px; -`; - -export const StatDescription = styled.span` - ${caption1MediumTypography} - color: ${({ theme }) => theme.colors.v3.text.secondary}; -`; diff --git a/src/components/Insights/InsightsCatalog/InsightsStats/types.ts b/src/components/Insights/InsightsCatalog/InsightsStats/types.ts deleted file mode 100644 index e92da8a8d..000000000 --- a/src/components/Insights/InsightsCatalog/InsightsStats/types.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { InsightFilterType } from "../types"; - -export interface InsightStatsProps { - onChange: (selected: InsightFilterType[]) => void; - allIssuesCount?: number; - unreadCount: number; - criticalCount?: number; -} - -export interface StatsProps { - $selected?: boolean; -} diff --git a/src/components/Insights/InsightsCatalog/index.tsx b/src/components/Insights/InsightsCatalog/index.tsx index 11c5ebab4..8c16f739c 100644 --- a/src/components/Insights/InsightsCatalog/index.tsx +++ b/src/components/Insights/InsightsCatalog/index.tsx @@ -1,13 +1,13 @@ -import { useCallback, useEffect, useState } from "react"; +import { useEffect, useState } from "react"; import { createPortal } from "react-dom"; import { useTheme } from "styled-components"; -import { useGlobalStore } from "../../../containers/Main/stores/globalStore"; +import { useGlobalStore } from "../../../containers/Main/stores/useGlobalStore"; +import { useInsightsStore } from "../../../containers/Main/stores/useInsightsStore"; import { getFeatureFlagValue } from "../../../featureFlags"; import { useDebounce } from "../../../hooks/useDebounce"; import { usePersistence } from "../../../hooks/usePersistence"; import { usePrevious } from "../../../hooks/usePrevious"; import { isNumber } from "../../../typeGuards/isNumber"; -import { isString } from "../../../typeGuards/isString"; import { isUndefined } from "../../../typeGuards/isUndefined"; import { FeatureFlag } from "../../../types"; import { sendUserActionTrackingEvent } from "../../../utils/actions/sendUserActionTrackingEvent"; @@ -22,14 +22,18 @@ import { SearchInput } from "../../common/SearchInput"; import { SortingSelector } from "../../common/SortingSelector"; import { SORTING_ORDER, Sorting } from "../../common/SortingSelector/types"; import { ChevronIcon } from "../../common/icons/16px/ChevronIcon"; -import { GroupIcon } from "../../common/icons/16px/GroupIcon"; +import { EyeIcon } from "../../common/icons/16px/EyeIcon"; +import { FunnelIcon } from "../../common/icons/16px/FunnelIcon"; import { RefreshIcon } from "../../common/icons/16px/RefreshIcon"; import { Direction } from "../../common/icons/types"; import { Button } from "../../common/v3/Button"; +import { NewIconButton } from "../../common/v3/NewIconButton"; import { Tooltip } from "../../common/v3/Tooltip"; +import { IssuesFilter } from "../Issues/IssuesFilter"; import { trackingEvents } from "../tracking"; +import { EnvironmentSelector } from "./EnvironmentSelector"; +import { FilterPanel } from "./FilterPanel"; import { InsightsPage } from "./InsightsPage"; -import { InsightStats } from "./InsightsStats"; import { PromotionCard } from "./PromotionCard"; import * as s from "./styles"; import { @@ -57,31 +61,25 @@ const isPromotionEnabled = (dismissalDate: number | null | undefined) => { }; export const InsightsCatalog = ({ - insightViewType, - insights, onJiraTicketCreate, - defaultQuery, - totalCount, - dismissedCount, - unreadCount, - onQueryChange, - onRefresh, - filterComponent + onRefresh }: InsightsCatalogProps) => { - const [page, setPage] = useState(0); - const previousPage = usePrevious(page); - const [searchInputValue, setSearchInputValue] = useState( - defaultQuery.searchQuery - ); - - const [selectedFilters, setSelectedFilters] = useState( - [] - ); + const insightViewType = useInsightsStore.use.insightViewType(); + const mode = useInsightsStore.use.viewMode(); + const setMode = useInsightsStore.use.setViewMode(); + const page = useInsightsStore.use.page(); + const setPage = useInsightsStore.use.setPage(); + const searchInputValue = useInsightsStore.use.search(); + const setSearchInputValue = useInsightsStore.use.setSearch(); const debouncedSearchInputValue = useDebounce(searchInputValue, 1000); - const [sorting, setSorting] = useState(defaultQuery.sorting); - const previousSorting = usePrevious(sorting); - const previousFilters = usePrevious(selectedFilters); - const previousSearchQuery = usePrevious(debouncedSearchInputValue); + const sorting = useInsightsStore.use.sorting(); + const setSorting = useInsightsStore.use.setSorting(); + const filters = useInsightsStore.use.filters(); + const data = useInsightsStore.use.data(); + const insights = data?.insights ?? []; + const totalCount = data?.totalCount ?? 0; + const dismissedCount = data?.dismissedCount; + const unreadCount = data?.unreadCount ?? 0; const pageStartItemNumber = page * PAGE_SIZE + 1; const pageEndItemNumber = Math.min( pageStartItemNumber + PAGE_SIZE - 1, @@ -89,11 +87,11 @@ export const InsightsCatalog = ({ ); const insightStats = useGlobalStore.use.insightStats(); const environment = useGlobalStore.use.environment(); + const environments = useGlobalStore.use.environments(); const scope = useGlobalStore.use.scope(); - const previousScopeSpan = usePrevious(scope?.span); + const scopeSpanCodeObjectId = scope?.span?.spanCodeObjectId; + const isAtSpan = Boolean(scope?.span?.spanCodeObjectId); const backendInfo = useGlobalStore.use.backendInfo(); - const [mode, setMode] = useState(ViewMode.All); - const previousMode = usePrevious(mode); const theme = useTheme(); const { isMarkingAllAsReadInProgress, markAllAsRead } = useMarkingAllAsRead( scope?.span ?? null @@ -103,6 +101,7 @@ export const InsightsCatalog = ({ ); const [showRegistration, setShowRegistration] = useState(false); const [showDiscardConfirmation, setShowDiscardConfirmation] = useState(false); + const [isFiltersToolbarVisible, setIsFiltersToolbarVisible] = useState(false); const [dismissalDate, setDismissalDate] = usePersistence( PROMOTION_PERSISTENCE_KEY, "application" @@ -157,52 +156,34 @@ export const InsightsCatalog = ({ setShowDiscardConfirmation(true); }; + const isIssuesView = insightViewType === "Issues"; + const isPromotionVisible = - insightViewType == "Issues" && - !promotionCompleted && - isPromotionEnabled(dismissalDate); - const areInsightsStatsVisible = insightViewType === "Issues"; + isIssuesView && !promotionCompleted && isPromotionEnabled(dismissalDate); const isDismissalViewModeButtonVisible = - insightViewType === "Issues" && - (isUndefined(dismissedCount) || dismissedCount > 0); // isUndefined - check for backward compatibility, always show when BE does not return this counter + isIssuesView && (isUndefined(dismissedCount) || dismissedCount > 0); // isUndefined - check for backward compatibility, always show when BE does not return this counter const isMarkingAsReadOptionsEnabled = - insightViewType === "Issues" && + isIssuesView && isNumber(unreadCount) && - selectedFilters.length === 1 && - selectedFilters[0] === "unread" && + filters.length === 1 && + filters[0] === "unread" && unreadCount > 0; const areInsightStatsEnabled = getFeatureFlagValue( backendInfo, FeatureFlag.ARE_INSIGHT_STATS_ENABLED ); + const isIssuesFilterVisible = getFeatureFlagValue( + backendInfo, + FeatureFlag.ARE_ISSUES_FILTERS_ENABLED + ); const mainContainer = document.getElementById(MAIN_CONTAINER_ID); - const refreshData = useCallback(() => { - onQueryChange({ - ...defaultQuery, - page, - sorting, - searchQuery: debouncedSearchInputValue, - showDismissed: mode === ViewMode.OnlyDismissed, - showUnreadOnly: isShowUnreadOnly(selectedFilters), - filters: selectedFilters - }); - }, [ - page, - sorting, - debouncedSearchInputValue, - onQueryChange, - defaultQuery, - mode, - selectedFilters - ]); - const handleRefreshButtonClick = () => { sendUserActionTrackingEvent(trackingEvents.REFRESH_BUTTON_CLICKED, { viewMode: mode }); - refreshData(); + onRefresh(); }; const handleDismissalViewModeButtonClick = () => { @@ -219,126 +200,130 @@ export const InsightsCatalog = ({ setMode(ViewMode.All); }; - const handleFilterSelectionChange = useCallback( - (selectedFilter: InsightFilterType[]) => { - setSelectedFilters(selectedFilter); - }, - [] - ); + const handleFilterButtonClick = () => { + setIsFiltersToolbarVisible(!isFiltersToolbarVisible); + }; - useEffect(() => { - if (previousIsMarkingAllAsReadInProgress && !isMarkingAllAsReadInProgress) { - refreshData(); - } - }, [ - isMarkingAllAsReadInProgress, - previousIsMarkingAllAsReadInProgress, - refreshData, - scope - ]); + const handleSearchInputChange = (val: string | null) => { + setSearchInputValue(val ?? ""); + }; useEffect(() => { - if (!previousScopeSpan || previousScopeSpan !== scope?.span) { - setSearchInputValue(""); - } - }, [scope?.span, previousScopeSpan]); + setSearchInputValue(""); + }, [scopeSpanCodeObjectId, setSearchInputValue]); useEffect(() => { setPage(0); - }, [environment?.id, scope?.span, mode]); + }, [environment?.id, scopeSpanCodeObjectId, mode, setPage]); useEffect(() => { - refreshData(); - }, []); + if (previousIsMarkingAllAsReadInProgress && !isMarkingAllAsReadInProgress) { + onRefresh(); + } + }, [ + isMarkingAllAsReadInProgress, + previousIsMarkingAllAsReadInProgress, + onRefresh + ]); - useEffect(() => { + const renderFilterPanel = () => { if ( - (isNumber(previousPage) && previousPage !== page) || - Boolean(previousSorting && previousSorting !== sorting) || - (isString(previousSearchQuery) && - previousSearchQuery !== debouncedSearchInputValue) || - previousMode !== mode || - previousFilters !== selectedFilters + !isIssuesView || + Boolean(searchInputValue) || + (insights.length === 0 && filters.length === 0) ) { - refreshData(); + return null; } - }, [ - previousSorting, - sorting, - previousPage, - page, - debouncedSearchInputValue, - previousSearchQuery, - refreshData, - mode, - previousMode, - previousFilters, - selectedFilters - ]); + + return ( + + ); + }; return ( <> - {!isUndefined(filterComponent) && filterComponent} - { - 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 - } - ]} - defaultSorting={defaultQuery.sorting} - /> - - - + {isAtSpan && environments && environments.length > 1 && ( + + )} + {!isAtSpan && renderFilterPanel()} + + + + + + + + + + - - {mode === ViewMode.All ? ( + {isFiltersToolbarVisible && ( <> - {!searchInputValue && - areInsightsStatsVisible && - (insights.length > 0 || selectedFilters.length > 0) && ( - + {isIssuesView && isIssuesFilterVisible && } + + { + 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 } - onChange={handleFilterSelectionChange} - /> - )} - {selectedFilters.length === 1 && ( + ]} + defaultSorting={sorting} + /> + + + )} + {isPromotionVisible && ( + + + + )} + {mode === ViewMode.All ? ( + <> + {filters.length === 1 && ( - {isShowUnreadOnly(selectedFilters) ? "Unread" : "Critical"} + {isShowUnreadOnly(filters) ? "Unread" : "Critical"} {isMarkingAsReadOptionsEnabled && ( @@ -374,14 +359,6 @@ export const InsightsCatalog = ({ )} )} - {isPromotionVisible && ( - - - - )} {totalCount > 0 && ( @@ -416,7 +393,7 @@ export const InsightsCatalog = ({