22
33import type { ColumnDef } from "@tanstack/react-table" ;
44import { format } from "date-fns" ;
5- import { PlayIcon , TrashIcon } from "lucide-react" ;
5+ import { AlertTriangleIcon , TrashIcon } from "lucide-react" ;
6+ import Link from "next/link" ;
67import { useMemo , useState } from "react" ;
78import { toast } from "sonner" ;
8- import type { ThirdwebClient } from "thirdweb" ;
99import {
1010 deleteWebhook ,
1111 type WebhookFilters ,
1212 type WebhookResponse ,
1313} from "@/api/insight/webhooks" ;
14+ import type { Project } from "@/api/projects" ;
1415import { TWTable } from "@/components/blocks/TWTable" ;
1516import { Badge } from "@/components/ui/badge" ;
1617import { Button } from "@/components/ui/button" ;
1718import { CopyTextButton } from "@/components/ui/CopyTextButton" ;
1819import { Spinner } from "@/components/ui/Spinner/Spinner" ;
1920import { useDashboardRouter } from "@/lib/DashboardRouter" ;
20- import { useTestWebhook } from "../hooks/useTestWebhook" ;
21- import { CreateContractWebhookButton } from "./CreateWebhookModal" ;
2221import { RelativeTime } from "./RelativeTime" ;
2322
2423function getEventType ( filters : WebhookFilters ) : string {
@@ -41,27 +40,24 @@ function maskWebhookSecret(secret: string): string {
4140
4241interface WebhooksTableProps {
4342 webhooks : WebhookResponse [ ] ;
44- projectClientId : string ;
45- supportedChainIds : number [ ] ;
46- client : ThirdwebClient ;
43+ project : Project ;
4744}
4845
4946export function ContractsWebhooksTable ( {
5047 webhooks,
51- projectClientId,
52- client,
53- supportedChainIds,
48+ project,
5449} : WebhooksTableProps ) {
5550 const [ isDeleting , setIsDeleting ] = useState < Record < string , boolean > > ( { } ) ;
56- const { testWebhookEndpoint, isTestingMap } = useTestWebhook ( projectClientId ) ;
5751 const router = useDashboardRouter ( ) ;
5852
59- const handleDeleteWebhook = async ( webhookId : string ) => {
53+ const webhooksPath = `/team/${ project . teamId } /${ project . slug } /webhooks` ;
54+
55+ const _handleDeleteWebhook = async ( webhookId : string ) => {
6056 if ( isDeleting [ webhookId ] ) return ;
6157
6258 try {
6359 setIsDeleting ( ( prev ) => ( { ...prev , [ webhookId ] : true } ) ) ;
64- await deleteWebhook ( webhookId , projectClientId ) ;
60+ await deleteWebhook ( webhookId , project . publishableKey ) ;
6561 toast . success ( "Webhook deleted successfully" ) ;
6662 router . refresh ( ) ;
6763 } catch ( error ) {
@@ -77,41 +73,31 @@ export function ContractsWebhooksTable({
7773 }
7874 } ;
7975
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-
9676 const columns : ColumnDef < WebhookResponse > [ ] = [
9777 {
9878 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+ } ,
10692 header : "Name" ,
10793 } ,
10894 {
10995 accessorKey : "filters" ,
11096 cell : ( { getValue } ) => {
11197 const filters = getValue ( ) as WebhookFilters ;
112- if ( ! filters ) return < span > -</ span > ;
98+ if ( ! filters ) return < span className = "text-muted-foreground" > -</ span > ;
11399 const eventType = getEventType ( filters ) ;
114- return < span > { eventType } </ span > ;
100+ return < span className = "text-muted-foreground" > { eventType } </ span > ;
115101 } ,
116102 header : "Event Type" ,
117103 } ,
@@ -121,7 +107,9 @@ export function ContractsWebhooksTable({
121107 const url = getValue ( ) as string ;
122108 return (
123109 < 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 >
125113 < CopyTextButton
126114 className = "flex h-6 w-6 items-center justify-center"
127115 copyIconPosition = "right"
@@ -171,7 +159,7 @@ export function ContractsWebhooksTable({
171159 return (
172160 < div className = "flex flex-col" >
173161 < RelativeTime date = { date } />
174- < span className = "text-muted-foreground text-xs" >
162+ < span className = "text-muted-foreground text-xs opacity-50 " >
175163 { formattedDate }
176164 </ span >
177165 </ div >
@@ -181,12 +169,10 @@ export function ContractsWebhooksTable({
181169 } ,
182170 {
183171 accessorKey : "suspended_at" ,
184- cell : ( { row } ) => {
185- const webhook = row . original ;
186- const isSuspended = Boolean ( webhook . suspended_at ) ;
172+ cell : ( ) => {
187173 return (
188- < Badge variant = { isSuspended ? "destructive" : "default" } >
189- { isSuspended ? "Suspended" : "Active" }
174+ < Badge variant = "secondary" className = "bg-gray-100 text-gray-600" >
175+ Deprecated
190176 </ Badge >
191177 ) ;
192178 } ,
@@ -199,25 +185,11 @@ export function ContractsWebhooksTable({
199185
200186 return (
201187 < 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 >
216188 < Button
217189 aria-label = { `Delete webhook ${ webhook . name } ` }
218190 className = "h-8 w-8 text-red-500 hover:border-red-700 hover:text-red-700"
219191 disabled = { isDeleting [ webhook . id ] }
220- onClick = { ( ) => handleDeleteWebhook ( webhook . id ) }
192+ onClick = { ( ) => _handleDeleteWebhook ( webhook . id ) }
221193 size = "icon"
222194 variant = "outline"
223195 >
@@ -250,20 +222,36 @@ export function ContractsWebhooksTable({
250222
251223 return (
252224 < 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+
253248 < TWTable
254249 columns = { columns }
255250 data = { sortedWebhooks }
256251 isFetched = { true }
257252 isPending = { false }
258- title = "Webhooks"
253+ title = "Legacy Webhooks"
259254 />
260- < div className = "mt-4 flex justify-end" >
261- < CreateContractWebhookButton
262- client = { client }
263- projectClientId = { projectClientId }
264- supportedChainIds = { supportedChainIds }
265- />
266- </ div >
267255 </ div >
268256 ) ;
269257}
0 commit comments