diff --git a/src/components/Errors/GlobalErrorsList/GlobalErrorsFilters/index.tsx b/src/components/Errors/GlobalErrorsList/GlobalErrorsFilters/index.tsx index 04037684b..7de4e6933 100644 --- a/src/components/Errors/GlobalErrorsList/GlobalErrorsFilters/index.tsx +++ b/src/components/Errors/GlobalErrorsList/GlobalErrorsFilters/index.tsx @@ -1,15 +1,21 @@ import { useCallback, useEffect, useMemo, useState } from "react"; +import { getFeatureFlagValue } from "../../../../featureFlags"; import { useFetchData } from "../../../../hooks/useFetchData"; import { usePrevious } from "../../../../hooks/usePrevious"; import { useConfigSelector } from "../../../../store/config/useConfigSelector"; import { + ErrorCriticality, ErrorFilter, + ErrorHandlingType, GlobalErrorsFiltersState } from "../../../../store/errors/errorsSlice"; import { useErrorsSelector } from "../../../../store/errors/useErrorsSelector"; import { useStore } from "../../../../store/useStore"; +import { FeatureFlag } from "../../../../types"; import { sendUserActionTrackingEvent } from "../../../../utils/actions/sendUserActionTrackingEvent"; import { FilterPopup } from "../../../common/FilterPopup"; +import { EyeIcon } from "../../../common/icons/12px/EyeIcon"; +import { WarningTriangleIcon } from "../../../common/icons/12px/WarningTriangleIcon"; import { WrenchIcon } from "../../../common/icons/12px/WrenchIcon"; import { CrossCircleIcon } from "../../../common/icons/CrossCircleIcon"; import { EndpointIcon } from "../../../common/icons/EndpointIcon"; @@ -28,7 +34,14 @@ const getSelectPlaceholder = (options: SelectItem[], placeholder: string) => options.filter((x) => x.selected).length > 0 ? placeholder : "All"; export const GlobalErrorsFilters = () => { - const { environment } = useConfigSelector(); + const { environment, backendInfo } = useConfigSelector(); + + const areGlobalErrorsCriticalityAndUnhandledFiltersEnabled = + getFeatureFlagValue( + backendInfo, + FeatureFlag.ARE_GLOBAL_ERRORS_CRITICALITY_AND_UNHANDLED_FILTERS_ENABLED + ); + const { globalErrorsFilters, globalErrorsSelectedFilters } = useErrorsSelector(); const { setGlobalErrorsFilters, setGlobalErrorsSelectedFilters } = @@ -47,6 +60,12 @@ export const GlobalErrorsFilters = () => { const [selectedErrorTypes, setSelectedErrorTypes] = useState( globalErrorsSelectedFilters.errorTypes ); + const [selectedCriticalities, setSelectedCriticalities] = useState< + ErrorCriticality[] + >(globalErrorsSelectedFilters.criticalities); + const [selectedHandlingTypes, setSelectedHandlingTypes] = useState< + ErrorHandlingType[] + >(globalErrorsSelectedFilters.handlingTypes); const getLastSelectedFilterValues = useCallback( (changedFilter: ErrorFilter) => { @@ -171,29 +190,111 @@ export const GlobalErrorsFilters = () => { setSelectedErrorTypes(newValue); }; - const servicesFilterOptions: SelectItem[] = - services?.map((x) => ({ - label: x, - value: x, - selected: selectedServices.includes(x), - enabled: true - })) ?? []; - - const endpointsFilterOptions: SelectItem[] = - endpoints?.map((x) => ({ - label: x.displayName, - value: x.spanCodeObjectId, - selected: selectedEndpoints.includes(x.spanCodeObjectId), - enabled: true - })) ?? []; - - const errorTypesFilterOptions: SelectItem[] = - errorTypes?.map((x) => ({ - label: x, - value: x, - selected: selectedErrorTypes.includes(x), - enabled: true - })) ?? []; + const handleCriticalityChange = (value: string | string[]) => { + sendUserActionTrackingEvent( + trackingEvents.GLOBAL_ERRORS_VIEW_CRITICALITY_FILTER_CHANGED + ); + const newValue = Array.isArray(value) ? value : [value]; + setSelectedCriticalities(newValue as ErrorCriticality[]); + }; + + const handleHandlingTypeChange = (value: string | string[]) => { + sendUserActionTrackingEvent( + trackingEvents.GLOBAL_ERRORS_VIEW_UNHANDLED_FILTER_CHANGED + ); + const newValue = value === "All" ? [] : [value]; + setSelectedHandlingTypes(newValue as ErrorHandlingType[]); + }; + + const servicesFilterOptions: SelectItem[] = useMemo( + () => + services?.map((x) => ({ + label: x, + value: x, + selected: selectedServices.includes(x), + enabled: true + })) ?? [], + [services, selectedServices] + ); + + const endpointsFilterOptions: SelectItem[] = useMemo( + () => + endpoints?.map((x) => ({ + label: x.displayName, + value: x.spanCodeObjectId, + selected: selectedEndpoints.includes(x.spanCodeObjectId), + enabled: true + })) ?? [], + [endpoints, selectedEndpoints] + ); + + const errorTypesFilterOptions: SelectItem[] = useMemo( + () => + errorTypes?.map((x) => ({ + label: x, + value: x, + selected: selectedErrorTypes.includes(x), + enabled: true + })) ?? [], + [errorTypes, selectedErrorTypes] + ); + + const criticalityFilterOptions: SelectItem[] = useMemo( + () => [ + { + label: "Critical", + value: "High", + selected: selectedCriticalities.includes("High"), + enabled: true + }, + { + label: "Medium", + value: "Medium", + selected: selectedCriticalities.includes("Medium"), + enabled: true + }, + { + label: "Low", + value: "Low", + selected: selectedCriticalities.includes("Low"), + enabled: true + } + ], + [selectedCriticalities] + ); + + const handlingTypeFilterOptions: SelectItem[] = useMemo( + () => [ + { + label: "Yes", + value: "Handled", + selected: + selectedHandlingTypes.length === 1 && + selectedHandlingTypes[0] === "Handled", + enabled: true + }, + { + label: "No", + value: "Unhandled", + selected: + selectedHandlingTypes.length === 1 && + selectedHandlingTypes[0] === "Unhandled", + enabled: true + }, + { + label: "All", + value: "All", + selected: selectedHandlingTypes.length === 0, + enabled: true + } + ], + [selectedHandlingTypes] + ); + + const selectedHandlingTypeOption = useMemo( + () => handlingTypeFilterOptions.find((x) => x.selected), + [handlingTypeFilterOptions] + ); const filters = [ { @@ -255,7 +356,47 @@ export const GlobalErrorsFilters = () => { disabled={errorTypesFilterOptions?.length === 0} /> ) - } + }, + ...(areGlobalErrorsCriticalityAndUnhandledFiltersEnabled + ? [ + { + title: "Criticality", + component: ( + ( + + + + )} + /> + ) + }, + { + title: "Unhandled", + component: ( + ( + + + + )} + /> + ) + } + ] + : []) ]; const applyFilters = () => { @@ -263,7 +404,9 @@ export const GlobalErrorsFilters = () => { ...globalErrorsSelectedFilters, services: selectedServices, endpoints: selectedEndpoints, - errorTypes: selectedErrorTypes + errorTypes: selectedErrorTypes, + criticalities: selectedCriticalities, + handlingTypes: selectedHandlingTypes }); }; @@ -281,12 +424,16 @@ export const GlobalErrorsFilters = () => { setSelectedServices([]); setSelectedEndpoints([]); setSelectedErrorTypes([]); + setSelectedCriticalities([]); + setSelectedHandlingTypes([]); }; const selectedFiltersCount = [ selectedServices.length, selectedEndpoints.length, - selectedErrorTypes.length + selectedErrorTypes.length, + selectedCriticalities.length, + selectedHandlingTypes.length ].filter((x) => x > 0).length; const handlePopupOpenStateChange = (isOpen: boolean) => { diff --git a/src/components/Errors/GlobalErrorsList/index.tsx b/src/components/Errors/GlobalErrorsList/index.tsx index 5bae0efd7..946f5fa1d 100644 --- a/src/components/Errors/GlobalErrorsList/index.tsx +++ b/src/components/Errors/GlobalErrorsList/index.tsx @@ -66,6 +66,12 @@ export const GlobalErrorsList = () => { FeatureFlag.ARE_GLOBAL_ERRORS_FILTERS_ENABLED ); + const areGlobalErrorsCriticalityAndUnhandledFiltersEnabled = + getFeatureFlagValue( + backendInfo, + FeatureFlag.ARE_GLOBAL_ERRORS_CRITICALITY_AND_UNHANDLED_FILTERS_ENABLED + ); + const environmentId = environment?.id; const sortingMenuItems = Object.values(GLOBAL_ERROR_SORTING_CRITERION).map( @@ -98,8 +104,12 @@ export const GlobalErrorsList = () => { ? { services: selectedFilters.services, endpoints: selectedFilters.endpoints, - errorTypes: selectedFilters.errorTypes, - criticality: selectedFilters.criticality, + errorTypes: selectedFilters.errorTypes + } + : {}), + ...(areGlobalErrorsCriticalityAndUnhandledFiltersEnabled + ? { + criticalities: selectedFilters.criticalities, handlingTypes: selectedFilters.handlingTypes } : {}) @@ -110,10 +120,11 @@ export const GlobalErrorsList = () => { sorting, page, areGlobalErrorsFiltersEnabled, + areGlobalErrorsCriticalityAndUnhandledFiltersEnabled, selectedFilters.services, selectedFilters.endpoints, selectedFilters.errorTypes, - selectedFilters.criticality, + selectedFilters.criticalities, selectedFilters.handlingTypes ] ); @@ -140,7 +151,7 @@ export const GlobalErrorsList = () => { selectedFilters.services, selectedFilters.endpoints, selectedFilters.errorTypes, - selectedFilters.criticality, + selectedFilters.criticalities, selectedFilters.handlingTypes ]); @@ -209,7 +220,7 @@ export const GlobalErrorsList = () => { selectedFilters.services, selectedFilters.endpoints, selectedFilters.errorTypes, - selectedFilters.criticality, + selectedFilters.criticalities, selectedFilters.handlingTypes ].some((x) => x.length > 0); diff --git a/src/components/Errors/GlobalErrorsList/types.ts b/src/components/Errors/GlobalErrorsList/types.ts index f33f1c803..8f7e4abe4 100644 --- a/src/components/Errors/GlobalErrorsList/types.ts +++ b/src/components/Errors/GlobalErrorsList/types.ts @@ -14,7 +14,7 @@ export interface GetGlobalErrorsDataPayload { services?: string[]; endpoints?: string[]; errorTypes?: string[]; - criticality?: ErrorCriticality[]; + criticalities?: ErrorCriticality[]; handlingTypes?: ErrorHandlingType[]; } diff --git a/src/components/Errors/tracking.ts b/src/components/Errors/tracking.ts index 796cd7f2b..76676bced 100644 --- a/src/components/Errors/tracking.ts +++ b/src/components/Errors/tracking.ts @@ -26,6 +26,10 @@ export const trackingEvents = addPrefix( "global errors view endpoints filter changed", GLOBAL_ERRORS_VIEW_ERROR_TYPES_FILTER_CHANGED: "global errors view error types filter changed", + GLOBAL_ERRORS_VIEW_CRITICALITY_FILTER_CHANGED: + "global errors view criticality filter changed", + GLOBAL_ERRORS_VIEW_UNHANDLED_FILTER_CHANGED: + "global errors view unhandled filter changed", GLOBAL_ERRORS_VIEW_FILTERS_CLOSE_BUTTON_CLICKED: "global errors view filters close button clicked", GLOBAL_ERRORS_VIEW_CLEAR_FILTERS_BUTTON_CLICKED: diff --git a/src/featureFlags.ts b/src/featureFlags.ts index 29b4a46b3..b034a2490 100644 --- a/src/featureFlags.ts +++ b/src/featureFlags.ts @@ -22,7 +22,9 @@ export const featureFlagMinBackendVersions: Record = { [FeatureFlag.IS_METRICS_REPORT_ENDPOINT_VIEW_ENABLED]: "0.3.122-alpha.3", [FeatureFlag.ARE_GLOBAL_ERRORS_ENABLED]: "0.3.129", [FeatureFlag.ARE_GLOBAL_ERRORS_FILTERS_ENABLED]: "0.3.140-alpha.2", - [FeatureFlag.IS_ERROR_OCCURRENCE_CHART_ENABLED]: "0.3.141-alpha.3" + [FeatureFlag.IS_ERROR_OCCURRENCE_CHART_ENABLED]: "0.3.141-alpha.3", + [FeatureFlag.ARE_GLOBAL_ERRORS_CRITICALITY_AND_UNHANDLED_FILTERS_ENABLED]: + "0.3.145" }; export const getFeatureFlagValue = ( diff --git a/src/store/errors/errorsSlice.ts b/src/store/errors/errorsSlice.ts index 24bcb60ab..b1b8ec9ca 100644 --- a/src/store/errors/errorsSlice.ts +++ b/src/store/errors/errorsSlice.ts @@ -26,7 +26,7 @@ export interface GlobalErrorsSelectedFiltersState { services: string[]; endpoints: string[]; errorTypes: string[]; - criticality: ErrorCriticality[]; + criticalities: ErrorCriticality[]; handlingTypes: ErrorHandlingType[]; } @@ -47,7 +47,7 @@ const selectedFiltersInitialState = { services: [], endpoints: [], errorTypes: [], - criticality: [], + criticalities: [], handlingTypes: [] }; diff --git a/src/types.ts b/src/types.ts index 500bdc1df..02ac34faa 100644 --- a/src/types.ts +++ b/src/types.ts @@ -24,7 +24,8 @@ export enum FeatureFlag { IS_METRICS_REPORT_ENDPOINT_VIEW_ENABLED, ARE_GLOBAL_ERRORS_ENABLED, ARE_GLOBAL_ERRORS_FILTERS_ENABLED, - IS_ERROR_OCCURRENCE_CHART_ENABLED + IS_ERROR_OCCURRENCE_CHART_ENABLED, + ARE_GLOBAL_ERRORS_CRITICALITY_AND_UNHANDLED_FILTERS_ENABLED } export enum InsightType {