Skip to content

Commit 036409c

Browse files
committed
feat: payments overview page
1 parent eed0838 commit 036409c

File tree

5 files changed

+193
-32
lines changed

5 files changed

+193
-32
lines changed
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import { ArrowUpRightIcon } from "lucide-react";
2+
import { redirect } from "next/navigation";
3+
import { ResponsiveSearchParamsProvider } from "responsive-rsc";
4+
import { getAuthToken } from "@/api/auth-token";
5+
import { getProject } from "@/api/projects";
6+
import { getClientThirdwebClient } from "@/constants/thirdweb-client.client";
7+
import { loginRedirect } from "@/utils/redirects";
8+
import { PayAnalytics } from "../components/PayAnalytics";
9+
import { getUniversalBridgeFiltersFromSearchParams } from "../components/time";
10+
11+
export default async function Page(props: {
12+
params: Promise<{
13+
team_slug: string;
14+
project_slug: string;
15+
}>;
16+
searchParams: Promise<{
17+
from?: string | undefined | string[];
18+
to?: string | undefined | string[];
19+
interval?: string | undefined | string[];
20+
}>;
21+
}) {
22+
const [params, authToken] = await Promise.all([props.params, getAuthToken()]);
23+
24+
const project = await getProject(params.team_slug, params.project_slug);
25+
26+
if (!authToken) {
27+
loginRedirect(
28+
`/team/${params.team_slug}/${params.project_slug}/universal-bridge`,
29+
);
30+
}
31+
32+
if (!project) {
33+
redirect(`/team/${params.team_slug}`);
34+
}
35+
36+
const searchParams = await props.searchParams;
37+
const { range, interval } = getUniversalBridgeFiltersFromSearchParams({
38+
defaultRange: "last-30",
39+
from: searchParams.from,
40+
interval: searchParams.interval,
41+
to: searchParams.to,
42+
});
43+
44+
const client = getClientThirdwebClient({
45+
jwt: authToken,
46+
teamId: project.teamId,
47+
});
48+
49+
return (
50+
<ResponsiveSearchParamsProvider value={searchParams}>
51+
<div>
52+
<PayAnalytics
53+
client={client}
54+
interval={interval}
55+
projectClientId={project.publishableKey}
56+
projectId={project.id}
57+
range={range}
58+
teamId={project.teamId}
59+
/>
60+
61+
<div className="h-10" />
62+
<div className="relative overflow-hidden rounded-lg border-2 border-green-500/20 bg-gradient-to-br from-card/80 to-card/50 p-4 shadow-[inset_0_1px_2px_0_rgba(0,0,0,0.02)]">
63+
<div className="absolute inset-0 bg-gradient-to-br from-green-500/5 to-transparent" />
64+
<div className="relative flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between">
65+
<div className="flex flex-col gap-1">
66+
<h3 className="font-medium text-lg">Get Started with Payments</h3>
67+
<p className="text-muted-foreground text-sm">
68+
Simple, instant, and secure payments across any token and chain.
69+
</p>
70+
</div>
71+
<a
72+
className="inline-flex items-center gap-2 rounded-md bg-green-600 px-4 py-2 font-medium text-sm text-white transition-all hover:bg-green-600/90 hover:shadow-sm"
73+
href="https://portal.thirdweb.com/payments"
74+
rel="noopener noreferrer"
75+
target="_blank"
76+
>
77+
Learn More
78+
<ArrowUpRightIcon className="size-4" />
79+
</a>
80+
</div>
81+
</div>
82+
</div>
83+
</ResponsiveSearchParamsProvider>
84+
);
85+
}
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { TabButtons } from "@/components/ui/tabs";
88

99
type Tab = "embed" | "sdk" | "api";
1010

