Skip to content

Commit 3e51f3c

Browse files
committed
Merge branch 'main' into getting-started-component
2 parents 26184da + ca693c0 commit 3e51f3c

File tree

18 files changed

+916
-729
lines changed

18 files changed

+916
-729
lines changed

.changeset/angry-pugs-drive.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@hyperdx/app": patch
3+
---
4+
5+
Improvements to Webhooks rendering (grouping, icons, etc)
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@hyperdx/common-utils": minor
3+
---
4+
5+
Add support for visualizing histogram counts

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,4 +76,7 @@ docker-compose.prod.yml
7676
.volumes
7777

7878
# NX
79-
.nx/
79+
.nx/
80+
81+
# webstorm
82+
.idea

packages/app/src/AlertsPage.tsx

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import { ErrorBoundary } from '@/components/ErrorBoundary';
3737
import { PageHeader } from '@/components/PageHeader';
3838

3939
import { isAlertSilenceExpired } from './utils/alerts';
40+
import { getWebhookChannelIcon } from './utils/webhookIcons';
4041
import api from './api';
4142
import { withAppNav } from './layout';
4243
import type { AlertsPageItem } from './types';
@@ -367,13 +368,11 @@ function AlertDetails({ alert }: { alert: AlertsPageItem }) {
367368
}, [alert]);
368369

369370
const notificationMethod = React.useMemo(() => {
370-
if (alert.channel.type === 'webhook') {
371-
return (
372-
<Group gap={2}>
373-
Notify via <IconBrandSlack size={16} /> Webhook
374-
</Group>
375-
);
376-
}
371+
return (
372+
<Group gap={5}>
373+
Notify via {getWebhookChannelIcon(alert.channel.type)} Webhook
374+
</Group>
375+
);
377376
}, [alert]);
378377

