@ampath/esm-login-app 8.0.0-next.2 → 8.0.0-next.21
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/4bc56e5b0b0e91da.png +0 -0
- package/dist/5940.js +1 -1
- package/dist/5976.js +1 -1
- package/dist/5976.js.map +1 -1
- package/dist/647e55b5cedf5df2.png +0 -0
- package/dist/7144.js +12 -12
- package/dist/7144.js.map +1 -1
- package/dist/7244.js +1 -0
- package/dist/7244.js.map +1 -0
- package/dist/a6792134b9df70c4.png +0 -0
- package/dist/acd6ab71c5f6bcb6.jpg +0 -0
- package/dist/d0bf081185f017f3.jpg +0 -0
- package/dist/d48e253df6a333a7.png +0 -0
- package/dist/esm-login-app.js +2 -2
- package/dist/esm-login-app.js.buildmanifest.json +48 -34
- package/dist/main.js +3 -3
- package/dist/main.js.map +1 -1
- package/dist/routes.json +1 -1
- package/package.json +1 -1
- package/src/assets/Taifa-Care.png +0 -0
- package/src/assets/ampath-logo.png +0 -0
- package/src/assets/dha.png +0 -0
- package/src/assets/gok.png +0 -0
- package/src/assets/medicine.jpg +0 -0
- package/src/assets/openmrs.jpg +0 -0
- package/src/common/resend-timer/resend-timer.component.tsx +7 -2
- package/src/config-schema.ts +32 -37
- package/src/declarations.d.ts +4 -0
- package/src/forgot-password/forgot-password.component.tsx +113 -0
- package/src/forgot-password/forgot-password.resource.ts +11 -0
- package/src/forgot-password/forgot-password.scss +51 -0
- package/src/forgot-password/reset-password/reset-password.component.tsx +131 -0
- package/src/forgot-password/reset-password/reset-password.resource.ts +11 -0
- package/src/login/login.component.tsx +246 -205
- package/src/login/login.scss +110 -4
- package/src/logo.component.tsx +12 -11
- package/src/otp/otp.component.tsx +41 -30
- package/src/otp/otp.module.scss +61 -0
- package/src/resources/otp.resource.ts +77 -26
- package/src/root.component.tsx +4 -0
- package/src/utils/get-base-url.ts +17 -2
- package/yarnrc.yml +2 -2
- package/dist/3748.js +0 -1
- package/dist/3748.js.map +0 -1
- package/src/otp/otp.scss +0 -46
|
@@ -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,45 @@ import {
|
|
|
17
10
|
useConfig,
|
|
18
11
|
useConnectivity,
|
|
19
12
|
useSession,
|
|
20
|
-
} from
|
|
21
|
-
import { type ConfigSchema } from
|
|
22
|
-
import Logo from
|
|
23
|
-
import
|
|
24
|
-
import
|
|
25
|
-
import {
|
|
13
|
+
} from '@openmrs/esm-framework';
|
|
14
|
+
import { type ConfigSchema } from '../config-schema';
|
|
15
|
+
import Logo from '../logo.component';
|
|
16
|
+
import styles from './login.scss';
|
|
17
|
+
import { getEmailAndPhone, getOtp } from '../resources/otp.resource';
|
|
18
|
+
import { getOtpEnabledStatus } from '../utils/get-base-url';
|
|
19
|
+
import image from '../assets/medicine.jpg';
|
|
20
|
+
import openmrsLogo from '../assets/openmrs.jpg';
|
|
21
|
+
import dhaLogo from '../assets/dha.png';
|
|
22
|
+
import gokLogo from '../assets/gok.png';
|
|
26
23
|
|
|
27
24
|
export interface LoginReferrer {
|
|
28
25
|
referrer?: string;
|
|
29
26
|
}
|
|
30
27
|
|
|
31
28
|
const Login: React.FC = () => {
|
|
32
|
-
const {
|
|
33
|
-
showPasswordOnSeparateScreen,
|
|
34
|
-
provider: loginProvider,
|
|
35
|
-
links: loginLinks,
|
|
36
|
-
} = useConfig<ConfigSchema>();
|
|
29
|
+
const { showPasswordOnSeparateScreen, provider: loginProvider, links: loginLinks } = useConfig<ConfigSchema>();
|
|
37
30
|
const isLoginEnabled = useConnectivity();
|
|
38
31
|
const { t } = useTranslation();
|
|
39
32
|
const { user } = useSession();
|
|
40
|
-
const location = useLocation() as unknown as Omit<Location,
|
|
33
|
+
const location = useLocation() as unknown as Omit<Location, 'state'> & {
|
|
41
34
|
state: LoginReferrer;
|
|
42
35
|
};
|
|
43
36
|
const navigate = useNavigate();
|
|
44
37
|
|
|
45
|
-
const [errorMessage, setErrorMessage] = useState(
|
|
38
|
+
const [errorMessage, setErrorMessage] = useState('');
|
|
46
39
|
const [isLoggingIn, setIsLoggingIn] = useState(false);
|
|
47
|
-
const [password, setPassword] = useState(
|
|
48
|
-
const [username, setUsername] = useState(
|
|
40
|
+
const [password, setPassword] = useState('');
|
|
41
|
+
const [username, setUsername] = useState('');
|
|
49
42
|
const [showPasswordField, setShowPasswordField] = useState(false);
|
|
50
43
|
const passwordInputRef = useRef<HTMLInputElement>(null);
|
|
51
44
|
const usernameInputRef = useRef<HTMLInputElement>(null);
|
|
52
45
|
|
|
53
46
|
useEffect(() => {
|
|
54
47
|
if (!user) {
|
|
55
|
-
if (loginProvider.type ===
|
|
48
|
+
if (loginProvider.type === 'oauth2') {
|
|
56
49
|
openmrsNavigate({ to: loginProvider.loginUrl });
|
|
57
|
-
} else if (!username && location.pathname ===
|
|
58
|
-
navigate(
|
|
50
|
+
} else if (!username && location.pathname === '/login/confirm') {
|
|
51
|
+
navigate('/login');
|
|
59
52
|
}
|
|
60
53
|
}
|
|
61
54
|
}, [username, navigate, location, user, loginProvider]);
|
|
@@ -71,6 +64,12 @@ const Login: React.FC = () => {
|
|
|
71
64
|
}
|
|
72
65
|
}
|
|
73
66
|
}, [showPasswordField, showPasswordOnSeparateScreen]);
|
|
67
|
+
useEffect(() => {
|
|
68
|
+
document.body.classList.add('hide-top-nav');
|
|
69
|
+
return () => {
|
|
70
|
+
document.body.classList.remove('hide-top-nav');
|
|
71
|
+
};
|
|
72
|
+
}, []);
|
|
74
73
|
|
|
75
74
|
const continueLogin = useCallback(() => {
|
|
76
75
|
const currentUsername = usernameInputRef.current?.value?.trim();
|
|
@@ -83,14 +82,8 @@ const Login: React.FC = () => {
|
|
|
83
82
|
}
|
|
84
83
|
}, []);
|
|
85
84
|
|
|
86
|
-
const changeUsername = useCallback(
|
|
87
|
-
|
|
88
|
-
[]
|
|
89
|
-
);
|
|
90
|
-
const changePassword = useCallback(
|
|
91
|
-
(evt: React.ChangeEvent<HTMLInputElement>) => setPassword(evt.target.value),
|
|
92
|
-
[]
|
|
93
|
-
);
|
|
85
|
+
const changeUsername = useCallback((evt: React.ChangeEvent<HTMLInputElement>) => setUsername(evt.target.value), []);
|
|
86
|
+
const changePassword = useCallback((evt: React.ChangeEvent<HTMLInputElement>) => setPassword(evt.target.value), []);
|
|
94
87
|
|
|
95
88
|
const handleSubmit = useCallback(
|
|
96
89
|
async (evt: React.FormEvent<HTMLFormElement>) => {
|
|
@@ -98,9 +91,9 @@ const Login: React.FC = () => {
|
|
|
98
91
|
evt.stopPropagation();
|
|
99
92
|
|
|
100
93
|
// If credentials were autofilled, input onChange might not have been called
|
|
101
|
-
const currentUsername =
|
|
102
|
-
usernameInputRef.current?.value?.trim() || username;
|
|
94
|
+
const currentUsername = usernameInputRef.current?.value?.trim() || username;
|
|
103
95
|
const currentPassword = passwordInputRef.current?.value || password;
|
|
96
|
+
const isOtpEnabled: boolean = await getOtpEnabledStatus();
|
|
104
97
|
|
|
105
98
|
if (showPasswordOnSeparateScreen && !showPasswordField) {
|
|
106
99
|
continueLogin();
|
|
@@ -114,41 +107,86 @@ const Login: React.FC = () => {
|
|
|
114
107
|
|
|
115
108
|
try {
|
|
116
109
|
setIsLoggingIn(true);
|
|
117
|
-
const sessionStore = await refetchCurrentUser(
|
|
118
|
-
currentUsername,
|
|
119
|
-
currentPassword
|
|
120
|
-
);
|
|
110
|
+
const sessionStore = await refetchCurrentUser(currentUsername, currentPassword);
|
|
121
111
|
const session = sessionStore.session;
|
|
122
112
|
const authenticated = sessionStore?.session?.authenticated;
|
|
123
113
|
|
|
124
|
-
if (
|
|
125
|
-
if (
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
if (location
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
114
|
+
if (isOtpEnabled === true) {
|
|
115
|
+
if (authenticated) {
|
|
116
|
+
if (session.sessionLocation) {
|
|
117
|
+
let to = loginLinks?.loginSuccess || '/home';
|
|
118
|
+
if (location?.state?.referrer) {
|
|
119
|
+
if (location.state.referrer.startsWith('/')) {
|
|
120
|
+
to = `\${openmrsSpaBase}${location.state.referrer}`;
|
|
121
|
+
} else {
|
|
122
|
+
to = location.state.referrer;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
const uuid = session.user.person.uuid;
|
|
126
|
+
try {
|
|
127
|
+
const { email, phone } = await getEmailAndPhone(uuid, username, password);
|
|
128
|
+
const res = await getOtp(username, password, email, phone);
|
|
129
|
+
navigate('otp', {
|
|
130
|
+
state: {
|
|
131
|
+
username,
|
|
132
|
+
password,
|
|
133
|
+
referrer: location?.state?.referrer,
|
|
134
|
+
message: res,
|
|
135
|
+
},
|
|
136
|
+
});
|
|
137
|
+
} catch (err: any) {
|
|
138
|
+
setErrorMessage(err.message);
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
} else if (!session.sessionLocation) {
|
|
142
|
+
const uuid = session.user.person.uuid;
|
|
143
|
+
const { email, phone } = await getEmailAndPhone(uuid, username, password);
|
|
144
|
+
try {
|
|
145
|
+
const res = await getOtp(username, password, email, phone);
|
|
146
|
+
navigate('otp', {
|
|
147
|
+
state: {
|
|
148
|
+
username,
|
|
149
|
+
password,
|
|
150
|
+
referrer: location?.state?.referrer,
|
|
151
|
+
message: res,
|
|
152
|
+
},
|
|
153
|
+
});
|
|
154
|
+
} catch (err: any) {
|
|
155
|
+
setErrorMessage(err.message);
|
|
156
|
+
return;
|
|
132
157
|
}
|
|
133
158
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
},
|
|
142
|
-
});
|
|
159
|
+
} else {
|
|
160
|
+
setErrorMessage(t('invalidCredentials', 'Invalid username or password'));
|
|
161
|
+
setUsername('');
|
|
162
|
+
setPassword('');
|
|
163
|
+
if (showPasswordOnSeparateScreen) {
|
|
164
|
+
setShowPasswordField(false);
|
|
165
|
+
}
|
|
143
166
|
}
|
|
144
167
|
} else {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
168
|
+
if (authenticated) {
|
|
169
|
+
if (session.sessionLocation) {
|
|
170
|
+
let to = loginLinks?.loginSuccess || '/home';
|
|
171
|
+
if (location?.state?.referrer) {
|
|
172
|
+
if (location.state.referrer.startsWith('/')) {
|
|
173
|
+
to = `\${openmrsSpaBase}${location.state.referrer}`;
|
|
174
|
+
} else {
|
|
175
|
+
to = location.state.referrer;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
openmrsNavigate({ to });
|
|
180
|
+
} else {
|
|
181
|
+
navigate('/login/location');
|
|
182
|
+
}
|
|
183
|
+
} else {
|
|
184
|
+
setErrorMessage(t('invalidCredentials', 'Invalid username or password'));
|
|
185
|
+
setUsername('');
|
|
186
|
+
setPassword('');
|
|
187
|
+
if (showPasswordOnSeparateScreen) {
|
|
188
|
+
setShowPasswordField(false);
|
|
189
|
+
}
|
|
152
190
|
}
|
|
153
191
|
}
|
|
154
192
|
|
|
@@ -157,12 +195,10 @@ const Login: React.FC = () => {
|
|
|
157
195
|
if (error instanceof Error) {
|
|
158
196
|
setErrorMessage(error.message);
|
|
159
197
|
} else {
|
|
160
|
-
setErrorMessage(
|
|
161
|
-
t("invalidCredentials", "Invalid username or password")
|
|
162
|
-
);
|
|
198
|
+
setErrorMessage(t('invalidCredentials', 'Invalid username or password'));
|
|
163
199
|
}
|
|
164
|
-
setUsername(
|
|
165
|
-
setPassword(
|
|
200
|
+
setUsername('');
|
|
201
|
+
setPassword('');
|
|
166
202
|
if (showPasswordOnSeparateScreen) {
|
|
167
203
|
setShowPasswordField(false);
|
|
168
204
|
}
|
|
@@ -180,147 +216,152 @@ const Login: React.FC = () => {
|
|
|
180
216
|
location,
|
|
181
217
|
t,
|
|
182
218
|
continueLogin,
|
|
183
|
-
]
|
|
219
|
+
],
|
|
184
220
|
);
|
|
185
221
|
|
|
186
|
-
|
|
222
|
+
const handleForgotPassword = () => {
|
|
223
|
+
navigate('forgot-password');
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
if (!loginProvider || loginProvider.type === 'basic') {
|
|
187
227
|
return (
|
|
188
|
-
|
|
189
|
-
<
|
|
190
|
-
{
|
|
191
|
-
<div className={styles.
|
|
192
|
-
<
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
<div className={styles.center}>
|
|
201
|
-
<Logo t={t} />
|
|
202
|
-
</div>
|
|
203
|
-
<form onSubmit={handleSubmit}>
|
|
204
|
-
<div className={styles.inputGroup}>
|
|
205
|
-
<TextInput
|
|
206
|
-
id="username"
|
|
207
|
-
type="text"
|
|
208
|
-
name="username"
|
|
209
|
-
autoComplete="username"
|
|
210
|
-
labelText={t("username", "Username")}
|
|
211
|
-
value={username}
|
|
212
|
-
onChange={changeUsername}
|
|
213
|
-
ref={usernameInputRef}
|
|
214
|
-
required
|
|
215
|
-
autoFocus
|
|
216
|
-
/>
|
|
217
|
-
{showPasswordOnSeparateScreen ? (
|
|
218
|
-
<>
|
|
219
|
-
<div
|
|
220
|
-
className={
|
|
221
|
-
showPasswordField ? undefined : styles.hiddenPasswordField
|
|
222
|
-
}
|
|
223
|
-
>
|
|
224
|
-
<PasswordInput
|
|
225
|
-
id="password"
|
|
226
|
-
labelText={t("password", "Password")}
|
|
227
|
-
name="password"
|
|
228
|
-
autoComplete="current-password"
|
|
229
|
-
onChange={changePassword}
|
|
230
|
-
ref={passwordInputRef}
|
|
231
|
-
required
|
|
232
|
-
value={password}
|
|
233
|
-
showPasswordLabel={t("showPassword", "Show password")}
|
|
234
|
-
invalidText={t(
|
|
235
|
-
"validValueRequired",
|
|
236
|
-
"A valid value is required"
|
|
237
|
-
)}
|
|
238
|
-
aria-hidden={!showPasswordField}
|
|
239
|
-
tabIndex={showPasswordField ? 0 : -1}
|
|
228
|
+
<>
|
|
229
|
+
<div className={styles.wrapperContainer}>
|
|
230
|
+
<div className={styles.logoContainer}>
|
|
231
|
+
<div className={styles.container}>
|
|
232
|
+
<Tile className={styles.loginCard}>
|
|
233
|
+
{errorMessage && (
|
|
234
|
+
<div className={styles.errorMessage}>
|
|
235
|
+
<InlineNotification
|
|
236
|
+
kind="error"
|
|
237
|
+
subtitle={errorMessage}
|
|
238
|
+
title={getCoreTranslation('error')}
|
|
239
|
+
onClick={() => setErrorMessage('')}
|
|
240
240
|
/>
|
|
241
241
|
</div>
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
{
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
description={t("loggingIn", "Logging in") + "..."}
|
|
312
|
-
/>
|
|
242
|
+
)}
|
|
243
|
+
<div className={styles.center}>
|
|
244
|
+
<Logo t={t} />
|
|
245
|
+
</div>
|
|
246
|
+
<form onSubmit={handleSubmit}>
|
|
247
|
+
<div className={styles.inputGroup}>
|
|
248
|
+
<TextInput
|
|
249
|
+
id="username"
|
|
250
|
+
type="text"
|
|
251
|
+
name="username"
|
|
252
|
+
autoComplete="username"
|
|
253
|
+
labelText={t('username', 'Username')}
|
|
254
|
+
value={username}
|
|
255
|
+
onChange={changeUsername}
|
|
256
|
+
ref={usernameInputRef}
|
|
257
|
+
required
|
|
258
|
+
autoFocus
|
|
259
|
+
/>
|
|
260
|
+
{showPasswordOnSeparateScreen ? (
|
|
261
|
+
<>
|
|
262
|
+
<div className={showPasswordField ? undefined : styles.hiddenPasswordField}>
|
|
263
|
+
<PasswordInput
|
|
264
|
+
id="password"
|
|
265
|
+
labelText={t('password', 'Password')}
|
|
266
|
+
name="password"
|
|
267
|
+
autoComplete="current-password"
|
|
268
|
+
onChange={changePassword}
|
|
269
|
+
ref={passwordInputRef}
|
|
270
|
+
required
|
|
271
|
+
value={password}
|
|
272
|
+
showPasswordLabel={t('showPassword', 'Show password')}
|
|
273
|
+
invalidText={t('validValueRequired', 'A valid value is required')}
|
|
274
|
+
aria-hidden={!showPasswordField}
|
|
275
|
+
tabIndex={showPasswordField ? 0 : -1}
|
|
276
|
+
/>
|
|
277
|
+
</div>
|
|
278
|
+
{showPasswordField ? (
|
|
279
|
+
<Button
|
|
280
|
+
type="submit"
|
|
281
|
+
className={styles.continueButton}
|
|
282
|
+
renderIcon={(props) => <ArrowRightIcon size={24} {...props} />}
|
|
283
|
+
iconDescription={t('loginButtonIconDescription', 'Log in button')}
|
|
284
|
+
disabled={!isLoginEnabled || isLoggingIn}
|
|
285
|
+
>
|
|
286
|
+
{isLoggingIn ? (
|
|
287
|
+
<InlineLoading
|
|
288
|
+
className={styles.loader}
|
|
289
|
+
description={t('loggingIn', 'Logging in') + '...'}
|
|
290
|
+
/>
|
|
291
|
+
) : (
|
|
292
|
+
t('login', 'Log in')
|
|
293
|
+
)}
|
|
294
|
+
</Button>
|
|
295
|
+
) : (
|
|
296
|
+
<Button
|
|
297
|
+
type="submit"
|
|
298
|
+
className={styles.continueButton}
|
|
299
|
+
renderIcon={(props) => <ArrowRightIcon size={24} {...props} />}
|
|
300
|
+
iconDescription="Continue to password"
|
|
301
|
+
onClick={(evt) => {
|
|
302
|
+
evt.preventDefault();
|
|
303
|
+
continueLogin();
|
|
304
|
+
}}
|
|
305
|
+
disabled={!isLoginEnabled}
|
|
306
|
+
>
|
|
307
|
+
{t('continue', 'Continue')}
|
|
308
|
+
</Button>
|
|
309
|
+
)}
|
|
310
|
+
</>
|
|
313
311
|
) : (
|
|
314
|
-
|
|
312
|
+
<>
|
|
313
|
+
<PasswordInput
|
|
314
|
+
id="password"
|
|
315
|
+
labelText={t('password', 'Password')}
|
|
316
|
+
name="password"
|
|
317
|
+
autoComplete="current-password"
|
|
318
|
+
onChange={changePassword}
|
|
319
|
+
ref={passwordInputRef}
|
|
320
|
+
required
|
|
321
|
+
value={password}
|
|
322
|
+
showPasswordLabel={t('showPassword', 'Show password')}
|
|
323
|
+
invalidText={t('validValueRequired', 'A valid value is required')}
|
|
324
|
+
/>
|
|
325
|
+
<Button
|
|
326
|
+
type="submit"
|
|
327
|
+
className={styles.continueButton}
|
|
328
|
+
renderIcon={(props) => <ArrowRightIcon size={24} {...props} />}
|
|
329
|
+
iconDescription="Log in"
|
|
330
|
+
disabled={!isLoginEnabled || isLoggingIn}
|
|
331
|
+
>
|
|
332
|
+
{isLoggingIn ? (
|
|
333
|
+
<InlineLoading
|
|
334
|
+
className={styles.loader}
|
|
335
|
+
description={t('loggingIn', 'Logging in') + '...'}
|
|
336
|
+
/>
|
|
337
|
+
) : (
|
|
338
|
+
t('login', 'Log in')
|
|
339
|
+
)}
|
|
340
|
+
</Button>
|
|
341
|
+
<div>
|
|
342
|
+
Forgot Password?
|
|
343
|
+
<a style={{ textDecoration: 'none', cursor: 'pointer' }} onClick={handleForgotPassword}>
|
|
344
|
+
{' '}
|
|
345
|
+
Click to Reset
|
|
346
|
+
</a>
|
|
347
|
+
</div>
|
|
348
|
+
</>
|
|
315
349
|
)}
|
|
316
|
-
</
|
|
317
|
-
|
|
318
|
-
|
|
350
|
+
</div>
|
|
351
|
+
</form>
|
|
352
|
+
</Tile>
|
|
353
|
+
</div>
|
|
354
|
+
<div className={styles.logoSection}>
|
|
355
|
+
<div className={styles.leftLogos}>
|
|
356
|
+
<img src={gokLogo} alt="GOK logo" className={styles.supportingLogos} />
|
|
357
|
+
<img src={dhaLogo} alt="DHA logo" className={styles.dhaLogo} />
|
|
358
|
+
</div>
|
|
359
|
+
<img src={openmrsLogo} alt="OpenMRS logo" className={styles.openmsrsLogo} />
|
|
319
360
|
</div>
|
|
320
|
-
</
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
361
|
+
</div>
|
|
362
|
+
<img className={styles.image} src={image} alt="TAIFA CARE" />
|
|
363
|
+
</div>
|
|
364
|
+
</>
|
|
324
365
|
);
|
|
325
366
|
}
|
|
326
367
|
return null;
|