Skip to content

Commit ffbea7f

Browse files
committed
Dashboard: Add webhooks tab in Contracts page
1 parent e232527 commit ffbea7f

File tree

10 files changed

+251
-128
lines changed

10 files changed

+251
-128
lines changed

apps/dashboard/src/app/(app)/account/contracts/DeployedContractsPageHeader.tsx

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ export function DeployedContractsPageHeader(props: {
1616
const [importModalOpen, setImportModalOpen] = useState(false);
1717

1818
return (
19-
<div className="border-b">
19+
<div>
2020
<ImportModal
2121
client={props.client}
2222
isOpen={importModalOpen}
@@ -28,24 +28,28 @@ export function DeployedContractsPageHeader(props: {
2828
type="contract"
2929
/>
3030

31-
<div className="container flex max-w-7xl flex-col gap-3 py-10 lg:flex-row lg:items-center lg:justify-between">
31+
<div className="container flex max-w-7xl flex-col gap-3 pt-10 pb-5 lg:flex-row lg:items-center lg:justify-between">
3232
<div>
3333
<h1 className="font-semibold text-2xl tracking-tight lg:text-3xl">
3434
Contracts
3535
</h1>
36+
<p className="text-muted-foreground">
37+
Deploy and manage contracts for your project
38+
</p>
3639
</div>
3740
<div className="flex gap-3 [&>*]:grow">
3841
<Button
39-
className="gap-2 bg-card"
42+
className="gap-1.5 bg-card"
43+
size="sm"
4044
variant="outline"
4145
onClick={() => {
4246
setImportModalOpen(true);
4347
}}
4448
>
45-
<DownloadIcon className="size-4" />
49+
<DownloadIcon className="size-4 text-muted-foreground" />
4650
Import contract
4751
</Button>
48-
<Button asChild className="gap-2">
52+
<Button asChild className="gap-1.5" size="sm">
4953
<Link href="/explore">
5054
<PlusIcon className="size-4" />
5155
Deploy contract

apps/dashboard/src/app/(app)/account/contracts/_components/DeployedContractsPage.tsx

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import { Spinner } from "@/components/ui/Spinner/Spinner";
33
import { ContractTable } from "components/contract-components/tables/contract-table";
44
import { Suspense } from "react";
55
import type { ThirdwebClient } from "thirdweb";
6-
import { DeployedContractsPageHeader } from "../DeployedContractsPageHeader";
76
import { DeployViaCLIOrImportCard } from "./DeployViaCLIOrImportCard";
87
import { getSortedDeployedContracts } from "./getSortedDeployedContracts";
98

@@ -16,24 +15,16 @@ export function DeployedContractsPage(props: {
1615
projectSlug: string;
1716
}) {
1817
return (
19-
<div className="flex grow flex-col">
20-
<DeployedContractsPageHeader
18+
<div className="container flex max-w-7xl grow flex-col">
19+
<Suspense fallback={<Loading />}>
20+
<DeployedContractsPageAsync {...props} />
21+
</Suspense>
22+
<div className="h-8" />
23+
<DeployViaCLIOrImportCard
24+
client={props.client}
2125
teamId={props.teamId}
2226
projectId={props.projectId}
23-
client={props.client}
2427
/>
25-
<div className="h-6" />
26-
<div className="container flex max-w-7xl grow flex-col">
27-
<Suspense fallback={<Loading />}>
28-
<DeployedContractsPageAsync {...props} />
29-
</Suspense>
30-
<div className="h-8" />
31-
<DeployViaCLIOrImportCard
32-
client={props.client}
33-
teamId={props.teamId}
34-
projectId={props.projectId}
35-
/>
36-
</div>
3728
</div>
3829
);
3930
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import { getProject } from "@/api/projects";
2+
import { getTeamBySlug } from "@/api/team";
3+
import { TabPathLinks } from "@/components/ui/tabs";
4+
import { getClientThirdwebClient } from "@/constants/thirdweb-client.client";
5+
import { DeployedContractsPageHeader } from "@app/account/contracts/DeployedContractsPageHeader";
6+
import { getAuthToken } from "@app/api/lib/getAuthToken";
7+
import { loginRedirect } from "@app/login/loginRedirect";
8+
import { redirect } from "next/navigation";
9+
10+
export default async function Layout(props: {
11+
children: React.ReactNode;
12+
params: Promise<{ team_slug: string; project_slug: string }>;
13+
}) {
14+
const params = await props.params;
15+
16+
const [authToken, team, project] = await Promise.all([
17+
getAuthToken(),
18+
getTeamBySlug(params.team_slug),
19+
getProject(params.team_slug, params.project_slug),
20+
]);
21+
22+
if (!authToken) {
23+
loginRedirect(`/team/${params.team_slug}/${params.project_slug}/contracts`);
24+
}
25+
26+
if (!team) {
27+
redirect("/team");
28+
}
29+
30+
if (!project) {
31+
redirect(`/team/${params.team_slug}`);
32+
}
33+
34+
const client = getClientThirdwebClient({
35+
jwt: authToken,
36+
teamId: team.id,
37+
});
38+
39+
const layoutPath = `/team/${params.team_slug}/${params.project_slug}/contracts`;
40+
41+
return (
42+
<div className="flex grow flex-col">
43+
<DeployedContractsPageHeader
44+
teamId={team.id}
45+
projectId={project.id}
46+
client={client}
47+
/>
48+
<TabPathLinks
49+
scrollableClassName="container max-w-7xl"
50+
links={[
51+
{
52+
name: "Contracts",
53+
path: layoutPath,
54+
exactMatch: true,
55+
},
56+
{
57+
name: "Webhooks",
58+
path: `${layoutPath}/webhooks`,
59+
},
60+
]}
61+
/>
62+
<div className="h-6" />
63+
{props.children}
64+
</div>
65+
);
66+
}

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import { getProject } from "@/api/projects";
22
import { getTeamBySlug } from "@/api/team";
33
import { getClientThirdwebClient } from "@/constants/thirdweb-client.client";
4+
import { DeployedContractsPage } from "@app/account/contracts/_components/DeployedContractsPage";
5+
import { getAuthToken } from "@app/api/lib/getAuthToken";
6+
import { loginRedirect } from "@app/login/loginRedirect";
47
import { redirect } from "next/navigation";
5-
import { DeployedContractsPage } from "../../../../../account/contracts/_components/DeployedContractsPage";
6-
import { getAuthToken } from "../../../../../api/lib/getAuthToken";
7-
import { loginRedirect } from "../../../../../login/loginRedirect";
88
import { FooterLinksSection } from "../components/footer/FooterLinksSection";
99

1010
export default async function Page(props: {
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { getProject } from "@/api/projects";
2+
import { getTeamBySlug } from "@/api/team";
3+
import { getAuthToken } from "@app/api/lib/getAuthToken";
4+
import { loginRedirect } from "@app/login/loginRedirect";
5+
import { redirect } from "next/navigation";
6+
import { ContractsWebhooksPageContent } from "../../webhooks/contract-webhooks/contract-webhooks-page";
7+
8+
export default async function Page(props: {
9+
params: Promise<{ team_slug: string; project_slug: string }>;
10+
}) {
11+
const params = await props.params;
12+
13+
const [authToken, team, project] = await Promise.all([
14+
getAuthToken(),
15+
getTeamBySlug(params.team_slug),
16+
getProject(params.team_slug, params.project_slug),
17+
]);
18+
19+
if (!authToken) {
20+
loginRedirect(
21+
`/team/${params.team_slug}/${params.project_slug}/contracts/webhooks`,
22+
);
23+
}
24+
25+
if (!team) {
26+
redirect("/team");
27+
}
28+
29+
if (!project) {
30+
redirect(`/team/${params.team_slug}`);
31+
}
32+
33+
return (
34+
<div className="container flex max-w-7xl grow flex-col">
35+
<ContractsWebhooksPageContent project={project} authToken={authToken} />
36+
</div>
37+
);
38+
}

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

Lines changed: 15 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -52,26 +52,21 @@ export default async function Layout(props: {
5252
</div>
5353
</header>
5454

55-
<div className="relative">
56-
<div className="absolute right-0 bottom-0 left-0 h-[1px] bg-border" />
57-
<div className="container max-w-7xl">
58-
<TabPathLinks
59-
className="pt-2"
60-
bottomLineClassName="hidden"
61-
links={[
62-
{
63-
name: "Engine Instances",
64-
path: `${linkPrefix}`,
65-
exactMatch: true,
66-
},
67-
{
68-
name: "Import Engine",
69-
path: `${linkPrefix}/import`,
70-
},
71-
]}
72-
/>
73-
</div>
74-
</div>
55+
<TabPathLinks
56+
className="pt-2"
57+
scrollableClassName="container max-w-7xl"
58+
links={[
59+
{
60+
name: "Engine Instances",
61+
path: `${linkPrefix}`,
62+
exactMatch: true,
63+
},
64+
{
65+
name: "Import Engine",
66+
path: `${linkPrefix}/import`,
67+
},
68+
]}
69+
/>
7570

7671
<div className="container max-w-7xl pt-8 pb-10">{props.children}</div>
7772
</div>

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

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import { FilterDetailsStep } from "./FilterDetailsStep";
1919
import ReviewStep from "./ReviewStep";
2020

2121
import { createWebhook } from "@/api/insight/webhooks";
22-
import { XIcon } from "lucide-react";
22+
import { PlusIcon, XIcon } from "lucide-react";
2323
import type { ThirdwebClient } from "thirdweb";
2424
import { useAbiMultiFetch } from "../hooks/useAbiProcessing";
2525
import { useTestWebhook } from "../hooks/useTestWebhook";
@@ -45,7 +45,7 @@ interface CreateWebhookModalProps {
4545
client: ThirdwebClient;
4646
}
4747

48-
export function CreateWebhookModal({
48+
export function CreateContractWebhookButton({
4949
projectClientId,
5050
supportedChainIds,
5151
client,
@@ -224,7 +224,10 @@ export function CreateWebhookModal({
224224
return (
225225
<Dialog open={isOpen}>
226226
<DialogTrigger asChild>
227-
<Button onClick={handleOpenModal}>New Webhook</Button>
227+
<Button onClick={handleOpenModal} className="gap-1.5" size="sm">
228+
<PlusIcon className="size-4" />
229+
Create Webhook
230+
</Button>
228231
</DialogTrigger>
229232

230233
<DialogContent

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

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import { useMemo, useState } from "react";
1818
import { toast } from "sonner";
1919
import type { ThirdwebClient } from "thirdweb";
2020
import { useTestWebhook } from "../hooks/useTestWebhook";
21-
import { CreateWebhookModal } from "./CreateWebhookModal";
21+
import { CreateContractWebhookButton } from "./CreateWebhookModal";
2222
import { RelativeTime } from "./RelativeTime";
2323

2424
function getEventType(filters: WebhookFilters): string {
@@ -37,7 +37,7 @@ interface WebhooksTableProps {
3737
client: ThirdwebClient;
3838
}
3939

40-
export function WebhooksTable({
40+
export function ContractsWebhooksTable({
4141
webhooks,
4242
projectClientId,
4343
client,
@@ -216,22 +216,22 @@ export function WebhooksTable({
216216
}, [webhooks]);
217217

218218
return (
219-
<div className="w-full">
220-
<div className="mb-4 flex items-center justify-end">
221-
<CreateWebhookModal
222-
projectClientId={projectClientId}
223-
supportedChainIds={supportedChainIds}
224-
client={client}
225-
/>
226-
</div>
219+
<div className="w-full overflow-hidden rounded-lg border">
227220
<TWTable
228221
data={sortedWebhooks}
229222
columns={columns}
230223
isPending={false}
231224
isFetched={true}
232225
title="Webhooks"
233-
tableContainerClassName="mt-4"
226+
tableContainerClassName="border-none rounded-none"
234227
/>
228+
<div className="flex items-end justify-end gap-3 border-t bg-card px-6 py-4">
229+
<CreateContractWebhookButton
230+
projectClientId={projectClientId}
231+
supportedChainIds={supportedChainIds}
232+
client={client}
233+
/>
234+
</div>
235235
</div>
236236
);
237237
}

0 commit comments

Comments
 (0)