Skip to content

Commit

Permalink
refacto(login) WIP refactoring more accessible, resilient, maintainab…
Browse files Browse the repository at this point in the history
…le and lighter frontend
  • Loading branch information
LiquidITGuy committed Dec 5, 2022
1 parent b20b15b commit f5376aa
Show file tree
Hide file tree
Showing 12 changed files with 13,292 additions and 12,440 deletions.
1 change: 0 additions & 1 deletion .env.development
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,3 @@ PORT=4500
REACT_APP_API_URL=http://127.0.0.1:3000
REACT_APP_IS_DEBUG=TRUE
REACT_APP_DEFAULT_PASSWORD=captain42

12 changes: 2 additions & 10 deletions public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<meta name="theme-color" content="#1b8ad3" />
<link rel="stylesheet" type="text/css" href="/theme.css">
<!--
manifest.json provides metadata used when your web app is added to the
homescreen on Android. See https://developers.google.com/web/fundamentals/web-app-manifest/
Expand All @@ -30,16 +31,7 @@
<body>
<noscript> You need to enable JavaScript to run this app. </noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.

To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>

</html>
</html>
3 changes: 3 additions & 0 deletions public/theme.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
:root {
--main-bg-color: white;
}
67 changes: 34 additions & 33 deletions src/containers/Login.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { LockOutlined } from '@ant-design/icons'
import { Button, Card, Collapse, Input, Radio, Row } from 'antd'
// import { Button, Collapse, Input, Radio, Row, Card } from 'antd'
import { Collapse, Input } from 'antd'
import Card from '../ui/Card'
import SubmitButton from '../ui/Form/SubmitButton'
import RadioButton, { ElementDisplay } from '../ui/Form/radioButton'
import React, { ReactComponentElement } from 'react'
import { Redirect, RouteComponentProps } from 'react-router'
import ApiManager from '../api/ApiManager'
Expand All @@ -12,6 +16,21 @@ const NO_SESSION = 1
const SESSION_STORAGE = 2
const LOCAL_STORAGE = 3

const RADIO_BUTTON: Array<ElementDisplay> = [
{
legend: 'No session persistence (Most Secure)',
value: NO_SESSION,
},
{
legend: 'Use sessionStorage',
value: SESSION_STORAGE,
},
{
legend: 'Use localStorage (Most Persistent)',
value: LOCAL_STORAGE,
},
]

export default class Login extends ApiComponent<RouteComponentProps<any>, any> {
constructor(props: any) {
super(props)
Expand Down Expand Up @@ -100,7 +119,8 @@ class NormalLoginForm extends React.Component<
}
}

