Skip to content

Commit 1c4b0f7

Browse files
committed
Redirect after onboarding according to next param from login
1 parent dc5c295 commit 1c4b0f7

File tree

2 files changed

+125
-91
lines changed

2 files changed

+125
-91
lines changed

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

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -49,28 +49,29 @@ function CustomConnectEmmbed() {
4949
const accountQuery = useAccount();
5050

5151
async function onLoginSuccessful() {
52-
if (nextSearchParam && isValidRedirectPath(nextSearchParam)) {
53-
router.replace(nextSearchParam);
54-
} else {
55-
const dashboardType = getCookie("x-dashboard-type");
56-
const account = await accountQuery.refetch();
57-
if (!account.data) throw new Error("No account found.");
58-
const existingAccountPreferences = await getAccountPreferences({
59-
accountId: account.data.id,
60-
});
61-
62-
console.log(account.data.email);
63-
64-
if (!existingAccountPreferences) {
65-
router.replace(
66-
`/onboarding?${account.data.email ? `email=${account.data.email}` : ""}`,
67-
);
68-
}
52+
const account = await accountQuery.refetch();
53+
if (!account.data) throw new Error("No account found.");
54+
const existingAccountPreferences = await getAccountPreferences({
55+
accountId: account.data.id,
56+
});
6957

70-
if (dashboardType === "team") {
71-
router.replace("/team");
58+
if (!existingAccountPreferences) {
59+
console.log("hello");
60+
console.log(account);
61+
router.replace(
62+
`/onboarding?${account.data.email ? `email=${account.data.email}` : ""}${nextSearchParam && isValidRedirectPath(nextSearchParam) ? `${account.data.email ? "&" : ""}next=${nextSearchParam}` : ""}`,
63+
);
64+
} else {
65+
if (nextSearchParam && isValidRedirectPath(nextSearchParam)) {
66+
router.replace(nextSearchParam);
7267
} else {
73-
router.replace("/dashboard");
68+
const dashboardType = getCookie("x-dashboard-type");
69+
70+
if (dashboardType === "team") {
71+
router.replace("/team");
72+
} else {
73+
router.replace("/dashboard");
74+
}
7475
}
7576
}
7677
}

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

Lines changed: 104 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
FormControl,
77
FormField,
88
FormItem,
9+
FormLabel,
910
FormMessage,
1011
} from "@/components/ui/form";
1112
import { Input } from "@/components/ui/input";
@@ -21,6 +22,7 @@ import {
2122
import { cn } from "@/lib/utils";
2223
import { useAccount } from "@3rdweb-sdk/react/hooks/useApi";
2324
import { Checkbox } from "@chakra-ui/react";
25+
import { zodResolver } from "@hookform/resolvers/zod";
2426
import * as RadioGroupPrimitive from "@radix-ui/react-radio-group";
2527
import { RadioGroup } from "@radix-ui/react-radio-group";
2628
import { THIRDWEB_ANALYTICS_API_HOST } from "constants/urls";
@@ -38,7 +40,79 @@ import {
3840
import { getCookie } from "stores/SyncStoreToCookies";
3941
import { Blobbie } from "thirdweb/react";
4042
import { shortenAddress } from "thirdweb/utils";
41-
import { FormLabel } from "tw-components";
43+
import { z } from "zod";
44+
45+
const interestValues = [
46+
{
47+
key: "SOCIAL_LOGIN",
48+
label: "Social Login",
49+
description:
50+
"Let users login to your app with Email, Phone, Telegram, and more",
51+
},
52+
{
53+
key: "WALLET_CONECTORS",
54+
label: "Wallet Connectors",
55+
description: "Allow users to connect to over 350 web3 wallets",
56+
},
57+
{
58+
key: "SPONSOR_TRANSACTIONS",
59+
label: "Sponsor Transactions",
60+
description:
61+
"Abstract away signatures & gas using Account Abstraction and set up sponsorship rules",
62+
},
63+
{
64+
key: "UNIFIED_IDENTITY",
65+
label: "Unified Identity",
66+
description:
67+
"Enable your users to link multiple onchain and offchain identities to a single ID",
68+
},
69+
{
70+
key: "CUSTOM_AUTH",
71+
label: "Custom Auth",
72+
description: "Authenticate with your backend using SIWE or JWT",
73+
},
74+
{
75+
key: "QUERY_BLOCKCHAIN_DATA",
76+
label: "Query Blockchain Data",
77+
description: "All your data are belong to us",
78+
},
79+
{
80+
key: "AUTO_TXN_MGMT",
81+
label: "Automated Transaction Management",
82+
description: "Scale transaction throughput with nonce management",
83+
},
84+
{
85+
key: "GAMING_TOOLS",
86+
label: "Gaming Tools",
87+
description: "Everything you need to build a game",
88+
},
89+
{
90+
key: "TOKEN_SWAPS",
91+
label: "Token Swaps",
92+
description:
93+
"Bridge to and from tokens on any EVM, directly in your application",
94+
},
95+
{
96+
key: "FIAT_ONRAMPS",
97+
label: "Fiat Onramps",
98+
description:
99+
"Allow users to purchase with a credit card within your application.",
100+
},
101+
];
102+
103+
const formSchema = z.object({
104+
email: z.string().email("Email is not valid").optional(),
105+
userType: z.string().optional(),
106+
name: z
107+
.string()
108+
.refine((name) => /^[a-zA-Z0-9 ]*$/.test(name), {
109+
message: "Name can only contain letters, numbers and spaces",
110+
})
111+
.optional(),
112+
role: z.string().optional(),
113+
industry: z.string().optional(),
114+
interests: z.array(z.string()).optional(),
115+
});
42116

43117
interface FormData {
44118
email: string;
@@ -72,22 +146,37 @@ const RadioGroupItemButton = React.forwardRef<
72146
);
73147
});
74148

149+
function isValidRedirectPath(encodedPath: string): boolean {
150+
try {
151+
// Decode the URI component
152+
const decodedPath = decodeURIComponent(encodedPath);
153+
// ensure the path always starts with a _single_ slash
154+
// dobule slash could be interpreted as `//example.com` which is not allowed
155+
return decodedPath.startsWith("/") && !decodedPath.startsWith("//");
156+
} catch {
157+
// If decoding fails, return false
158+
return false;
159+
}
160+
}
161+
75162
export default function OnboardingPage({
76163
searchParams,
77-
}: { searchParams: { address: string; email: string | undefined } }) {
164+
}: { searchParams: { email: string | undefined; next: string | undefined } }) {
78165
const accountQuery = useAccount();
79166
const [step, setStep] = useState(searchParams.email ? 2 : 1);
80167
const [direction, setDirection] = useState(1);
81168
const router = useRouter();
82169

83170
const form = useForm<FormData>({
171+
resolver: zodResolver(formSchema),
84172
defaultValues: {
85173
interests: [],
86174
email: searchParams.email ?? "",
87175
},
88176
});
89177

90178
const onSubmit: SubmitHandler<FormData> = async (data) => {
179+
console.log(data);
91180
const res = await fetch(
92181
`${THIRDWEB_ANALYTICS_API_HOST}/v1/preferences/account`,
93182
{
@@ -112,11 +201,15 @@ export default function OnboardingPage({
112201
throw new Error(json.message);
113202
}
114203

115-
const dashboardType = getCookie("x-dashboard-type");
116-
if (dashboardType === "team") {
117-
router.push("/team");
204+
if (searchParams.next && isValidRedirectPath(searchParams.next)) {
205+
router.replace(searchParams.next);
118206
} else {
119-
router.push("/dashboard");
207+
const dashboardType = getCookie("x-dashboard-type");
208+
if (dashboardType === "team") {
209+
router.push("/team");
210+
} else {
211+
router.push("/dashboard");
212+
}
120213
}
121214
};
122215

@@ -192,13 +285,11 @@ export default function OnboardingPage({
192285
};
193286

194287
const Step1: React.FC = () => (
195-
// className="box-border flex-col space-y-2"
196-
197288
<FormField
198289
control={form.control}
199290
name="email"
200291
render={({ field }) => (
201-
<FormItem>
292+
<FormItem className="box-border flex-col space-y-2">
202293
<FormLabel>What's your email?</FormLabel>
203294
<FormControl>
204295
<Input
@@ -355,7 +446,7 @@ export default function OnboardingPage({
355446
Social
356447
</SelectItem>
357448
<SelectItem key={"other"} value={"other"}>
358-
Social
449+
Other
359450
</SelectItem>
360451
</SelectContent>
361452
</Select>
@@ -366,64 +457,6 @@ export default function OnboardingPage({
366457
</div>
367458
);
368459

369-
const interestValues = [
370-
{
371-
key: "SOCIAL_LOGIN",
372-
label: "Social Login",
373-
description:
374-
"Let users login to your app with Email, Phone, Telegram, and more",
375-
},
376-
{
377-
key: "WALLET_CONECTORS",
378-
label: "Wallet Connectors",
379-
description: "Allow users to connect to over 350 web3 wallets",
380-
},
381-
{
382-
key: "SPONSOR_TRANSACTIONS",
383-
label: "Sponsor Transactions",
384-
description:
385-
"Abstract away signatures & gas using Account Abstraction and set up sponsorship rules",
386-
},
387-
{
388-
key: "UNIFIED_IDENTITY",
389-
label: "Unified Identity",
390-
description:
391-
"Enable your users to link multiple onchain and offchain identities to a single ID",
392-
},
393-
{
394-
key: "CUSTOM_AUTH",
395-
label: "Custom Auth",
396-
description: "Authenticate with your backend using SIWE or JWT",
397-
},
398-
{
399-
key: "QUERY_BLOCKCHAIN_DATA",
400-
label: "Query Blockchain Data",
401-
description: "All your data are belong to us",
402-
},
403-
{
404-
key: "AUTO_TXN_MGMT",
405-
label: "Automated Transaction Management",
406-
description: "Scale transaction throughput with nonce management",
407-
},
408-
{
409-
key: "GAMING_TOOLS",
410-
label: "Gaming Tools",
411-
description: "Everything you need to build a game",
412-
},
413-
{
414-
key: "TOKEN_SWAPS",
415-
label: "Token Swaps",
416-
description:
417-
"Bridge to and from tokens on any EVM, directly in your application",
418-
},
419-
{
420-
key: "FIAT_ONRAMPS",
421-
label: "Fiat Onramps",
422-
description:
423-
"Allow users to purchase with a credit card within your application.",
424-
},
425-
];
426-
427460
const Step3: React.FC<StepProps> = ({ register }) => (
428461
<div className="flex max-h-[700px] flex-col space-y-4 overflow-scroll">
429462
<FormField
@@ -441,7 +474,7 @@ export default function OnboardingPage({
441474
<Card
442475
key={interest.key}
443476
className={cn(
444-
"flex aspect-square cursor-pointer flex-col items-start justify-start space-y-1 p-4 transition-colors hover:bg-muted",
477+
"flex aspect-[4/3] cursor-pointer flex-col items-start justify-start space-y-1 p-4 transition-colors hover:bg-muted md:aspect-[16/9]",
445478
isChecked && "border-primary bg-muted",
446479
)}
447480
onClick={(event) => {
@@ -533,10 +566,10 @@ export default function OnboardingPage({
533566
<Building className="size-8" />
534567
)
535568
) : (
536-
<div className="size-7 overflow-hidden rounded-full">
569+
<div className="size-9 overflow-hidden rounded-full">
537570
<Blobbie
538571
address={accountQuery.data?.creatorWalletAddress ?? ""}
539-
size={28}
572+
size={48}
540573
/>
541574
</div>
542575
)}

0 commit comments

Comments
 (0)