From 7960a7bcc905b24f2b398d71c3cd1fe1ac3c0427 Mon Sep 17 00:00:00 2001 From: Shaheer Kochai Date: Tue, 27 Aug 2024 13:04:25 +0430 Subject: [PATCH] alert rule history skeleton using static data (#5688) * feat: alert history basic tabs and fitlers UI * feat: route based tabs for alert history and overview and improve the UI to match designs * feat: top contributors UI using static data * feat: avg. resolution time and total triggered stats card UI using static data * feat: tabs component * feat: timeline tabs and filters * feat: overall status graph UI using dummy data with graph placeholder * feat: timeline table and pagination UI using dummy data * fix: bugfix in reset tabs * feat: add popover to go to logs/traces to top contributors and timeline table * chore: remove comments * chore: rename AlertIcon to AlertState * fix: add cursor pointer to timeline table rows * feat: add parent tabs to alert history * chore: add icon to the configure tab * fix: display popover on hovering the more button in see more component * fix: wrap key value label * feat: alert rule history enable/disable toggle UI * Feat: get alert history data from API (#5718) * feat: alert history basic tabs and fitlers UI * feat: route based tabs for alert history and overview and improve the UI to match designs * feat: data state renderer component * feat: get total triggered and avg. resolution cards data from API * fix: hide stats card if we get NaN * chore: improve rule stats types * feat: get top contributors data from API * feat: get timeline table data from API * fix: properly render change percentage indicator * feat: total triggered and avg resolution empty states * fix: fix stats height issue that would cause short border-right in empty case * feat: top contributors empty state * fix: fix table and graph borders * feat: build alert timeline labels filter and handle client side filtering * fix: select the first tab on clicking reset * feat: set param and send in payload on clicking timeline filter tabs * Feat: alert history timeline remaining subtasks except graphs (#5720) * feat: alert history basic tabs and fitlers UI * feat: route based tabs for alert history and overview and improve the UI to match designs * feat: implement timeline table sorting * chore: add initial count to see more and alert labels * chore: move PaginationInfoText component to /periscope * chore: implement top contributor rows using Ant Table * feat: top contributors view all * fix: hide border for last row and prevent layout shift in top contributors by specifying height * feat: properly display duration in average resolution time * fix: properly display normal alert rule state * feat: add/remove view all top contributors param to url on opening/closing view all * feat: calculate start and end time from relative time and add/remove param to url * fix: fix console warnings * fix: enable timeline table query only if start and end times exist * feat: handle enable/disable alert rule toggle request * chore: replace string values with constants * fix: hide stats card if only past data is available + remove unnecessary states from AlertState * fix: redirect configure alert rule to alert overview tab * fix: display total triggers in timeline chart wrapper based on API response data * fix: choosing the same relative time doesn't udpate start and end time * Feat: total triggered and avg. resolution time graph (#5750) * feat: alert history basic tabs and fitlers UI * feat: route based tabs for alert history and overview and improve the UI to match designs * feat: handle enable/disable alert rule toggle request * feat: stats card line chart * fix: overall improvements to stats card graph * fix: overall UI improvements to match the Figma screens * chore: remove duplicate hook * fix: make the changes w.r.t timeline table API changes to prevent breaking the page * fix: update stats card null check based on updated API response * feat: stats card no previous data UI * feat: redirect to 404 page if rule id is invalid * chore: improve alert enable toggle success toast message * feat: get top contributors row and timeline table row related logs and traces links from API * feat: get total items from API and make pagination work * feat: implement timeline filters based on API response * fix: in case of current and target units, convert the value unit in timeline table * fix: timeline table y axis unit null check * fix: hide stats card graph if only a single entry is there in timeseries * chore: redirect alert from all alerts to overview tab * fix: prevent adding extra unnecessary params on clicking alerts top level tabs * chore: use conditional alert popover in timeline table and import the scss file * fix: prevent infinity if we receive totalPastTriggers as '0' * fix: improve UI to be pixel perfect based on figma designs * fix: fix the incorrect change direction * fix: add height to top contributors row * feat: alert history light mode * fix: remove the extra padding from alert overview query builder tabs * chore: overall improvements * chore: remove mock file * fix: overall improvements * fix: add dark mode support for top contributors empty state * chore: improve timeline chart placeholder bg in light mode * Feat: alert history horizontal timeline chart (#5773) * feat: timeline horizontal chart * fix: remove the labels from horizontal timeline chart * chore: add null check to timeline chart * chore: hide cursor from timeline chart * fix: fix the blank container being displayed in loading state --- frontend/src/AppRoutes/pageComponents.ts | 4 +- frontend/src/api/alerts/patch.ts | 4 + frontend/src/api/alerts/ruleStats.ts | 28 + frontend/src/api/alerts/timelineGraph.ts | 33 + frontend/src/api/alerts/timelineTable.ts | 36 + frontend/src/api/alerts/topContributors.ts | 33 + .../src/assets/AlertHistory/ConfigureIcon.tsx | 41 ++ frontend/src/assets/AlertHistory/LogsIcon.tsx | 65 ++ .../AlertDetailsFilters/Filters.tsx | 9 +- .../AlertDetailsFilters/filters.styles.scss | 7 + .../components/TabsAndFilters/Tabs/Tabs.tsx | 41 ++ .../TabsAndFilters/Tabs/tabs.styles.scss} | 3 +- .../TabsAndFilters/TabsAndFilters.tsx | 16 + .../components/TabsAndFilters/constants.ts | 5 + .../TabsAndFilters/tabsAndFilters.styles.scss | 18 + frontend/src/constants/reactQueryKeys.ts | 5 + .../AlertPopover/AlertPopover.styles.scss | 3 + .../AlertPopover/AlertPopover.tsx | 113 +++ .../AverageResolutionCard.tsx | 28 + .../AlertHistory/Statistics/Statistics.tsx | 23 + .../Statistics/StatsCard/StatsCard.tsx | 124 ++++ .../StatsCard/StatsGraph/StatsGraph.tsx | 90 +++ .../StatsCard/statsCard.styles.scss | 112 +++ .../StatsCardsRenderer/StatsCardsRenderer.tsx | 102 +++ .../TopContributorsCard.tsx | 83 +++ .../TopContributorsContent.tsx | 59 ++ .../TopContributorsRows.tsx | 86 +++ .../TopContributorsCard/ViewAllDrawer.tsx | 46 ++ .../topContributorsCard.styles.scss | 190 +++++ .../Statistics/TopContributorsCard/types.ts | 6 + .../TopContributorsRenderer.tsx | 42 ++ .../TotalTriggeredCard/TotalTriggeredCard.tsx | 26 + .../Statistics/statistics.styles.scss | 14 + .../AlertHistory/Timeline/Graph/Graph.tsx | 100 +++ .../AlertHistory/Timeline/Graph/constants.ts | 32 + .../Timeline/Graph/graph.styles.scss | 52 ++ .../Timeline/GraphWrapper/GraphWrapper.tsx | 46 ++ .../AlertHistory/Timeline/Table/Table.tsx | 66 ++ .../Timeline/Table/table.styles.scss | 127 ++++ .../AlertHistory/Timeline/Table/types.ts | 9 + .../Timeline/Table/useTimelineTable.tsx | 106 +++ .../TabsAndFilters/TabsAndFilters.tsx | 89 +++ .../TabsAndFilters/tabsAndFilters.styles.scss | 32 + .../AlertHistory/Timeline/Timeline.tsx | 58 ++ .../Timeline/timeline.styles.scss | 14 + .../AlertHistory/alertHistory.styles.scss | 5 + .../src/container/AlertHistory/constants.ts | 1 + frontend/src/container/AlertHistory/index.tsx | 17 +- frontend/src/container/AlertHistory/types.ts | 11 + frontend/src/container/AppLayout/index.tsx | 6 +- .../FormAlertRules/QuerySection.styles.scss | 3 + .../src/container/FormAlertRules/index.tsx | 3 +- .../container/ListAlertRules/ListAlert.tsx | 2 +- .../lib/uPlotLib/plugins/timelinePlugin.ts | 670 ++++++++++++++++++ .../ActionButtons/actionButtons.styles.scss | 27 + .../AlertHeader/ActionButtons/index.tsx | 29 +- .../AlertDetails/AlertHeader/AlertHeader.tsx | 36 +- .../AlertHeader/AlertIcon/AlertIcon.tsx | 49 -- .../AlertHeader/AlertLabels/AlertLabels.tsx | 17 +- .../AlertLabels/alertLabels.styles.scss | 3 +- .../AlertHeader/AlertState/AlertState.tsx | 71 ++ .../AlertState/alertState.styles.scss | 10 + .../AlertStatus/alertStatus.styles.scss | 6 + .../AlertHeader/alertHeader.styles.scss | 66 +- .../AlertDetails/alertDetails.styles.scss | 166 ++++- frontend/src/pages/AlertDetails/hooks.tsx | 375 +++++++++- frontend/src/pages/AlertDetails/index.tsx | 83 +-- frontend/src/pages/AlertList/index.tsx | 45 +- frontend/src/pages/EditRules/index.tsx | 5 +- .../copyToClipboard.styles.scss | 27 +- .../components/CopyToClipboard/index.tsx | 9 +- .../DataStateRenderer/DataStateRenderer.tsx | 46 ++ .../KeyValueLabel/keyValueLabel.styles.scss | 27 +- .../PaginationInfoText/PaginationInfoText.tsx | 24 + .../periscope/components/SeeMore/SeeMore.tsx | 42 +- .../components/SeeMore/seeMore.styles.scss | 16 + .../src/periscope/components/Tabs2/Tabs2.tsx | 79 +++ .../components/Tabs2/tabs2.styles.scss | 48 ++ frontend/src/types/api/alerts/def.ts | 66 +- frontend/src/types/api/alerts/ruleStats.ts | 7 + .../src/types/api/alerts/timelineGraph.ts | 11 + .../src/types/api/alerts/timelineTable.ts | 16 + .../src/types/api/alerts/topContributors.ts | 7 + frontend/src/utils/calculateChange.ts | 31 + frontend/src/utils/timeUtils.ts | 68 ++ 85 files changed, 4250 insertions(+), 206 deletions(-) create mode 100644 frontend/src/api/alerts/ruleStats.ts create mode 100644 frontend/src/api/alerts/timelineGraph.ts create mode 100644 frontend/src/api/alerts/timelineTable.ts create mode 100644 frontend/src/api/alerts/topContributors.ts create mode 100644 frontend/src/assets/AlertHistory/ConfigureIcon.tsx create mode 100644 frontend/src/assets/AlertHistory/LogsIcon.tsx create mode 100644 frontend/src/components/TabsAndFilters/Tabs/Tabs.tsx rename frontend/src/{pages/AlertDetails/AlertHeader/AlertIcon/alertIcon.styles.scss => components/TabsAndFilters/Tabs/tabs.styles.scss} (62%) create mode 100644 frontend/src/components/TabsAndFilters/TabsAndFilters.tsx create mode 100644 frontend/src/components/TabsAndFilters/constants.ts create mode 100644 frontend/src/components/TabsAndFilters/tabsAndFilters.styles.scss create mode 100644 frontend/src/container/AlertHistory/AlertPopover/AlertPopover.styles.scss create mode 100644 frontend/src/container/AlertHistory/AlertPopover/AlertPopover.tsx create mode 100644 frontend/src/container/AlertHistory/Statistics/AverageResolutionCard/AverageResolutionCard.tsx create mode 100644 frontend/src/container/AlertHistory/Statistics/Statistics.tsx create mode 100644 frontend/src/container/AlertHistory/Statistics/StatsCard/StatsCard.tsx create mode 100644 frontend/src/container/AlertHistory/Statistics/StatsCard/StatsGraph/StatsGraph.tsx create mode 100644 frontend/src/container/AlertHistory/Statistics/StatsCard/statsCard.styles.scss create mode 100644 frontend/src/container/AlertHistory/Statistics/StatsCardsRenderer/StatsCardsRenderer.tsx create mode 100644 frontend/src/container/AlertHistory/Statistics/TopContributorsCard/TopContributorsCard.tsx create mode 100644 frontend/src/container/AlertHistory/Statistics/TopContributorsCard/TopContributorsContent.tsx create mode 100644 frontend/src/container/AlertHistory/Statistics/TopContributorsCard/TopContributorsRows.tsx create mode 100644 frontend/src/container/AlertHistory/Statistics/TopContributorsCard/ViewAllDrawer.tsx create mode 100644 frontend/src/container/AlertHistory/Statistics/TopContributorsCard/topContributorsCard.styles.scss create mode 100644 frontend/src/container/AlertHistory/Statistics/TopContributorsCard/types.ts create mode 100644 frontend/src/container/AlertHistory/Statistics/TopContributorsRenderer/TopContributorsRenderer.tsx create mode 100644 frontend/src/container/AlertHistory/Statistics/TotalTriggeredCard/TotalTriggeredCard.tsx create mode 100644 frontend/src/container/AlertHistory/Statistics/statistics.styles.scss create mode 100644 frontend/src/container/AlertHistory/Timeline/Graph/Graph.tsx create mode 100644 frontend/src/container/AlertHistory/Timeline/Graph/constants.ts create mode 100644 frontend/src/container/AlertHistory/Timeline/Graph/graph.styles.scss create mode 100644 frontend/src/container/AlertHistory/Timeline/GraphWrapper/GraphWrapper.tsx create mode 100644 frontend/src/container/AlertHistory/Timeline/Table/Table.tsx create mode 100644 frontend/src/container/AlertHistory/Timeline/Table/table.styles.scss create mode 100644 frontend/src/container/AlertHistory/Timeline/Table/types.ts create mode 100644 frontend/src/container/AlertHistory/Timeline/Table/useTimelineTable.tsx create mode 100644 frontend/src/container/AlertHistory/Timeline/TabsAndFilters/TabsAndFilters.tsx create mode 100644 frontend/src/container/AlertHistory/Timeline/TabsAndFilters/tabsAndFilters.styles.scss create mode 100644 frontend/src/container/AlertHistory/Timeline/Timeline.tsx create mode 100644 frontend/src/container/AlertHistory/Timeline/timeline.styles.scss create mode 100644 frontend/src/container/AlertHistory/constants.ts create mode 100644 frontend/src/lib/uPlotLib/plugins/timelinePlugin.ts delete mode 100644 frontend/src/pages/AlertDetails/AlertHeader/AlertIcon/AlertIcon.tsx create mode 100644 frontend/src/pages/AlertDetails/AlertHeader/AlertState/AlertState.tsx create mode 100644 frontend/src/pages/AlertDetails/AlertHeader/AlertState/alertState.styles.scss create mode 100644 frontend/src/periscope/components/DataStateRenderer/DataStateRenderer.tsx create mode 100644 frontend/src/periscope/components/PaginationInfoText/PaginationInfoText.tsx create mode 100644 frontend/src/periscope/components/Tabs2/Tabs2.tsx create mode 100644 frontend/src/periscope/components/Tabs2/tabs2.styles.scss create mode 100644 frontend/src/types/api/alerts/ruleStats.ts create mode 100644 frontend/src/types/api/alerts/timelineGraph.ts create mode 100644 frontend/src/types/api/alerts/timelineTable.ts create mode 100644 frontend/src/types/api/alerts/topContributors.ts create mode 100644 frontend/src/utils/calculateChange.ts diff --git a/frontend/src/AppRoutes/pageComponents.ts b/frontend/src/AppRoutes/pageComponents.ts index a02a2e6c00..6da2191971 100644 --- a/frontend/src/AppRoutes/pageComponents.ts +++ b/frontend/src/AppRoutes/pageComponents.ts @@ -93,11 +93,11 @@ export const CreateNewAlerts = Loadable( ); export const AlertHistory = Loadable( - () => import(/* webpackChunkName: "Alert History" */ 'pages/AlertDetails'), + () => import(/* webpackChunkName: "Alert History" */ 'pages/AlertList'), ); export const AlertOverview = Loadable( - () => import(/* webpackChunkName: "Alert Overview" */ 'pages/AlertDetails'), + () => import(/* webpackChunkName: "Alert Overview" */ 'pages/AlertList'), ); export const CreateAlertChannelAlerts = Loadable( diff --git a/frontend/src/api/alerts/patch.ts b/frontend/src/api/alerts/patch.ts index 920b53ae9f..0f422cd66c 100644 --- a/frontend/src/api/alerts/patch.ts +++ b/frontend/src/api/alerts/patch.ts @@ -19,6 +19,10 @@ const patch = async ( payload: response.data.data, }; } catch (error) { + if (window.location.href.includes('alerts/history')) { + throw error as AxiosError; + } + return ErrorResponseHandler(error as AxiosError); } }; diff --git a/frontend/src/api/alerts/ruleStats.ts b/frontend/src/api/alerts/ruleStats.ts new file mode 100644 index 0000000000..2e09751e0f --- /dev/null +++ b/frontend/src/api/alerts/ruleStats.ts @@ -0,0 +1,28 @@ +import axios from 'api'; +import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; +import { AxiosError } from 'axios'; +import { ErrorResponse, SuccessResponse } from 'types/api'; +import { AlertRuleStatsPayload } from 'types/api/alerts/def'; +import { RuleStatsProps } from 'types/api/alerts/ruleStats'; + +const ruleStats = async ( + props: RuleStatsProps, +): Promise | ErrorResponse> => { + try { + const response = await axios.post(`/rules/${props.id}/history/stats`, { + start: props.start, + end: props.end, + }); + + return { + statusCode: 200, + error: null, + message: response.data.status, + payload: response.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default ruleStats; diff --git a/frontend/src/api/alerts/timelineGraph.ts b/frontend/src/api/alerts/timelineGraph.ts new file mode 100644 index 0000000000..8073943d72 --- /dev/null +++ b/frontend/src/api/alerts/timelineGraph.ts @@ -0,0 +1,33 @@ +import axios from 'api'; +import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; +import { AxiosError } from 'axios'; +import { ErrorResponse, SuccessResponse } from 'types/api'; +import { AlertRuleTimelineGraphResponsePayload } from 'types/api/alerts/def'; +import { GetTimelineGraphRequestProps } from 'types/api/alerts/timelineGraph'; + +const timelineGraph = async ( + props: GetTimelineGraphRequestProps, +): Promise< + SuccessResponse | ErrorResponse +> => { + try { + const response = await axios.post( + `/rules/${props.id}/history/overall_status`, + { + start: props.start, + end: props.end, + }, + ); + + return { + statusCode: 200, + error: null, + message: response.data.status, + payload: response.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default timelineGraph; diff --git a/frontend/src/api/alerts/timelineTable.ts b/frontend/src/api/alerts/timelineTable.ts new file mode 100644 index 0000000000..8d7f3edee7 --- /dev/null +++ b/frontend/src/api/alerts/timelineTable.ts @@ -0,0 +1,36 @@ +import axios from 'api'; +import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; +import { AxiosError } from 'axios'; +import { ErrorResponse, SuccessResponse } from 'types/api'; +import { AlertRuleTimelineTableResponsePayload } from 'types/api/alerts/def'; +import { GetTimelineTableRequestProps } from 'types/api/alerts/timelineTable'; + +const timelineTable = async ( + props: GetTimelineTableRequestProps, +): Promise< + SuccessResponse | ErrorResponse +> => { + try { + const response = await axios.post(`/rules/${props.id}/history/timeline`, { + start: props.start, + end: props.end, + offset: props.offset, + limit: props.limit, + order: props.order, + state: props.state, + // TODO(shaheer): implement filters + filters: props.filters, + }); + + return { + statusCode: 200, + error: null, + message: response.data.status, + payload: response.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default timelineTable; diff --git a/frontend/src/api/alerts/topContributors.ts b/frontend/src/api/alerts/topContributors.ts new file mode 100644 index 0000000000..7d3f2baec1 --- /dev/null +++ b/frontend/src/api/alerts/topContributors.ts @@ -0,0 +1,33 @@ +import axios from 'api'; +import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; +import { AxiosError } from 'axios'; +import { ErrorResponse, SuccessResponse } from 'types/api'; +import { AlertRuleTopContributorsPayload } from 'types/api/alerts/def'; +import { TopContributorsProps } from 'types/api/alerts/topContributors'; + +const topContributors = async ( + props: TopContributorsProps, +): Promise< + SuccessResponse | ErrorResponse +> => { + try { + const response = await axios.post( + `/rules/${props.id}/history/top_contributors`, + { + start: props.start, + end: props.end, + }, + ); + + return { + statusCode: 200, + error: null, + message: response.data.status, + payload: response.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default topContributors; diff --git a/frontend/src/assets/AlertHistory/ConfigureIcon.tsx b/frontend/src/assets/AlertHistory/ConfigureIcon.tsx new file mode 100644 index 0000000000..05268b8f5f --- /dev/null +++ b/frontend/src/assets/AlertHistory/ConfigureIcon.tsx @@ -0,0 +1,41 @@ +interface ConfigureIconProps { + width?: number; + height?: number; + fill?: string; +} + +function ConfigureIcon({ + width, + height, + fill, +}: ConfigureIconProps): JSX.Element { + return ( + + + + + ); +} + +ConfigureIcon.defaultProps = { + width: 16, + height: 16, + fill: 'none', +}; +export default ConfigureIcon; diff --git a/frontend/src/assets/AlertHistory/LogsIcon.tsx b/frontend/src/assets/AlertHistory/LogsIcon.tsx new file mode 100644 index 0000000000..8ffcaaa90b --- /dev/null +++ b/frontend/src/assets/AlertHistory/LogsIcon.tsx @@ -0,0 +1,65 @@ +interface LogsIconProps { + width?: number; + height?: number; + fill?: string; + strokeColor?: string; + strokeWidth?: number; +} + +function LogsIcon({ + width, + height, + fill, + strokeColor, + strokeWidth, +}: LogsIconProps): JSX.Element { + return ( + + + + + + + + + ); +} + +LogsIcon.defaultProps = { + width: 14, + height: 14, + fill: 'none', + strokeColor: '#C0C1C3', + strokeWidth: 1.167, +}; + +export default LogsIcon; diff --git a/frontend/src/components/AlertDetailsFilters/Filters.tsx b/frontend/src/components/AlertDetailsFilters/Filters.tsx index f50ca588a8..c5a2f95fee 100644 --- a/frontend/src/components/AlertDetailsFilters/Filters.tsx +++ b/frontend/src/components/AlertDetailsFilters/Filters.tsx @@ -2,6 +2,7 @@ import './filters.styles.scss'; import { Button } from 'antd'; import { QueryParams } from 'constants/query'; +import { RelativeTimeMap } from 'container/TopNav/DateTimeSelection/config'; import DateTimeSelector from 'container/TopNav/DateTimeSelectionV2'; import useUrlQuery from 'hooks/useUrlQuery'; import { Undo } from 'lucide-react'; @@ -10,10 +11,12 @@ import { useHistory } from 'react-router-dom'; export function Filters(): JSX.Element { const urlQuery = useUrlQuery(); const history = useHistory(); + const relativeTime = urlQuery.get(QueryParams.relativeTime); const handleFiltersReset = (): void => { - urlQuery.delete(QueryParams.relativeTime); - + urlQuery.set(QueryParams.relativeTime, RelativeTimeMap['30min']); + urlQuery.delete(QueryParams.startTime); + urlQuery.delete(QueryParams.endTime); history.replace({ pathname: history.location.pathname, search: `?${urlQuery.toString()}`, @@ -21,7 +24,7 @@ export function Filters(): JSX.Element { }; return (
- {urlQuery.has(QueryParams.relativeTime) && ( + {relativeTime !== RelativeTimeMap['30min'] && (