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

Commit 028daff

Browse files
linus345jackcmeyer
andauthored
fix(login): improved login validation
Co-authored-by: Jack Meyer <[email protected]>
1 parent d756cb6 commit 028daff

File tree

5 files changed

+55
-7
lines changed

5 files changed

+55
-7
lines changed

src/__tests__/user/user-slice.test.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ describe('user slice', () => {
9898
})
9999

100100
it('should dispatch login error if login was not successful', async () => {
101-
jest.spyOn(remoteDb, 'logIn').mockRejectedValue({ status: '401' })
101+
jest.spyOn(remoteDb, 'logIn').mockRejectedValue({ status: 401 })
102102
jest.spyOn(remoteDb, 'getUser').mockResolvedValue({
103103
_id: 'userId',
104104
metadata: {
@@ -113,7 +113,7 @@ describe('user slice', () => {
113113
expect(remoteDb.getUser).not.toHaveBeenCalled()
114114
expect(store.getActions()[0]).toEqual({
115115
type: loginError.type,
116-
payload: 'user.login.error',
116+
payload: { message: 'user.login.error.message.incorrect' },
117117
})
118118
})
119119
})

src/login/Login.tsx

+11-1
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,14 @@ import { Redirect } from 'react-router-dom'
66

77
import TextInputWithLabelFormGroup from '../shared/components/input/TextInputWithLabelFormGroup'
88
import { remoteDb } from '../shared/config/pouchdb'
9+
import useTranslator from '../shared/hooks/useTranslator'
910
import logo from '../shared/static/images/logo-on-transparent.png'
1011
import { RootState } from '../shared/store'
1112
import { getCurrentSession, login } from '../user/user-slice'
1213

1314
const Login = () => {
1415
const dispatch = useDispatch()
16+
const { t } = useTranslator()
1517
const [username, setUsername] = useState('')
1618
const [password, setPassword] = useState('')
1719
const { loginError, user } = useSelector((root: RootState) => root.user)
@@ -61,13 +63,18 @@ const Login = () => {
6163
<img src={logo} alt="HospitalRun" style={{ width: '100%', textAlign: 'center' }} />
6264
<form>
6365
<Panel title="Please Sign In" color="primary">
64-
{loginError && <Alert color="danger" message={loginError} title="Unable to login" />}
66+
{loginError?.message && (
67+
<Alert color="danger" message={t(loginError?.message)} title="Unable to login" />
68+
)}
6569
<TextInputWithLabelFormGroup
6670
isEditable
6771
label="username"
6872
name="username"
6973
value={username}
7074
onChange={onUsernameChange}
75+
isRequired
76+
isInvalid={!!loginError?.username && !username}
77+
feedback={t(loginError?.username)}
7178
/>
7279
<TextInputWithLabelFormGroup
7380
isEditable
@@ -76,6 +83,9 @@ const Login = () => {
7683
name="password"
7784
value={password}
7885
onChange={onPasswordChange}
86+
isRequired
87+
isInvalid={!!loginError?.password && !password}
88+
feedback={t(loginError?.password)}
7989
/>
8090
<Button block onClick={onSignInClick}>
8191
Sign In

src/shared/locales/enUs/translations/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import scheduling from './scheduling'
1010
import settings from './settings'
1111
import sex from './sex'
1212
import states from './states'
13+
import user from './user'
1314

1415
export default {
1516
...actions,
@@ -23,5 +24,6 @@ export default {
2324
...labs,
2425
...incidents,
2526
...settings,
27+
...user,
2628
...bloodType,
2729
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
export default {
2+
user: {
3+
login: {
4+
error: {
5+
message: {
6+
required: 'Missing required fields.',
7+
incorrect: 'Incorrect username or password.',
8+
},
9+
username: {
10+
required: 'Username is required.',
11+
},
12+
password: {
13+
required: 'Password is required.',
14+
},
15+
},
16+
},
17+
},
18+
}

src/user/user-slice.ts

+22-4
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,16 @@ import Permissions from '../shared/model/Permissions'
66
import User from '../shared/model/User'
77
import { AppThunk } from '../shared/store'
88

9+
interface LoginError {
10+
message?: string
11+
username?: string
12+
password?: string
13+
}
14+
915
interface UserState {
1016
permissions: (Permissions | null)[]
1117
user?: User
12-
loginError?: string
18+
loginError?: LoginError
1319
}
1420

1521
const initialState: UserState = {
@@ -48,7 +54,7 @@ const userSlice = createSlice({
4854
state.user = payload.user
4955
state.permissions = initialState.permissions
5056
},
51-
loginError(state, { payload }: PayloadAction<string>) {
57+
loginError(state, { payload }: PayloadAction<LoginError>) {
5258
state.loginError = payload
5359
},
5460
logoutSuccess(state) {
@@ -89,8 +95,20 @@ export const login = (username: string, password: string): AppThunk => async (di
8995
}),
9096
)
9197
} catch (error) {
92-
if (error.status === '401') {
93-
dispatch(loginError('user.login.error'))
98+
if (!username || !password) {
99+
dispatch(
100+
loginError({
101+
message: 'user.login.error.message.required',
102+
username: 'user.login.error.username.required',
103+
password: 'user.login.error.password.required',
104+
}),
105+
)
106+
} else if (error.status === 401) {
107+
dispatch(
108+
loginError({
109+
message: 'user.login.error.message.incorrect',
110+
}),
111+
)
94112
}
95113
}
96114
}

0 commit comments

Comments
 (0)