mirror of
https://github.com/ragestudio/comty.git
synced 2025-06-11 03:24:16 +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
|
// © Jack Hanford https://github.com/hanford/react-drag-drawer
|
||||||
import React, { Component } from "react";
|
import React, { Component } from "react"
|
||||||
import { Motion, spring, presets } from "react-motion";
|
import { Motion, spring, presets } from "react-motion"
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types"
|
||||||
import document from "global/document";
|
import document from "global/document"
|
||||||
import Observer from "react-intersection-observer";
|
import Observer from "react-intersection-observer"
|
||||||
import { css } from "@emotion/css";
|
import { css } from "@emotion/css"
|
||||||
import { createPortal } from "react-dom";
|
import { createPortal } from "react-dom"
|
||||||
|
|
||||||
import {
|
import {
|
||||||
isDirectionBottom,
|
isDirectionBottom,
|
||||||
@ -67,11 +67,16 @@ export default class Drawer extends Component {
|
|||||||
listenersAttached: false
|
listenersAttached: false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DESKTOP_MODE = false
|
||||||
DRAGGER_HEIGHT_SIZE = 100
|
DRAGGER_HEIGHT_SIZE = 100
|
||||||
MAX_NEGATIVE_SCROLL = 5
|
MAX_NEGATIVE_SCROLL = 5
|
||||||
SCROLL_TO_CLOSE = 475
|
SCROLL_TO_CLOSE = 475
|
||||||
ALLOW_DRAWER_TRANSFORM = true
|
ALLOW_DRAWER_TRANSFORM = true
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.DESKTOP_MODE = !window.isMobile
|
||||||
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps, nextState) {
|
componentDidUpdate(prevProps, nextState) {
|
||||||
// in the process of closing the drawer
|
// in the process of closing the drawer
|
||||||
if (!this.props.open && prevProps.open) {
|
if (!this.props.open && prevProps.open) {
|
||||||
@ -333,6 +338,17 @@ export default class Drawer extends Component {
|
|||||||
this.ALLOW_DRAWER_TRANSFORM = inView
|
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()
|
preventDefault = (event) => event.preventDefault()
|
||||||
stopPropagation = (event) => event.stopPropagation()
|
stopPropagation = (event) => event.stopPropagation()
|
||||||
|
|
||||||
@ -392,7 +408,7 @@ export default class Drawer extends Component {
|
|||||||
<div
|
<div
|
||||||
id={id}
|
id={id}
|
||||||
style={containerStyle}
|
style={containerStyle}
|
||||||
onClick={this.props.onRequestClose}
|
onMouseDown={this.onClickOutside}
|
||||||
className={`${Container} ${containerElementClass} `}
|
className={`${Container} ${containerElementClass} `}
|
||||||
ref={getContainerRef}
|
ref={getContainerRef}
|
||||||
>
|
>
|
||||||
|
@ -1,58 +1,28 @@
|
|||||||
import React from "react"
|
import React from "react"
|
||||||
import * as antd from "antd"
|
import * as antd from "antd"
|
||||||
import { Icons } from "components/Icons"
|
import { Icons } from "components/Icons"
|
||||||
|
import classnames from "classnames"
|
||||||
|
|
||||||
import AuthModel from "models/auth"
|
import AuthModel from "models/auth"
|
||||||
import config from "config"
|
import config from "config"
|
||||||
|
|
||||||
import "./index.less"
|
import "./index.less"
|
||||||
|
|
||||||
const LoginSteps = {
|
const stepsOnError = {
|
||||||
"username": (props) => {
|
username: "This username or email is not exist",
|
||||||
const handleUpdate = (e) => {
|
password: "Password is incorrect",
|
||||||
props.onUpdate(e.target.value)
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return <div className="field">
|
const stepsValidations = {
|
||||||
<span><Icons.Mail /> Username or Email</span>
|
username: async (state) => {
|
||||||
|
const check = await AuthModel.usernameValidation(state.username).catch((err) => {
|
||||||
|
return {
|
||||||
|
exists: false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
<div className="component">
|
return check.exists
|
||||||
<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) => {
|
|
||||||
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 = {
|
const phasesToSteps = {
|
||||||
@ -72,6 +42,8 @@ export default class Login extends React.Component {
|
|||||||
phase: 0,
|
phase: 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
formRef = React.createRef()
|
||||||
|
|
||||||
handleFinish = async () => {
|
handleFinish = async () => {
|
||||||
const payload = {
|
const payload = {
|
||||||
username: this.state.loginInputs.username,
|
username: this.state.loginInputs.username,
|
||||||
@ -136,6 +108,14 @@ export default class Login extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onUpdateInput = (input, value) => {
|
onUpdateInput = (input, value) => {
|
||||||
|
// remove error from ref
|
||||||
|
this.formRef.current.setFields([
|
||||||
|
{
|
||||||
|
name: input,
|
||||||
|
errors: []
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
loginInputs: {
|
loginInputs: {
|
||||||
...this.state.loginInputs,
|
...this.state.loginInputs,
|
||||||
@ -144,19 +124,28 @@ export default class Login extends React.Component {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
renderCurrentInput = () => {
|
nextStep = async () => {
|
||||||
const { phase } = this.state
|
const phase = phasesToSteps[this.state.phase]
|
||||||
|
|
||||||
const step = phasesToSteps[phase]
|
if (typeof stepsValidations[phase] === "function") {
|
||||||
|
this.toogleLoading(true)
|
||||||
|
|
||||||
return React.createElement(LoginSteps[step], {
|
const result = await stepsValidations[phase](this.state.loginInputs)
|
||||||
onUpdate: (...props) => this.onUpdateInput(step, ...props),
|
|
||||||
next: this.nextStep,
|
this.toogleLoading(false)
|
||||||
defaultValue: this.state.loginInputs[step],
|
|
||||||
})
|
if (!result) {
|
||||||
}
|
this.formRef.current.setFields([
|
||||||
|
{
|
||||||
|
name: phase,
|
||||||
|
errors: [stepsOnError[phase]]
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
nextStep = () => {
|
|
||||||
const to = this.state.phase + 1
|
const to = this.state.phase + 1
|
||||||
|
|
||||||
if (!phasesToSteps[to]) {
|
if (!phasesToSteps[to]) {
|
||||||
@ -204,36 +193,71 @@ export default class Login extends React.Component {
|
|||||||
To continue to {config.app.siteName}
|
To continue to {config.app.siteName}
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
<div className="fields">
|
<antd.Form
|
||||||
{this.renderCurrentInput()}
|
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">
|
<antd.Form.Item
|
||||||
<div className="component-row">
|
name="password"
|
||||||
|
className={classnames(
|
||||||
|
"field",
|
||||||
{
|
{
|
||||||
this.state.phase > 0 && <antd.Button
|
["hidden"]: this.state.phase !== 1,
|
||||||
onClick={this.prevStep}
|
|
||||||
disabled={this.state.loading}
|
|
||||||
>
|
|
||||||
Back
|
|
||||||
</antd.Button>
|
|
||||||
}
|
}
|
||||||
<antd.Button
|
)}
|
||||||
onClick={this.nextStep}
|
>
|
||||||
disabled={!this.canNext() || this.state.loading}
|
<span><Icons.Lock /> Password</span>
|
||||||
loading={this.state.loading}
|
<antd.Input.Password
|
||||||
>
|
placeholder="********"
|
||||||
Continue
|
onChange={(e) => this.onUpdateInput("password", e.target.value)}
|
||||||
</antd.Button>
|
onPressEnter={this.nextStep}
|
||||||
</div>
|
/>
|
||||||
</div>
|
</antd.Form.Item>
|
||||||
|
</antd.Form>
|
||||||
|
|
||||||
<div className="field-error">
|
<div className="component-row">
|
||||||
{this.state.error}
|
{
|
||||||
</div>
|
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}>
|
<div className="field-error">
|
||||||
<a>You need a account?</a>
|
{this.state.error}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="field" onClick={this.onClickRegister}>
|
||||||
|
<a>You need a account?</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -60,23 +60,27 @@
|
|||||||
|
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
|
|
||||||
.component {
|
transition: all 150ms ease-in-out;
|
||||||
margin-top: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.component-row {
|
&.hidden {
|
||||||
display: inline-flex;
|
height: 0px;
|
||||||
flex-direction: row;
|
opacity: 0;
|
||||||
|
margin: 0;
|
||||||
justify-content: space-between;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.field-error {
|
.component-row {
|
||||||
color: red;
|
display: inline-flex;
|
||||||
font-size: 0.8rem;
|
flex-direction: row;
|
||||||
margin: 20px 0;
|
|
||||||
}
|
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 request from "../../handlers/request"
|
||||||
import SessionModel from "../session"
|
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 {
|
export default class AuthModel {
|
||||||
static login = async (payload) => {
|
static login = async (payload) => {
|
||||||
const response = await request({
|
const response = await request({
|
||||||
@ -50,4 +52,31 @@ export default class AuthModel {
|
|||||||
|
|
||||||
return response
|
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