Skip to content
This repository has been archived by the owner on Jan 9, 2023. It is now read-only.

Commit

Permalink
fix(login): improved login validation
Browse files Browse the repository at this point in the history
Co-authored-by: Jack Meyer <jackcmeyer@gmail.com>
  • Loading branch information
linus345 and jackcmeyer committed Jul 8, 2020
1 parent d756cb6 commit 028daff
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 7 deletions.
4 changes: 2 additions & 2 deletions src/__tests__/user/user-slice.test.ts
Expand Up @@ -98,7 +98,7 @@ describe('user slice', () => {
})

it('should dispatch login error if login was not successful', async () => {
jest.spyOn(remoteDb, 'logIn').mockRejectedValue({ status: '401' })
jest.spyOn(remoteDb, 'logIn').mockRejectedValue({ status: 401 })
jest.spyOn(remoteDb, 'getUser').mockResolvedValue({
_id: 'userId',
metadata: {
Expand All @@ -113,7 +113,7 @@ describe('user slice', () => {
expect(remoteDb.getUser).not.toHaveBeenCalled()
expect(store.getActions()[0]).toEqual({
type: loginError.type,
payload: 'user.login.error',
payload: { message: 'user.login.error.message.incorrect' },
})
})
})
Expand Down
12 changes: 11 additions & 1 deletion src/login/Login.tsx
Expand Up @@ -6,12 +6,14 @@ import { Redirect } from 'react-router-dom'

import TextInputWithLabelFormGroup from '../shared/components/input/TextInputWithLabelFormGroup'
import { remoteDb } from '../shared/config/pouchdb'
import useTranslator from '../shared/hooks/useTranslator'
import logo from '../shared/static/images/logo-on-transparent.png'
import { RootState } from '../shared/store'
import { getCurrentSession, login } from '../user/user-slice'

const Login = () => {
const dispatch = useDispatch()
const { t } = useTranslator()
const [username, setUsername] = useState('')
const [password, setPassword] = useState('')
const { loginError, user } = useSelector((root: RootState) => root.user)
Expand Down Expand Up @@ -61,13 +63,18 @@ const Login = () => {
<img src={logo} alt="HospitalRun" style={{ width: '100%', textAlign: 'center' }} />
<form>
<Panel title="Please Sign In" color="primary">
{loginError && <Alert color="danger" message={loginError} title="Unable to login" />}
{loginError?.message && (
<Alert color="danger" message={t(loginError?.message)} title="Unable to login" />
)}
<TextInputWithLabelFormGroup
isEditable
label="username"
name="username"
value={username}
onChange={onUsernameChange}
isRequired
isInvalid={!!loginError?.username && !username}
feedback={t(loginError?.username)}
/>
<TextInputWithLabelFormGroup
isEditable
Expand All @@ -76,6 +83,9 @@ const Login = () => {
name="password"
value={password}
onChange={onPasswordChange}
isRequired
isInvalid={!!loginError?.password && !password}
feedback={t(loginError?.password)}
/>
<Button block onClick={onSignInClick}>
Sign In
Expand Down
2 changes: 2 additions & 0 deletions src/shared/locales/enUs/translations/index.ts
Expand Up @@ -10,6 +10,7 @@ import scheduling from './scheduling'
import settings from './settings'
import sex from './sex'
import states from './states'
import user from './user'

export default {
...actions,
Expand All @@ -23,5 +24,6 @@ export default {
...labs,
...incidents,
...settings,
...user,
...bloodType,
}
18 changes: 18 additions & 0 deletions src/shared/locales/enUs/translations/user/index.ts
@@ -0,0 +1,18 @@
export default {
user: {
login: {
error: {
message: {
required: 'Missing required fields.',
incorrect: 'Incorrect username or password.',
},
username: {
required: 'Username is required.',
},
password: {
required: 'Password is required.',
},
},
},
},
}
26 changes: 22 additions & 4 deletions src/user/user-slice.ts
Expand Up @@ -6,10 +6,16 @@ import Permissions from '../shared/model/Permissions'
import User from '../shared/model/User'
import { AppThunk } from '../shared/store'

interface LoginError {
message?: string
username?: string
password?: string
}

interface UserState {
permissions: (Permissions | null)[]
user?: User
loginError?: string
loginError?: LoginError
}

const initialState: UserState = {
Expand Down Expand Up @@ -48,7 +54,7 @@ const userSlice = createSlice({
state.user = payload.user
state.permissions = initialState.permissions
},
loginError(state, { payload }: PayloadAction<string>) {
loginError(state, { payload }: PayloadAction<LoginError>) {
state.loginError = payload
},
logoutSuccess(state) {
Expand Down Expand Up @@ -89,8 +95,20 @@ export const login = (username: string, password: string): AppThunk => async (di
}),
)
} catch (error) {
if (error.status === '401') {
dispatch(loginError('user.login.error'))
if (!username || !password) {
dispatch(
loginError({
message: 'user.login.error.message.required',
username: 'user.login.error.username.required',
password: 'user.login.error.password.required',
}),
)
} else if (error.status === 401) {
dispatch(
loginError({
message: 'user.login.error.message.incorrect',
}),
)
}
}
}
Expand Down

1 comment on commit 028daff

@vercel
Copy link

@vercel vercel bot commented on 028daff Jul 8, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.