diff --git a/src/components/Errors/GlobalErrorsList/index.tsx b/src/components/Errors/GlobalErrorsList/index.tsx index 946f5fa1d..65b1df20a 100644 --- a/src/components/Errors/GlobalErrorsList/index.tsx +++ b/src/components/Errors/GlobalErrorsList/index.tsx @@ -1,4 +1,5 @@ import { useEffect, useMemo, useRef, useState } from "react"; +import { dispatcher } from "../../../dispatcher"; import { getFeatureFlagValue } from "../../../featureFlags"; import { DataFetcherConfiguration, @@ -38,7 +39,7 @@ import { export const GlobalErrorsList = () => { const { goTo } = useHistory(); const [isSortingMenuOpen, setIsSortingMenuOpen] = useState(false); - const listContainerRef = useRef(null); + const listContainerRef = useRef(null); const { environment, backendInfo } = useConfigSelector(); @@ -129,11 +130,29 @@ export const GlobalErrorsList = () => { ] ); - const { data } = useFetchData< + const { data, getData } = useFetchData< GetGlobalErrorsDataPayload, SetGlobalErrorsDataPayload >(dataFetcherConfiguration, payload); + // Refresh data after pin/unpin actions + useEffect(() => { + dispatcher.addActionListener(actions.SET_PIN_ERROR_RESULT, getData); + dispatcher.addActionListener(actions.SET_UNPIN_ERROR_RESULT, getData); + + return () => { + dispatcher.removeActionListener(actions.SET_PIN_ERROR_RESULT, getData); + dispatcher.removeActionListener(actions.SET_UNPIN_ERROR_RESULT, getData); + }; + }, [getData]); + + // Cleanup errors store slice on unmount + useMount(() => { + return () => { + resetGlobalErrors(); + }; + }); + // Set data to store on fetch useEffect(() => { if (data) { @@ -167,13 +186,6 @@ export const GlobalErrorsList = () => { } }, [environmentId, search, sorting, page]); - // Cleanup errors store slice on unmount - useMount(() => { - return () => { - resetGlobalErrors(); - }; - }); - const handleErrorSourceLinkClick = (errorId: string) => { goTo(errorId); }; diff --git a/src/components/Errors/GlobalErrorsList/types.ts b/src/components/Errors/GlobalErrorsList/types.ts index 8f7e4abe4..4bf9c3d83 100644 --- a/src/components/Errors/GlobalErrorsList/types.ts +++ b/src/components/Errors/GlobalErrorsList/types.ts @@ -41,6 +41,7 @@ export interface GlobalErrorData { Unhandled: number; }; }; + isPinned?: boolean; } export interface SetGlobalErrorsDataPayload { diff --git a/src/components/Errors/NewErrorCard/index.tsx b/src/components/Errors/NewErrorCard/index.tsx index 115f0197b..a9ab7413e 100644 --- a/src/components/Errors/NewErrorCard/index.tsx +++ b/src/components/Errors/NewErrorCard/index.tsx @@ -11,14 +11,16 @@ import { } from "../../common/AffectedEndpointsSelector"; import { Option } from "../../common/AffectedEndpointsSelector/types"; import { HistogramIcon } from "../../common/icons/16px/HistogramIcon"; +import { PinIcon } from "../../common/icons/16px/PinIcon"; import { NewIconButton } from "../../common/v3/NewIconButton"; import { Tooltip } from "../../common/v3/Tooltip"; +import { actions } from "../actions"; import { TimestampKeyValue } from "../ErrorCard/TimestampKeyValue"; import { getTagType, HIGH_SEVERITY_SCORE_THRESHOLD } from "../Score"; import { trackingEvents } from "../tracking"; import { OccurrenceChart } from "./OccurrenceChart"; import * as s from "./styles"; -import { NewErrorCardProps } from "./types"; +import { NewErrorCardProps, PinErrorPayload, UnpinErrorPayload } from "./types"; export const getStatusString = (status: string) => status.toLowerCase().startsWith("recent") ? "Recent" : status; @@ -29,13 +31,18 @@ export const NewErrorCard = ({ }: NewErrorCardProps) => { const [isHistogramVisible, setIsHistogramVisible] = useState(false); const chartContainerRef = useRef(null); - const { backendInfo } = useConfigSelector(); + const { environment, backendInfo } = useConfigSelector(); const isOccurrenceChartEnabled = getFeatureFlagValue( backendInfo, FeatureFlag.IS_ERROR_OCCURRENCE_CHART_ENABLED ); + const isPinEnabled = getFeatureFlagValue( + backendInfo, + FeatureFlag.IS_GLOBAL_ERROR_PIN_ENABLED + ); + const { id, affectedEndpoints, @@ -112,12 +119,69 @@ export const NewErrorCard = ({ setIsHistogramVisible(!isHistogramVisible); }; + const handlePinUnpinButtonClick = () => { + const value = !data.isPinned; + sendUserActionTrackingEvent( + trackingEvents.ERROR_CARD_PIN_UNPIN_BUTTON_CLICKED, + { + value + } + ); + + if (!environment) { + return; + } + + if (value) { + window.sendMessageToDigma({ + action: actions.PIN_ERROR, + payload: { + id, + environment: environment.id + } + }); + } else { + window.sendMessageToDigma({ + action: actions.UNPIN_ERROR, + payload: { + id, + environment: environment.id + } + }); + } + }; + const isCritical = score.score > HIGH_SEVERITY_SCORE_THRESHOLD; const selectorValue = selectedEndpoint ? getEndpointKey(selectedEndpoint) : undefined; + const toolbarActions = [ + ...(isPinEnabled + ? [ + + ] + : []), + ...(isOccurrenceChartEnabled && selectedEndpoint + ? [ + + ] + : []) + ]; + return ( @@ -186,16 +250,9 @@ export const NewErrorCard = ({ /> - - - )} + {toolbarActions.length > 0 && {toolbarActions}} ); }; diff --git a/src/components/Errors/NewErrorCard/types.ts b/src/components/Errors/NewErrorCard/types.ts index b26c49966..957a535e7 100644 --- a/src/components/Errors/NewErrorCard/types.ts +++ b/src/components/Errors/NewErrorCard/types.ts @@ -14,3 +14,13 @@ export interface OccurrenceChartContainerProps { $transitionDuration: number; $transitionClassName: string; } + +export interface PinErrorPayload { + id: string; + environment: string; +} + +export interface UnpinErrorPayload { + id: string; + environment: string; +} diff --git a/src/components/Errors/actions.ts b/src/components/Errors/actions.ts index bbe717a65..ea8f49364 100644 --- a/src/components/Errors/actions.ts +++ b/src/components/Errors/actions.ts @@ -17,5 +17,9 @@ export const actions = addPrefix(ACTION_PREFIX, { GET_GLOBAL_ERRORS_FILTERS_DATA: "GET_GLOBAL_ERRORS_FILTERS_DATA", SET_GLOBAL_ERRORS_FILTERS_DATA: "SET_GLOBAL_ERRORS_FILTERS_DATA", GET_ERROR_TIME_SERIES_DATA: "GET_ERROR_TIME_SERIES_DATA", - SET_ERROR_TIME_SERIES_DATA: "SET_ERROR_TIME_SERIES_DATA" + SET_ERROR_TIME_SERIES_DATA: "SET_ERROR_TIME_SERIES_DATA", + PIN_ERROR: "PIN_ERROR", + SET_PIN_ERROR_RESULT: "SET_PIN_ERROR_RESULT", + UNPIN_ERROR: "UNPIN_ERROR", + SET_UNPIN_ERROR_RESULT: "SET_UNPIN_ERROR_RESULT" }); diff --git a/src/components/Errors/tracking.ts b/src/components/Errors/tracking.ts index 76676bced..69521eeae 100644 --- a/src/components/Errors/tracking.ts +++ b/src/components/Errors/tracking.ts @@ -39,7 +39,8 @@ export const trackingEvents = addPrefix( "error card selected affected endpoint changed", ERROR_CARD_AFFECTED_ENDPOINT_LINK_CLICKED: "error card affected endpoint link clicked", - ERROR_CARD_HISTOGRAM_BUTTON_CLICKED: "error card histogram button clicked" + ERROR_CARD_HISTOGRAM_BUTTON_CLICKED: "error card histogram button clicked", + ERROR_CARD_PIN_UNPIN_BUTTON_CLICKED: "error card pin unpin button clicked" }, " " ); diff --git a/src/featureFlags.ts b/src/featureFlags.ts index b034a2490..423a44cf4 100644 --- a/src/featureFlags.ts +++ b/src/featureFlags.ts @@ -24,7 +24,8 @@ export const featureFlagMinBackendVersions: Record = { [FeatureFlag.ARE_GLOBAL_ERRORS_FILTERS_ENABLED]: "0.3.140-alpha.2", [FeatureFlag.IS_ERROR_OCCURRENCE_CHART_ENABLED]: "0.3.141-alpha.3", [FeatureFlag.ARE_GLOBAL_ERRORS_CRITICALITY_AND_UNHANDLED_FILTERS_ENABLED]: - "0.3.145" + "0.3.145", + [FeatureFlag.IS_GLOBAL_ERROR_PIN_ENABLED]: "0.3.147" // TODO: Verify this value after release }; export const getFeatureFlagValue = ( diff --git a/src/types.ts b/src/types.ts index 02ac34faa..3cfc5239e 100644 --- a/src/types.ts +++ b/src/types.ts @@ -25,7 +25,8 @@ export enum FeatureFlag { ARE_GLOBAL_ERRORS_ENABLED, ARE_GLOBAL_ERRORS_FILTERS_ENABLED, IS_ERROR_OCCURRENCE_CHART_ENABLED, - ARE_GLOBAL_ERRORS_CRITICALITY_AND_UNHANDLED_FILTERS_ENABLED + ARE_GLOBAL_ERRORS_CRITICALITY_AND_UNHANDLED_FILTERS_ENABLED, + IS_GLOBAL_ERROR_PIN_ENABLED } export enum InsightType {