1
1
"use client" ;
2
2
3
- import { Flex , IconButton , Select , Skeleton } from "@chakra-ui/react" ;
4
- import { createColumnHelper } from "@tanstack/react-table" ;
5
- import { Legacy_CopyButton } from "chakra/button" ;
6
- import { Text } from "chakra/text" ;
7
- import {
8
- ChevronFirstIcon ,
9
- ChevronLastIcon ,
10
- ChevronLeftIcon ,
11
- ChevronRightIcon ,
12
- } from "lucide-react" ;
3
+ import { ArrowUpRightIcon } from "lucide-react" ;
4
+ import Link from "next/link" ;
13
5
import { useMemo , useState } from "react" ;
14
6
import type { ThirdwebContract } from "thirdweb" ;
15
7
import { getAccounts , totalAccounts } from "thirdweb/extensions/erc4337" ;
16
8
import { useReadContract } from "thirdweb/react" ;
17
- import { TWTable } from "@/components/blocks/TWTable" ;
9
+ import { PaginationButtons } from "@/components/blocks/pagination-buttons" ;
10
+ import { CopyTextButton } from "@/components/ui/CopyTextButton" ;
11
+ import { Skeleton } from "@/components/ui/skeleton" ;
12
+ import {
13
+ Table ,
14
+ TableBody ,
15
+ TableCell ,
16
+ TableContainer ,
17
+ TableHead ,
18
+ TableHeader ,
19
+ TableRow ,
20
+ } from "@/components/ui/table" ;
18
21
import { useChainSlug } from "@/hooks/chains/chainSlug" ;
19
- import { useDashboardRouter } from "@/lib/DashboardRouter" ;
20
22
import type { ProjectMeta } from "../../../../../../team/[team_slug]/[project_slug]/contract/[chainIdOrSlug]/[contractAddress]/types" ;
21
23
import { buildContractPagePath } from "../../_utils/contract-page-path" ;
22
24
23
- const columnHelper = createColumnHelper < { account : string } > ( ) ;
24
-
25
- const columns = [
26
- columnHelper . accessor ( "account" , {
27
- cell : ( info ) => (
28
- < Flex align = "center" gap = { 2 } >
29
- < Text fontFamily = "mono" > { info . getValue ( ) } </ Text >
30
- < Legacy_CopyButton
31
- aria-label = "Copy account address"
32
- colorScheme = "primary"
33
- value = { info . getValue ( ) }
34
- />
35
- </ Flex >
36
- ) ,
37
- header : "Account" ,
38
- } ) ,
39
- ] ;
25
+ const pageSize = 10 ;
40
26
41
27
type AccountsTableProps = {
42
28
contract : ThirdwebContract ;
43
29
projectMeta : ProjectMeta | undefined ;
44
30
} ;
45
31
46
- export const AccountsTable : React . FC < AccountsTableProps > = ( {
47
- contract,
48
- projectMeta,
49
- } ) => {
50
- const router = useDashboardRouter ( ) ;
32
+ export function AccountsTable ( { contract, projectMeta } : AccountsTableProps ) {
51
33
const chainSlug = useChainSlug ( contract . chain . id ) ;
52
34
53
35
const [ currentPage , setCurrentPage ] = useState ( 0 ) ;
54
- // default page size of 25
55
- const [ pageSize , setPageSize ] = useState ( 25 ) ;
56
36
57
37
const totalAccountsQuery = useReadContract ( totalAccounts , { contract } ) ;
58
38
@@ -72,7 +52,7 @@ export const AccountsTable: React.FC<AccountsTableProps> = ({
72
52
) ;
73
53
}
74
54
return currentPage * pageSize + pageSize ;
75
- } , [ currentPage , pageSize , totalAccountsQuery . data ] ) ;
55
+ } , [ currentPage , totalAccountsQuery . data ] ) ;
76
56
77
57
const accountsQuery = useReadContract ( getAccounts , {
78
58
contract,
@@ -82,105 +62,91 @@ export const AccountsTable: React.FC<AccountsTableProps> = ({
82
62
} ) ;
83
63
84
64
const totalPages = Math . ceil ( totalAccountsNum / pageSize ) ;
85
-
86
- const canNextPage = currentPage < totalPages - 1 ;
87
- const canPreviousPage = currentPage > 0 ;
65
+ const showPagination = totalPages > 1 ;
88
66
89
67
const data = accountsQuery . data || [ ] ;
90
68
91
69
return (
92
- < Flex direction = "column" gap = { 4 } >
70
+ < div className = "border rounded-lg overflow-hidden" >
93
71
{ /* TODO add a skeleton when loading*/ }
94
- < TWTable
95
- columns = { columns }
96
- data = { data . map ( ( account ) => ( { account } ) ) }
97
- isFetched = { accountsQuery . isFetched }
98
- isPending = { accountsQuery . isPending }
99
- onRowClick = { ( row ) => {
100
- const accountContractPagePath = buildContractPagePath ( {
101
- chainIdOrSlug : chainSlug . toString ( ) ,
102
- contractAddress : row . account ,
103
- projectMeta,
104
- } ) ;
105
-
106
- router . push ( accountContractPagePath ) ;
107
- } }
108
- title = "account"
109
- />
110
- { /* pagination */ }
111
- < div className = "flex w-full items-center justify-center" >
112
- < Flex align = "center" direction = "row" gap = { 2 } >
113
- < IconButton
114
- aria-label = "first page"
115
- icon = { < ChevronFirstIcon className = "size-4" /> }
116
- isDisabled = { totalAccountsQuery . isPending }
117
- onClick = { ( ) => setCurrentPage ( 0 ) }
118
- />
119
- < IconButton
120
- aria-label = "previous page"
121
- icon = { < ChevronLeftIcon className = "size-4" /> }
122
- isDisabled = { totalAccountsQuery . isPending || ! canPreviousPage }
123
- onClick = { ( ) => {
124
- setCurrentPage ( ( curr ) => {
125
- if ( curr > 0 ) {
126
- return curr - 1 ;
127
- }
128
- return curr ;
129
- } ) ;
130
- } }
131
- />
132
- < Text whiteSpace = "nowrap" >
133
- Page < strong > { currentPage + 1 } </ strong > of{ " " }
134
- < Skeleton
135
- as = "span"
136
- display = "inline"
137
- isLoaded = { totalAccountsQuery . isSuccess }
138
- >
139
- < strong > { totalPages } </ strong >
140
- </ Skeleton >
141
- </ Text >
142
- < IconButton
143
- aria-label = "next page"
144
- icon = { < ChevronRightIcon className = "size-4" /> }
145
- isDisabled = { totalAccountsQuery . isPending || ! canNextPage }
146
- onClick = { ( ) =>
147
- setCurrentPage ( ( curr ) => {
148
- if ( curr < totalPages - 1 ) {
149
- return curr + 1 ;
150
- }
151
- return curr ;
152
- } )
153
- }
154
- />
155
- < IconButton
156
- aria-label = "last page"
157
- icon = { < ChevronLastIcon className = "size-4" /> }
158
- isDisabled = { totalAccountsQuery . isPending || ! canNextPage }
159
- onClick = { ( ) => setCurrentPage ( totalPages - 1 ) }
72
+ < TableContainer className = "border-0 rounded-none" >
73
+ < Table >
74
+ < TableHeader >
75
+ < TableRow >
76
+ < TableHead > Account</ TableHead >
77
+ </ TableRow >
78
+ </ TableHeader >
79
+ < TableBody >
80
+ { accountsQuery . isPending &&
81
+ new Array ( pageSize ) . fill ( 0 ) . map ( ( _ , index ) => (
82
+ // biome-ignore lint/suspicious/noArrayIndexKey: ok
83
+ < SkeletonRow key = { index } />
84
+ ) ) }
85
+
86
+ { ! accountsQuery . isPending &&
87
+ data . map ( ( account ) => {
88
+ const accountContractPagePath = buildContractPagePath ( {
89
+ chainIdOrSlug : chainSlug . toString ( ) ,
90
+ contractAddress : account ,
91
+ projectMeta,
92
+ } ) ;
93
+
94
+ return (
95
+ < TableRow linkBox key = { account } className = "hover:bg-muted/50" >
96
+ < TableCell className = "py-6" >
97
+ < Link
98
+ href = { accountContractPagePath }
99
+ target = "_blank"
100
+ rel = "noopener noreferrer"
101
+ className = "before:absolute before:inset-0"
102
+ aria-label = "View account"
103
+ />
104
+
105
+ < div className = "flex items-center justify-between gap-2" >
106
+ < CopyTextButton
107
+ textToShow = { account }
108
+ textToCopy = { account }
109
+ variant = "ghost"
110
+ tooltip = "Copy account address"
111
+ copyIconPosition = "right"
112
+ className = "z-10 relative"
113
+ />
114
+
115
+ < ArrowUpRightIcon className = "size-4 text-muted-foreground" />
116
+ </ div >
117
+ </ TableCell >
118
+ </ TableRow >
119
+ ) ;
120
+ } ) }
121
+ </ TableBody >
122
+ </ Table >
123
+
124
+ { ! accountsQuery . isPending && data . length === 0 && (
125
+ < div className = "px-4 text-center py-20 flex items-center justify-center text-muted-foreground" >
126
+ No accounts
127
+ </ div >
128
+ ) }
129
+ </ TableContainer >
130
+
131
+ { showPagination && (
132
+ < div className = "py-4 border-t bg-card" >
133
+ < PaginationButtons
134
+ activePage = { currentPage + 1 }
135
+ totalPages = { totalPages }
136
+ onPageClick = { ( page ) => setCurrentPage ( page - 1 ) }
160
137
/>
138
+ </ div >
139
+ ) }
140
+ </ div >
141
+ ) ;
142
+ }
161
143
162
- < Select
163
- isDisabled = { totalAccountsQuery . isPending }
164
- onChange = { ( e ) => {
165
- const newPageSize = Number . parseInt ( e . target . value as string , 10 ) ;
166
- // compute the new page number based on the new page size
167
- const newPage = Math . floor (
168
- ( currentPage * pageSize ) / newPageSize ,
169
- ) ;
170
- setCurrentPage ( newPage ) ;
171
- setPageSize ( newPageSize ) ;
172
- } }
173
- value = { pageSize }
174
- >
175
- < option value = "25" > 25</ option >
176
- < option value = "50" > 50</ option >
177
- < option value = "100" > 100</ option >
178
- < option value = "250" > 250</ option >
179
- < option value = "500" > 500</ option >
180
- </ Select >
181
- </ Flex >
182
- </ div >
183
- </ Flex >
144
+ function SkeletonRow ( ) {
145
+ return (
146
+ < TableRow >
147
+ < TableCell className = "py-6" >
148
+ < Skeleton className = "h-6 w-[364px]" />
149
+ </ TableCell >
150
+ </ TableRow >
184
151
) ;
185
- } ;
186
- //
152
+ }
0 commit comments