Skip to content

Commit 91f69e2

Browse files
committed
User Onboarding UI Revamp
1 parent 1cf99be commit 91f69e2

File tree

31 files changed

+626
-708
lines changed

31 files changed

+626
-708
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
"use server";
2+
3+
import { getRawAccount } from "../../app/account/settings/getAccount";
4+
5+
export async function getRawAccountAction() {
6+
return getRawAccount();
7+
}

apps/dashboard/src/@/api/team.ts

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
import "server-only";
2-
import { COOKIE_ACTIVE_ACCOUNT, COOKIE_PREFIX_TOKEN } from "@/constants/cookie";
32
import { API_SERVER_URL } from "@/constants/env";
4-
import { cookies } from "next/headers";
53
import { getAuthToken } from "../../app/api/lib/getAuthToken";
64

75
export type Team = {
@@ -38,14 +36,9 @@ export async function getTeamBySlug(slug: string) {
3836
}
3937

4038
export async function getTeams() {
41-
const cookiesManager = await cookies();
42-
const activeAccount = cookiesManager.get(COOKIE_ACTIVE_ACCOUNT)?.value;
43-
const token = activeAccount
44-
? cookiesManager.get(COOKIE_PREFIX_TOKEN + activeAccount)?.value
45-
: null;
46-
39+
const token = await getAuthToken();
4740
if (!token) {
48-
return [];
41+
return null;
4942
}
5043

5144
const teamsRes = await fetch(`${API_SERVER_URL}/v1/teams`, {
@@ -56,7 +49,7 @@ export async function getTeams() {
5649
if (teamsRes.ok) {
5750
return (await teamsRes.json())?.result as Team[];
5851
}
59-
return [];
52+
return null;
6053
}
6154

6255
type TeamNebulWaitList = {

apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/(chainPage)/components/client/FaucetButton.tsx

Lines changed: 8 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,16 @@ import { useLoggedInUser } from "@3rdweb-sdk/react/hooks/useLoggedInUser";
1313
import { Turnstile } from "@marsidev/react-turnstile";
1414
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
1515
import type { CanClaimResponseType } from "app/api/testnet-faucet/can-claim/CanClaimResponseType";
16-
import { Onboarding } from "components/onboarding";
1716
import { mapV4ChainToV5Chain } from "contexts/map-chains";
1817
import { useTrack } from "hooks/analytics/useTrack";
19-
import { useState } from "react";
18+
import Link from "next/link";
2019
import { useForm } from "react-hook-form";
2120
import { toast } from "sonner";
2221
import { toUnits } from "thirdweb";
2322
import type { ChainMetadata } from "thirdweb/chains";
2423
import { useActiveAccount, useWalletBalance } from "thirdweb/react";
2524
import { z } from "zod";
25+
import { isOnboardingComplete } from "../../../../../../login/isOnboardingRequired";
2626

2727
function formatTime(seconds: number) {
2828
const rtf = new Intl.RelativeTimeFormat("en", { numeric: "auto" });
@@ -118,7 +118,6 @@ export function FaucetButton({
118118

119119
const accountQuery = useAccount();
120120
const userQuery = useLoggedInUser();
121-
const [showOnboarding, setShowOnBoarding] = useState(false);
122121

123122
const canClaimFaucetQuery = useQuery({
124123
queryKey: ["testnet-faucet-can-claim", chainId],
@@ -201,23 +200,14 @@ export function FaucetButton({
201200
);
202201
}
203202

204-
// Email verification is required to claim from the faucet
205-
if (
206-
!accountQuery.data.emailConfirmedAt &&
207-
!accountQuery.data.unconfirmedEmail
208-
) {
203+
if (!isOnboardingComplete(accountQuery.data)) {
209204
return (
210-
<>
211-
<Button
212-
variant="outline"
213-
className="!opacity-100 w-full"
214-
onClick={() => setShowOnBoarding(true)}
215-
>
205+
<Button variant="outline" asChild>
206+
{/* TODO - instead of opening in new tab - set the next search param and open in same tab */}
207+
<Link href="/login" target="_blank">
216208
Verify your Email
217-
</Button>
218-
{/* We will show the modal only if the user click on it, because this is a public page */}
219-
{showOnboarding && <Onboarding onOpenChange={setShowOnBoarding} />}
220-
</>
209+
</Link>
210+
</Button>
221211
);
222212
}
223213

apps/dashboard/src/app/account/layout.tsx

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,24 @@
11
import { getProjects } from "@/api/projects";
2-
import { getTeams } from "@/api/team";
2+
import { type Team, getTeams } from "@/api/team";
33
import { AppFooter } from "@/components/blocks/app-footer";
44
import type React from "react";
55
import { TabPathLinks } from "../../@/components/ui/tabs";
66
import { TWAutoConnect } from "../components/autoconnect";
7+
import { loginRedirect } from "../login/loginRedirect";
78
import { AccountHeader } from "./components/AccountHeader";
89

910
export default async function AccountLayout(props: {
1011
children: React.ReactNode;
1112
}) {
13+
const teams = await getTeams();
14+
if (!teams) {
15+
loginRedirect("/account");
16+
}
17+
1218
return (
1319
<div className="flex min-h-screen flex-col bg-background">
1420
<div className="flex grow flex-col">
15-
<HeaderAndNav />
21+
<HeaderAndNav teams={teams} />
1622
{props.children}
1723
</div>
1824
<TWAutoConnect />
@@ -21,11 +27,11 @@ export default async function AccountLayout(props: {
2127
);
2228
}
2329

24-
async function HeaderAndNav() {
25-
const teams = await getTeams();
26-
30+
async function HeaderAndNav(props: {
31+
teams: Team[];
32+
}) {
2733
const teamsAndProjects = await Promise.all(
28-
teams.map(async (team) => ({
34+
props.teams.map(async (team) => ({
2935
team,
3036
projects: await getProjects(team.slug),
3137
})),

apps/dashboard/src/app/account/page.tsx

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,16 @@
11
import { getTeams } from "@/api/team";
22
import { getMembers } from "@/api/team-members";
33
import { getThirdwebClient } from "@/constants/thirdweb.server";
4-
import { redirect } from "next/navigation";
4+
import { loginRedirect } from "../login/loginRedirect";
55
import { AccountTeamsUI } from "./overview/AccountTeamsUI";
6-
import { getAccount } from "./settings/getAccount";
6+
import { getValidAccount } from "./settings/getAccount";
77

88
export default async function Page() {
9-
const account = await getAccount();
10-
11-
if (!account) {
12-
redirect("/login?next=/account");
13-
}
14-
9+
const account = await getValidAccount("/account");
1510
const teams = await getTeams();
11+
if (!teams) {
12+
loginRedirect("/account");
13+
}
1614

1715
const teamsWithRole = (
1816
await Promise.all(

apps/dashboard/src/app/account/settings/getAccount.ts

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,20 @@
11
import { API_SERVER_URL } from "@/constants/env";
22
import type { Account } from "@3rdweb-sdk/react/hooks/useApi";
33
import { getAuthToken } from "../../api/lib/getAuthToken";
4+
import { isOnboardingComplete } from "../../login/isOnboardingRequired";
5+
import { loginRedirect } from "../../login/loginRedirect";
46

5-
export async function getAccount() {
7+
/**
8+
* Just get the account object without enforcing onboarding.
9+
* In most cases - you should just be using `getValidAccount`
10+
*/
11+
export async function getRawAccount() {
612
const authToken = await getAuthToken();
713

14+
if (!authToken) {
15+
return undefined;
16+
}
17+
818
const res = await fetch(`${API_SERVER_URL}/v1/account/me`, {
919
method: "GET",
1020
headers: {
@@ -21,3 +31,19 @@ export async function getAccount() {
2131

2232
return json.data as Account;
2333
}
34+
35+
/**
36+
* If no account, redirect to login page
37+
* if account and not onboarded, redirect to get-started page
38+
* @param pagePath - the path of the current page to redirect back to after login/onboarding
39+
*/
40+
export async function getValidAccount(pagePath: string) {
41+
const account = await getRawAccount();
42+
43+
// enforce login & onboarding
44+
if (!account || !isOnboardingComplete(account)) {
45+
loginRedirect(pagePath);
46+
}
47+
48+
return account;
49+
}

apps/dashboard/src/app/account/settings/page.tsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
import { getThirdwebClient } from "@/constants/thirdweb.server";
2-
import { redirect } from "next/navigation";
32
import { getAuthToken } from "../../api/lib/getAuthToken";
3+
import { loginRedirect } from "../../login/loginRedirect";
44
import { AccountSettingsPage } from "./AccountSettingsPage";
5-
import { getAccount } from "./getAccount";
5+
import { getValidAccount } from "./getAccount";
66

77
export default async function Page() {
8-
const account = await getAccount();
8+
const pagePath = "/account";
9+
const account = await getValidAccount(pagePath);
910
const token = await getAuthToken();
1011

11-
if (!account || !token) {
12-
redirect(`/login?next=${encodeURIComponent("/account")}`);
12+
if (!token) {
13+
loginRedirect(pagePath);
1314
}
1415

1516
return (

0 commit comments

Comments
 (0)