@@ -5,62 +5,21 @@ import { Button } from '@/components/ui/button';
55import { Badge } from '@/components/ui/badge' ;
66import { Avatar , AvatarFallback } from '@/components/ui/avatar' ;
77import { Clock , ArrowUpRight , ArrowDownLeft , ExternalLink , CheckCircle , XCircle } from 'lucide-react' ;
8-
9- interface PendingTransaction {
10- id : string ;
11- type : 'send' | 'receive' | 'swap' ;
12- amount : string ;
13- token : string ;
14- status : 'pending' | 'confirming' | 'failed' ;
15- timestamp : string ;
16- from ?: string ;
17- to ?: string ;
18- hash ?: string ;
19- }
8+ import { SafeMultisigTransactionResponse } from '@safe-global/types-kit' ;
9+ import { usePendingTransactions } from '@/hooks/safe/usePendingTransactions' ;
2010
2111interface PendingTransactionsProps {
22- transactions ?: PendingTransaction [ ] ;
2312 onViewAll ?: ( ) => void ;
2413}
2514
26- const mockTransactions : PendingTransaction [ ] = [
27- {
28- id : '1' ,
29- type : 'send' ,
30- amount : '0.5' ,
31- token : 'ETH' ,
32- status : 'pending' ,
33- timestamp : '2 min ago' ,
34- to : '0x1234...5678' ,
35- hash : '0xabcd...efgh' ,
36- } ,
37- {
38- id : '2' ,
39- type : 'swap' ,
40- amount : '1000' ,
41- token : 'USDC' ,
42- status : 'confirming' ,
43- timestamp : '5 min ago' ,
44- hash : '0x9876...5432' ,
45- } ,
46- {
47- id : '3' ,
48- type : 'receive' ,
49- amount : '2.5' ,
50- token : 'ETH' ,
51- status : 'pending' ,
52- timestamp : '10 min ago' ,
53- from : '0x5678...1234' ,
54- hash : '0xdcba...hgfe' ,
55- } ,
56- ] ;
15+ export function PendingTransactions ( { onViewAll } : PendingTransactionsProps ) {
16+ const { pendingTransactions, loading, totalCount, error } = usePendingTransactions ( ) ;
5717
58- export function PendingTransactions ( { transactions = mockTransactions , onViewAll } : PendingTransactionsProps ) {
5918 const getStatusIcon = ( status : string ) => {
6019 switch ( status ) {
61- case 'confirming ' :
20+ case 'AWAITING_EXECUTION ' :
6221 return < CheckCircle className = 'h-4 w-4 text-green-500' /> ;
63- case 'failed ' :
22+ case 'FAILED ' :
6423 return < XCircle className = 'h-4 w-4 text-red-500' /> ;
6524 default :
6625 return < Clock className = 'h-4 w-4 text-yellow-500' /> ;
@@ -69,44 +28,59 @@ export function PendingTransactions({ transactions = mockTransactions, onViewAll
6928
7029 const getStatusBadge = ( status : string ) => {
7130 switch ( status ) {
72- case 'confirming ' :
31+ case 'AWAITING_EXECUTION ' :
7332 return (
7433 < Badge variant = 'default' className = 'bg-green-500' >
75- Confirming
34+ Ready to Execute
7635 </ Badge >
7736 ) ;
78- case 'failed ' :
37+ case 'FAILED ' :
7938 return < Badge variant = 'destructive' > Failed</ Badge > ;
39+ case 'AWAITING_CONFIRMATIONS' :
40+ return < Badge variant = 'secondary' > Awaiting Confirmations</ Badge > ;
8041 default :
8142 return < Badge variant = 'secondary' > Pending</ Badge > ;
8243 }
8344 } ;
8445
85- const getTypeIcon = ( type : string ) => {
86- switch ( type ) {
87- case 'send' :
88- return < ArrowUpRight className = 'h-4 w-4 text-red-500' /> ;
89- case 'receive' :
90- return < ArrowDownLeft className = 'h-4 w-4 text-green-500' /> ;
91- case 'swap' :
92- return < ArrowUpRight className = 'h-4 w-4 text-blue-500' /> ;
93- default :
94- return < ArrowUpRight className = 'h-4 w-4' /> ;
46+ const getTypeIcon = ( tx : SafeMultisigTransactionResponse ) => {
47+ // Determine transaction type based on data and value
48+ if ( tx . value && tx . value !== '0' ) {
49+ return < ArrowUpRight className = 'h-4 w-4 text-red-500' /> ;
9550 }
51+ if ( tx . data && tx . data !== '0x' ) {
52+ return < ArrowUpRight className = 'h-4 w-4 text-blue-500' /> ;
53+ }
54+ return < ArrowUpRight className = 'h-4 w-4' /> ;
55+ } ;
56+
57+ const formatAmount = ( value : string ) => {
58+ if ( ! value || value === '0' ) return '0' ;
59+ // Convert from wei to ether (simplified)
60+ const etherValue = parseFloat ( value ) / Math . pow ( 10 , 18 ) ;
61+ return etherValue . toFixed ( 6 ) ;
62+ } ;
63+
64+ const formatTimestamp = ( timestamp : string ) => {
65+ const date = new Date ( timestamp ) ;
66+ const now = new Date ( ) ;
67+ const diffInMinutes = Math . floor ( ( now . getTime ( ) - date . getTime ( ) ) / ( 1000 * 60 ) ) ;
68+
69+ if ( diffInMinutes < 1 ) return 'Just now' ;
70+ if ( diffInMinutes < 60 ) return `${ diffInMinutes } min ago` ;
71+ if ( diffInMinutes < 1440 ) return `${ Math . floor ( diffInMinutes / 60 ) } hours ago` ;
72+ return `${ Math . floor ( diffInMinutes / 1440 ) } days ago` ;
9673 } ;
9774
9875 return (
9976 < Card >
10077 < CardHeader className = 'pb-4' >
10178 < div className = 'flex items-center justify-between' >
10279 < div className = 'flex items-center gap-3' >
103- < div className = 'w-8 h-8 bg-yellow-500/10 rounded-lg flex items-center justify-center' >
104- < Clock className = 'h-4 w-4 text-yellow-500' />
105- </ div >
10680 < div >
10781 < CardTitle className = 'text-lg' > Pending Transactions</ CardTitle >
10882 < p className = 'text-sm text-muted-foreground' >
109- { transactions . length } transaction{ transactions . length !== 1 ? 's' : '' } waiting
83+ { loading ? 'Loading...' : ` ${ totalCount } transaction${ totalCount !== 1 ? 's' : '' } waiting` }
11084 </ p >
11185 </ div >
11286 </ div >
@@ -118,36 +92,54 @@ export function PendingTransactions({ transactions = mockTransactions, onViewAll
11892 </ div >
11993 </ CardHeader >
12094 < CardContent className = 'space-y-4' >
121- { transactions . length === 0 ? (
95+ { loading ? (
96+ < div className = 'space-y-3' >
97+ { [ 1 , 2 , 3 ] . map ( ( i ) => (
98+ < div key = { i } className = 'flex items-center justify-between p-3 rounded-lg border' >
99+ < div className = 'flex items-center gap-3' >
100+ < div className = 'h-8 w-8 bg-muted rounded-full animate-pulse' />
101+ < div className = 'space-y-2' >
102+ < div className = 'h-4 w-24 bg-muted rounded animate-pulse' />
103+ < div className = 'h-3 w-16 bg-muted rounded animate-pulse' />
104+ </ div >
105+ </ div >
106+ < div className = 'h-6 w-20 bg-muted rounded animate-pulse' />
107+ </ div >
108+ ) ) }
109+ </ div >
110+ ) : error ? (
111+ < div className = 'text-center py-8' >
112+ < Clock className = 'h-12 w-12 text-muted-foreground mx-auto mb-4' />
113+ < p className = 'text-muted-foreground' > Failed to load transactions</ p >
114+ </ div >
115+ ) : pendingTransactions . length === 0 ? (
122116 < div className = 'text-center py-8' >
123117 < Clock className = 'h-12 w-12 text-muted-foreground mx-auto mb-4' />
124118 < p className = 'text-muted-foreground' > No pending transactions</ p >
125119 </ div >
126120 ) : (
127121 < div className = 'space-y-3' >
128- { transactions . map ( ( tx ) => (
129- < div key = { tx . id } className = 'flex items-center justify-between p-3 rounded-lg border' >
122+ { pendingTransactions . slice ( 0 , 5 ) . map ( ( tx ) => (
123+ < div key = { tx . safeTxHash } className = 'flex items-center justify-between p-3 rounded-lg border' >
130124 < div className = 'flex items-center gap-3' >
131125 < Avatar className = 'h-8 w-8' >
132- < AvatarFallback className = 'text-xs' > { getTypeIcon ( tx . type ) } </ AvatarFallback >
126+ < AvatarFallback className = 'text-xs' > { getTypeIcon ( tx ) } </ AvatarFallback >
133127 </ Avatar >
134128 < div className = 'flex-1' >
135129 < div className = 'flex items-center gap-2' >
136- < span className = 'font-medium' >
137- { tx . amount } { tx . token }
138- </ span >
139- { getStatusIcon ( tx . status ) }
130+ < span className = 'font-medium' > { formatAmount ( tx . value ) } ETH</ span >
131+ { getStatusIcon ( tx . executionDate ? 'AWAITING_EXECUTION' : 'AWAITING_CONFIRMATIONS' ) }
140132 </ div >
141133 < div className = 'flex items-center gap-2 text-sm text-muted-foreground' >
142- < span className = 'capitalize' > { tx . type } </ span >
134+ < span > Nonce: { tx . nonce } </ span >
143135 < span > •</ span >
144- < span > { tx . timestamp } </ span >
136+ < span > { formatTimestamp ( tx . submissionDate || new Date ( ) . toISOString ( ) ) } </ span >
145137 </ div >
146138 </ div >
147139 </ div >
148140 < div className = 'flex items-center gap-2' >
149- { getStatusBadge ( tx . status ) }
150- { tx . hash && (
141+ { getStatusBadge ( tx . executionDate ? 'AWAITING_EXECUTION' : 'AWAITING_CONFIRMATIONS' ) }
142+ { tx . safeTxHash && (
151143 < Button variant = 'ghost' size = 'sm' className = 'h-8 w-8 p-0' >
152144 < ExternalLink className = 'h-3 w-3' />
153145 </ Button >
0 commit comments