1+ import React from "react" ;
2+
13import Button from "@/common/Button" ;
2- import FormField from "@/common/FormField " ;
4+ import CaptchaBox from "@/common/CaptchaBox " ;
35import Card from "@/common/Card" ;
4- import PasswordInput from "@/common/PasswordInput " ;
6+ import FormField from "@/common/FormField " ;
57import Logo from "@/common/Logo" ;
6- import CaptchaBox from "@/common/CaptchaBox " ;
8+ import PasswordInput from "@/common/PasswordInput " ;
79import Separator from "@/common/Separator" ;
8- import { useLoginManager } from "./hooks/useLoginManager" ;
9- import { useLoginForm } from "./hooks/useLoginForm" ;
1010import SocialProviderButton from "@/common/SocialProviderButton" ;
11- import { getIcon } from "@/utils/iconUtils" ;
12- import Alert from "@/common/Alert" ;
11+
1312import { BrandingProvider } from "@/context/BrandingProvider" ;
13+ import { getIcon } from "@/utils/iconUtils" ;
14+ import { getSdkErrorForField } from "@/utils/errorUtils" ;
15+ import { useLoginForm } from "./hooks/useLoginForm" ;
16+ import { useLoginManager } from "./hooks/useLoginManager" ;
17+ import type { SdkError } from "@/utils/errorUtils" ;
1418
1519const LoginScreen : React . FC = ( ) => {
1620 const { handleLogin, handleSocialLogin, loginInstance } = useLoginManager ( ) ;
1721 const { usernameRef, passwordRef, captchaRef, getFormValues } =
1822 useLoginForm ( ) ;
1923
20- const onLoginClick = ( e : React . FormEvent ) => {
21- e . preventDefault ( ) ;
22- const { username, password, captcha } = getFormValues ( ) ;
23- handleLogin ( username , password , captcha ) ;
24- } ;
25-
26- // CAPTCHA related variables
2724 const texts = loginInstance ?. screen ?. texts || { } ;
25+ // IMP: This is a to set the page title dynamically
26+ const pageTitle = texts ?. pageTitle || "Login" ;
27+ document . title = pageTitle ;
28+
29+ const sdkErrors : SdkError [ ] = ( loginInstance ?. transaction ?. errors ||
30+ [ ] ) as SdkError [ ] ;
2831 const isCaptchaAvailable = ! ! loginInstance ?. screen ?. captcha ;
2932 const captchaImage = loginInstance ?. screen ?. captcha ?. image || "" ;
3033 const captchaLabelText =
3134 ( texts . captchaCodePlaceholder || "Enter the code shown above" ) + "*" ;
3235
33- // Prepare global messages for the Alert component
34- // Attempt to get screen messages, default to empty array if undefined
35- const screenMessages : { type : string ; text : string } [ ] =
36- ( loginInstance ?. screen as any ) ?. messages || [ ] ;
37- const firstErrorMessage = screenMessages . find (
38- ( m : { type : string ; text : string } ) => m . type === "error" ,
39- ) ;
36+ const onLoginClick = ( e : React . FormEvent ) => {
37+ e . preventDefault ( ) ;
38+ const { username, password, captcha } = getFormValues ( ) ;
39+ handleLogin ( username , password , captcha ) ;
40+ } ;
4041
41- // Additionally, check for a general pageError from the loginInstance
42- const pageError = ( loginInstance as any ) ?. pageError ;
43- let errorMessageToShow : string | undefined = firstErrorMessage ?. text ;
44- if ( ! errorMessageToShow && typeof pageError === "string" && pageError ) {
45- errorMessageToShow = pageError ;
46- }
47- // TODO: Potentially handle other message types (warning, info, success) or multiple messages.
42+ const getFieldError = ( fieldName : string ) : string | undefined => {
43+ return getSdkErrorForField ( fieldName , sdkErrors ) ;
44+ } ;
4845
4946 return (
5047 < BrandingProvider screenInstance = { loginInstance } >
51- < div className = "min-h-screen flex items-center justify-center p-4" >
48+ < div className = "min-h-screen flex items-center justify-center px-10 py-20" >
49+ { /* Parent Card */ }
5250 < Card className = "w-full max-w-[400px]" >
51+ { /* Header section */ }
5352 < Logo imageClassName = "h-13" />
5453 < h1 className = "text-2xl font-normal text-center text-text-default mt-6 mb-4" >
55- { loginInstance ?. screen ?. texts ?. title || "Welcome" }
54+ { loginInstance ?. screen ?. texts ?. title }
5655 </ h1 >
5756 < p className = "text-center text-text-default text-sm mb-4" >
58- { loginInstance ?. screen ?. texts ?. description || "Log in to continue." }
57+ { loginInstance ?. screen ?. texts ?. description }
5958 </ p >
6059
61- { errorMessageToShow && (
62- < Alert
63- type = "error"
64- message = { errorMessageToShow }
65- title = {
66- loginInstance ?. screen ?. texts ?. alertListTitle ||
67- texts . titleLoginError ||
68- "Login Error"
69- }
70- className = "mb-4"
71- />
72- ) }
73-
60+ { /* Login form */ }
7461 < form onSubmit = { onLoginClick } className = "space-y-4" >
7562 < FormField
7663 className = "mb-4"
7764 labelProps = { {
78- children : `${ loginInstance ?. screen ?. texts ?. phoneOrUsernameOrEmailPlaceholder || "Phone or Username or Email" } *` ,
65+ children : `${ loginInstance ?. screen ?. texts ?. usernameOrEmailPlaceholder } *` ,
7966 htmlFor : "email-login" ,
8067 } }
8168 inputProps = { {
@@ -85,18 +72,21 @@ const LoginScreen: React.FC = () => {
8572 ref : usernameRef ,
8673 placeholder : "\u00A0" ,
8774 autoComplete : "email" ,
75+ required : true ,
8876 } }
89- error = "this is a dummy error message for testing"
77+ error = { getFieldError ( "username" ) || getFieldError ( "email" ) }
9078 />
9179
9280 < PasswordInput
9381 className = "mb-4"
94- label = { `${ loginInstance ?. screen ?. texts ? .passwordPlaceholder || "Password" } *` }
82+ label = { `${ texts . passwordPlaceholder || "Password" } *` }
9583 name = "password"
9684 inputProps = { {
9785 ref : passwordRef ,
9886 autoComplete : "current-password" ,
87+ required : true ,
9988 } }
89+ error = { getFieldError ( "password" ) }
10090 />
10191
10292 { isCaptchaAvailable && captchaImage && (
@@ -107,33 +97,49 @@ const LoginScreen: React.FC = () => {
10797 imageUrl = { captchaImage }
10898 inputProps = { {
10999 ref : captchaRef ,
100+ required : isCaptchaAvailable ,
110101 } }
111102 imageClassName = "h-16"
103+ error = { getFieldError ( "captcha" ) }
112104 />
113105 ) }
114106 < div className = "mt-6 text-left" >
115- < Button variant = "link" size = "sm" className = "p-1 font-bold" >
116- { loginInstance ?. screen ?. texts ?. forgotPasswordText ||
117- "Forgot password?" }
118- </ Button >
107+ { loginInstance ?. screen ?. links ?. reset_password &&
108+ loginInstance ?. screen ?. texts ?. forgotPasswordText && (
109+ < a
110+ href = { loginInstance . screen . links . reset_password }
111+ className = "text-sm text-link hover:text-link-hover active:text-link-pressed font-bold p-1"
112+ >
113+ { loginInstance . screen . texts . forgotPasswordText }
114+ </ a >
115+ ) }
119116 </ div >
117+
118+ { /* Login button */ }
120119 < Button type = "submit" fullWidth >
121- { loginInstance ?. screen ?. texts ?. buttonText || "Continue" }
120+ { loginInstance ?. screen ?. texts ?. buttonText }
122121 </ Button >
123122 </ form >
124123
124+ { /* Footer text */ }
125125 < div className = "mt-4 text-left" >
126126 < span className = "text-sm" >
127127 { loginInstance ?. screen ?. texts ?. dontHaveAccountText ||
128128 "Don't have an account?" }
129129 </ span > { " " }
130- < Button variant = "link" size = "sm" className = "px-1" >
131- { loginInstance ?. screen ?. texts ?. signUpText || "Sign up" }
132- </ Button >
130+ { loginInstance ?. screen ?. links ?. signup && (
131+ < a
132+ href = { loginInstance . screen . links . signup }
133+ className = "text-sm font-bold text-link hover:text-link-hover active:text-link-pressed px-1"
134+ >
135+ { loginInstance . screen . texts ?. signUpText || "Sign up" }
136+ </ a >
137+ ) }
133138 </ div >
134139
135140 < Separator text = "OR" />
136141
142+ { /* Social login buttons */ }
137143 < div className = "space-y-3" >
138144 { loginInstance ?. transaction ?. alternateConnections ?. map (
139145 ( connection ) => (
0 commit comments