379378
const linkTitle = React.useMemo(() => {

packages/app/src/DBSearchPageAlertModal.tsx

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ import {
4848
import { AlertPreviewChart } from './components/AlertPreviewChart';
4949
import { AlertChannelForm } from './components/Alerts';
5050
import { SQLInlineEditorControlled } from './components/SQLInlineEditor';
51+
import { getWebhookChannelIcon } from './utils/webhookIcons';
5152
import api from './api';
5253
import { AlertWithCreatedBy, SearchConfig } from './types';
5354
import { optionsToSelectData } from './utils';
@@ -61,10 +62,6 @@ const SavedSearchAlertFormSchema = z
6162
})
6263
.passthrough();
6364

64-
const CHANNEL_ICONS = {
65-
webhook: <IconBrandSlack size={14} />,
66-
};
67-
6865
const AlertForm = ({
6966
sourceId,
7067
where,
@@ -422,7 +419,7 @@ export const DBSearchPageAlertModal = ({
422419
{(savedSearch?.alerts || []).map((alert, index) => (
423420
<Tabs.Tab key={alert.id} value={`${index}`}>
424421
<Group gap="xs">
425-
{CHANNEL_ICONS[alert.channel.type]} Alert {index + 1}
422+
{getWebhookChannelIcon(alert.channel.type)} Alert {index + 1}
426423
</Group>
427424
</Tabs.Tab>
428425
))}

packages/app/src/SVGIcons.tsx

Lines changed: 12 additions & 307 deletions
Large diffs are not rendered by default.

packages/app/src/ServicesDashboardPage.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import {
3131
Tooltip,
3232
} from '@mantine/core';
3333
import {
34+
IconChartHistogram,
3435
IconChartLine,
3536
IconFilter,
3637
IconFilterEdit,
@@ -67,7 +68,6 @@ import {
6768
useServiceDashboardExpressions,
6869
} from '@/serviceDashboard';
6970
import { useSource, useSources } from '@/source';
70-
import { Histogram } from '@/SVGIcons';
7171
import { parseTimeQuery, useNewTimeQuery } from '@/timeQuery';
7272

7373
import usePresetDashboardFilters from './hooks/usePresetDashboardFilters';
@@ -234,7 +234,7 @@ export function EndpointLatencyChart({
234234
title="Histogram"
235235
onClick={() => setLatencyChartType('histogram')}
236236
>
237-
<Histogram width={12} color="currentColor" />
237+
<IconChartHistogram size={14} />
238238
</Button>
239239
</Button.Group>
240240
</Box>

packages/app/src/TeamPage.tsx

Lines changed: 5 additions & 156 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,9 @@
1-
import { Fragment, useCallback, useMemo, useState } from 'react';
1+
import { useCallback, useState } from 'react';
22
import Head from 'next/head';
3-
import { HTTPError } from 'ky';
43
import { CopyToClipboard } from 'react-copy-to-clipboard';
54
import { SubmitHandler, useForm, useWatch } from 'react-hook-form';
65
import { DEFAULT_METADATA_MAX_ROWS_TO_READ } from '@hyperdx/common-utils/dist/core/metadata';
7-
import {
8-
TeamClickHouseSettings,
9-
WebhookService,
10-
} from '@hyperdx/common-utils/dist/types';
6+
import { TeamClickHouseSettings } from '@hyperdx/common-utils/dist/types';
117
import {
128
Box,
139
Button,
@@ -25,7 +21,6 @@ import {
2521
TextInput,
2622
Tooltip,
2723
} from '@mantine/core';
28-
import { useDisclosure } from '@mantine/hooks';
2924
import { notifications } from '@mantine/notifications';
3025
import {
3126
IconCheck,
@@ -42,13 +37,11 @@ import { IS_LOCAL_MODE } from '@/config';
4237

4338
import { PageHeader } from './components/PageHeader';
4439
import TeamMembersSection from './components/TeamSettings/TeamMembersSection';
45-
import { WebhookForm } from './components/TeamSettings/WebhookForm';
40+
import WebhooksSection from './components/TeamSettings/WebhooksSection';
4641
import api from './api';
4742
import { useConnections } from './connection';
4843
import { DEFAULT_QUERY_TIMEOUT, DEFAULT_SEARCH_ROW_LIMIT } from './defaults';
4944
import { withAppNav } from './layout';
50-
import type { Webhook } from './types';
51-
import { useConfirm } from './useConfirm';
5245

5346
function ConnectionsSection() {
5447
const { data: connections } = useConnections();
@@ -159,159 +152,15 @@ function SourcesSection() {
159152
</Box>
160153
);
161154
}
162-
163-
function DeleteWebhookButton({
164-
webhookId,
165-
webhookName,
166-
onSuccess,
167-
}: {
168-
webhookId: string;
169-
webhookName: string;
170-
onSuccess: VoidFunction;
171-
}) {
172-
const confirm = useConfirm();
173-
const deleteWebhook = api.useDeleteWebhook();
174-
175-
const handleDelete = async () => {
176-
if (
177-
await confirm(
178-
`Are you sure you want to delete ${webhookName} webhook?`,
179-
'Delete',
180-
)
181-
) {
182-
try {
183-
await deleteWebhook.mutateAsync({ id: webhookId });
184-
notifications.show({
185-
color: 'green',
186-
message: 'Webhook deleted successfully',
187-
});
188-
onSuccess();
189-
} catch (e) {
190-
console.error(e);
191-
const message =
192-
(e instanceof HTTPError
193-
? (await e.response.json())?.message
194-
: null) || 'Something went wrong. Please contact HyperDX team.';
195-
notifications.show({
196-
message,
197-
color: 'red',
198-
autoClose: 5000,
199-
});
200-
}
201-
}
202-
};
203-
204-
return (
205-
<Button
206-
color="red"
207-
size="compact-xs"
208-
variant="outline"
209-
onClick={handleDelete}
210-
loading={deleteWebhook.isPending}
211-
>
212-
Delete
213-
</Button>
214-
);
215-
}
216-
217155
function IntegrationsSection() {
218-
const { data: webhookData, refetch: refetchWebhooks } = api.useWebhooks([
219-
WebhookService.Slack,
220-
WebhookService.Generic,
221-
WebhookService.IncidentIO,
222-
]);
223-
224-
const allWebhooks = useMemo<Webhook[]>(() => {
225-
return Array.isArray(webhookData?.data) ? webhookData.data : [];
226-
}, [webhookData]);
227-
228-
const [editedWebhookId, setEditedWebhookId] = useState<string | null>(null);
229-
const [
230-
isAddWebhookModalOpen,
231-
{ open: openWebhookModal, close: closeWebhookModal },
232-
] = useDisclosure();
233-
234156
return (
235157
<Box id="integrations">
236158
<Text size="md">Integrations</Text>
237159
<Divider my="md" />
238160
<Card>
239-
<Text mb="xs">Webhooks</Text>
240-
241-
<Stack>
242-
{allWebhooks.map(webhook => (
243-
<Fragment key={webhook._id}>
244-
<Group justify="space-between" align="flex-start">
245-
<Stack gap={0}>
246-
<Text size="sm">
247-
{webhook.name} ({webhook.service})
248-
</Text>
249-
<Text size="xs" opacity={0.7}>
250-
{webhook.url}
251-
</Text>
252-
{webhook.description && (
253-
<Text size="xxs" opacity={0.7}>
254-
{webhook.description}
255-
</Text>
256-
)}
257-
</Stack>
258-
<Group gap="xs">
259-
{editedWebhookId !== webhook._id && (
260-
<>
261-
<Button
262-
variant="subtle"
263-
onClick={() => setEditedWebhookId(webhook._id)}
264-
size="compact-xs"
265-
leftSection={<IconPencil size={14} />}
266-
>
267-
Edit
268-
</Button>
269-
<DeleteWebhookButton
270-
webhookId={webhook._id}
271-
webhookName={webhook.name}
272-
onSuccess={refetchWebhooks}
273-
/>
274-
</>
275-
)}
276-
{editedWebhookId === webhook._id && (
277-
<Button
278-
variant="subtle"
279-
onClick={() => setEditedWebhookId(null)}
280-
size="compact-xs"
281-
>
282-
<IconX size={14} className="me-2" /> Cancel
283-
</Button>
284-
)}
285-
</Group>
286-
</Group>
287-
{editedWebhookId === webhook._id && (
288-
<WebhookForm
289-
webhook={webhook}
290-
onClose={() => setEditedWebhookId(null)}
291-
onSuccess={() => {
292-
setEditedWebhookId(null);
293-
refetchWebhooks();
294-
}}
295-
/>
296-
)}
297-
<Divider />
298-
</Fragment>
299-
))}
161+
<Stack gap="md">
162+
<WebhooksSection />
300163
</Stack>
301-
302-
{!isAddWebhookModalOpen ? (
303-
<Button variant="outline" onClick={openWebhookModal}>
304-
Add Webhook
305-
</Button>
306-
) : (
307-
<WebhookForm
308-
onClose={closeWebhookModal}
309-
onSuccess={() => {
310-
refetchWebhooks();
311-
closeWebhookModal();
312-
}}
313-
/>
314-
)}
315164
</Card>
316165
</Box>
317166
);

packages/app/src/components/TeamSettings/WebhookForm.tsx

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@ import { Controller, SubmitHandler, useForm, useWatch } from 'react-hook-form';
44
import { ZodIssue } from 'zod';
55
import { json, jsonParseLinter } from '@codemirror/lang-json';
66
import { linter } from '@codemirror/lint';
7-
import { AlertState, WebhookService } from '@hyperdx/common-utils/dist/types';
7+
import {
8+
AlertState,
9+
WebhookApiData,
10+
WebhookService,
11+
} from '@hyperdx/common-utils/dist/types';
812
import { isValidSlackUrl } from '@hyperdx/common-utils/dist/validation';
913
import {
1014
Alert,
@@ -23,7 +27,6 @@ import ReactCodeMirror, {
2327
} from '@uiw/react-codemirror';
2428

2529
import api from '@/api';
26-
import { Webhook } from '@/types';
2730
import { isValidUrl } from '@/utils';
2831

2932
const DEFAULT_GENERIC_WEBHOOK_BODY = [
@@ -47,7 +50,7 @@ const jsonLinterWithEmptyCheck = () => (editorView: EditorView) => {
4750
type WebhookForm = {
4851
name: string;
4952
url: string;
50-
service: string;
53+
service: WebhookService;
5154
description?: string;
5255
body?: string;
5356
headers?: string;
@@ -58,7 +61,7 @@ export function WebhookForm({
5861
onClose,
5962
onSuccess,
6063
}: {
61-
webhook?: Webhook;
64+
webhook?: WebhookApiData;
6265
onClose: VoidFunction;
6366
onSuccess: (webhookId?: string) => void;
6467
}) {
@@ -280,14 +283,12 @@ export function WebhookForm({
280283
label="Service Type"
281284
required
282285
value={service}
283-
onChange={value => {
284-
form.setValue('service', value);
285-
}}
286+
onChange={value => form.setValue('service', value as WebhookService)}
286287
>
287288
<Group mt="xs">
288289
<Radio value={WebhookService.Slack} label="Slack" />
290+
<Radio value={WebhookService.IncidentIO} label="incident.io" />
289291
<Radio value={WebhookService.Generic} label="Generic" />
290-
<Radio value={WebhookService.IncidentIO} label="Incident.io" />
291292
</Group>
292293
</Radio.Group>
293294
<TextInput
@@ -297,6 +298,7 @@ export function WebhookForm({
297298
error={form.formState.errors.name?.message}
298299
{...form.register('name', { required: true })}
299300
/>
301+
300302
<TextInput
301303
label="Webhook URL"
302304
placeholder={
@@ -318,6 +320,7 @@ export function WebhookForm({
318320
: isValidUrl(value) || 'URL must be valid',
319321
})}
320322
/>
323+
321324
<TextInput
322325
label="Webhook Description (optional)"
323326
placeholder="To be used for dev alerts"

0 commit comments

Comments
 (0)