@ampath/esm-login-app 8.0.0-next.3 → 8.0.0-next.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/2310.js +1 -1
- package/dist/2310.js.map +1 -1
- package/dist/esm-login-app.js.buildmanifest.json +6 -6
- package/dist/main.js +1 -1
- package/dist/main.js.map +1 -1
- package/dist/routes.json +1 -1
- package/package.json +1 -1
- package/src/login/login.component.tsx +105 -118
package/dist/routes.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"$schema":"https://json.openmrs.org/routes.schema.json","backendDependencies":{"webservices.rest":">=2.2.0"},"pages":[{"component":"root","route":"login","online":true,"offline":true},{"component":"root","route":"logout","online":true,"offline":true},{"component":"root","route":"change-password","online":true,"offline":true}],"extensions":[{"name":"location-picker","slot":"location-picker","component":"locationPicker","online":true,"offline":true},{"name":"logout-button","slot":"user-panel-bottom-slot","component":"logoutButton","online":true,"offline":true},{"name":"password-changer","slot":"user-panel-slot","component":"changePasswordLink","online":true,"offline":true},{"name":"location-changer","slot":"top-nav-info-slot","component":"changeLocationLink","online":true,"offline":true,"order":1}],"modals":[{"name":"change-password-modal","component":"changePasswordModal"}],"version":"8.0.0-next.
|
|
1
|
+
{"$schema":"https://json.openmrs.org/routes.schema.json","backendDependencies":{"webservices.rest":">=2.2.0"},"pages":[{"component":"root","route":"login","online":true,"offline":true},{"component":"root","route":"logout","online":true,"offline":true},{"component":"root","route":"change-password","online":true,"offline":true}],"extensions":[{"name":"location-picker","slot":"location-picker","component":"locationPicker","online":true,"offline":true},{"name":"logout-button","slot":"user-panel-bottom-slot","component":"logoutButton","online":true,"offline":true},{"name":"password-changer","slot":"user-panel-slot","component":"changePasswordLink","online":true,"offline":true},{"name":"location-changer","slot":"top-nav-info-slot","component":"changeLocationLink","online":true,"offline":true,"order":1}],"modals":[{"name":"change-password-modal","component":"changePasswordModal"}],"version":"8.0.0-next.5"}
|
package/package.json
CHANGED
|
@@ -1,14 +1,7 @@
|
|
|
1
|
-
import React, { useState, useRef, useEffect, useCallback } from
|
|
2
|
-
import { useLocation, useNavigate } from
|
|
3
|
-
import { useTranslation } from
|
|
4
|
-
import {
|
|
5
|
-
Button,
|
|
6
|
-
InlineLoading,
|
|
7
|
-
InlineNotification,
|
|
8
|
-
PasswordInput,
|
|
9
|
-
TextInput,
|
|
10
|
-
Tile,
|
|
11
|
-
} from "@carbon/react";
|
|
1
|
+
import React, { useState, useRef, useEffect, useCallback } from 'react';
|
|
2
|
+
import { useLocation, useNavigate } from 'react-router-dom';
|
|
3
|
+
import { useTranslation } from 'react-i18next';
|
|
4
|
+
import { Button, InlineLoading, InlineNotification, PasswordInput, TextInput, Tile } from '@carbon/react';
|
|
12
5
|
import {
|
|
13
6
|
ArrowRightIcon,
|
|
14
7
|
getCoreTranslation,
|
|
@@ -17,45 +10,43 @@ import {
|
|
|
17
10
|
useConfig,
|
|
18
11
|
useConnectivity,
|
|
19
12
|
useSession,
|
|
20
|
-
} from
|
|
21
|
-
import { type ConfigSchema } from
|
|
22
|
-
import Logo from
|
|
23
|
-
import Footer from
|
|
24
|
-
import styles from
|
|
25
|
-
import { getOtp } from
|
|
13
|
+
} from '@openmrs/esm-framework';
|
|
14
|
+
import { type ConfigSchema } from '../config-schema';
|
|
15
|
+
import Logo from '../logo.component';
|
|
16
|
+
import Footer from '../footer.component';
|
|
17
|
+
import styles from './login.scss';
|
|
18
|
+
import { getOtp } from '../resources/otp.resource';
|
|
19
|
+
import { getOtpEnabledStatus } from '../utils/get-base-url';
|
|
20
|
+
import { boolean } from 'zod';
|
|
26
21
|
|
|
27
22
|
export interface LoginReferrer {
|
|
28
23
|
referrer?: string;
|
|
29
24
|
}
|
|
30
25
|
|
|
31
26
|
const Login: React.FC = () => {
|
|
32
|
-
const {
|
|
33
|
-
showPasswordOnSeparateScreen,
|
|
34
|
-
provider: loginProvider,
|
|
35
|
-
links: loginLinks,
|
|
36
|
-
} = useConfig<ConfigSchema>();
|
|
27
|
+
const { showPasswordOnSeparateScreen, provider: loginProvider, links: loginLinks } = useConfig<ConfigSchema>();
|
|
37
28
|
const isLoginEnabled = useConnectivity();
|
|
38
29
|
const { t } = useTranslation();
|
|
39
30
|
const { user } = useSession();
|
|
40
|
-
const location = useLocation() as unknown as Omit<Location,
|
|
31
|
+
const location = useLocation() as unknown as Omit<Location, 'state'> & {
|
|
41
32
|
state: LoginReferrer;
|
|
42
33
|
};
|
|
43
34
|
const navigate = useNavigate();
|
|
44
35
|
|
|
45
|
-
const [errorMessage, setErrorMessage] = useState(
|
|
36
|
+
const [errorMessage, setErrorMessage] = useState('');
|
|
46
37
|
const [isLoggingIn, setIsLoggingIn] = useState(false);
|
|
47
|
-
const [password, setPassword] = useState(
|
|
48
|
-
const [username, setUsername] = useState(
|
|
38
|
+
const [password, setPassword] = useState('');
|
|
39
|
+
const [username, setUsername] = useState('');
|
|
49
40
|
const [showPasswordField, setShowPasswordField] = useState(false);
|
|
50
41
|
const passwordInputRef = useRef<HTMLInputElement>(null);
|
|
51
42
|
const usernameInputRef = useRef<HTMLInputElement>(null);
|
|
52
43
|
|
|
53
44
|
useEffect(() => {
|
|
54
45
|
if (!user) {
|
|
55
|
-
if (loginProvider.type ===
|
|
46
|
+
if (loginProvider.type === 'oauth2') {
|
|
56
47
|
openmrsNavigate({ to: loginProvider.loginUrl });
|
|
57
|
-
} else if (!username && location.pathname ===
|
|
58
|
-
navigate(
|
|
48
|
+
} else if (!username && location.pathname === '/login/confirm') {
|
|
49
|
+
navigate('/login');
|
|
59
50
|
}
|
|
60
51
|
}
|
|
61
52
|
}, [username, navigate, location, user, loginProvider]);
|
|
@@ -83,14 +74,8 @@ const Login: React.FC = () => {
|
|
|
83
74
|
}
|
|
84
75
|
}, []);
|
|
85
76
|
|
|
86
|
-
const changeUsername = useCallback(
|
|
87
|
-
|
|
88
|
-
[]
|
|
89
|
-
);
|
|
90
|
-
const changePassword = useCallback(
|
|
91
|
-
(evt: React.ChangeEvent<HTMLInputElement>) => setPassword(evt.target.value),
|
|
92
|
-
[]
|
|
93
|
-
);
|
|
77
|
+
const changeUsername = useCallback((evt: React.ChangeEvent<HTMLInputElement>) => setUsername(evt.target.value), []);
|
|
78
|
+
const changePassword = useCallback((evt: React.ChangeEvent<HTMLInputElement>) => setPassword(evt.target.value), []);
|
|
94
79
|
|
|
95
80
|
const handleSubmit = useCallback(
|
|
96
81
|
async (evt: React.FormEvent<HTMLFormElement>) => {
|
|
@@ -98,9 +83,9 @@ const Login: React.FC = () => {
|
|
|
98
83
|
evt.stopPropagation();
|
|
99
84
|
|
|
100
85
|
// If credentials were autofilled, input onChange might not have been called
|
|
101
|
-
const currentUsername =
|
|
102
|
-
usernameInputRef.current?.value?.trim() || username;
|
|
86
|
+
const currentUsername = usernameInputRef.current?.value?.trim() || username;
|
|
103
87
|
const currentPassword = passwordInputRef.current?.value || password;
|
|
88
|
+
const isOtpEnabled: boolean = await getOtpEnabledStatus();
|
|
104
89
|
|
|
105
90
|
if (showPasswordOnSeparateScreen && !showPasswordField) {
|
|
106
91
|
continueLogin();
|
|
@@ -114,41 +99,70 @@ const Login: React.FC = () => {
|
|
|
114
99
|
|
|
115
100
|
try {
|
|
116
101
|
setIsLoggingIn(true);
|
|
117
|
-
const sessionStore = await refetchCurrentUser(
|
|
118
|
-
currentUsername,
|
|
119
|
-
currentPassword
|
|
120
|
-
);
|
|
102
|
+
const sessionStore = await refetchCurrentUser(currentUsername, currentPassword);
|
|
121
103
|
const session = sessionStore.session;
|
|
122
104
|
const authenticated = sessionStore?.session?.authenticated;
|
|
123
105
|
|
|
124
|
-
if (
|
|
125
|
-
if (
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
if (location
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
106
|
+
if (isOtpEnabled === true) {
|
|
107
|
+
if (authenticated) {
|
|
108
|
+
if (session.sessionLocation) {
|
|
109
|
+
let to = loginLinks?.loginSuccess || '/home';
|
|
110
|
+
if (location?.state?.referrer) {
|
|
111
|
+
if (location.state.referrer.startsWith('/')) {
|
|
112
|
+
to = `\${openmrsSpaBase}${location.state.referrer}`;
|
|
113
|
+
} else {
|
|
114
|
+
to = location.state.referrer;
|
|
115
|
+
}
|
|
132
116
|
}
|
|
117
|
+
await getOtp(username, password);
|
|
118
|
+
navigate('otp', {
|
|
119
|
+
state: {
|
|
120
|
+
username,
|
|
121
|
+
password,
|
|
122
|
+
referrer: location?.state?.referrer,
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
} else if (!session.sessionLocation) {
|
|
126
|
+
await getOtp(username, password);
|
|
127
|
+
navigate('otp', {
|
|
128
|
+
state: {
|
|
129
|
+
username,
|
|
130
|
+
password,
|
|
131
|
+
referrer: location?.state?.referrer,
|
|
132
|
+
},
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
} else {
|
|
136
|
+
setErrorMessage(t('invalidCredentials', 'Invalid username or password'));
|
|
137
|
+
setUsername('');
|
|
138
|
+
setPassword('');
|
|
139
|
+
if (showPasswordOnSeparateScreen) {
|
|
140
|
+
setShowPasswordField(false);
|
|
133
141
|
}
|
|
134
|
-
|
|
135
|
-
await getOtp(username, password);
|
|
136
|
-
navigate("otp", {
|
|
137
|
-
state: {
|
|
138
|
-
username,
|
|
139
|
-
password,
|
|
140
|
-
referrer: location?.state?.referrer,
|
|
141
|
-
},
|
|
142
|
-
});
|
|
143
142
|
}
|
|
144
143
|
} else {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
144
|
+
if (authenticated) {
|
|
145
|
+
if (session.sessionLocation) {
|
|
146
|
+
let to = loginLinks?.loginSuccess || '/home';
|
|
147
|
+
if (location?.state?.referrer) {
|
|
148
|
+
if (location.state.referrer.startsWith('/')) {
|
|
149
|
+
to = `\${openmrsSpaBase}${location.state.referrer}`;
|
|
150
|
+
} else {
|
|
151
|
+
to = location.state.referrer;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
openmrsNavigate({ to });
|
|
156
|
+
} else {
|
|
157
|
+
navigate('/login/location');
|
|
158
|
+
}
|
|
159
|
+
} else {
|
|
160
|
+
setErrorMessage(t('invalidCredentials', 'Invalid username or password'));
|
|
161
|
+
setUsername('');
|
|
162
|
+
setPassword('');
|
|
163
|
+
if (showPasswordOnSeparateScreen) {
|
|
164
|
+
setShowPasswordField(false);
|
|
165
|
+
}
|
|
152
166
|
}
|
|
153
167
|
}
|
|
154
168
|
|
|
@@ -157,12 +171,10 @@ const Login: React.FC = () => {
|
|
|
157
171
|
if (error instanceof Error) {
|
|
158
172
|
setErrorMessage(error.message);
|
|
159
173
|
} else {
|
|
160
|
-
setErrorMessage(
|
|
161
|
-
t("invalidCredentials", "Invalid username or password")
|
|
162
|
-
);
|
|
174
|
+
setErrorMessage(t('invalidCredentials', 'Invalid username or password'));
|
|
163
175
|
}
|
|
164
|
-
setUsername(
|
|
165
|
-
setPassword(
|
|
176
|
+
setUsername('');
|
|
177
|
+
setPassword('');
|
|
166
178
|
if (showPasswordOnSeparateScreen) {
|
|
167
179
|
setShowPasswordField(false);
|
|
168
180
|
}
|
|
@@ -180,10 +192,10 @@ const Login: React.FC = () => {
|
|
|
180
192
|
location,
|
|
181
193
|
t,
|
|
182
194
|
continueLogin,
|
|
183
|
-
]
|
|
195
|
+
],
|
|
184
196
|
);
|
|
185
197
|
|
|
186
|
-
if (!loginProvider || loginProvider.type ===
|
|
198
|
+
if (!loginProvider || loginProvider.type === 'basic') {
|
|
187
199
|
return (
|
|
188
200
|
<div className={styles.container}>
|
|
189
201
|
<Tile className={styles.loginCard}>
|
|
@@ -192,8 +204,8 @@ const Login: React.FC = () => {
|
|
|
192
204
|
<InlineNotification
|
|
193
205
|
kind="error"
|
|
194
206
|
subtitle={t(errorMessage)}
|
|
195
|
-
title={getCoreTranslation(
|
|
196
|
-
onClick={() => setErrorMessage(
|
|
207
|
+
title={getCoreTranslation('error')}
|
|
208
|
+
onClick={() => setErrorMessage('')}
|
|
197
209
|
/>
|
|
198
210
|
</div>
|
|
199
211
|
)}
|
|
@@ -207,7 +219,7 @@ const Login: React.FC = () => {
|
|
|
207
219
|
type="text"
|
|
208
220
|
name="username"
|
|
209
221
|
autoComplete="username"
|
|
210
|
-
labelText={t(
|
|
222
|
+
labelText={t('username', 'Username')}
|
|
211
223
|
value={username}
|
|
212
224
|
onChange={changeUsername}
|
|
213
225
|
ref={usernameInputRef}
|
|
@@ -216,25 +228,18 @@ const Login: React.FC = () => {
|
|
|
216
228
|
/>
|
|
217
229
|
{showPasswordOnSeparateScreen ? (
|
|
218
230
|
<>
|
|
219
|
-
<div
|
|
220
|
-
className={
|
|
221
|
-
showPasswordField ? undefined : styles.hiddenPasswordField
|
|
222
|
-
}
|
|
223
|
-
>
|
|
231
|
+
<div className={showPasswordField ? undefined : styles.hiddenPasswordField}>
|
|
224
232
|
<PasswordInput
|
|
225
233
|
id="password"
|
|
226
|
-
labelText={t(
|
|
234
|
+
labelText={t('password', 'Password')}
|
|
227
235
|
name="password"
|
|
228
236
|
autoComplete="current-password"
|
|
229
237
|
onChange={changePassword}
|
|
230
238
|
ref={passwordInputRef}
|
|
231
239
|
required
|
|
232
240
|
value={password}
|
|
233
|
-
showPasswordLabel={t(
|
|
234
|
-
invalidText={t(
|
|
235
|
-
"validValueRequired",
|
|
236
|
-
"A valid value is required"
|
|
237
|
-
)}
|
|
241
|
+
showPasswordLabel={t('showPassword', 'Show password')}
|
|
242
|
+
invalidText={t('validValueRequired', 'A valid value is required')}
|
|
238
243
|
aria-hidden={!showPasswordField}
|
|
239
244
|
tabIndex={showPasswordField ? 0 : -1}
|
|
240
245
|
/>
|
|
@@ -243,31 +248,21 @@ const Login: React.FC = () => {
|
|
|
243
248
|
<Button
|
|
244
249
|
type="submit"
|
|
245
250
|
className={styles.continueButton}
|
|
246
|
-
renderIcon={(props) =>
|
|
247
|
-
|
|
248
|
-
)}
|
|
249
|
-
iconDescription={t(
|
|
250
|
-
"loginButtonIconDescription",
|
|
251
|
-
"Log in button"
|
|
252
|
-
)}
|
|
251
|
+
renderIcon={(props) => <ArrowRightIcon size={24} {...props} />}
|
|
252
|
+
iconDescription={t('loginButtonIconDescription', 'Log in button')}
|
|
253
253
|
disabled={!isLoginEnabled || isLoggingIn}
|
|
254
254
|
>
|
|
255
255
|
{isLoggingIn ? (
|
|
256
|
-
<InlineLoading
|
|
257
|
-
className={styles.loader}
|
|
258
|
-
description={t("loggingIn", "Logging in") + "..."}
|
|
259
|
-
/>
|
|
256
|
+
<InlineLoading className={styles.loader} description={t('loggingIn', 'Logging in') + '...'} />
|
|
260
257
|
) : (
|
|
261
|
-
t(
|
|
258
|
+
t('login', 'Log in')
|
|
262
259
|
)}
|
|
263
260
|
</Button>
|
|
264
261
|
) : (
|
|
265
262
|
<Button
|
|
266
263
|
type="submit"
|
|
267
264
|
className={styles.continueButton}
|
|
268
|
-
renderIcon={(props) =>
|
|
269
|
-
<ArrowRightIcon size={24} {...props} />
|
|
270
|
-
)}
|
|
265
|
+
renderIcon={(props) => <ArrowRightIcon size={24} {...props} />}
|
|
271
266
|
iconDescription="Continue to password"
|
|
272
267
|
onClick={(evt) => {
|
|
273
268
|
evt.preventDefault();
|
|
@@ -275,7 +270,7 @@ const Login: React.FC = () => {
|
|
|
275
270
|
}}
|
|
276
271
|
disabled={!isLoginEnabled}
|
|
277
272
|
>
|
|
278
|
-
{t(
|
|
273
|
+
{t('continue', 'Continue')}
|
|
279
274
|
</Button>
|
|
280
275
|
)}
|
|
281
276
|
</>
|
|
@@ -283,35 +278,27 @@ const Login: React.FC = () => {
|
|
|
283
278
|
<>
|
|
284
279
|
<PasswordInput
|
|
285
280
|
id="password"
|
|
286
|
-
labelText={t(
|
|
281
|
+
labelText={t('password', 'Password')}
|
|
287
282
|
name="password"
|
|
288
283
|
autoComplete="current-password"
|
|
289
284
|
onChange={changePassword}
|
|
290
285
|
ref={passwordInputRef}
|
|
291
286
|
required
|
|
292
287
|
value={password}
|
|
293
|
-
showPasswordLabel={t(
|
|
294
|
-
invalidText={t(
|
|
295
|
-
"validValueRequired",
|
|
296
|
-
"A valid value is required"
|
|
297
|
-
)}
|
|
288
|
+
showPasswordLabel={t('showPassword', 'Show password')}
|
|
289
|
+
invalidText={t('validValueRequired', 'A valid value is required')}
|
|
298
290
|
/>
|
|
299
291
|
<Button
|
|
300
292
|
type="submit"
|
|
301
293
|
className={styles.continueButton}
|
|
302
|
-
renderIcon={(props) =>
|
|
303
|
-
<ArrowRightIcon size={24} {...props} />
|
|
304
|
-
)}
|
|
294
|
+
renderIcon={(props) => <ArrowRightIcon size={24} {...props} />}
|
|
305
295
|
iconDescription="Log in"
|
|
306
296
|
disabled={!isLoginEnabled || isLoggingIn}
|
|
307
297
|
>
|
|
308
298
|
{isLoggingIn ? (
|
|
309
|
-
<InlineLoading
|
|
310
|
-
className={styles.loader}
|
|
311
|
-
description={t("loggingIn", "Logging in") + "..."}
|
|
312
|
-
/>
|
|
299
|
+
<InlineLoading className={styles.loader} description={t('loggingIn', 'Logging in') + '...'} />
|
|
313
300
|
) : (
|
|
314
|
-
t(
|
|
301
|
+
t('login', 'Log in')
|
|
315
302
|
)}
|
|
316
303
|
</Button>
|
|
317
304
|
</>
|