Skip to content

Commit c8b58f6

Browse files
committed
Dashboard: Migrate contract/accounts page from chakra to tailwind
1 parent b371bf9 commit c8b58f6

File tree

5 files changed

+162
-260
lines changed

5 files changed

+162
-260
lines changed
Lines changed: 35 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,47 @@
11
"use client";
22

3-
import { ButtonGroup, Flex } from "@chakra-ui/react";
4-
import { LinkButton } from "chakra/button";
5-
import { Heading } from "chakra/heading";
63
import type { ThirdwebContract } from "thirdweb";
4+
import { UnderlineLink } from "@/components/ui/UnderlineLink";
75
import type { ProjectMeta } from "../../../../../team/[team_slug]/[project_slug]/contract/[chainIdOrSlug]/[contractAddress]/types";
8-
import { AccountsCount } from "./components/accounts-count";
96
import { AccountsTable } from "./components/accounts-table";
107
import { CreateAccountButton } from "./components/create-account-button";
118

12-
interface AccountsPageProps {
9+
export function AccountsPage(props: {
1310
contract: ThirdwebContract;
1411
isLoggedIn: boolean;
1512
projectMeta: ProjectMeta | undefined;
16-
}
17-
18-
export const AccountsPage: React.FC<AccountsPageProps> = ({
19-
contract,
20-
isLoggedIn,
21-
projectMeta,
22-
}) => {
13+
}) {
2314
return (
24-
<Flex direction="column" gap={6}>
25-
<Flex
26-
align={{ base: "left", md: "center" }}
27-
direction={{ base: "column", md: "row" }}
28-
gap={4}
29-
justify="space-between"
30-
>
31-
<Heading size="title.sm">Accounts</Heading>
32-
<ButtonGroup
33-
flexDirection={{ base: "column", md: "row" }}
34-
gap={2}
35-
w="inherit"
36-
>
37-
<LinkButton
38-
href="https://portal.thirdweb.com/wallets/smart-wallet/get-started#3-connect-smart-wallets-in-your-application"
39-
isExternal
40-
variant="solid"
41-
>
42-
View Documentation
43-
</LinkButton>
44-
<CreateAccountButton contract={contract} isLoggedIn={isLoggedIn} />
45-
</ButtonGroup>
46-
</Flex>
47-
<AccountsCount contract={contract} />
48-
<AccountsTable contract={contract} projectMeta={projectMeta} />
49-
</Flex>
15+
<div>
16+
<div className="flex flex-col md:justify-between gap-4 md:flex-row md:items-center">
17+
<div>
18+
<h1 className="text-2xl font-semibold tracking-tight mb-1">
19+
Accounts
20+
</h1>
21+
<p className="text-sm text-muted-foreground">
22+
View list of smart accounts that have been created for this
23+
contract.{" "}
24+
<UnderlineLink
25+
href="https://portal.thirdweb.com/transactions/sponsor"
26+
target="_blank"
27+
rel="noopener noreferrer"
28+
>
29+
Learn more about gas sponsorship
30+
</UnderlineLink>
31+
</p>
32+
</div>
33+
<CreateAccountButton
34+
contract={props.contract}
35+
isLoggedIn={props.isLoggedIn}
36+
/>
37+
</div>
38+
39+
<div className="h-5" />
40+
41+
<AccountsTable
42+
contract={props.contract}
43+
projectMeta={props.projectMeta}
44+
/>
45+
</div>
5046
);
51-
};
47+
}

apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/accounts/components/accounts-count.tsx

Lines changed: 0 additions & 23 deletions
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,38 @@
11
"use client";
22

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";
135
import { useMemo, useState } from "react";
146
import type { ThirdwebContract } from "thirdweb";
157
import { getAccounts, totalAccounts } from "thirdweb/extensions/erc4337";
168
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";
1821
import { useChainSlug } from "@/hooks/chains/chainSlug";
19-
import { useDashboardRouter } from "@/lib/DashboardRouter";
2022
import type { ProjectMeta } from "../../../../../../team/[team_slug]/[project_slug]/contract/[chainIdOrSlug]/[contractAddress]/types";
2123
import { buildContractPagePath } from "../../_utils/contract-page-path";
2224

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;
4026

4127
type AccountsTableProps = {
4228
contract: ThirdwebContract;
4329
projectMeta: ProjectMeta | undefined;
4430
};
4531

46-
export const AccountsTable: React.FC<AccountsTableProps> = ({
47-
contract,
48-
projectMeta,
49-
}) => {
50-
const router = useDashboardRouter();
32+
export function AccountsTable({ contract, projectMeta }: AccountsTableProps) {
5133
const chainSlug = useChainSlug(contract.chain.id);
5234

5335
const [currentPage, setCurrentPage] = useState(0);
54-
// default page size of 25
55-
const [pageSize, setPageSize] = useState(25);
5636

5737
const totalAccountsQuery = useReadContract(totalAccounts, { contract });
5838

@@ -72,7 +52,7 @@ export const AccountsTable: React.FC<AccountsTableProps> = ({
7252
);
7353
}
7454
return currentPage * pageSize + pageSize;
75-
}, [currentPage, pageSize, totalAccountsQuery.data]);
55+
}, [currentPage, totalAccountsQuery.data]);
7656

7757
const accountsQuery = useReadContract(getAccounts, {
7858
contract,
@@ -82,105 +62,91 @@ export const AccountsTable: React.FC<AccountsTableProps> = ({
8262
});
8363

8464
const totalPages = Math.ceil(totalAccountsNum / pageSize);
85-
86-
const canNextPage = currentPage < totalPages - 1;
87-
const canPreviousPage = currentPage > 0;
65+
const showPagination = totalPages > 1;
8866

8967
const data = accountsQuery.data || [];
9068

9169
return (
92-
<Flex direction="column" gap={4}>
70+
<div className="border rounded-lg overflow-hidden">
9371
{/* 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)}
160137
/>
138+
</div>
139+
)}
140+
</div>
141+
);
142+
}
161143

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>
184151
);
185-
};
186-
//
152+
}

0 commit comments

Comments
 (0)