mirror of
https://github.com/ragestudio/comty.git
synced 2025-06-10 02:54:15 +00:00
Merge pull request #103 from ragestudio/improve-login-form
Improve login form
This commit is contained in:
commit
a564e62fbf
@ -1,11 +1,11 @@
|
||||
// © Jack Hanford https://github.com/hanford/react-drag-drawer
|
||||
import React, { Component } from "react";
|
||||
import { Motion, spring, presets } from "react-motion";
|
||||
import PropTypes from "prop-types";
|
||||
import document from "global/document";
|
||||
import Observer from "react-intersection-observer";
|
||||
import { css } from "@emotion/css";
|
||||
import { createPortal } from "react-dom";
|
||||
import React, { Component } from "react"
|
||||
import { Motion, spring, presets } from "react-motion"
|
||||
import PropTypes from "prop-types"
|
||||
import document from "global/document"
|
||||
import Observer from "react-intersection-observer"
|
||||
import { css } from "@emotion/css"
|
||||
import { createPortal } from "react-dom"
|
||||
|
||||
import {
|
||||
isDirectionBottom,
|
||||
@ -67,11 +67,16 @@ export default class Drawer extends Component {
|
||||
listenersAttached: false
|
||||
}
|
||||
|
||||
DESKTOP_MODE = false
|
||||
DRAGGER_HEIGHT_SIZE = 100
|
||||
MAX_NEGATIVE_SCROLL = 5
|
||||
SCROLL_TO_CLOSE = 475
|
||||
ALLOW_DRAWER_TRANSFORM = true
|
||||
|
||||
componentDidMount() {
|
||||
this.DESKTOP_MODE = !window.isMobile
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps, nextState) {
|
||||
// in the process of closing the drawer
|
||||
if (!this.props.open && prevProps.open) {
|
||||
@ -333,6 +338,17 @@ export default class Drawer extends Component {
|
||||
this.ALLOW_DRAWER_TRANSFORM = inView
|
||||
}
|
||||
|
||||
onClickOutside = (event) => {
|
||||
if (!this.props.allowClose) {
|
||||
return false
|
||||
}
|
||||
|
||||
// check if is clicking outside main component
|
||||
if (this.drawer && !this.drawer.contains(event.target)) {
|
||||
this.props.onRequestClose(this)
|
||||
}
|
||||
}
|
||||
|
||||
preventDefault = (event) => event.preventDefault()
|
||||
stopPropagation = (event) => event.stopPropagation()
|
||||
|
||||
@ -392,7 +408,7 @@ export default class Drawer extends Component {
|
||||
<div
|
||||
id={id}
|
||||
style={containerStyle}
|
||||
onClick={this.props.onRequestClose}
|
||||
onMouseDown={this.onClickOutside}
|
||||
className={`${Container} ${containerElementClass} `}
|
||||
ref={getContainerRef}
|
||||
>
|
||||
|
@ -1,58 +1,28 @@
|
||||
import React from "react"
|
||||
import * as antd from "antd"
|
||||
import { Icons } from "components/Icons"
|
||||
import classnames from "classnames"
|
||||
|
||||
import AuthModel from "models/auth"
|
||||
import config from "config"
|
||||
|
||||
import "./index.less"
|
||||
|
||||
const LoginSteps = {
|
||||
"username": (props) => {
|
||||
const handleUpdate = (e) => {
|
||||
props.onUpdate(e.target.value)
|
||||
}
|
||||
const stepsOnError = {
|
||||
username: "This username or email is not exist",
|
||||
password: "Password is incorrect",
|
||||
}
|
||||
|
||||
return <div className="field">
|
||||
<span><Icons.Mail /> Username or Email</span>
|
||||
const stepsValidations = {
|
||||
username: async (state) => {
|
||||
const check = await AuthModel.usernameValidation(state.username).catch((err) => {
|
||||
return {
|
||||
exists: false
|
||||
}
|
||||
})
|
||||
|
||||
<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>
|
||||
return check.exists
|
||||
},
|
||||
"password": (props) => {
|
||||
const handleUpdate = (e) => {
|
||||
props.onUpdate(e.target.value)
|
||||
}
|
||||
|
||||
return <div className="field">
|
||||
<span><Icons.Lock /> Password</span>
|
||||
|
||||
<div className="component">
|
||||
<antd.Input.Password
|
||||
name="password"
|
||||
defaultValue={props.defaultValue}
|
||||
onChange={handleUpdate}
|
||||
onPressEnter={props.next}
|
||||
autoCorrect="off"
|
||||
autoCapitalize="none"
|
||||
autoComplete="on"
|
||||
autoFocus
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
const phasesToSteps = {
|
||||
@ -72,6 +42,8 @@ export default class Login extends React.Component {
|
||||
phase: 0,
|
||||
}
|
||||
|
||||
formRef = React.createRef()
|
||||
|
||||
handleFinish = async () => {
|
||||
const payload = {
|
||||
username: this.state.loginInputs.username,
|
||||
@ -136,6 +108,14 @@ export default class Login extends React.Component {
|
||||
}
|
||||
|
||||
onUpdateInput = (input, value) => {
|
||||
// remove error from ref
|
||||
this.formRef.current.setFields([
|
||||
{
|
||||
name: input,
|
||||
errors: []
|
||||
}
|
||||
])
|
||||
|
||||
this.setState({
|
||||
loginInputs: {
|
||||
...this.state.loginInputs,
|
||||
@ -144,19 +124,28 @@ export default class Login extends React.Component {
|
||||
})
|
||||
}
|
||||
|
||||
renderCurrentInput = () => {
|
||||
const { phase } = this.state
|
||||
nextStep = async () => {
|
||||
const phase = phasesToSteps[this.state.phase]
|
||||
|
||||
const step = phasesToSteps[phase]
|
||||
if (typeof stepsValidations[phase] === "function") {
|
||||
this.toogleLoading(true)
|
||||
|
||||
return React.createElement(LoginSteps[step], {
|
||||
onUpdate: (...props) => this.onUpdateInput(step, ...props),
|
||||
next: this.nextStep,
|
||||
defaultValue: this.state.loginInputs[step],
|
||||
})
|
||||
}
|
||||
const result = await stepsValidations[phase](this.state.loginInputs)
|
||||
|
||||
this.toogleLoading(false)
|
||||
|
||||
if (!result) {
|
||||
this.formRef.current.setFields([
|
||||
{
|
||||
name: phase,
|
||||
errors: [stepsOnError[phase]]
|
||||
},
|
||||
])
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
nextStep = () => {
|
||||
const to = this.state.phase + 1
|
||||
|
||||
if (!phasesToSteps[to]) {
|
||||
@ -204,36 +193,71 @@ export default class Login extends React.Component {
|
||||
To continue to {config.app.siteName}
|
||||
</h3>
|
||||
|
||||
<div className="fields">
|
||||
{this.renderCurrentInput()}
|
||||
<antd.Form
|
||||
name="login"
|
||||
className="fields"
|
||||
autoCorrect="off"
|
||||
autoCapitalize="none"
|
||||
autoComplete="on"
|
||||
onFinish={this.handleFinish}
|
||||
ref={this.formRef}
|
||||
>
|
||||
<antd.Form.Item
|
||||
name="username"
|
||||
className="field"
|
||||
>
|
||||
<span><Icons.Mail /> Username or Email</span>
|
||||
<antd.Input
|
||||
placeholder="myusername / myemail@example.com"
|
||||
onChange={(e) => this.onUpdateInput("username", e.target.value)}
|
||||
onPressEnter={this.nextStep}
|
||||
disabled={this.state.phase !== 0}
|
||||
autoFocus
|
||||
/>
|
||||
</antd.Form.Item>
|
||||
|
||||
<div className="field">
|
||||
<div className="component-row">
|
||||
<antd.Form.Item
|
||||
name="password"
|
||||
className={classnames(
|
||||
"field",
|
||||
{
|
||||
this.state.phase > 0 && <antd.Button
|
||||
onClick={this.prevStep}
|
||||
disabled={this.state.loading}
|
||||
>
|
||||
Back
|
||||
</antd.Button>
|
||||
["hidden"]: this.state.phase !== 1,
|
||||
}
|
||||
<antd.Button
|
||||
onClick={this.nextStep}
|
||||
disabled={!this.canNext() || this.state.loading}
|
||||
loading={this.state.loading}
|
||||
>
|
||||
Continue
|
||||
</antd.Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
>
|
||||
<span><Icons.Lock /> Password</span>
|
||||
<antd.Input.Password
|
||||
placeholder="********"
|
||||
onChange={(e) => this.onUpdateInput("password", e.target.value)}
|
||||
onPressEnter={this.nextStep}
|
||||
/>
|
||||
</antd.Form.Item>
|
||||
</antd.Form>
|
||||
|
||||
<div className="field-error">
|
||||
{this.state.error}
|
||||
</div>
|
||||
<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 className="field" onClick={this.onClickRegister}>
|
||||
<a>You need a account?</a>
|
||||
</div>
|
||||
<div className="field-error">
|
||||
{this.state.error}
|
||||
</div>
|
||||
|
||||
<div className="field" onClick={this.onClickRegister}>
|
||||
<a>You need a account?</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -60,23 +60,27 @@
|
||||
|
||||
margin-bottom: 20px;
|
||||
|
||||
.component {
|
||||
margin-top: 5px;
|
||||
}
|
||||
transition: all 150ms ease-in-out;
|
||||
|
||||
.component-row {
|
||||
display: inline-flex;
|
||||
flex-direction: row;
|
||||
|
||||
justify-content: space-between;
|
||||
&.hidden {
|
||||
height: 0px;
|
||||
opacity: 0;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.field-error {
|
||||
color: red;
|
||||
font-size: 0.8rem;
|
||||
margin: 20px 0;
|
||||
}
|
||||
.component-row {
|
||||
display: inline-flex;
|
||||
flex-direction: row;
|
||||
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.field-error {
|
||||
color: red;
|
||||
font-size: 0.8rem;
|
||||
margin: 20px 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,8 @@
|
||||
import request from "../../handlers/request"
|
||||
import SessionModel from "../session"
|
||||
|
||||
const emailRegex = new RegExp(/^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/)
|
||||
|
||||
export default class AuthModel {
|
||||
static login = async (payload) => {
|
||||
const response = await request({
|
||||
@ -50,4 +52,31 @@ export default class AuthModel {
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
static usernameValidation = async (username) => {
|
||||
let payload = {}
|
||||
|
||||
// check if usename arguemnt is an email
|
||||
if (emailRegex.test(username)) {
|
||||
payload.email = username
|
||||
} else {
|
||||
payload.username = username
|
||||
}
|
||||
|
||||
const response = await request({
|
||||
method: "get",
|
||||
url: "/auth/login/validation",
|
||||
params: payload,
|
||||
}).catch((error) => {
|
||||
console.error(error)
|
||||
|
||||
return false
|
||||
})
|
||||
|
||||
if (!response) {
|
||||
throw new Error("Unable to validate user")
|
||||
}
|
||||
|
||||
return response.data
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
import { User } from "@models"
|
||||
|
||||
export default {
|
||||
method: "GET",
|
||||
route: "/login/validation",
|
||||
fn: async function (req, res) {
|
||||
// just check if the provided user or/and email exists, if is return false, otherwise return true
|
||||
const { username, email } = req.query
|
||||
|
||||
if (!username && !email) {
|
||||
return res.status(400).json({
|
||||
message: "Missing username or email",
|
||||
})
|
||||
}
|
||||
|
||||
const user = await User.findOne({
|
||||
$or: [
|
||||
{ username: username },
|
||||
{ email: email },
|
||||
]
|
||||
}).catch((error) => {
|
||||
return false
|
||||
})
|
||||
|
||||
if (user) {
|
||||
return res.json({
|
||||
message: "User already exists",
|
||||
exists: true,
|
||||
})
|
||||
} else {
|
||||
return res.json({
|
||||
message: "User doesn't exists",
|
||||
exists: false,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user