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 {
@@ -32,27 +31,24 @@ function getEventType(filters: WebhookFilters): string {
32
31
33
32
interface WebhooksTableProps {
34
33
webhooks : WebhookResponse [ ] ;
35
- projectClientId : string ;
36
- supportedChainIds : number [ ] ;
37
- client : ThirdwebClient ;
34
+ project : Project ;
38
35
}
39
36
40
37
export function ContractsWebhooksTable ( {
41
38
webhooks,
42
- projectClientId,
43
- client,
44
- supportedChainIds,
39
+ project,
45
40
} : WebhooksTableProps ) {
46
41
const [ isDeleting , setIsDeleting ] = useState < Record < string , boolean > > ( { } ) ;
47
- const { testWebhookEndpoint, isTestingMap } = useTestWebhook ( projectClientId ) ;
48
42
const router = useDashboardRouter ( ) ;
49
43
50
- const handleDeleteWebhook = async ( webhookId : string ) => {
44
+ const webhooksPath = `/team/${ project . teamId } /${ project . slug } /webhooks` ;
45
+
46
+ const _handleDeleteWebhook = async ( webhookId : string ) => {
51
47
if ( isDeleting [ webhookId ] ) return ;
52
48
53
49
try {
54
50
setIsDeleting ( ( prev ) => ( { ...prev , [ webhookId ] : true } ) ) ;
55
- await deleteWebhook ( webhookId , projectClientId ) ;
51
+ await deleteWebhook ( webhookId , project . publishableKey ) ;
56
52
toast . success ( "Webhook deleted successfully" ) ;
57
53
router . refresh ( ) ;
58
54
} catch ( error ) {
@@ -68,41 +64,31 @@ export function ContractsWebhooksTable({
68
64
}
69
65
} ;
70
66
71
- const handleTestWebhook = async ( webhook : WebhookResponse ) => {
72
- const filterType = getEventType ( webhook . filters ) ;
73
- if ( filterType === "Unknown" ) {
74
- toast . error ( "Cannot test webhook" , {
75
- description :
76
- "This webhook does not have a valid event type (event or transaction)." ,
77
- } ) ;
78
- return ;
79
- }
80
- await testWebhookEndpoint (
81
- webhook . webhook_url ,
82
- filterType . toLowerCase ( ) as "event" | "transaction" ,
83
- webhook . id ,
84
- ) ;
85
- } ;
86
-
87
67
const columns : ColumnDef < WebhookResponse > [ ] = [
88
68
{
89
69
accessorKey : "name" ,
90
- cell : ( { row } ) => (
91
- < div className = "flex items-center gap-2" >
92
- < span className = "max-w-40 truncate" title = { row . original . name } >
93
- { row . original . name }
94
- </ span >
95
- </ div >
96
- ) ,
70
+ cell : ( { row } ) => {
71
+ const webhook = row . original ;
72
+ return (
73
+ < div className = "flex items-center gap-2" >
74
+ < span
75
+ className = "max-w-40 truncate text-muted-foreground"
76
+ title = { webhook . name }
77
+ >
78
+ { webhook . name }
79
+ </ span >
80
+ </ div >
81
+ ) ;
82
+ } ,
97
83
header : "Name" ,
98
84
} ,
99
85
{
100
86
accessorKey : "filters" ,
101
87
cell : ( { getValue } ) => {
102
88
const filters = getValue ( ) as WebhookFilters ;
103
- if ( ! filters ) return < span > -</ span > ;
89
+ if ( ! filters ) return < span className = "text-muted-foreground" > -</ span > ;
104
90
const eventType = getEventType ( filters ) ;
105
- return < span > { eventType } </ span > ;
91
+ return < span className = "text-muted-foreground" > { eventType } </ span > ;
106
92
} ,
107
93
header : "Event Type" ,
108
94
} ,
@@ -112,7 +98,9 @@ export function ContractsWebhooksTable({
112
98
const url = getValue ( ) as string ;
113
99
return (
114
100
< div className = "flex items-center gap-2" >
115
- < span className = "max-w-60 truncate" > { url } </ span >
101
+ < span className = "max-w-60 truncate text-muted-foreground" >
102
+ { url }
103
+ </ span >
116
104
< CopyTextButton
117
105
className = "flex h-6 w-6 items-center justify-center"
118
106
copyIconPosition = "right"
@@ -138,7 +126,7 @@ export function ContractsWebhooksTable({
138
126
return (
139
127
< div className = "flex flex-col" >
140
128
< RelativeTime date = { date } />
141
- < span className = "text-muted-foreground text-xs" >
129
+ < span className = "text-muted-foreground text-xs opacity-50 " >
142
130
{ formattedDate }
143
131
</ span >
144
132
</ div >
@@ -148,12 +136,10 @@ export function ContractsWebhooksTable({
148
136
} ,
149
137
{
150
138
accessorKey : "suspended_at" ,
151
- cell : ( { row } ) => {
152
- const webhook = row . original ;
153
- const isSuspended = Boolean ( webhook . suspended_at ) ;
139
+ cell : ( ) => {
154
140
return (
155
- < Badge variant = { isSuspended ? "destructive" : "default" } >
156
- { isSuspended ? "Suspended" : "Active" }
141
+ < Badge variant = "secondary" className = "bg-gray-100 text-gray-600" >
142
+ Deprecated
157
143
</ Badge >
158
144
) ;
159
145
} ,
@@ -166,25 +152,11 @@ export function ContractsWebhooksTable({
166
152
167
153
return (
168
154
< div className = "flex items-center justify-end gap-2" >
169
- < Button
170
- aria-label = { `Test webhook ${ webhook . name } ` }
171
- className = "h-8 w-8"
172
- disabled = { isTestingMap [ webhook . id ] || isDeleting [ webhook . id ] }
173
- onClick = { ( ) => handleTestWebhook ( webhook ) }
174
- size = "icon"
175
- variant = "outline"
176
- >
177
- { isTestingMap [ webhook . id ] ? (
178
- < Spinner className = "h-4 w-4" />
179
- ) : (
180
- < PlayIcon className = "h-4 w-4" />
181
- ) }
182
- </ Button >
183
155
< Button
184
156
aria-label = { `Delete webhook ${ webhook . name } ` }
185
157
className = "h-8 w-8 text-red-500 hover:border-red-700 hover:text-red-700"
186
158
disabled = { isDeleting [ webhook . id ] }
187
- onClick = { ( ) => handleDeleteWebhook ( webhook . id ) }
159
+ onClick = { ( ) => _handleDeleteWebhook ( webhook . id ) }
188
160
size = "icon"
189
161
variant = "outline"
190
162
>
@@ -217,20 +189,36 @@ export function ContractsWebhooksTable({
217
189
218
190
return (
219
191
< div className = "w-full" >
192
+ { /* Deprecation Notice */ }
193
+ < div className = "mb-4 rounded-lg border border-amber-200 bg-amber-50 p-4" >
194
+ < div className = "flex items-start gap-3" >
195
+ < AlertTriangleIcon className = "h-5 w-5 text-amber-600 mt-0.5" />
196
+ < div className = "flex-1" >
197
+ < h3 className = "text-sm font-medium text-amber-800" >
198
+ Legacy Webhooks (Deprecated)
199
+ </ h3 >
200
+ < p className = "mt-1 text-sm text-amber-700" >
201
+ Contract webhooks are deprecated, but will continue to work for
202
+ the time being. New unified webhooks are available in the{ " " }
203
+ < Link
204
+ 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"
205
+ href = { webhooksPath }
206
+ >
207
+ Webhooks
208
+ </ Link > { " " }
209
+ section.
210
+ </ p >
211
+ </ div >
212
+ </ div >
213
+ </ div >
214
+
220
215
< TWTable
221
216
columns = { columns }
222
217
data = { sortedWebhooks }
223
218
isFetched = { true }
224
219
isPending = { false }
225
- title = "Webhooks"
220
+ title = "Legacy Webhooks"
226
221
/>
227
- < div className = "mt-4 flex justify-end" >
228
- < CreateContractWebhookButton
229
- client = { client }
230
- projectClientId = { projectClientId }
231
- supportedChainIds = { supportedChainIds }
232
- />
233
- </ div >
234
222
</ div >
235
223
) ;
236
224
}
0 commit comments