88
99import React , { useState } from 'react' ;
1010import { useAuth } from './useAuth' ;
11+ import type { AuthLinkComponentProps } from './types' ;
12+
13+ /** Translatable labels for the ForgotPasswordForm */
14+ export interface ForgotPasswordFormLabels {
15+ emailLabel ?: string ;
16+ emailPlaceholder ?: string ;
17+ submitButton ?: string ;
18+ submittingButton ?: string ;
19+ successTitle ?: string ;
20+ successDescription ?: string ;
21+ backToSignInText ?: string ;
22+ rememberPasswordText ?: string ;
23+ signInText ?: string ;
24+ }
1125
1226export interface ForgotPasswordFormProps {
1327 /** Callback on successful submission */
@@ -20,8 +34,16 @@ export interface ForgotPasswordFormProps {
2034 title ?: string ;
2135 /** Custom description */
2236 description ?: string ;
37+ /** Custom link component for SPA navigation (e.g. React Router's Link) */
38+ linkComponent ?: React . ComponentType < AuthLinkComponentProps > ;
39+ /** Override default labels for i18n */
40+ labels ?: ForgotPasswordFormLabels ;
2341}
2442
43+ const DefaultLink = ( { href, className, children } : AuthLinkComponentProps ) => (
44+ < a href = { href } className = { className } > { children } </ a >
45+ ) ;
46+
2547/**
2648 * Forgot password form component.
2749 * Sends a password reset email to the user.
@@ -40,12 +62,26 @@ export function ForgotPasswordForm({
4062 loginUrl = '/login' ,
4163 title = 'Reset your password' ,
4264 description = 'Enter your email address and we\'ll send you a link to reset your password' ,
65+ linkComponent : LinkComp = DefaultLink ,
66+ labels = { } ,
4367} : ForgotPasswordFormProps ) {
4468 const { forgotPassword, isLoading } = useAuth ( ) ;
4569 const [ email , setEmail ] = useState ( '' ) ;
4670 const [ error , setError ] = useState < string | null > ( null ) ;
4771 const [ submitted , setSubmitted ] = useState ( false ) ;
4872
73+ const l = {
74+ emailLabel : labels . emailLabel ?? 'Email' ,
75+ emailPlaceholder : labels . emailPlaceholder ?? 'name@example.com' ,
76+ submitButton : labels . submitButton ?? 'Send Reset Link' ,
77+ submittingButton : labels . submittingButton ?? 'Sending...' ,
78+ successTitle : labels . successTitle ?? 'Check your email' ,
79+ successDescription : labels . successDescription ?? "We've sent a password reset link to {{email}}. Please check your inbox." ,
80+ backToSignInText : labels . backToSignInText ?? 'Back to sign in' ,
81+ rememberPasswordText : labels . rememberPasswordText ?? 'Remember your password?' ,
82+ signInText : labels . signInText ?? 'Sign in' ,
83+ } ;
84+
4985 const handleSubmit = async ( e : React . FormEvent ) => {
5086 e . preventDefault ( ) ;
5187 setError ( null ) ;
@@ -62,28 +98,28 @@ export function ForgotPasswordForm({
6298 } ;
6399
64100 if ( submitted ) {
101+ const successMsg = l . successDescription . includes ( '{{email}}' )
102+ ? l . successDescription . replace ( '{{email}}' , email )
103+ : `${ l . successDescription } ${ email } ` ;
65104 return (
66- < div className = "mx-auto flex w-full flex-col justify-center space-y-6 sm:w-[350px ]" >
105+ < div className = "mx-auto flex w-full flex-col justify-center space-y-6 sm:w-[380px ]" >
67106 < div className = "flex flex-col space-y-2 text-center" >
68- < h1 className = "text-2xl font-semibold tracking-tight" > Check your email</ h1 >
69- < p className = "text-sm text-muted-foreground" >
70- We've sent a password reset link to < strong > { email } </ strong > .
71- Please check your inbox and follow the instructions.
72- </ p >
107+ < h1 className = "text-2xl font-semibold tracking-tight" > { l . successTitle } </ h1 >
108+ < p className = "text-sm text-muted-foreground" > { successMsg } </ p >
73109 </ div >
74110 { loginUrl && (
75111 < p className = "px-8 text-center text-sm text-muted-foreground" >
76- < a href = { loginUrl } className = "text-primary underline-offset-4 hover:underline" >
77- Back to sign in
78- </ a >
112+ < LinkComp href = { loginUrl } className = "text-primary underline-offset-4 hover:underline" >
113+ { l . backToSignInText }
114+ </ LinkComp >
79115 </ p >
80116 ) }
81117 </ div >
82118 ) ;
83119 }
84120
85121 return (
86- < div className = "mx-auto flex w-full flex-col justify-center space-y-6 sm:w-[350px ]" >
122+ < div className = "mx-auto flex w-full flex-col justify-center space-y-6 sm:w-[380px ]" >
87123 < div className = "flex flex-col space-y-2 text-center" >
88124 < h1 className = "text-2xl font-semibold tracking-tight" > { title } </ h1 >
89125 < p className = "text-sm text-muted-foreground" > { description } </ p >
@@ -98,12 +134,12 @@ export function ForgotPasswordForm({
98134
99135 < div className = "space-y-2" >
100136 < label htmlFor = "forgot-email" className = "text-sm font-medium leading-none" >
101- Email
137+ { l . emailLabel }
102138 </ label >
103139 < input
104140 id = "forgot-email"
105141 type = "email"
106- placeholder = "name@example.com"
142+ placeholder = { l . emailPlaceholder }
107143 value = { email }
108144 onChange = { ( e ) => setEmail ( e . target . value ) }
109145 required
@@ -118,16 +154,16 @@ export function ForgotPasswordForm({
118154 disabled = { isLoading }
119155 className = "inline-flex h-10 w-full items-center justify-center rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground ring-offset-background transition-colors hover:bg-primary/90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50"
120156 >
121- { isLoading ? 'Sending...' : 'Send Reset Link' }
157+ { isLoading ? l . submittingButton : l . submitButton }
122158 </ button >
123159 </ form >
124160
125161 { loginUrl && (
126162 < p className = "px-8 text-center text-sm text-muted-foreground" >
127- Remember your password? { ' ' }
128- < a href = { loginUrl } className = "text-primary underline-offset-4 hover:underline" >
129- Sign in
130- </ a >
163+ { l . rememberPasswordText } { ' ' }
164+ < LinkComp href = { loginUrl } className = "text-primary underline-offset-4 hover:underline" >
165+ { l . signInText }
166+ </ LinkComp >
131167 </ p >
132168 ) }
133169 </ div >
0 commit comments