Skip to content

Commit c7f2b51

Browse files
committed
Dashboard: Show Purchase data in bridge tx page, add tx status card in project>bridge page
1 parent 43b0680 commit c7f2b51

File tree

7 files changed

+184
-121
lines changed

7 files changed

+184
-121
lines changed

apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/tx/[txHash]/bridge-status.stories.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,3 +104,17 @@ export const NotFound: Story = {
104104
client: storybookThirdwebClient,
105105
},
106106
};
107+
108+
export const WithPurchaseData: Story = {
109+
args: {
110+
bridgeStatus: {
111+
...completedStatus,
112+
purchaseData: {
113+
userId: "68d645b7ded999651272bf1e",
114+
credits: 32000,
115+
transactionId: "fd2606d1-90df-45c6-bd2c-b19a34764a31",
116+
},
117+
},
118+
client: storybookThirdwebClient,
119+
},
120+
};

apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/tx/[txHash]/bridge-status.tsx

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
"use client";
22
import { useQuery } from "@tanstack/react-query";
3+
import { CodeClient } from "@workspace/ui/components/code/code.client";
34
import { Img } from "@workspace/ui/components/img";
45
import { Spinner } from "@workspace/ui/components/spinner";
56
import { ArrowRightIcon, CircleCheckIcon, CircleXIcon } from "lucide-react";
67
import Link from "next/link";
7-
import type { ThirdwebClient } from "thirdweb";
8+
import { NATIVE_TOKEN_ADDRESS, type ThirdwebClient } from "thirdweb";
89
import type { Status, Token } from "thirdweb/bridge";
910
import { status } from "thirdweb/bridge";
1011
import { toTokens } from "thirdweb/utils";
@@ -15,11 +16,17 @@ import { cn } from "@/lib/utils";
1516
import { fetchChain } from "@/utils/fetchChain";
1617
import { resolveSchemeWithErrorHandler } from "@/utils/resolveSchemeWithErrorHandler";
1718

