Refactor and redesign LoginModal

This commit is contained in:
Justin Edmund 2022-02-28 16:40:16 -08:00
parent 35cf0ee369
commit 1c7e602464
2 changed files with 109 additions and 75 deletions

View file

@ -1,14 +1,31 @@
.LoginForm { .Login.Dialog form {
#fields { display: flex;
display: flex; flex-direction: column;
flex-direction: column; gap: $unit / 2;
gap: $unit; margin-bottom: $unit;
}
.fieldset {
display: flex;
flex-direction: column;
gap: 4px;
margin-bottom: 8px;
}
}
.Button {
font-size: $font-regular;
padding: ($unit * 1.5) ($unit * 2);
width: 100%;
&.btn-disabled {
background: $grey-90;
color: $grey-70;
cursor: not-allowed;
}
&:not(.btn-disabled) {
background: $grey-90;
color: $grey-40;
&:hover {
background: $grey-80;
}
}
}
input {
background: $grey-90;
}
}

View file

@ -1,21 +1,18 @@
import React, { useState } from 'react' import React, { useState } from 'react'
import { withCookies, Cookies } from 'react-cookie' import { useCookies } from 'react-cookie'
import { createPortal } from 'react-dom'
import * as Dialog from '@radix-ui/react-dialog'
import api from '~utils/api' import api from '~utils/api'
import { accountState } from '~utils/accountState' import { accountState } from '~utils/accountState'
import Button from '~components/Button' import Button from '~components/Button'
import Fieldset from '~components/Fieldset' import Fieldset from '~components/Fieldset'
import Modal from '~components/Modal'
import Overlay from '~components/Overlay'
import CrossIcon from '~public/icons/Cross.svg'
import './index.scss' import './index.scss'
interface Props { interface Props {}
cookies: Cookies
close: () => void
}
interface ErrorMap { interface ErrorMap {
[index: string]: string [index: string]: string
@ -26,31 +23,40 @@ interface ErrorMap {
const emailRegex = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ const emailRegex = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
const LoginModal = (props: Props) => { const LoginModal = (props: Props) => {
const emailInput: React.RefObject<HTMLInputElement> = React.createRef() // Set up form states and error handling
const passwordInput: React.RefObject<HTMLInputElement> = React.createRef()
const form: React.RefObject<HTMLInputElement>[] = [emailInput, passwordInput]
const [formValid, setFormValid] = useState(false) const [formValid, setFormValid] = useState(false)
const [errors, setErrors] = useState<ErrorMap>({ const [errors, setErrors] = useState<ErrorMap>({
email: '', email: '',
password: '' password: ''
}) })
function handleChange(event: React.ChangeEvent<HTMLInputElement>) { // Cookies
event.preventDefault() const [cookies, setCookies] = useCookies()
// States
const [open, setOpen] = useState(false)
// Set up form refs
const emailInput: React.RefObject<HTMLInputElement> = React.createRef()
const passwordInput: React.RefObject<HTMLInputElement> = React.createRef()
const form: React.RefObject<HTMLInputElement>[] = [emailInput, passwordInput]
function handleChange(event: React.ChangeEvent<HTMLInputElement>) {
const { name, value } = event.target const { name, value } = event.target
let newErrors = errors let newErrors = {...errors}
switch(name) { switch(name) {
case 'email': case 'email':
errors.email = emailRegex.test(value) if (value.length == 0)
? '' newErrors.email = 'Please enter your email'
: 'That email address is not valid' else if (!emailRegex.test(value))
newErrors.email = 'That email address is not valid'
else
newErrors.email = ''
break break
case 'password': case 'password':
errors.password = value.length == 0 newErrors.password = value.length == 0
? 'Please enter your password' ? 'Please enter your password'
: '' : ''
break break
@ -60,10 +66,10 @@ const LoginModal = (props: Props) => {
} }
setErrors(newErrors) setErrors(newErrors)
setFormValid(validateForm()) setFormValid(validateForm(newErrors))
} }
function validateForm() { function validateForm(errors: ErrorMap) {
let valid = true let valid = true
Object.values(form).forEach( Object.values(form).forEach(
@ -73,11 +79,11 @@ const LoginModal = (props: Props) => {
Object.values(errors).forEach( Object.values(errors).forEach(
(error) => error.length > 0 && (valid = false) (error) => error.length > 0 && (valid = false)
) )
return valid return valid
} }
function submit(event: React.FormEvent) { function login(event: React.FormEvent) {
event.preventDefault() event.preventDefault()
const body = { const body = {
@ -89,65 +95,76 @@ const LoginModal = (props: Props) => {
if (formValid) { if (formValid) {
api.login(body) api.login(body)
.then((response) => { .then((response) => {
const cookies = props.cookies
const cookieObj = { const cookieObj = {
user_id: response.data.user.id, user_id: response.data.user.id,
username: response.data.user.username, username: response.data.user.username,
access_token: response.data.access_token access_token: response.data.access_token
} }
cookies.set('user', cookieObj, { path: '/'}) setCookies('user', cookieObj, { path: '/'})
accountState.account.authorized = true accountState.account.authorized = true
accountState.account.user = { accountState.account.user = {
id: cookieObj.user_id, id: cookieObj.user_id,
username: cookieObj.username username: cookieObj.username
} }
props.close() setOpen(false)
}, (error) => { }, (error) => {
console.error(error) console.error(error)
}) })
} }
} }
return ( function openChange(open: boolean) {
createPortal( setOpen(open)
<div> setErrors({
<Modal email: '',
title="Log in" password: ''
styleName="LoginForm" })
close={ () => {} } }
>
<form className="form" onSubmit={submit}>
<div id="fields">
<Fieldset
fieldName="email"
placeholder="Email address"
onChange={handleChange}
error={errors.email}
ref={emailInput}
/>
<Fieldset return (
fieldName="password" <Dialog.Root open={open} onOpenChange={openChange}>
placeholder="Password" <Dialog.Trigger asChild>
onChange={handleChange} <li className="MenuItem">
error={errors.password} <span>Log in</span>
ref={passwordInput} </li>
/> </Dialog.Trigger>
</div> <Dialog.Portal>
<div id="ModalBottom"> <Dialog.Content className="Login Dialog" onOpenAutoFocus={ (event) => event.preventDefault() }>
<a>Forgot your password?</a> <div className="DialogHeader">
<Button disabled={!formValid}>Log in</Button> <Dialog.Title className="DialogTitle">Log in</Dialog.Title>
</div> <Dialog.Close className="DialogClose" asChild>
<span>
<CrossIcon />
</span>
</Dialog.Close>
</div>
<form className="form" onSubmit={login}>
<Fieldset
fieldName="email"
placeholder="Email address"
onChange={handleChange}
error={errors.email}
ref={emailInput}
/>
<Fieldset
fieldName="password"
placeholder="Password"
onChange={handleChange}
error={errors.password}
ref={passwordInput}
/>
<Button disabled={!formValid}>Log in</Button>
</form> </form>
</Modal> </Dialog.Content>
<Overlay onClick={props.close} /> <Dialog.Overlay className="Overlay" />
</div>, </Dialog.Portal>
document.body </Dialog.Root>
)
) )
} }
export default withCookies(LoginModal) export default LoginModal