Skip to content

Commit

Permalink
Fix: bugfixes and overall improvements to alert history (#5841)
Browse files Browse the repository at this point in the history
* fix: don't display severity label

* chore: remove id from alert header

* chore: add tooltip to enable/disable alert toggle

* chore: update enable/disbale toast message

* fix: set default relative time to 6h if relative time is not provided

* chore: update empty top contributors text and remove configure alert

* chore: temporarily hide value column from timeline column

* fix: use correct links for logs and traces in alert popover

* fix: properly set timeline table offset

* fix: display all values in graph

* fix: resolve conflicts

* chore: remove style for value column in timeline table

* chore: temporarily hide labels search

* fix: incorrect current page in pagination info text

* chore: remove label QB search

* chore: remove value column

* chore: remove commented code

* fix: show traces button when trace link is available

* fix: display horizontal chart even for a single entry

* fix: show inactive state in horizontal similar to normal state

* fix: properly render inactive state in horizontal chart

* fix: properly handle preserving alert toggle between overview and history tabs

* feat: get page size from query param

* chore: remove commented code + minor refactor

* chore: remove tsconfi.tmp

* fix: don't add default relative time if start and times exist in the url

* feat: display date range preview for stat cards

* chore: remove custom dropdown renderer component
  • Loading branch information
ahmadshaheer committed Sep 4, 2024
1 parent a539bb5 commit a7a2209
Show file tree
Hide file tree
Showing 16 changed files with 249 additions and 297 deletions.
35 changes: 19 additions & 16 deletions frontend/src/AppRoutes/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { ResourceProvider } from 'hooks/useResourceAttribute';
import history from 'lib/history';
import { identity, pick, pickBy } from 'lodash-es';
import posthog from 'posthog-js';
import AlertRuleProvider from 'providers/Alert';
import { DashboardProvider } from 'providers/Dashboard/Dashboard';
import { QueryBuilderProvider } from 'providers/QueryBuilder';
import { Suspense, useEffect, useState } from 'react';
Expand Down Expand Up @@ -236,22 +237,24 @@ function App(): JSX.Element {
<QueryBuilderProvider>
<DashboardProvider>
<KeyboardHotkeysProvider>
<AppLayout>
<Suspense fallback={<Spinner size="large" tip="Loading..." />}>
<Switch>
{routes.map(({ path, component, exact }) => (
<Route
key={`${path}`}
exact={exact}
path={path}
component={component}
/>
))}

<Route path="*" component={NotFound} />
</Switch>
</Suspense>
</AppLayout>
<AlertRuleProvider>
<AppLayout>
<Suspense fallback={<Spinner size="large" tip="Loading..." />}>
<Switch>
{routes.map(({ path, component, exact }) => (
<Route
key={`${path}`}
exact={exact}
path={path}
component={component}
/>
))}

<Route path="*" component={NotFound} />
</Switch>
</Suspense>
</AppLayout>
</AlertRuleProvider>
</KeyboardHotkeysProvider>
</DashboardProvider>
</QueryBuilderProvider>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ function PopoverContent({
const isDarkMode = useIsDarkMode();
return (
<div className="contributor-row-popover-buttons">
{!!relatedTracesLink && (
{!!relatedLogsLink && (
<Link
to={`${ROUTES.LOGS_EXPLORER}?${relatedTracesLink}`}
to={`${ROUTES.LOGS_EXPLORER}?${relatedLogsLink}`}
className="contributor-row-popover-buttons__button"
>
<div className="icon">
Expand All @@ -36,9 +36,9 @@ function PopoverContent({
<div className="text">View Logs</div>
</Link>
)}
{!!relatedLogsLink && (
{!!relatedTracesLink && (
<Link
to={`${ROUTES.TRACES_EXPLORER}?${relatedLogsLink}`}
to={`${ROUTES.TRACES_EXPLORER}?${relatedTracesLink}`}
className="contributor-row-popover-buttons__button"
>
<div className="icon">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
import './StatsCard.styles.scss';

import { Color } from '@signozhq/design-tokens';
import { Tooltip } from 'antd';
import { QueryParams } from 'constants/query';
import useUrlQuery from 'hooks/useUrlQuery';
import { ArrowDownLeft, ArrowUpRight, Calendar } from 'lucide-react';
import { AlertRuleStats } from 'types/api/alerts/def';
import { calculateChange } from 'utils/calculateChange';

import StatsGraph from './StatsGraph/StatsGraph';
import {
convertTimestampToLocaleDateString,
extractDayFromTimestamp,
} from './utils';

type ChangePercentageProps = {
percentage: number;
Expand Down Expand Up @@ -78,6 +84,25 @@ function StatsCard({
totalPastCount,
);

const startTime = urlQuery.get(QueryParams.startTime);
const endTime = urlQuery.get(QueryParams.endTime);

let displayTime = relativeTime;

if (!displayTime && startTime && endTime) {
const formattedStartDate = extractDayFromTimestamp(startTime);
const formattedEndDate = extractDayFromTimestamp(endTime);
displayTime = `${formattedStartDate} to ${formattedEndDate}`;
}

if (!displayTime) {
displayTime = '';
}
const formattedStartTimeForTooltip = convertTimestampToLocaleDateString(
startTime,
);
const formattedEndTimeForTooltip = convertTimestampToLocaleDateString(endTime);

return (
<div className={`stats-card ${isEmpty ? 'stats-card--empty' : ''}`}>
<div className="stats-card__title-wrapper">
Expand All @@ -86,7 +111,15 @@ function StatsCard({
<div className="icon">
<Calendar size={14} color={Color.BG_SLATE_200} />
</div>
<div className="text">{relativeTime}</div>
{relativeTime ? (
<div className="text">{displayTime}</div>
) : (
<Tooltip
title={`From ${formattedStartTimeForTooltip} to ${formattedEndTimeForTooltip}`}
>
<div className="text">{displayTime}</div>
</Tooltip>
)}
</div>
</div>

Expand Down
12 changes: 12 additions & 0 deletions frontend/src/container/AlertHistory/Statistics/StatsCard/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export const extractDayFromTimestamp = (timestamp: string | null): string => {
if (!timestamp) return '';
const date = new Date(parseInt(timestamp, 10));
return date.getDate().toString();
};

export const convertTimestampToLocaleDateString = (
timestamp: string | null,
): string => {
if (!timestamp) return '';
return new Date(parseInt(timestamp, 10)).toLocaleString();
};
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
import { Button } from 'antd';
import { QueryParams } from 'constants/query';
import ROUTES from 'constants/routes';
import useUrlQuery from 'hooks/useUrlQuery';
import history from 'lib/history';

import TopContributorsRows from './TopContributorsRows';
import { TopContributorsCardProps } from './types';

Expand All @@ -13,34 +7,13 @@ function TopContributorsContent({
}: TopContributorsCardProps): JSX.Element {
const isEmpty = !topContributorsData.length;

const urlQuery = useUrlQuery();
const ruleIdKey = QueryParams.ruleId;
const relativeTimeKey = QueryParams.relativeTime;

const handleRedirectToOverview = (): void => {
const params = `${ruleIdKey}=${urlQuery.get(
ruleIdKey,
)}&${relativeTimeKey}=${urlQuery.get(relativeTimeKey)}`;

history.push(`${ROUTES.ALERT_OVERVIEW}?${params}`);
};

if (isEmpty) {
return (
<div className="empty-content">
<div className="empty-content__icon">ℹ️</div>
<div className="empty-content__text">
<span className="bold-text">Add Group By Field</span> To view top
contributors, please add at least one group by field to your query.
</div>
<div className="empty-content__button-wrapper">
<Button
type="default"
className="configure-alert-rule-button"
onClick={handleRedirectToOverview}
>
Configure Alert Rule
</Button>
Top contributors highlight the most frequently triggering group-by
attributes in multi-dimensional alerts
</div>
</div>
);
Expand Down
35 changes: 22 additions & 13 deletions frontend/src/container/AlertHistory/Timeline/Graph/Graph.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,28 @@ function HorizontalTimelineGraph({
isDarkMode: boolean;
data: AlertRuleTimelineGraphResponse[];
}): JSX.Element {
const transformedData: AlignedData = useMemo(
() =>
data?.length > 1
? [
data.map((item: AlertRuleTimelineGraphResponse) => item.start / 1000),
data.map(
(item: AlertRuleTimelineGraphResponse) => ALERT_STATUS[item.state],
),
]
: [[], []],

[data],
);
const transformedData: AlignedData = useMemo(() => {
if (!data?.length) {
return [[], []];
}

// add a first and last entry to make sure the graph displays all the data
const FIVE_MINUTES_IN_SECONDS = 300;

const timestamps = [
data[0].start / 1000 - FIVE_MINUTES_IN_SECONDS, // 5 minutes before the first entry
...data.map((item) => item.start / 1000),
data[data.length - 1].end / 1000, // end value of last entry
];

const states = [
ALERT_STATUS[data[0].state], // Same state as the first entry
...data.map((item) => ALERT_STATUS[item.state]),
ALERT_STATUS[data[data.length - 1].state], // Same state as the last entry
];

return [timestamps, states];
}, [data]);

const options: uPlot.Options = useMemo(
() => ({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Color } from '@signozhq/design-tokens';

export const ALERT_STATUS: { [key: string]: number } = {
firing: 0,
inactive: 1,
normal: 1,
'no-data': 2,
disabled: 3,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,11 @@
font-size: 12px;
font-weight: 500;
padding: 12px 16px 8px !important;
&:last-of-type,
&:nth-last-of-type(2) {
&:last-of-type
// TODO(shaheer): uncomment when we display value column
// ,
// &:nth-last-of-type(2)
{
text-align: right;
}
}
Expand Down
29 changes: 3 additions & 26 deletions frontend/src/container/AlertHistory/Timeline/Table/Table.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,24 @@
import './Table.styles.scss';

import { Table } from 'antd';
import { initialFilters } from 'constants/queryBuilder';
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
import {
useGetAlertRuleDetailsTimelineTable,
useTimelineTable,
} from 'pages/AlertDetails/hooks';
import { useMemo, useState } from 'react';
import { useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { useQueryClient } from 'react-query';
import { PayloadProps } from 'types/api/alerts/get';
import { TagFilter } from 'types/api/queryBuilder/queryBuilderData';

import { timelineTableColumns } from './useTimelineTable';

function TimelineTable(): JSX.Element {
const [filters, setFilters] = useState<TagFilter>(initialFilters);

const {
isLoading,
isRefetching,
isError,
data,
isValidRuleId,
ruleId,
} = useGetAlertRuleDetailsTimelineTable({ filters });
} = useGetAlertRuleDetailsTimelineTable();

const { timelineData, totalItems } = useMemo(() => {
const response = data?.payload?.data;
Expand All @@ -39,22 +32,6 @@ function TimelineTable(): JSX.Element {
totalItems: totalItems ?? 0,
});

const queryClient = useQueryClient();

const { currentUnit, targetUnit } = useMemo(() => {
const alertDetailsQuery = queryClient.getQueryData([
REACT_QUERY_KEY.ALERT_RULE_DETAILS,
ruleId,
]) as {
payload: PayloadProps;
};
const condition = alertDetailsQuery?.payload?.data?.condition;
const { targetUnit } = condition ?? {};
const { unit: currentUnit } = condition?.compositeQuery ?? {};

return { currentUnit, targetUnit };
}, [queryClient, ruleId]);

const { t } = useTranslation('common');

if (isError || !isValidRuleId || !ruleId) {
Expand All @@ -65,7 +42,7 @@ function TimelineTable(): JSX.Element {
<div className="timeline-table">
<Table
rowKey={(row): string => `${row.fingerprint}-${row.value}-${row.unixMilli}`}
columns={timelineTableColumns(filters, setFilters, currentUnit, targetUnit)}
columns={timelineTableColumns()}
dataSource={timelineData}
pagination={paginationConfig}
size="middle"
Expand Down
Loading

0 comments on commit a7a2209

Please sign in to comment.