diff --git a/autogpt_platform/frontend/src/app/login/page.tsx b/autogpt_platform/frontend/src/app/login/page.tsx index ebb2a550b..dd1fd767c 100644 --- a/autogpt_platform/frontend/src/app/login/page.tsx +++ b/autogpt_platform/frontend/src/app/login/page.tsx @@ -235,6 +235,9 @@ export default function LoginPage() {

{feedback}

+ + Forgot your password? + ); diff --git a/autogpt_platform/frontend/src/app/reset_password/page.tsx b/autogpt_platform/frontend/src/app/reset_password/page.tsx new file mode 100644 index 000000000..45a17cb49 --- /dev/null +++ b/autogpt_platform/frontend/src/app/reset_password/page.tsx @@ -0,0 +1,216 @@ +"use client"; +import { useSupabase } from "@/components/providers/SupabaseProvider"; +import { Button } from "@/components/ui/button"; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form"; +import { Input } from "@/components/ui/input"; +import useUser from "@/hooks/useUser"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { useRouter } from "next/navigation"; +import { useState } from "react"; +import { useForm } from "react-hook-form"; +import { FaSpinner } from "react-icons/fa"; +import { z } from "zod"; + +const emailFormSchema = z.object({ + email: z.string().email().min(2).max(64), +}); + +const resetPasswordFormSchema = z + .object({ + password: z.string().min(6).max(64), + confirmPassword: z.string().min(6).max(64), + }) + .refine((data) => data.password === data.confirmPassword, { + message: "Passwords don't match", + path: ["confirmPassword"], + }); + +export default function ResetPasswordPage() { + const { supabase, isLoading: isSupabaseLoading } = useSupabase(); + const { user, isLoading: isUserLoading } = useUser(); + const router = useRouter(); + const [isLoading, setIsLoading] = useState(false); + const [feedback, setFeedback] = useState(null); + + const emailForm = useForm>({ + resolver: zodResolver(emailFormSchema), + defaultValues: { + email: "", + }, + }); + + const resetPasswordForm = useForm>({ + resolver: zodResolver(resetPasswordFormSchema), + defaultValues: { + password: "", + confirmPassword: "", + }, + }); + + if (isUserLoading || isSupabaseLoading) { + return ( +
+ +
+ ); + } + + if (!supabase) { + return ( +
+ User accounts are disabled because Supabase client is unavailable +
+ ); + } + + async function onSendEmail(d: z.infer) { + setIsLoading(true); + setFeedback(null); + + if (!(await emailForm.trigger())) { + setIsLoading(false); + return; + } + + const { data, error } = await supabase!.auth.resetPasswordForEmail( + d.email, + { + redirectTo: `${window.location.origin}/reset_password`, + }, + ); + + if (error) { + setFeedback(error.message); + setIsLoading(false); + return; + } + + setFeedback("Password reset email sent. Please check your email."); + setIsLoading(false); + } + + async function onResetPassword(d: z.infer) { + setIsLoading(true); + setFeedback(null); + + if (!(await resetPasswordForm.trigger())) { + setIsLoading(false); + return; + } + + const { data, error } = await supabase!.auth.updateUser({ + password: d.password, + }); + + if (error) { + setFeedback(error.message); + setIsLoading(false); + return; + } + + await supabase!.auth.signOut(); + router.push("/login"); + } + + return ( +
+
+

Reset Password

+ {user ? ( +
+ + ( + + Password + + + + + + )} + /> + ( + + Confirm Password + + + + + + )} + /> + + + + ) : ( +
+ + ( + + Email + + + + + + )} + /> + + {feedback ? ( +
+ {feedback} +
+ ) : null} + + + )} +
+
+ ); +}