2
2
3
3
import type { ColumnDef } from "@tanstack/react-table" ;
4
4
import { format } from "date-fns" ;
5
- import { PlayIcon , TrashIcon } from "lucide-react" ;
5
+ import { AlertTriangleIcon , TrashIcon } from "lucide-react" ;
6
+ import Link from "next/link" ;
6
7
import { useMemo , useState } from "react" ;
7
8
import { toast } from "sonner" ;
8
- import type { ThirdwebClient } from "thirdweb" ;
9
9
import {
10
10
deleteWebhook ,
11
11
type WebhookFilters ,
12
12
type WebhookResponse ,
13
13
} from "@/api/insight/webhooks" ;
14
+ import type { Project } from "@/api/projects" ;
14
15
import { TWTable } from "@/components/blocks/TWTable" ;
15
16
import { Badge } from "@/components/ui/badge" ;
16
17
import { Button } from "@/components/ui/button" ;
17
18
import { CopyTextButton } from "@/components/ui/CopyTextButton" ;
18
19
import { Spinner } from "@/components/ui/Spinner/Spinner" ;
19
20
import { useDashboardRouter } from "@/lib/DashboardRouter" ;
20
- import { useTestWebhook } from "../hooks/useTestWebhook" ;
21
- import { CreateContractWebhookButton } from "./CreateWebhookModal" ;
22
21
import { RelativeTime } from "./RelativeTime" ;
23
22
24
23
function getEventType ( filters : WebhookFilters ) : string {
@@ -41,27 +40,24 @@ function maskWebhookSecret(secret: string): string {
41
40
42
41
interface WebhooksTableProps {
43
42
webhooks : WebhookResponse [ ] ;
44
- projectClientId : string ;
45
- supportedChainIds : number [ ] ;
46
- client : ThirdwebClient ;
43
+ project : Project ;
47
44
}
48
45
49
46
export function ContractsWebhooksTable ( {
50
47
webhooks,
51
- projectClientId,
52
- client,
53
- supportedChainIds,
48
+ project,
54
49
} : WebhooksTableProps ) {
55
50
const [ isDeleting , setIsDeleting ] = useState < Record < string , boolean > > ( { } ) ;
56
- const { testWebhookEndpoint, isTestingMap } = useTestWebhook ( projectClientId ) ;
57
51
const router = useDashboardRouter ( ) ;
58
52
59
- const handleDeleteWebhook = async ( webhookId : string ) => {
53
+ const webhooksPath = `/team/${ project . teamId } /${ project . slug } /webhooks` ;
54
+
55
+ const _handleDeleteWebhook = async ( webhookId : string ) => {
60
56
if ( isDeleting [ webhookId ] ) return ;
61
57
62
58
try {
63
59
setIsDeleting ( ( prev ) => ( { ...prev , [ webhookId ] : true } ) ) ;
64
- await deleteWebhook ( webhookId , projectClientId ) ;
60
+ await deleteWebhook ( webhookId , project . publishableKey ) ;
65
61
toast . success ( "Webhook deleted successfully" ) ;
66
62
router . refresh ( ) ;
67
63
} catch ( error ) {
@@ -77,41 +73,31 @@ export function ContractsWebhooksTable({
77
73
}
78
74
} ;
79
75
80
- const handleTestWebhook = async ( webhook : WebhookResponse ) => {
81
- const filterType = getEventType ( webhook . filters ) ;
82
- if ( filterType === "Unknown" ) {
83
- toast . error ( "Cannot test webhook" , {
84
- description :
85
- "This webhook does not have a valid event type (event or transaction)." ,
86
- } ) ;
87
- return ;
88
- }
89
- await testWebhookEndpoint (
90
- webhook . webhook_url ,
91
- filterType . toLowerCase ( ) as "event" | "transaction" ,
92
- webhook . id ,
93
- ) ;
94
- } ;
95
-
96
76
const columns : ColumnDef < WebhookResponse > [ ] = [
97
77
{
98
78
accessorKey : "name" ,
99
- cell : ( { row } ) => (
100
- < div className = "flex items-center gap-2" >
101
- < span className = "max-w-40 truncate" title = { row . original . name } >
102
- { row . original . name }
103
- </ span >
104
- </ div >
105
- ) ,
79
+ cell : ( { row } ) => {
80
+ const webhook = row . original ;
81
+ return (
82
+ < div className = "flex items-center gap-2" >
83
+ < span
84
+ className = "max-w-40 truncate text-muted-foreground"
85
+ title = { webhook . name }
86
+ >
87
+ { webhook . name }
88
+ </ span >
89
+ </ div >
90
+ ) ;
91
+ } ,
106
92
header : "Name" ,
107
93
} ,
108
94
{
109
95
accessorKey : "filters" ,
110
96
cell : ( { getValue } ) => {
111
97
const filters = getValue ( ) as WebhookFilters ;
112
- if ( ! filters ) return < span > -</ span > ;
98
+ if ( ! filters ) return < span className = "text-muted-foreground" > -</ span > ;
113
99
const eventType = getEventType ( filters ) ;
114
- return < span > { eventType } </ span > ;
100
+ return < span className = "text-muted-foreground" > { eventType } </ span > ;
115
101
} ,
116
102
header : "Event Type" ,
117
103
} ,
@@ -121,7 +107,9 @@ export function ContractsWebhooksTable({
121
107
const url = getValue ( ) as string ;
122
108
return (
123
109
< div className = "flex items-center gap-2" >
124
- < span className = "max-w-60 truncate" > { url } </ span >
110
+ < span className = "max-w-60 truncate text-muted-foreground" >
111
+ { url }
112
+ </ span >
125
113
< CopyTextButton
126
114
className = "flex h-6 w-6 items-center justify-center"
127
115
copyIconPosition = "right"
@@ -171,7 +159,7 @@ export function ContractsWebhooksTable({
171
159
return (
172
160
< div className = "flex flex-col" >
173
161
< RelativeTime date = { date } />
174
- < span className = "text-muted-foreground text-xs" >
162
+ < span className = "text-muted-foreground text-xs opacity-50 " >
175
163
{ formattedDate }
176
164
</ span >
177
165
</ div >
@@ -181,12 +169,10 @@ export function ContractsWebhooksTable({
181
169
} ,
182
170
{
183
171
accessorKey : "suspended_at" ,
184
- cell : ( { row } ) => {
185
- const webhook = row . original ;
186
- const isSuspended = Boolean ( webhook . suspended_at ) ;
172
+ cell : ( ) => {
187
173
return (
188
- < Badge variant = { isSuspended ? "destructive" : "default" } >
189
- { isSuspended ? "Suspended" : "Active" }
174
+ < Badge variant = "secondary" className = "bg-gray-100 text-gray-600" >
175
+ Deprecated
190
176
</ Badge >
191
177
) ;
192
178
} ,
@@ -199,25 +185,11 @@ export function ContractsWebhooksTable({
199
185
200
186
return (
201
187
< div className = "flex items-center justify-end gap-2" >
202
- < Button
203
- aria-label = { `Test webhook ${ webhook . name } ` }
204
- className = "h-8 w-8"
205
- disabled = { isTestingMap [ webhook . id ] || isDeleting [ webhook . id ] }
206
- onClick = { ( ) => handleTestWebhook ( webhook ) }
207
- size = "icon"
208
- variant = "outline"
209
- >
210
- { isTestingMap [ webhook . id ] ? (
211
- < Spinner className = "h-4 w-4" />
212
- ) : (
213
- < PlayIcon className = "h-4 w-4" />
214
- ) }
215
- </ Button >
216
188
< Button
217
189
aria-label = { `Delete webhook ${ webhook . name } ` }
218
190
className = "h-8 w-8 text-red-500 hover:border-red-700 hover:text-red-700"
219
191
disabled = { isDeleting [ webhook . id ] }
220
- onClick = { ( ) => handleDeleteWebhook ( webhook . id ) }
192
+ onClick = { ( ) => _handleDeleteWebhook ( webhook . id ) }
221
193
size = "icon"
222
194
variant = "outline"
223
195
>
@@ -250,20 +222,36 @@ export function ContractsWebhooksTable({
250
222
251
223
return (
252
224
< div className = "w-full" >
225
+ { /* Deprecation Notice */ }
226
+ < div className = "mb-4 rounded-lg border border-amber-200 bg-amber-50 p-4" >
227
+ < div className = "flex items-start gap-3" >
228
+ < AlertTriangleIcon className = "h-5 w-5 text-amber-600 mt-0.5" />
229
+ < div className = "flex-1" >
230
+ < h3 className = "text-sm font-medium text-amber-800" >
231
+ Legacy Webhooks (Deprecated)
232
+ </ h3 >
233
+ < p className = "mt-1 text-sm text-amber-700" >
234
+ Contract webhooks are deprecated, but will continue to work for
235
+ the time being. New unified webhooks are available in the{ " " }
236
+ < Link
237
+ className = "inline-flex items-center rounded-md bg-blue-50 px-2.5 py-0.5 text-xs font-medium text-blue-700 ring-1 ring-inset ring-blue-700/10 hover:bg-blue-100 transition-colors"
238
+ href = { webhooksPath }
239
+ >
240
+ Webhooks
241
+ </ Link > { " " }
242
+ section.
243
+ </ p >
244
+ </ div >
245
+ </ div >
246
+ </ div >
247
+
253
248
< TWTable
254
249
columns = { columns }
255
250
data = { sortedWebhooks }
256
251
isFetched = { true }
257
252
isPending = { false }
258
- title = "Webhooks"
253
+ title = "Legacy Webhooks"
259
254
/>
260
- < div className = "mt-4 flex justify-end" >
261
- < CreateContractWebhookButton
262
- client = { client }
263
- projectClientId = { projectClientId }
264
- supportedChainIds = { supportedChainIds }
265
- />
266
- </ div >
267
255
</ div >
268
256
) ;
269
257
}
0 commit comments