From 8448d52838bc02d6bc3a1245e0da70b18e7c73f2 Mon Sep 17 00:00:00 2001 From: Chloe-Xiao Date: Fri, 31 Oct 2025 22:24:22 +1100 Subject: [PATCH 1/8] feature/reset-forgot-psw-front --- .../components/SuccessModal.tsx | 89 ++++++ src/app/(public)/forgot-password/page.tsx | 253 +++++++++++++++ .../schemas/forgotPasswordSchema.ts | 7 + .../(public)/login/component/FormField.tsx | 3 +- .../(public)/login/component/LoginForm.tsx | 29 +- src/app/(public)/login/schemas/loginSchema.ts | 4 + .../login/ui/controller/ControllerInput.tsx | 4 +- src/app/(public)/reset-password/page.tsx | 288 ++++++++++++++++++ .../schemas/resetPasswordSchema.ts | 11 + 9 files changed, 684 insertions(+), 4 deletions(-) create mode 100644 src/app/(public)/forgot-password/components/SuccessModal.tsx create mode 100644 src/app/(public)/forgot-password/page.tsx create mode 100644 src/app/(public)/forgot-password/schemas/forgotPasswordSchema.ts create mode 100644 src/app/(public)/reset-password/page.tsx create mode 100644 src/app/(public)/reset-password/schemas/resetPasswordSchema.ts diff --git a/src/app/(public)/forgot-password/components/SuccessModal.tsx b/src/app/(public)/forgot-password/components/SuccessModal.tsx new file mode 100644 index 0000000..c235c12 --- /dev/null +++ b/src/app/(public)/forgot-password/components/SuccessModal.tsx @@ -0,0 +1,89 @@ +'use client'; + +import styled from 'styled-components'; + +const Overlay = styled.div` + position: fixed; + inset: 0; + background: rgba(80, 80, 80, 0.6); + z-index: 1000; + display: flex; + align-items: center; + justify-content: center; +`; + +const ModalBox = styled.div` + background: #fff; + border-radius: 16px; + padding: 36px 32px 28px 32px; + box-shadow: 0 4px 32px rgba(0, 0, 0, 0.12); + min-width: 340px; + max-width: 400px; + text-align: center; + position: relative; +`; + +const CloseBtn = styled.button` + position: absolute; + top: 16px; + right: 16px; + background: none; + border: none; + font-size: 22px; + color: #888; + cursor: pointer; +`; + +const SuccessIcon = () => ( + + + + + + + + + +); + +interface SuccessModalProps { + email?: string; + title?: string; + description?: React.ReactNode; + onClose: () => void; +} + +export default function SuccessModal({ + email, + title, + description, + onClose, +}: SuccessModalProps) { + return ( + + + + × + + +

+ {title} +

+
+ {description} +
+
+
+ ); +} diff --git a/src/app/(public)/forgot-password/page.tsx b/src/app/(public)/forgot-password/page.tsx new file mode 100644 index 0000000..925207e --- /dev/null +++ b/src/app/(public)/forgot-password/page.tsx @@ -0,0 +1,253 @@ +'use client'; + +import { zodResolver } from '@hookform/resolvers/zod'; +import Image from 'next/image'; +import { useRouter } from 'next/navigation'; +import { useEffect, useState } from 'react'; +import { useForm } from 'react-hook-form'; +import styled from 'styled-components'; + +import FormField from '@/app/(public)/login/component/FormField'; +import Button from '@/app/(public)/login/ui/Button'; +import ControllerInput from '@/app/(public)/login/ui/controller/ControllerInput'; + +import SuccessModal from './components/SuccessModal'; +import { forgotPasswordSchema } from './schemas/forgotPasswordSchema'; + +const PageContainer = styled.div` + min-height: 100vh; + background-color: #fafafa; + display: flex; + align-items: center; + justify-content: center; + padding: 20px; +`; + +const FormContainer = styled.div` + width: 100%; + max-width: 700px; + margin: 0 auto; + padding: 40px; + padding-bottom: 100px; + border-radius: 24px; + box-shadow: 0 0 24px 0 rgba(0, 0, 0, 0.03); + background-color: white; + + @media (max-width: 600px) { + padding: 20px; + padding-bottom: 60px; + } +`; + +const IconWrapper = styled.div` + position: absolute; + top: 24px; + left: 24px; + z-index: 2; + display: none; + + @media (max-width: 600px) { + display: block; + } +`; + +const RelativeContainer = styled.div` + position: relative; +`; + +const LogoContainer = styled.div` + display: flex; + justify-content: center; + margin-bottom: 32px; + + @media (max-width: 600px) { + margin-bottom: 24px; + } +`; + +const LogoImageWrapper = styled.div` + width: 200px; + height: 100px; + + @media (max-width: 600px) { + margin-top: 32px; + width: 105px; + height: 25px; + } + + img { + width: 100% !important; + height: 100% !important; + object-fit: contain; + } +`; + +const Title = styled.h2` + text-align: center; + font-size: 22px; + font-weight: 600; + margin-bottom: 24px; +`; + +const Subtitle = styled.p` + text-align: center; + color: #444; + font-size: 16px; + margin-bottom: 50px; + margin-top: -12px; +`; + +const TopRightWrapper = styled.div` + position: absolute; + top: 56px; + right: 80px; + z-index: 10; +`; + +const BackButton = styled.button` + display: flex; + align-items: center; + gap: 6px; + background: #fafafa; + border: 1px solid #e0e0e0; + border-radius: 12px; + font-size: 14px; + padding: 10px 16px; + cursor: pointer; + transition: background 0.2s; + + &:hover { + background: #f0f0f0; + } +`; + +export default function ForgotPasswordPage() { + const [mounted, setMounted] = useState(false); + const router = useRouter(); + const [showSuccess, setShowSuccess] = useState(false); + const [sentEmail, setSentEmail] = useState(''); + const { control, handleSubmit, formState } = useForm<{ email: string }>({ + resolver: zodResolver(forgotPasswordSchema), + defaultValues: { email: '' }, + }); + + useEffect(() => { + setMounted(true); + }, []); + + const onSubmit = (data: { email: string }) => { + // TODO: handle forgot password logic + setSentEmail(data.email); + setShowSuccess(true); + }; + + if (!mounted) { + return ( +
+ Loading... +
+ ); + } + + return ( + + {showSuccess && ( + + An email has been sent to{' '} + {sentEmail} with + instructions for resetting your password. This email may take a + few minutes to arrive in your inbox. + + } + onClose={() => setShowSuccess(false)} + /> + )} + + router.back()}> + + + + + + + Back + + + + + + + + + Logo + + + Forgot Password + + Fill in your email and we'll send you a link to reset your password. + +
void handleSubmit(onSubmit)(e)} noValidate> + + + + +
+
+
+ ); +} diff --git a/src/app/(public)/forgot-password/schemas/forgotPasswordSchema.ts b/src/app/(public)/forgot-password/schemas/forgotPasswordSchema.ts new file mode 100644 index 0000000..ec75d95 --- /dev/null +++ b/src/app/(public)/forgot-password/schemas/forgotPasswordSchema.ts @@ -0,0 +1,7 @@ +import { z } from 'zod'; + +import { emailSchema } from '@/app/(public)/login/schemas/loginSchema'; + +export const forgotPasswordSchema = z.object({ + email: emailSchema, +}); diff --git a/src/app/(public)/login/component/FormField.tsx b/src/app/(public)/login/component/FormField.tsx index 301273f..a48c97f 100644 --- a/src/app/(public)/login/component/FormField.tsx +++ b/src/app/(public)/login/component/FormField.tsx @@ -1,8 +1,7 @@ import React from 'react'; import styled from 'styled-components'; - interface FormFieldProps { - label?: string; + label?: React.ReactNode; children: React.ReactNode; size?: 'small' | 'normal' | 'large'; mb?: number; diff --git a/src/app/(public)/login/component/LoginForm.tsx b/src/app/(public)/login/component/LoginForm.tsx index 38c0562..6d3320c 100644 --- a/src/app/(public)/login/component/LoginForm.tsx +++ b/src/app/(public)/login/component/LoginForm.tsx @@ -99,6 +99,22 @@ const ErrorMessage = styled.div` margin-bottom: 16px; `; +const ForgotPasswordWrapper = styled.div` + width: 100%; + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 6px; +`; + +const ForgotPasswordLink = styled.a` + font-family: Roboto; + font-size: 14px; + font-weight: bold; + color: #0687ff; + text-decoration: none; +`; + export default function LoginForm() { const { control, handleSubmit } = useForm({ resolver: zodResolver(loginSchema), @@ -133,7 +149,18 @@ export default function LoginForm() { disabled={isLoading} /> - + + + Password + + Forgot password? + + + } + mb={0} + >
{ type?: string; fullWidth?: boolean; disabled?: boolean; + hideError?: boolean; } const StyledBox = styled(Box)<{ $fullWidth?: boolean }>` @@ -52,6 +53,7 @@ export default function ControllerInput({ type = 'text', fullWidth = true, disabled, + hideError = false, }: ControllerInputProps) { return ( ({ $hasError={!!error} disabled={disabled} /> - {error && {error.message}} + {!hideError && error && {error.message}} )} /> diff --git a/src/app/(public)/reset-password/page.tsx b/src/app/(public)/reset-password/page.tsx new file mode 100644 index 0000000..43c31ca --- /dev/null +++ b/src/app/(public)/reset-password/page.tsx @@ -0,0 +1,288 @@ +'use client'; + +import { zodResolver } from '@hookform/resolvers/zod'; +import Image from 'next/image'; +import { useRouter } from 'next/navigation'; +import { useEffect, useState } from 'react'; +import { useForm } from 'react-hook-form'; +import styled from 'styled-components'; + +import SuccessModal from '@/app/(public)/forgot-password/components/SuccessModal'; +import FormField from '@/app/(public)/login/component/FormField'; +import Button from '@/app/(public)/login/ui/Button'; +import ControllerInput from '@/app/(public)/login/ui/controller/ControllerInput'; + +import { resetPasswordSchema } from './schemas/resetPasswordSchema'; + +const PageContainer = styled.div` + min-height: 100vh; + background-color: #fafafa; + display: flex; + align-items: center; + justify-content: center; + padding: 20px; +`; + +const FormContainer = styled.div` + width: 100%; + max-width: 700px; + margin: 0 auto; + padding: 40px; + padding-bottom: 100px; + border-radius: 24px; + box-shadow: 0 0 24px 0 rgba(0, 0, 0, 0.03); + background-color: white; + + @media (max-width: 600px) { + padding: 20px; + padding-bottom: 60px; + } +`; + +const TopRightWrapper = styled.div` + position: absolute; + top: 56px; + right: 80px; + z-index: 10; +`; + +const BackButton = styled.button` + display: flex; + align-items: center; + gap: 6px; + background: #fafafa; + border: 1px solid #e0e0e0; + border-radius: 12px; + font-size: 14px; + padding: 10px 16px; + cursor: pointer; + transition: background 0.2s; + + &:hover { + background: #f0f0f0; + } +`; + +const LogoContainer = styled.div` + display: flex; + justify-content: center; + margin-bottom: 32px; + + @media (max-width: 600px) { + margin-bottom: 24px; + } +`; + +const LogoImageWrapper = styled.div` + width: 200px; + height: 100px; + + @media (max-width: 600px) { + margin-top: 32px; + width: 105px; + height: 25px; + } + + img { + width: 100% !important; + height: 100% !important; + object-fit: contain; + } +`; + +const Title = styled.h2` + text-align: center; + font-size: 22px; + font-weight: 600; + margin-bottom: 24px; +`; + +const EyeIcon = () => ( + + + + + + +); + +const EyeOffIcon = () => ( + + + + + + +); + +export default function ResetPasswordPage() { + const [mounted, setMounted] = useState(false); + const [showSuccess, setShowSuccess] = useState(false); + const [showPassword, setShowPassword] = useState(false); + const [showConfirmPassword, setShowConfirmPassword] = useState(false); + const router = useRouter(); + const { control, handleSubmit, formState } = useForm<{ + password: string; + confirmPassword: string; + }>({ + resolver: zodResolver(resetPasswordSchema), + defaultValues: { password: '', confirmPassword: '' }, + }); + + useEffect(() => { + setMounted(true); + }, []); + + const onSubmit = (data: { password: string; confirmPassword: string }) => { + // TODO: handle reset password logic (API call) + setShowSuccess(true); + }; + + if (!mounted) return null; + + return ( + + {showSuccess && ( + router.push('/login')} + /> + )} + + router.back()}> + + + + + + + Back + + + + + + Logo + + + Reset Your Password +
void handleSubmit(onSubmit)(e)} noValidate> + +
+ + +
+ {formState.errors.password && ( +
+ {formState.errors.password.message} +
+ )} +
+ +
+ + +
+ {formState.errors.confirmPassword && ( +
+ {formState.errors.confirmPassword.message} +
+ )} +
+ +
+
+
+ ); +} diff --git a/src/app/(public)/reset-password/schemas/resetPasswordSchema.ts b/src/app/(public)/reset-password/schemas/resetPasswordSchema.ts new file mode 100644 index 0000000..a32c18c --- /dev/null +++ b/src/app/(public)/reset-password/schemas/resetPasswordSchema.ts @@ -0,0 +1,11 @@ +import * as z from 'zod'; + +export const resetPasswordSchema = z + .object({ + password: z.string().min(6, 'Password must be at least 6 characters'), + confirmPassword: z.string(), + }) + .refine(data => data.password === data.confirmPassword, { + message: "Passwords don't match", + path: ['confirmPassword'], + }); From b63d8258e85333094582f6d096654281e3698579 Mon Sep 17 00:00:00 2001 From: Chloe-Xiao Date: Tue, 4 Nov 2025 10:53:38 +1100 Subject: [PATCH 2/8] fix/reset-psw-structure-refine --- .../components/ResetPasswordForm.tsx | 204 ++++++++++++++++++ src/app/(public)/reset-password/page.tsx | 169 +-------------- 2 files changed, 206 insertions(+), 167 deletions(-) create mode 100644 src/app/(public)/reset-password/components/ResetPasswordForm.tsx diff --git a/src/app/(public)/reset-password/components/ResetPasswordForm.tsx b/src/app/(public)/reset-password/components/ResetPasswordForm.tsx new file mode 100644 index 0000000..8e671a0 --- /dev/null +++ b/src/app/(public)/reset-password/components/ResetPasswordForm.tsx @@ -0,0 +1,204 @@ +'use client'; + +import { zodResolver } from '@hookform/resolvers/zod'; +import { useRouter } from 'next/navigation'; +import { useEffect, useState } from 'react'; +import { useForm } from 'react-hook-form'; + +import SuccessModal from '@/app/(public)/forgot-password/components/SuccessModal'; +import FormField from '@/app/(public)/login/component/FormField'; +import Button from '@/app/(public)/login/ui/Button'; +import ControllerInput from '@/app/(public)/login/ui/controller/ControllerInput'; + +import { resetPasswordSchema } from '../schemas/resetPasswordSchema'; + +const EyeIcon = () => ( + + + + + + +); + +const EyeOffIcon = () => ( + + + + + + +); + +export default function ResetPasswordForm() { + const [mounted, setMounted] = useState(false); + const [showSuccess, setShowSuccess] = useState(false); + const [showPassword, setShowPassword] = useState(false); + const [showConfirmPassword, setShowConfirmPassword] = useState(false); + const router = useRouter(); + const { control, handleSubmit, formState } = useForm<{ + password: string; + confirmPassword: string; + }>({ + resolver: zodResolver(resetPasswordSchema), + defaultValues: { password: '', confirmPassword: '' }, + }); + + useEffect(() => { + setMounted(true); + }, []); + + const onSubmit = async (data: { + password: string; + confirmPassword: string; + }) => { + try { + // Call the backend API to reset password (mocked for now) + const res = await fetch('/api/auth/reset-password', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + password: data.password, + confirmPassword: data.confirmPassword, + }), + }); + + if (!res.ok) { + // Optionally handle error response + const data = (await res.json()) as unknown; + let errorMessage: string | undefined; + if (typeof data === 'object' && data !== null && 'message' in data) { + const msg = (data as Record).message; + if (typeof msg === 'string') errorMessage = msg; + } + alert(errorMessage ?? 'Failed to reset password'); + return; + } + + setShowSuccess(true); + } catch (err) { + alert('Network error. Please try again.'); + } + }; + + if (!mounted) return null; + + return ( + <> + {showSuccess && ( + router.push('/login')} + /> + )} +
void handleSubmit(onSubmit)(e)} noValidate> + +
+ + +
+ {formState.errors.password && ( +
+ {formState.errors.password.message} +
+ )} +
+ +
+ + +
+ {formState.errors.confirmPassword && ( +
+ {formState.errors.confirmPassword.message} +
+ )} +
+ +
+ + ); +} diff --git a/src/app/(public)/reset-password/page.tsx b/src/app/(public)/reset-password/page.tsx index 43c31ca..654340e 100644 --- a/src/app/(public)/reset-password/page.tsx +++ b/src/app/(public)/reset-password/page.tsx @@ -1,18 +1,10 @@ 'use client'; -import { zodResolver } from '@hookform/resolvers/zod'; import Image from 'next/image'; import { useRouter } from 'next/navigation'; -import { useEffect, useState } from 'react'; -import { useForm } from 'react-hook-form'; import styled from 'styled-components'; -import SuccessModal from '@/app/(public)/forgot-password/components/SuccessModal'; -import FormField from '@/app/(public)/login/component/FormField'; -import Button from '@/app/(public)/login/ui/Button'; -import ControllerInput from '@/app/(public)/login/ui/controller/ControllerInput'; - -import { resetPasswordSchema } from './schemas/resetPasswordSchema'; +import ResetPasswordForm from './components/ResetPasswordForm'; const PageContainer = styled.div` min-height: 100vh; @@ -97,76 +89,11 @@ const Title = styled.h2` margin-bottom: 24px; `; -const EyeIcon = () => ( - - - - - - -); - -const EyeOffIcon = () => ( - - - - - - -); - export default function ResetPasswordPage() { - const [mounted, setMounted] = useState(false); - const [showSuccess, setShowSuccess] = useState(false); - const [showPassword, setShowPassword] = useState(false); - const [showConfirmPassword, setShowConfirmPassword] = useState(false); const router = useRouter(); - const { control, handleSubmit, formState } = useForm<{ - password: string; - confirmPassword: string; - }>({ - resolver: zodResolver(resetPasswordSchema), - defaultValues: { password: '', confirmPassword: '' }, - }); - - useEffect(() => { - setMounted(true); - }, []); - - const onSubmit = (data: { password: string; confirmPassword: string }) => { - // TODO: handle reset password logic (API call) - setShowSuccess(true); - }; - - if (!mounted) return null; return ( - {showSuccess && ( - router.push('/login')} - /> - )} router.back()}> @@ -189,99 +116,7 @@ export default function ResetPasswordPage() { Reset Your Password -
void handleSubmit(onSubmit)(e)} noValidate> - -
- - -
- {formState.errors.password && ( -
- {formState.errors.password.message} -
- )} - - -
- - -
- {formState.errors.confirmPassword && ( -
- {formState.errors.confirmPassword.message} -
- )} -
- - +
); From f10ae191221de76eae377d524cc29f74d7ca9eea Mon Sep 17 00:00:00 2001 From: Chloe-Xiao Date: Tue, 4 Nov 2025 13:06:04 +1100 Subject: [PATCH 3/8] fix/token-update --- .../reset-password/components/ResetPasswordForm.tsx | 9 +++++++-- src/app/(public)/reset-password/page.tsx | 6 ++++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/app/(public)/reset-password/components/ResetPasswordForm.tsx b/src/app/(public)/reset-password/components/ResetPasswordForm.tsx index 8e671a0..87ccfaa 100644 --- a/src/app/(public)/reset-password/components/ResetPasswordForm.tsx +++ b/src/app/(public)/reset-password/components/ResetPasswordForm.tsx @@ -48,7 +48,11 @@ const EyeOffIcon = () => ( ); -export default function ResetPasswordForm() { +interface ResetPasswordFormProps { + token: string; +} + +export default function ResetPasswordForm({ token }: ResetPasswordFormProps) { const [mounted, setMounted] = useState(false); const [showSuccess, setShowSuccess] = useState(false); const [showPassword, setShowPassword] = useState(false); @@ -72,10 +76,11 @@ export default function ResetPasswordForm() { }) => { try { // Call the backend API to reset password (mocked for now) - const res = await fetch('/api/auth/reset-password', { + const res = await fetch('/auth/reset-password', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ + token, password: data.password, confirmPassword: data.confirmPassword, }), diff --git a/src/app/(public)/reset-password/page.tsx b/src/app/(public)/reset-password/page.tsx index 654340e..87d5ae3 100644 --- a/src/app/(public)/reset-password/page.tsx +++ b/src/app/(public)/reset-password/page.tsx @@ -1,7 +1,7 @@ 'use client'; import Image from 'next/image'; -import { useRouter } from 'next/navigation'; +import { useRouter, useSearchParams } from 'next/navigation'; import styled from 'styled-components'; import ResetPasswordForm from './components/ResetPasswordForm'; @@ -91,6 +91,8 @@ const Title = styled.h2` export default function ResetPasswordPage() { const router = useRouter(); + const searchParams = useSearchParams(); + const token = searchParams.get('token') ?? ''; return ( @@ -116,7 +118,7 @@ export default function ResetPasswordPage() { Reset Your Password - + ); From bf2d7d8bc6b1ca05572405bbf56bae68c96c96f9 Mon Sep 17 00:00:00 2001 From: Chloe-Xiao Date: Tue, 11 Nov 2025 16:53:31 +1100 Subject: [PATCH 4/8] forgot-reset-link-hardcode --- src/app/(public)/forgot-password/page.tsx | 22 ++++++++++++++++--- .../components/ResetPasswordForm.tsx | 2 +- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/app/(public)/forgot-password/page.tsx b/src/app/(public)/forgot-password/page.tsx index 925207e..f0f7b37 100644 --- a/src/app/(public)/forgot-password/page.tsx +++ b/src/app/(public)/forgot-password/page.tsx @@ -135,11 +135,27 @@ export default function ForgotPasswordPage() { setMounted(true); }, []); - const onSubmit = (data: { email: string }) => { - // TODO: handle forgot password logic +const onSubmit = async (data: { email: string }) => { + try { + const res = await fetch('http://localhost:4000/api/auth/forgot-password', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ email: data.email }), + }); + + // Optionally handle error response + if (!res.ok) { + const result = await res.json(); + alert(result.message || 'Failed to send reset email'); + return; + } + setSentEmail(data.email); setShowSuccess(true); - }; + } catch (err) { + alert('Network error. Please try again.'); + } +}; if (!mounted) { return ( diff --git a/src/app/(public)/reset-password/components/ResetPasswordForm.tsx b/src/app/(public)/reset-password/components/ResetPasswordForm.tsx index 87ccfaa..262fe81 100644 --- a/src/app/(public)/reset-password/components/ResetPasswordForm.tsx +++ b/src/app/(public)/reset-password/components/ResetPasswordForm.tsx @@ -76,7 +76,7 @@ export default function ResetPasswordForm({ token }: ResetPasswordFormProps) { }) => { try { // Call the backend API to reset password (mocked for now) - const res = await fetch('/auth/reset-password', { + const res = await fetch('http://localhost:4000/api/auth/reset-password', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ From 36c0c30a9f9284855485424106dee4d19de53614 Mon Sep 17 00:00:00 2001 From: Mengying Xiao <113824239+ChloeXiao0409@users.noreply.github.com> Date: Mon, 1 Dec 2025 13:04:36 +1100 Subject: [PATCH 5/8] Update src/app/(public)/forgot-password/page.tsx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/app/(public)/forgot-password/page.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/(public)/forgot-password/page.tsx b/src/app/(public)/forgot-password/page.tsx index f0f7b37..df3d263 100644 --- a/src/app/(public)/forgot-password/page.tsx +++ b/src/app/(public)/forgot-password/page.tsx @@ -198,12 +198,12 @@ const onSubmit = async (data: { email: string }) => { viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" > - + From a835f340ca6cff3dd9955ff2e8f43cde60e610f2 Mon Sep 17 00:00:00 2001 From: Mengying Xiao <113824239+ChloeXiao0409@users.noreply.github.com> Date: Mon, 1 Dec 2025 13:04:48 +1100 Subject: [PATCH 6/8] Update src/app/(public)/forgot-password/components/SuccessModal.tsx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/app/(public)/forgot-password/components/SuccessModal.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/(public)/forgot-password/components/SuccessModal.tsx b/src/app/(public)/forgot-password/components/SuccessModal.tsx index c235c12..74347b3 100644 --- a/src/app/(public)/forgot-password/components/SuccessModal.tsx +++ b/src/app/(public)/forgot-password/components/SuccessModal.tsx @@ -41,9 +41,9 @@ const SuccessIcon = () => ( viewBox="0 0 60 60" xmlns="http://www.w3.org/2000/svg" > - + - + Date: Mon, 1 Dec 2025 13:06:27 +1100 Subject: [PATCH 7/8] Update src/app/(public)/forgot-password/page.tsx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/app/(public)/forgot-password/page.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/(public)/forgot-password/page.tsx b/src/app/(public)/forgot-password/page.tsx index df3d263..b2a27ef 100644 --- a/src/app/(public)/forgot-password/page.tsx +++ b/src/app/(public)/forgot-password/page.tsx @@ -126,7 +126,7 @@ export default function ForgotPasswordPage() { const router = useRouter(); const [showSuccess, setShowSuccess] = useState(false); const [sentEmail, setSentEmail] = useState(''); - const { control, handleSubmit, formState } = useForm<{ email: string }>({ + const { control, handleSubmit } = useForm<{ email: string }>({ resolver: zodResolver(forgotPasswordSchema), defaultValues: { email: '' }, }); From 5c6b8175ee46f6f684d6ea76a132f4f19117f042 Mon Sep 17 00:00:00 2001 From: Chloe-Xiao Date: Mon, 1 Dec 2025 16:02:07 +1100 Subject: [PATCH 8/8] fix-lint --- src/app/(public)/forgot-password/page.tsx | 41 ++++++++++++----------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/src/app/(public)/forgot-password/page.tsx b/src/app/(public)/forgot-password/page.tsx index b2a27ef..5cfe902 100644 --- a/src/app/(public)/forgot-password/page.tsx +++ b/src/app/(public)/forgot-password/page.tsx @@ -135,27 +135,30 @@ export default function ForgotPasswordPage() { setMounted(true); }, []); -const onSubmit = async (data: { email: string }) => { - try { - const res = await fetch('http://localhost:4000/api/auth/forgot-password', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ email: data.email }), - }); + const onSubmit = async (data: { email: string }) => { + try { + const res = await fetch( + 'http://localhost:4000/api/auth/forgot-password', + { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ email: data.email }), + }, + ); - // Optionally handle error response - if (!res.ok) { - const result = await res.json(); - alert(result.message || 'Failed to send reset email'); - return; - } + // Optionally handle error response + if (!res.ok) { + const result = (await res.json()) as { message?: string }; + alert(result.message ?? 'Failed to send reset email'); + return; + } - setSentEmail(data.email); - setShowSuccess(true); - } catch (err) { - alert('Network error. Please try again.'); - } -}; + setSentEmail(data.email); + setShowSuccess(true); + } catch { + alert('Network error. Please try again.'); + } + }; if (!mounted) { return (