Skip to content

Commit 8f93d2e

Browse files
committed
Update Nebula Waitlist Page
1 parent 3488281 commit 8f93d2e

File tree

9 files changed

+137
-212
lines changed

9 files changed

+137
-212
lines changed

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

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { getProjects } from "@/api/projects";
2-
import { getTeams } from "@/api/team";
2+
import { getTeamNebulaWaitList, getTeams } from "@/api/team";
33
import { TabPathLinks } from "@/components/ui/tabs";
44
import { notFound } from "next/navigation";
55
import { TeamHeaderLoggedIn } from "../../components/TeamHeader/team-header-logged-in.client";
@@ -22,6 +22,9 @@ export default async function TeamLayout(props: {
2222
notFound();
2323
}
2424

25+
const isOnNebulaWaitList = (await getTeamNebulaWaitList(team.slug))
26+
?.onWaitlist;
27+
2528
return (
2629
<div className="flex h-full grow flex-col">
2730
<div className="bg-muted/50">
@@ -55,10 +58,14 @@ export default async function TeamLayout(props: {
5558
path: `/team/${params.team_slug}/~/ecosystem`,
5659
name: "Ecosystems",
5760
},
58-
{
59-
path: `/team/${params.team_slug}/~/nebula`,
60-
name: "Nebula",
61-
},
61+
...(isOnNebulaWaitList
62+
? [
63+
{
64+
path: `/team/${params.team_slug}/~/nebula`,
65+
name: "Nebula",
66+
},
67+
]
68+
: []),
6269
{
6370
path: `/team/${params.team_slug}/~/usage`,
6471
name: "Usage",
Lines changed: 4 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,16 @@
1-
import { getTeamBySlug, getTeamNebulaWaitList } from "@/api/team";
2-
import { redirect } from "next/navigation";
3-
import { JoinNebulaWaitlistPage } from "../../../[project_slug]/nebula/components/nebula-waitlist-page.client";
1+
import { NebulaWaitListPage } from "../../../[project_slug]/nebula/components/nebula-waitlist-page";
42

53
export default async function Page(props: {
64
params: Promise<{
75
team_slug: string;
86
}>;
97
}) {
108
const params = await props.params;
11-
const team = await getTeamBySlug(params.team_slug);
12-
13-
if (!team) {
14-
redirect(
15-
`/login?next=${encodeURIComponent(`/team/${params.team_slug}/~/nebula`)}`,
16-
);
17-
}
18-
19-
const nebulaWaitList = await getTeamNebulaWaitList(team.slug);
20-
21-
// this should never happen
22-
if (!nebulaWaitList) {
23-
return (
24-
<div className="container flex grow flex-col py-8">
25-
<div className="flex min-h-[300px] grow flex-col items-center justify-center rounded-lg border p-6 text-destructive-text">
26-
Something went wrong trying to fetch the nebula waitlist
27-
</div>
28-
</div>
29-
);
30-
}
319

3210
return (
33-
<JoinNebulaWaitlistPage
34-
onWaitlist={nebulaWaitList.onWaitlist}
35-
teamSlug={team.slug}
11+
<NebulaWaitListPage
12+
redirectOnNoTeam={`/login?next=${encodeURIComponent(`/team/${params.team_slug}/~/nebula`)}`}
13+
teamSlug={params.team_slug}
3614
/>
3715
);
3816
}

apps/dashboard/src/app/team/[team_slug]/[project_slug]/layout.tsx

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { getProjects } from "@/api/projects";
2-
import { getTeams } from "@/api/team";
2+
import { getTeamNebulaWaitList, getTeams } from "@/api/team";
33
import { TabPathLinks } from "@/components/ui/tabs";
44
import { notFound, redirect } from "next/navigation";
55
import { TeamHeaderLoggedIn } from "../../components/TeamHeader/team-header-logged-in.client";
@@ -34,6 +34,9 @@ export default async function TeamLayout(props: {
3434
redirect(`/team/${params.team_slug}`);
3535
}
3636

37+
const isOnNebulaWaitList = (await getTeamNebulaWaitList(team.slug))
38+
?.onWaitlist;
39+
3740
return (
3841
<div className="flex grow flex-col">
3942
<div className="bg-muted/50">
@@ -58,10 +61,14 @@ export default async function TeamLayout(props: {
5861
path: `/team/${params.team_slug}/${params.project_slug}/contracts`,
5962
name: "Contracts",
6063
},
61-
{
62-
path: `/team/${params.team_slug}/${params.project_slug}/nebula`,
63-
name: "Nebula",
64-
},
64+
...(isOnNebulaWaitList
65+
? [
66+
{
67+
path: `/team/${params.team_slug}/${params.project_slug}/nebula`,
68+
name: "Nebula",
69+
},
70+
]
71+
: []),
6572
{
6673
path: `/team/${params.team_slug}/${params.project_slug}/settings`,
6774
name: "Settings",

apps/dashboard/src/app/team/[team_slug]/[project_slug]/nebula/components/nebula-waitlist-page-ui.client.tsx

Lines changed: 8 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,9 @@
1-
"use client";
2-
3-
import { Spinner } from "@/components/ui/Spinner/Spinner";
41
import { Button } from "@/components/ui/button";
5-
import { ToolTipLabel } from "@/components/ui/tooltip";
6-
import { useDashboardRouter } from "@/lib/DashboardRouter";
7-
import { useMutation } from "@tanstack/react-query";
8-
import { ArrowRightIcon, CheckIcon, OrbitIcon, ShareIcon } from "lucide-react";
2+
import { OrbitIcon } from "lucide-react";
93
import Link from "next/link";
10-
import { useState } from "react";
11-
import { toast } from "sonner";
12-
13-
export function JoinNebulaWaitlistPageUI(props: {
14-
onWaitlist: boolean;
15-
joinWaitList: () => Promise<void>;
16-
}) {
17-
const router = useDashboardRouter();
4+
import { ShareButton } from "./share-button.client";
185

6+
export function NebulaWaitListPageUI() {
197
return (
208
<div className="flex grow flex-col">
219
{/* Header */}
@@ -31,89 +19,16 @@ export function JoinNebulaWaitlistPageUI(props: {
3119
</div>
3220

3321
<div className="container flex grow flex-col overflow-hidden pt-32 pb-48">
34-
{props.onWaitlist ? (
35-
<CenteredCard
36-
key="on-waitlist"
37-
title="You're on the waitlist"
38-
description="You should receive access to Nebula soon!"
39-
footer={<ShareButton />}
40-
/>
41-
) : (
42-
<CenteredCard
43-
key="not-on-waitlist"
44-
title="Nebula"
45-
description="Blockchain-first AI that can read & write onchain in realtime."
46-
footer={
47-
<JoinWaitingListButton
48-
joinWaitList={props.joinWaitList}
49-
onSuccess={() => {
50-
router.refresh();
51-
}}
52-
/>
53-
}
54-
/>
55-
)}
22+
<CenteredCard
23+
title="You're on the waitlist"
24+
description="You should receive access to Nebula soon!"
25+
footer={<ShareButton />}
26+
/>
5627
</div>
5728
</div>
5829
);
5930
}
6031

61-
function ShareButton() {
62-
const [isCopied, setIsCopied] = useState(false);
63-
64-
return (
65-
<ToolTipLabel label="Copy Page Link">
66-
<Button
67-
variant="outline"
68-
className="gap-2"
69-
onClick={() => {
70-
navigator.clipboard.writeText("https://thirdweb.com/team/~/~/nebula");
71-
setIsCopied(true);
72-
setTimeout(() => setIsCopied(false), 1000);
73-
}}
74-
>
75-
Share
76-
{isCopied ? (
77-
<CheckIcon className="size-4 text-green-500" />
78-
) : (
79-
<ShareIcon className="size-4" />
80-
)}
81-
</Button>
82-
</ToolTipLabel>
83-
);
84-
}
85-
86-
function JoinWaitingListButton(props: {
87-
joinWaitList: () => Promise<void>;
88-
onSuccess: () => void;
89-
}) {
90-
const joinWaitListMutation = useMutation({
91-
mutationFn: props.joinWaitList,
92-
onSuccess: props.onSuccess,
93-
});
94-
95-
return (
96-
<Button
97-
className="gap-2 rounded-full"
98-
variant="primary"
99-
onClick={() => {
100-
const promise = joinWaitListMutation.mutateAsync();
101-
toast.promise(promise, {
102-
success: "Joined the waitlist!",
103-
error: "Failed to join waitlist",
104-
});
105-
}}
106-
>
107-
Join the waitlist
108-
{joinWaitListMutation.isPending ? (
109-
<Spinner className="size-4" />
110-
) : (
111-
<ArrowRightIcon className="size-4" />
112-
)}
113-
</Button>
114-
);
115-
}
116-
11732
function CenteredCard(props: {
11833
footer: React.ReactNode;
11934
title: React.ReactNode;

apps/dashboard/src/app/team/[team_slug]/[project_slug]/nebula/components/nebula-waitlist-page.client.tsx

Lines changed: 0 additions & 21 deletions
This file was deleted.
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import { joinTeamWaitlist } from "@/actions/joinWaitlist";
2+
import { getTeamBySlug, getTeamNebulaWaitList } from "@/api/team";
3+
import { Button } from "@/components/ui/button";
4+
import Link from "next/link";
5+
import { redirect } from "next/navigation";
6+
import { NebulaWaitListPageUI } from "./nebula-waitlist-page-ui.client";
7+
8+
export async function NebulaWaitListPage(props: {
9+
redirectOnNoTeam: string;
10+
teamSlug: string;
11+
}) {
12+
const team = await getTeamBySlug(props.teamSlug);
13+
14+
if (!team) {
15+
redirect(props.redirectOnNoTeam);
16+
}
17+
18+
const nebulaWaitList = await getTeamNebulaWaitList(team.slug);
19+
20+
// this should never happen
21+
if (!nebulaWaitList) {
22+
return (
23+
<UnexpectedErrorPage message="Failed to get Nebula waitlist status for your team" />
24+
);
25+
}
26+
27+
// if not already on the waitlist, join the waitlist
28+
if (!nebulaWaitList.onWaitlist) {
29+
const joined = await joinTeamWaitlist({
30+
scope: "nebula",
31+
teamSlug: team.slug,
32+
}).catch(() => null);
33+
34+
// this should never happen
35+
if (!joined) {
36+
return (
37+
<UnexpectedErrorPage message="Failed to join Nebula waitlist status for your team" />
38+
);
39+
}
40+
}
41+
42+
return <NebulaWaitListPageUI />;
43+
}
44+
45+
function UnexpectedErrorPage(props: {
46+
message: string;
47+
}) {
48+
return (
49+
<div className="container flex grow flex-col py-10">
50+
<div className="flex min-h-[300px] grow flex-col items-center justify-center rounded-lg border">
51+
<div className="flex flex-col items-center gap-4">
52+
<span className="text-destructive-text">{props.message}</span>
53+
<Button asChild variant="outline">
54+
<Link href="/support">Get Support</Link>
55+
</Button>
56+
</div>
57+
</div>
58+
</div>
59+
);
60+
}
Lines changed: 6 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import type { Meta, StoryObj } from "@storybook/react";
2-
import { Toaster } from "sonner";
32
import { mobileViewport } from "stories/utils";
4-
import { JoinNebulaWaitlistPageUI } from "./nebula-waitlist-page-ui.client";
3+
import { NebulaWaitListPageUI } from "./nebula-waitlist-page-ui.client";
54

65
const meta = {
76
title: "nebula/waitlist",
@@ -16,48 +15,19 @@ const meta = {
1615
export default meta;
1716
type Story = StoryObj<typeof meta>;
1817

19-
export const NotInWaitingListDesktop: Story = {
18+
export const Desktop: Story = {
2019
args: {
2120
inWaitlist: false,
2221
},
2322
};
2423

25-
export const InWaitingListDesktop: Story = {
26-
args: {
27-
inWaitlist: true,
28-
},
29-
};
30-
31-
export const NotInWaitingListMobile: Story = {
32-
args: {
33-
inWaitlist: false,
34-
},
35-
parameters: {
36-
viewport: mobileViewport("iphone14"),
37-
},
38-
};
39-
40-
export const InWaitingListMobile: Story = {
41-
args: {
42-
inWaitlist: true,
43-
},
24+
export const Mobile: Story = {
25+
args: {},
4426
parameters: {
4527
viewport: mobileViewport("iphone14"),
4628
},
4729
};
4830

49-
function Story(props: {
50-
inWaitlist: boolean;
51-
}) {
52-
return (
53-
<>
54-
<JoinNebulaWaitlistPageUI
55-
onWaitlist={props.inWaitlist}
56-
joinWaitList={async () => {
57-
await new Promise((resolve) => setTimeout(resolve, 1500));
58-
}}
59-
/>
60-
<Toaster richColors />
61-
</>
62-
);
31+
function Story() {
32+
return <NebulaWaitListPageUI />;
6333
}

0 commit comments

Comments
 (0)