66 FormControl ,
77 FormField ,
88 FormItem ,
9+ FormLabel ,
910 FormMessage ,
1011} from "@/components/ui/form" ;
1112import { Input } from "@/components/ui/input" ;
@@ -21,6 +22,7 @@ import {
2122import { cn } from "@/lib/utils" ;
2223import { useAccount } from "@3rdweb-sdk/react/hooks/useApi" ;
2324import { Checkbox } from "@chakra-ui/react" ;
25+ import { zodResolver } from "@hookform/resolvers/zod" ;
2426import * as RadioGroupPrimitive from "@radix-ui/react-radio-group" ;
2527import { RadioGroup } from "@radix-ui/react-radio-group" ;
2628import { THIRDWEB_ANALYTICS_API_HOST } from "constants/urls" ;
@@ -38,7 +40,79 @@ import {
3840import { getCookie } from "stores/SyncStoreToCookies" ;
3941import { Blobbie } from "thirdweb/react" ;
4042import { 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 - z A - Z 0 - 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
43117interface 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+
75162export 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