Skip to content

Commit d254505

Browse files
committed
Add Pay page in teams layout (#4387)
## Problem solved Short description of the bug fixed or feature added <!-- start pr-codex --> --- ## PR-Codex overview The focus of this PR is to enhance the `PayConfig` component and update related components to use `clientId` instead of `apiKey.key`. ### Detailed summary - Updated `PayConfig` to use `clientId` instead of `apiKey.key` - Updated `PayAnalytics` to accept `clientId` instead of `apiKey` - Updated `WebhooksPage` to use `clientId` instead of `apiKey.key` > ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}` <!-- end pr-codex -->
1 parent b663959 commit d254505

File tree

8 files changed

+146
-24
lines changed

8 files changed

+146
-24
lines changed

apps/dashboard/src/@/components/ui/skeleton.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,12 @@ function SkeletonContainer<T>(props: {
3636
isLoading ? "opacity-0" : "opacity-100",
3737
)}
3838
>
39-
{props.render(props.loadedData ?? props.skeletonData, !isLoading)}
39+
{props.render(
40+
props.loadedData === undefined
41+
? props.skeletonData
42+
: props.loadedData,
43+
!isLoading,
44+
)}
4045
</div>
4146
</div>
4247
</div>

apps/dashboard/src/app/(dashboard)/dashboard/connect/pay/components/pay-ui.client.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,9 @@ export function PayUI(props: {
7373
</div>
7474

7575
{/* TODO: split this into sub-pages */}
76-
{activeTab === "analytics" && <PayAnalytics apiKey={selectedKey} />}
76+
{activeTab === "analytics" && <PayAnalytics clientId={selectedKey.key} />}
7777
{activeTab === "settings" && <PayConfig apiKey={selectedKey} />}
78-
{activeTab === "webhooks" && <WebhooksPage apiKey={selectedKey} />}
78+
{activeTab === "webhooks" && <WebhooksPage clientId={selectedKey.key} />}
7979
</>
8080
);
8181
}

apps/dashboard/src/app/(dashboard)/dashboard/connect/pay/components/webhooks.client.tsx

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ import {
3535
TableHeader,
3636
TableRow,
3737
} from "@/components/ui/table";
38-
import type { ApiKey } from "@3rdweb-sdk/react/hooks/useApi";
3938
import { zodResolver } from "@hookform/resolvers/zod";
4039
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
4140
import { formatDistanceToNow } from "date-fns";
@@ -56,15 +55,15 @@ type Webhook = {
5655
};
5756

5857
export type WebhooksPageProps = {
59-
apiKey: ApiKey;
58+
clientId: string;
6059
};
6160

6261
export function WebhooksPage(props: WebhooksPageProps) {
6362
const webhooksQuery = useQuery({
64-
queryKey: ["webhooks", props.apiKey.key],
63+
queryKey: ["webhooks", props.clientId],
6564
queryFn: async () => {
6665
const res = await fetch(
67-
`/api/server-proxy/pay/webhooks/get-all?clientId=${props.apiKey.key}`,
66+
`/api/server-proxy/pay/webhooks/get-all?clientId=${props.clientId}`,
6867
{
6968
headers: {
7069
"Content-Type": "application/json",
@@ -84,7 +83,7 @@ export function WebhooksPage(props: WebhooksPageProps) {
8483
return (
8584
<div className="border border-border rounded-lg p-8 text-center flex flex-col items-center gap-8">
8685
<h2 className="text-xl font-semibold">No webhooks configured yet.</h2>
87-
<CreateWebhookButton apiKey={props.apiKey}>
86+
<CreateWebhookButton clientId={props.clientId}>
8887
<Button variant="primary" className="gap-1">
8988
<PlusIcon className="size-4" />
9089
<span>Create Webhook</span>
@@ -104,7 +103,7 @@ export function WebhooksPage(props: WebhooksPageProps) {
104103
<TableHead>Secret</TableHead>
105104
<TableHead>Created</TableHead>
106105
<TableHead className="text-right">
107-
<CreateWebhookButton apiKey={props.apiKey}>
106+
<CreateWebhookButton clientId={props.clientId}>
108107
<Button size="sm" variant="primary" className="gap-1">
109108
<PlusIcon className="size-4" />
110109
<span>Create New Webhook</span>
@@ -133,7 +132,7 @@ export function WebhooksPage(props: WebhooksPageProps) {
133132
</TableCell>
134133
<TableCell className="text-right">
135134
<DeleteWebhookButton
136-
apiKey={props.apiKey}
135+
clientId={props.clientId}
137136
webhookId={webhook.id}
138137
>
139138
<Button variant="ghost" size="icon">
@@ -171,13 +170,13 @@ function CreateWebhookButton(props: PropsWithChildren<WebhooksPageProps>) {
171170
headers: {
172171
"Content-Type": "application/json",
173172
},
174-
body: JSON.stringify({ ...values, clientId: props.apiKey.key }),
173+
body: JSON.stringify({ ...values, clientId: props.clientId }),
175174
});
176175
const json = await res.json();
177176
return json.result as string;
178177
},
179178
onSuccess: () => {
180-
return queryClient.invalidateQueries(["webhooks", props.apiKey.key]);
179+
return queryClient.invalidateQueries(["webhooks", props.clientId]);
181180
},
182181
});
183182
return (
@@ -287,13 +286,13 @@ function DeleteWebhookButton(
287286
headers: {
288287
"Content-Type": "application/json",
289288
},
290-
body: JSON.stringify({ id, clientId: props.apiKey.key }),
289+
body: JSON.stringify({ id, clientId: props.clientId }),
291290
});
292291
const json = await res.json();
293292
return json.result as string;
294293
},
295294
onSuccess: () => {
296-
return queryClient.invalidateQueries(["webhooks", props.apiKey.key]);
295+
return queryClient.invalidateQueries(["webhooks", props.clientId]);
297296
},
298297
});
299298
return (
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
"use client";
2+
3+
import {} from "@/components/ui/select";
4+
import { TabButtons } from "@/components/ui/tabs";
5+
import type { ApiKey } from "@3rdweb-sdk/react/hooks/useApi";
6+
import { PayAnalytics } from "components/pay/PayAnalytics/PayAnalytics";
7+
import { PayConfig } from "components/pay/PayConfig";
8+
import { useState } from "react";
9+
import { WebhooksPage } from "../../../../../(dashboard)/dashboard/connect/pay/components/webhooks.client";
10+
11+
export function PayPageUI(props: {
12+
apiKey: Pick<
13+
ApiKey,
14+
| "key"
15+
| "services"
16+
| "id"
17+
| "name"
18+
| "domains"
19+
| "bundleIds"
20+
| "services"
21+
| "redirectUrls"
22+
>;
23+
}) {
24+
const [activeTab, setActiveTab] = useState<
25+
"settings" | "analytics" | "webhooks"
26+
>("analytics");
27+
28+
return (
29+
<>
30+
<div className="flex-col gap-4 flex lg:flex-row w-full relative">
31+
<TabButtons
32+
containerClassName="w-full"
33+
tabs={[
34+
{
35+
name: "Analytics",
36+
isActive: activeTab === "analytics",
37+
onClick: () => setActiveTab("analytics"),
38+
isEnabled: true,
39+
},
40+
{
41+
name: "Webhooks",
42+
isActive: activeTab === "webhooks",
43+
onClick: () => setActiveTab("webhooks"),
44+
isEnabled: true,
45+
},
46+
{
47+
name: "Settings",
48+
isActive: activeTab === "settings",
49+
onClick: () => setActiveTab("settings"),
50+
isEnabled: true,
51+
},
52+
]}
53+
/>
54+
</div>
55+
56+
{/* TODO: split this into sub-pages */}
57+
{activeTab === "analytics" && (
58+
<PayAnalytics clientId={props.apiKey.key} />
59+
)}
60+
{activeTab === "settings" && <PayConfig apiKey={props.apiKey} />}
61+
{activeTab === "webhooks" && <WebhooksPage clientId={props.apiKey.key} />}
62+
</>
63+
);
64+
}
Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,53 @@
1-
export default function Page() {
1+
import Link from "next/link";
2+
import { notFound } from "next/navigation";
3+
import { getProject } from "../../../../../../@/api/projects";
4+
import { PayPageUI } from "./PayPageUI";
5+
6+
export default async function Page(props: {
7+
params: {
8+
team_slug: string;
9+
project_slug: string;
10+
};
11+
}) {
12+
const project = await getProject(
13+
props.params.team_slug,
14+
props.params.project_slug,
15+
);
16+
17+
if (!project) {
18+
return notFound();
19+
}
20+
221
return (
3-
<div className="h-full py-6 container flex items-center justify-center">
4-
<h1 className="text-4xl tracking-tighter text-muted-foreground">Pay</h1>
22+
<div className="max-sm:pt-6 pb-10">
23+
<div className="flex flex-col gap-8 w-full">
24+
<div className="flex flex-col lg:flex-row gap-6 justify-between items-start">
25+
<div className="max-w-[700px]">
26+
<h1 className="text-3xl md:text-4xl tracking-tight font-bold mb-3">
27+
Pay
28+
</h1>
29+
<p className="text-sm md:text-base text-muted-foreground leading-relaxed">
30+
Pay allows your users to purchase cryptocurrencies and execute
31+
transactions with their credit card or debit card, or with any
32+
token via cross-chain routing.{" "}
33+
<Link
34+
target="_blank"
35+
href="https://portal.thirdweb.com/connect/pay/overview"
36+
className="!text-link-foreground"
37+
>
38+
Learn more
39+
</Link>
40+
</p>
41+
</div>
42+
</div>
43+
44+
<PayPageUI
45+
apiKey={{
46+
...project,
47+
key: project.publishableKey, // clientId
48+
}}
49+
/>
50+
</div>
551
</div>
652
);
753
}

apps/dashboard/src/components/pay/PayAnalytics/PayAnalytics.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import {
66
SelectTrigger,
77
SelectValue,
88
} from "@/components/ui/select";
9-
import type { ApiKey } from "@3rdweb-sdk/react/hooks/useApi";
109
import { format } from "date-fns";
1110
import { useState } from "react";
1211
import { PayCustomersTable } from "./components/PayCustomersTable";
@@ -49,8 +48,8 @@ type Range = {
4948
to: Date;
5049
};
5150

52-
export function PayAnalytics(props: { apiKey: ApiKey }) {
53-
const clientId = props.apiKey.key;
51+
export function PayAnalytics(props: { clientId: string }) {
52+
const clientId = props.clientId;
5453
const [range, setRange] = useState<Range>(() =>
5554
getLastNDaysRange("last-120"),
5655
);

apps/dashboard/src/components/pay/PayAnalytics/components/PaymentsSuccessRate.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ export function PaymentsSuccessRate(props: {
103103
<div className="w-full relative flex flex-col">
104104
<div className="flex justify-between gap-2 items-center">
105105
<CardHeading> Payments </CardHeading>
106-
{uiQuery.data && (
106+
{!uiQuery.isLoading && (
107107
<Select
108108
value={type}
109109
onValueChange={(value: PayVolumeType) => {
@@ -197,7 +197,7 @@ function InfoRow(props: {
197197
<div className="flex items-center gap-2">
198198
<div
199199
className={`size-5 rounded-lg ${
200-
!props.amount
200+
props.amount === undefined
201201
? "bg-accent"
202202
: props.type === "success"
203203
? "bg-success-text"
@@ -210,7 +210,7 @@ function InfoRow(props: {
210210
loadedData={
211211
props.isEmpty
212212
? "$-"
213-
: props.amount
213+
: props.amount !== undefined
214214
? `$${props.amount.toLocaleString()}`
215215
: undefined
216216
}

apps/dashboard/src/components/pay/PayConfig.tsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,16 @@ import { useForm } from "react-hook-form";
2626
import { toast } from "sonner";
2727

2828
interface PayConfigProps {
29-
apiKey: ApiKey;
29+
apiKey: Pick<
30+
ApiKey,
31+
| "services"
32+
| "id"
33+
| "name"
34+
| "domains"
35+
| "bundleIds"
36+
| "services"
37+
| "redirectUrls"
38+
>;
3039
}
3140

3241
const TRACKING_CATEGORY = "pay";

0 commit comments

Comments
 (0)