Skip to content

Commit

Permalink
Chart alerts: Add UI to Chart Builder
Browse files Browse the repository at this point in the history
  • Loading branch information
svc-shorpo committed Nov 13, 2023
1 parent e904ec3 commit 89ea338
Show file tree
Hide file tree
Showing 5 changed files with 193 additions and 10 deletions.
5 changes: 5 additions & 0 deletions .changeset/sharp-meals-cheat.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@hyperdx/app': patch
---

Chart alerts: Add UI to chart builder
55 changes: 50 additions & 5 deletions packages/app/src/EditChartForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ import produce from 'immer';
import HDXMarkdownChart from './HDXMarkdownChart';
import Select from 'react-select';
import { Button, Form, InputGroup, Modal } from 'react-bootstrap';

import * as config from './config';
import type { Alert } from './types';
import Checkbox from './Checkbox';
import HDXLineChart from './HDXLineChart';
import {
AGG_FNS,
Expand All @@ -15,9 +17,10 @@ import {
import { hashCode, useDebounce } from './utils';
import HDXHistogramChart from './HDXHistogramChart';
import { LogTableWithSidePanel } from './LogTableWithSidePanel';

import EditChartFormAlerts from './EditChartFormAlerts';
import HDXNumberChart from './HDXNumberChart';
import HDXTableChart from './HDXTableChart';
import { intervalToGranularity } from './Alert';

export type Chart = {
id: string;
Expand Down Expand Up @@ -69,6 +72,16 @@ export type Chart = {
)[];
};

const DEFAULT_ALERT: Alert = {
channel: {
type: 'webhook',
},
threshold: 1,
interval: '1m',
type: 'presence',
source: 'CHART',
};

export const EditMarkdownChartForm = ({
chart,
onClose,
Expand Down Expand Up @@ -818,6 +831,8 @@ export const EditLineChartForm = ({
const CHART_TYPE = 'time';

const [editedChart, setEditedChart] = useState<Chart | undefined>(chart);
const [editedAlert, setEditedAlert] = useState<Alert | undefined>();
const [alertEnabled, setAlertEnabled] = useState(editedAlert != null);

const chartConfig = useMemo(
() =>
Expand All @@ -828,11 +843,14 @@ export const EditLineChartForm = ({
field: editedChart.series[0].field ?? '', // TODO: Fix in definition
groupBy: editedChart.series[0].groupBy[0],
where: editedChart.series[0].where,
granularity: convertDateRangeToGranularityString(dateRange, 60),
granularity:
alertEnabled && editedAlert?.interval
? intervalToGranularity(editedAlert?.interval)
: convertDateRangeToGranularityString(dateRange, 60),
dateRange,
}
: null,
[editedChart, dateRange],
[editedChart, alertEnabled, editedAlert?.interval, dateRange],
);
const previewConfig = useDebounce(chartConfig, 500);

Expand Down Expand Up @@ -944,6 +962,26 @@ export const EditLineChartForm = ({
);
}}
/>

{config.CHART_ALERTS_ENABLED && (
<div className="mt-4 border-top border-bottom border-grey p-2 py-3">
<Checkbox
id="check"
label="Enable alerts"
checked={alertEnabled}
onChange={() => setAlertEnabled(!alertEnabled)}
/>
{alertEnabled && (
<div className="mt-2">
<EditChartFormAlerts
alert={editedAlert ?? DEFAULT_ALERT}
setAlert={setEditedAlert}
/>
</div>
)}
</div>
)}

<div className="d-flex justify-content-between my-3 ps-2">
<Button
variant="outline-success"
Expand All @@ -959,7 +997,14 @@ export const EditLineChartForm = ({
<div className="mt-4">
<div className="mb-3 text-muted ps-2 fs-7">Chart Preview</div>
<div style={{ height: 400 }}>
<HDXLineChart config={previewConfig} />
<HDXLineChart
config={previewConfig}
{...(alertEnabled && {
alertThreshold: editedAlert?.threshold,
alertThresholdType:
editedAlert?.type === 'presence' ? 'above' : 'below',
})}
/>
</div>
</div>
{editedChart.series[0].table === 'logs' ? (
Expand Down
130 changes: 130 additions & 0 deletions packages/app/src/EditChartFormAlerts.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import * as React from 'react';
import { Form } from 'react-bootstrap';
import type { Alert } from './types';
import produce from 'immer';

import {
ALERT_INTERVAL_OPTIONS,
ALERT_CHANNEL_OPTIONS,
SlackChannelForm,
} from './Alert';

type ChartAlertFormProps = {
alert: Alert;
setAlert: (alert?: Alert) => void;
};

export default function EditChartFormAlerts({
alert,
setAlert,
}: ChartAlertFormProps) {
return (
<>
<div className="d-flex align-items-center gap-3">
Alert when the value
<Form.Select
id="type"
size="sm"
style={{
width: 140,
}}
value={alert?.type}
onChange={e => {
setAlert(
produce(alert, draft => {
draft.type = e.target.value as 'presence' | 'absence';
}),
);
}}
>
<option key="presence" value="presence">
exceeds
</option>
<option key="absence" value="absence">
falls below
</option>
</Form.Select>
<Form.Control
style={{ width: 70 }}
type="number"
required
id="threshold"
size="sm"
defaultValue={1}
value={alert?.threshold}
onChange={e => {
setAlert(
produce(alert, draft => {
draft.threshold = parseFloat(e.target.value);
}),
);
}}
/>
over
<Form.Select
id="interval"
size="sm"
style={{
width: 140,
}}
value={alert?.interval}
onChange={e => {
setAlert(
produce(alert, draft => {
draft.interval = e.target
.value as keyof typeof ALERT_INTERVAL_OPTIONS;
}),
);
}}
>
{Object.entries(ALERT_INTERVAL_OPTIONS).map(([value, text]) => (
<option key={value} value={value}>
{text}
</option>
))}
</Form.Select>
window via
<Form.Select
id="channel"
size="sm"
style={{ width: 200 }}
value={alert?.channel?.type}
onChange={e => {
setAlert(
produce(alert, draft => {
draft.channel = {
type: e.target.value as keyof typeof ALERT_CHANNEL_OPTIONS,
};
}),
);
}}
>
{Object.entries(ALERT_CHANNEL_OPTIONS).map(([value, text]) => (
<option key={value} value={value}>
{text}
</option>
))}
</Form.Select>
</div>
<div className="mt-3">
{alert?.channel?.type === 'webhook' && (
<SlackChannelForm
webhookSelectProps={{
value: alert?.channel?.webhookId,
onChange: e => {
setAlert(
produce(alert, draft => {
draft.channel = {
type: 'webhook',
webhookId: e.target.value,
};
}),
);
},
}}
/>
)}
</div>
</>
);
}
3 changes: 3 additions & 0 deletions packages/app/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,6 @@ export const HDX_COLLECTOR_URL =
'http://localhost:4318';

export const IS_OSS = process.env.NEXT_PUBLIC_IS_OSS ?? 'true' === 'true';

// Features in development
export const CHART_ALERTS_ENABLED = process.env.NODE_ENV === 'development';
10 changes: 5 additions & 5 deletions packages/app/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,13 @@ export type AlertChannel = {
};

export type Alert = {
_id: string;
_id?: string;
channel: AlertChannel;
cron: string;
cron?: string;
interval: AlertInterval;
state: 'ALERT' | 'OK';
threshold: number;
timezone: string;
state?: 'ALERT' | 'OK';
threshold?: number;
timezone?: string;
type: AlertType;
source: 'LOG' | 'CHART';

Expand Down

0 comments on commit 89ea338

Please sign in to comment.