@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/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.3"}
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,6 +1,6 @@
1
1
  {
2
2
  "name": "@ampath/esm-login-app",
3
- "version": "8.0.0-next.3",
3
+ "version": "8.0.0-next.5",
4
4
  "license": "MPL-2.0",
5
5
  "description": "The login microfrontend for the OpenMRS SPA",
6
6
  "browser": "dist/esm-login-app.js",
@@ -1,14 +1,7 @@
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 {
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 "@openmrs/esm-framework";
21
- import { type ConfigSchema } from "../config-schema";
22
- import Logo from "../logo.component";
23
- import Footer from "../footer.component";
24
- import styles from "./login.scss";
25
- import { getOtp } from "../resources/otp.resource";
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, "state"> & {
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 === "oauth2") {
46
+ if (loginProvider.type === 'oauth2') {
56
47
  openmrsNavigate({ to: loginProvider.loginUrl });
57
- } else if (!username && location.pathname === "/login/confirm") {
58
- navigate("/login");
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
- (evt: React.ChangeEvent<HTMLInputElement>) => setUsername(evt.target.value),
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 (authenticated) {
125
- if (session.sessionLocation) {
126
- let to = loginLinks?.loginSuccess || "/home";
127
- if (location?.state?.referrer) {
128
- if (location.state.referrer.startsWith("/")) {
129
- to = `\${openmrsSpaBase}${location.state.referrer}`;
130
- } else {
131
- to = location.state.referrer;
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
- setErrorMessage(
146
- t("invalidCredentials", "Invalid username or password")
147
- );
148
- setUsername("");
149
- setPassword("");
150
- if (showPasswordOnSeparateScreen) {
151
- setShowPasswordField(false);
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 === "basic") {
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("error")}
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("username", "Username")}
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("password", "Password")}
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("showPassword", "Show password")}
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
- <ArrowRightIcon size={24} {...props} />
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("login", "Log in")
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("continue", "Continue")}
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("password", "Password")}
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("showPassword", "Show password")}
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("login", "Log in")
301
+ t('login', 'Log in')
315
302
  )}
316
303
  </Button>
317
304
  </>