Skip to content

Commit 1305527

Browse files
committed
[TOOL-4900] Dashboard: Add ERC20 token page links in /routes (#7493)
<!-- ## title your PR with this format: "[SDK/Dashboard/Portal] Feature/Fix: Concise title for the changes" If you did not copy the branch name from Linear, paste the issue tag here (format is TEAM-0000): ## Notes for the reviewer Anything important to call out? Be sure to also clarify these in your comments. ## How to test Unit tests, playground, etc. --> <!-- start pr-codex --> --- ## PR-Codex overview This PR focuses on refactoring components in the `routes-table`, `routelist-card`, and `routelist-row` files to improve structure and readability, while introducing new components like `TokenName` and `TokenInfo` for better token representation. ### Detailed summary - Replaced `TableContainer` with a simpler structure in `routes-table.tsx`. - Changed table header structure to improve readability. - Refactored `routelist-card.tsx` to use `TokenName` for token display. - Introduced `TokenInfo` component in `routelist-row.tsx` for consistent token representation. - Removed inline token display logic in favor of new components. > ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}` <!-- end pr-codex --> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added clickable links to ERC20 token pages for improved navigation and clarity in token displays. * Introduced new helper components for consistent token information display. * **Refactor** * Updated token display layouts to use flexbox for better alignment and readability. * Consolidated repeated token rendering logic into reusable components. * **Style** * Simplified and removed custom styling from tables and cards for a cleaner appearance. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
1 parent b882ad7 commit 1305527

File tree

3 files changed

+132
-102
lines changed

3 files changed

+132
-102
lines changed

apps/dashboard/src/app/(app)/(dashboard)/(bridge)/routes/components/server/routelist-card.tsx

Lines changed: 57 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
import { defineChain } from "thirdweb";
2-
import { getChainMetadata } from "thirdweb/chains";
1+
import { ExternalLinkIcon } from "lucide-react";
2+
import Link from "next/link";
3+
import { defineChain, getAddress, NATIVE_TOKEN_ADDRESS } from "thirdweb";
4+
import { type ChainMetadata, getChainMetadata } from "thirdweb/chains";
35
import { Card, CardContent, CardHeader } from "@/components/ui/card";
46
import { serverThirdwebClient } from "@/constants/thirdweb-client.server";
57
import { resolveSchemeWithErrorHandler } from "@/utils/resolveSchemeWithErrorHandler";
@@ -53,7 +55,7 @@ export async function RouteListCard({
5355

5456
return (
5557
<div className="relative h-full">
56-
<Card className="h-full w-full transition-colors hover:border-active-border">
58+
<Card className="h-full w-full">
5759
<CardHeader className="flex flex-row items-center justify-between p-4">
5860
<div className="flex flex-row items-center gap-2">
5961
{resolvedOriginTokenIconUri ? (
@@ -80,32 +82,60 @@ export async function RouteListCard({
8082
</CardHeader>
8183

8284
<CardContent className="px-4 pt-0 pb-4">
83-
<table className="w-full">
84-
<tbody className="text-sm [&_td>*]:min-h-[25px]">
85-
<tr>
86-
<th className="text-left font-normal text-base">
87-
{originTokenName === "ETH"
88-
? originChain.nativeCurrency.name
89-
: originTokenName}
90-
</th>
91-
<td className="text-right text-base text-muted-foreground">
92-
{originChain.name}
93-
</td>
94-
</tr>
95-
<tr>
96-
<th className="text-left font-normal text-base">
97-
{destinationTokenName === "ETH"
98-
? destinationChain.nativeCurrency.name
99-
: destinationTokenName}
100-
</th>
101-
<td className="text-right text-base text-muted-foreground">
102-
{destinationChain.name}
103-
</td>
104-
</tr>
105-
</tbody>
106-
</table>
85+
<div className="flex flex-col gap-1">
86+
<div className="flex justify-between gap-2">
87+
<TokenName
88+
chainMetadata={originChain}
89+
tokenAddress={originTokenAddress}
90+
tokenName={originTokenName}
91+
/>
92+
<div className="text-right text-base text-muted-foreground">
93+
{originChain.name}
94+
</div>
95+
</div>
96+
97+
<div className="flex justify-between gap-2">
98+
<TokenName
99+
chainMetadata={destinationChain}
100+
tokenAddress={destinationTokenAddress}
101+
tokenName={destinationTokenName}
102+
/>
103+
<div className="text-right text-base text-muted-foreground">
104+
{destinationChain.name}
105+
</div>
106+
</div>
107+
</div>
107108
</CardContent>
108109
</Card>
109110
</div>
110111
);
111112
}
113+
114+
const nativeTokenAddress = getAddress(NATIVE_TOKEN_ADDRESS);
115+
116+
function TokenName(props: {
117+
tokenAddress: string;
118+
tokenName: string;
119+
chainMetadata: ChainMetadata;
120+
}) {
121+
const isERC20 = getAddress(props.tokenAddress) !== nativeTokenAddress;
122+
123+
if (isERC20) {
124+
return (
125+
<Link
126+
className="flex items-center gap-1.5 font-normal text-base group/link hover:underline underline-offset-4 decoration-muted-foreground/50 decoration-dotted"
127+
href={`https://thirdweb.com/${props.chainMetadata.slug}/${props.tokenAddress}`}
128+
target="_blank"
129+
>
130+
{props.tokenName}
131+
<ExternalLinkIcon className="size-3.5 text-muted-foreground group-hover/link:text-foreground" />
132+
</Link>
133+
);
134+
}
135+
136+
return (
137+
<div className="text-left font-normal text-base">
138+
{props.chainMetadata.nativeCurrency.name}
139+
</div>
140+
);
141+
}
Lines changed: 68 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
1-
import { defineChain, getChainMetadata } from "thirdweb/chains";
2-
import { CopyTextButton } from "@/components/ui/CopyTextButton";
1+
import { ExternalLinkIcon } from "lucide-react";
2+
import Link from "next/link";
3+
import { getAddress, NATIVE_TOKEN_ADDRESS } from "thirdweb";
4+
import {
5+
type ChainMetadata,
6+
defineChain,
7+
getChainMetadata,
8+
} from "thirdweb/chains";
9+
import { shortenAddress } from "thirdweb/utils";
10+
import { Img } from "@/components/blocks/Img";
11+
import { Button } from "@/components/ui/button";
312
import { TableCell, TableRow } from "@/components/ui/table";
413
import { serverThirdwebClient } from "@/constants/thirdweb-client.server";
514
import { resolveSchemeWithErrorHandler } from "@/utils/resolveSchemeWithErrorHandler";
@@ -52,72 +61,27 @@ export async function RouteListRow({
5261
]);
5362

5463
return (
55-
<TableRow className="group transition-colors hover:bg-accent/50" linkBox>
64+
<TableRow className="group">
5665
<TableCell>
57-
<div className="flex flex-row items-center gap-3">
58-
<div className="flex items-center gap-2">
59-
{resolvedOriginTokenIconUri ? (
60-
// For now we're using a normal img tag because the domain for these images is unknown
61-
// eslint-disable-next-line @next/next/no-img-element
62-
<img
63-
alt={originTokenAddress}
64-
className="size-7 rounded-full border border-border/50 shadow-sm transition-transform group-hover:scale-105"
65-
src={resolvedOriginTokenIconUri}
66-
/>
67-
) : (
68-
<div className="size-7 rounded-full bg-muted-foreground/20" />
69-
)}
70-
{originTokenSymbol && (
71-
<CopyTextButton
72-
className="relative z-10 font-medium text-base"
73-
copyIconPosition="right"
74-
textToCopy={originTokenAddress}
75-
textToShow={
76-
originTokenSymbol === "ETH"
77-
? originChain.nativeCurrency.symbol
78-
: originTokenSymbol
79-
}
80-
tooltip="Copy Token Address"
81-
variant="ghost"
82-
/>
83-
)}
84-
</div>
85-
</div>
66+
<TokenInfo
67+
chainMetadata={originChain}
68+
tokenAddress={originTokenAddress}
69+
tokenIconUri={resolvedOriginTokenIconUri}
70+
tokenSymbol={originTokenSymbol}
71+
/>
8672
</TableCell>
8773

8874
<TableCell className="text-muted-foreground/90">
8975
<span className="font-medium">{originChain.name}</span>
9076
</TableCell>
9177

9278
<TableCell>
93-
<div className="flex flex-row items-center gap-3">
94-
<div className="flex items-center gap-2">
95-
{resolvedDestinationTokenIconUri ? (
96-
// eslint-disable-next-line @next/next/no-img-element
97-
<img
98-
alt={destinationTokenAddress}
99-
className="size-7 rounded-full border border-border/50 shadow-sm transition-transform group-hover:scale-105"
100-
src={resolvedDestinationTokenIconUri}
101-
/>
102-
) : (
103-
<div className="size-7 rounded-full bg-muted-foreground/20" />
104-
)}
105-
{destinationTokenSymbol && (
106-
<CopyTextButton
107-
className="relative z-10 font-medium text-base"
108-
copyIconPosition="right"
109-
textToCopy={destinationTokenAddress}
110-
textToShow={
111-
destinationTokenSymbol === "ETH"
112-
? destinationChain.nativeCurrency.symbol
113-
: destinationTokenSymbol
114-
}
115-
tooltip="Copy Token Address"
116-
variant="ghost"
117-
/>
118-
)}
119-
</div>
120-
</div>
79+
<TokenInfo
80+
chainMetadata={destinationChain}
81+
tokenAddress={destinationTokenAddress}
82+
tokenIconUri={resolvedDestinationTokenIconUri}
83+
tokenSymbol={destinationTokenSymbol}
84+
/>
12185
</TableCell>
12286

12387
<TableCell className="text-muted-foreground/90">
@@ -126,3 +90,47 @@ export async function RouteListRow({
12690
</TableRow>
12791
);
12892
}
93+
94+
const nativeTokenAddress = getAddress(NATIVE_TOKEN_ADDRESS);
95+
96+
function TokenInfo(props: {
97+
tokenAddress: string;
98+
tokenSymbol: string | undefined;
99+
chainMetadata: ChainMetadata;
100+
tokenIconUri: string | undefined;
101+
}) {
102+
const isERC20 = getAddress(props.tokenAddress) !== nativeTokenAddress;
103+
104+
return (
105+
<div className="flex items-center gap-1.5">
106+
{props.tokenIconUri ? (
107+
<Img
108+
alt={props.tokenAddress}
109+
className="size-7 rounded-full border border-border/50"
110+
src={props.tokenIconUri}
111+
/>
112+
) : (
113+
<div className="size-7 rounded-full bg-muted-foreground/20" />
114+
)}
115+
{isERC20 ? (
116+
<Button
117+
asChild
118+
className="h-auto w-auto py-1 px-1.5 gap-2"
119+
variant="ghost"
120+
>
121+
<Link
122+
href={`https://thirdweb.com/${props.chainMetadata.slug}/${props.tokenAddress}`}
123+
target="_blank"
124+
>
125+
{props.tokenSymbol || shortenAddress(props.tokenAddress)}
126+
<ExternalLinkIcon className="size-3.5 text-muted-foreground" />
127+
</Link>
128+
</Button>
129+
) : (
130+
<span className="font-medium text-sm px-1.5 py-1">
131+
{props.chainMetadata.nativeCurrency.symbol}
132+
</span>
133+
)}
134+
</div>
135+
);
136+
}

apps/dashboard/src/app/(app)/(dashboard)/(bridge)/routes/components/server/routes-table.tsx

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -86,22 +86,14 @@ export async function RoutesData(props: {
8686
<p className="text-2xl">No Results found</p>
8787
</div>
8888
) : props.activeView === "table" ? (
89-
<TableContainer className="overflow-hidden rounded-xl border border-border/50 bg-card/50 shadow-sm transition-all">
89+
<TableContainer>
9090
<Table>
91-
<TableHeader className="z-0">
92-
<TableRow className="border-border/50 border-b bg-muted/50">
93-
<TableHead className="py-4 font-medium text-muted-foreground/80 text-xs uppercase tracking-wider">
94-
Origin Token
95-
</TableHead>
96-
<TableHead className="py-4 font-medium text-muted-foreground/80 text-xs uppercase tracking-wider">
97-
Origin Chain
98-
</TableHead>
99-
<TableHead className="py-4 font-medium text-muted-foreground/80 text-xs uppercase tracking-wider">
100-
Destination Token
101-
</TableHead>
102-
<TableHead className="py-4 font-medium text-muted-foreground/80 text-xs uppercase tracking-wider">
103-
Destination Chain
104-
</TableHead>
91+
<TableHeader>
92+
<TableRow>
93+
<TableHead>Origin Token</TableHead>
94+
<TableHead>Origin Chain</TableHead>
95+
<TableHead>Destination Token</TableHead>
96+
<TableHead>Destination Chain</TableHead>
10597
</TableRow>
10698
</TableHeader>
10799
<TableBody>

0 commit comments

Comments
 (0)