19+
type PurchaseData = Exclude<Status, { status: "NOT_FOUND" }>["purchaseData"];
20+
1821
export function BridgeStatus(props: {
1922
bridgeStatus: Status;
2023
client: ThirdwebClient;
2124
}) {
2225
const { bridgeStatus } = props;
26+
const purchaseDataString =
27+
bridgeStatus.status !== "NOT_FOUND" && bridgeStatus.purchaseData
28+
? getPurchaseData(bridgeStatus.purchaseData)
29+
: undefined;
2330

2431
return (
2532
<div className="bg-card rounded-xl border relative">
@@ -39,7 +46,7 @@ export function BridgeStatus(props: {
3946
/>
4047
)}
4148

42-
<div className="px-6 lg:px-10 py-7 space-y-1.5">
49+
<div className="px-6 lg:px-8 py-7 space-y-1.5">
4350
<div className="flex justify-between items-center">
4451
<p className="text-sm text-muted-foreground "> Status </p>
4552

@@ -80,6 +87,18 @@ export function BridgeStatus(props: {
8087
/>
8188
</div>
8289
</div>
90+
91+
{purchaseDataString && (
92+
<div className="px-6 lg:px-8 py-7 space-y-2 border-t border-dashed">
93+
<p className="text-sm text-muted-foreground ">Purchase Data</p>
94+
<CodeClient
95+
code={purchaseDataString}
96+
lang="json"
97+
className="[&_code]:text-xs"
98+
scrollableClassName="p-3"
99+
/>
100+
</div>
101+
)}
83102
</div>
84103
);
85104
}
@@ -96,7 +115,7 @@ function TokenInfo(props: {
96115
const chainQuery = useChainQuery(props.token.chainId);
97116

98117
return (
99-
<div className="flex-1 pt-10 pb-9 px-6 lg:px-10">
118+
<div className="flex-1 pt-10 pb-9 px-6 lg:px-8">
100119
<div className="flex justify-between items-center">
101120
<h3 className="text-xs font-medium uppercase tracking-wider text-muted-foreground">
102121
{props.label}
@@ -107,7 +126,12 @@ function TokenInfo(props: {
107126
<div className="flex items-center gap-3">
108127
<div className="relative hover:ring-2 hover:ring-offset-2 hover:ring-offset-card hover:ring-foreground/30 rounded-full">
109128
<Link
110-
href={`/${chainQuery.data?.slug || props.token.chainId}/${props.token.address}`}
129+
href={
130+
props.token.address.toLowerCase() ===
131+
NATIVE_TOKEN_ADDRESS.toLowerCase()
132+
? `/${chainQuery.data?.slug || props.token.chainId}`
133+
: `/${chainQuery.data?.slug || props.token.chainId}/${props.token.address}`
134+
}
111135
target="_blank"
112136
aria-label="View Token"
113137
className="absolute inset-0 z-10"
@@ -263,7 +287,7 @@ function FailedBridgeStatusContent(props: {
263287
client: ThirdwebClient;
264288
}) {
265289
return (
266-
<div className="px-6 lg:px-10 py-7 space-y-1.5 border-b border-dashed">
290+
<div className="px-6 lg:px-8 py-7 space-y-1.5 border-b border-dashed">
267291
<h3 className="text-base font-medium tracking-tight mb-3">
268292
Transactions
269293
</h3>
@@ -370,3 +394,11 @@ export function BridgeStatusWithPolling(props: {
370394
/>
371395
);
372396
}
397+
398+
function getPurchaseData(purchaseData: PurchaseData) {
399+
try {
400+
return JSON.stringify(purchaseData, null, 2);
401+
} catch {
402+
return undefined;
403+
}
404+
}

apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/tx/[txHash]/page.tsx

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import {
88
Clock4Icon,
99
InfoIcon,
1010
} from "lucide-react";
11-
import { notFound } from "next/navigation";
1211
import { toTokens } from "thirdweb";
1312
import { status } from "thirdweb/bridge";
1413
import type { ChainMetadata } from "thirdweb/chains";
@@ -55,19 +54,28 @@ export default async function Page(props: {
5554
const [transaction, receipt, bridgeStatus] = await Promise.all([
5655
eth_getTransactionByHash(rpcRequest, {
5756
hash: params.txHash,
58-
}),
57+
}).catch(() => undefined),
5958
eth_getTransactionReceipt(rpcRequest, {
6059
hash: params.txHash,
61-
}),
60+
}).catch(() => undefined),
6261
status({
6362
chainId: chain.chainId,
6463
transactionHash: params.txHash,
6564
client: serverThirdwebClient,
6665
}).catch(() => undefined),
6766
]);
6867

69-
if (!transaction.blockHash) {
70-
notFound();
68+
if (!transaction?.blockHash || !receipt) {
69+
return (
70+
<div className="flex flex-col items-center justify-center grow">
71+
<div className="flex flex-col items-center justify-center gap-3">
72+
<div className="p-2 rounded-full bg-background border">
73+
<CircleAlertIcon className="size-5 text-muted-foreground" />
74+
</div>
75+
Transaction not found
76+
</div>
77+
</div>
78+
);
7179
}
7280

7381
const block = await eth_getBlockByHash(rpcRequest, {

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/bridge/RouteDiscovery.tsx

Lines changed: 36 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
import { zodResolver } from "@hookform/resolvers/zod";
33
import { useMutation } from "@tanstack/react-query";
44
import type { ProjectResponse } from "@thirdweb-dev/service-utils";
5+
import { Button } from "@workspace/ui/components/button";
6+
import { Spinner } from "@workspace/ui/components/spinner";
7+
import { PlusIcon } from "lucide-react";
58
import { useForm } from "react-hook-form";
69
import { toast } from "sonner";
710
import type { ThirdwebClient } from "thirdweb";
@@ -19,7 +22,6 @@ import {
1922
type RouteDiscoveryValidationSchema,
2023
routeDiscoveryValidationSchema,
2124
} from "@/schema/validations";
22-
import { RouteDiscoveryCard } from "./RouteDiscoveryCard";
2325

2426
export const RouteDiscovery = ({
2527
project,
@@ -77,33 +79,21 @@ export const RouteDiscovery = ({
7779
},
7880
);
7981

82+
const errorText = form.getFieldState("tokenAddress").error?.message;
83+
8084
return (
8185
<Form {...form}>
8286
<form autoComplete="off" onSubmit={handleSubmit}>
83-
<RouteDiscoveryCard
84-
bottomText=""
85-
errorText={form.getFieldState("tokenAddress").error?.message}
86-
noPermissionText={undefined}
87-
saveButton={
88-
// Only show the submit button in the default state
89-
{
90-
disabled: !form.formState.isDirty,
91-
isPending: submitDiscoveryMutation.isPending,
92-
type: "submit",
93-
variant: "outline",
94-
}
95-
}
96-
>
97-
<div>
98-
<h3 className="font-semibold text-xl tracking-tight">
87+
<div className="relative rounded-lg border border-border bg-card">
88+
<div className="relative border-dashed border-b px-4 py-6 lg:px-6">
89+
<h3 className="font-semibold text-xl tracking-tight mb-1">
9990
Add a token to Bridge
10091
</h3>
101-
<p className="mt-1.5 mb-4 text-muted-foreground max-w-3xl text-sm text-pretty">
92+
<p className="mb-4 text-muted-foreground max-w-3xl text-sm text-pretty">
10293
Select your chain and input the token address to automatically
10394
kick-off the token route discovery process. <br /> This may take
10495
up to 20-40 minutes to complete.
10596
</p>
106-
10797
<div className="grid grid-cols-1 gap-4 lg:grid-cols-2 max-w-3xl">
10898
<FormField
10999
control={form.control}
@@ -142,7 +132,33 @@ export const RouteDiscovery = ({
142132
/>
143133
</div>
144134
</div>
145-
</RouteDiscoveryCard>
135+
<div>
136+
<div className="flex items-center justify-between gap-2 px-4 py-4 lg:px-6">
137+
{errorText ? (
138+
<p className="text-destructive-text text-sm">{errorText}</p>
139+
) : (
140+
<div />
141+
)}
142+
143+
<Button
144+
className="gap-1.5 rounded-full"
145+
disabled={
146+
!form.formState.isDirty || submitDiscoveryMutation.isPending
147+
}
148+
size="sm"
149+
type="submit"
150+
variant={"outline"}
151+
>
152+
{submitDiscoveryMutation.isPending ? (
153+
<Spinner className="size-3" />
154+
) : (
155+
<PlusIcon className="size-4 text-muted-foreground" />
156+
)}
157+
Add Token
158+
</Button>
159+
</div>
160+
</div>
161+
</div>
146162
</form>
147163
</Form>
148164
);

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/bridge/RouteDiscoveryCard.tsx

Lines changed: 0 additions & 84 deletions
This file was deleted.

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/bridge/page.tsx

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { PayAnalytics } from "../payments/components/PayAnalytics";
1111
import { getUniversalBridgeFiltersFromSearchParams } from "../payments/components/time";
1212
import { QuickStartSection } from "./QuickstartSection.client";
1313
import { RouteDiscovery } from "./RouteDiscovery";
14+
import { ViewTxStatus } from "./view-tx-status";
1415

1516
export default async function Page(props: {
1617
params: Promise<{
@@ -84,7 +85,7 @@ export default async function Page(props: {
8485
],
8586
}}
8687
>
87-
<div className="flex flex-col gap-12">
88+
<div className="flex flex-col gap-6">
8889
<ResponsiveSearchParamsProvider value={searchParams}>
8990
<PayAnalytics
9091
client={client}
@@ -99,12 +100,16 @@ export default async function Page(props: {
99100

100101
<RouteDiscovery client={client} project={project} />
101102

102-
<QuickStartSection
103-
projectSlug={params.project_slug}
104-
teamSlug={params.team_slug}
105-
clientId={project.publishableKey}
106-
teamId={project.teamId}
107-
/>
103+
<ViewTxStatus client={client} />
104+
105+
<div className="pt-4">
106+
<QuickStartSection
107+
projectSlug={params.project_slug}
108+
teamSlug={params.team_slug}
109+
clientId={project.publishableKey}
110+
teamId={project.teamId}
111+
/>
112+
</div>
108113
</div>
109114
</ProjectPage>
110115
);

0 commit comments

Comments
 (0)