handleSubmit = () => {
handleSubmit = (e: React.FormEvent) => {
e.preventDefault()
const self = this
self.props.onLoginRequested(
self.state.passwordEntered,
Expand All @@ -111,7 +131,7 @@ class NormalLoginForm extends React.Component<
render() {
const self = this
return (
<form>
<form onSubmit={this.handleSubmit}>
<Input.Password
required
prefix={
Expand All @@ -124,39 +144,20 @@ class NormalLoginForm extends React.Component<
autoFocus
/>
<div style={{ marginTop: 20, marginBottom: 20 }}>
<Row justify="end">
<Button
type="primary"
htmlType="submit"
onClick={() => {
self.handleSubmit()
}}
>
Login
</Button>
</Row>
<div style={{textAlign: "right"}}>
<SubmitButton value="Login"/>
</div>
</div>
<Collapse>
<Collapse.Panel header="Remember Me" key="1">
<Radio.Group
onChange={(e) => {
console.log(e.target.value)
self.setState({
loginOption: e.target.value,
})
}}
value={self.state.loginOption}
>
<Radio style={radioStyle} value={NO_SESSION}>
No session persistence (Most Secure)
</Radio>
<Radio style={radioStyle} value={SESSION_STORAGE}>
Use sessionStorage
</Radio>
<Radio style={radioStyle} value={LOCAL_STORAGE}>
Use localStorage (Most Persistent)
</Radio>
</Radio.Group>
<RadioButton listOfValues={RADIO_BUTTON} name="login_presistance"
onChange={(e) => {
self.setState({
loginOption: e.target.value as unknown as number,
})
}}
value={self.state.loginOption}
/>
</Collapse.Panel>
</Collapse>
</form>
Expand Down
22 changes: 22 additions & 0 deletions src/ui/Card.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
.card-class {
background-color: var(--main-bg-color);
border-radius: 1rem;
padding: 0;
}

.card-class>header {
padding: 1rem;
min-height: 48px;
border-bottom: 1px solid rgb(240,240,240);
}
.card-class>header>h1 {
color: rgba(0,0,0,0.8);
font-size: 1.2em;
}
.card-class>main {
margin-top: 1rem;
padding: 1rem;
font-size: 1em;
color: rgba(255,255,255,0.9);
font-size: 0.8rem;
}
26 changes: 26 additions & 0 deletions src/ui/Card.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// @ts-ignore
import styles from './Card.css'
import React, { CSSProperties, ReactComponentElement } from 'react'
const classNames = require('classnames')

export interface CardProps {
title?: string,
style?: CSSProperties,
}
classNames.bind(styles)

export default class Card extends React.Component<CardProps> {
render(): ReactComponentElement<any> {
const cardClass = classNames({
'card-class': true
})
return (
<article style={this.props.style} className={cardClass}>
<header><h1>{this.props.title}</h1></header>
<main>
{this.props.children}
</main>
</article>
)
}
}
27 changes: 27 additions & 0 deletions src/ui/Form/SubmitButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// @ts-ignore
import styles from './primaryButton.css'
import React, { CSSProperties, ReactComponentElement } from 'react'
const classNames = require('classnames')

export enum buttonType {
PRIMARY = 'primary',
SECONDARY = 'secondary'
}

export interface FormButtonProps {
type?: buttonType,
style?: CSSProperties,
value: string
}
classNames.bind(styles)

export default class SubmitButton extends React.Component<FormButtonProps> {
render(): ReactComponentElement<any> {
const submitClass = classNames({
'submit-class': true
})
return (
<input className={ submitClass } type="submit" value={this.props.value}/>
)
}
}
17 changes: 17 additions & 0 deletions src/ui/Form/primaryButton.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
.submit-class {
opacity: 1;
color: rgb(255, 255, 255);
background: rgb(27, 138, 211);
border: none;
border-radius: 10px;
text-shadow: rgb(0 0 0 / 12%) 0px -1px 0px;
box-shadow: rgb(0 0 0 / 4%) 0px 2px 0px;
height: 2rem;
line-height: 2rem;
padding: 0 1rem;
cursor: pointer
}
.submit-class:hover {
opacity: .8;
transition: opacity .8s ease-out;
}
20 changes: 20 additions & 0 deletions src/ui/Form/radioButton.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
.radio-button {
list-style-type: none;
line-height: 2rem;
padding-inline-start: 0;
margin: 0;
}

.radio-button-input {
vertical-align: top;
margin-right: 0.5rem;
width: 1rem;
height: 1rem;
cursor: pointer;
}

.radio-button-label {
vertical-align: top;
cursor: pointer;
line-height: 1rem;
}
64 changes: 64 additions & 0 deletions src/ui/Form/radioButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// @ts-ignore
import styles from './radioButton.css'
import React, { CSSProperties, ReactComponentElement } from 'react'
const classNames = require('classnames')

export interface ElementDisplay {
value: string | number,
legend: string,
}

export interface RadioButtonProps {
name: string
value: string | number
legend?: string,
style?: CSSProperties,
onChange?: React.ChangeEventHandler<HTMLInputElement>
listOfValues: Array<ElementDisplay>
}
classNames.bind(styles)

classNames({
'radio-button': true,
'radio-button-input': true,
'radio-button-label': true,
})

const displayLegend = (elementDisplay: RadioButtonProps): React.ReactFragment => {
if (elementDisplay.legend) {
return (<legend>{elementDisplay.legend}</legend>)
}
return (<></>)
}

const displayRadioButton = (elementDisplay: ElementDisplay, props: RadioButtonProps): React.ReactFragment => (
<li
key={props.name + elementDisplay.value + props.value}>
<input type="radio"
className={'radio-button-input'}
id={ props.name + elementDisplay.value}
name={ props.name}
value={elementDisplay.value}
onChange={props.onChange}
checked={ elementDisplay.value.toString() === props.value.toString() }
/>
<label
className={'radio-button-label'}
htmlFor={ props.name + elementDisplay.value}>{elementDisplay.legend}</label>
</li>
)



export default class SubmitButton extends React.Component<RadioButtonProps> {
render(): ReactComponentElement<any> {
return (
<fieldset>
{displayLegend(this.props)}
<ul className={'radio-button'}>
{this.props.listOfValues.map(display => displayRadioButton(display, this.props))}
</ul>
</fieldset>
)
}
}
6 changes: 3 additions & 3 deletions src/utils/Utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const MAXIMUM_HEXO_LENGTH = 255
const MAXIMUM_HEXA_LENGTH = 255

export default {
copyObject<T>(obj: T): T {
Expand All @@ -9,7 +9,7 @@ export default {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(
/[xy]/g,
function (c) {
let r = (Math.random() * 16) | 0,
let r = Math.round(Math.random() * 16) | 0,
v = c === 'x' ? r : (r & 0x3) | 0x8
return v.toString(16)
}
Expand Down Expand Up @@ -80,7 +80,7 @@ export default {
matches.length === 2
) {
const hexLength = Number(matches[1])
if (hexLength > MAXIMUM_HEXO_LENGTH) {
if (hexLength > MAXIMUM_HEXA_LENGTH) {
// capping out the maximum length to 255 to prevent maximum value of Number in js.
inputString = inputString.replace(matches[0], '')
} else {
Expand Down
Loading

0 comments on commit f5376aa

Please sign in to comment.