From 93049e864ae6d281b60c23dee868bca3f585dd4a Mon Sep 17 00:00:00 2001 From: Mohamed Bassem Date: Thu, 10 Jul 2025 08:35:32 +0000 Subject: feat: Add support for email verification --- apps/web/app/check-email/page.tsx | 128 +++++++++++++++++++++++++++++++ apps/web/app/verify-email/page.tsx | 152 +++++++++++++++++++++++++++++++++++++ 2 files changed, 280 insertions(+) create mode 100644 apps/web/app/check-email/page.tsx create mode 100644 apps/web/app/verify-email/page.tsx (limited to 'apps/web/app') diff --git a/apps/web/app/check-email/page.tsx b/apps/web/app/check-email/page.tsx new file mode 100644 index 00000000..96f0afb4 --- /dev/null +++ b/apps/web/app/check-email/page.tsx @@ -0,0 +1,128 @@ +"use client"; + +import { useState } from "react"; +import { useRouter, useSearchParams } from "next/navigation"; +import { Alert, AlertDescription } from "@/components/ui/alert"; +import { Button } from "@/components/ui/button"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { api } from "@/lib/trpc"; +import { Loader2, Mail } from "lucide-react"; + +export default function CheckEmailPage() { + const searchParams = useSearchParams(); + const router = useRouter(); + const [message, setMessage] = useState(""); + + const email = searchParams.get("email"); + + const resendEmailMutation = api.users.resendVerificationEmail.useMutation({ + onSuccess: () => { + setMessage( + "A new verification email has been sent to your email address.", + ); + }, + onError: (error) => { + setMessage(error.message || "Failed to resend verification email."); + }, + }); + + const handleResendEmail = () => { + if (email) { + resendEmailMutation.mutate({ email }); + } + }; + + const handleBackToSignIn = () => { + router.push("/signin"); + }; + + if (!email) { + return ( +
+ + + + Invalid Request + + + No email address provided. Please try signing up again. + + + + + + +
+ ); + } + + return ( +
+ + + Check Your Email + + We've sent a verification link to your email address + + + +
+ +
+ +
+

+ We've sent a verification email to: +

+

{email}

+

+ Click the link in the email to verify your account and complete + your registration. +

+
+ + {message && ( + + + {message} + + + )} + +
+ + +
+
+
+
+ ); +} diff --git a/apps/web/app/verify-email/page.tsx b/apps/web/app/verify-email/page.tsx new file mode 100644 index 00000000..e8792465 --- /dev/null +++ b/apps/web/app/verify-email/page.tsx @@ -0,0 +1,152 @@ +"use client"; + +import { useEffect, useState } from "react"; +import { useRouter, useSearchParams } from "next/navigation"; +import { Alert, AlertDescription } from "@/components/ui/alert"; +import { Button } from "@/components/ui/button"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { api } from "@/lib/trpc"; +import { CheckCircle, Loader2, XCircle } from "lucide-react"; + +export default function VerifyEmailPage() { + const searchParams = useSearchParams(); + const router = useRouter(); + const [status, setStatus] = useState<"loading" | "success" | "error">( + "loading", + ); + const [message, setMessage] = useState(""); + + const token = searchParams.get("token"); + const email = searchParams.get("email"); + + const verifyEmailMutation = api.users.verifyEmail.useMutation({ + onSuccess: () => { + setStatus("success"); + setMessage( + "Your email has been successfully verified! You can now sign in.", + ); + }, + onError: (error) => { + setStatus("error"); + setMessage( + error.message || + "Failed to verify email. The link may be invalid or expired.", + ); + }, + }); + + const resendEmailMutation = api.users.resendVerificationEmail.useMutation({ + onSuccess: () => { + setMessage( + "A new verification email has been sent to your email address.", + ); + }, + onError: (error) => { + setMessage(error.message || "Failed to resend verification email."); + }, + }); + + useEffect(() => { + if (token && email) { + verifyEmailMutation.mutate({ token, email }); + } else { + setStatus("error"); + setMessage("Invalid verification link. Missing token or email."); + } + }, [token, email]); + + const handleResendEmail = () => { + if (email) { + resendEmailMutation.mutate({ email }); + } + }; + + const handleSignIn = () => { + router.push("/signin"); + }; + + return ( +
+ + + + Email Verification + + + {status === "loading" && "Verifying your email address..."} + {status === "success" && "Email verified successfully!"} + {status === "error" && "Verification failed"} + + + + {status === "loading" && ( +
+ +
+ )} + + {status === "success" && ( + <> +
+ +
+ + + {message} + + + + + )} + + {status === "error" && ( + <> +
+ +
+ + + {message} + + + {email && ( +
+ + +
+ )} + + )} +
+
+
+ ); +} -- cgit v1.2.3-70-g09d2