Skip to content

Commit f623214

Browse files
sarimrmalikdrankou
andauthored
Refactor: Webhooks page (#303)
[Ticket](https://linear.app/e2b/issue/ENG-3765/refactor-webhooks-page-according-to-figma) ## Summary - Refactors the webhooks page into focused components and matches the Dashboard 2.0 Figma - Aligns the page with the API Keys / Members conventions ## Test plan - Manually verified the webhooks page against the Figma: empty state, search behavior, event badge tooltips, and row URL copy. - Walked through the create / update / delete / rotate-secret flows on the Vercel preview, including the discard-changes confirmation when closing the dialog with unsaved edits. - Confirmed `bun run build` passes with no type errors. --------- Co-authored-by: Alex Drankou <alex@e2b.dev>
1 parent 069a4bf commit f623214

28 files changed

Lines changed: 1661 additions & 1165 deletions
Lines changed: 13 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,29 @@
11
import { notFound } from 'next/navigation'
22
import { INCLUDE_ARGUS } from '@/configs/flags'
3-
import WebhookAddEditDialog from '@/features/dashboard/settings/webhooks/add-edit-dialog'
4-
import WebhooksTable from '@/features/dashboard/settings/webhooks/table'
5-
import Frame from '@/ui/frame'
6-
import { Button } from '@/ui/primitives/button'
7-
import {
8-
Card,
9-
CardContent,
10-
CardDescription,
11-
CardHeader,
12-
} from '@/ui/primitives/card'
13-
import { AddIcon } from '@/ui/primitives/icons'
3+
import { Page } from '@/features/dashboard/layouts/page'
4+
import { WebhooksPageContent } from '@/features/dashboard/settings/webhooks/webhooks-page-content'
5+
import { HydrateClient, prefetch, trpc } from '@/trpc/server'
146

15-
interface WebhooksPageClientProps {
7+
interface WebhooksPageProps {
168
params: Promise<{
179
teamSlug: string
1810
}>
1911
}
2012

21-
export default async function WebhooksPage({
22-
params,
23-
}: WebhooksPageClientProps) {
13+
export default async function WebhooksPage({ params }: WebhooksPageProps) {
2414
if (!INCLUDE_ARGUS) {
2515
return notFound()
2616
}
2717

28-
return (
29-
<Frame
30-
classNames={{
31-
wrapper: 'w-full max-md:p-0',
32-
frame: 'max-md:border-none',
33-
}}
34-
>
35-
<Card className="w-full">
36-
<CardHeader>
37-
<div className="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between sm:gap-6">
38-
<CardDescription className="max-w-[600px] text-fg">
39-
Webhooks allow your external service to be notified when sandbox
40-
lifecycle events happen. When the specified event happens, we'll
41-
send a POST request to the configured URLs.
42-
</CardDescription>
18+
const { teamSlug } = await params
4319

44-
<WebhookAddEditDialog mode="add">
45-
<Button className="w-full sm:w-auto sm:self-start">
46-
<AddIcon className="size-4" /> Add Webhook
47-
</Button>
48-
</WebhookAddEditDialog>
49-
</div>
50-
</CardHeader>
20+
prefetch(trpc.webhooks.list.queryOptions({ teamSlug }))
5121

52-
<CardContent>
53-
<div className="w-full overflow-x-auto">
54-
<WebhooksTable params={params} className="min-w-[900px]" />
55-
</div>
56-
</CardContent>
57-
</Card>
58-
</Frame>
22+
return (
23+
<HydrateClient>
24+
<Page>
25+
<WebhooksPageContent />
26+
</Page>
27+
</HydrateClient>
5928
)
6029
}

src/core/modules/webhooks/repository.server.ts

Lines changed: 63 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import 'server-only'
22

33
import { SUPABASE_AUTH_HEADERS } from '@/configs/api'
4+
import type { UpsertWebhookInput } from '@/core/server/functions/webhooks/schema'
45
import { infra } from '@/core/shared/clients/api'
56
import type { components as ArgusComponents } from '@/core/shared/contracts/argus-api.types'
67
import { repoErrorFromHttp } from '@/core/shared/errors'
@@ -14,16 +15,6 @@ type WebhooksRepositoryDeps = {
1415

1516
export type WebhooksScope = TeamRequestScope
1617

17-
export interface UpsertWebhookInput {
18-
mode: 'create' | 'edit'
19-
webhookId?: string
20-
name: string
21-
url: string
22-
events: string[]
23-
signatureSecret?: string
24-
enabled: boolean
25-
}
26-
2718
export interface WebhooksRepository {
2819
listWebhooks(): Promise<
2920
RepoResult<ArgusComponents['schemas']['WebhookDetail'][]>
@@ -68,34 +59,68 @@ export function createWebhooksRepository(
6859
return ok(response.data ?? [])
6960
},
7061
async upsertWebhook(input) {
71-
const response =
72-
input.mode === 'edit'
73-
? await deps.infraClient.PATCH('/events/webhooks/{webhookID}', {
74-
headers: {
75-
...deps.authHeaders(scope.accessToken, scope.teamId),
76-
},
77-
params: {
78-
path: { webhookID: input.webhookId ?? '' },
79-
},
80-
body: {
81-
name: input.name,
82-
url: input.url,
83-
events: input.events,
84-
enabled: input.enabled,
85-
},
86-
})
87-
: await deps.infraClient.POST('/events/webhooks', {
88-
headers: {
89-
...deps.authHeaders(scope.accessToken, scope.teamId),
90-
},
91-
body: {
92-
name: input.name,
93-
url: input.url,
94-
events: input.events,
95-
enabled: input.enabled,
96-
signatureSecret: input.signatureSecret ?? '',
97-
},
98-
})
62+
if (input.mode === 'update') {
63+
if (!input.webhookId) {
64+
return err(
65+
repoErrorFromHttp(
66+
400,
67+
'webhookId is required when updating a webhook'
68+
)
69+
)
70+
}
71+
72+
const response = await deps.infraClient.PATCH(
73+
'/events/webhooks/{webhookID}',
74+
{
75+
headers: {
76+
...deps.authHeaders(scope.accessToken, scope.teamId),
77+
},
78+
params: {
79+
path: { webhookID: input.webhookId },
80+
},
81+
body: {
82+
name: input.name,
83+
url: input.url,
84+
events: input.events,
85+
enabled: input.enabled,
86+
},
87+
}
88+
)
89+
90+
if (!response.response.ok || response.error) {
91+
return err(
92+
repoErrorFromHttp(
93+
response.response.status,
94+
response.error?.message ?? 'Failed to upsert webhook',
95+
response.error
96+
)
97+
)
98+
}
99+
100+
return ok(undefined)
101+
}
102+
103+
if (!input.signatureSecret) {
104+
return err(
105+
repoErrorFromHttp(
106+
400,
107+
'signatureSecret is required when creating a webhook'
108+
)
109+
)
110+
}
111+
112+
const response = await deps.infraClient.POST('/events/webhooks', {
113+
headers: {
114+
...deps.authHeaders(scope.accessToken, scope.teamId),
115+
},
116+
body: {
117+
name: input.name,
118+
url: input.url,
119+
events: input.events,
120+
enabled: input.enabled,
121+
signatureSecret: input.signatureSecret,
122+
},
123+
})
99124

100125
if (!response.response.ok || response.error) {
101126
return err(

src/core/server/actions/webhooks-actions.ts

Lines changed: 0 additions & 157 deletions
This file was deleted.

src/core/server/api/routers/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { sandboxesRouter } from './sandboxes'
66
import { supportRouter } from './support'
77
import { teamsRouter } from './teams'
88
import { templatesRouter } from './templates'
9+
import { webhooksRouter } from './webhooks'
910

1011
export const trpcAppRouter = createTRPCRouter({
1112
sandbox: sandboxRouter,
@@ -15,6 +16,7 @@ export const trpcAppRouter = createTRPCRouter({
1516
billing: billingRouter,
1617
support: supportRouter,
1718
teams: teamsRouter,
19+
webhooks: webhooksRouter,
1820
})
1921

2022
export type TRPCAppRouter = typeof trpcAppRouter

0 commit comments

Comments
 (0)