Skip to content

Commit a51192f

Browse files
committed
[TOOL-3055] Dashboard: Add Refresh Balances button for engine backend wallets (#6153)
<!-- start pr-codex --> ## PR-Codex overview This PR focuses on enhancing the `BackendWalletsTable` component by improving the balance display functionality, adding a refresh feature, and optimizing some data fetching mechanisms. ### Detailed summary - Updated `backendWalletBalance` keys in `cache-keys.ts`. - Introduced `backendWalletBalanceAll` function. - Added `isFetching` state to balance fetching logic. - Changed balance skeleton size. - Replaced `LinkButton` with `Link` for external navigation. - Added a refresh button with tooltip for balance updates. - Included `queryClient` in dependencies for memoized columns. > ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}` <!-- end pr-codex -->
1 parent 6fd36aa commit a51192f

File tree

2 files changed

+51
-21
lines changed

2 files changed

+51
-21
lines changed

apps/dashboard/src/@3rdweb-sdk/react/cache-keys.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,8 @@ export const engineKeys = {
9595
backendWallet: (address: string, chainId: number) =>
9696
["backendWallet", address, chainId] as const,
9797
backendWalletBalance: (address: string, chainId: number) =>
98-
[...engineKeys.backendWallet(address, chainId), "balance"] as const,
98+
["backendWallet", "balance", address, chainId] as const,
99+
backendWalletBalanceAll: () => ["backendWallet", "balance"] as const,
99100
corsUrls: (instance: string) =>
100101
[...engineKeys.all, instance, "corsUrls"] as const,
101102
ipAllowlist: (instance: string) =>

apps/dashboard/src/app/team/[team_slug]/(team)/~/engine/(instance)/[engineId]/overview/components/backend-wallets-table.tsx

Lines changed: 49 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import { Button } from "@/components/ui/button";
66
import { Checkbox, CheckboxWithLabel } from "@/components/ui/checkbox";
77
import { FormItem } from "@/components/ui/form";
88
import { Skeleton } from "@/components/ui/skeleton";
9+
import { ToolTipLabel } from "@/components/ui/tooltip";
10+
import { engineKeys } from "@3rdweb-sdk/react";
911
import {
1012
type BackendWallet,
1113
useEngineBackendWalletBalance,
@@ -30,7 +32,7 @@ import {
3032
type UseDisclosureReturn,
3133
useDisclosure,
3234
} from "@chakra-ui/react";
33-
import { useQuery } from "@tanstack/react-query";
35+
import { useQuery, useQueryClient } from "@tanstack/react-query";
3436
import { type ColumnDef, createColumnHelper } from "@tanstack/react-table";
3537
import { ChainIcon } from "components/icons/ChainIcon";
3638
import { TWTable } from "components/shared/TWTable";
@@ -40,18 +42,21 @@ import { EngineBackendWalletOptions } from "lib/engine";
4042
import { useV5DashboardChain } from "lib/v5-adapter";
4143
import {
4244
DownloadIcon,
45+
ExternalLinkIcon,
4346
PencilIcon,
47+
RefreshCcwIcon,
4448
TrashIcon,
4549
TriangleAlertIcon,
4650
UploadIcon,
4751
} from "lucide-react";
52+
import Link from "next/link";
4853
import QRCode from "qrcode";
4954
import { useMemo, useState } from "react";
5055
import { useForm } from "react-hook-form";
5156
import { toast } from "sonner";
5257
import { getAddress } from "thirdweb";
5358
import { shortenAddress } from "thirdweb/utils";
54-
import { FormHelperText, FormLabel, LinkButton, Text } from "tw-components";
59+
import { FormHelperText, FormLabel, Text } from "tw-components";
5560
import { prettyPrintCurrency } from "./utils";
5661

5762
interface BackendWalletsTableProps {
@@ -83,41 +88,40 @@ const BackendWalletBalanceCell: React.FC<BackendWalletBalanceCellProps> = ({
8388
authToken,
8489
chainId,
8590
}) => {
86-
const { data: backendWalletBalance } = useEngineBackendWalletBalance({
87-
instanceUrl: instanceUrl,
88-
address,
89-
authToken,
90-
chainId,
91-
});
91+
const { data: backendWalletBalance, isFetching } =
92+
useEngineBackendWalletBalance({
93+
instanceUrl: instanceUrl,
94+
address,
95+
authToken,
96+
chainId,
97+
});
9298
const chain = useV5DashboardChain(chainId);
9399

94-
if (!backendWalletBalance) {
95-
return <Skeleton className="h-5 w-32 rounded-lg" />;
100+
if (!backendWalletBalance || isFetching) {
101+
return <Skeleton className="h-6 w-36 rounded-lg" />;
96102
}
97103

98104
const balanceDisplay = prettyPrintCurrency({
99105
amount: backendWalletBalance.displayValue,
100106
symbol: backendWalletBalance.symbol || chain.nativeCurrency?.symbol,
101107
});
102108

103-
const balanceComponent = (
104-
<div className="text-muted-foreground">{balanceDisplay}</div>
105-
);
109+
const balanceComponent = <span>{balanceDisplay}</span>;
106110

107111
const explorer = chain.blockExplorers?.[0];
108112
if (!explorer) {
109113
return balanceComponent;
110114
}
111115

112116
return (
113-
<LinkButton
114-
variant="ghost"
115-
isExternal
116-
size="xs"
117+
<Link
118+
target="_blank"
117119
href={`${explorer.url}/address/${address}`}
120+
className="inline-flex items-center gap-2.5 rounded-lg px-2 py-1 text-muted-foreground hover:bg-muted hover:text-foreground"
118121
>
119122
{balanceComponent}
120-
</LinkButton>
123+
<ExternalLinkIcon className="size-4" />
124+
</Link>
121125
);
122126
};
123127

@@ -133,6 +137,7 @@ export const BackendWalletsTable: React.FC<BackendWalletsTableProps> = ({
133137
const receiveDisclosure = useDisclosure();
134138
const sendDisclosure = useDisclosure();
135139
const deleteDisclosure = useDisclosure();
140+
const queryClient = useQueryClient();
136141

137142
const columns = useMemo(() => {
138143
return [
@@ -160,7 +165,31 @@ export const BackendWalletsTable: React.FC<BackendWalletsTableProps> = ({
160165
},
161166
}),
162167
columnHelper.accessor("address", {
163-
header: "Balance",
168+
header: () => (
169+
<div className="flex w-[180px] items-center gap-1.5">
170+
Balance
171+
<ToolTipLabel
172+
label="Refresh Balance"
173+
contentClassName="capitalize font-normal tracking-normal leading-normal"
174+
>
175+
<Button
176+
className="z-20 h-auto p-1.5 [&[data-pending='true']_svg]:animate-spin"
177+
variant="ghost"
178+
size="sm"
179+
onClick={async (e) => {
180+
const buttonEl = e.currentTarget;
181+
buttonEl.setAttribute("data-pending", "true");
182+
await queryClient.invalidateQueries({
183+
queryKey: engineKeys.backendWalletBalanceAll(),
184+
});
185+
buttonEl.setAttribute("data-pending", "false");
186+
}}
187+
>
188+
<RefreshCcwIcon className="size-4" />
189+
</Button>
190+
</ToolTipLabel>
191+
</div>
192+
),
164193
cell: (cell) => {
165194
const address = cell.getValue();
166195
return (
@@ -175,7 +204,7 @@ export const BackendWalletsTable: React.FC<BackendWalletsTableProps> = ({
175204
id: "balance",
176205
}),
177206
];
178-
}, [instanceUrl, authToken, chainId]);
207+
}, [instanceUrl, authToken, chainId, queryClient]);
179208

180209
const [selectedBackendWallet, setSelectedBackendWallet] =
181210
useState<BackendWallet>();

0 commit comments

Comments
 (0)