11-
export function PayEmbedFTUX(props: {
11+
export function BuyWidgetFTUX(props: {
1212
clientId: string;
1313
codeExamples: {
1414
embed: React.ReactNode;
@@ -21,7 +21,7 @@ export function PayEmbedFTUX(props: {
2121
<div className="rounded-lg border bg-card">
2222
<div className="border-b border-dashed p-4">
2323
<h2 className="font-semibold text-lg tracking-tight">
24-
Start Monetizing Your App
24+
Setup Payments to View Analytics
2525
</h2>
2626
</div>
2727

@@ -56,7 +56,7 @@ export function PayEmbedFTUX(props: {
5656
<Button asChild size="sm" variant="outline">
5757
<Link
5858
className="gap-2"
59-
href="https://portal.thirdweb.com/pay"
59+
href="https://portal.thirdweb.com/payments"
6060
rel="noopener noreferrer"
6161
target="_blank"
6262
>

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/PayAnalytics.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ import {
77
import type { Range } from "@/components/analytics/date-range-selector";
88
import { CodeServer } from "@/components/ui/code/code.server";
99
import { Skeleton } from "@/components/ui/skeleton";
10+
import { BuyWidgetFTUX } from "./BuyWidgetFTUX";
1011
import { apiCode, embedCode, sdkCode } from "./code-examples";
1112
import { PayAnalyticsFilter } from "./PayAnalyticsFilter";
1213
import { PayCustomersTable } from "./PayCustomersTable";
13-
import { PayEmbedFTUX } from "./PayEmbedFTUX";
1414
import { PaymentHistory } from "./PaymentHistory";
1515
import { PaymentsSuccessRate } from "./PaymentsSuccessRate";
1616
import { PayNewCustomers } from "./PayNewCustomers";
@@ -67,7 +67,7 @@ export async function PayAnalytics(props: {
6767

6868
if (!hasVolume && !hasWallet) {
6969
return (
70-
<PayEmbedFTUX
70+
<BuyWidgetFTUX
7171
clientId={props.projectClientId}
7272
codeExamples={
7373
{

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/layout.tsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export default async function Layout(props: {
1818
redirect(`/team/${params.team_slug}`);
1919
}
2020

21-
const payLayoutPath = `/team/${params.team_slug}/${params.project_slug}/universal-bridge`;
21+
const payLayoutPath = `/team/${params.team_slug}/${params.project_slug}/payments`;
2222

2323
return (
2424
<div className="flex grow flex-col">
@@ -32,7 +32,7 @@ export default async function Layout(props: {
3232
cryptocurrencies and execute transactions with any fiat options or
3333
tokens via cross-chain routing.{" "}
3434
<UnderlineLink
35-
href="https://portal.thirdweb.com/pay"
35+
href="https://portal.thirdweb.com/payments"
3636
rel="noopener noreferrer"
3737
target="_blank"
3838
>
@@ -45,10 +45,15 @@ export default async function Layout(props: {
4545
<TabPathLinks
4646
className="w-full"
4747
links={[
48+
{
49+
exactMatch: true,
50+
name: "Overview",
51+
path: `${payLayoutPath}`,
52+
},
4853
{
4954
exactMatch: true,
5055
name: "Analytics",
51-
path: payLayoutPath,
56+
path: `${payLayoutPath}/analytics`,
5257
},
5358
{
5459
name: "Webhooks",

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

Lines changed: 95 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,20 @@
1-
import { ArrowUpRightIcon } from "lucide-react";
1+
import {
2+
ArrowLeftRightIcon,
3+
ArrowUpRightIcon,
4+
BadgeDollarSignIcon,
5+
BellDotIcon,
6+
CoinsIcon,
7+
HammerIcon,
8+
LinkIcon,
9+
} from "lucide-react";
10+
import Link from "next/link";
211
import { redirect } from "next/navigation";
312
import { ResponsiveSearchParamsProvider } from "responsive-rsc";
413
import { getAuthToken } from "@/api/auth-token";
514
import { getProject } from "@/api/projects";
6-
import { getClientThirdwebClient } from "@/constants/thirdweb-client.client";
15+
import { Button } from "@/components/ui/button";
16+
import { Card } from "@/components/ui/card";
717
import { loginRedirect } from "@/utils/redirects";
8-
import { PayAnalytics } from "./components/PayAnalytics";
9-
import { getUniversalBridgeFiltersFromSearchParams } from "./components/time";
1018

1119
export default async function Page(props: {
1220
params: Promise<{
@@ -34,29 +42,68 @@ export default async function Page(props: {
3442
}
3543

3644
const searchParams = await props.searchParams;
37-
const { range, interval } = getUniversalBridgeFiltersFromSearchParams({
38-
defaultRange: "last-30",
39-
from: searchParams.from,
40-
interval: searchParams.interval,
41-
to: searchParams.to,
42-
});
43-
44-
const client = getClientThirdwebClient({
45-
jwt: authToken,
46-
teamId: project.teamId,
47-
});
4845

4946
return (
5047
<ResponsiveSearchParamsProvider value={searchParams}>
5148
<div>
52-
<PayAnalytics
53-
client={client}
54-
interval={interval}
55-
projectClientId={project.publishableKey}
56-
projectId={project.id}
57-
range={range}
58-
teamId={project.teamId}
59-
/>
49+
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
50+
<FeatureCard
51+
title="Earn Fees"
52+
description="Setup fees to earn any time a user swaps or bridges funds."
53+
icon={<BadgeDollarSignIcon className="size-5" />}
54+
link={{
55+
href: `/team/${params.team_slug}/${params.project_slug}/payments/settings`,
56+
label: "Configure",
57+
}}
58+
/>
59+
<FeatureCard
60+
title="Create Payment Links"
61+
description="Create shareable URLs to receive any token in seconds."
62+
icon={<LinkIcon className="size-5" />}
63+
link={{
64+
href: `/pay`,
65+
label: "Create",
66+
}}
67+
/>
68+
<FeatureCard
69+
title="Sell Your Token"
70+
description="Allow users to swap from any token to your token from your app."
71+
icon={<ArrowLeftRightIcon className="size-5" />}
72+
link={{
73+
href: `/team/${params.team_slug}/${params.project_slug}/tokens`,
74+
label: "Launch Token",
75+
}}
76+
/>
77+
<FeatureCard
78+
title="Get Notified"
79+
description="Create Webhooks to get notified on each purchase or transaction."
80+
icon={<BellDotIcon className="size-5" />}
81+
link={{
82+
href: `/team/${params.team_slug}/${params.project_slug}/payments/webhooks`,
83+
label: "Setup",
84+
}}
85+
/>
86+
<FeatureCard
87+
title="Sell Products"
88+
description="Sell physical or digital products with an easy-to-configure component."
89+
icon={<CoinsIcon className="size-5" />}
90+
link={{
91+
href: "https://portal.thirdweb.com/payments/products",
92+
label: "Get Started",
93+
target: "_blank",
94+
}}
95+
/>
96+
<FeatureCard
97+
title="Customize Your Experience"
98+
description="Fully customizable backend API to create your own branded flows."
99+
icon={<HammerIcon className="size-5" />}
100+
link={{
101+
href: "https://payments.thirdweb.com/reference",
102+
label: "Docs",
103+
target: "_blank",
104+
}}
105+
/>
106+
</div>
60107

61108
<div className="h-10" />
62109
<div className="relative overflow-hidden rounded-lg border-2 border-green-500/20 bg-gradient-to-br from-card/80 to-card/50 p-4 shadow-[inset_0_1px_2px_0_rgba(0,0,0,0.02)]">
@@ -70,7 +117,7 @@ export default async function Page(props: {
70117
</div>
71118
<a
72119
className="inline-flex items-center gap-2 rounded-md bg-green-600 px-4 py-2 font-medium text-sm text-white transition-all hover:bg-green-600/90 hover:shadow-sm"
73-
href="https://portal.thirdweb.com/pay"
120+
href="https://portal.thirdweb.com/payments"
74121
rel="noopener noreferrer"
75122
target="_blank"
76123
>
@@ -83,3 +130,27 @@ export default async function Page(props: {
83130
</ResponsiveSearchParamsProvider>
84131
);
85132
}
133+
134+
function FeatureCard(props: {
135+
title: string;
136+
description: string;
137+
icon: React.ReactNode;
138+
link: { href: string; label: string; target?: string };
139+
}) {
140+
return (
141+
<Card className="p-4 flex flex-col items-start gap-4">
142+
<div className="text-muted-foreground rounded-full border bg-background size-12 flex items-center justify-center">
143+
{props.icon}
144+
</div>
145+
<div className="flex flex-col gap-0.5">
146+
<h3 className="font-semibold">{props.title}</h3>
147+
<p className="text-muted-foreground text-sm">{props.description}</p>
148+
</div>
149+
<Button size="sm" variant="default" className="h-8" asChild>
150+
<Link href={props.link.href} target={props.link.target}>
151+
{props.link.label}
152+
</Link>
153+
</Button>
154+
</Card>
155+
);
156+
}

0 commit comments

Comments
 (0)