From 5e883ac550292e6fb92cdbd856f51ecf1cdfa534 Mon Sep 17 00:00:00 2001 From: MananTank Date: Tue, 29 Jul 2025 17:36:25 +0000 Subject: [PATCH] Dashboard: Migrate contract/accounts page from chakra to tailwind (#7739) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ## PR-Codex overview This PR focuses on refactoring components in the `accounts` section of the dashboard, enhancing code structure and UI elements while removing deprecated features. ### Detailed summary - Deleted the `accounts-count.tsx` file. - Removed `Legacy_CopyButton` and its usage. - Updated `AccountsPage` to use `UnderlineLink` and improved layout. - Refactored `CreateAccountButton` to streamline props handling. - Replaced `TWTable` with custom table components in `AccountsTable`. - Added pagination functionality and improved loading states. > ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}` ## Summary by CodeRabbit * **Refactor** * Simplified the Accounts page layout and styling by switching from Chakra UI to Tailwind CSS. * Streamlined the AccountsTable with a new table design, fixed page size, and updated pagination controls. * Updated Create Account button with unified styling, improved labeling, and a new icon. * Removed the accounts count display from the page. * Updated external documentation link and link styling for clarity. * Removed legacy copy button component and related clipboard functionality. * **Style** * Improved visual consistency with updated button and table styles. --- .../accounts/AccountsPage.tsx | 74 +++--- .../accounts/components/accounts-count.tsx | 23 -- .../accounts/components/accounts-table.tsx | 228 ++++++++---------- .../components/create-account-button.tsx | 59 +++-- apps/dashboard/src/chakra/button.tsx | 38 +-- 5 files changed, 162 insertions(+), 260 deletions(-) delete mode 100644 apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/accounts/components/accounts-count.tsx diff --git a/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/accounts/AccountsPage.tsx b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/accounts/AccountsPage.tsx index 6ce4e1c8a40..ad30b9da57f 100644 --- a/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/accounts/AccountsPage.tsx +++ b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/accounts/AccountsPage.tsx @@ -1,51 +1,47 @@ "use client"; -import { ButtonGroup, Flex } from "@chakra-ui/react"; -import { LinkButton } from "chakra/button"; -import { Heading } from "chakra/heading"; import type { ThirdwebContract } from "thirdweb"; +import { UnderlineLink } from "@/components/ui/UnderlineLink"; import type { ProjectMeta } from "../../../../../team/[team_slug]/[project_slug]/contract/[chainIdOrSlug]/[contractAddress]/types"; -import { AccountsCount } from "./components/accounts-count"; import { AccountsTable } from "./components/accounts-table"; import { CreateAccountButton } from "./components/create-account-button"; -interface AccountsPageProps { +export function AccountsPage(props: { contract: ThirdwebContract; isLoggedIn: boolean; projectMeta: ProjectMeta | undefined; -} - -export const AccountsPage: React.FC = ({ - contract, - isLoggedIn, - projectMeta, -}) => { +}) { return ( - - - Accounts - - - View Documentation - - - - - - - +
+
+
+

+ Accounts +

+

+ View list of smart accounts that have been created for this + contract.{" "} + + Learn more about gas sponsorship + +

+
+ +
+ +
+ + +
); -}; +} diff --git a/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/accounts/components/accounts-count.tsx b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/accounts/components/accounts-count.tsx deleted file mode 100644 index ad67c38ac80..00000000000 --- a/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/accounts/components/accounts-count.tsx +++ /dev/null @@ -1,23 +0,0 @@ -"use client"; - -import type { ThirdwebContract } from "thirdweb"; -import { totalAccounts } from "thirdweb/extensions/erc4337"; -import { useReadContract } from "thirdweb/react"; -import { StatCard } from "../../overview/components/stat-card"; - -type AccountsCountProps = { - contract: ThirdwebContract; -}; - -export const AccountsCount: React.FC = ({ contract }) => { - const totalAccountsQuery = useReadContract(totalAccounts, { contract }); - return ( -
- -
- ); -}; diff --git a/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/accounts/components/accounts-table.tsx b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/accounts/components/accounts-table.tsx index 5d549279d1f..a65399d8438 100644 --- a/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/accounts/components/accounts-table.tsx +++ b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/accounts/components/accounts-table.tsx @@ -1,58 +1,38 @@ "use client"; -import { Flex, IconButton, Select, Skeleton } from "@chakra-ui/react"; -import { createColumnHelper } from "@tanstack/react-table"; -import { Legacy_CopyButton } from "chakra/button"; -import { Text } from "chakra/text"; -import { - ChevronFirstIcon, - ChevronLastIcon, - ChevronLeftIcon, - ChevronRightIcon, -} from "lucide-react"; +import { ArrowUpRightIcon } from "lucide-react"; +import Link from "next/link"; import { useMemo, useState } from "react"; import type { ThirdwebContract } from "thirdweb"; import { getAccounts, totalAccounts } from "thirdweb/extensions/erc4337"; import { useReadContract } from "thirdweb/react"; -import { TWTable } from "@/components/blocks/TWTable"; +import { PaginationButtons } from "@/components/blocks/pagination-buttons"; +import { CopyTextButton } from "@/components/ui/CopyTextButton"; +import { Skeleton } from "@/components/ui/skeleton"; +import { + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableHeader, + TableRow, +} from "@/components/ui/table"; import { useChainSlug } from "@/hooks/chains/chainSlug"; -import { useDashboardRouter } from "@/lib/DashboardRouter"; import type { ProjectMeta } from "../../../../../../team/[team_slug]/[project_slug]/contract/[chainIdOrSlug]/[contractAddress]/types"; import { buildContractPagePath } from "../../_utils/contract-page-path"; -const columnHelper = createColumnHelper<{ account: string }>(); - -const columns = [ - columnHelper.accessor("account", { - cell: (info) => ( - - {info.getValue()} - - - ), - header: "Account", - }), -]; +const pageSize = 10; type AccountsTableProps = { contract: ThirdwebContract; projectMeta: ProjectMeta | undefined; }; -export const AccountsTable: React.FC = ({ - contract, - projectMeta, -}) => { - const router = useDashboardRouter(); +export function AccountsTable({ contract, projectMeta }: AccountsTableProps) { const chainSlug = useChainSlug(contract.chain.id); const [currentPage, setCurrentPage] = useState(0); - // default page size of 25 - const [pageSize, setPageSize] = useState(25); const totalAccountsQuery = useReadContract(totalAccounts, { contract }); @@ -72,7 +52,7 @@ export const AccountsTable: React.FC = ({ ); } return currentPage * pageSize + pageSize; - }, [currentPage, pageSize, totalAccountsQuery.data]); + }, [currentPage, totalAccountsQuery.data]); const accountsQuery = useReadContract(getAccounts, { contract, @@ -82,105 +62,91 @@ export const AccountsTable: React.FC = ({ }); const totalPages = Math.ceil(totalAccountsNum / pageSize); - - const canNextPage = currentPage < totalPages - 1; - const canPreviousPage = currentPage > 0; + const showPagination = totalPages > 1; const data = accountsQuery.data || []; return ( - +
{/* TODO add a skeleton when loading*/} - ({ account }))} - isFetched={accountsQuery.isFetched} - isPending={accountsQuery.isPending} - onRowClick={(row) => { - const accountContractPagePath = buildContractPagePath({ - chainIdOrSlug: chainSlug.toString(), - contractAddress: row.account, - projectMeta, - }); - - router.push(accountContractPagePath); - }} - title="account" - /> - {/* pagination */} -
- - } - isDisabled={totalAccountsQuery.isPending} - onClick={() => setCurrentPage(0)} - /> - } - isDisabled={totalAccountsQuery.isPending || !canPreviousPage} - onClick={() => { - setCurrentPage((curr) => { - if (curr > 0) { - return curr - 1; - } - return curr; - }); - }} - /> - - Page {currentPage + 1} of{" "} - - {totalPages} - - - } - isDisabled={totalAccountsQuery.isPending || !canNextPage} - onClick={() => - setCurrentPage((curr) => { - if (curr < totalPages - 1) { - return curr + 1; - } - return curr; - }) - } - /> - } - isDisabled={totalAccountsQuery.isPending || !canNextPage} - onClick={() => setCurrentPage(totalPages - 1)} + + + + + Account + + + + {accountsQuery.isPending && + new Array(pageSize).fill(0).map((_, index) => ( + // biome-ignore lint/suspicious/noArrayIndexKey: ok + + ))} + + {!accountsQuery.isPending && + data.map((account) => { + const accountContractPagePath = buildContractPagePath({ + chainIdOrSlug: chainSlug.toString(), + contractAddress: account, + projectMeta, + }); + + return ( + + + + +
+ + + +
+
+
+ ); + })} +
+
+ + {!accountsQuery.isPending && data.length === 0 && ( +
+ No accounts +
+ )} +
+ + {showPagination && ( +
+ setCurrentPage(page - 1)} /> +
+ )} +
+ ); +} - - -
-
+function SkeletonRow() { + return ( + + + + + ); -}; -// +} diff --git a/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/accounts/components/create-account-button.tsx b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/accounts/components/create-account-button.tsx index 30868c0ddac..712233a0389 100644 --- a/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/accounts/components/create-account-button.tsx +++ b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/accounts/components/create-account-button.tsx @@ -1,7 +1,12 @@ "use client"; +import { PlusIcon } from "lucide-react"; import type { ThirdwebContract } from "thirdweb"; -import * as ERC4337Ext from "thirdweb/extensions/erc4337"; +import { + createAccount, + getAccountsOfSigner, + isAccountDeployed, +} from "thirdweb/extensions/erc4337"; import { useActiveAccount, useReadContract, @@ -11,39 +16,30 @@ import { TransactionButton } from "@/components/tx-button"; import { Button } from "@/components/ui/button"; import { ToolTipLabel } from "@/components/ui/tooltip"; -interface CreateAccountButtonProps { +export function CreateAccountButton(props: { contract: ThirdwebContract; isLoggedIn: boolean; -} - -export const CreateAccountButton: React.FC = ({ - contract, - isLoggedIn, - ...restButtonProps -}) => { +}) { const sendTxMutation = useSendAndConfirmTransaction(); const address = useActiveAccount()?.address; - const isAccountDeployedQuery = useReadContract(ERC4337Ext.isAccountDeployed, { + const isAccountDeployedQuery = useReadContract(isAccountDeployed, { adminSigner: address || "", - contract, + contract: props.contract, data: "0x", queryOptions: { enabled: !!address, }, }); - const accountsForAddressQuery = useReadContract( - ERC4337Ext.getAccountsOfSigner, - { - contract, - queryOptions: { - enabled: !!address, - }, - signer: address || "", + const accountsForAddressQuery = useReadContract(getAccountsOfSigner, { + contract: props.contract, + queryOptions: { + enabled: !!address, }, - ); + signer: address || "", + }); if (!address) { return null; @@ -52,8 +48,9 @@ export const CreateAccountButton: React.FC = ({ if (isAccountDeployedQuery.data && accountsForAddressQuery.data?.length) { return ( - ); @@ -61,23 +58,25 @@ export const CreateAccountButton: React.FC = ({ return ( { - const tx = ERC4337Ext.createAccount({ + const tx = createAccount({ admin: address, - contract, + contract: props.contract, data: "0x", }); sendTxMutation.mutate(tx); }} - transactionCount={1} - txChainID={contract.chain.id} - {...restButtonProps} + transactionCount={undefined} + txChainID={props.contract.chain.id} > + Create Account ); -}; +} diff --git a/apps/dashboard/src/chakra/button.tsx b/apps/dashboard/src/chakra/button.tsx index 242ec153afb..d7aaac8641f 100644 --- a/apps/dashboard/src/chakra/button.tsx +++ b/apps/dashboard/src/chakra/button.tsx @@ -4,8 +4,6 @@ import { Button as ChakraButton, type ButtonProps as ChakraButtonProps, forwardRef, - IconButton, - type IconButtonProps, LightMode, Link, useButtonGroup, @@ -15,9 +13,8 @@ import { letterSpacings, lineHeights, } from "chakra/theme/typography"; -import { CheckIcon, CopyIcon, ExternalLinkIcon } from "lucide-react"; +import { ExternalLinkIcon } from "lucide-react"; import { forwardRef as reactForwardRef } from "react"; -import { useClipboard } from "@/hooks/useClipboard"; import { ChakraNextLink } from "./link"; import { convertFontSizeToCSSVar } from "./utils/typography"; @@ -125,36 +122,3 @@ export const LinkButton = reactForwardRef( ); LinkButton.displayName = "LinkButton"; - -interface Legacy_CopyButtonProps extends IconButtonProps { - value: string; -} - -export const Legacy_CopyButton = forwardRef( - ({ value, ...restButtonProps }, ref) => { - const { onCopy, hasCopied } = useClipboard(value); - - const copy = (e: React.MouseEvent) => { - e.preventDefault(); - e.stopPropagation(); - onCopy(); - }; - - return ( - : - } - onClick={copy} - ref={ref} - size="sm" - variant="ghost" - {...restButtonProps} - /> - ); - }, -); - -Legacy_CopyButton.displayName = "Legacy_CopyButton";