improve login component

This commit is contained in:
srgooglo 2022-10-25 11:52:32 +00:00
parent ee0c7c4a2d
commit 255e6968c0
2 changed files with 261 additions and 105 deletions

View File

@ -7,85 +7,85 @@ import config from "config"
import "./index.less" import "./index.less"
const formInstance = [ const LoginSteps = {
{ "username": (props) => {
id: "username", const handleUpdate = (e) => {
element: { props.onUpdate(e.target.value)
component: "Input",
icon: "User",
placeholder: "Username",
props: {
autoCorrect: "off",
autoCapitalize: "none",
className: "login-form-username",
},
},
item: {
hasFeedback: true,
rules: [
{
required: true,
message: 'Please input your Username!',
},
],
} }
return <div className="field">
<span><Icons.Mail /> Username or Email</span>
<div className="component">
<antd.Input
name="username"
defaultValue={props.defaultValue}
placeholder="myusername / myemail@example.com"
onChange={handleUpdate}
onPressEnter={props.next}
autoCorrect="off"
autoCapitalize="none"
autoComplete="on"
autoFocus
/>
</div>
</div>
}, },
{ "password": (props) => {
id: "password", const handleUpdate = (e) => {
element: { props.onUpdate(e.target.value)
component: "Input",
icon: "Lock",
placeholder: "Password",
props: {
type: "password"
}
},
item: {
hasFeedback: true,
rules: [
{
required: true,
message: 'Please input your Password!',
},
],
} }
},
{ return <div className="field">
id: "login_btn", <span><Icons.Lock /> Password</span>
withValidation: true,
element: { <div className="component">
component: "Button", <antd.Input.Password
props: { name="password"
icon: "User", defaultValue={props.defaultValue}
children: "Login", onChange={handleUpdate}
type: "primary", onPressEnter={props.next}
htmlType: "submit" autoCorrect="off"
} autoCapitalize="none"
} autoComplete="on"
}, autoFocus
] />
</div>
</div>
}
}
const phasesToSteps = {
0: "username",
1: "password",
}
export default class Login extends React.Component { export default class Login extends React.Component {
static pageStatement = { static pageStatement = {
bottomBarAllowed: false bottomBarAllowed: false
} }
handleFinish = async (values, ctx) => { state = {
ctx.toogleValidation(true) loading: false,
loginInputs: {},
error: null,
phase: 0,
}
handleFinish = async () => {
const payload = { const payload = {
username: values.username, username: this.state.loginInputs.username,
password: values.password, password: this.state.loginInputs.password,
allowRegenerate: values.allowRegenerate,
} }
this.clearError()
this.toogleLoading(true)
this.props.sessionController.login(payload, (error, response) => { this.props.sessionController.login(payload, (error, response) => {
ctx.toogleValidation(false) this.toogleLoading(false)
ctx.clearErrors()
if (error) { if (error) {
ctx.shake("all") return this.onError(error)
return ctx.error("result", error)
} else { } else {
if (response.status === 200) { if (response.status === 200) {
this.onDone() this.onDone()
@ -104,31 +104,129 @@ export default class Login extends React.Component {
} }
} }
toogleLoading = (to) => {
if (typeof to === "undefined") {
to = !this.state.loading
}
this.setState({
loading: to
})
}
clearError = () => {
this.setState({
error: null
})
}
onError = (error) => {
this.setState({
error: error
})
}
onUpdateInput = (input, value) => {
this.setState({
loginInputs: {
...this.state.loginInputs,
[input]: value
}
})
}
renderCurrentInput = () => {
const { phase } = this.state
const step = phasesToSteps[phase]
return React.createElement(LoginSteps[step], {
onUpdate: (...props) => this.onUpdateInput(step, ...props),
next: this.nextStep,
defaultValue: this.state.loginInputs[step],
})
}
nextStep = () => {
const to = this.state.phase + 1
if (!phasesToSteps[to]) {
return this.handleFinish()
}
this.setState({
phase: to
})
}
prevStep = () => {
const to = this.state.phase - 1
if (!phasesToSteps[to]) {
console.warn("No step found for phase", to)
return
}
this.setState({
phase: to
})
}
canNext = () => {
if (this.state.loading) {
return false
}
const { phase } = this.state
const step = phasesToSteps[phase]
return !!this.state.loginInputs[step]
}
render() { render() {
return <div className="login"> return <div className="login_wrapper">
<div className="header"> <div className="content">
<div className="logo"> <h1>
<img src={config.logo?.full} /> Sign in
</h1>
<h3>
To continue to {config.app.siteName}
</h3>
<div className="fields">
{this.renderCurrentInput()}
<div className="field">
<div className="component-row">
{
this.state.phase > 0 && <antd.Button
onClick={this.prevStep}
disabled={this.state.loading}
>
Back
</antd.Button>
}
<antd.Button
onClick={this.nextStep}
disabled={!this.canNext() || this.state.loading}
loading={this.state.loading}
>
Continue
</antd.Button>
</div>
</div>
<div className="field-error">
{this.state.error}
</div>
<div className="field">
<a>You need a account?</a>
</div>
</div> </div>
</div> </div>
{this.props.session && <div className="session_available">
<h3><Icons.AlertCircle /> You already have a valid session.</h3>
<div className="session_card">
@{this.props.session.username}
</div>
<antd.Button
type="primary"
onClick={() => window.app.setLocation(config.app?.mainPath ?? "/home")} >
Go to home
</antd.Button>
</div>}
<FormGenerator
name="normal_login"
renderLoadingIcon
className="login-form"
items={formInstance}
onFinish={this.handleFinish}
/>
</div> </div>
} }
} }

View File

@ -1,16 +1,22 @@
.login { .login_wrapper {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
> div { width: 100%;
margin-bottom: 20px;
}
.header { .header {
display: flex;
flex-direction: column;
width: 100%;
.logo { .logo {
width: 15vw; max-width: 200px;
margin-bottom: 40px;
img { img {
width: 100%; width: 100%;
@ -18,21 +24,73 @@
} }
} }
} }
}
.login-form { h1 {
display: flex; font-family: "Space Grotesk", sans-serif;
flex-direction: column; margin: 0;
align-items: center; }
justify-content: center;
}
.login-form-username { h3 {
font-size: 20px!important; font-size: 0.8rem;
}
input {
padding: 20px!important; .content {
font-size: 20px!important; display: flex;
flex-direction: column;
width: 80%;
max-width: 600px;
margin: auto;
padding: 40px 0;
border-radius: 12px;
.fields {
display: flex;
flex-direction: column;
margin-top: 20px;
.field {
display: flex;
flex-direction: column;
margin-bottom: 20px;
.component {
margin-top: 5px;
}
.component-row {
display: inline-flex;
flex-direction: row;
justify-content: space-between;
}
}
.field-error {
color: red;
font-size: 0.8rem;
}
}
}
.ant-input {
padding: 5px;
border-radius: 8px;
}
.ant-input-affix-wrapper {
border-radius: 8px;
.ant-input {
border-radius: 0;
}
} }
} }
@ -55,9 +113,9 @@
height: fit-content; height: fit-content;
margin: 10px; margin: 10px;
padding: 5px 10px; padding: 5px 10px;
border: 1px solid #e5e5e5; border: 1px solid #e5e5e5;
border-radius: 8px; border-radius: 8px;
} }
} }