Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions src/app/(auth)/_components/signup-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ export function SignUpForm() {
defaultValues: {
email: "",
password: "",
username: "",
phoneNumber: "",
},
})

Expand All @@ -48,6 +50,8 @@ export function SignUpForm() {
await signUp.create({
emailAddress: data.email,
password: data.password,
username: data.username,
...(data.phoneNumber && { phoneNumber: data.phoneNumber }),
})

// Send email verification code
Expand Down Expand Up @@ -82,6 +86,32 @@ export function SignUpForm() {
</FormItem>
)}
/>
<FormField
control={form.control}
name="username"
render={({ field }) => (
<FormItem>
<FormLabel>Username</FormLabel>
<FormControl>
<Input placeholder="rodneymullen180" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="phoneNumber"
render={({ field }) => (
<FormItem>
<FormLabel>Phone Number</FormLabel>
<FormControl>
<Input placeholder="+1234567890" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="password"
Expand Down
17 changes: 14 additions & 3 deletions src/app/(auth)/_components/verify-email-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
} from "@/components/ui/form"
import { Input } from "@/components/ui/input"
import { Icons } from "@/components/icons"
import { toast } from "sonner"

type Inputs = z.infer<typeof verifyEmailSchema>

Expand Down Expand Up @@ -50,10 +51,20 @@ export function VerifyEmailForm() {
or if the user needs to complete more steps.*/
console.log(JSON.stringify(completeSignUp, null, 2))
}
if (completeSignUp.status === "complete") {
await setActive({ session: completeSignUp.createdSessionId })
const emailStatus = completeSignUp.verifications.emailAddress.status
if (emailStatus && ["complete", "verified"].includes(emailStatus)) {
// await setActive({ session: completeSignUp.createdSessionId })

router.push(`${window.location.origin}/`)
// router.push(`${window.location.origin}/`)
// Send phone verification code
await signUp.preparePhoneNumberVerification({
strategy: "phone_code",
})

router.push("/signup/verify-phone")
toast.message("Check your phone", {
description: "We sent you a 6-digit verification code.",
})
}
} catch (err) {
showErrorToast(err)
Expand Down
105 changes: 105 additions & 0 deletions src/app/(auth)/_components/verify-phone-form.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
"use client"

import * as React from "react"
import { useRouter } from "next/navigation"
import { useSignUp } from "@clerk/nextjs"
import { zodResolver } from "@hookform/resolvers/zod"
import { useForm } from "react-hook-form"
import type { z } from "zod"

import { showErrorToast } from "@/lib/handle-error"
import { verifyPhoneSchema } from "@/lib/validations/auth"
import { Button } from "@/components/ui/button"
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form"
import { Input } from "@/components/ui/input"
import { Icons } from "@/components/icons"
import { toast } from "sonner"

type Inputs = z.infer<typeof verifyPhoneSchema>

export function VerifyPhoneForm() {
const router = useRouter()
const { isLoaded, signUp, setActive } = useSignUp()
const [loading, setLoading] = React.useState(false)

// react-hook-form
const form = useForm<Inputs>({
resolver: zodResolver(verifyPhoneSchema),
defaultValues: {
code: "",
},
})

async function onSubmit(data: Inputs) {
if (!isLoaded) return

setLoading(true)

try {
const completeSignUp = await signUp.attemptPhoneNumberVerification({
code: data.code,
})
if (completeSignUp.status !== "complete") {
/* investigate the response, to see if there was an error
or if the user needs to complete more steps.*/
console.log(JSON.stringify(completeSignUp, null, 2))
}
if (completeSignUp.status === "complete") {
await setActive({ session: completeSignUp.createdSessionId })

router.push(`${window.location.origin}/`)
toast.message("Account Created!", {
description: "Congratulations! Your account has been created.",
})
}
} catch (err) {
showErrorToast(err)
} finally {
setLoading(false)
}
}

return (
<Form {...form}>
<form className="grid gap-4" onSubmit={form.handleSubmit(onSubmit)}>
<FormField
control={form.control}
name="code"
render={({ field }) => (
<FormItem>
<FormLabel>Verification Code</FormLabel>
<FormControl>
<Input
placeholder="169420"
{...field}
onChange={(e) => {
e.target.value = e.target.value.trim()
field.onChange(e)
}}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<Button className="mt-2" disabled={loading}>
{loading && (
<Icons.spinner
className="mr-2 size-4 animate-spin"
aria-hidden="true"
/>
)}
Create account
<span className="sr-only">Create account</span>
</Button>
</form>
</Form>
)
}
36 changes: 36 additions & 0 deletions src/app/(auth)/signup/verify-phone/[[...verify-phone]]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { type Metadata } from "next"
import { env } from "@/env.js"

import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card"
import { Shell } from "@/components/shell"
import { VerifyPhoneForm } from "@/app/(auth)/_components/verify-phone-form"

export const metadata: Metadata = {
metadataBase: new URL(env.NEXT_PUBLIC_APP_URL),
title: "Verify Phone Number",
description: "Verify your phone number to continue with your sign up",
}

export default function VerifyPhonePage() {
return (
<Shell className="max-w-lg">
<Card>
<CardHeader className="space-y-1">
<CardTitle className="text-2xl">Verify phone number</CardTitle>
<CardDescription>
Verify your phone number to complete your account creation
</CardDescription>
</CardHeader>
<CardContent className="grid gap-4">
<VerifyPhoneForm />
</CardContent>
</Card>
</Shell>
)
}
20 changes: 20 additions & 0 deletions src/lib/validations/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@ export const authSchema = z.object({
.max(100, {
message: "Password must be at most 100 characters long",
}),
username: z.string().min(3, {
message: "Username must be at least 3 characters long",
}),
phoneNumber: z.string()
.regex(/^\+91[0-9]{10}$/, {
message: "Please enter a valid Indian phone number starting with +91 followed by 10 digits",
}),
})

export const verifyEmailSchema = z.object({
Expand All @@ -23,10 +30,23 @@ export const verifyEmailSchema = z.object({
.max(6),
})

export const verifyPhoneSchema = z.object({
code: z
.string()
.min(6, {
message: "Verification code must be 6 characters long",
})
.max(6),
})

export const checkEmailSchema = z.object({
email: authSchema.shape.email,
})

export const checkPhoneSchema = z.object({
phone: authSchema.shape.phoneNumber,
})

export const resetPasswordSchema = z
.object({
password: authSchema.shape.password,
Expand Down