2 changed files with 205 additions and 0 deletions
@ -0,0 +1,202 @@ |
|||
'use client' |
|||
|
|||
import { useState, useEffect } from 'react' |
|||
import { useSearchParams } from 'next/navigation' |
|||
import Link from 'next/link' |
|||
import { Button } from '@/components/ui/button' |
|||
import { |
|||
Card, |
|||
CardContent, |
|||
CardDescription, |
|||
CardFooter, |
|||
CardHeader, |
|||
CardTitle, |
|||
} from '@/components/ui/card' |
|||
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert' |
|||
import { Loader2, CheckCircle, XCircle, Mail, ArrowRight } from 'lucide-react' |
|||
import { useMutation } from '@tanstack/react-query' |
|||
import httpBrowserClient from '@/lib/httpBrowserClient' |
|||
import { ApiEndpoints } from '@/config/api' |
|||
|
|||
const ErrorAlert = ({ message }: { message: string }) => ( |
|||
<Alert |
|||
variant='destructive' |
|||
className='bg-red-50 text-red-700 border-red-200' |
|||
> |
|||
<XCircle className='h-5 w-5 text-red-600' /> |
|||
<AlertTitle className='text-lg font-semibold'>Error</AlertTitle> |
|||
<AlertDescription>{message}</AlertDescription> |
|||
</Alert> |
|||
) |
|||
|
|||
const SendVerificationButton = ({ |
|||
onClick, |
|||
isLoading, |
|||
hasVerificationCode, |
|||
}: { |
|||
onClick: () => void |
|||
isLoading: boolean |
|||
hasVerificationCode: boolean |
|||
}) => ( |
|||
<Button |
|||
className='w-full text-lg py-6' |
|||
onClick={onClick} |
|||
disabled={isLoading} |
|||
> |
|||
{isLoading ? ( |
|||
<Loader2 className='mr-2 h-5 w-5 animate-spin' /> |
|||
) : ( |
|||
<Mail className='mr-2 h-5 w-5' /> |
|||
)} |
|||
{hasVerificationCode |
|||
? 'Resend Verification Email' |
|||
: 'Send Verification Email'} |
|||
</Button> |
|||
) |
|||
|
|||
const SendVerificationEmail = () => { |
|||
const [successMessage, setSuccessMessage] = useState<string>('') |
|||
const [error, setError] = useState<string>('') |
|||
|
|||
const { |
|||
mutate: sendVerificationEmailMutation, |
|||
isPending: isSendingVerificationEmail, |
|||
} = useMutation({ |
|||
mutationFn: () => |
|||
httpBrowserClient.post( |
|||
ApiEndpoints.auth.sendEmailVerificationEmail(), |
|||
{} |
|||
), |
|||
onSuccess: () => { |
|||
setSuccessMessage('Verification email has been sent to your inbox') |
|||
}, |
|||
onError: (error: any) => { |
|||
setError(error.message || 'Failed to send verification email') |
|||
}, |
|||
}) |
|||
|
|||
const renderContent = () => { |
|||
if (successMessage) |
|||
return ( |
|||
<Alert |
|||
variant='default' |
|||
className='bg-blue-50 text-blue-700 border-blue-200' |
|||
> |
|||
<Mail className='h-5 w-5 text-blue-600' /> |
|||
<AlertTitle className='text-lg font-semibold'>Email Sent</AlertTitle> |
|||
<AlertDescription>{successMessage}</AlertDescription> |
|||
</Alert> |
|||
) |
|||
if (error) return <ErrorAlert message={error} /> |
|||
return null |
|||
} |
|||
|
|||
return ( |
|||
<Card className='w-full max-w-md'> |
|||
<CardHeader> |
|||
<CardTitle className='text-2xl font-bold'>Email Verification</CardTitle> |
|||
<CardDescription> |
|||
Send a verification email to verify your account |
|||
</CardDescription> |
|||
</CardHeader> |
|||
<CardContent className='space-y-4'>{renderContent()}</CardContent> |
|||
<CardFooter> |
|||
<SendVerificationButton |
|||
onClick={() => sendVerificationEmailMutation()} |
|||
isLoading={isSendingVerificationEmail} |
|||
hasVerificationCode={false} |
|||
/> |
|||
</CardFooter> |
|||
</Card> |
|||
) |
|||
} |
|||
|
|||
const VerifyEmail = ({ |
|||
userId, |
|||
verificationCode, |
|||
}: { |
|||
userId: string |
|||
verificationCode: string |
|||
}) => { |
|||
const [successMessage, setSuccessMessage] = useState<string>('') |
|||
const [error, setError] = useState<string>('') |
|||
const [isVerified, setIsVerified] = useState(false) |
|||
|
|||
const { mutate: verifyEmailMutation, isPending: isVerifyingEmail } = |
|||
useMutation({ |
|||
mutationFn: () => |
|||
httpBrowserClient.post('/auth/verify-email', { |
|||
userId, |
|||
verificationCode, |
|||
}), |
|||
onSuccess: () => { |
|||
setIsVerified(true) |
|||
setSuccessMessage('Your email has been successfully verified') |
|||
}, |
|||
onError: (error: any) => { |
|||
setError(error.message || 'Failed to verify email') |
|||
}, |
|||
}) |
|||
|
|||
useEffect(() => { |
|||
verifyEmailMutation() |
|||
}, [verifyEmailMutation]) |
|||
|
|||
const renderContent = () => { |
|||
if (isVerifyingEmail) |
|||
return ( |
|||
<div className='flex justify-center py-8'> |
|||
<Loader2 className='h-12 w-12 animate-spin text-primary' /> |
|||
</div> |
|||
) |
|||
if (isVerified) |
|||
return ( |
|||
<Alert |
|||
variant='default' |
|||
className='bg-green-50 text-green-700 border-green-200' |
|||
> |
|||
<CheckCircle className='h-5 w-5 text-green-600' /> |
|||
<AlertTitle className='text-lg font-semibold'>Success</AlertTitle> |
|||
<AlertDescription>{successMessage}</AlertDescription> |
|||
</Alert> |
|||
) |
|||
if (error) return <ErrorAlert message={error} /> |
|||
return null |
|||
} |
|||
|
|||
return ( |
|||
<Card className='w-full max-w-md'> |
|||
<CardHeader> |
|||
<CardTitle className='text-2xl font-bold'>Email Verification</CardTitle> |
|||
<CardDescription>Verifying your email address...</CardDescription> |
|||
</CardHeader> |
|||
<CardContent className='space-y-4'>{renderContent()}</CardContent> |
|||
<CardFooter className='flex flex-col gap-4'> |
|||
{isVerified && ( |
|||
<Button className='w-full text-lg py-6' asChild> |
|||
<Link href='/dashboard'> |
|||
Go to Dashboard |
|||
<ArrowRight className='ml-2 h-5 w-5' /> |
|||
</Link> |
|||
</Button> |
|||
)} |
|||
</CardFooter> |
|||
</Card> |
|||
) |
|||
} |
|||
|
|||
export default function VerifyEmailPage() { |
|||
const searchParams = useSearchParams() |
|||
const userId = searchParams.get('userId') |
|||
const verificationCode = searchParams.get('verificationCode') |
|||
|
|||
return ( |
|||
<div className='flex min-h-screen items-center justify-center bg-gray-100 dark:bg-gray-900 p-4'> |
|||
{userId && verificationCode ? ( |
|||
<VerifyEmail userId={userId} verificationCode={verificationCode} /> |
|||
) : ( |
|||
<SendVerificationEmail /> |
|||
)} |
|||
</div> |
|||
) |
|||
} |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue