Skip to content

Commit

Permalink
feat: Add chart preview to alert modal
Browse files Browse the repository at this point in the history
  • Loading branch information
ernestii committed Jan 21, 2025
1 parent ecd4530 commit 21dcf30
Show file tree
Hide file tree
Showing 6 changed files with 145 additions and 50 deletions.
44 changes: 36 additions & 8 deletions packages/app/src/DBSearchPageAlertModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { NativeSelect, NumberInput } from 'react-hook-form-mantine';
import { z } from 'zod';
import { zodResolver } from '@hookform/resolvers/zod';
import {
Accordion,
Box,
Button,
Group,
Expand All @@ -16,8 +17,7 @@ import {
} from '@mantine/core';
import { notifications } from '@mantine/notifications';

import { AlertSchema } from '@/commonTypes';
import { SQLInlineEditorControlled } from '@/components/SQLInlineEditor';
import { AlertSchema, SavedSearch } from '@/commonTypes';
import { useSavedSearch } from '@/savedSearch';
import { useSource } from '@/source';
import {
Expand All @@ -26,7 +26,9 @@ import {
ALERT_THRESHOLD_TYPE_OPTIONS,
} from '@/utils/alerts';

import { AlertPreviewChart } from './components/AlertPreviewChart';
import { WebhookChannelForm } from './components/Alerts';
import { SQLInlineEditorControlled } from './components/SQLInlineEditor';
import api from './api';

const CHANNEL_ICONS = {
Expand All @@ -40,29 +42,29 @@ const zAlertForm = AlertSchema;
type AlertForm = z.infer<typeof zAlertForm>;

const AlertForm = ({
sourceId,
savedSearch,
defaultValues,
loading,
deleteLoading,
onDelete,
onSubmit,
onClose,
}: {
sourceId?: string;
savedSearch?: SavedSearch;
defaultValues?: null | AlertForm;
loading?: boolean;
deleteLoading?: boolean;
onDelete: (id: string) => void;
onSubmit: (data: AlertForm) => void;
onClose: () => void;
}) => {
const { data: source } = useSource({ id: sourceId });
const { data: source } = useSource({ id: savedSearch?.source });

const databaseName = source?.from.databaseName;
const tableName = source?.from.tableName;
const connectionId = source?.connection;

const { control, handleSubmit } = useForm<AlertForm>({
const { control, handleSubmit, watch } = useForm<AlertForm>({
defaultValues: defaultValues || {
interval: '5m',
threshold: 1,
Expand Down Expand Up @@ -139,6 +141,26 @@ const AlertForm = ({
<WebhookChannelForm control={control} name={`channel.webhookId`} />
</Paper>
</Stack>

{savedSearch && (
<Accordion defaultValue={'chart'} mt="sm" mx={-16}>
<Accordion.Item value="chart">
<Accordion.Control icon={<i className="bi bi-chart"></i>}>
<Text size="sm">Threshold chart</Text>
</Accordion.Control>
<Accordion.Panel>
<AlertPreviewChart
savedSearch={savedSearch}
interval={watch('interval')}
groupBy={watch('groupBy')}
threshold={watch('threshold')}
thresholdType={watch('thresholdType')}
/>
</Accordion.Panel>
</Accordion.Item>
</Accordion>
)}

<Group mt="lg" justify="space-between" gap="xs">
<div>
{defaultValues && (
Expand Down Expand Up @@ -249,7 +271,13 @@ export const DBSearchPageAlertModal = ({
};

return (
<Modal opened={open} onClose={onClose} size="xl" withCloseButton={false}>
<Modal
opened={open}
onClose={onClose}
size="xl"
withCloseButton={false}
zIndex={9999}
>
<Box pos="relative">
<LoadingOverlay
visible={isLoading}
Expand Down Expand Up @@ -291,7 +319,7 @@ export const DBSearchPageAlertModal = ({
</Tabs>

<AlertForm
sourceId={savedSearch?.source}
savedSearch={savedSearch}
key={activeIndex}
defaultValues={
activeIndex === 'stage'
Expand Down
41 changes: 6 additions & 35 deletions packages/app/src/HDXMultiSeriesTimeChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -237,8 +237,7 @@ export const MemoChart = memo(function MemoChart({
groupKeys,
lineNames,
lineColors,
alertThreshold,
alertThresholdType,
referenceLines,
logReferenceTimestamp,
displayType = DisplayType.Line,
numberFormat,
Expand All @@ -254,8 +253,7 @@ export const MemoChart = memo(function MemoChart({
groupKeys: string[];
lineNames: string[];
lineColors: Array<string | undefined>;
alertThreshold?: number;
alertThresholdType?: 'above' | 'below';
referenceLines?: React.ReactNode;
displayType?: DisplayType;
numberFormat?: NumberFormat;
logReferenceTimestamp?: number;
Expand Down Expand Up @@ -501,31 +499,7 @@ export const MemoChart = memo(function MemoChart({
}}
allowEscapeViewBox={{ y: true }}
/>
{alertThreshold != null && alertThresholdType === 'below' && (
<ReferenceArea
y1={0}
y2={alertThreshold}
ifOverflow="extendDomain"
strokeWidth={0}
fillOpacity={0.05}
/>
)}
{alertThreshold != null && alertThresholdType === 'above' && (
<ReferenceArea
y1={alertThreshold}
ifOverflow="extendDomain"
strokeWidth={0}
fillOpacity={0.05}
/>
)}
{alertThreshold != null && (
<ReferenceLine
y={alertThreshold}
label={<Label value="Alert Threshold" fill={'white'} />}
stroke="red"
strokeDasharray="3 3"
/>
)}
{referenceLines}
{highlightStart && highlightEnd ? (
<ReferenceArea
// yAxisId="1"
Expand Down Expand Up @@ -569,8 +543,7 @@ const HDXMultiSeriesTimeChart = memo(
displayType: displayTypeProp = DisplayType.Line,
},
onSettled,
alertThreshold,
alertThresholdType,
referenceLines,
showDisplaySwitcher = true,
setDisplayType,
logReferenceTimestamp,
Expand All @@ -583,8 +556,7 @@ const HDXMultiSeriesTimeChart = memo(
displayType?: DisplayType;
};
onSettled?: () => void;
alertThreshold?: number;
alertThresholdType?: 'above' | 'below';
referenceLines?: React.ReactNode;
showDisplaySwitcher?: boolean;
setDisplayType?: (type: DisplayType) => void;
logReferenceTimestamp?: number;
Expand Down Expand Up @@ -858,11 +830,10 @@ const HDXMultiSeriesTimeChart = memo(
isClickActive={activeClickPayload}
setIsClickActive={setActiveClickPayload}
dateRange={dateRange}
alertThreshold={alertThreshold}
alertThresholdType={alertThresholdType}
displayType={displayType}
numberFormat={numberFormat}
logReferenceTimestamp={logReferenceTimestamp}
referenceLines={referenceLines}
/>
</div>
</div>
Expand Down
2 changes: 1 addition & 1 deletion packages/app/src/commonTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export const SavedSearchSchema = z.object({
name: z.string(),
select: z.string(),
where: z.string(),
whereLanguage: z.string().optional(),
whereLanguage: z.union([z.literal('sql'), z.literal('lucene')]).optional(),
source: z.string(),
tags: z.array(z.string()),
orderBy: z.string().optional(),
Expand Down
61 changes: 61 additions & 0 deletions packages/app/src/components/AlertPreviewChart.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import React from 'react';
import { Label, ReferenceArea, ReferenceLine } from 'recharts';
import { Paper } from '@mantine/core';

import { SavedSearch } from '@/commonTypes';
import { DBTimeChart } from '@/components/DBTimeChart';
import { useSource } from '@/source';
import { AlertInterval } from '@/types';
import { intervalToDateRange, intervalToGranularity } from '@/utils/alerts';

import { getAlertReferenceLines } from './Alerts';

export type AlertPreviewChartProps = {
savedSearch?: SavedSearch;
interval: AlertInterval;
groupBy?: string;
thresholdType: 'above' | 'below';
threshold: number;
};

export const AlertPreviewChart = ({
savedSearch,
interval,
groupBy,
threshold,
thresholdType,
}: AlertPreviewChartProps) => {
const { data: source } = useSource({ id: savedSearch?.source });

if (!savedSearch || !source) {
return null;
}

return (
<Paper w="100%" h={200}>
<DBTimeChart
sourceId={savedSearch.source}
showDisplaySwitcher={false}
referenceLines={getAlertReferenceLines({ threshold, thresholdType })}
config={{
where: savedSearch.where || '',
whereLanguage: savedSearch.whereLanguage,
dateRange: intervalToDateRange(interval),
granularity: intervalToGranularity(interval),
groupBy,
select: [
{
aggFn: 'count' as const,
aggCondition: '',
aggConditionLanguage: 'sql',
valueExpression: '',
},
],
timestampValueExpression: source.timestampValueExpression,
from: source.from,
connection: source.connection,
}}
/>
</Paper>
);
};
38 changes: 38 additions & 0 deletions packages/app/src/components/Alerts.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Link from 'next/link';
import { Select, SelectProps } from 'react-hook-form-mantine';
import { Label, ReferenceArea, ReferenceLine } from 'recharts';
import { Button, Group } from '@mantine/core';

import api from '@/api';
Expand Down Expand Up @@ -38,3 +39,40 @@ export const WebhookChannelForm = <T extends object>(
</div>
);
};

export const getAlertReferenceLines = ({
thresholdType,
threshold,
// TODO: zScore
}: {
thresholdType: 'above' | 'below';
threshold: number;
}) => (
<>
{threshold != null && thresholdType === 'below' && (
<ReferenceArea
y1={0}
y2={threshold}
ifOverflow="extendDomain"
strokeWidth={0}
fillOpacity={0.15}
/>
)}
{threshold != null && thresholdType === 'above' && (
<ReferenceArea
y1={threshold}
ifOverflow="extendDomain"
strokeWidth={0}
fillOpacity={0.15}
/>
)}
{threshold != null && (
<ReferenceLine
y={threshold}
label={<Label value="Alert Threshold" fill={'white'} />}
stroke="red"
strokeDasharray="3 3"
/>
)}
</>
);
9 changes: 3 additions & 6 deletions packages/app/src/components/DBTimeChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,9 @@ export function DBTimeChart({
config,
sourceId,
onSettled,
alertThreshold,
alertThresholdType,
referenceLines,
showDisplaySwitcher = true,
setDisplayType,
logReferenceTimestamp,
queryKeyPrefix,
enabled = true,
onTimeRangeSelect,
Expand All @@ -38,11 +36,9 @@ export function DBTimeChart({
config: ChartConfigWithDateRange;
sourceId?: string;
onSettled?: () => void;
alertThreshold?: number;
alertThresholdType?: 'above' | 'below';
showDisplaySwitcher?: boolean;
setDisplayType?: (type: DisplayType) => void;
logReferenceTimestamp?: number;
referenceLines?: React.ReactNode;
queryKeyPrefix?: string;
enabled?: boolean;
onTimeRangeSelect?: (start: Date, end: Date) => void;
Expand Down Expand Up @@ -289,6 +285,7 @@ export function DBTimeChart({
onTimeRangeSelect={onTimeRangeSelect}
showLegend={showLegend}
numberFormat={config.numberFormat}
referenceLines={referenceLines}
/>
</div>
</div>
Expand Down

0 comments on commit 21dcf30

Please sign in to comment.